Welcome to Doom9's Forum, THE in-place to be for everyone interested in DVD conversion.

Before you start posting please read the forum rules. By posting to this forum you agree to abide by the rules.

 

Go Back   Doom9's Forum > General > Newbies

Reply
 
Thread Tools Search this Thread Display Modes
Old 24th July 2023, 00:34   #1  |  Link
LStation
Registered User
 
Join Date: Sep 2020
Posts: 19
Help verify RGB to YUV conversion

Hi, I'm authoring sRGB clips and I'd like guidance on converting them to the YUV colorspace and verify it was done correctly.

my first source is this page https://www.silicondust.com/yuv-to-r...-for-tv-video/
they link to the ITU-R REC-BT.709-6 and give an equation, I punch that into an excel spreadsheet and my own guess on what page 6/19 says

to avoid any decoder YUV->RGB conversion, I'll be reading the raw.yuv with ImHex as the hex editor, data visualizing: Hexadecimal (8 bits)
with the answer from this source https://stackoverflow.com/questions/...v422-raw-video
I should be looking at the right addresses for Y0 U0 V0

to start I'll open paint and select a custom color of RGB 52,52,52, fill the canvas, then crop the canvas to 2x2 pixels, then save it as a 24bit bitmap named 52_52_52.bmp
the spreadsheet says my YUV values should be 52,128,128, those values converted by DEC2HEX should be 33,80,80
however my results are 3D,80,80 with multiple methods which I'll list below (and attached an image)

Quote:
ffmpeg -y -i 52_52_52.bmp -pix_fmt yuyv422 -f rawvideo ffmpeg.yuv
ffmpeg -y -color_trc iec61966-2-1 -i 52_52_52.bmp -pix_fmt yuyv422 -f rawvideo ffmpeg.yuv
ffmpeg -y -colorspace bt709 -color_trc iec61966-2-1 -i 52_52_52.bmp -pix_fmt yuyv422 -f rawvideo ffmpeg.yuv
ffmpeg -y -i 52_52_52.bmp -vf zscale=r=limited:matrix=709 -color_trc iec61966_2_1 -colorspace bt709 -color_primaries bt709 -pix_fmt yuyv422 -f rawvideo ffmpeg.yuv

yuy2.avs | ConvertToYUY2(ImageReader("n:\52_52_52.bmp",0000,0000))
ffmpeg -y -i yuy2.avs -f rawvideo avisynth.yuv

52_52_52.bmp in virtualdub2 | save video… Uncompressed… Pixel Format YUYV-709… save as 'any format by ffmpeg'... vd2.yuv
52_52_52.bmp in virtualdub2 | filters… convert format… YUY2 Rec. 709 Limited… save video… Uncompressed… Pixel Format… Filters checked… save as 'any format by ffmpeg'... vd2.yuv
With 3 tools agreeing that 52_52_52.bmp should be 3D,80,80 and not 33,80,80.. can someone clear up my confusion?
Attached Images
 
LStation is offline   Reply With Quote
Old 24th July 2023, 04:12   #2  |  Link
poisondeathray
Registered User
 
Join Date: Sep 2007
Posts: 5,409
Quote:
Originally Posted by LStation View Post
to start I'll open paint and select a custom color of RGB 52,52,52, fill the canvas, then crop the canvas to 2x2 pixels, then save it as a 24bit bitmap named 52_52_52.bmp
the spreadsheet says my YUV values should be 52,128,128, those values converted by DEC2HEX should be 33,80,80
however my results are 3D,80,80 with multiple methods which I'll list below (and attached an image)
If I test one of those methods, eg. zscale, I get the wrong YUV values.

RGB 52,52,52 to YUV 52,128,128 implies a limited range RGB to limited range YUV conversion using 709 matrix only (no primaries or transfer adjustments)

It should be rin=limited for limited range input for zscale in ffmpeg, but it appears broken for yuyv422 or yuv422p , limited RGB to limited YUV conversion for ffmpeg. (IIRC , There are several posts about this on the bug tracker already). Usually the "planar" formats work correctly, but not in this case

zimg(avsresize) , the same library as zscale, works in avisynth or vapoursynth - you can use avs demuxer in ffmpeg or vspipe to ffmpeg

In ffmpeg it should have been


YUY2 (packed) => produces YUV 60,128,128
Code:
ffmpeg -y -i RGB52,52,52.bmp -vf zscale=rangein=limited:range=limited:matrix=709,format=yuyv422 -f rawvideo ffmpeg_limitedrangeRGB_to_limitedrangeYUV_709_yuyv422.yuv
YV16 (planar) => produces YUV 61,128,128
Code:
ffmpeg -y -i RGB52,52,52.bmp -vf zscale=rangein=limited:range=limited:matrix=709,format=yuv422p -f rawvideo ffmpeg_limitedrangeRGB_to_limitedrangeYUV_709_yuv422p.yuv

