In memory image compression is essential for any type of visual computing application. Images alone can take a good amount of memory, and when creating an application which uses many images, you may soon find yourself using several gigabytes of memory. While this may be fine for your personal workstation, it may not be okay if you intend to release your application to the public. To work around this memory consumption problem, you’ll want to use in-memory image compression techniques to reduce your program’s memory footprint.

Lossy compression

There are two types of image compressions, lossless and lossy. For the purposes of this article, we’ll be talking about lossy. This means that the compressed image will be very similar to, but not identical to the original image. There are two main reasons lossy compression is often favored in programs that need in memory image compression. The first reason is that the human user may not be able to notice a difference between the original and compressed image. The second reason, is that the amount of compression for lossy images is much higher than the compression ratio for lossless images. The difference is often ten fold, but can be adjusted with compression quality parameters. So if you want to dramatically reduce your memory footprint, you may have to choose lossy compression unless you have absolutely no choice.

How to perform in memory compression in C++

First off, image compression is a highly studied topic. Having said that, you should only embark on creating your own image compressor from scratch if you have a strong desire to do so. Otherwise, it would be wise to utilize libraries or source code others have created and so generously shared with the world. There are plenty of options when it comes to image compression libraries. Personally, I prefer to use this code:

It isn’t a library, it is merely four files, jpgd.cpp, jpgd.h, jpge.cpp and jpge.h. These files contain all the routines you will need. For in memory image compression, I convert raw bitmaps to jpeg using compress_image_to_jpeg_file_in_memory. When needed, I use decompress_jpeg_image_from_memory to decompress the image in memory. Below is an example of how to compress an image.

 To compress raw image

#include "jpge.h"
#include "jpgd.h"

using namespace jpgd;
using namespace jpge;

// datastride is number of bytes for each row. For RGB formats, this is usually 3*imageWidth, but may be slightly larger

int resultBufSize =  _dataStride*_height;
unsigned char *pBufferCompressed = new unsigned char[resultBufSize]; //The particular format I use is R,G,B,R,G,B,...
if (pBufferCompressed) // This may be null if you run out of memory. Always test dynamically allocated objects before using them to maintain stability
	// Below is the actual function to compress the data held in _pData. For my purposes, there are three channels, R,G and B.
	// You can have as many or as few channels as you want, since that is a parameter you can pass in. So having an alpha channel,
	// or just a black and white image is not a problem, as long as you properly specify the number of channels in the function call.
	// There is also an additional optional parameter where you can set the image quality. By default, it is set to 85.
	bool temp = compress_image_to_jpeg_file_in_memory(pBufferCompressed, resultBufSize, _width,_height,3,_pData);
	if (temp)
		// _pData is now compressed, we can get rid of it!
		delete[] _pData;
		// For convenience, I use the same variable to store compressed data. Note we're using the compressed data size for the
		// new memory allocation. This is where the memory savings comes from.
		_pData = new unsigned char[resultBufSize];
		if (_pData)
			memcpy(_pData,pBufferCompressed,sizeof(unsigned char)*resultBufSize);
	delete[] pBufferCompressed; // Fincally, delete the temporary buffer

To decompress the jpeg image to a raw bitmap

int uncompressedWidth;
int uncompressedHeight;
int uncompressedNumChannels;
// Below is the function call to decompress the image. _compressedBufferSize is the size of the compressed data, _pData.
unsigned char *decompressedImage = decompress_jpeg_image_from_memory(_pData, _compressedBufferSize,
	&uncompressedWidth, &uncompressedHeight, &uncompressedNumChannels,3);

return decompressedImage;	// if it fails, it'll be null

Beware that there are a few caveats not shown above. For example, you may need to pad your image such that the width and height are divisible by 4 in order for the compression to work properly. Such a drawback is minimal, and considering the simplicity of using the code, it’s one that I an accept for use in my code.