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.

 

Go Back   Doom9's Forum > Capturing and Editing Video > Avisynth Usage

Reply
 
Thread Tools Search this Thread Display Modes
Old 8th May 2021, 11:55   #1  |  Link
Dogway
Registered User
 
Join Date: Nov 2009
Posts: 1,547
Transforms Pack v1.0 RC18

Transforms Pack


GitHub: TransformsPack.v1.0.RC18.avsi

As a color geek myself I waited and waited through the years for AviSynth to catch up, but dissapointed to see almost no progress on the area aside from the old cretindesalpes' Dither tools I decided to create and port part of my work from last year.
The Transform Pack is a set of functions for color managing AviSynth+. From function transforms like proper and performant piecewise gamma functions (EOTF, OETF) to color gamut conversion and white point mapping with kelvin temperature inputs (this last in Grade pack).
While on it I also created YUV<->RGB conversion functions to expand on its possibilities like new matrix coefficients or chroma resamplers, and an array of building block Matrix operators.


So what differentiates this from the Dither tools or other solutions like avsresize?
To start with it has no dependencies, everything is done with internal expressions so it's faster than say the Dither tools along supporting HBD out of the box.
Compared to avsresize, it's 38% the speed of it for this first version, but in opposition it's easier to use, more straight forward, the code is simple to understand and fix, includes more color spaces and finally more accurate conversions (derivations and higher float precision constants).

v1.0 focuses on SDR color spaces, v2.0 will target HDR and tonemappers.



Description of the main functions:

ColorSpace(): Full featured color space conversion filter. Includes novel spaces like DCI-P3 (Theater), ACES2065 or ACEScg aside from AdobeRGB. Automatically does gamut and gamma conversions and matrix strings use a lazy mode input so you don't have to remember exactly the string name to use.

Display_Referred(): Very similar to ColorSpace() except it's designed to convert the video working space into your display space, with an option to input a .cube 3D LUT from your calibration software. Also includes arguments to control the up format conversion kernel along the b, c, and p coefficients for the upsampler.

ConvertFormat(): If you don't pretend to change colorimetry and only want to convert formats like RGB<->YUV or YUV <->YUV this function is best suited. It supports a fast version of feisty2's chroma reconstruction filter and allows to set resampling kernel with aliases; catrom, mitchell, hermite, etc. It's planned to replace LinearResize(), so upscaling/downscaling in HBD and linear light (or other spaces) can be achieved while reconstructing chroma at the same time optionally.

moncurve_f/moncurve_r: EOTF, OETF. Optimized piecewise gamma transfer functions from AMPAS that only require the alpha coefficient, beta and phi are properly derived in float precision.

Also includes YUV_to_RGB(), RGB_to_YUV(), YcCbcCrc_to_RGB(), RGB_to_YcCbcCrc(), RGB_to_XYZ(), XYZ_to_RGB(), matrix operators (invert, transpose, divide, multiply, dot, cross...) and some more helper functions.



Speed comparison on a i7-4790K non-OC (only gamut conversion) with Prefetch(6):

The main performance limiter in ColorSpace() is the use of 7 lutxyz and 2 lutxy expressions. Any improvement in speed in said AviSynth+ internal expression function or even its support for GPU processing will translate in a massive increase in speed.

Dither tools (but no color space/primaries conversion)
28fps
Code:
Dither_convert_8_to_16()
Dither_convert_yuv_to_rgb (matrix="601", output="rgb48y", lsb_in=true, tv_range=true)
r = SelectEvery (3, 0)
g = SelectEvery (3, 1)
b = SelectEvery (3, 2)
Dither_convert_rgb_to_yuv (r, g, b, matrix="709", tv_range=true, lsb=true, mode=6, output="YV12")
HDRTools (scarce in options, not user friendly)
52fps
Code:
ConvertBits(16)
ConvertYUVtoXYZ(Color=3)
ConvertXYZtoYUV(Color=2,pColor=2) # Color=3 was noop, strange logic
TransformsPack
62fps (Prefetch 8) (33fps in 32-bit)
Code:
ConvertBits(16)
ConvertFormat(1,1,"420","601","709",true,true,true,noring=false, scale_space="gamma",kernel="mitchell")
FrancesBB LUTs
82fps (scarce in options)
Code:
ConvertBits(16)
ConvertToPlanarRGB(matrix="Rec601",chromaresample="bicubic")
Cube("C:\Program Files (x86)\AviSynth+\LUTs\BT601_NTSC_to_BT709.cube")
ConvertToYUV420(matrix="Rec709",chromaresample="bicubic")
# LinearTransformation("Linear_BT601_NTSC", "Linear_BT709") # AIO pack for above, but misses the format conversion matrices.
avsresize (not 32-bit or RGB friendly)
160fps (version r7 not working with AVSmeter)
Code:
ConvertBits(16)
z_ConvertFormat(pixel_type="YUV420P16",colorspace_op="601:601:170M:l=>709:601:709:l", dither_type="none")
HDR Core
244fps (also scarce in options)
Code:
ConvertBits(16)
ConvertToDoubleWidth()
Matrix(from=601, to=709, rg=1.0, gg=1.0, bg=1.0, a=16, b=235, ao=16, bo=235, bitdepth=16)
ConvertFromDoubleWidth()

