Opened 5 years ago

Closed 5 years ago

Last modified 5 years ago

#3785 closed defect (needs_more_info)

SWSCALE: in_range, in_color_matrix, out_range, out_color_matrix incorrect

Reported by: troy_s Owned by:
Priority: normal Component: swscale
Version: git-master Keywords: range
Cc: Blocked By:
Blocking: Reproduced by developer: no
Analyzed by developer: no

Description

Summary of the bug:
Seems that swscale is still sadly mangling RGB to YCbCr and vice versa transforms. Disabled all ASM speedups to avoid code path issues, as were previously hard coded to 601 coefficents etc.

How to reproduce:
Encode a test chart and check values. Test chart available from http://www.belle-nuit.com/test-chart.

Expected versus Results:
BT.709 and sRGB share identical luminance coefficients. An sRGB image, encoded at full range using BT.709 primaries should decode to a perfect 1:1 regarding RGB values, compression notwithstanding.

Sampling the upper row of colors using the provided test chart, we can see some issues with the encoding / decoding step, when using FFPLAY with the corresponding scale commands.

For example, an encode using

./ffmpeg -loop 1 -i ~/Downloads/testchart.tif -t 3 -c:v h264 -vf "scale=in_range=full:in_color_matrix=bt709:out_range=full:out_color_matrix=bt709" out709-full.mp4

Should produce a perfect 1:1 when played back with

./ffplay -vf "scale=in_range=full:in_color_matrix=bt709:out_range=full:out_color_matrix=bt709" out709-full.mp4

When we sample the sRGB / 709 values using an image editor, the top row should result in RGB values as follows:

Name      R   G   B
Yellow:   180 180 16
Cyan:     16  180 180
Magenta:  180 16  180
Green:    16  180 16
Red:      180 16  16
Blue:     16  16  180

Given that 709 and sRGB share identical primaries, and broadcast scaling is set to full range, there should be a perfect 1:1 result with the values. However, what we find with playback via the ffmpeg command set with proper values is:

Name      R   G   B
Yellow:   179 190 22
Cyan:     31  197 180
Magenta:  167 0   176
Green:    30  209 21
Red:      166 0   19
Blue:     17  5   173

There appears to be breakage with regard to the application of the coefficients, as well as possibly misinterpretation of the full range settings.

The are some transforms that yield vaguely close values. However, these suffer from quantization issues where no such should occur. It would seem that all transformations are likely being performed at the relative bit depth for performance. Sadly, this corrupts the values quite significantly.

./ffmpeg -loop 1 -i ~/Downloads/testchart.tif -t 3 -c:v h264 -vf "scale=in_range=full:in_color_matrix=bt601:out_range=full:out_color_matrix=bt601" out601-full.mp4

The following, despite being 601 coefficients, should yield perfect 1:1 with RGB values (RGB being a relative color space) but quantization issues again result.

./ffplay -vf "scale=in_range=full:in_color_matrix=bt601:out_range=full:out_color_matrix=bt601" out601-full.mp4

Further testing of the in_range and out_range specifics will be conducted when the basic color coefficients issue is corrected.

Conclusion:
Some mangling is still present in the color handling in FFMPEG. Quantization issues can likely be expected until a float (likely non-realtime) option is introduced for higher quality frame by frame encoding / decoding.

However, even with quantization issues, the encoding / decoding coefficients are not responding / respected in correct fashion for 709 to 709 (1:1 with sRGB content).

Attachments (4)

RGB-to-broadcast-YCbCr-709.ods (17.9 KB) - added by troy_s 5 years ago.
ODS spreadsheet of the matrix values used to obtain the EXR result off of the sRGB testchart.
testchart-709-studio.exr.gz (310.3 KB) - added by troy_s 5 years ago.
32 bit float EXR of broadcast scaled 709 values using the matrix and offsets included in adjacent attachment.
testchart-709-studio-8bit.png (108.3 KB) - added by troy_s 5 years ago.
8 bit conversion of the 32 bit EXR in PNG format.
RGB-YCbCr-Calculator.ods (21.1 KB) - added by troy_s 5 years ago.
A LibreOffice? Calc spreadsheet to calculate YCbCr from RGB values for REC.709, REC.601, and SMPTE240M coerfficients.

