VideoHelp Forum
+ Reply to Thread
Page 1 of 2
1 2 LastLast
Results 1 to 30 of 44
Thread
  1. Hi everyone!

    We had a brief talk about it, but we never talked about the cause why. Using aviSynth Level() will effect the Black/White levels based on the parameters you use, but also raise the chroma values. I used Level() often, and when the results has blown out colors, I merged back the original chroma. But that makes me wonder. Why does Level(), which expect white/black levels, also effect the chroma? Is that actually a good practice to modify the Chroma values when you change the Luma? When I use level() and the colors are not blown out, I wonder if It's better to merge the original Chroma, or stick with the new Chroma values Level() modified.

    Thanks!
    Quote Quote  
  2. Does it really change the chroma? Any change in levels will tend to change the saturation and intensity of the colors, which can also make them appear to shift.
    Quote Quote  
  3. Perhaps I missed used the word "Chroma" (I used it because I return the color level to the default using "MergeChrome()" method). According to jagabo, Level() also increases saturation.
    Quote Quote  
  4. It's 'Levels()'. What happens when you do this?

    Levels(Coring=False)
    Quote Quote  
  5. Levels() increases saturation when you increase the contrast. It reduces the saturation when you reduce the contrast. Ie, it changes the levels of the U and V channels as well as the Y channel.

    http://avisynth.nl/index.php/Levels

    For adjusting brightness or contrast in YUV mode, it may be better (depending on the effect you are looking for) to use Tweak or ColorYUV, because Levels changes the chroma of the clip.
    Last edited by jagabo; 3rd Nov 2020 at 13:38.
    Quote Quote  
  6. It depends on what you are doing

    There is YLevels if you want Y affected only
    http://avisynth.nl/index.php/Ylevels
    Quote Quote  
  7. So that's what I wonder about. Is there a reason why Levels() adjust the saturation? should Saturation be normally increased when increasing the contrast? How can one know when to use Levels() (with Saturation change) vs when to use YLevels/Tweak? is there any rule of a thumb?
    Quote Quote  
  8. There is no "rule of thumb",

    It depends on your source, and what you want to do, and how you want to do it

    And often there is more than one way to get a similar end result

    People have different approaches to performing color manipulations. eg. Some prefer to work in RGB, hue/saturation curves, RGB levels and curves.

    Even "saturation" has slightly different definitions and ways of calculating and adjusting it.

    Classically, in RGB , increasing contrast increases the saturation. (e.g If you were to check a waveform monitor and vectorscope)
    Quote Quote  
  9. Levels() was meant to emulate VirtualDub's Levels filter working in RGB mode. The transform that calculated from the values given to Levels() is applied equally to each RGB channel. The result is that the U and V channels change along with the Y channel. Here's an AviSynth script that shows this:

    Code:
    function ChangeLevels(clip v, float mult)
    {
        Levels(v, 0,1.0,255, 0,int(255.0*mult))
    }
    
    BlankClip(color=$808020, pixel_type="RGB32, frames=100")
    
    Animate(0,100,"ChangeLevels", last,1.0, last,2.0)
    ConvertToYV24().Histogram(mode="color")
    You can see the UV dot move away from the center as you increase the contrast. And you can see that the RGB values change linearly too. 128,128,32 becomes approximately 255,255,64 (ie, all multiplied by ~2)
    Quote Quote  
  10. Classically, in RGB , increasing contrast increases the saturation. (e.g If you were to check a waveform monitor and vectorscope)
    Levels() was meant to emulate VirtualDub's Levels filter working in RGB mode.
    Ha! OK. So that's the missing link. Classically, increasing the contrast also increased saturation, and Level() just emulate what VirtualDub do.

    So when I see the RGB is blown out after Levels(), I use MergeChroma to apply the original Chroma. If it's not - I leave with the RGB values Levels(). But I wonder - how does one properly calibrate colors? Is it even possible to do it without calibrated screen?
    Quote Quote  
  11. Originally Posted by Okiba View Post
    how does one properly calibrate colors? Is it even possible to do it without calibrated screen?
    You don't necessarily need a perfectly calibrated screen. But being close helps a lot. Checking colors visually is somewhat subjective. Even with a perfectly calibrated monitor the brightness and color temperature of the room lights can make colors look different. Or staring at say, a blue screen for a while, then switching to a greyscale image will make those greys look non-grey. You can read RGB values off the screen with a program like CSamp:

    https://forum.videohelp.com/threads/361379-VCR-Hi8-capture-tests-help-evaluate/page7#post2303973

    Unless your video is a color chart it's hard to know exactly what the colors are supposed to be. Full black and white are a place to start. If you have something you know is medium grey that gets you some of the middle shades.

    You can use a tool to look for illegal YUV combinations:

    http://forum.doom9.org/showthread.php?t=154731

    That function is very slow to start up -- it used to take about a minute on my old i5 2500K. So I wrote a faster and simpler function that's a little less accurate though usually adequate, HighlightBadRGB():

    https://forum.videohelp.com/threads/360935-Capturing-Correct-Chroma-and-Hue-Levels-Fro...gb#post2289672

    Reading through the rest of that thread may help.
    Quote Quote  
  12. Originally Posted by Okiba View Post
    But I wonder - how does one properly calibrate colors? Is it even possible to do it without calibrated screen?
    I am no expert, so just some thoughts:

    One can calibrate a monitor with the help of a colorimeter AFAIK. It may not be required though.
    Basic monitor adjustments are normally done with tools like
    http://www.lagom.nl/lcd-test/

    A monitor or TV will display the colors according to its settings, and these settings are normally adjusted for a subjectively pleasant or 'natural' view.
    Test pictures (like colorbars or more advanced test patterns) can help with the basic adjustments.

    Even with a calibrated monitor it is however not possible to reversely tune a video source reliably and absolutely IMO. Where in the natural picture is the 'standard red' for example? Does it exist at all in the filmed object or in the video?

    The Avisynth color tools (levels(), colorYUV(), Tweak()) help to set the color data 'technically correct' i.e. for complying with a specification (range, gamut ...), for example. They are however not very intuitive, and to obtain a desired correction can be tiresome. For advanced color management one would have to resort to specialized tools/GUIs - and your (calibrated) monitor + your eyes at the end.

    In addition to the avisynth wiki documentation you may find these links useful:

    http://poynton.ca/notes/colour_and_gamma/
    http://www.poynton.com/notes/brightness_and_contrast/index.html
    https://worqx.com/color/index.htm
    http://handprint.com/HP/WCL/color18a.html
    Quote Quote  
  13. Thank you both. I will be reading all the links you shared later on, but reading between the lines It sounds like it's pretty hard to adjust colors - and most people without profession gear or a lot of experience will adjust them subjectively by "that's looks better to me"?

    As you remember, my source is VHS tapes. A workflow that sounds reasonable to me is use `Level()`to set contract/brights and check the color histogram afterwards. If no color are getting blown, keep the saturation Level() has picked (what was called "technically correct"). If any of the colors are blown out out, use MergeChroma to restore the original Chroma before Level() were applied. That's sound like a valid mid-way?

    Thanks!
    Quote Quote  
  14. Originally Posted by Okiba View Post
    As you remember, my source is VHS tapes. A workflow that sounds reasonable to me is use `Level()`to set contract/brights and check the color histogram afterwards. If no color are getting blown, keep the saturation Level() has picked (what was called "technically correct"). If any of the colors are blown out out, use MergeChroma to restore the original Chroma before Level() were applied. That's sound like a valid mid-way?
    You may still need to follow up with other color adjustments. And it may not be necessary to merge the original chroma back, depending on what other adjustments you are making. And even keeping the original chroma may lead to illegal colors.

    Look again at the image of the RGB cube within the YUV cube:

    Image
    [Attachment 55735 - Click to enlarge]


    Remember the inner cube is all the legal RGB values, the outer cube is all possible YUV combinations. Only the YUV values inside the RGB cube result in valid RGB values. Say you have a pixel that's very near the cyan corner of the RGB cube. If you raise the Y value, even without change the U and V values, it will move outside the RGB cube, producing an illegal color.
    Quote Quote  
  15. FWIW I have been using this script based on avisynth's 'Limiter()' for testing legal values.
    The grey small images on the right get colored when U,V are out of range. Color codes according to the Limiter() documentation.
    Code:
    AVISource("sample.avi")
    tweak(hue=0,sat=1.5,bright=50,cont=1,coring=false)
    
    histomode="levels" #classic, levels, color, color2
    converttoYV12()
    width=width()
    height=height()
    checkLevels=colorYUV(analyze=true).Histogram(histomode).subtitle("source")  #Note: The histogram includes the text of the 'analyze'
    checkLuma=Limiter(16,235,16,240,show="luma_grey").bilinearresize(width/2,height/2).subtitle("limit check luma")
    checkChroma=Limiter(16,235,16,240,show="chroma_grey").bilinearresize(width/2,height/2).subtitle("limit check chroma")
    stackhorizontal(checkLevels,stackvertical(checkLuma,checkChroma))
    return last
    Edit: Ooops, I just read jagabo's post. I think the script does not consider the inner RGB block .... so illegal RGB values are still possible. i.e. not getting alarmed......
    Last edited by Sharc; 4th Nov 2020 at 10:06.
    Quote Quote  
  16. Originally Posted by Sharc View Post
    Edit: Ooops, I just read jagabo's post. I think the script does not consider the inner RGB block .... so illegal RGB values are still possible. i.e. not getting alarmed......
    Yes, Limter doesn't help with colors outside the inner block. It just limits YUV values at the outer edges of the outer block.
    Quote Quote  
  17. Right. I remember that one. Even have that picture saved

    So yes, even if the original chroma might lead to illegal colors. So it sounds the choices boils down to that:

    - Apply Levels(), check chroma values are legal.
    - If legal, leave it at that (even if those are not 'precise'. But at least technically right. Until I can get the proper eye to tune it manually).
    - If after Level() chroma values are still illegal, Merge the original chroma values.
    - Check the values again. If they are not legal with the original chroma values, It mean more advanced tuning is needed.

    So the two open questions are:
    1. How can I check if Chroma values are legal. Checking the Color Histogram?
    2. If the original Chroma values are wrong - what's better - Using Level to tune Contrast/Brightness and accept chroma is illegal, or skip using Level() all together (hoping that original capture chroma values are legal).
    Quote Quote  
  18. Originally Posted by Okiba View Post
    So the two open questions are:
    1. How can I check if Chroma values are legal. Checking the Color Histogram?
    Use jagabo's script. See his last link in post #11:
    https://forum.videohelp.com/threads/360935-Capturing-Correct-Chroma-and-Hue-Levels-Fro...gb#post2289672
    2. If the original Chroma values are wrong - what's better - Using Level to tune Contrast/Brightness and accept chroma is illegal, or skip using Level() all together (hoping that original capture chroma values are legal).
    I think it depends on the severity and specifics of the violation. It may be difficult to make everything RGB-legal (inner cube). I would perhaps make sure that Y, Cb(U) and Cr(V) fit in the outer cube (TV range) without clipping (except noise and artefact spikes, which you may want to 'remove' by blurring the picture for the color analysis). As long as you don't clip Y,Cb,Cr you can always restore the original colors, even when they represent illegal RGB values.
    But sometimes less is more
    Last edited by Sharc; 4th Nov 2020 at 13:52.
    Quote Quote  
  19. Use jagabo's script.
    Oh, great! Thanks!

    I would perhaps make sure that Y, Cb(U) and Cr(V) fit in the outer cube (TV range) without clipping
    jagabo script highlight the clipping RGB values right (inner cube)? is there a way to know the outer cube clips?
    Quote Quote  
  20. Originally Posted by Okiba View Post
    jagabo script highlight the clipping RGB values right (inner cube)? is there a way to know the outer cube clips?
    You can use the script which I posted in post #15.
    You could also combine the 2 scripts and have everything under one umbrella.

    Normally you will find the Cb(U), Cr(V) values to be well within the limits (0.... 240) leaving quite some headroom for tweaking, so you shouldn't have to worry much - unless something went wrong with the VHS capturing process.
    Last edited by Sharc; 4th Nov 2020 at 16:10.
    Quote Quote  
  21. Sounds good. I will start adding that to my workflow. Adjust Level() based on the histogram and checking illegal values with your script afterwards. If three are, I will modify the Level() values in small steps - under most/all of the colors are legal.

    Great! Thank again everyone!
    Quote Quote  
  22. So I felt free to add jagabo's RGB check to the script. It shows the (tweaked) source and the outer (Y,Cb,Cr) and inner RGB limits side by side.
    I hope the script is valid and has no basic flaws....
    You may want to try it with your VHS sources, and apply tweaks (section 2) until everything fits within the limits. Good luck!
    Code:
    #------------------------------------
    #     COLOR ANALYSIS
    #------------------------------------
    
    # SOURCE
    AVISource("your.avi")
    
    # 1) PRE-PROCESSING, as required
    converttoYV16(interlaced=true,matrix="Rec601")
    assumeTFF().separatefields().selectodd()
    crop(16,8,-16,-16)  #remove any borders and crud
    
    # 2) COLOR TWEAK AND FILTER OPTIONS
    #Levels(32,1.3,200,36,200,coring=false)  #Levels w/o chroma copyback
    #mergechroma(Levels(0,1.0,255,0,240,coring=false),last)   #Levels with Chroma copyback
    Tweak(hue=15,sat=0.8,bright=6,cont=0.86,coring=false)  
    #colorYUV(gain_y=0,off_y=0,cont_y=0,gain_u=0,off_u=0,cont_u=0,gain_v=0,off_v=-0,cont_v=0,f2c=false)
    
    # more filters .....
    
    # 3) ANALYZE
    histomode="levels"  #levels, classic, color, color2
    converttoYV12()
    width=width()
    height=height()
    checkLevels=colorYUV(analyze=true).Histogram(histomode).subtitle("source (filtered)")  #Note: The histogram includes the analyze text
    checkLuma=Limiter(16,235,16,240,show="luma").bilinearresize(width/2,height/2).subtitle("highlight illegal luma Y")
    checkChroma=Limiter(16,235,16,240,show="chroma").bilinearresize(width/2,height/2).subtitle("highlight illegal chroma Cb,Cr")
    IllegalRGB=HighlightBadRGB(color_red)
    
    # 4) LAYOUT
    limit=stackhorizontal(checkLevels,stackvertical(checkLuma,checkChroma),IllegalRGB.horizontalreduceby2().subtitle("highlight illegal RGB"))
    return limit
    
    
    #---------------- FUNCTION  © jagabo@videohelp  -------------------------------------------------------------------------------
    # https://forum.videohelp.com/threads/360935-Capturing-Correct-Chroma-and-Hue-Levels-From-VHS/page2?highlight=highlightbadrgb#post2289672
    function HighlightBadRGB(clip vid, int "color")
    {
      color = default(color, $ff0000)
    
      badcolor = BlankClip(vid, color=color)
      Subtract(ConvertToYV24(vid), ConvertToYV24(vid).ConvertToRGB().ConvertToYV24())
      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")
      ColorYUV(gain_y=65000)
      Overlay(vid,badcolor,0,0,last)
    }
    Quote Quote  
  23. Whoo, awesome! Thanks!
    One thing though, why Tweak? and colorYuv? only to show other ways to modify brightness/contract? (I know tweak won't touch the chroma).
    Quote Quote  
  24. Originally Posted by Okiba View Post
    Whoo, awesome! Thanks!
    One thing though, why Tweak? and colorYuv? only to show other ways to modify brightness/contract? (I know tweak won't touch the chroma).
    Yes, simply to list the usual options (proposals you can select from for doing your adjustments; normally just use one of these).
    Quote Quote  
  25. Great! I create a function out of your code, so now I have one for Inner verification and one for outer
    I wonder though, what does the "Limit Check Luma" does? and what's the middle histogram is good for?
    Quote Quote  
  26. Originally Posted by Okiba View Post
    I wonder though, what does the "Limit Check Luma" does?
    What are you referring to? The Y (luma) has to be checked for being in the legal range for the outer cube as well, not only the Chroma (Cb,Cr)

    and what's the middle histogram is good for?
    Well, it provides additional info, e.g. to for adjusting the levels() or other color tweaks. What is shown there is selectable in the script under section 3) as histomode="xxxx". Set xxxx="classic" or "levels" or "color" or "color2". Just keep im mind what is shown there includes the text in the basic picture on the left.
    Quote Quote  
  27. The Y (luma) has to be checked for being in the legal range for the outer cube as well
    Oh, I see. I already verify that when I set Level().

    Thank you!
    Quote Quote  
  28. looks its settled in this thread, so I post Vapoursynth port of script from post #22,

    that shows filtered video, illegal Y in green, illegal chroma in yellow and bad RGB in red
    Code:
    import vapoursynth as vs
    from vapoursynth import core
    import adjust                  #if using Tweak filter, google "github vapoursynth adjust.py" by dubhater
    import functools               #standard python library, comes with python
    #for histogram: download dll from github, search "github vapoursynth histogram" by dubhater
    
    clip = core.avisource.AVISource("some.avi")   #YUV420P8
    
    def print_stats(clip):
        def stats(n, clip):
            f=clip.get_frame(n)
            return clip.text.Text(
               f"Frame {n}\nPlanes   Y:      U:      V:\n"
                "Average  {:.2f}{:8.2f}{:8.2f}\n"
                "Minimum  {: <8}{: <8}{: <8}\n"
                "Maximum  {: <8}{: <8}{: <8}".format(f.props["STATS_Y_Average"]*255 ,f.props["STATS_U_Average"]*255, f.props["STATS_V_Average"]*255,
                                                     f.props["STATS_Y_Min"],         f.props["STATS_U_Min"],         f.props["STATS_V_Min"],
                                                     f.props["STATS_Y_Max"],         f.props["STATS_U_Max"],         f.props["STATS_V_Max"])
                                  )
        return core.std.FrameEval(clip, functools.partial(stats, clip=clip))
    
    def HighlightBadRGB(vid, color=(81,90,240)):
        badcolor = core.std.BlankClip(vid, color=color)
        YUV444P8 = vid.resize.Point(format=vs.YUV444P8)
        subtract = core.std.MakeDiff(YUV444P8, YUV444P8.resize.Point(format=vs.RGB24, matrix_in_s='170m').resize.Point(format=vs.YUV444P8, matrix_s='170m'))
        #value for mask in python syntax: value = 0 if (x==128 and y==128 and z==128) else 255
        mask = core.std.Expr([subtract.std.ShufflePlanes(plane, vs.GRAY) for plane in [0,1,2]], ['x 128 = y 128 = and z 128 = and 0 255 ?'])
        return core.std.MaskedMerge(vid, badcolor, mask)
    
    
    # 1) PRE-PROCESSING
    clip = core.std.SetFrameProp(clip, prop="_Matrix", intval=6)    #6 or 5 (170m or 470bg, in case of Matrix, it is the same, bt601)
    
    #2) FILTER OPTIONS in floating point
    YUV444PS = core.resize.Bicubic(clip, format=vs.YUV444PS)
    #YUV444PS = adjust.Tweak(YUV444PS, hue=15,sat=0.8,bright=6/255,cont=0.86,coring=False) 
    
    # 3) ANALYZE
    histomode = "Levels"  #Classic, Levels, Color, Color2
    YUV420P8 = YUV444PS.resize.Bicubic(format=vs.YUV420P8)
    YUV420P8 = YUV420P8.std.PlaneStats(plane=0, prop="STATS_Y_").std.PlaneStats(plane=1, prop="STATS_U_").std.PlaneStats(plane=2, prop="STATS_V_")
    #histogram_clip = core.hist.Levels(YUV420P8)
    histogram_clip = getattr(core.hist, histomode)(YUV420P8).text.Text("source (filtered)", alignment=1)   
    checkLevels = print_stats(histogram_clip)
    
    illegal_luma_mask  = core.std.Expr(YUV420P8.std.ShufflePlanes(0, vs.GRAY), 'x 16 < x 235 > or 255 0 ?')
    illegal_luma_color = core.std.BlankClip(YUV420P8, color=(145,54,34))  #YUV green
    checkLuma = core.std.MaskedMerge(YUV420P8, illegal_luma_color, illegal_luma_mask).resize.Bilinear(width=YUV420P8.width/2, height=YUV420P8.height/2).text.Text("highlight illegal luma Y (green)")
    
    illegal_chroma_mask  = core.std.Expr([YUV420P8.std.ShufflePlanes(plane, vs.GRAY) for plane in [1,2]], 'x 16 < x 240 > or y 16 < or y 240 > or 255 0 ?')
    illegal_chroma_color = core.std.BlankClip(YUV420P8, width=YUV420P8.width/2, height=YUV420P8.height/2, color=(210,16,146))  #YUV yellow
    checkChroma = core.std.MaskedMerge(YUV420P8.resize.Bilinear(width=YUV420P8.width/2, height=YUV420P8.height/2), illegal_chroma_color, illegal_chroma_mask).text.Text("highlight illegal chroma Cb,Cr (yellow)")
    
    IllegalRGB=HighlightBadRGB(YUV420P8, color=(81,90,240)) #YUV red (81,90,240)
    
    # 4) LAYOUT
    limit = core.std.StackHorizontal([checkLevels, core.std.StackVertical([checkLuma,checkChroma]),IllegalRGB.text.Text("highlight illegal RGB (red)")])
    limit.set_output()
    Vapoursynth does not have ColorYUV, Overlay, etc., so it is done with Expressions, which is not bad sometimes, because you can see exactly what it does.
    Avisynths, colorYUV(...., analyze=true) was done with created print function in vapoursynth.
    Expressions seemed to make it easier to figure out illegal RGB color together with MakeDiff() which might be Avisynths Subtract() equivalent.
    Also Avisynths Limiter(...., show="luma" or show="chroma") was done by extracting planes and using expressions getting masks and then merging them into video using particular color.
    Last edited by _Al_; 8th Nov 2020 at 00:56.
    Quote Quote  
  29. Screen comparisons avisynth and vapoursynth:

    avisynth seams to load video with wrong colors, not sure why, but not using avs so not important, maybe something in the menu of AvsPMod.
    Image Attached Thumbnails Click image for larger version

Name:	analyze_in_avisynth.png
Views:	96
Size:	995.3 KB
ID:	55768  

    Click image for larger version

Name:	analyze_in_vapoursynth.png
Views:	68
Size:	902.9 KB
ID:	55769  

    Quote Quote  
  30. Originally Posted by _Al_ View Post
    avisynth seams to load video with wrong colors
    No, AviSynth loads the video with whatever YUV values are in the video. Your problem here is that when those YUV values are displayed they are using the wrong matrix to convert to RGB, rec.601 vs. rec.709. If you want to see the correct RGB values from AviSynth use ConvertToRGB(matrix="rec601"), or ConvertToRGB(matrix="rec709") whichever is appropriate.

    The HighlightBadRGB() I posted assumes limited range rec.601 colors.
    Quote Quote  



Similar Threads

Visit our sponsor! Try DVDFab and backup Blu-rays!