YUV 420 to RGB conversion benchmark:
Code:
ConvertBits(16)
300 fps z_ConvertFormat(pixel_type="RGBP16",colorspace_op="709:auto:auto:l=>rgb:same:same:f",resample_filter ="bicubic",filter_param_a=0.333,filter_param_b=0.333)
188 fps ConvertToPlanarRGB(matrix="Rec709")
164 fps YUV_to_RGB("709",kernel="mitchell")
135 fps ConvertYUVtoLinearRGB(Color=2) # no gamma reencode output

TODO
  • Fix/assess chroma placement
  • v2.0. Update AP1 and AP0 transformation matrices and include the RRT
  • v2.0. Add PQ and HLG functions and tonemappers (2446, Log flavours, Hable, Hejl, etc)

Last edited by Dogway; 28th June 2021 at 21:46.
Dogway is offline   Reply With Quote
Old 8th May 2021, 14:39   #2  |  Link
kedautinh12
Registered User
 
Join Date: Jan 2018
Posts: 863
Thanks
kedautinh12 is offline   Reply With Quote
Old 8th May 2021, 15:37   #3  |  Link
FranceBB
Broadcast Encoder
 
FranceBB's Avatar
 
Join Date: Nov 2013
Location: Metropolitan City of Milan, Italy
Posts: 1,846
Looks very promising!
Well done!
FranceBB is online now   Reply With Quote
Old 9th May 2021, 08:47   #4  |  Link
StvG
Registered User
 
Join Date: Jul 2018
Posts: 241
Quick test with two of the examples from op:
Code:
ColorBars(pixel_type="yuv420p16")
z_ConvertFormat(pixel_type="YUV420P16",colorspace_op="601:601:709:l=>709:709:709:l",resample_filter="spline36",  dither_type="none")
Subtitle("""z_ConvertFormat(pixel_type="YUV420P16",resample_filter="spline36",""" + """\ncolorspace_op="601:601:709:l=>709:709:709:l", dither_type="none")""", lsp=1)
Code:
ColorBars(pixel_type="yuv420p16")
Import("TransformsPack.v1.0.RC1.avsi")
ColorSpace("601","709","spline36","spline36",true,false)
Subtitle(""" ColorSpace("601","709","spline36","spline36",true,false) """)
https://imgbox.com/g/eduZVZW9ZC
StvG is offline   Reply With Quote
Old 9th May 2021, 10:45   #5  |  Link
Dogway
Registered User
 
Join Date: Nov 2009
Posts: 1,547
Thanks for the comparison, it's easier to see the accuracy difference between ColorSpace and avsresize.
zlib is known to employ "shortcuts" for performance reasons (link).

As you can see from the code I use higher precision floats, be it for gamut transformation matrices, YUV<->RGB coefficients or D65 illuminant (0.312713, 0.329016) in my case.

Currently I'm on the development of the fast-mode, looking forward to reach at least 100fps for gamut conversion. As for the gamma conversion process currently ColorSpace() is 60% faster than avsresize
Dogway is offline   Reply With Quote
Old 9th May 2021, 15:38   #6  |  Link
Dogway
Registered User
 
Join Date: Nov 2009
Posts: 1,547
Updated to RC2, small fix.
I tested running it through only 3 lutxyz expressions and curiously it is less than half the speed. Not sure why. Should optimize the math instead.

