EleonoreMizo / fmtconv

Format conversion tools for Vapoursynth and Avisynth+
Do What The F*ck You Want To Public License
67 stars 14 forks source link

bitdepth() does not set _ColorRange property after conversion #20

Closed webghost009 closed 2 years ago

webghost009 commented 3 years ago

I noticed that when a color range conversion is performed using bitdepth() it does not set the _ColorRange property accordingly. matrix() however does.

Here is a small repro:

import vapoursynth as vs
from vapoursynth import core

def TestColorRange(clip, title, expected, method):
    if clip.get_frame(0).props['_ColorRange'] == expected:
        color = (149 << 8, 43 << 8, 21 << 8) # Green
    else:
        color = (76 << 8, 84 << 8, 255 << 8) # Red

    clip = core.std.Expr(clip, expr=color, format=vs.YUV444P16)
    clip = clip.text.FrameProps(props="_ColorRange")
    return clip.text.Text("%s\nExpected _ColorRange == %d\n%s" % (title, expected, method), alignment=2)

c1 = core.std.BlankClip(width=320, height=240, format=vs.RGBS)
c1 = c1.std.SetFrameProp(prop="_ColorRange", intval=0)
c1 = c1.fmtc.matrix(mats="rgb", matd="709", col_fam=vs.YUV)
c1 = c1.fmtc.bitdepth(bits=16, fulls=True, fulld=False)
c1 = TestColorRange(c1, "c1", 1, "bitdepth(fulls=True, fulld=False)")

c2 = core.std.BlankClip(width=320, height=240, format=vs.RGBS)
c2 = c2.std.SetFrameProp(prop="_ColorRange", intval=0)
c2 = c2.fmtc.matrix(mats="rgb", matd="709", fulls=True, fulld=False, col_fam=vs.YUV)
c2 = c2.fmtc.bitdepth(bits=16)
c2 = TestColorRange(c2, "c2", 1, "matrix(fulls=True, fulld=False)")

c3 = core.std.BlankClip(width=320, height=240, format=vs.YUV444P8)
c3 = c3.std.SetFrameProp(prop="_ColorRange", intval=1)
c3 = c3.fmtc.matrix(mats="601", matd="709")
c3 = c3.fmtc.bitdepth(bits=16, fulls=False, fulld=True)
c3 = TestColorRange(c3, "c3", 0, "bitdepth(fulls=False, fulld=True)")

c4 = core.std.BlankClip(width=320, height=240, format=vs.YUV444P8)
c4 = c4.std.SetFrameProp(prop="_ColorRange", intval=1)
c4 = c4.fmtc.matrix(mats="601", matd="709", fulls=False, fulld=True)
c4 = c4.fmtc.bitdepth(bits=16)
c4 = TestColorRange(c4, "c4", 0, "matrix(fulls=False, fulld=True)")

c1and2 = core.std.StackHorizontal(clips=[c1, c2])
c3and4 = core.std.StackHorizontal(clips=[c3, c4])
oc = core.std.StackVertical(clips=[c1and2, c3and4])
oc.set_output()

Which results in the following output. Red squares indicate an unexpected value for _ColorRange while green squares indicate expected values:

image

webghost009 commented 3 years ago

After thinking a bit more about this, I am unsure if the c2 case is correct. The input to matrix() is RGBS and the output is YUV444PS. Since fulls and fulld are, according to the documentation, meaningless for float data should the range conversion requested in the call to matrix() be ignored or possibly result in an error? Or, is it perhaps intended that the state of _ColorRange is modified so that it can be applied later when/if the clip is converted to an integer format?

EleonoreMizo commented 3 years ago

It is fixed now (d63d7d5be82ce856a1ccddd935cb09f2b965233e), and it will be available in the next release. For floating point formats, _ColorRange just reflects the values passed with fulls or fulld, which are ignored for the actual conversion process. I’m not sure if there would be an added value to issue an error when the range is specified on a floating point format.

webghost009 commented 3 years ago

Great! Thank you so much for finding the time to fix this! And your reasoning for not throwing an error in the floating point case makes sense to me. I appreciate you taking the time to think about it.