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) }