Code:
function ColorSpace_fast (clip clp, string "source", string "target", string "kernel_up", string "kernel_down", bool "gamut", bool "gamma" \
                       , string "cplace", bool "tv_range_in", bool "tv_range_out", float "b_up", float "c_up", float "p_up", float "b_down", float "c_down", float "p_down") {

    gamut       = Default (gamut, true)           # convert gamut
    gamma       = Default (gamma, true)           # convert gamma
    tv_range_in = Default (tv_range_in,  !isRGB(clp) )
    tv_range_out= Default (tv_range_out, tv_range_in )
    kernelu     = Default (kernel_up, "bicubic")
    kerneld     = Default (kernel_down, kernelu)
    cplace      = Default (cplace, "MPEG2")
    bu          = Default (b_up, 0.00)
    cu          = Default (c_up, 0.75)   # Precise Bicubic
    pu          = Default (p_up, 0.25)
    bd          = Default (b_down, -0.5)
    cd          = Default (c_down, 0.25) # Didée's Bicubic
    pd          = Default (p_down, 0.25)

    source = Matrix_fuzzy_search (source)
    target = Matrix_fuzzy_search (target)

    s_num =
\     (source == "sRGB"      ) ? 0
\   : (source == "Rec601"    ) ? 1
\   : (source == "240M"      ) ? 2
\   : (source == "470BG"     ) ? 3
\   : (source == "Rec2020"   ) ? 4
\   : (source == "Rec709"    ) ? 5
\   : (source == "1886a"     ) ? 6
\   : (source == "AdobeRGB"  ) ? 7
\   : (source == "Display-P3") ? 0
\   : (source == "DCI-P3"    ) ? 8
\   : (source == "ACEScg"    ) ? 9
\   : (source == "ACES2065"  ) ? 9
\   : Assert (false, "Unsupported Color Space.")

    t_num =
\     (target == "sRGB"      ) ? 0
\   : (target == "Rec601"    ) ? 1
\   : (target == "240M"      ) ? 2
\   : (target == "470BG"     ) ? 3
\   : (target == "Rec2020"   ) ? 4
\   : (target == "Rec709"    ) ? 5
\   : (target == "1886a"     ) ? 6
\   : (target == "AdobeRGB"  ) ? 7
\   : (target == "Display-P3") ? 0
\   : (target == "DCI-P3"    ) ? 8
\   : (target == "ACEScg"    ) ? 9
\   : (target == "ACES2065"  ) ? 9
\   : Assert (false, "Unsupported Color Space.")

    bdpth = BitsPerComponent(clp) > 10 ? 0.09929682680944 : 0.099

    #                                  SMPTE-C     Y’PbPr 601  PAL/SECAM BT-2020    Rec.709    BT-1886a  AdobeRGB     DCI-P3   AP1/AP0
    #                         sRGB     SMPTE 170M  SMPTE 240M  BT-470BG  BT-2020    BT-1886    BT-1886a  AdobeRGB     DCI-P3      ACES
    t_temp  = Select (t_num,  6504   ,  6504   ,   6504   ,    6504   ,      6504,   6504   ,   6504   ,     6504 ,   6305  ,     6000 )
    s_off   = Select (s_num,  0.055  ,  0.099  ,   0.1115 ,    0.099  ,     bdpth,   0      ,   0      ,   0      ,   0     ,      0   )
    s_gamma = Select (s_num,  2.40   ,  2.22222,   2.22222,    2.80   ,   2.22222,   2.40   ,   2.60   ,   2.1992 ,   2.60  ,      1.0 )

    t_off   = Select (t_num,  0.055  ,  0.099  ,   0.1115 ,    0.099  ,     bdpth,   0      ,   0      ,   0      ,   0     ,      0   )
    t_gamma = Select (t_num,  2.40   ,  2.22222,   2.22222,    2.80   ,   2.22222,   2.40   ,   2.60   ,   2.1992 ,   2.60  ,      1.0 )


    clp

    # YUV to RGB
    RGBc = YCbCrToRGB_mat(source)
    RGB =  ["x ymin - "+string(RGBc[0])+" * y range_half - "+string(RGBc[1])+" * + z range_half - "+string(RGBc[2])+" * +", \
            "x ymin - "+string(RGBc[3])+" * y range_half - "+string(RGBc[4])+" * + z range_half - "+string(RGBc[5])+" * +", \
            "x ymin - "+string(RGBc[6])+" * y range_half - "+string(RGBc[7])+" * + z range_half - "+string(RGBc[8])+" * +"]


    # Linearization
    fs  = "{s_gamma} 1.0 - {s_off} / {s_off} {s_gamma} * {s_gamma} 1.0 - 1.0 {s_off} + * / {s_gamma} ^ *"
    xb  = "{s_off} {s_gamma} 1.0 - /"
    outR = s_off > 0.0 ? Format(""+RGB[0] + " range_max / A@ "+xb+" > A {s_off} + 1.0 {s_off} + / {s_gamma} ^ range_max * A "+fs+" * range_max * ? ") : \
                         Format(""+RGB[0] + " range_max / {s_gamma} ^ range_max * ")
    outG = s_off > 0.0 ? Format(""+RGB[1] + " range_max / A@ "+xb+" > A {s_off} + 1.0 {s_off} + / {s_gamma} ^ range_max * A "+fs+" * range_max * ? ") : \
                         Format(""+RGB[1] + " range_max / {s_gamma} ^ range_max * ")
    outB = s_off > 0.0 ? Format(""+RGB[2] + " range_max / A@ "+xb+" > A {s_off} + 1.0 {s_off} + / {s_gamma} ^ range_max * A "+fs+" * range_max * ? ") : \
                         Format(""+RGB[2] + " range_max / {s_gamma} ^ range_max * ")

    # Gamut conversion
    mata = RGB_to_XYZ(source, list=true)
    matb = XYZ_to_RGB(target, list=true)
    mat = MatrixDot(mata, matb)

    toMATR = outR + string(mat[0])+" * " + outG + string(mat[3])+" * + " + outB + string(mat[6])+" * +"
    toMATG = outR + string(mat[1])+" * " + outG + string(mat[4])+" * + " + outB + string(mat[7])+" * +"
    toMATB = outR + string(mat[2])+" * " + outG + string(mat[5])+" * + " + outB + string(mat[8])+" * +"

    # Gamma conversion
    yb  = "{t_off} {t_gamma} * {t_gamma} 1.0 - 1.0 {t_off} + * / {t_gamma} ^"
    rs  = "{t_gamma} 1.0 - {t_off} / {t_gamma} 1.0 - ^ 1.0 {t_off} + {t_gamma} / {t_gamma} ^ *"
    outR = t_off > 0.0 ? Format(""+toMATR+" range_max / A@ "+yb+" > 1.0 {t_off} + A 1.0 {t_gamma} / ^ * {t_off} - range_max * A "+rs+" * range_max * ? ") : \
                         Format(""+toMATR+" range_max / 1.0 {t_gamma} / ^ range_max * ")
    outG = t_off > 0.0 ? Format(""+toMATG+" range_max / A@ "+yb+" > 1.0 {t_off} + A 1.0 {t_gamma} / ^ * {t_off} - range_max * A "+rs+" * range_max * ? ") : \
                         Format(""+toMATG+" range_max / 1.0 {t_gamma} / ^ range_max * ")
    outB = t_off > 0.0 ? Format(""+toMATB+" range_max / A@ "+yb+" > 1.0 {t_off} + A 1.0 {t_gamma} / ^ * {t_off} - range_max * A "+rs+" * range_max * ? ") : \
                         Format(""+toMATB+" range_max / 1.0 {t_gamma} / ^ range_max * ")
    # RGB to YUV
    YUVc = RGBToYCbCr_mat(target)
    YUV =  ["      ymin " + outR + string(YUVc[0])+" * + " + outG + string(YUVc[1])+" * + " + outB + string(YUVc[2])+" * + ", \
            "range_half " + outR + string(YUVc[3])+" * + " + outG + string(YUVc[4])+" * + " + outB + string(YUVc[5])+" * + ",  \
            "range_half " + outR + string(YUVc[6])+" * + " + outG + string(YUVc[7])+" * + " + outB + string(YUVc[8])+" * + "]


    w =  width(clp)
    h = height(clp)
    cplace = is444(clp) ? ",src_left=0.0" : cplace=="MPEG2" ? ",src_left=0.25" : ",src_left=0.0"
    resampler = kernelu == "nnedi3" ? "nnedi3_resize16(" + String(w) + "," + String(h) + cplace +")"         : \
                kernelu == "bicubic"?   "BicubicResize(" + String(w) + "," + String(h) + cplace +",b=bu,c=cu)" : \
                kernelu == "gauss"  ?     "GaussResize(" + String(w) + "," + String(h) + cplace +",p=pu)"     : \
                                     kernelu + "Resize(" + String(w) + "," + String(h) + cplace +")"

    Y = ExtractY(clp)
    U = Eval("ExtractU(clp)." + resampler)
    V = Eval("ExtractV(clp)." + resampler)


    Yo  = Expr(Y, U, V, YUV[0])
    Cbo = Expr(Y, U, V, YUV[1])
    Cro = Expr(Y, U, V, YUV[2])

    p_type = PixelType(clp)

    p_type4 = FindStr(p_type, "44")>0
    p_type2 = FindStr(p_type, "22")>0

    nw = round(w/2.0)
    nh = round(h/2.0)
    cplace = p_type4 || cplace=="MPEG2" ? ",src_left=-0.50" : ",src_left=0.0"
    resampler = kerneld == "nnedi3" ? "nnedi3_resize16(" + String(nw+nw%2) + "," + String(p_type2 ? h : nh+nh%2) + cplace + ")"         : \
                kerneld == "bicubic"?   "BicubicResize(" + String(nw+nw%2) + "," + String(p_type2 ? h : nh+nh%2) + cplace + ",b=bd,c=cd)" : \
                kerneld == "gauss"  ?     "GaussResize(" + String(nw+nw%2) + "," + String(p_type2 ? h : nh+nh%2) + cplace + ",p=pd)"     : \
                                     kerneld + "Resize(" + String(nw+nw%2) + "," + String(p_type2 ? h : nh+nh%2) + cplace + ")"

    Cbo = p_type4 ? Cbo : Eval("Cbo." + resampler)
    Cro = p_type4 ? Cro : Eval("Cro." + resampler)

    CombinePlanes(Yo, Cbo, Cro, planes="YUV", pixel_type=p_type) }