Download all attachments as: .zip

Change History (22)

comment:1 Changed 5 years ago by troy_s

  • Component changed from undetermined to swscale

Tested yuv444p10be and yuv444p16be and the quantization issues still arise. Could be attributed to a rounding error due to bitshifting?

comment:2 Changed 5 years ago by troy_s

On advice of michaelni, using the scale option to set range and color coefficients is not updating the flags in the stream:

Input #0, mov,mp4,m4a,3gp,3g2,mj2, from './out709-full.mp4':
  Metadata:
    major_brand     : isom
    minor_version   : 512
    compatible_brands: isomiso2avc1mp41
    encoder         : Lavf55.48.101
  Duration: 00:00:03.00, start: 0.000000, bitrate: 168 kb/s
    Stream #0:0(und): Video: h264 (High 4:4:4 Predictive) (avc1 / 0x31637661), yuv444p, 1920x1080 [SAR 1:1 DAR 16:9], 163 kb/s, 25 fps, 25 tbr, 12800 tbn, 50 tbc (default)
    Metadata:
      handler_name    : VideoHandler

Setting the flags manually via -colorspace bt709 and -color_range jpeg results in the correct flags being set in the stream:

Input #0, mov,mp4,m4a,3gp,3g2,mj2, from './out709-full.mp4':
  Metadata:
    major_brand     : isom
    minor_version   : 512
    compatible_brands: isomiso2avc1mp41
    encoder         : Lavf55.48.101
  Duration: 00:00:03.00, start: 0.000000, bitrate: 168 kb/s
    Stream #0:0(und): Video: h264 (High 4:4:4 Predictive) (avc1 / 0x31637661), yuvj444p(pc, bt709), 1920x1080 [SAR 1:1 DAR 16:9], 163 kb/s, 25 fps, 25 tbr, 12800 tbn, 50 tbc (default)
    Metadata:
      handler_name    : VideoHandler

comment:3 Changed 5 years ago by cehoyos

  • Keywords colormanagement colorspace 709 601 removed

Aren't you testing the h264 encoder in your command lines?
Why don't you output to raw yuv files and inspect the values?
Please provide a command line that allows to reproduce the issue together with the complete, uncut console output to make this a valid ticket.

comment:4 Changed 5 years ago by troy_s

I will likely need a little help to assert that the output I'll be getting is precisely what we are discussing. I have used the following ffmpeg command to dump the raw YCbCr to a planar file:

./ffmpeg -i ~/Downloads/testchart.tif -pix_fmt yuv444p -vf scale="in_range=full:in_color_matrix=bt709:out_range=full:out_color_matrix=bt709" out.yuv

I am hoping that, given 444 encoding:

  1. Three planes of Y, Cb, and Cr respectively at the test chart's 1920x1080
    1. Y at 1920x1080 with full JFIF / JPEG range.
    2. Cb / Cr (again with yuv444p) at 1920x1080 with full JFIF / JPEG range.
  2. Encoded using ITU-REC-709 luminance coefficients.

Can we assert that the given line will result in the above?

ffmpeg version N-64740-g6be71e9 Copyright (c) 2000-2014 the FFmpeg developers
  built on Jul 18 2014 15:56:09 with gcc 4.8 (Ubuntu 4.8.2-19ubuntu1)
  configuration: --enable-gpl --enable-libx264 --disable-asm
  libavutil      52. 92.101 / 52. 92.101
  libavcodec     55. 69.100 / 55. 69.100
  libavformat    55. 48.101 / 55. 48.101
  libavdevice    55. 13.102 / 55. 13.102
  libavfilter     4. 11.102 /  4. 11.102
  libswscale      2.  6.100 /  2.  6.100
  libswresample   0. 19.100 /  0. 19.100
  libpostproc    52.  3.100 / 52.  3.100
Input #0, image2, from '/home/aphorism/Downloads/testchart.tif':
  Duration: 00:00:00.04, start: 0.000000, bitrate: N/A
    Stream #0:0: Video: tiff, rgb24, 1920x1080 [SAR 1:1 DAR 16:9], 25 tbr, 25 tbn, 25 tbc
