Lossless transform YCbCr 4:2:2 → JPEG → YCbCr 4:2:2

292 views
Skip to first unread message

Mathieu Malaterre

unread,
Nov 25, 2021, 4:39:54 AM11/25/21
to libjpeg-turbo User Discussion/Support
Hi all,

I am trying to implement (if possible ?) a lossless transform from a YCbCr 4:2:2 raw input into JPEG.

I've written a quick sample application:

* https://github.com/malaterre/PublicRep/blob/master/dicom/yuv2jpg/yuv2jpg.cxx

Basically:

```
cinfo->in_color_space = JCS_YCbCr; /* colorspace of input image, 24bits */
jpeg_set_defaults(cinfo);
jpeg_set_quality(cinfo, 100, TRUE /* limit to baseline-JPEG values */);
```

I also made sure that chroma subsampling was setup to reflect input:

```
cinfo->comp_info[0].h_samp_factor = 2;
cinfo->comp_info[0].v_samp_factor = 1;
cinfo->comp_info[1].h_samp_factor = 1;
cinfo->comp_info[1].v_samp_factor = 1;
cinfo->comp_info[2].h_samp_factor = 1;
cinfo->comp_info[2].v_samp_factor = 1;
```

The above code seems to be giving the correct output for simple inputs (*), but I see difference at the bit-level when re-decompressing back to YCbCr 4:2:2 raw for real-life examples.

Could someone confirm that:
1. My code properly skips color transform step,
2. Discrete cosine transform + quantization step are stable (feeding the output of this step back as input should produce the same output).

Thanks
-Mathieu

(*)
% convert -size 512x512 -depth 8 xc:#000000 black.ppm
% convert -size 512x512 -depth 8 xc:#ffffff white.ppm
% convert -size 512x512 -depth 8 xc:#ff0000 red.ppm
% convert -size 512x512 -depth 8 xc:#00ff00 green.ppm
% convert -size 512x512 -depth 8 xc:#0000ff blue.ppm
% for f in *.ppm; do cjpeg -sample 2x1,1x1,1x1 -optimize -quality 100 -outfile $f.jpg $f; done

DRC

unread,
Nov 25, 2021, 10:33:05 AM11/25/21
to libjpeg-t...@googlegroups.com
I don’t have time to look at the code thoroughly, but I can give you the basic run-down. If you use an input colorspace of YCbCr along with jpeg_write_scanlines(), then the library expects that the input data will be non-subsampled packed pixels. If you want to compress pre-subsampled planar YCbCr data, then you need to use jpeg_write_raw_data(). (libjpeg.txt has more information about that function.) Refer to the tjCompressFromYUVPlanes() function in the TurboJPEG API for an example of its usage (or simply use the TurboJPEG API.)

On Nov 25, 2021, at 3:39 AM, Mathieu Malaterre <mathieu....@gmail.com> wrote:

Hi all,
--
You received this message because you are subscribed to the Google Groups "libjpeg-turbo User Discussion/Support" group.
To unsubscribe from this group and stop receiving emails from it, send an email to libjpeg-turbo-u...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/libjpeg-turbo-users/d2cffd89-1bc2-4790-9e9f-24faf62af87dn%40googlegroups.com.

Mathieu Malaterre

unread,
Nov 26, 2021, 5:22:34 AM11/26/21
to libjpeg-turbo User Discussion/Support
hi,

Here is a quick experiment (*):

$ script.sh && echo "success"
success

Which means in some particular cases I can do a full round-trip PGM → JPG → PGM. However anytime I use a random image, eg:

$ cjpeg -grayscale -optimize -quality 100 -outfile output1.jpg lena.512.pgm
$ djpeg -outfile output1.pgm output1.jpg

At this stage, we know there has not been any color transform and quantization tables are just a bunch of 1's. So let's compress it again (dogfooding):

$ cjpeg -grayscale -optimize -quality 100 -outfile output2.jpg  output1.pgm
$ djpeg -outfile output2.pgm output2.jpg
% cmp output2.pgm output1.pgm
output2.pgm output1.pgm differ: byte 60, line 4

So clearly DCT step is a lossy operation which does not allow round-trip operations. That was the piece of information, I was missing.

-M


(*)
#!/bin/sh -e

for i in $(seq 0 255); do
  value=$(printf "%02x%02x%02x\n" $i $i $i)
  convert -colorspace gray -size 512x512 -depth 8 xc:#$value test.pgm
  # quality:100 imply quant tables are 1's only
  cjpeg -grayscale -optimize -quality 100 -outfile test.jpg test.pgm
  djpeg -outfile test.jpg.pgm test.jpg
  cmp test.jpg.pgm test.pgm
done

DRC

unread,
Dec 4, 2021, 2:28:41 PM12/4/21
to libjpeg-t...@googlegroups.com

Yes, even with quality=100, there is still some slight mathematical error (but no perceptual loss.)  The mathematical error occurs most often in the darker areas of the image.  The original lossless JPEG standard (sometimes called "JPEG-LS") uses DPCM rather than DCT for that reason (https://en.wikipedia.org/wiki/Lossless_JPEG).  Your example doesn't use color conversion, but RGB-to-YCbCr and YCbCr-to-RGB will incur round-off error in addition to the DCT error.

Grayscale loss for lena.512.pgm using the floating point DCT/IDCT:

  max err.= 1 avg err.= 0.084037 rms= 0.290457 PSNR= 58.903

Grayscale loss for lena.512.pgm using the integer DCT/IDCT:

  max err.= 1 avg err.= 0.092814 rms= 0.305248 PSNR= 58.4716

In other words, the output component values never drift by more than +/- 1 relative to the input, and the average drift is < 0.1 across the entire image.

Reply all
Reply to author
Forward
0 new messages