SLIM is a lossless image and video codec focused on very fast encoding, primarily for scientific workloads. It is designed to compress imagery as quickly as possible during an experiment, with the understanding that the data can always be compressed further later. Under advantageous conditions, SLIM achieves compression ratios of 100x at speeds above 2 GB/s.
SLIM is built on top of QOIR, which is based on the exceptionally elegant QOI format. It modifies QOIR to support more data types, any number of channels, and to better handle images featuring large alpha gradients and low entropy regions.
SLIM is still experimental and under active development.
- Lossless encoding and decoding.
- Fast U8 RGB/RGBA/grayscale image paths.
- Support for integer and floating-point sample types, including U8, U16, U32, U64, S8, S16, S32, S64, F16, F32, and F64.
- Multi-channel and byte-plane aware layouts.
- Tile-parallel encoding with configurable thread count.
- Delta-frame encoding against a previous frame/reference.
- Optional lossy noise-floor controls for workflows that can tolerate bounded integer changes.
- Bad-pixel masking and incompressible-byte handling.
- Small C API with no required runtime allocation ownership surprises: returned buffers are freed with
free()unless custom allocation hooks are used.
Requirements:
- CMake
- A C99 compiler
makeor another CMake-supported build tool
Build and run tests:
cmake -S . -B build
cmake --build build -j
ctest --test-dir build --output-on-failureThe main static library target is slim. Benchmark and utility binaries are also built under build/, including examples such as:
build/slim_perf_matrix
build/slim_delta_frames_bench
build/slim_png_bench
build/slim_recover
// ! API subject to change
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include "slim.h"
int main(void) {
const uint32_t width = 2;
const uint32_t height = 2;
/* Interleaved RGB, 8 bits per channel. */
uint8_t pixels[2 * 2 * 3] = {
255, 0, 0, 0, 255, 0,
0, 0, 255, 255, 255, 255,
};
slim_desc desc = {0};
desc.width = width;
desc.height = height;
desc.channels = 3;
desc.bytes_per_channel = 1;
desc.premultiplied_alpha = 0;
desc.data_type = SLIM_DATA_U8;
slim_encode_options opt = {0};
opt.nthreads = 4; /* Use 1 for deterministic single-thread benchmarking. */
size_t encoded_len = 0;
void *encoded = slim_encode_ex(pixels, &desc, &opt, &encoded_len);
if (!encoded) {
fprintf(stderr, "SLIM encode failed\n");
return 1;
}
printf("encoded %zu raw bytes into %zu SLIM bytes\n",
sizeof(pixels), encoded_len);
free(encoded);
return 0;
}Decode with slim_decode() or slim_decode_into():
slim_desc decoded_desc = {0};
uint8_t *decoded = slim_decode(encoded, encoded_len, &decoded_desc);
/* ...use decoded... */
free(decoded);While most image codecs emphasize decode speed, encode speed is SLIM's primary design goal. SLIM's decode speed has not yet been optimized, but it will be very similar to its encode speed, which can reach several gigabytes per second.
Proper benchmarking will come once SLIM is more finalized, but I've tentatively tested SLIM against a few images using libpng, optipng, and jpeg-xl, in both t=1 serial and default t=4 multithreaded mode.
libpng: compression level 1
optipng: libpng level 1 PNG + optipng -o0
JPEG XL: cjxl --distance=0 --effort=1
The FiveK dataset are high quality photographs from https://data.csail.mit.edu/graphics/fivek/
The Icons dataset are small simple images from https://qoiformat.org/benchmark/
The scientific dataset is a sequence of 17 high resolution images.
┌────────────┬───────────────┬──────────────┐
│ Codec │ Median encode │ Median ratio │
├────────────┼───────────────┼──────────────┤
│ libpng l1 │ 65.6 MiB/s │ 0.485 │
├────────────┼───────────────┼──────────────┤
│ optipng o0 │ 50.6 MiB/s │ 0.484 │
├────────────┼───────────────┼──────────────┤
│ jpeg-xl e1 │ 112.0 MiB/s │ 0.737 │
├────────────┼───────────────┼──────────────┤
│ SLIM t1 │ 357.2 MiB/s │ 0.630 │
├────────────┼───────────────┼──────────────┤
│ SLIM t4 │ 1116.1 MiB/s │ 0.630 │
└────────────┴───────────────┴──────────────┘
SLIM t4: 17.1× faster than libpng, 10.0× faster than JXL.
┌────────────┬───────────────┬──────────────┐
│ Codec │ Median encode │ Median ratio │
├────────────┼───────────────┼──────────────┤
│ libpng l1 │ 45.4 MiB/s │ 0.773 │
├────────────┼───────────────┼──────────────┤
│ optipng o0 │ 36.5 MiB/s │ 0.772 │
├────────────┼───────────────┼──────────────┤
│ jpeg-xl e1 │ 164.4 MiB/s │ 1.364 │
├────────────┼───────────────┼──────────────┤
│ SLIM t1 │ 297.3 MiB/s │ 0.816 │
├────────────┼───────────────┼──────────────┤
│ SLIM t4 │ 957.8 MiB/s │ 0.816 │
└────────────┴───────────────┴──────────────┘
SLIM t4: 21.9× faster than libpng, 5.6× faster than JXL.
JXL had pathological performance on the chosen 16-bit images. A different image subset showed 573.7 MiB/s to achieve a 0.922 ratio. 🤷
┌────────────┬───────────────┬──────────────┐
│ Codec │ Median encode │ Median ratio │
├────────────┼───────────────┼──────────────┤
│ libpng l1 │ 68.6 MiB/s │ 0.363 │
├────────────┼───────────────┼──────────────┤
│ optipng o0 │ 56.1 MiB/s │ 0.362 │
├────────────┼───────────────┼──────────────┤
│ jpeg-xl e1 │ 240.0 MiB/s │ 0.285 │
├────────────┼───────────────┼──────────────┤
│ SLIM t1 │ 448.9 MiB/s │ 0.376 │
├────────────┼───────────────┼──────────────┤
│ SLIM t4 │ 1238.6 MiB/s │ 0.376 │
└────────────┴───────────────┴──────────────┘
SLIM t4: 17.7× faster than libpng, 5.4× faster than JXL.
┌────────────┬───────────────┬──────────────┐
│ Codec │ Median encode │ Median ratio │
├────────────┼───────────────┼──────────────┤
│ libpng l1 │ 45.3 MiB/s │ 0.776 │
├────────────┼───────────────┼──────────────┤
│ optipng o0 │ 36.2 MiB/s │ 0.775 │
├────────────┼───────────────┼──────────────┤
│ jpeg-xl e1 │ 163.0 MiB/s │ 1.368 │
├────────────┼───────────────┼──────────────┤
│ SLIM t1 │ 387.6 MiB/s │ 0.688 │
├────────────┼───────────────┼──────────────┤
│ SLIM t4 │ 933.5 MiB/s │ 0.688 │
└────────────┴───────────────┴──────────────┘
SLIM t4: 20.2× faster than libpng, 5.7× faster than JXL.
JXL again had pathological behavior on this image set. On a different image set it got 834.7 MiB/s for 0.602 median ratio.
┌────────────┬───────────────┬──────────────┐
│ Codec │ Median encode │ Median ratio │
├────────────┼───────────────┼──────────────┤
│ libpng l1 │ 133.9 MiB/s │ 0.081 │
├────────────┼───────────────┼──────────────┤
│ optipng o0 │ 86.2 MiB/s │ 0.081 │
├────────────┼───────────────┼──────────────┤
│ jpeg-xl e1 │ 98.0 MiB/s │ 0.069 │
├────────────┼───────────────┼──────────────┤
│ SLIM t1 │ 1295.3 MiB/s │ 0.078 │
├────────────┼───────────────┼──────────────┤
│ SLIM t4 │ 2792.3 MiB/s │ 0.078 │
└────────────┴───────────────┴──────────────┘
SLIM t4: 21.6× faster than libpng, 28.9× faster than JXL.
┌────────────────┬───────────────┬───────────┬─────────────┬──────────────┐
│ Codec / mode │ Encoded bytes │ Raw ratio │ Encode time │ Encode speed │
├────────────────┼───────────────┼───────────┼─────────────┼──────────────┤
│ libpng level 1 │ 9,115,366 │ 0.02634 │ 1.950 s │ 169.2 MiB/s │
├────────────────┼───────────────┼───────────┼─────────────┼──────────────┤
│ optipng -o0 │ 9,102,154 │ 0.02630 │ 2.834 s │ 116.5 MiB/s │
├────────────────┼───────────────┼───────────┼─────────────┼──────────────┤
│ jpeg-xl e1 │ 5,356,755 │ 0.01548 │ 1.565 s │ 210.9 MiB/s │
├────────────────┼───────────────┼───────────┼─────────────┼──────────────┤
│ SLIM intra t1 │ 6,829,905 │ 0.01973 │ 0.202 s │ 1634.6 MiB/s │
├────────────────┼───────────────┼───────────┼─────────────┼──────────────┤
│ SLIM intra t4 │ 6,829,905 │ 0.01973 │ 0.070 s │ 4726.9 MiB/s │
├────────────────┼───────────────┼───────────┼─────────────┼──────────────┤
│ SLIM delta t1 │ 5,500,414 │ 0.01589 │ 0.641 s │ 514.6 MiB/s │
├────────────────┼───────────────┼───────────┼─────────────┼──────────────┤
│ SLIM delta t4 │ 5,500,414 │ 0.01589 │ 0.457 s │ 722.9 MiB/s │
└────────────────┴───────────────┴───────────┴─────────────┴──────────────┘
SLIM intra indicates each image was processed independently. SLIM delta indicates temporal information was exploited. One quarter of 64x64 tiles did not change between frames on this dataset.
Note, the delta code-path is not yet optimized.
┌─────────────────────────┬───────────────┬───────────┬─────────────┬──────────────┐
│ Codec / mode │ Encoded bytes │ Raw ratio │ Encode time │ Encode speed │
├─────────────────────────┼───────────────┼───────────┼─────────────┼──────────────┤
│ libpng level 1 │ 6,880,842 │ 0.019882 │ 1.995 s │ 165.4 MiB/s │
├─────────────────────────┼───────────────┼───────────┼─────────────┼──────────────┤
│ optipng -o0 │ 6,870,846 │ 0.019853 │ 2.848 s │ 115.9 MiB/s │
├─────────────────────────┼───────────────┼───────────┼─────────────┼──────────────┤
│ jpeg-xl e1 │ 3,624,673 │ 0.010473 │ 1.566 s │ 210.8 MiB/s │
├─────────────────────────┼───────────────┼───────────┼─────────────┼──────────────┤
│ SLIM intra t1 │ 4,915,442 │ 0.014203 │ 0.306 s │ 1080.3 MiB/s │
├─────────────────────────┼───────────────┼───────────┼─────────────┼──────────────┤
│ SLIM intra t4 │ 4,915,442 │ 0.014203 │ 0.163 s │ 2025.4 MiB/s │
├─────────────────────────┼───────────────┼───────────┼─────────────┼──────────────┤
│ SLIM delta all-modes t1 │ 3,512,229 │ 0.010148 │ 0.877 s │ 376.5 MiB/s │
├─────────────────────────┼───────────────┼───────────┼─────────────┼──────────────┤
│ SLIM delta all-modes t4 │ 3,512,229 │ 0.010148 │ 0.649 s │ 508.4 MiB/s │
└─────────────────────────┴───────────────┴───────────┴─────────────┴──────────────┘
This variant changes pixel of value exactly 1 to 0, to aid in compression and mitigate thermal sensor noise. libpng, optipng, and jpeg-xl all had their data altered before being passed to them, while SLIM's timings are inclusive of the cost of this alteration logic.
In this final benchmark, SLIM's delta mode was set to a higher-effort mode.