Dogway is offline   Reply With Quote
Old 9th May 2021, 15:59   #7  |  Link
StainlessS
HeartlessS Usurer
 
StainlessS's Avatar
 
Join Date: Dec 2009
Location: Over the rainbow
Posts: 9,737
Doggy, clarify please, what does one do with the addition "ColorSpace_fast", add it to the existing RC2 script ?

EDIT: Thanx Dog
__________________
I sometimes post sober.
StainlessS@MediaFire ::: AND/OR ::: StainlessS@SendSpace

"Some infinities are bigger than other infinities", but how many of them are infinitely bigger ???

Last edited by StainlessS; 9th May 2021 at 17:39.
StainlessS is offline   Reply With Quote
Old 9th May 2021, 17:08   #8  |  Link
StvG
Registered User
 
Join Date: Jul 2018
Posts: 241
Quote:
Originally Posted by Dogway View Post
Thanks for the comparison, it's easier to see the accuracy difference between ColorSpace and avsresize.
zlib is known to employ "shortcuts" for performance reasons (link).

As you can see from the code I use higher precision floats, be it for gamut transformation matrices, YUV<->RGB coefficients or D65 illuminant (0.312713, 0.329016) in my case.

Currently I'm on the development of the fast-mode, looking forward to reach at least 100fps for gamut conversion. As for the gamma conversion process currently ColorSpace() is 60% faster than avsresize
zlib is performing any colorspace_op=... in float.

