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. |
23rd January 2018, 19:57 | #1 | Link |
Registered User
Join Date: Nov 2004
Location: Poland
Posts: 2,843
|
Out of gamut detection
Anyone willing to write something to detect out of gamut (with some threshold) script? I may be able to pay some money for it.
This is the spec which it should follow: https://tech.ebu.ch/docs/r/r103.pdf Video would be piped through ffmpeg I just need some indication if file has passed or not check. |
23rd January 2018, 21:42 | #2 | Link |
Registered User
Join Date: Nov 2004
Location: Poland
Posts: 2,843
|
Hmm...looks to me like it's exactly what I want.
I know bit of Python itself, but struggle link vs with it. Understand 2nd half to the script. How do I check every frame while it's processed? Or do I need script to process all frames and then check result in stored values? |
24th January 2018, 00:45 | #3 | Link |
Registered User
Join Date: Dec 2005
Location: Germany
Posts: 1,795
|
Use FrameEval to check on each frame http://www.vapoursynth.com/doc/functions/frameeval.html
__________________
AVSRepoGUI // VSRepoGUI - Package Manager for AviSynth // VapourSynth VapourSynth Portable FATPACK || VapourSynth Database |
24th January 2018, 10:31 | #4 | Link |
Registered User
Join Date: Nov 2004
Location: Poland
Posts: 2,843
|
Thank you.
I've looked at vs engine's doc yesterday and I was close, very close. Myrsloik said that vspipe is better approach and should be faster. Now everything makes perfect sense, except c = core.std.Convolution(c, matrix=[1, 2, 3, 4, 3, 2, 1], mode='h') c = core.std.Convolution(c, matrix=[1, 2, 1], mode='v') which I assume is low pass filtering. Looking at numbers I get them, but I don't fully understand it. What if file is interlaced? We need to process each filed separately? Where is Rec.709 or Rec.601 here which I assumed needs to be specified when you go from YUV back to RGB? Last edited by kolak; 24th January 2018 at 12:00. |
24th January 2018, 11:40 | #5 | Link |
Registered User
Join Date: Nov 2004
Location: Poland
Posts: 2,843
|
Script itself works, but actual out of gamut detection doesn't.
Where is RGB here as when I do: f.format it shows me YUV? There has to be somewhere place where we convert it to RGB and then check if RGB planes are out of range. Gamut errors are detected when YUV is converted back to RGB and creates illegal RGB values. Last edited by kolak; 24th January 2018 at 12:02. |
24th January 2018, 11:52 | #6 | Link |
Registered User
Join Date: Nov 2004
Location: Poland
Posts: 2,843
|
After converting to RGB now it seems to work.
Should I convert it before or after low pass filtering? Is simple: c = core.resize.Bicubic(clip=c,format=vs.RGB30) good enough or should I use something better? ( this is for 10bit video). I also realised that I have to check each RGB plane separately. I've made files which have issues only on R, G or B channel only and it seems to be detecting it fine. Now I have to simulate below and above 1% frame coverage. |
24th January 2018, 17:24 | #7 | Link |
Registered User
Join Date: Nov 2004
Location: Poland
Posts: 2,843
|
I've verified that all seems to be working fine for 8bit.
For 10bit there may be an issue as reported max plane values are 32768, not like expected e.g. 768 (some strange scaling is happening- I assume to 16bit?). My test file with 920x80 pixels bad area did produce perfect score of 3.549% of bad pixels for HD frame. With low pass filtering it's 3.493% which suggest it's all working well (I expected bit lower value). Last edited by kolak; 24th January 2018 at 17:30. |
24th January 2018, 17:30 | #8 | Link | |
Registered User
Join Date: Nov 2004
Location: Poland
Posts: 2,843
|
Quote:
File "src\cython\vapoursynth.pyx", line 528, in vapoursynth.typedDictToMap ValueError: invalid literal for int() with base 10: 'limited' Looks like a bug. Last edited by kolak; 25th January 2018 at 00:12. |
|
24th January 2018, 23:16 | #9 | Link |
Registered User
Join Date: Nov 2004
Location: Poland
Posts: 2,843
|
It was PlaneStatMax when I used RGB30 in resize, but without std.Expr stage. I was checking what values are provided by vs. I was expecting 10bit levels (e.g. 64-940), but saw things like 32768.
I was using old vs, so will try again with current one. My other note is that: std.Expr(c, 'x 5 < x 246 > or 255 0 ?') and PlaneStats operate on integers? This means precision is lost a bit. |
24th January 2018, 23:57 | #11 | Link |
Registered User
Join Date: Nov 2004
Location: Poland
Posts: 2,843
|
Quite interesting.
Even ProRes files with bars can show as low as e.g. 14 values where you would expect 64. Then if you turn on filtering this is not anymore detected and lowest values is e.g. 56. I was never expecting ProRes to affect simple things like bars that much In the same time YUV values are almost perfect (some 60 values can be found for 10bit). This suggests that issue for RGB is at bars edges and it's due to chroma up sampling during conversion to RGB (at least in my opinion). It also shows that low pass filtering does well its role Last edited by kolak; 25th January 2018 at 00:05. |
25th January 2018, 11:46 | #13 | Link | |
Registered User
Join Date: Nov 2004
Location: Poland
Posts: 2,843
|
Quote:
For example my file has area which R is coming at eg. 8.56. Looks like I can't have: std.Expr(c, 'x 8.57 < x 246 > or 255 0 ?') it's either x < 8 or x <9 I understand logic behind and I like this logic Last edited by kolak; 25th January 2018 at 12:09. |
|
25th January 2018, 13:46 | #14 | Link |
Registered User
Join Date: Nov 2004
Location: Poland
Posts: 2,843
|
Another question.
This R103 check requires RGB to be in specified values, but also Y out of YUV signal. Can we do both checks in one go (RGB and original Y)? How can we pass Y into checking function? Looks like this has to be 2nd pass, no? Also, where low pass filtering should happen: on YUY or RGB? ( I would say YUV) Last edited by kolak; 25th January 2018 at 13:57. |
25th January 2018, 14:07 | #15 | Link |
Professional Code Monkey
Join Date: Jun 2003
Location: Kinnarps Chair
Posts: 2,548
|
It's so vague about things. Is it talking about limited range rgb as well? I know you've mentioned other products that check these things and throwing carefully constructed samples at once of those solutions may be the only way we'll ever find out...
__________________
VapourSynth - proving that scripting languages and video processing isn't dead yet |
25th January 2018, 14:19 | #16 | Link |
Registered User
Join Date: Nov 2004
Location: Poland
Posts: 2,843
|
It's not end of the world as difference between low pass filtered on YUV v RGB is very small.
I assume it's limited range as otherwise it would not make sense? How can I check original Y (if it's in limits) during the same check as RGB? Last edited by kolak; 25th January 2018 at 14:21. |
25th January 2018, 14:20 | #17 | Link |
Professional Code Monkey
Join Date: Jun 2003
Location: Kinnarps Chair
Posts: 2,548
|
Post the script so far and I'll show you, it's a quite simple addition...
__________________
VapourSynth - proving that scripting languages and video processing isn't dead yet |
25th January 2018, 14:25 | #18 | Link |
Registered User
Join Date: Nov 2004
Location: Poland
Posts: 2,843
|
Code:
def check_gamut(n, f, c): if f.props['PlaneStatsRAverage'] > 0.01 or f.props['PlaneStatsGAverage'] > 0.01 or f.props['PlaneStatsBAverage'] > 0.01: print("Bad frame: "+str(n)) exit() return c c = core.ffms2.Source() c = core.std.Convolution(c, matrix=[1, 2, 3, 4, 3, 2, 1], mode='h') c = core.std.Convolution(c, matrix=[1, 2, 1], mode='v') c = core.resize.Bicubic(c, format=vs.RGB30, range_s="limited") c = core.std.Expr(c, 'x 20 < x 984 > or 1023 0 ?') c = core.std.PlaneStats(c, plane=0, prop='PlaneStatsR') c = core.std.PlaneStats(c, plane=1, prop='PlaneStatsG') c = core.std.PlaneStats(c, plane=2, prop='PlaneStatsB') c = core.std.FrameEval(c, functools.partial(check_gamut, c=c), prop_src=c) c.set_output() |
25th January 2018, 14:25 | #19 | Link |
Professional Code Monkey
Join Date: Jun 2003
Location: Kinnarps Chair
Posts: 2,548
|
Basically the idea is to convert the input to yuv/rgb (opposite of what it is).
Then run the original and opposite format clips through the appropriate checks. Use stackhorizontal last to make both branches of the script be fetched. Done. Probably.
__________________
VapourSynth - proving that scripting languages and video processing isn't dead yet |
25th January 2018, 14:28 | #20 | Link |
Registered User
Join Date: Nov 2004
Location: Poland
Posts: 2,843
|
I need to somehow do this:
Code:
c = core.ffms2.Source() c = core.std.Convolution(c, matrix=[1, 2, 3, 4, 3, 2, 1], mode='h') c = core.std.Convolution(c, matrix=[1, 2, 1], mode='v') ... y=c y = core.std.Expr(y, 'x 20 < x 984 > or 1023 0 ?') y = core.std.PlaneStats(y, plane=0, prop='PlaneStatsY') c = core.std.FrameEval(c, functools.partial(check_gamut, c=c), prop_src=c) c.set_output() I need this: c = core.std.FrameEval(c, functools.partial(check_gamut, c=c, y=y), prop_src=[c,y]) Last edited by kolak; 25th January 2018 at 14:31. |
Thread Tools | Search this Thread |
Display Modes | |
|
|