10
votes

I am using libjpeg to decode a jpeg image from disk to a memory buffer allocated on the heap. I use jpeg_read_scanlines to read and decode each scanline from the file. This is working perfectly, decoding each pixel as a 24-bit RGB value.

The problem is that I am using an additional third-party library which requires a buffer in BGR format (rather than RGB). When using this library I get odd results as the channels are in the wrong order.

Therefore, I would like to find a way to make libjpeg decode to BGR format rather than RGB. I have trawled the web and cannot find how to configure libjpeg to do this? I know I could do an additional pass over the memory buffer and re-order the colour channels manually, however the application I am working on is extremely time critical and must be as fast and as efficient as possible.

6

6 Answers

9
votes

Several solutions for you:

  • Do the transformation as suggested. If you work on groups of 4 pixels, you can do everything with three 32-bit reads and writes, bitmasks and shifts, and be very fast.
  • Modify libjpeg's YUV to RGB transformation or the stage just after so that it swaps R and B.
  • Use libjpeg-turbo. It is backwards compatible with libjpeg, has SIMD acceleration, and provides JCS_EXT_BGR and JCS_EXT_BGRX colorspaces.
  • Modify your source images so that their R and B channels are swapped. Sounds silly, but it requires zero source code modification.

Also, you say you are after speed yet you manipulate BGR data (instead of BGRX). This does not make much sense to me since aligning pixels on 32 bits boundaries is probably going to be much faster.

4
votes

As Antun Tun says, the config is in jmorecfg.h. In my version of libjpeg (v7), it is on line 320:

#define RGB_RED     0   /* Offset of Red in an RGB scanline element */
#define RGB_GREEN   1   /* Offset of Green */
#define RGB_BLUE    2   /* Offset of Blue */

So you just change those to:

#define RGB_RED     2   /* Offset of Red in an RGB scanline element */
#define RGB_GREEN   1   /* Offset of Green */
#define RGB_BLUE    0   /* Offset of Blue */

And you're done. The comments further say:

/*
 * RESTRICTIONS:
 * 1. The sample applications cjpeg,djpeg do NOT support modified RGB formats.
 * 2. These macros only affect RGB<=>YCbCr color conversion, so they are not
 *    useful if you are using JPEG color spaces other than YCbCr or grayscale.
 * 3. The color quantizer modules will not behave desirably if RGB_PIXELSIZE
 *    is not 3 (they don't understand about dummy color components!).  So you
 *    can't use color quantization if you change that value.
 */
3
votes

JPEGLIB says that you have to modify jmorecfg.h in order to change usual R G B order of values. Here is the link to document: http://www.opensource.apple.com/source/tcl/tcl-20/tcl_ext/tkimg/tkimg/libjpeg/libjpeg.doc It is in Data Formats section

1
votes

Take a look at my JPEG codec.

I haven't actually tested it for speed against libjpeg. If you could do that it might be a revelation. Anyway, the decoder is all in one file, and it's will be pretty simple to simply reverse the order of the channels.

I maintain the JPEG code here:

https://github.com/MalcolmMcLean/babyxrc/tree/master/src

1
votes

Since the last versions of libjpeg, it is also possible to modify the out_color_space attribute of the cinfo object obtained after the jpeg_read_header() call.

Quoting libjpeg.txt:

J_COLOR_SPACE out_color_space

Output color space. jpeg_read_header() sets an appropriate default based on jpeg_color_space; typically it will be RGB or grayscale. The application can change this field to request output in a different colorspace. For example, set it to JCS_GRAYSCALE to get grayscale output from a color file.

So, if you want your images to be decoded as BGR, you can add this line while decompressing:

cinfo.out_color_space = JCS_EXT_BGR;
-3
votes

Libjpeg doesn't have a way to do this, as far as I know.
In any case, it's only an O(n) transformation.