Code:
ColorBars(pixel_type="yuv420p16")
z_ConvertFormat(pixel_type="YUV420P16",colorspace_op="601:601:709:l=>709:709:709:l",resample_filter="spline36",  dither_type="none")
Code:
ColorBars(pixel_type="yuv420p16")
ConvertBits(32, fulls=false, fulld=true)
z_ConvertFormat(pixel_type="YUV420Ps",colorspace_op="601:601:709=>709:709:709",resample_filter="spline36",  dither_type="none")
z_ConvertFormat(pixel_type="yuv420p16")
Both codes give identical result.


The only shortcut zlib has (performance reasons) is approximate_gamma for gamma<->linear conversion.
StvG is offline   Reply With Quote
Old 9th May 2021, 17:28   #9  |  Link
Dogway
Registered User
 
Join Date: Nov 2009
Posts: 1,547
@StainlessS: No, leave it alone, it was a proof of concept to optimize for speed, happens to be half the speed :S

@StvG: Kb and Kg might be floats but if you reference them from the standard values in ITU they are truncated to 3 or 4 digits. I derive them from the primaries with no limit in float precision, same for the gamut transformation matrices. Although if I'm not wrong avs+ will truncate them to single floats (?).

I will try to mix the YUV<->RGB conversion matrices with the gamut one to try to optimize it although I'm not optimistic since the format conversion should happen in gamma encoded sources. Also I will try to show some comparisons on color ramps since color bars are obviously designed with the standard in mind.
Dogway is offline   Reply With Quote
Old 9th May 2021, 17:58   #10  |  Link
poisondeathray
Registered User
 
Join Date: Sep 2007
Posts: 4,771
The updated filename is "TransformsPack.v1.0.RC1.avsi" , even though the text shows "Transforms Pack v1.0 RC2 (09-05-2021) ", consider updating the filename when you have time

Code:
ColorBars(pixel_type="YV24")
YCbCrToRGB("601")
Quote:
Script error: There is no function named 'Resize'
TransformsPack.., line 241
poisondeathray is offline   Reply With Quote
Old 9th May 2021, 18:22   #11  |  Link
Dogway
Registered User
 
Join Date: Nov 2009
Posts: 1,547
This is the way:
Code:
YCbCrToRGB(YCbCrToRGB_mat("601"))
Didn't update the name since I wanted to keep the file history in GitHub for these mini changes, as I'm currently on debug mode. Things are prone to change so if people find your call easier I might change it no problem.

Last edited by Dogway; 9th May 2021 at 18:24.
Dogway is offline   Reply With Quote
Old 9th May 2021, 18:22   #12  |  Link
StvG
Registered User
 
Join Date: Jul 2018
Posts: 241
Quote:
Originally Posted by Dogway View Post
@StvG: Kb and Kg might be floats but if you reference them from the standard values in ITU they are truncated to 3 or 4 digits. I derive them from the primaries with no limit in float precision, same for the gamut transformation matrices. Although if I'm not wrong avs+ will truncate them to single floats (?).
Quick test changing kb and kr to 0.0722 and 0.2126.

https://imgbox.com/g/MClMislf6m