File 'out.yuv' already exists. Overwrite ? [y/N] y
Output #0, rawvideo, to 'out.yuv':
  Metadata:
    encoder         : Lavf55.48.101
    Stream #0:0: Video: rawvideo (444P / 0x50343434), yuv444p, 1920x1080 [SAR 1:1 DAR 16:9], q=2-31, 200 kb/s, 25 fps, 25 tbn, 25 tbc
    Metadata:
      encoder         : Lavc55.69.100 rawvideo
Stream mapping:
  Stream #0:0 -> #0:0 (tiff (native) -> rawvideo (native))
Press [q] to stop, [?] for help
frame=    1 fps=0.0 q=0.0 Lsize=    6075kB time=00:00:00.04 bitrate=1244160.0kbits/s    
video:6075kB audio:0kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: 0.000000%

If we can agree on the encoding command to generate the correct YCbCr output, I can proceed from here.

Thanks for all of your help in all of this. Hopefully we can dig through some of the color issues in FFMPEG and generate test units.

comment:5 Changed 5 years ago by troy_s

Analysing the resultant output image as a raw RGB image for statistical purposes, based on a simple renaming of the planar file to avoid YUV conversion in ImageMagick?:

aphorism@u64x6:~/Develop/ffmpeg$ identify -verbose  -size 1920x1080 -depth 8 -interlace plane rgb:./out.raw
Image: ./out.raw
  Base filename: out.raw
  Format: RGB (Raw red, green, and blue samples)
  Class: DirectClass
  Geometry: 1920x1080+0+0
  Resolution: 72x72
  Print size: 26.6667x15
  Units: Undefined
  Type: TrueColor
  Endianess: LSB
  Colorspace: sRGB
  Depth: 8-bit
  Channel depth:
    red: 8-bit
    green: 8-bit
    blue: 8-bit
  Channel statistics:
    Red:
      min: 16 (0.0627451)
      max: 235 (0.921569)
      mean: 104.958 (0.411601)
      standard deviation: 72.5761 (0.284612)
      kurtosis: -1.32201
      skewness: 0.447203
    Green:
      min: 32 (0.12549)
      max: 240 (0.941176)
      mean: 130.152 (0.510402)
      standard deviation: 37.936 (0.148768)
      kurtosis: 0.516604
      skewness: 0.17742
    Blue:
      min: 0 (0)
      max: 255 (1)
      mean: 1.00469 (0.00393995)
      standard deviation: 12.6817 (0.0497322)
      kurtosis: 251.198
      skewness: 15.0671
  Image statistics:
    Overall:
      min: 0 (0)
      max: 255 (1)
      mean: 78.7052 (0.308648)
      standard deviation: 47.8444 (0.187625)
      kurtosis: 7.50915
      skewness: 1.50966

