I'm developing a C++ project, in which I have to compress bitmap data in JPEG format, and write as output a .jpg file that could be opened by a normal image viewer.
I cannot use any kind of libraries.
All the JPEG compression is done, the only thing I'm missing is how to write the stuff in the file correctly.
I already looked into the JFIF format file specification, and googled a lot, but can't figure out how to do it.
More in detail, I should have all the headers put correctly in the file, what I really miss is, after I have the 3 color components ready to be written, how can I do that? (in which order to write the components, how to handle subsampled components (is there other stuff?))
EDIT:
Link to a sample output image (starting from a random generated 8x8 RGB bitmap).
https://dl.dropbox.com/u/46024798/out.jpg
The headers of the image (should) specify that this is a JPEG 8x8px with 3 color components, subsampling 4:4:4.
More in detail, what I did is:
Generate 3 random 8x8 blocks, with values in range [0..255]
Subtract 128 to all the elements (now in range [-128..127])
Apply the Discrete Cosene Transormation to the 3 blocks
Quantize the result
Put the results of quantization in zig-zag order
Look up in the Huffman Tables for the values to write in the files (with End Of Block marker and that kind of stuff)
And for the JPEG compression, that should be ok.
Then I write the file:
First, I write the SOI header, the APP0 marker, the "magic string" JFIF, version, units, density and thumbnail info
Then the quantization table
Then the Start Of Frame marker, with image precision, dimensions, number of components, subsampling info, a DC Huffman Table and an AC Huffman Table
Then the Start Of Scan header (probably where I messed up), in which I point the IDs of the Huffman Tables to use for each component and other stuff for which I don't know exactly the meaning (spectral selection??, successive approximation??)
Finally, I write the Huffman Encoded values in this order:
All the Y block
All the Cb block
All the Cr block
And End Of Image
Related
I checked and roughly understood the JPEG encoding algorithm from the wiki page about it.
Transform to YCbCr, downsample
Split into 8x8 blocks
Apply DCT on blocks
Divide resulting matrices by quantization table entries
Entropy encoding
I understand that the quantization table in a file depends on what created the image, e.g. a camera manufacturer likely has their own proprietary QT algorithms, photoshop etc have their own QTs, there are public ones, etc.
Now, if one opens 'real' JPEG files they may contain several quantization tables. How can this be? I'd assume the decoding algorithm looks like this:
Decode entropy encoding, receive blocks
Multiply blocks by quantization table entries
revert other operations
What does the second/third/... QT do/when is it used? Is there an upper limit on the number of QTs in a JPEG file? When does it happen that a second QT is added to a JPEG file?
The quantization tables are used for different color components.
Like you already know, in a first step the image is transformed into YCbCr Color Space. In this color space you have three colors: Luminance (Y), Chrominance blue (Cb) and Chrominance red (Cr). As the human eye is less sensitive to colors but very sensitive for brightness, multiple quantization tables are used for the different components.
The Quantization Tables used for the Luminance consists of "lower" values, such that the dividing and rounding will not loose to much information on this component. Blue and red on the other hand have "higher" values as information is not needed that much.
I just tried to convert few JPEGs to a GIF image using some online services. For a collection of 1.8 MB of randomly selected JPEGs, the resultant GIF was about 3.8 MB in size (without any extra compression enabled).
I understand GIF is lossless compression. And that's why I expected the resultant output to be around 1.8 MB (input size). Can someone please help me understand what's happening with this extra space ?
Additionally, is there a better way to bundle a set of images which are similar to each other (for transmission) ?
JPEG is a lossy compressed file, but still it is compressed. When it uncompresses into raw pixel data and then recompressed into GIF, it is logical to get that bigger a size
GIF is worse as a compression method for photographs, it is suited for flat colored drawings mostly. It uses RLE [run-length encoding] if I remember well, that is you get entries in the compressed file that mean "repeat this value N times", so you need to have lots of same colored pixels in horizontal sequence to get good compression.
If you have images that are similar to each other, maybe you should consider packing them as consequtive frames (the more similar should be closer) of a video stream and use some lossless compressor (or even risk it with a lossy one) for video, but maybe this is an overkill.
If you have a color image, multiply the width x height x 3. That is the normal size of the uncompressed image data.
GIF and JPEG are two difference methods for compressing that data. GIF uses the LZW method of compression. In that method the encoder creates a dictionary of previously encountered data sequences. The encoder write codes representing sequences rather than the actual data. This can actual result in an file larger than the actual image data if the encode cannot find such sequences.
These GIF sequences are more likely to occur in drawing where the same colors are used, rather than in photographic images where the color varies subtly through out.
JPEG uses a series of compression steps. These have the drawback that you might not get out exactly what you put in. The first of these is conversion from RGB to YCbCr. There is not a 1-to-1 mapping between these colorspaces so modification can occur there.
Next is subsampling.The reason for going to YCbCr is that you can sample the Cb and Cr components at a lower rate than the Y component and still get good representation of the original image. If you do 1 Y to 4 Cb and 4 Cr you reduce the amount of data to compress by half.
Next is the discrete cosine transform. This is a real number calculation performed on integers. That can produce rounding errors.
Next is quantization. In this step less significant values from the DCT are discarded (less data to compress). It also introduces errors from integer division.
I'm very interested in understanding how graphic file format (PNG, JPG, GIF) work. Are there any code examples that demonstrate how these files are made and also how they are interpreted (viewed in browser)?
Regardless of which graphic file format you are working with, you need to understand the basic nature that all graphic files have in common.
File Header
File Type, Version, (Time & Date Stamp - if included)
Possible data structure/s info or chunks
Flags for which color type to be expected, if compression is available and which type, byte order (endian), has transparency, and other various flags.
Image Data Info
Width normally in pixels sometimes in pels, bits or bytes
Height normally in pixels sometimes in pels, bits or bytes
Bits Per Pixel or Pixel Depth
Image Size in Bytes: numPixelsWidth * numPixelsHeight * ((bits or bytes) for each pixel)
Color Type: - Each Pixel has color data which can vary
Gray Scale
Palette
Color RGB
Color RGBA
Possible Others
If Compression Is Present Which Coding and Encoding Is Used
The actual image data
Once you understand this basic structure then parsing image files becomes easier once you know the specification to the file structure you are working with. When you know how many bytes to read in to your file pointer that includes all headers and chunks, then you can advance your file pointer to the data structure that will read in or write out all the pixel (color) data. In many cases the pixel data is usually 24bits per pixel such that each channel RGBA - Red, Green, Blue, and Alpha are 8bits each or one byte same as an unsigned char. This is represented in either a structure or a two dimensional array. Either way once you know the file's structure and know how to read in the actual image or color data you can easily store it into a single array. Then what you do with it from there depends on your application's needs.
The most detailed information can be obtained by reading the file format specification and implementing a parser in the language you know best.
A good way would be to read the format and transform it into an array of four byte tupples (RGBA, the red, green, blue and alpha parts of a color) This will allow you to use this format as an in between format between formats for easy conversion. At the same time most APIs support the displaying of this raw format.
A good format to get started with is BMP. As old as it is, if this is your first encounter with writing a parser this is a safe an 'easy' format. A good second format is PNG. Start with the uncompressed variations and later add the compression.
Next step is TGA to learn reading chunks or JPG to learn more about compression.
Extra tip: Some implementations of writers contain(ed) errors causing images to be in violation of the format. Others added extra features that never made it to the official specs. When writing a parser this can be a real pain. When you are running into problems always second guess the image you are trying to read. A good binary/hex file reader/editor can be a very helpful tool. I used AXE, if I remember correctly it allows you to overlay the hex codes with a format so you can quickly recognize the header and chunks.
I'm trying to understand the JPEG compression process and performed the following steps to verify a few things.
I take an input image img1.jpg and compress it by using IrfanView, say quality=50 (img1_compress.jpg).
Then I crop a small block from the input image img1.jpg (block.jpg of size 8x8 at X,Y=16,16) and compress it by using the same value of quality parameter (50). Let's call it block_compress.jpg.
Now when I compare this block's pixel values with the one in fully compressed image, they don't match.
To clarify, the pixel value at position 0,0 in block_compress.jpg should match with the pixel value at position 16,16 in img1_compress.jpg.
I'm confused why pixel values don't match? Any ideas?
I just did this experiment with my JPEG codec and the pixel values match. Irfanview may be applying some kind of noise filter or other modifications when it compresses JPEG images. Without seeing the source code to the codec you can't know what it's doing. Your experiment is valid, but by using other people's code to test your theory you can't know what's really going on inside their code.
JPEG is lossy compression algorithm. Compressing one image with identical compression settings in different tools can produce differ result. You need use one of lossless algorithms if you want pixel-to-pixel result. I.e. you can use PNG
"the DC component of each 8x8 block is predicted from the previous block.” : by Oli Charlesworth
In my jpeg file there are few FFDA markers. From which marker my data starts, and how do I know from which marker I decode the file?
The JPEG standard has many options that are not used very often. A typical color image will have 3 color components (Y, Cr, Cb) interleaved in a single scan (one FFDA marker). They can also be stored in any combination in separate scans. A progressive JPEG image encodes multiple scans with more and more detail (AC coefficients) in each successive scan, but the standard allows any combination of color components and coefficients to be mixed in different scans. I have only seen one case of a non-progressive JPEG with separate scans for each color component; it came from an IP camera.
Your JPEG is probably progressive which means you have to decode the data after at least the first FFDA marker, which will bring you an intermediate result.
If this is your first attempt at making a JPEG decoder you should choose another image and try to implement a baseline decoder instead. Progressive images adds a lot of complexity to the problem.