Results aren't identical but there is no visible difference.
StvG is offline   Reply With Quote
Old 9th May 2021, 18:44   #13  |  Link
Dogway
Registered User
 
Join Date: Nov 2009
Posts: 1,547
Quote:
Originally Posted by StvG View Post
Quick test changing kb and kr to 0.0722 and 0.2126.

https://imgbox.com/g/MClMislf6m

Results aren't identical but there is no visible difference.
I get kr=0.212649 and kb=0.072182 for Rec709. Also this is the transformation matrix from Rec601 to Rec709, unless I got the SMPTE-C primaries wrong:

Code:
0.9395467042922974, 0.017773162573575974, -0.0016219262033700943
0.05017891526222229, 0.9657943844795227, -0.004370423033833504
0.010274499654769897, 0.01643248274922371, 1.0059921741485596

SMPTE-C Primaries:

Code:
[0.630,  0.340]
[0.310,  0.595]
[0.155,  0.070]

EDIT: By the way, plot twist. Rec601 doesn't use SMPTE-C based kb and kr coefficients, they kept those from SMPTE 470M, the old 1953 NTSC from the FCC. Those are kr=0.298967, kg=0.586421 and kb=0.114612. Updated the code according.

Last edited by Dogway; 9th May 2021 at 19:07.
Dogway is offline   Reply With Quote
Old 13th May 2021, 17:52   #14  |  Link
StvG
Registered User
 
Join Date: Jul 2018
Posts: 241
Shouldn't ColorSpace("601","709","spline36","spline36",true,false) be the same as YCbCrToRGB(YCbCrToRGB_mat("601")).RGBToYCbCr(RGBToYCbCr_mat("709")) ?

Code:
ColorBars(pixel_type="yv12")
z_ConvertFormat(colorspace_op="601:601:709:l=>709:709:709:l",resample_filter="spline36",  dither_type="none")
Subtitle("""z_ConvertFormat(colorspace_op="601:601:709:l=>709:709:709:l",\n resample_filter="spline36",  dither_type="none")""", lsp=1)
Code:
ColorBars(pixel_type="yv12")
Import("TransformsPack.v1.0.RC1.avsi")
ColorSpace("601","709","spline36","spline36",true,false)
Subtitle("""ColorSpace("601","709","spline36","spline36",true,false) """, lsp=1)
Code:
ColorBars(pixel_type="yv12")
Import("TransformsPack.v1.0.RC1.avsi")
YCbCrToRGB(YCbCrToRGB_mat("601")).RGBToYCbCr(RGBToYCbCr_mat("709"))
Subtitle(""" YCbCrToRGB(YCbCrToRGB_mat("601")) \n RGBToYCbCr(RGBToYCbCr_mat("709")) """, lsp=1)
https://imgbox.com/g/1QhTQhu3Gt
StvG is offline   Reply With Quote
Old 13th May 2021, 19:55   #15  |  Link
Dogway
Registered User
 
Join Date: Nov 2009
Posts: 1,547
I don't think so. RGB isn't space agnostic. When you convert from Rec601 YUV to RGB format you do it with Rec601 coefficients and end with RGB in Rec601 color space.
Assuming the RGB is in Rec709 as in RGBToYCbCr(RGBToYCbCr_mat("709")) is therefore wrong without a prior gamut conversion between 601 and 709.

In any case in RC3 I had some mistakes with the format conversion assuming PC levels where it shouldn't, so for the time being just avoid using Transforms Pack in production. I fixed it already internally in RC4 but it's not ready for release (tangled in modern HDR compliant formats like ICtCp and IPT-PQ).
__________________
[i7-4790K@Stock::GTX 1070] AviSynth+ filters and mods on GitHub
Dogway is offline   Reply With Quote
Old 13th May 2021, 22:00   #16  |  Link
StvG
Registered User
 
