The code in this repository implements the NASA ICER image compression algorithm as a C library. Said compression algorithm is a progressive, wavelet-based image compression algorithm designed to be resistant to data loss, making it suitable for use as the image compression algorithm when encoding images to be transmitted over unreliable delivery channels, such as those in satellite radio communications.
This library was designed with memory-constrained embedded systems in mind, hence the language choice of C, but it should function just as well on normal machines.
- Full color image compression using YUV color space
- Progressive image compression with configurable quality levels
- Wavelet-based compression using various filter types
- Error containment segments to limit data loss effects
- Configurable output size targeting
- Support for both lossless and lossy compression
- Integer-only arithmetic operations for embedded systems
- Memory-conscious implementation with no dynamic allocation
Below are sample images of the compression algorithm producing output images of varying quality depending on the space allocated for it. 10 error containment segments are used, and 9 bitplanes were compressed
original color image (512x512, 768 kilobyte) | 140 kilobyte quota |
100 kilobyte quota | 70 kilobyte quota |
original image (512x512, 262 kilobyte) | 70 kilobyte quota |
50 kilobyte quota | 30 kilobyte quota |
The compression effectiveness depends greatly on the number of error containment segments chosen.
This compression algorithm is based on a document published by NASA. This repository implements the algorithm as described by the paper, with additional support for color images.
This algorithm is best described by the abstract of the document by NASA, which reads as follows:
ICER is a progressive, wavelet-based image data compressor designed to meet the specialized needs of deep-space applications while achieving state-of-the-art compression effectiveness. ICER can provide lossless and lossy compression, and incorporates an error-containment scheme to limit the effects of data loss during transmission. The Mars Exploration Rover (MER) mission will rely primarily on a software implementation of ICER for image compression. This article describes ICER and the methods it uses to meet its goals, and explains the rationale behind the choice of methods. Performance results also are presented.
This library uses code from:
- CRC32 checksum library from the SNIPPETS C Source Code Archive
- Image parsing libraries from STB libraries
There are a few key design considerations which were taking into account when writing this library, which are as follows:
- Designed to only utilise integer arithmetic operations, as designed by NASA
- Avoids dynamic memory allocation in the library, so memory can be better managed in memory-constrained embedded systems
- Optimised for execution on embedded systems
- Error containment features prevent data loss from affecting the quality of the whole image
- Ability to set a output size target and stop compression once the quota is reached
- Able to compress images losslessly (if the output quota permits)
(again, recommended that you carefully read the NASA document to understand why and how these are significant)
The project is designed as a C-library and as such is intended to be used as a part of a larger program, not as a standalone program.
To include the library, select the appropriate flags for which parts of the library to compile:
#define USE_ENCODE_FUNCTIONS // Compile encode functions
#define USE_DECODE_FUNCTIONS // Compile decode functions
#define USE_UINT16_FUNCTIONS // Compile functions for uint16_t arrays
#define USE_UINT8_FUNCTIONS // Compile functions for uint8_t arrays
#include "icer.h"
Then, somewhere at the start, one must call
int icer_init();
in order to initialise the constants and look up tables used in encode and decode functions.
The main functions which are important for the usage of the library are (here we only show the uint8_t
functions, but the uint16_t
versions exists too):
- Compression functions
// For grayscale/single channel images
int icer_compress_image_uint8(uint8_t *image, size_t image_w, size_t image_h, uint8_t stages, enum icer_filter_types filt, uint8_t segments, icer_output_data_buf_typedef *output_data);
// For colour images
int icer_compress_image_yuv_uint8(uint8_t *y_channel, uint8_t *u_channel, uint8_t *v_channel, size_t image_w, size_t image_h, uint8_t stages, enum icer_filter_types filt, uint8_t segments, icer_output_data_buf_typedef *output_data);
which is the function which enables the user to specify an image buffer of a specific width and height, as well as the filter coefficients to use, the number of time to perform wavelet decomposition, and the number of error containment segments to subdivide the image into
- Decompression functions
// For grayscale/single channel images
int icer_decompress_image_uint8(uint8_t *image, size_t *image_w, size_t *image_h, size_t image_bufsize, uint8_t *datastream, size_t data_length, uint8_t stages, enum icer_filter_types filt, uint8_t segments);
// For colour images
int icer_decompress_image_yuv_uint8(uint8_t *y_channel, uint8_t *u_channel, uint8_t *v_channel, size_t *image_w, size_t *image_h, size_t image_bufsize, const uint8_t *datastream, size_t data_length, uint8_t stages, enum icer_filter_types filt, uint8_t segments);
which is the function which enables the user to decompress a byte stream stored inside datastream
and fill the image and the dimensions into the buffer specified.
- Utility Functions
int icer_get_image_dimensions(const uint8_t *datastream, size_t data_length, size_t *image_w, size_t *image_h);
which can be used to get the dimensions of an encoded image (useful for knowing how much space to allocate for the image to decode it).
To run the example code, simply build it and place the boat.512.bmp
(and boatcolor.512.bmp
for color encode) image as the same folder as the executable to generate the output.
The library provides several compile-time configuration options through preprocessor definitions that allow you to optimize memory usage and customize functionality for your specific needs.
// Maximum number of error containment segments
#ifndef ICER_MAX_SEGMENTS
#define ICER_MAX_SEGMENTS 32
#endif
// Maximum number of wavelet decomposition stages
#ifndef ICER_MAX_DECOMP_STAGES
#define ICER_MAX_DECOMP_STAGES 6
#endif
// Maximum number of packets for 8-bit and 16-bit processing
#ifndef ICER_MAX_PACKETS
#define ICER_MAX_PACKETS 300
#endif
#ifndef ICER_MAX_PACKETS_16
#define ICER_MAX_PACKETS_16 800
#endif
// Number of bitplanes to compress for 8-bit and 16-bit modes
#ifndef ICER_BITPLANES_TO_COMPRESS_8
#define ICER_BITPLANES_TO_COMPRESS_8 7
#endif
#ifndef ICER_BITPLANES_TO_COMPRESS_16
#define ICER_BITPLANES_TO_COMPRESS_16 9
#endif
// Select which bit depth functions to compile
#if !defined(USE_UINT8_FUNCTIONS) && !defined(USE_UINT16_FUNCTIONS)
#define USE_UINT8_FUNCTIONS
#define USE_UINT16_FUNCTIONS
#endif
// Select whether to compile encode/decode functions
#if !defined(USE_DECODE_FUNCTIONS) && !defined(USE_ENCODE_FUNCTIONS)
#define USE_DECODE_FUNCTIONS
#define USE_ENCODE_FUNCTIONS
#endif
// Enable user-provided buffers for fine-grained memory control
//#define USER_PROVIDED_BUFFERS
When USER_PROVIDED_BUFFERS
is defined, you must allocate the following buffers:
For 8-bit encoding:
icer_packet_context icer_packets[ICER_MAX_PACKETS];
icer_image_segment_typedef *icer_rearrange_segments_8[ICER_CHANNEL_MAX + 1][ICER_MAX_DECOMP_STAGES + 1][ICER_SUBBAND_MAX + 1][7][ICER_MAX_SEGMENTS + 1];
For 8-bit decoding:
icer_image_segment_typedef *icer_reconstruct_data_8[ICER_CHANNEL_MAX + 1][ICER_MAX_DECOMP_STAGES + 1][ICER_SUBBAND_MAX + 1][ICER_MAX_SEGMENTS + 1][7];
For 16-bit encoding:
icer_packet_context icer_packets_16[ICER_MAX_PACKETS_16];
icer_image_segment_typedef *icer_rearrange_segments_16[ICER_CHANNEL_MAX + 1][ICER_MAX_DECOMP_STAGES + 1][ICER_SUBBAND_MAX + 1][15][ICER_MAX_SEGMENTS + 1];
For 16-bit decoding:
icer_image_segment_typedef *icer_reconstruct_data_16[ICER_CHANNEL_MAX + 1][ICER_MAX_DECOMP_STAGES + 1][ICER_SUBBAND_MAX + 1][ICER_MAX_SEGMENTS + 1][15];
Common encoding buffer:
uint16_t icer_encode_circ_buf[ICER_CIRC_BUF_SIZE]; // ICER_CIRC_BUF_SIZE = 2048
// Define custom CRC32 implementation (e.g., hardware acceleration)
//#define CRC32BUF_FUNCTION(x, y) your_custom_crc32_function
When defined, you can provide your own CRC32 implementation with the following signature:
uint32_t crc32buf(char *buf, size_t len)
The library now supports:
- 8-bit and 16-bit image processing
- Full color support using YUV color space
- Multiple wavelet filter types
- Configurable error containment segments
- Progressive compression with size targeting
Future improvements may include:
- Additional color space support
- Enhanced compression options for different color channels