YUY2 avs script
Code:
ImageSource("RGB52,52,52.bmp").ConvertToPlanarRGB #zimg requires planar
#blankclip(pixel_type="RGBP", colors=[52,52,52]) #you can generate directly in avisynth
z_convertformat(pixel_type="YV16", colorspace_op="rgb:709:709:l=>709:709:709:l")
ConvertToYUY2()
encode avs script to raw YUY2 video => produces the expected YUV 52,128,128
Code:
ffmpeg -i yuy2.avs -c:v rawvideo yuy2.yuv

You can view YUV, RGB, HEX numbers in avspmod as well. RawSourcePlus to load Rawvideo

Last edited by poisondeathray; 24th July 2023 at 04:17.
poisondeathray is offline   Reply With Quote
Old 24th July 2023, 05:04   #3  |  Link
LStation
Registered User
 
Join Date: Sep 2020
Posts: 19
I would like to go from full RGB to Rec.709 (limited), there will be loss.. I just want to make sure it's the expected outcome.

prior to this post I was having multiple outcomes when converting a color chart image, I wanted to know how to get to the right answer so I could check my work, so I picked RGB 52,52,52 as an example since it should be straight forward and work from there.

yuy2/yuyv422 was the first one I came across about how to read it in a hex editor, I read yuv420p in ffmpeg has optimizations that aren't accurate when coming from RGB sources, but I didn't know about any bugs with yuy2 though.

google gave me a bunch of rgb -> (yuv) YCbCr calculators that didn't agree with each other, so I had tried using the document.. but I guess that doesn't tell me enough of what's involved, is there a resource you could point to that lays out the pipeline?

[edit] this page some stackoverflow answers cite
Quote:
https://web.archive.org/web/20180416...onversion.html
For high definition TV (HDTV), different coefficients are used. The possible range of values is identical to the SDTV, to provide the necessary footroom and headroom. The following equation describes the color conversion from RGB to YCbCr for HDTV (according to ITU-R BT.709):
Y = 0.183*R+0.614*G+0.062*B+16
Cb = -0.101*C4-0.339*C5+0.439*C6+128
Cr = 0.439*R-0.399*G-0.04*B+128
that gives 60.668,127.984,128 which could be rounded or truncated.. but I still wonder where this comes from when I don't see this listed in that BT.709 document. https://www.itu.int/rec/R-REC-BT.709 (all revisions here)

Last edited by LStation; 24th July 2023 at 06:33.
LStation is offline   Reply With Quote
Old 24th July 2023, 20:38   #4  |  Link
poisondeathray
Registered User
 
Join Date: Sep 2007
Posts: 5,409
The problem in ffmpeg is only with limited range RGB. Full range RGB <=> Limited range YUV works ok with swscale or zscale

They are all derived from the ITU document. For full range RGB (aka. "computer RGB") , RGB 0,0,0 is black, RGB 255,255,255 is white. So there is a scaling factor applied to RGB as described in your 1st link.

The avisynth explanations and equations
http://avisynth.nl/index.php/Color_conversions

Note that the 1st set of avisynth equations is a variation on full range equations depicted in the ITU document. You have to scale RGB to 16-235 to get the same values as you would with limited RGB to limited YUV and use the "normal" limited range YUV <=> full range RGB equations

But zimg (avsresize in avisynth) , does it correctly for all versions and variants; it's just the ffmpeg zscale implemenation seems broken for limited range RGB (and there are tickets on the bug tracker for that)

You can verify with known expected colors, such as 75% color bars. Actual 8, 10, and 12 bit Y,Cb,Cr values are given in RP219 in a chart for primaries , secondaries . e.g. 8bit 75% red in computer (full range) RGB would be 191,0,0 (red channel 255*0.75 rounded). This would correspond to YCbCr 51,109,212. You should get within expected +/-3 errors due to rounding and precision with 8bit conversions for all colors. If the conversions are done correctly, 10bit YUV should correspond to perfect 8bit RGB colors

Last edited by poisondeathray; 24th July 2023 at 20:49.
poisondeathray is offline   Reply With Quote
Old 24th July 2023, 21:28   #5  |  Link
LStation
Registered User
 
Join Date: Sep 2020
Posts: 19
I came across some github project <https://github.com/nsprljan/ImageCodingResearchTools/blob/master/YUV/rgb2yuv.m> that pointed out the sources for each set of coef's, but there's dead links, however...

