i have several jpeg images and i want to combine them into one big jpeg image.
i can do that by creating a Bitmap and then combining them there but that way if i save it again as jpeg the image will deteriorate.
so is there any method that i can do that without losing quality from decoding/encoding?
in acdsee program i saw an option to rotate jpegs images without deteriorating quality.
so there should be a way to combine several images without losing quality.
thanks
views:
126answers:
3According to Wikipedia/Jpeg it could be possible if your images have sizes that are multiples of 16.
Wikipedia/Lossless editing/JPEG also talks about JPEGJoin that can combine several images.
There is nothing build in in .NET Framework, but you might be able to use the above tools from C#.
Almost all lossless JPEG tools are based on jpegtran from http://sylvana.net/jpegcrop/jpegtran/ (source code is available).
What you need is to extend the jpeg canvas and then use the still experimental "drop" functionality to put an image into another image.
I am not sure of this, but I think the images need to use the same quantization tables (~ encoding quality) in order to be joined losslessly.
well i have written the code and so i wanted to share it here:)
please note that the code wont work on all situations but its fine for my use.
i am using the LibJpeg.Net library http://bitmiracle.com/libjpeg .
also there is a bug in the library (or a bug in me:) ) that you cant get the component height_in_blocks this is why that code will only work on square tiles.
i think that the images need to have the same quantization table as Vlasta mentioned.
i think this code can be expanded to support that but well i didnt need such support.
and now here comes the code :)
public void CreateBigImage()
{
const int iTileWidth = 256;
const int iTileHeigth = 256;
int iImageWidthInTiles = 2;
int iImageHeigthInTiles = 2;
//Open Image to read its header in the new image
BitMiracle.LibJpeg.Classic.jpeg_decompress_struct objJpegDecompressHeader = new BitMiracle.LibJpeg.Classic.jpeg_decompress_struct();
System.IO.FileStream objFileStreamHeaderImage = new System.IO.FileStream(GetImagePath(0), System.IO.FileMode.Open, System.IO.FileAccess.Read);
objJpegDecompressHeader.jpeg_stdio_src(objFileStreamHeaderImage);
objJpegDecompressHeader.jpeg_read_header(true);
BitMiracle.LibJpeg.Classic.jvirt_array<BitMiracle.LibJpeg.Classic.JBLOCK>[] varrJBlockBigImage = new BitMiracle.LibJpeg.Classic.jvirt_array<BitMiracle.LibJpeg.Classic.JBLOCK>[10];
for (int i = 0; i < 3; i++)//3 compounds per image (YCbCr)
{
int iComponentWidthInBlocks = objJpegDecompressHeader.Comp_info[i].Width_in_blocks;
int iComponentHeigthInBlocks = iComponentWidthInBlocks;//there is no Height_in_blocks in the library so will use widht for heigth also (wont work if image is not rectangular)
varrJBlockBigImage[i] = BitMiracle.LibJpeg.Classic.jpeg_common_struct.CreateBlocksArray(iComponentWidthInBlocks * iImageWidthInTiles, iComponentHeigthInBlocks * iImageHeigthInTiles);
}
for (int iX = 0; iX < iImageWidthInTiles; iX++)
{
for (int iY = 0; iY < iImageHeigthInTiles; iY++)
{
WriteImageToJBlockArr(varrJBlockBigImage, GetImagePath(iY*iImageHeigthInTiles+iX), iX, iY);
}
}
System.IO.FileStream objFileStreamMegaMap = System.IO.File.Create(GetImagePath(999));
BitMiracle.LibJpeg.Classic.jpeg_compress_struct objJpegCompress = new BitMiracle.LibJpeg.Classic.jpeg_compress_struct();
objJpegCompress.jpeg_stdio_dest(objFileStreamMegaMap);
objJpegDecompressHeader.jpeg_copy_critical_parameters(objJpegCompress);//will copy the critical parameter from the header image
objJpegCompress.Image_height = iTileHeigth * iImageHeigthInTiles;
objJpegCompress.Image_width = iTileWidth * iImageWidthInTiles;
objJpegCompress.jpeg_write_coefficients(varrJBlockBigImage);
objJpegCompress.jpeg_finish_compress();
objFileStreamMegaMap.Close();
objJpegDecompressHeader.jpeg_abort_decompress();
objFileStreamHeaderImage.Close();
}
public void WriteImageToJBlockArr(BitMiracle.LibJpeg.Classic.jvirt_array<BitMiracle.LibJpeg.Classic.JBLOCK>[] varrJBlockNew, string strImagePath, int iTileX, int iTileY)
{
BitMiracle.LibJpeg.Classic.jpeg_decompress_struct objJpegDecompress = new BitMiracle.LibJpeg.Classic.jpeg_decompress_struct();
System.IO.FileStream objFileStreamImage = new System.IO.FileStream(strImagePath, System.IO.FileMode.Open, System.IO.FileAccess.Read);
objJpegDecompress.jpeg_stdio_src(objFileStreamImage);
objJpegDecompress.jpeg_read_header(true);
BitMiracle.LibJpeg.Classic.jvirt_array<BitMiracle.LibJpeg.Classic.JBLOCK>[] varrJBlockOrg = objJpegDecompress.jpeg_read_coefficients();
for (int i = 0; i < 3; i++)//3 compounds per image (YCbCr)
{
int iComponentWidthInBlocks = objJpegDecompress.Comp_info[i].Width_in_blocks;
int iComponentHeigthInBlocks = iComponentWidthInBlocks;//there is no Height_in_blocks in the library so will use widht for heigth also (wont work if image is not rectangular)
for (int iY = 0; iY < iComponentHeigthInBlocks; iY++)
{
for (int iX = 0; iX < iComponentWidthInBlocks; iX++)
{
varrJBlockNew[i].Access(iY + iTileY * iComponentHeigthInBlocks, 1)[0][iX + iTileX * iComponentWidthInBlocks] = varrJBlockOrg[i].Access(iY, 1)[0][iX];
}
}
}
objJpegDecompress.jpeg_finish_decompress();
objFileStreamImage.Close();
}