komakai / libjpeg-turbo

Main libjpeg-turbo repository
Other
2 stars 1 forks source link

What is this?

This is a forked version of libjpeg-turbo with optimizations for low-memory decoding of progressive-mode JPEGs. For details of libjpeg-turbo and non-turbo libjpeg please refer to README-turbo.md and README.ijg.

Some Background

In a previous life I worked on a media server product that ran on very low memory devices (usually in the range 64-128MB of RAM). One of the functions of the media server was JPEG transcoding, and this was implemented using libjpeg-6b. Usually the transcoder worked without hitch - it received an image file, decoded it, then re-encoded the image at a lower resolution and sent it out again. However we noted that certain images when decoded on certain devices caused the server to crash. It was somewhat of a mystery but eventually we were able to determine a pattern: the problem occurred when decoding progressive-mode JPEG images on devices that did not have any internal storage (or with very small internal storage). We found that the transcoder when writing to the backing store was exhausting all available memory. (Please check structures.txt and the source code for full details of what a backing store is.) On devices with no internal memory the backing store was being created on a RAM drive of about 10MB which soon ran out in most cases. To work around the issue we added a feature to disable transcoding for progressive-mode JPEG; later we added a pixel limit so that relatively small progressive-mode JPEG files could still be transcoded. However intuitively I was sure that there must be another way to decode these images without using hundreds of megabytes of RAM and so I came up with my low memory progressive-mode JPEG decode implementation.

How the implementation works

Two passes are used. The first determines the locations of each of the scans; the second does the actual decoding.

First pass - reads through the file to determine the location of all the "start of scan" markers. Initializes a "scan context" for each scan. Second pass - restores the scan context of the first scan, reads one row of MCUs, saves the updated scan context for the first scan. Repeats for the second scan and all subsequent scans until a complete row of MCUs is decoded. Outputs the completed row of MCUs and repeats for the second row of MCUs etc. The implementation relies on the data source being "seekable" where as the original libjpeg does not. A "check_seekable" method in the jpeg_source_mgr checks for working standard C library seek/tell functions.

OK you say this optimizes memory use for decoding progressive-mode JPEG. How much memory are we actually talking about?

Memory (bytes) required to decode Camera Model Resolution Without optimization With optimization
Sony DSC-T30 6000x4500 108,226,801 723,820
Olympus E-500 2448x3264 48,023,542 451,441
Olympus C750UZ 2288x1712 23,580,721 424,876
Phase One A/S P25 9713x12943 755,173,340 1,672,945

OK these numbers look impressive. How can we verify these results?

In file jmorecfg.h change the line

#undef ENABLE_MEMORY_TEST

to

#define ENABLE_MEMORY_TEST

and rebuild. Then run

tjbench -memtest <JPEG-file-you-want-to-test>

To disable the low memory progressive-mode JPEG decoding optimizations, change the line

#define LOWMEM_PROGRESSIVE_DECODE

to

#undef LOWMEM_PROGRESSIVE_DECODE

and rebuild.

When to use this fork

You want to decode all types of JPEG images without worrying about running out of memory You have a seekable data-source You don't require progressive rendering You don't require ABI (application binary interface) compatibility with other libjpeg libraries

Reporting an issue

If you find a bug in the low memory progressive-mode JPEG decoding optimizations please create an issue with the GitHub issue tracker.

Update: Feb 2022 - rebased onto upstream/main and changed the default branch to main