while searching for 'bt709 matlab' <https://www.mathworks.com/help/vision/ref/colorspaceconversion.html#mw_6992df0c-b50e-487d-96ff-a22fdb5f6679>
Use conversion specified by = Rec. 709 (HDTV) | Scanning standard = 1125/60/2:1 looks like the one equasys was doing with the wording of the ITU-R document, perhaps I just don't understand the document enough to reproduce the matrix they provide there.

thank you for taking the time to respond at length, I've done some RGB->yuv444p10le->RGB round trips before but I didn't have to do/understand the math for it like I'm attempting atm.

[edit]
http://avisynth.nl/index.php/Color_conversions
Quote:
y - 16 = (Kr*219/255)*r + (Kg*219/255)*g + (Kb*219/255)*b
v - 128 = 112/255*r - Kg*112/255*g/(1-Kr) - Kb*112/255*b/(1-Kr)
u - 128 = - Kr*112/255*r/(1-Kb) - Kg*112/255*g/(1-Kb) + 112/255*b
so for Y.. 0.2126*219/255R is how people arrived to 0.183R okay, I didn't understand what was going on in 3.4/3.5 of the ITU-R document but this clears it up.
I didn't see anyone say 0.183R was a simplified version, just 'oh here's some coef's to use' not where from or how.

Last edited by LStation; 24th July 2023 at 22:08.
LStation is offline   Reply With Quote
Old 25th July 2023, 03:42   #6  |  Link
LStation
Registered User
 
Join Date: Sep 2020
Posts: 19
hmmm, still not seeing the expected outcome.

[edit] I've updated my attempts since Editor4.png, I should've tried the example ffmpeg.org colorspace gives in the sRGB section sooner, added poisondeathrays zscale example too.
Attached Images
  
Attached Files
File Type: zip px.zip (7.8 KB, 29 views)

Last edited by LStation; 25th July 2023 at 13:15.
LStation is offline   Reply With Quote
Old 25th July 2023, 14:27   #7  |  Link
poisondeathray
Registered User
 
Join Date: Sep 2007
Posts: 5,409
Nobody can see your attachments. Attachments can take a while to get approved (sometimes days or weeks...) . Consider uploading to a 3rd party hosting site e.g. google drive, mediafire, dropbox etc....
poisondeathray is offline   Reply With Quote
Old 25th July 2023, 18:39   #8  |  Link
LStation
Registered User
 
Join Date: Sep 2020
Posts: 19
thanks for the heads up, here's a list of my attempts (imgur.com) I was attaching.

when I first posted (months ago) I thought the forum told me when attachments would be pending approval and it hadn't done that recently.

[edit] after passing a small color chart, I'll compare them to each other using any of these neat one of every color images on https://allrgb.com/ (hmm, possible nsfw because Lenna, center fold model, is there too)

Last edited by LStation; 25th July 2023 at 19:02.
LStation is offline   Reply With Quote
Old 25th July 2023, 19:53   #9  |  Link
poisondeathray
Registered User
 
Join Date: Sep 2007
Posts: 5,409
I mentioned this earlier, but there is an issue with planar vs. packed for ffmpeg zscale (I mean in general, separate issue from the limited range RGB issue in ffmpeg)

If you take your last "fail" in the screenshot

RGB 115,82,68

Full range RGB to limited range YUV, 709 matrix only
expected YUV 92,119,143

ffmpeg -i RGB_115,82,68.bmp -vf zscale=rin=full:r=limited:m=709,format=yuyv422 -c:v rawvideo RGB_115,82,68_ffmpeg_zscale709_yuyv422.yuv -y
YUV 93,116,143

ffmpeg -i RGB_115,82,68.bmp -vf zscale=rin=full:r=limited:m=709,format=yuv422p -c:v rawvideo RGB_115,82,68_ffmpeg_zscale709_yuv422p.yuv -y
YUV 92,119,143


YUY2 script (Converted to YUY2 otherwise it would be planar too, YV16)
Code:
#blankclip(width=64,height=64,pixel_type="RGBP", colors=[115,82,68])
ImageSource("RGB_115,82,68.bmp").ConvertToPlanarRGB()
z_convertformat(pixel_type="YV16", colorspace_op="rgb:709:709:f=>709:709:709:l")
ConvertToYUY2()
ffmpeg -i yuy2.avs -c:v rawvideo avs_yuy2.yuv
YUV 92,119,143

Avs works ok too . Notice the colorspace op was changed f for RGB, because now we want full range RGB to limited range YUV



In general, processing planar pixel formats is more optimized and faster, it's preferred

