Recently I’ve been doing a lot of embedded uClinux programming for the Analog Devices Blackfin BF537 processor. Writing drivers, application code, things like that. It’s a lot of fun.
![]()
One of the things I’d been battling with was finding a reasonably fast JPEG encoder. Now you need to understand that the BF537 doesn’t have a JPG compression engine or anything like that, so it’ll never be screaming fast. The default uClinux JPEG library is libjpeg. Libjpeg is fairly slow. I wrote a reasonably-optimised DCT function in blackfin assembler for libjpeg & posted it over at http://blackfin.uclinux.org You can find the files, and a description, here at my original posting:
Faster JPEG encoding – files attached
What’s cool is it appears to have been accepted into the mainstream blackfin uClinux distribution (SVN), judging by this tracker entry. So pretty soon everyone will be getting this faster libjpeg code by default.
Which is nice, and it helps, but it’s still too slow. My example 752 x 512 RGB image was taking around 260 ms to JPG encode using the “improved” libjpeg function. Too long. So I looked for alternatives.
I had tried FFMPEG. It’s faster, because the Analog Devices folks wrote some optimisations for it. It’s tricky to tell exactly how fast, because FFMPEG will duplicate frames to give you the framerate you request. Just because FFMPEG gives you 25 frames/sec doesn’t mean you’re getting 25 different frames per second. But that aside, the big problem I had with FFMPEG was its tendancy to malloc() and free() chunks of memory constantly. This would eventually result in memory fragmentation & malloc() failing, sometimes in minutes, sometimes in hours. Problems like this I can do without, and it’d be a hard one to fix.
SCS Vision wrote a JPEG encoder for their leanXcam product. Their encoder appears to be based on one written by Nitin Gupta. Based on comments they wrote in their comment block (which I assumed to be correct & did not personally verify), I calculated it would encode my image in around 290 ms. Obviously that’s not the big improvement I’m looking for.
Analog Devices has a very fast optimised JPEG encoder available, right off their Blackfin website, for free. But it only works with their proprietary VDSP+ tools. It’s not compatible with any of the open-source tools (gcc, uClinux, etc) and it’s only distributed in object code form, which makes it impossible to port. Tantalising, but useless.
Then recently I had a break. I stumbled across the Surveyor robot folks. A very bright fellow over there wrote a pretty decent JPG encoder, which took advantage of an Analog Devices optimised DCT function, as well as doing some other smart things in the code. It only handled YUV images, whereas my image is RGB. So before I could try it, I had to hack up a quick RGB24 to YUV422 conversion routine. But once I did, I was encoding my image in 165 ms. That’s clearly a substantial improvement over the libjpeg 260 ms.
With that success, I set to further optimising the code, both theirs and mine. By the time I was finished, my image was JPEG encoding in 96 ms. Go baby go!
Fast JPG encoding seems to be a common question for Blackfin, so I’m posting the final code here. Click on the link below to get to the download page:
Now here’s how to call the function.
If you’re familiar with libjpeg, you’ll find this easier, because there’s nothing to set up or tear down. encode_image() needs a pointer to the raw image in memory, a pointer to a chunk of memory where it can write the resulting JPG image, and a few other parameters. It returns another pointer, which is the next free location in memory where it had just been writing. So that you can work out how large the just-created JPG image is. It’s up to you to write the JPG image to a file (if indeed you even want to do that).
Here’s the function prototype:
UINT8* encode_image (UINT8 *input_ptr, UINT8 *output_ptr, UINT32 quality_factor, UINT32 image_format, UINT32 image_width, UINT32 image_height);
It’s pretty simple. Pointers to the input raw image buffer (YUV or RGB) and an output image (JPG) buffer. A quality factor which ranges from 1 (highest) to 8 (lowest). I use 2. Setting this to 1 results in much longer execution time & much larger filesizes. Image format can be:
FOUR_ZERO_ZERO (monochrome YUV) is reported in the comments below as also working.
Image width & height should be self-explanatory.
To call it, set up your image buffers as you see fit, then do something like this:
jpeg_buff_end = encode_image(text_img_buff, jpeg_buff, 2, RGB, 640, 480);
Your JPG image is of filesize: (jpeg_buff_end – jpeg_buff)
If you want to save your JPG to a file, you can do something like:
jpegfp = fopen ("myimage.jpg", "w"); fwrite (jpeg_buff, 1, jpeg_buff_end-jpeg_buff, jpegfp); fclose (jpegfp);
That’s it. Easy to use, and it’s the fastest thing, by far, I’m aware of for JPEG encoding on Blackfin uClinux.
8 Responses
flash-player
06|Jul|2009 1Great post!
Strubi
28|Sep|2009 2Hi Frank
Just some quick feedback: FOUR_ZERO_ZERO works, too. Tested it with a monochrome cam.
Greetings!
Frank
30|Oct|2009 3Hi Frank,
I was looking for a fast Jpeg encoder for windows ce and found yours. I had it working after making a few minimal changes of the C code version. The output is very fast. The only problem is that the image comes inverted and the resulting picture is blue-ish. I am pretty sure my camera outputs RGB24.
Thanks a lot
Frank
frank
30|Oct|2009 4Kudos to you for getting it largely running on wince. Obviously you commented out the assembler routines to use the C functions (DCT, huffman, etc).
Chances are the image is inverted because that’s how it comes from the camera. A great many cameras return an inverted image. Not all, but lots.
A BMP image is actually upside down. So if you’re going from a BMP to a JPG, that’s likely what you’re seeing. A BMP stores the image inverted. So if the image comes from the camera inverted & is stored in a BMP “as-is”, the BMP viewer will invert it, resulting in a right-way-up image when viewing the BMP. But a JPG file stores the image as-is, so an inverted source image will result in an inverted JPG image.
RGB24 is a somewhat ambiguous thing. Some drivers think the byte order is red – green – blue. Other drivers think it’s blue- green – red. I’ve seen both. In either case green will be correct, but sometimes red & blue will be swapped.
This can easily be seen by pointing the camera at a strongly-coloured red & blue object. If the colours display backwards, you know where the problem is.
A blue cast (or any other colour cast) over the image may also be caused by a wrong (or completely missing) white balance algorithm. Artificial lighting can also cause a colour cast (our eyes/brains are remarkably good at ignoring such things) – try changing the lighting a bit.
Have fun!
Franklin
30|Oct|2009 5Frank,
You are a genius! The following change produces the expected picture:
INT16 *CB_Ptr = CR;//CB;
INT16 *CR_Ptr = CB;//CR;
Next step, the inverted image.
Thanks a lot
Franklin
Steve Howell
04|Jan|2010 6Hi,
I’ve been trying to build and use this jpeg code in a Microsoft Visual C++ project to convert 24 bit RGB images to JPG. I removed the __attribute__ stuff and switched it back to using all the C routines instead of the assembler ones, but I can’t get it to work properly. It seems to produce a JPEG file in which the first few 8×8 blocks of pixels look correct but the rest of the image is corrupted. On quality level 8 the length of the strip of blocks which look right is longer than it is on quality level 1. I don’t suppose you’d have any idea what I might have got wrong?
The only changes I made to the code were:
Remove the __attribute__s.
In “encodeMCU”, called “quantization” instead of “quantization_asm”.
In “DCT”, commented out the part were it just calls “jpegdct” and returns.
In “huffman”, made it call the PUTBITS macro instead of “putbits_asm”.
In “read_rgb24_format”, I changed the “#if 0″s to “#if 1″s and vice versa, this stopping it using “read_rgb_yuv”.
Any help gratefully accepted!
Thanks
Steve Howell
frank
04|Jan|2010 7Hi Steve,
Hmm, I don’t know. At one point I had a similar problem to what you’re describing, where the JPG image started out correct but then got messed up. It was due to the assembly language DCT algorithm being used at the time. Switching to the C code DCT function fixed the problem, and I then wrote an assembly version of that C code to make it run faster. Is there any chance this might be a data type problem (eg you’re running on a 64 bit machine when this code perhaps assumes 32 bits) or something like that?
Good luck!
Frank.
Steve Howell
04|Jan|2010 8Hi Frank,
Thanks for the very quick reply! I don’t think it’s a 64/32 bit problem. I have 32 bit int’s, 16 bit short’s and 8 bit char’s, as expected by the code. Thanks anyway. I’ll keep investigating.
Steve
Leave a reply
Search
Categories
Archives
Links
Calendar
A design creation of Design Disease
Copyright © 2009 - Frank's Random Wanderings - is proudly powered by WordPress
InSense 1.0 Theme by Design Disease.