Assuming the above command generates raw YCbCr in agreement with our estimations, a few things pop out:

  1. The luma (Y' == R channel here) is still in broadcast range.
  2. The Cb is odd. Given that we have a blue gradient going from 0-255, as well as pure white and pure black swatches, we should expect a full range of Cb along the yellow / blue axis. Instead, we are seeing something that looks very close to broadcast range (16-240 8 bit) with a doubled up offset to 32.
  3. The Cr is delivering correct range levels at a cursory glance of the distribution numbers.

comment:6 Changed 5 years ago by troy_s

Attached a (hopefully) accurate EXR with the YCbCr values based off of 709 coefficients.

Attached an ODS of the matrix formula used to convert the sRGB (shared primaries with 709) test chart. Notes on values obtained via the formulas revealed by selecting a cell:

  • The matrix scales the luma via the coefficients for sRGB / 709 primaries as per XYZ positions. The luminance (Y) values for sRGB / 709 primaries for red, green, and blue are 0.2126, 0.7152, and 0.0722 respectively[1]
  • The matrix scales for broadcast range of luma (Y') and Cb / Cr based on 219/255 for luma, and 224/255 respectively.
  • The offsets are give to correctly position the values for Y, Cb, and Cr. These offsets are 16, 128, and 128 respectively.

[1] We should revisit the need for unique coefficients to cover variable color spaces, including the need for uniques for input color spaces alone. This extends into the forthcoming ITU-BT-2020 needs that have XYZ absolute space models.

comment:7 Changed 5 years ago by michael

Fixed the full range issue in b53bdae11f1eceea1a2e25a98aee81e1d1954e14
is there a problem remaining ? (its not easy to tell from the floats and tables, we need 8bit per sample reference output because thats what comes out)

comment:8 Changed 5 years ago by michael

a raw image that can be compared with tests/tiny_psnr would be quite usefull for example

comment:9 Changed 5 years ago by troy_s

Just performed a git pull and tested the raw YCbCr output again. Looks better, but still not quite there given that there are channels that use the whole range of values.

Working on a better test case, but until then, here's the result which again shows error in the Cb plane from what I can see:

Channel statistics:
    Red:
      min: 0 (0)
      max: 255 (1)
      mean: 103.415 (0.40555)
      standard deviation: 84.4958 (0.331356)
      kurtosis: -1.32109
      skewness: 0.447833
    Green:
      min: 18 (0.0705882)
      max: 255 (1)
      mean: 130.343 (0.51115)
      standard deviation: 43.1051 (0.169039)
      kurtosis: 0.529155
      skewness: 0.18529
    Blue:
      min: 0 (0)
      max: 255 (1)
      mean: 0.940625 (0.00368873)
      standard deviation: 13.2052 (0.051785)
      kurtosis: 269.06
      skewness: 15.9677

Cb showing 18 is close to broadcast range, but still off. No idea what might be causing it, but it suggests more than pure broadcast scaling.

comment:10 Changed 5 years ago by troy_s

Ridiculously stupid question likely, but how does one use tiny_psnr? It doesn't appear built by default, nor do I have any clue on how to use it.

comment:11 Changed 5 years ago by cehoyos

$ make tests/tiny_psnr

Changed 5 years ago by troy_s

ODS spreadsheet of the matrix values used to obtain the EXR result off of the sRGB testchart.

Changed 5 years ago by troy_s

32 bit float EXR of broadcast scaled 709 values using the matrix and offsets included in adjacent attachment.

comment:12 Changed 5 years ago by troy_s

Correction on my part, due to the test pattern selected, the ranges are not going to span the entire gamut. A better test chart is being researched for this purpose.

Changed 5 years ago by troy_s

8 bit conversion of the 32 bit EXR in PNG format.

Changed 5 years ago by troy_s

A LibreOffice? Calc spreadsheet to calculate YCbCr from RGB values for REC.709, REC.601, and SMPTE240M coerfficients.

comment:13 Changed 5 years ago by troy_s

Added a YCbCr calculator for testing raw values. Coefficients and formulas referenced from Poynton and company.

comment:14 Changed 5 years ago by troy_s

Can someone instruct me as to how tiny_psnr is intended to be used with a few sample test cases? Thank you.

comment:15 Changed 5 years ago by AndyF

Well I fell at the first hurdle trying to follow this - that being that the testchart.tif are just strange.

They have bars labelled 100% but their RGB values as shown by display (image magick) are eg, 16, 16, 235 - I can't see that makes any sense in an RGB tiff.

Other things - your ffplay command converts to 420 then it (maybe sdl) will stretch the yuv to full range rgb on display.

You can't expect to convert rgb to yuv and back without loss - I think doing this will be the best way to test Issues that may exist as you should still be able to see levels Poynton -

http://www.poynton.com/PDFs/Merging_RGB_and_422.pdf

comment:16 Changed 5 years ago by troy_s

  • Keywords 709 REC.709 YCbCr added
  • Resolution set to needs_more_info
  • Status changed from new to closed

In the interest of clarity, I am going to close this ticket and break down issues on a particular case by case basis.

The case by cases thus far registered are:

SWSCALE: Incorrect Values in Full Range Conversion RGB to YCbCr
https://trac.ffmpeg.org/ticket/3801

SWSCALE: Quantization Errors in Y Studio Range
https://trac.ffmpeg.org/ticket/3794

The spreadsheets here are still useful for anyone looking to test theoretical values.

comment:17 Changed 5 years ago by cehoyos

  • Keywords 709 REC.709 YCbCr removed

comment:18 Changed 5 years ago by cehoyos

  • Keywords range added
Note: See TracTickets for help on using tickets.