If you needed packed "YUY2" (yuyv422) for some reason, and wanted to use zscale in ffmpeg to avoid avs , you could convert with a 2nd format filter. Of each added filter slows down the processing slightly.

ffmpeg -i RGB_115,82,68.bmp -vf zscale=rin=full:r=limited:m=709,format=yuv422p,format=yuyv422 -c:v rawvideo RGB_115,82,68_ffmpeg_zscale709_yuv422p_to_yuyv422.yuv -y
YUV 92,119,143

Last edited by poisondeathray; 25th July 2023 at 20:10.
poisondeathray is offline   Reply With Quote
Old 25th July 2023, 23:18   #10  |  Link
LStation
Registered User
 
Join Date: Sep 2020
Posts: 19
I put off changing formats ( ConvertToYV16() ) because of rework, minimum width 4px complaints, but thank you for the reminder about packed pixel formats.. my intention was to switch to planar formats like 422p or 420p after I knew which way to read them (attempt 8, 9, and 10 did after I saw expected YUV outcomes from 6 and 7)
YUYV/YUY2 - YU·V - parse the first 4bits of address, 0 1 and 3 is tougher to understand at a glance
422p - YYYYUUVV - parse 8bits of address but they're in an easy to understand order at a glance
420p - YYYYUV - 6bits of address but in the hex editor the addresses shuffle around

I will likely change my width so 32bits of address is a line of a color and retest, thank you for providing that blankclip() example with w,h included that'll save a lot of time.
LStation is offline   Reply With Quote
Old 1st August 2023, 21:29   #11  |  Link
LStation
Registered User
 
Join Date: Sep 2020
Posts: 19
I had a 24 [small] frame color swatch that was coming out as expected values with some but not all commands using a hex editor (great).
Then swapped to an image the swatch was based on with all the colors and white text denoting their rgb values, converted to yuv444p.nut, copied their output frames, put them in an image editor with a strong difference filter applied, and differences were shown around the text (uh oh?).
With a limited review by hand(tools Krita, YUView, and Excel) of the differences observed between four raw outputs of 4:4:4 (YUV and YVU accounted for), zscale wasn't wrong in the differences I reviewed (promising).
I'd like to scale it up using a handful of 4096x4096 16,777,216 color images, so I've settled on taking those images and tiling them to 4wx2h frames to simulate what subsampling might be like.
ffmpeg decodes and pipes rawvideo to od aka "octal dump" which exports as many digits as needed to keep each frame as their own single line of text (in decimal) which can be easily referenced as a frame number in a text file.

the only thing I don't have yet is working with rstudio, 4:4:4 conversions I reproduced in excel seem straight forward.. are there any 4:2:0 quirks I should be aware of? best practices to avg U/V channels?
the text results of rstudio (RGB-> ?) can then be compared to the rawvideo text dumps, expected results vs actual results. Instead of different results vs other different results.

doing something like below (round trip) tests the filters and yuv444p10le specifically, which may not apply to others like 4:2:0 which I've noticed had luma values that drifted but should be full resolution right?
Quote:
ffmpeg -i 1.bmp -vf scale=out_color_matrix=bt709,format=yuv444p10le -f rawvideo - | ffmpeg -f rawvideo -pix_fmt yuv444p10le -s 4096x4096 -i - -vf scale=in_color_matrix=bt709,format=rgb24 roundtrip.bmp
ffmpeg -i 1.bmp -i roundtrip.bmp -lavfi ssim -f null -
[Parsed_ssim_0 @ 000001942a63e900] SSIM R:1.000000 (inf) G:1.000000 (inf) B:1.000000 (inf) All:1.000000 (inf)
LStation is offline   Reply With Quote
Old 2nd August 2023, 14:46   #12  |  Link
LStation
Registered User
 
Join Date: Sep 2020
Posts: 19
With RStudio I've imported the RGB logs did the math (googledrive) and exported what should be bt709 limited YCbCr 4:4:4, then exported 4:4:4 logs with ffmpeg, then in RStudio took RStudioLog-444log which exported its own log, and finally I've searched for any number that isn't zero (googledrive).

FFmpeg -colorspace bt709 -color_trc iec61966-2-1 -pix_fmt yuv444p; 333,556 flaws (no change adding -color_primaries bt709, or -sws_flags bitexact+accurate_rnd+full_chroma_inp)
FFmpeg -i avisynth.avs | ConvertToYUV444(clip,matrix="709"); 203,799 flaws
FFmpeg -i vd-pf1.nut | Virtualdub2 Pixel Format 4:4:4 Rec709 Limited; 48,944 flaws
FFmpeg -vf zscale=rangein=full:r=limited:m=709,format=yuv444p; 216 flaws
FFmpeg -i avisynth.avs | ConvertToPlanarRGB(clip) z_convertformat(pixel_type="YUV444",colorspace_op="rgb:709:709:f=>709:709:709:l"); 216 flaws