Join Date: Jul 2018
Posts: 241
Quote:
Originally Posted by Dogway View Post
I don't think so. RGB isn't space agnostic. When you convert from Rec601 YUV to RGB format you do it with Rec601 coefficients and end with RGB in Rec601 color space.
Assuming the RGB is in Rec709 as in RGBToYCbCr(RGBToYCbCr_mat("709")) is therefore wrong without a prior gamut conversion between 601 and 709.
Actually that way you're converting only matrix from 601(470bg) to 709 keeping transfer and primaries the same.
Here:
Code:
ColorBars(pixel_type="yuv444p16")
z_ConvertFormat(colorspace_op="601:709:709:l=>709:709:709:l",resample_filter="spline36",  dither_type="none")
Subtitle(""" z_ConvertFormat(colorspace_op="601:709:709:l=>709:709:709:l",\nresample_filter="spline36",  dither_type="none") """, lsp=1)
Code:
ColorBars(pixel_type="yuv444p16")
ConvertToRGB()
ConvertToYUV444(matrix="Rec709")
Subtitle(""" ConvertToRGB()\nConvertToYUV444(matrix="Rec709") """, lsp=1)
Code:
ColorBars(pixel_type="yuv444p16")
ConvertYUVToLinearRGB(Color=3, outputmode=2)
ConvertLinearRGBToYUV()
Subtitle(""" ConvertYUVToLinearRGB(Color=3, outputmode=2)\nConvertLinearRGBToYUV() """, lsp=1)
Code:
ColorBars(pixel_type="yuv444p16")
Import("TransformsPack.v1.0.RC1.avsi")
YCbCrToRGB(YCbCrToRGB_mat("601")).RGBToYCbCr(RGBToYCbCr_mat("709"))
Subtitle(""" YCbCrToRGB(YCbCrToRGB_mat("601")) \n RGBToYCbCr(RGBToYCbCr_mat("709")) """, lsp=1)
Code:
ColorBars(pixel_type="yuv444p16")
ConvertToStacked()
Dither_convert_yuv_to_rgb(matrix="601")
Dither_convert_rgb_to_yuv(matrix="709", output="YV24",lsb=true)
DitherPost(mode=-1)
ConvertFromStacked()
Subtitle("""Dither_convert_yuv_to_rgb(matrix="601")\nDither_convert_rgb_to_yuv(matrix="709", output="YV24",lsb=true)\nDitherPost(mode=-1) """, lsp=1)
Results: https://imgbox.com/g/nyDydHExob (z.lib, dither and internal functions have visible identical results)

So from the first post:
Code:
ConvertBits(16)
z_ConvertFormat(pixel_type="YUV420P16",colorspace_op="601:601:709:l=>709:709:709:l", dither_type="none")
Above is not equivalent of:

Code:
ConvertBits(16)
ColorSpace("601","709","bicubic","bicubic",true,false)
The last code doesn't do only matrix conversion but primaries and transfer too:
Code:
ColorBars(pixel_type="yuv444p16")
z_ConvertFormat(colorspace_op="601:470bg:470bg:l=>709:709:709:l",resample_filter="spline36",  dither_type="none")
Subtitle(""" z_ConvertFormat(colorspace_op="601:470bg:470bg:l=>709:709:709:l",\nresample_filter="spline36",  dither_type="none") """, lsp=1)
Code:
ColorBars(pixel_type="yuv444p16")
ConvertYUVtoXYZ(Color=3, outputmode=2).ConvertXYZtoYUV(Color=2, pColor=3)
Subtitle("""ConvertYUVtoXYZ(Color=3, outputmode=2).ConvertXYZtoYUV(Color=2, pColor=3) """, lsp=1)
Code:
ColorBars(pixel_type="yuv444p16")
Import("TransformsPack.v1.0.RC1.avsi")
ColorSpace("601","709","spline36","spline36",true,false)
Subtitle("""ColorSpace("601","709","spline36","spline36",true,false) """, lsp=1)
Results: https://imgbox.com/g/sTti4whkcx
StvG is offline   Reply With Quote
Old 14th May 2021, 15:16   #17  |  Link
Dogway
Registered User
 
Join Date: Nov 2009
Posts: 1,547
StvG:
Sorry if I can't follow you much. Feedback is nice so I appreciate your posts. The reason I didn't use the transfer curve in OP for comparison was probably because it wasn't a fair comparison, I'm using a power law gamma of 2.40 for Rec709 while I think other plugins use sRGB tranfer function, or simply 2.222.

Quote:
z.lib, dither and internal functions have visible identical results
My eyes might deceive me but my solution also looks identical.

I'm not looking into other code much except for certain specific things to shorten times. I'm doing this as I understand it should be done because I wasn't satisfied with what was already available, either useability, options or accuracy.
470bg is PAL Rec601, 170M is NTSC Rec601, but as I noted above the matrix coefficients used for NTSC Rec601 are not the ones derived from 170M but from the old FCC 1953 standard.

I don't have much time today but I will correct the OP example tomorrow and run some tests. For the time being I uploaded RC4, or what I had at the moment so you can test with correct range conversions (hopefully).
__________________
[i7-4790K@Stock::GTX 1070] AviSynth+ filters and mods on GitHub
Dogway is offline   Reply With Quote
Old 14th May 2021, 18:54   #18  |  Link
StvG
Registered User
 
Join Date: Jul 2018
Posts: 241
z.lib is using the transfer by Rec.1886 when transfer 601/709/2020(_10/_12) is used.
HDRTools is using the same transfer by Rec.1886 when Color=2 (in XYZ) is used.

Edit:
Quote:
Originally Posted by Dogway
Quote:
z.lib, dither and internal functions have visible identical results
My eyes might deceive me but my solution also looks identical.
Right, your solution also looks identical (colors, brightness) but there is some shift.

