libjpeg decoding to BGR
Asked Answered
W

6

10

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.

Whop answered 11/4, 2011 at 10:12 Comment(0)
G
10

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.

Gayden answered 11/4, 2011 at 11:17 Comment(7)
Thank you very much for this solution. So, I switched to libjpeg-turbo and set the colour space (jpeg_decompress_struct::out_color_space) to JCS_EXT_BGR. For further optimisations I am looking at moving to JCS_EXT_BGRX and modifying my client library to read in 4 byte chunks, reaping the benefits of fetching 32-bit aligned chunks rather than overlapping 24-bit chunks. I imagine this will be much more cache friendly.Whop
Oh and by the way, although our suggestion of modifying the input data was neat, I have no access to this data and should expect it in standard format.Whop
Glad to know it helped. Note that the reason 32-bit chunks may be faster is not exactly cache efficiency (24-bit chunks take less space and are therefore nicer to the cache) but data access simplicity, because accessing a random pixel with 24-bit pixels may require two 32-bit reads (or three 8-bit reads) whereas the same with 32-bit pixels will only require one 32-bit read.Gayden
Thanks for the explanation, this makes sense. I guess fetching a none 4byte aligned 24-bit pixel will incur two 32-bit reads on a modern x86 chip? One to read the first byte of the pixel, and the second to read bytes 2 and 3 of the pixel (also unnecessarily fetching another 2 bytes)?Whop
You cannot read 24 bits on any system I know. Either you read 32 bits and drop the 8 you do not need, or you read 16+8 bits, or you read 8+8+8 bits. If you read 32 bits and are unaligned, the CPU will probably read two 32-bit words and reassemble them for you (at a performance cost) but on some architectures it may crash. If you make several 8-bit reads, you get a performance hit as well, simply because that's more instructions. The difference may be tiny, but exactly how much depends the architecture.Gayden
Yes, I know you cannot read 24 bits at a time, thats why I was suggesting that if your 24-bit pixel value was not 4-byte aligned, this would most likely incur 1 or 2 32-bit reads based on how the 24-bit pixel was staggered across the 4-byte boundaries. In my scenario I switched to BGRX to make sure each pixel was 4-byte aligned and this actually made things slower. My only guess is that it was more expensive for the jpeg to be decoded to BGRX than BGR.Whop
It is possible that libjpeg-turbo has good byte shuffling logic that makes BGR writing faster; or that it has poor cache prefetch logic that makes BGRX writing slower. Hard to say without diving into the code.Gayden
F
5

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.
 */
Farceuse answered 5/10, 2013 at 10:51 Comment(0)
F
3

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

Fraternal answered 16/2, 2013 at 16:1 Comment(0)
O
1

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;
Ostentation answered 20/12, 2016 at 21:33 Comment(0)
A
1

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

Assignation answered 20/12, 2016 at 21:44 Comment(0)
I
-3

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

Igor answered 11/4, 2011 at 10:35 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.