Repository navigation
fix chroma resampling - #114
MartinPulec merged 5 commits into
Conversation
The preprocessor stored the chroma of the top-left pixel of each subsampling window (point sampling), which aliases and shifts chroma by half a pixel from the centered siting that JFIF (and decoders) assume. Average the window instead if the input has full chroma resolution and Cb and Cr have the same sampling. On four 4096x4096 orthophotos (q95, 4:2:0, decoded by libjpeg-turbo with fancy upsampling) this improves PSNR by 0.17-0.49 dB, the JPEG is 0.4-0.6 % smaller and the preprocessor kernel takes ~0.01 ms longer. The previous behavior can be selected by GPUJPEG_ENC_OPT_CHROMA_FILTER_BOOL.
The postprocessor replicated 4:2:0, 4:2:2 and 4:4:0 chroma. For
full-resolution output, interpolate it with the triangle filter that
libjpeg uses by default ("fancy upsampling", same weights and rounding).
Other layouts (dynamic kernels) and subsampled output formats are
unchanged. Unlike libjpeg, no exception is made for chroma width <= 2.
Decoding libjpeg-turbo q95 4:2:0 JPEGs of four 4096x4096 orthophotos, PSNR
improves by 0.32-0.70 dB (now equal to libjpeg-turbo), the postprocessor
kernel takes ~0.01 ms longer.
The previous behavior can be selected by GPUJPEG_DEC_OPT_CHROMA_FILTER_BOOL.
Compares the encoder's chroma planes and the decoder's full-resolution output with a CPU reference for 4:2:0, 4:2:2, 4:4:0 and 4:1:1 with the filters on and off, and the option change without reinitialization.
Check also the default (filter on) and the option change on the same coders for each layout (instead of only 4:2:0); express the 4:2:0 reference with the plain 9/3/3/1 weights.
|
Thanks, I've pushed several changes on top of the changes, namely:
Decoder is entirely untouched - I believe that encoder/decoder (opt) is not symmetric, actually. The encoder AFAIK must interpolate (mandated by JFIF). The decoder on the other side may copy the sample (as the chroma information is already partially lost) and the smoothing can be considered optional (also djpeg has Another observation - if encoding with libjpeg(-turbo), |
when we tried to adopt GPU jpeg for our project, I realised that it has slightly worse encoding quality compared to jpeg turbo. so I tasked Claude to look for and fix the issue. codex conducted a review.
I also have some other fixes, but I don't know what your LLM policy is, so I'm starting with this one. I'm happy to iterate on it, you can take it or close it :)
this is the llm summary:
Summary
Improve JPEG colour fidelity by filtering chroma during encoding and decoding.
4:2:2 and 4:4:0 chroma when producing full-resolution output.
previous behaviour.
Evidence
Measured on four real 4096×4096 RGB orthophoto tiles at quality 95, 4:2:0,
using an RTX 4090. RGB PSNR is measured against source pixels.
across the four tiles—equivalent to 14–27% lower mean squared error.
to 0.03–0.06 dB.
postprocessing per 4096×4096 image.
CTest passes all three suites. CUDA memcheck reports no errors or leaks,
and targeted chroma-kernel racecheck reports no hazards. Tests cover
sampling layouts, odd dimensions, padded input, default-on behaviour,
and toggling the filters between calls.