Last edited by StvG; 14th May 2021 at 19:29.
StvG is offline   Reply With Quote
Old 15th May 2021, 14:23   #19  |  Link
Dogway
Registered User
 
Join Date: Nov 2009
Posts: 1,547
I checked the source code of zimg and Rec.1886 gamma function is listed but I couldn't see it as an option in either documentation:
http://avisynth.nl/index.php/Avsresize
http://www.vapoursynth.com/doc/functions/resize.html

As far as above documentation goes Rec.709 uses Rec.709 gamma, which is identical to Rec.2020 gamma for <12-bits (R-REC-BT.2020-2-201510). This is called Camera Gamma and it's not supposed to be used in authored content in either bitdepth. Rec.1886 gamma (in practice a pure power 2.4 gamma) is recommended as display gamma for Rec.709 (read EBU Tech 3320 v4.1 - 1.5.4). If what you mean is ITU-R BT.2390-2, that's for HDR not HBD.


Code:
constexpr float REC709_ALPHA = 1.09929682680944f;
constexpr float REC709_BETA = 0.018053968510807f;

float rec_709_oetf(float x) noexcept
{
	if (x < REC709_BETA)
		x = x * 4.5f;
	else
		x = REC709_ALPHA * zimg_x_powf(x, 0.45f) - (REC709_ALPHA - 1.0f);

	return x;
}


float rec_1886_eotf(float x) noexcept
{
	return x < 0.0f ? 0.0f : zimg_x_powf(x, 2.4f);
}



About the shift, do you mean pixel shift? It's subsampling artifacts of upsampling and downsampling. Since ColorSpace() ( and WhitePoint() ) is doing a roundtrip in RC4 I hard coded it to PointResize() with a known fix for PointResize() pixel shift of -1.0. Hope it's fine now.


Finally avsresize has an issue where I'm not able to use "fcc" for Matrix coefficients for Rec.601 primaries, as it should. Both PAL and NTSC in YCbCr use the coefficients derived from the FCC primaries. In avsresize these coefficients are not computed but constants with a precision of 3 decimal floats only.
__________________
[i7-4790K@Stock::GTX 1070] AviSynth+ filters and mods on GitHub
Dogway is offline   Reply With Quote
Old 15th May 2021, 20:11   #20  |  Link
StvG
Registered User
 
Join Date: Jul 2018
Posts: 241
Quote:
Originally Posted by Dogway View Post
I checked the source code of zimg and Rec.1886 gamma function is listed but I couldn't see it as an option in either documentation:
http://avisynth.nl/index.php/Avsresize
http://www.vapoursynth.com/doc/functions/resize.html

As far as above documentation goes Rec.709 uses Rec.709 gamma, which is identical to Rec.2020 gamma for <12-bits (R-REC-BT.2020-2-201510). This is called Camera Gamma and it's not supposed to be used in authored content in either bitdepth. Rec.1886 gamma (in practice a pure power 2.4 gamma) is recommended as display gamma for Rec.709 (read EBU Tech 3320 v4.1 - 1.5.4). If what you mean is ITU-R BT.2390-2, that's for HDR not HBD.
Have a look at https://github.com/sekrit-twc/zimg/issues/111 and https://github.com/sekrit-twc/zimg/i...ment-471823652
Here transfers - https://github.com/sekrit-twc/zimg/b...pi/zimg.h#L302

"Rec.1886 gamma (in practice a pure power 2.4 gamma)" - in some cases could be the same as 2.4 gamma but Rec.1886 is not 2.4 gamma.

Quote:
Originally Posted by Dogway View Post
About the shift, do you mean pixel shift? It's subsampling artifacts of upsampling and downsampling. Since ColorSpace() ( and WhitePoint() ) is doing a roundtrip in RC4 I hard coded it to PointResize() with a known fix for PointResize() pixel shift of -1.0. Hope it's fine now.
Right, pixel shift.


Quote:
Originally Posted by Dogway View Post
Finally avsresize has an issue where I'm not able to use "fcc" for Matrix coefficients for Rec.601 primaries, as it should. Both PAL and NTSC in YCbCr use the coefficients derived from the FCC primaries. In avsresize these coefficients are not computed but constants with a precision of 3 decimal floats only.
What do you mean you're not able to use fcc matrix with 470bg primaries?

"In avsresize these coefficients are not computed but constants with a precision of 3 decimal floats only."

Can you show comparison where there is visual difference?
StvG is offline   Reply With Quote
Reply

Tags
colorspace, conversion, format, matrix, temperature

Thread Tools Search this Thread
Search this Thread:

Advanced Search
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT +1. The time now is 12:39.


Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2021, vBulletin Solutions Inc.