Fast SSIMULACRA2 derivative implementation in Zig.
fssimu2 | [version]
usage:
fssimu2 [options] <reference> <distorted>
options:
--json output result as json
--err-map <out> save error map to .png/.tga
-h, --help show this help
-v, --version show version information
sRGB PNG, PAM, JPEG, WebP, or AVIF input expectedExample output:
$ ./fssimu2 ref.png dst.png
79.83781132Performance tested on the Intel Core i7 13700k using a 3840x2160 test image. The numbers indicate that this implementation is up to 23% faster and uses ~40% less memory compared to the reference implementation.
poop "ssimulacra2 medium.png dst.png" "./fssimu2 medium.png dst.png"
Benchmark 1 (7 runs): ssimulacra2 medium.png dst.png
measurement mean ± σ min … max outliers delta
wall_time 809ms ± 46.8ms 760ms … 857ms 0 ( 0%) 0%
peak_rss 1.34GB ± 1.51MB 1.34GB … 1.34GB 0 ( 0%) 0%
cpu_cycles 3.90G ± 243M 3.65G … 4.15G 0 ( 0%) 0%
instructions 9.33G ± 3.00M 9.32G … 9.33G 1 (14%) 0%
cache_references 118M ± 1.33M 116M … 119M 0 ( 0%) 0%
cache_misses 60.0M ± 3.00M 57.0M … 63.4M 0 ( 0%) 0%
branch_misses 16.6M ± 101K 16.5M … 16.8M 0 ( 0%) 0%
Benchmark 2 (9 runs): ./fssimu2 medium.png dst.png
measurement mean ± σ min … max outliers delta
wall_time 618ms ± 10.4ms 603ms … 631ms 0 ( 0%) ⚡- 23.6% ± 4.2%
peak_rss 817MB ± 118KB 816MB … 817MB 0 ( 0%) ⚡- 39.1% ± 0.1%
cpu_cycles 3.05G ± 45.1M 2.99G … 3.10G 0 ( 0%) ⚡- 21.7% ± 4.5%
instructions 6.17G ± 24.8M 6.11G … 6.18G 3 (33%) ⚡- 33.8% ± 0.2%
cache_references 74.7M ± 13.7K 74.7M … 74.7M 1 (11%) ⚡- 36.6% ± 0.8%
cache_misses 34.9M ± 686K 34.1M … 35.8M 0 ( 0%) ⚡- 41.9% ± 3.7%
branch_misses 11.4M ± 138K 11.0M … 11.5M 1 (11%) ⚡- 31.2% ± 0.8%
Conformance to the reference SSIMULACRA2 implementation can be tested with validate.py by supplying the fssimu2 binary.
validate.py requires uv and libjxl.
validate.py --custom ~/git-cloning/fssimu2/zig-out/bin/fssimu2 ~/git-cloning/gb82-image-set/png/*Output on the gb82 image set invoking the above command:
SAMPLES: 75
LEVELS: 1.0 2.0 4.0
== custom vs ref ==
pairs: 75
ref mean: 77.5116 std: 10.6899
custom mean: 76.9932 std: 11.1606
mean diff (other - ref): -0.518406
diff stddev: 0.541576
diff stderr: 0.0625359
percentage error (mean diff / ref mean): 0.669%
max absolute error: 1.9165
== correlation ==
PCC (Pearson): 0.999700
SRCC (Spearman): 0.999403
KRCC (Kendall): 0.987748
The library optionally allows specifying an error map output image as either an uncompressed .tga (Targa) or PNG.
Error maps use the Turbo color map for error visualization, which emphasizes visually obvious errors well.
An example output is provided here, generated using ./fssimu2 --err-map map.png ref.png dst_bad.png which resulted in a score of 49.37005220.
Compilation requires:
- Zig (version 0.15.1)
- libjpeg-turbo
- libwebp
- libavif
Build locally:
zig build --release=fastThe binary will emit to zig-out/bin/fssimu2. The library will emit to zig-out/lib/libssimu2.so (or .dylib on macOS, .dll on Windows) and the include will be copied to zig-out/include/ssimu2.h.
Build and install to /usr/local/
zig build --release=fast --prefix /usr/localfssimu2 provides a C-compatible ABI with the ssimu2.h include file, as well as a Zig package for use with Zig's dependency management system.
Adding the fssimu2 dependency allows your Zig project to make use of the public computeSsimu2() in ssimulacra2.zig.
pub fn computeSsimu2(
allocator: std.mem.Allocator,
reference: []const u8,
distorted: []const u8,
width: u32,
height: u32,
channels: u32,
error_map: ?[]u32,
) Ssimu2Error!f64 {
...In order to add it to your project, follow the steps below.
-
Run one of the following commands:
# Pull from latest `main` zig fetch --save https://github.com/gianni-rosato/fssimu2/archive/refs/heads/main.tar.gz # Pull from a specific tag zig fetch --save https://github.com/gianni-rosato/fssimu2/archive/refs/tags/0.1.1.tar.gz
-
Add these lines somewhere in the
build()function inbuild.zigto expose the module to the build system:const fssimu2 = b.dependency("fssimu2", .{ .target = target, .optimize = optimize, });
-
Link the module to your library or binary:
bin.root_module.addImport("fssimu2", fssimu2.module("fssimu2")); bin.linkLibrary(fssimu2.artifact("ssimu2")); // ^ Put these before `b.installArtifact(bin);`
Now, you can import the SSIMULACRA2 computation like so:
const fssimu2 = @import("fssimu2");Once you've compiled and installed fssimu2, include the header in any relevant file to get started:
#include <ssimu2.h>The exposed functionality is as follows:
// Compute a SSIMULACRA2 score
// The caller must ensure that the reference and distorted buffers
// are at least (width * height * channels) bytes long. If not,
// could lead to UB in ReleaseFast
int ssimulacra2_score(
const uint8_t *reference,
const uint8_t *distorted,
const unsigned width,
const unsigned height,
const unsigned channels,
double *out_score
);
// Compute a SSIMULACRA2 score with distortion map
// The caller must ensure that the reference and distorted buffers
// are at least (width * height * channels) bytes long.
// The error_map buffer must be at least (width * height * 4) bytes long
// and will be filled with RGBA values representing the distortion map.
int ssimulacra2_score_with_map(
const uint8_t *reference,
const uint8_t *distorted,
const unsigned width,
const unsigned height,
const unsigned channels,
double *out_score,
uint32_t *error_map
);Builds will require -lssimu2 passed to the compiler.
An example C program is provided in the c_abi_example/ directory, featuring a simple PAM decoder and SSIMULACRA2 computation. See the test.c file for usage.
In order to build the test example, enter the c_abi_example/ dir and run:
cc test.c pam_dec.c -I../zig-out/include -L../zig-out/lib -lssimu2 -o test
# Set library path for Linux
LD_LIBRARY_PATH=../zig-out/lib ./test ref.pam dst.pam
# Set library path for macOS
DYLD_LIBRARY_PATH=../zig-out/lib ./test ref.pam dst.pamFor the error map example, enter the c_abi_example/ dir and run:
cc test_with_map.c pam_dec.c -I../zig-out/include -L../zig-out/lib -lssimu2 -o test_map
# Set library path for Linux
LD_LIBRARY_PATH=../zig-out/lib ./test_map ref.pam dst.pam
# Set library path for macOS
DYLD_LIBRARY_PATH=../zig-out/lib ./test_map ref.pam dst.pamWhen fssimu2 is properly installed system-wide, the library path specifier isn't needed.
This project is under the Apache 2.0 license. More details in LICENSE.
This project uses code from libspng, libminiz, and vapoursynth-zip. Special thanks to the authors. Licenses for third party code are included in the legal/ folder.
