View Full Version : zscale WEBP Error
Sharc
24th December 2024, 09:56
Ah, mapping to limited range RGB is missing there. Some food for thought. Thanks.
Hmmm..., if I understand this correctly from
https://avisynthplus.readthedocs.io/en/latest/avisynthdoc/advancedtopics/color_conversions.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.
poisondeathray
24th December 2024, 16:18
Hmmm..., if I understand this correctly from
https://avisynthplus.readthedocs.io/en/latest/avisynthdoc/advancedtopics/color_conversions.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.
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
But Avisynth resizer is not capable to chain bit depth conversion with rgb->yuv conversion so I'd say it'll never give such losslessness.
Sharc
25th December 2024, 09:53
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
Got it, thanks. So these are the matrices (equations) as defined in ITU-R BT.601-7 and ITU-R BT.709-6 recommendations.
***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
Thanks for the hints. Will try with avsresize.
poisondeathray
26th December 2024, 16:35
Got it, thanks. So these are the matrices (equations) as defined in ITU-R BT.601-7 and ITU-R BT.709-6 recommendations.
Yes, derived from the ITU BT.601/709 documents, expressed as equations for the "YUV <=> studio RGB (limited range RGB)" scenario ( and "YUV <=> computer RGB (full range RGB)" scenario)
Sharc
29th December 2024, 17:53
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 (https://mega.nz/file/DddgnD6b#TRsSenwRzYKr-dN6OaDraIKoywSCEmQULtWCokkZxjw) for limited range RGB ("studio") scenario.
######################################################################################
# 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)
}
poisondeathray
30th December 2024, 16:21
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 ?
Sharc
31st December 2024, 14:02
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.
Sharc
9th January 2025, 00:01
Nice sharc
Some suggestions - consider adding :
- adjustable "bad" RGB range, or a specific r103 broadcast scenario - .
- % of "bad RGB/out of gamut" pixels estimation.
Here a new version with a function added for adjustable bad RGB range, also supporting an EBU r103 scenario. Color pixels indicate out-of-gamut RGB for the various scenarios.
################################################################################
# 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)
}
vBulletin® v3.8.11, Copyright ©2000-2026, vBulletin Solutions Inc.