my flaw count is flawed at the moment
Quote:
RGB 10,51,54 (first of the 216 I checked)
01.82585 88235294 1 Y
03.34838 11764705 9 Cr
32.32576 Cb
36.49999 99999999 the default rounding behavior for numbers ending in .5 is to round to the nearest even number in RStudio, I'll be replacing that behavior

Last edited by LStation; 3rd August 2023 at 00:19.
LStation is offline   Reply With Quote
Old 2nd August 2023, 22:04   #13  |  Link
poisondeathray
Registered User
 
Join Date: Sep 2007
Posts: 5,409
Quote:
Originally Posted by LStation View Post
Iare there any 4:2:0 quirks I should be aware of? best practices to avg U/V channels?
The resampling kernal used in each direction (down to 4:2:0, up to 4:4:4 and then RGB) is going to give you different results. e.g. Bicubic, Lanczos 3 tap, Spline64, Mitchell, etc... Hundreds of choices and permutations

There is no "good way" to reduce CbCr in 1/2 width, 1/2 height. There is no "best" practice . It's situation dependent . Usually a softer kernal is used for 4:2:0 broadcast because of aliasing with "sharp" kernals

When you don't specify a kernel , the default is usually bicubic . But "bicubic" itself has settings, the settings can be different between different programs for the b,c value . eg. Bicubic in ffmpeg swscale doesn't give exactly the same result as bicubic in avisynth

Quote:
which may not apply to others like 4:2:0 which I've noticed had luma values that drifted but should be full resolution right?
Y is full resolution, but you would expect differences compared to the expected Y values regardless of chroma subsampling for 8bit RGB to 8bit YUV . 8bit RGB to YUV420P10 should give the same Y values as YUV444P10 - is that what you are seeing in terms of "drifting" ?
poisondeathray is offline   Reply With Quote
Old 3rd August 2023, 17:59   #14  |  Link
LStation
Registered User
 
Join Date: Sep 2020
Posts: 19
Quote:
Originally Posted by poisondeathray View Post
Y is full resolution, but you would expect differences compared to the expected Y values regardless of chroma subsampling for 8bit RGB to 8bit YUV
the values written to the file might be off because of BGR24 -> 420yuv conversion (was doing that specific thing), the scaling page suggests that http://trac.ffmpeg.org/wiki/Scaling
'When going from BGR (not RGB) to yuv420p the conversion is broken (off-by-one). Use -vf scale=flags=accurate_rnd to fix that.'

*being less than 2~0.0015% off of expected values for 4:4:4 rawvideo probably won't mean anything after compression gets applied, regardless of how much precision excel, or rstudio use it will be up to how the encoder does it but I wanted to confirm a majority of colors were going in the right direction.

last night I was looking at the RGB color 70,165,135, what is it in Y?
12.7810117647059+101.348047058824+8.37095294117647 (122.50001176470637)+16 138.5 should round up to 139.

Code:
012.7810 117647059
101.3480 47058824
008.3709 5294117647

122.4999 +16 this is likely why ffmpeg wrote down 138 for Y, because one digit further would've been 122.5 exactly
Then I had thought I have to tailor my expected values to encoder behavior, but what if zscale and -pix_fmt are different precision too? came to mind... which lead to me to *

whatever platform might encode 4:2:0 differently than I can test for, I feel I may as well feed them 4:4:4 that started off well.
and any clips I publish myself may be apples to oranges when compared (metrics) to another platform, to a small degree anyway.

I played with simulated truncating precision (googledrive), doesn't line up with that the encoder might be doing but I thought it was interesting for perspective
default precision - 6 digits, 38 mismatches
6 digits - 5 digits, 505 mismatches
6 digits - 4 digits, 5,051 mismatches
6 digits - 3 digits, 49,900 mismatches
6 digits - 2 digits, 496,074 mismatches

so default precision - zscale only having 199 mismatches after accounting for rounding up .5 appears to be really good by comparison
forgive my lack of background in programming and potential math behind it

final remarks (googledrive) on the topic, and for anyone curious here's the 16777216 color videos in .nut containers (googledrive)

Last edited by LStation; 6th August 2023 at 19:26.
LStation is offline   Reply With Quote
Reply

Thread Tools Search this Thread
Search this Thread:

Advanced Search
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT +1. The time now is 17:07.


Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2024, vBulletin Solutions Inc.