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. |
21st December 2024, 23:19 | #43 | Link | |
Registered User
Join Date: May 2006
Posts: 4,040
|
Quote:
- color distortions because out-of-gamut RGB components are typically just clipped at 255 or 0 (8 bit realm). - loss of details, loss of fine structures due to the clipping See my uploaded example. While the YUV components are within allowed range each (see the histogram), the bad red pixels are "red" represented by clipped red component 255 but fine red details are totally lost in that area. Same for the dark areas with bad pixels where the blue component is clipped at 0. On the other hand legalizing can be a pita as the fully legalized picture may look desaturated or "milky". For home use one may prefer to live with a compromise. Last edited by Sharc; 22nd December 2024 at 00:15. |
|
22nd December 2024, 02:21 | #44 | Link | |
Broadcast Encoder
Join Date: Nov 2013
Location: Royal Borough of Kensington & Chelsea, UK
Posts: 3,145
|
I'm unfortunately way too young and this story should be narrated by those who were there, but I'm gonna try to put things together and narrate the tale of those people I met before they retired.
Quote:
Some of you may be familiar with VideoTek() the function I made (alongside StainlessS, magiblot and algie) to have a Tektronix-like waveform monitor back in 2018. Although I've introduced other scales (especially for HDR) like nits, by default if you don't specify anything, VideoTek() defaults to "volts" as a scale rather than bits or nits, just like in the actual Tektronix waveform monitor. In an SDR Limited TV Range signal you have the signal going from 16 to 235 and this corresponds to 0.0V-0.7V or 700 mV. Now, an SDI cable can carry signals up to 0.8V or 800 mV however due to the fact that you need to have some parts reserved and that you can't use the whole available range for video (also think about audio, teletext subtitles etc, not just the timecode and other syncing stuff), you can't truly carry a full pc range signal. Now, let's forget about millivolt and let's focus on bandwidth, which is a good way to measure what a cable can carry. After all, we use that for all other kind of cables like USB, Thunderbolt etc, so we can use the same method for SDI cables as well. SDI cables went through quite a bit of a journey and, just like other cables that have been around for a very long time, there are several different revisions: 1) 270 Mbit/s 2) 540 Mbit/s 3) 1.4 Gbit/s 4) 2.9 Gbit/s 5) 6 Gbit/s 6) 12 Gbit/s 7) 24 Gbit/s 8) 48 Gbit/s Those revision are more commonly named by the bandwidth they carry, for instance revision 4 is also called 3G-SDI as it carries almost 3 Gbit/s, revision 5 is called 6G-SDI as it carries 6 Gbit/s and so on for all the other revisions, namely 12G-SDI, 24G-SDI, 48G-SDI. The most skilled of you would have realized by now that 48G-SDI has the same bandwidth as the latest HDMI revision, namely HDMI 2.1b, as they can both carry 48 Gbit/s, although nobody calls the latter 48G-HDMI which would have made much more sense than the "2.1b" revision number they went for (I'm looking at you, HDMI consortium!). Anyway, while 48 Gbit/s may sound a lot, when you consider the fact that the signals are uncompressed (both video and audio) and that you also need additional bandwidth for syncing purposes, they aren't really much at all. Anyway, while the initial SDI revision allowed for enough bandwidth to carry only SD streams, as we moved on with revisions we were able to carry larger resolution, larger bit depth and more fps, but we're still limited. The first revision was introduced in 1989, I wasn't even born, let alone being in the broadcast world (I started working at Sky on January 6th, 2016), but to get a sense of the limitation our ancestors faced, let's take 48G-SDI, the latest revision, as an example. If we were to try to display an 8K signal - which is what revision 8, 48G-SDI, is for - we would have a bit of a problem. To perform the calculations, we can use the following formula: (Width * Height) * (Bit Depth) * (Planes) * (frames per second) = video bitrate Calculation 1 7680x4320 4:4:4 Full PC Range 10bit 60fps (7680 * 4320) * (10) * (3) * (60) = 59,719,680,000 bits per second In other words, 59.7 Gbit/s, so we're out of luck. What if we try Limited TV Range, though? Calculation 2 7680x4320 4:2:2 Limited TV Range 10bit 60fps (7680 * 4320) * (10) * (2) * (60) = 39,813,120,000 bits per second In other words, 39.8 Gbit/s. Here the 2 for the planes comes from the fact that in 4:2:2 we have 1 luma channel at full resolution (Y) and 2 subsampled chroma channels (Cb and Cr) by a factor of two, so it would be: 1 + 2/2 = 1 + 1 = 2. Anyway, we've finally got to a feasible bitrate! Do we wanna include 8 stereo 2ch PCM 24bit 48000Hz audio tracks in the mix as well carrying the various languages (i.e Dub) for the movie/tv series? No problem, that would be: (Channels) * (Sample Rate) * (Bit Depth) (16) * (48000) * (24) = 18,432,000 bits per second so, just 18.4 Mbit/s or 0.0184 Gbit/s which, when added to our 39.8 Gbit/s video, makes it still insignificant and way below the 48 Gbit/s threshold. Wanna add subtitles? No problem, that would be around 300 kbit/s (294 kbit/s if we wanna be picky). For 8 subtitles, one per audio language, we would have 8 * 300 = 2400 kbit/s or 0.0024 Gbit/s. That still leaves us with plenty of room for the timecode and other syncing stuff that can be carried through. Obviously one could say "oh, well, but if we can carry a 7680x4320 4:2:2 Limited TV Range 10bit 60fps with 8 Stereo PCM 48000Hz 24bit tracks and 8 subtitles on modern cables, then we could totally use those modern ones to carry Full PC Range FULL HD 10bit 4:4:4 60fps video instead as that would just be 3.7 Gbit/s!". The answer is... yes, yes we could, given that the calculation for 1920x1080 4:4:4 Full PC Range 10bit 60fps would be (1920 * 1080) * (10) * (3) * (60) = 3,732,480,000 bits per second or 3.7 Gbit/s just for the video (excluding audio), but that's not the point. You see, back when FULL HD was a thing, revision 4 was in place, so the overall bandwidth was 2.9 Gbit/s (3G-SDI, basically 3 Gbit/s), so we couldn't. This is to say that just like engineers were limited by the things available at their time back in 1989, 35 years later we're still limited by what we have available in our time. Even now, 48G-SDI is really targeted to be the standard for 8K not 4K, so before we could even dare to attempt to move to something like 4:4:4 Full PC Range, the "next big thing / next hot thing" comes along and we're limited again. This is why, realistically, I don't see 4:4:4 Full PC Range ever becoming a standard, unfortunately. It's just not possible. Last edited by FranceBB; 22nd December 2024 at 19:02. |
|
22nd December 2024, 09:43 | #45 | Link |
Registered User
Join Date: May 2006
Posts: 4,040
|
In analog TV broadcast times there were no bits, no bitdepth, no bitrate, no x:y:z subsampling, but there were spectral bandwidth constraints, backward compatibility issues with existing B&W TV systems, and amplitude constraints to prevent overmodulation (clipping) of analog transmitters. That's when and why "YUV" was born as a technical solution to cope with these constraints. YUV (or component Y, Pb, Pr) is basically an analog concept which later became Y,Cb,Cr in the course of digitization.
Secondly, with respect to analog signal levels and signal excursions ("swing"), analog gear required some headroom/footroom to accommodate signal over- and undershoots due to filtering (ringing, Gibb's phenomenon) and analog circuitry imperfections to prevent clipping. |
22nd December 2024, 16:12 | #46 | Link | |
Registered User
Join Date: May 2006
Posts: 4,040
|
Quote:
https://forum.doom9.org/showthread.p...96#post1405496 ? Or change the equations for "Studio Matrix" ? Last edited by Sharc; 22nd December 2024 at 17:06. |
|
22nd December 2024, 17:43 | #47 | Link |
Registered User
Join Date: Aug 2024
Posts: 319
|
That's a nice reading, thanks!
Is it the cable can't evolve fast enough, or people always want to squeeze more pixels into that poor cable... well, maybe both The naming after speed is like a dream, in consumer grade cables / ports they probably just want to confuse consumers and profit from information asymmetry. By the way, somehow the Limited Range signal uses less bandwidth is not making sense to me, I mean, in digital domain, that's still 10 or 8 bits each plane pixel? |
22nd December 2024, 19:09 | #48 | Link | ||
Broadcast Encoder
Join Date: Nov 2013
Location: Royal Borough of Kensington & Chelsea, UK
Posts: 3,145
|
Quote:
I know, right? If only everything could be like this... Quote:
(no, they're not dead, they're just retired and 64+ miles away from where I work) |
||
22nd December 2024, 20:42 | #49 | Link | |
Registered User
Join Date: Sep 2007
Posts: 5,598
|
Quote:
Those amendments were for Rec709 , PC.709 and they map to full range RGB ; limited range RGB is not included there It would need an option for the "bad RGB" range for r103, which is different than the "bad RGB" range for other scenarios |
|
24th December 2024, 09:56 | #51 | Link | |
Registered User
Join Date: May 2006
Posts: 4,040
|
Quote:
https://avisynthplus.readthedocs.io/...nversions.html the part "YUV [0,255] <-> RGB [0,255] (0 <= [r,g,b] <= 255, 0 <= y <= 255, 0 < [u,v] < 255)" would apply for adding the missing mapping to limited range RGB in the script? I know the various matrix conversions but I am not really familiar with this notation. Last edited by Sharc; 24th December 2024 at 10:02. |
|
24th December 2024, 16:18 | #52 | Link | ||
Registered User
Join Date: Sep 2007
Posts: 5,598
|
Quote:
The studio RGB /limited range RGB equations were posted in another thread, I don't have them handy. They are also listed in "video demystified" , I can dig them up later if you want ***But at this point in avs+, I would just replace everything with avsresize - you have control over source/destination ranges , matrix/transfer/primaries, optimized SIMD (waaay faster) - and it does limited range RGB correctly - I verified the results with other programs "When done correctly " ... Not specifically for the YUV "bad RGB case", but people should be aware that AVS+ internal functions do not perform the RGB to YUV to RGB roundtrip ideally . Matlab / spreadsheet models etc... predict that +2 bits is enough for the YUV step. (ie. 10bit should be enough precision for 8bit RGB to 10bit YUV to 8bit RGB) - but there were unexpected errors in AVS+ internal functions. I investigated this a few years ago, pinterf's reply Quote:
|
||
25th December 2024, 09:53 | #53 | Link | ||
Registered User
Join Date: May 2006
Posts: 4,040
|
Quote:
Quote:
Last edited by Sharc; 25th December 2024 at 15:07. |
||
29th December 2024, 17:53 | #55 | Link |
Registered User
Join Date: May 2006
Posts: 4,040
|
So here a proposal for showing Bad RGB, based on a roundtrip YUV->RGB->YUV, using avsresize (fast).
Matrix and range can be set. Bad pixels are shown as color in the grey video. Example for limited range RGB ("studio") scenario. Code:
###################################################################################### # Analyzes the source video for Out-Of-Gamut RGB by YUV->RGB conversion # Requires avs+ and avsresize # v0.91 2024-12-30 ###################################################################################### source=<source filter here> ## options matrix="601" #601 or 709 matrix YUVrange="limited" #limited or full, (normally limited) RGBrange="limited" #limited for TV (studio) or full for PC interlaced=false #true for interlaced, false for progressive sources histogram_mode="levels" #levels (for histogram) or classic (for waveform) color_badRGB=color_cyan #pixel color for Bad RGB ## source=source.converttoYV24(interlaced=interlaced) #convert to planar for avsresize cs1=matrix+":auto:auto:"+YUVrange+"=>rgb:same:same:"+RGBrange cs2="rgb:auto:auto:"+RGBrange+"=>"+matrix+":same:same:"+YUVrange return stackhorizontal(source.histogram(histogram_mode).subtitle("source"), \BadRGB(source,color_badRGB,cs1,cs2,interlaced).subtitle("Bad RGB = color pixels (cyan)")) ## Function based on "HighlightBadRGB" by jagabo@VideoHelp ## Added % of Bad RGB indication and options for matrix, range and interlaced; using avsresize function BadRGB(clip vid, int "color",string "cs1",string "cs2",bool "interlaced") { color = default(color, $ff0000) cs1=default(cs1,"601:auto:auto:limited=>rgb:same:same:full") cs2=default(cs2,"rgb:auto:auto:full=>601:same:same:limited") interlaced=default(interlaced,false) badcolor = BlankClip(vid, color=color) subtract(vid,vid.z_ConvertFormat(pixel_type="RGBP10",interlaced=interlaced,colorspace_op=cs1,use_props=0) \.z_ConvertFormat(pixel_type="YV24",interlaced=interlaced,colorspace_op=cs2,use_props=0)) absY = Overlay(ColorYUV(off_y=-126), Invert().ColorYUV(off_y=-130), mode="add") absU = Overlay(UtoY().ColorYUV(off_y=-128), UtoY().Invert().ColorYUV(off_y=-128), mode="add") absV = Overlay(VtoY().ColorYUV(off_y=-128), VtoY().Invert().ColorYUV(off_y=-128), mode="add") Overlay(absU,absV, mode="add") Overlay(last,absY, mode="add") mask=ColorYUV(gain_y=65000) avg=mask.ScriptClip("Subtitle(String(AverageLuma/255*100),align=9)").crop(width()-90,0,-0,-(height()-20)).addborders(0,0,0,height()-20) Overlay(vid.grayscale().coloryuv(cont_y=-0),badcolor,0,0,mask) stackhorizontal(last,avg) } Last edited by Sharc; 31st December 2024 at 14:15. Reason: added % indication |
30th December 2024, 16:21 | #56 | Link |
Registered User
Join Date: Sep 2007
Posts: 5,598
|
Nice sharc
Some suggestions - consider adding : - adjustable "bad" RGB range, or a specific r103 broadcast scenario - because of the reserved code values for sync. Any of R,G,B values <=0 or >=255 would be flagged as "out of gamut" for 8bit in a r103 checker . - user adjustable chroma resampling algorithm. eg. bilinear vs. spline16 etc.... - % of "bad RGB/out of gamut" pixels estimation. Because r103 has a 1% leeway, it 's nice to know where you approximately stand as a % The way it's done in the vapoursynth calculation is a mask calculation on black BG - where white pixels are "illegal" according to the configured min/max settings. PlanestatsAverage *100 to express as a percentage. I think in avs+, perhaps the AverageLuma runtime function on a Y8/Y10 mask clip, or maybe RT_Stats eg. ScriptClip sucks, but for a Y8 clip it might look something like ScriptClip(last, "Subtitle(String(AverageLuma / 255 * 100))") - perhaps an aggregate /average stats calculation for the total video , not sure how, maybe RT_stats ? |
31st December 2024, 14:02 | #57 | Link |
Registered User
Join Date: May 2006
Posts: 4,040
|
Thank you pdr for your suggestions. I added the % of Bad RGB indication (per frame) to the script in post#55 as per your proposal. Seems to be reasonably accurate.
For the "1% leeway r103 broadcast assessment" one would probably have to revisit the absolute accuracy of the fast algo for the bad RGB discovery. Maybe using synthesized YCbCr sources as reference. Currently it's quite handy for a quick check of my tape captures though even though it might not be perfectly accurate. Last edited by Sharc; 31st December 2024 at 14:09. |
9th January 2025, 00:01 | #58 | Link | |
Registered User
Join Date: May 2006
Posts: 4,040
|
Quote:
Code:
################################################################################ # Analyzes YUV video sources for Out-Of-Gamut RGB # Scenarios: PC, TV, EBU r103, or user defined # Requires avs+ and avsresize # v2.0 2025-01-14 ################################################################################ ## SOURCE source=<source_filter_here> source=source.crop(18,8,-16,-8) # !! crop any borders to prevent biased statistics !! ## SETTINGS ## matrix="601" # 601 (=470bg) or 709 source matrix (or any supported by avsresize) interlaced=true # set true for interlaced sources, false for progressive sorces scenario=1 # 1=standard PC (limited->full), 2= standard TV (limited->limited), 3=EBU r103 Table 1, 4=custom settings as defined below histogram_mode=1 # 1=histogram, 2=waveform on side, 3= waveform on top, 4=vectorscope reduceby2=false # if set true: reduce output size color_badRGB=color_cyan # pixel color for Bad RGB # custom settings for scenario 4: YUVrange="limited" # "limited" or "full", (normally limited!) RGBrange="full" # "limited" for TV (studio RGB) or "full" for PC thr_high=1023 # upper threshold for scenario 4 , 0 ..... 1023 10bit scale thr_low=0 # lower threshold (10bit scale) for scenario 4 only, <thr_high PCtoTV=false # if set true: shrink full range source to limited range; for source tweaking only TVtoPC=false # if set true: expand limited range source to full range; for source tweaking only ## resample_filter="bilinear" # "point" or "bilinear" or "bicubic" or "spline16" or "spline36 or "spline64" or "lanczos" ## source=source.converttoYV24(interlaced=interlaced) #convert to planar for avsresize source=source.z_ConvertFormat(pixel_type="YUV444P10",interlaced=interlaced,resample_filter=resample_filter) #convert to 10bits if (scenario==1) {YUVrange="limited" RGBrange="full" thr_high=1023 thr_low=0 PCtoTV=false TVtoPC=false text=" out-of-gamut RGB; standard PC"} else {} if (scenario==2) {YUVrange="limited" RGBrange="limited" thr_high=1023 thr_low=0 PCtoTV=false TVtoPC=false text=" out-of-gamut RGB; standard TV"} else {} if (scenario==3) {YUVrange="limited" RGBrange="limited" thr_high=984 thr_low=20 PCtoTV=false TVtoPC=false text=" out-of-gamut RGB; EBU r103"} else {} if (scenario>=4) {text=" out-of-gamut RGB; customized"} else {} if (PCtoTV==true) {source=source.ColorYUV(levels="PC->TV")} else {} if (TVtoPC==true) {source=source.ColorYUV(levels="TV->PC")} else {} if(reduceby2==true) {source=source.z_bilinearresize(width(source)/2,max(256,height(source)/2), interlaced=interlaced)} else {} cs1=matrix+":auto:auto:"+YUVrange+"=>rgb:same:same:"+RGBrange OOG=ShowBadRGB(source,color_badRGB,cs1,thr_high,thr_low,interlaced,resample_filter) ## LAYOUT if (histogram_mode==1) {stackhorizontal(source.histogram("levels").subtitle(" source"), \OOG.subtitle(text))} else if (histogram_mode==2) {stackhorizontal(source.histogram("classic").subtitle(" source"), \OOG.subtitle(text))} else if (histogram_mode==3) {stackhorizontal(source.turnright().histogram().turnleft().subtitle(" source"), \OOG.subtitle(text).addborders(0,256,0,0))} else {stackhorizontal(source.histogram("color2").subtitle(" source"), \OOG.subtitle(text))} function ShowBadRGB(clip vid, int "color", string "cs1", int "thr_high",int "thr_low",bool "interlaced", string "resample_filter") { cs1=default(cs1,"601:auto:auto:limited=>rgb:same:same:full") thr_high=default(thr_high,984) #10bit thr_low=default(thr_low,20) #10bit badcolor = BlankClip(vid, color=color) interlaced=default(interlaced,false) resample_filter=default(resample_filter,"bicubic") rgb=vid.z_ConvertFormat(pixel_type="RGBP10",interlaced=interlaced,colorspace_op=cs1,resample_filter=resample_filter,use_props=0) downshift_high=rgb.RGBadjust(rb=-thr_high+1,gb=-thr_high+1,bb=-thr_high+1) amp_high=downshift_high.levels(0,1,1023-thr_high,0,1023) #10bit upshift=rgb.RGBadjust(rb=(1023-thr_low-1),gb=(1023-thr_low-1),bb=(1023-thr_low-1)) downshift_low=upshift.RGBadjust(rb=-(1023-thr_low-1),gb=-(1023-thr_low-1),bb=-(1023-thr_low-1)) amp_low=downshift_low.levels(0,1,thr_low,0,1023) overlay(amp_high,amp_low,opacity=0.5,mode="blend") mask_high=z_ConvertFormat(pixel_type="YUV444P10",interlaced=interlaced,use_props=0).mt_binarize(upper=false) mask_low=invert().z_ConvertFormat(pixel_type="YUV444P10",interlaced=interlaced,use_props=0).mt_binarize(upper=false) mask=overlay(mask_high,mask_low,opacity=0.5).colorYUV(gain_y=10000) overlay(vid.grayscale(),badcolor,0,0,mask) avg=mask.ScriptClip("Subtitle(String(AverageLuma/1024*100),align=9)").crop(width()-90,0,-0,-(height()-20)).addborders(0,0,0,height()-20) stackhorizontal(last,avg) } Last edited by Sharc; 14th January 2025 at 11:04. Reason: v2.0 |
|
Tags |
colorspace, colorspace conversion, ffmpeg |
Thread Tools | Search this Thread |
Display Modes | |
|
|