Enhance Avisynth video workflow and metadata tools

This commit is contained in:
ajp_anton
2026-07-30 03:20:19 +00:00
parent d60ce51b14
commit 100cad1cb5
26 changed files with 2350 additions and 1161 deletions
+409 -64
View File
@@ -4,18 +4,43 @@
# small and dependency-light unless the visible scripts explicitly document the
# required plugins.
function MBT_MarkSource(clip c, int source_id, int "matrix")
function MBT_MarkSource(clip c, int source_id, int "matrix", int "primaries", int "transfer", int "color_range", float "hdr_peak", float "absolute_timestamp_end_seconds", float "source_duration_seconds")
{
# Attach source identity to each frame. Python reads these properties after
# running the user-edited script and maps frames back to ffprobe timestamps.
# Timestamp/duration variables are supplied by generated ConditionalReader
# files when available.
matrix = Default(matrix, MBT_GetMatrixCode(c))
# Timing values are attached separately after ConditionalReader has supplied
# their per-frame variables.
matrix = Defined(matrix) ? matrix : 2
primaries = Defined(primaries) ? primaries : 2
transfer = Defined(transfer) ? transfer : 2
color_range = Defined(color_range) ? color_range : 0
hdr_peak = Default(hdr_peak, 1000.0)
absolute_timestamp_end_seconds = Default(absolute_timestamp_end_seconds, 0.0)
source_duration_seconds = Default(source_duration_seconds, 0.0)
runtime = """
last.propSet("mbt_source_id", """ + String(source_id) + """).\
propSet("mbt_source_frame", current_frame).\
propSet("mbt_drop_frame", 0).\
propSet("mbt_absolute_timestretch", 1.0).\
propSet("mbt_absolute_timestamp_end_seconds", """ + String(absolute_timestamp_end_seconds) + """).\
propSet("mbt_source_duration_seconds", """ + String(source_duration_seconds) + """).\
propSet("mbt_absolute_timestamp_start_seconds", """ + String(absolute_timestamp_end_seconds - source_duration_seconds) + """).\
propSet("_Matrix", """ + String(matrix) + """).\
propSet("mbt_hdr_peak", """ + String(hdr_peak) + """).\
propSet("_Primaries", """ + String(primaries) + """).\
propSet("_Transfer", """ + String(transfer) + """).\
propSet("_ColorRange", """ + String(color_range) + """)
"""
source = c.PropSet("mbt_hdr_peak", hdr_peak).\
MBT_SetColor(MBT_MatrixNameFromCode(matrix), primaries, transfer, color_range)
return source.ScriptClip(runtime)
}
function MBT_MarkTiming(clip c)
{
runtime = """
source_start = propGetFloat("mbt_absolute_timestamp_start_seconds")
last.propSet("mbt_absolute_timestamp_seconds", source_start + mbt_frame_time_seconds).\
propSet("mbt_frame_time_seconds", mbt_frame_time_seconds).\
propSet("mbt_relative_timestamp", mbt_relative_timestamp).\
propSet("mbt_absolute_timestamp", mbt_absolute_timestamp).\
@@ -25,6 +50,86 @@ function MBT_MarkSource(clip c, int source_id, int "matrix")
return c.ScriptClip(runtime)
}
function MBT_AbsoluteTimeStretch(clip c, float factor)
{
Assert(factor > 0.0, "MBT_AbsoluteTimeStretch factor must be greater than zero.")
runtime = """
source_end = propGetFloat("mbt_absolute_timestamp_end_seconds")
source_duration = propGetFloat("mbt_source_duration_seconds")
previous_factor = propGetFloat("mbt_absolute_timestretch")
total_factor = previous_factor * """ + String(factor) + """
Assert(source_end > 0.0 && source_duration > 0.0, "MBT_AbsoluteTimeStretch requires source end timestamp and duration metadata.")
source_start = source_end - source_duration * total_factor
stretched_timestamp = source_start + mbt_frame_time_seconds * total_factor
last.propSet("mbt_absolute_timestamp_start_seconds", source_start).\
propSet("mbt_absolute_timestamp_seconds", stretched_timestamp).\
propSet("mbt_absolute_timestamp", MBT_FormatUtcTimestamp(stretched_timestamp)).\
propSet("mbt_absolute_timestretch", total_factor)
"""
return c.ScriptClip(runtime)
}
function MBT_IsLeapYear(int year)
{
return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)
}
function MBT_DaysInYear(int year)
{
return MBT_IsLeapYear(year) ? 366 : 365
}
function MBT_DaysInMonth(int year, int month)
{
return month == 2 ? (MBT_IsLeapYear(year) ? 29 : 28)
\ : (month == 4 || month == 6 || month == 9 || month == 11) ? 30 : 31
}
function MBT_YearFromEpochDays(int days, int "year")
{
year = Default(year, 1970)
return days < MBT_DaysInYear(year) ? year : MBT_YearFromEpochDays(days - MBT_DaysInYear(year), year + 1)
}
function MBT_DayOfYear(int days, int "year")
{
year = Default(year, 1970)
return days < MBT_DaysInYear(year) ? days : MBT_DayOfYear(days - MBT_DaysInYear(year), year + 1)
}
function MBT_MonthFromDayOfYear(int year, int day, int "month")
{
month = Default(month, 1)
return day < MBT_DaysInMonth(year, month) ? month : MBT_MonthFromDayOfYear(year, day - MBT_DaysInMonth(year, month), month + 1)
}
function MBT_DayOfMonth(int year, int day, int "month")
{
month = Default(month, 1)
return day < MBT_DaysInMonth(year, month) ? day + 1 : MBT_DayOfMonth(year, day - MBT_DaysInMonth(year, month), month + 1)
}
function MBT_Pad(int value, int width)
{
text = String(value)
return StrLen(text) >= width ? text : "0" + MBT_Pad(value, width - 1)
}
function MBT_FormatUtcTimestamp(float timestamp)
{
whole_seconds = Int(Floor(timestamp))
microseconds = Round((timestamp - whole_seconds) * 1000000.0)
whole_seconds = microseconds >= 1000000 ? whole_seconds + 1 : whole_seconds
microseconds = microseconds >= 1000000 ? 0 : microseconds
days = Int(Floor(whole_seconds / 86400.0))
day_seconds = whole_seconds - days * 86400
year = MBT_YearFromEpochDays(days)
day = MBT_DayOfYear(days)
month = MBT_MonthFromDayOfYear(year, day)
return MBT_Pad(year, 4) + "-" + MBT_Pad(month, 2) + "-" + MBT_Pad(MBT_DayOfMonth(year, day), 2) + "T" + \
MBT_Pad(day_seconds / 3600, 2) + ":" + MBT_Pad((day_seconds % 3600) / 60, 2) + ":" + MBT_Pad(day_seconds % 60, 2) + "." + MBT_Pad(microseconds, 6) + "Z"
}
function MBT_AudioSource(clip video, string source_path, string audio_cache_path)
{
audio = FFAudioSource(source_path, cachefile=audio_cache_path)
@@ -94,12 +199,17 @@ function MBT_Info(clip c, int "size", int "align")
align = Default(align, 7)
runtime = """
drop_value = propGetInt("mbt_drop_frame") == 0 ? "False" : "True"
matrix = MBT_MatrixNameFromCode(propGetInt("_Matrix"))
transfer = propGetInt("_Transfer")
transfer_name = transfer == 16 ? "PQ" : (transfer == 18 ? "HLG" : "SDR")
range_name = propGetInt("_ColorRange") == 1 ? "full range" : "limited range"
text = "Absolute timestamp: " + propGetString("mbt_absolute_timestamp") + Chr(10) + \
"Relative timestamp: " + propGetString("mbt_relative_timestamp") + Chr(10) + \
Chr(10) + \
"Source frame: " + String(propGetInt("mbt_source_frame")) + Chr(10) + \
"Frame duration: " + propGetString("mbt_frame_duration") + Chr(10) + \
"Drop frame: " + drop_value
"Drop frame: " + drop_value + Chr(10) + \
"Color: " + matrix + ", " + transfer_name + ", " + range_name
Subtitle(last, text, size=""" + String(size) + """, align=""" + String(align) + """)
"""
return c.ScriptClip(runtime)
@@ -176,8 +286,8 @@ function Resize(clip v, int "w", int "h", float "dar", float "xcrop", float "ycr
crop_w = cropped_w - crop_extra_l - crop_extra_r
crop_h = cropped_h - crop_extra_t - crop_extra_b
input_matrix = Default(input_matrix, v.IsRGB ? MBT_DefaultMatrix(wo, ho) : MBT_GetMatrixName(v))
output_matrix = Default(output_matrix, MBT_DefaultMatrix(w, h))
input_matrix = Default(input_matrix, v.IsRGB ? MBT_DefaultMatrix(wo, ho) : mbt_color_matrix)
output_matrix = Default(output_matrix, MBT_DefaultOutputMatrix(w, h))
return v.MBT_LinearResize(w, h, crop_l, crop_t, crop_w, crop_h, linear=linear, input_matrix=input_matrix, output_matrix=output_matrix)
}
@@ -200,51 +310,85 @@ function MBT_LinearResize(clip src, int tw, int th,
p = Default(p , 30.0)
bitdepth = Default(bitdepth , src.BitsPerComponent)
linear = Defined(linear) ? linear : (sw / Float(tw) >= 1.5 || sh / Float(th) >= 1.5)
input_matrix = Default(input_matrix, src.IsRGB ? MBT_DefaultMatrix(src.Width, src.Height) : MBT_GetMatrixName(src))
output_matrix = Default(output_matrix, MBT_DefaultMatrix(tw, th))
Assert(
\ !linear || (FunctionExists("y_gamma_to_linear") && FunctionExists("y_linear_to_gamma")),
\ "Resize(linear=true) requires y_gamma_to_linear and y_linear_to_gamma. Load the missing dependency or call Resize(..., linear=false)."
\)
input_matrix = Default(input_matrix, src.IsRGB ? MBT_DefaultMatrix(src.Width, src.Height) : mbt_color_matrix)
output_matrix = Default(output_matrix, MBT_DefaultOutputMatrix(tw, th))
clp = (src.IsRGB ? (src.HasAlpha ? src.ConvertToPlanarRGBA
\ : src.ConvertToPlanarRGB)
\ : src.ConvertToPlanarRGB(matrix=input_matrix))
clp = clp.ConvertBits(32)
return MBT_IsHDR()
\ ? (linear
\ ? src.MBT_HDRLinearResize(tw, th, sl, st, sw, sh, hkernel, vkernel, b, c, taps, p, bitdepth, input_matrix, output_matrix)
\ : src.MBT_HDRResize(tw, th, sl, st, sw, sh, hkernel, vkernel, b, c, taps, p, input_matrix, output_matrix))
\ : src.MBT_SDRResize(tw, th, sl, st, sw, sh, hkernel, vkernel, b, c, taps, p, bitdepth, linear, input_matrix, output_matrix)
}
function MBT_HDRResize(clip src, int tw, int th, float sl, float st, float sw, float sh,
\ string hkernel, string vkernel, float b, float c, int "taps", float "p",
\ string "input_matrix", string "output_matrix")
{
Assert(input_matrix == output_matrix,
\ "HDR Resize preserves its color system. Use MBT_ToSDR before Resize to change it.")
return src.MBT_ResizePlanes(tw, th, sl, st, sw, sh, hkernel, vkernel, b, c, taps, p).PropCopy(src)
}
function MBT_SDRResize(clip src, int tw, int th, float sl, float st, float sw, float sh,
\ string hkernel, string vkernel, float b, float c, int "taps", float "p", int "bitdepth",
\ bool "linear", string "input_matrix", string "output_matrix")
{
Assert(!linear || (FunctionExists("y_gamma_to_linear") && FunctionExists("y_linear_to_gamma")),
\ "Resize(linear=true) requires y_gamma_to_linear and y_linear_to_gamma. Load the missing dependency or call Resize(..., linear=false).")
clp = (src.IsRGB ? (src.HasAlpha ? src.ConvertToPlanarRGBA : src.ConvertToPlanarRGB) : src.ConvertToPlanarRGB(matrix=input_matrix)).ConvertBits(32)
clp = linear ? clp.y_gamma_to_linear : clp
if (hkernel != vkernel) {
htaps = Default(taps, (hkernel == "Lanczos" ? 3 : 4))
vtaps = Default(taps, (vkernel == "Lanczos" ? 3 : 4))
clp = clp.MBT_LinearResize(tw, src.Height, sl, 0, sw, 0, hkernel, b, c, htaps, p, linear=false, input_matrix=input_matrix, output_matrix=input_matrix)
clp = clp.MBT_LinearResize(tw, th, 0, st, 0, sh, vkernel, b, c, vtaps, p, linear=false, input_matrix=input_matrix, output_matrix=output_matrix)
}
else {
taps = Default(taps, (hkernel == "Lanczos" ? 3 : 4))
clp =
\ (hkernel == "Bicubic" ) ? clp.BicubicResize(tw, th, b=b, c=c, src_left=sl, src_top=st, src_width=sw, src_height=sh)
\ : (hkernel == "Bilinear" ) ? clp.BilinearResize(tw, th, src_left=sl, src_top=st, src_width=sw, src_height=sh)
\ : (hkernel == "Blackman" ) ? clp.BlackmanResize(tw, th, src_left=sl, src_top=st, src_width=sw, src_height=sh, taps=taps)
\ : (hkernel == "Gauss" ) ? clp.GaussResize(tw, th, src_left=sl, src_top=st, src_width=sw, src_height=sh, p=p)
\ : (hkernel == "Lanczos" ) ? clp.LanczosResize(tw, th, src_left=sl, src_top=st, src_width=sw, src_height=sh, taps=taps)
\ : (hkernel == "Lanczos4" ) ? clp.Lanczos4Resize(tw, th, src_left=sl, src_top=st, src_width=sw, src_height=sh)
\ : (hkernel == "Point" ) ? clp.PointResize(tw, th, src_left=sl, src_top=st, src_width=sw, src_height=sh)
\ : (hkernel == "Sinc" ) ? clp.SincResize(tw, th, src_left=sl, src_top=st, src_width=sw, src_height=sh, taps=taps)
\ : (hkernel == "Spline16" ) ? clp.Spline16Resize(tw, th, src_left=sl, src_top=st, src_width=sw, src_height=sh)
\ : (hkernel == "Spline36" ) ? clp.Spline36Resize(tw, th, src_left=sl, src_top=st, src_width=sw, src_height=sh)
\ : (hkernel == "Spline64" ) ? clp.Spline64Resize(tw, th, src_left=sl, src_top=st, src_width=sw, src_height=sh)
\ : (hkernel == "CatmullRom" ) ? clp.BicubicResize(tw, th, b=0.0, c=0.5, src_left=sl, src_top=st, src_width=sw, src_height=sh)
\ : Assert(false, "Invalid resize kernel.")
}
clp = clp.MBT_ResizePlanes(tw, th, sl, st, sw, sh, hkernel, vkernel, b, c, taps, p)
clp = linear ? clp.y_linear_to_gamma : clp
clp = src.IsRGB ? (src.HasAlpha ? clp.ConvertToPlanarRGBA : clp.ConvertToPlanarRGB)
\ : (src.Is420 ? clp.ConvertToYUV420(matrix=output_matrix).ConvertBits(bitdepth).MBT_SetMatrix(output_matrix)
\ : (src.Is422 ? clp.ConvertToYUV422(matrix=output_matrix).ConvertBits(bitdepth).MBT_SetMatrix(output_matrix)
\ : (src.Is444 ? clp.ConvertToYUV444(matrix=output_matrix).ConvertBits(bitdepth).MBT_SetMatrix(output_matrix) : clp.ConvertBits(bitdepth).MBT_SetMatrix(output_matrix))))
clp = src.IsRGB ? clp.ConvertBits(bitdepth) : clp
\ : (src.Is420 ? clp.ConvertToYUV420(matrix=output_matrix)
\ : (src.Is422 ? clp.ConvertToYUV422(matrix=output_matrix) : clp.ConvertToYUV444(matrix=output_matrix)))
global mbt_color_matrix = output_matrix
global mbt_color_primaries = MBT_PrimariesForMatrix(output_matrix)
global mbt_color_transfer = 1
return src.IsRGB ? clp.ConvertBits(bitdepth).PropCopy(src) : clp.ConvertBits(bitdepth).PropCopy(src).MBT_SetSDRColor(output_matrix, mbt_color_range)
}
return clp
function MBT_HDRLinearResize(clip src, int tw, int th, float sl, float st, float sw, float sh,
\ string hkernel, string vkernel, float b, float c, int "taps", float "p", int "bitdepth",
\ string "input_matrix", string "output_matrix")
{
Assert(!src.IsRGB, "HDR Resize currently requires planar YUV input.")
Assert(input_matrix == output_matrix,
\ "HDR Resize preserves its color system. Use MBT_ToSDR before Resize to change it.")
Assert(mbt_color_primaries == 9,
\ "HDR Resize(linear=true) currently requires Rec2020 primaries. P3 HDR needs mastering-primary support.")
Assert(FunctionExists("ConvertYUVtoLinearRGB") && FunctionExists("ConvertLinearRGBtoYUV"),
\ "HDR Resize(linear=true) requires HDRTools. Load HDRTools or call Resize(..., linear=false).")
clp = src.MBT_LinearHDR(MBT_HDRMode(), mbt_color_range)
clp = clp.MBT_ResizePlanes(tw, th, sl, st, sw, sh, hkernel, vkernel, b, c, taps, p)
clp = clp.ConvertLinearRGBtoYUV(Color=0, OutputMode=MBT_YuvOutputMode(src), HDRMode=MBT_HDRMode(), OOTF=false, fullrange=mbt_color_range == 1)
return clp.ConvertBits(bitdepth).PropCopy(src).MBT_SetColor(input_matrix, mbt_color_primaries, mbt_color_transfer, mbt_color_range)
}
function MBT_ResizePlanes(clip clp, int tw, int th, float sl, float st, float sw, float sh,
\ string hkernel, string vkernel, float b, float c, int "taps", float "p")
{
if (hkernel != vkernel) {
htaps = Default(taps, hkernel == "Lanczos" ? 3 : 4)
vtaps = Default(taps, vkernel == "Lanczos" ? 3 : 4)
clp = clp.MBT_ResizePlanes(tw, clp.Height, sl, 0, sw, clp.Height, hkernel, hkernel, b, c, htaps, p)
return clp.MBT_ResizePlanes(tw, th, 0, st, clp.Width, sh, vkernel, vkernel, b, c, vtaps, p)
}
taps = Default(taps, hkernel == "Lanczos" ? 3 : 4)
return
\ (hkernel == "Bicubic" ) ? clp.BicubicResize(tw, th, b=b, c=c, src_left=sl, src_top=st, src_width=sw, src_height=sh)
\ : (hkernel == "Bilinear" ) ? clp.BilinearResize(tw, th, src_left=sl, src_top=st, src_width=sw, src_height=sh)
\ : (hkernel == "Blackman" ) ? clp.BlackmanResize(tw, th, src_left=sl, src_top=st, src_width=sw, src_height=sh, taps=taps)
\ : (hkernel == "Gauss" ) ? clp.GaussResize(tw, th, src_left=sl, src_top=st, src_width=sw, src_height=sh, p=p)
\ : (hkernel == "Lanczos" ) ? clp.LanczosResize(tw, th, src_left=sl, src_top=st, src_width=sw, src_height=sh, taps=taps)
\ : (hkernel == "Lanczos4" ) ? clp.Lanczos4Resize(tw, th, src_left=sl, src_top=st, src_width=sw, src_height=sh)
\ : (hkernel == "Point" ) ? clp.PointResize(tw, th, src_left=sl, src_top=st, src_width=sw, src_height=sh)
\ : (hkernel == "Sinc" ) ? clp.SincResize(tw, th, src_left=sl, src_top=st, src_width=sw, src_height=sh, taps=taps)
\ : (hkernel == "Spline16" ) ? clp.Spline16Resize(tw, th, src_left=sl, src_top=st, src_width=sw, src_height=sh)
\ : (hkernel == "Spline36" ) ? clp.Spline36Resize(tw, th, src_left=sl, src_top=st, src_width=sw, src_height=sh)
\ : (hkernel == "Spline64" ) ? clp.Spline64Resize(tw, th, src_left=sl, src_top=st, src_width=sw, src_height=sh)
\ : (hkernel == "CatmullRom" ) ? clp.BicubicResize(tw, th, b=0.0, c=0.5, src_left=sl, src_top=st, src_width=sw, src_height=sh)
\ : Assert(false, "Invalid resize kernel.")
}
function MBT_DefaultMatrix(int w, int h)
@@ -252,6 +396,58 @@ function MBT_DefaultMatrix(int w, int h)
return (w >= 3840 || h >= 2160) ? "Rec2020" : ((w >= 1280 || h >= 720) ? "Rec709" : "Rec601")
}
function MBT_IsHDR()
{
return mbt_color_transfer == 16 || mbt_color_transfer == 18
}
function MBT_DefaultOutputMatrix(int w, int h)
{
return MBT_IsHDR() ? mbt_color_matrix : MBT_DefaultMatrix(w, h)
}
function MBT_GetPrimariesCode(clip c)
{
primaries = c.PropGetInt("_Primaries")
return primaries == 0 ? 2 : primaries
}
function MBT_GetTransferCode(clip c)
{
transfer = c.PropGetInt("_Transfer")
return transfer == 0 ? 2 : transfer
}
function MBT_GetRangeCode(clip c)
{
return c.PropGetInt("_ColorRange")
}
function MBT_PrimariesForMatrix(string matrix)
{
return matrix == "Rec2020" ? 9 : 1
}
function MBT_SDRColorForMatrix(string matrix)
{
return matrix == "Rec2020" ? 1 : (matrix == "Rec601" ? 3 : 2)
}
function MBT_YuvOutputMode(clip c)
{
return c.Is420 ? 2 : (c.Is422 ? 1 : 0)
}
function MBT_LinearHDR(clip c, int hdr_mode, int color_range)
{
return c.ConvertYUVtoLinearRGB(Color=0, OutputMode=2, HDRMode=hdr_mode, OOTF=false, fullrange=color_range == 1)
}
function MBT_HDRMode()
{
return mbt_color_transfer == 18 ? 2 : 0
}
function MBT_MatrixNameFromCode(int matrix)
{
return (matrix == 1) ? "Rec709"
@@ -278,33 +474,182 @@ function MBT_MatrixInt(string matrix)
\ : 2
}
function MBT_GetMatrixCode(clip c)
{
matrix = MBT_MatrixNameFromCode(c.PropGetInt("_Matrix"))
return (matrix == "") ? MBT_MatrixInt(MBT_DefaultMatrix(c.Width, c.Height)) : c.PropGetInt("_Matrix")
}
function MBT_GetMatrixName(clip c)
{
matrix = MBT_MatrixNameFromCode(MBT_GetMatrixCode(c))
return (matrix == "") ? MBT_DefaultMatrix(c.Width, c.Height) : matrix
}
function MBT_SetMatrix(clip c, string matrix)
{
return c.PropSet("_Matrix", MBT_MatrixInt(matrix))
}
function MBT_SetColor(clip c, string matrix, int primaries, int transfer, int color_range)
{
return c.PropSet("_Matrix", MBT_MatrixInt(matrix)).\
PropSet("_Primaries", primaries).\
PropSet("_Transfer", transfer).\
PropSet("_ColorRange", color_range)
}
function MBT_SetSDRColor(clip c, string matrix, int color_range)
{
return c.MBT_SetColor(matrix, MBT_PrimariesForMatrix(matrix), 1, color_range)
}
function MBT_CorrectMatrix(clip c, string "input_matrix", string "output_matrix")
{
input_matrix = Default(input_matrix, MBT_GetMatrixName(c))
output_matrix = Default(output_matrix, MBT_DefaultMatrix(c.Width, c.Height))
input_matrix = Default(input_matrix, mbt_color_matrix)
output_matrix = Default(output_matrix, MBT_DefaultOutputMatrix(c.Width, c.Height))
was_hdr = MBT_IsHDR()
Assert(!was_hdr || input_matrix == output_matrix,
\ "MBT_CorrectMatrix does not convert HDR color systems. Use MBT_ToSDR or an explicit HDR conversion.")
if (!was_hdr) {
global mbt_color_matrix = output_matrix
global mbt_color_primaries = MBT_PrimariesForMatrix(output_matrix)
global mbt_color_transfer = 1
}
return was_hdr ? c.MBT_SetMatrix(input_matrix) : c.MBT_CorrectSDRMatrix(input_matrix, output_matrix)
}
function MBT_CorrectSDRMatrix(clip c, string input_matrix, string output_matrix)
{
matrix = MBT_MatrixCodeForConvert(input_matrix) + ":auto=>" + MBT_MatrixCodeForConvert(output_matrix) + ":same"
bitdepth = c.BitsPerComponent
return c.IsRGB ? c
\ : (c.Is420 ? c.ConvertToYUV420(matrix=matrix).ConvertBits(bitdepth).MBT_SetMatrix(output_matrix)
\ : (c.Is422 ? c.ConvertToYUV422(matrix=matrix).ConvertBits(bitdepth).MBT_SetMatrix(output_matrix)
\ : (c.Is444 ? c.ConvertToYUV444(matrix=matrix).ConvertBits(bitdepth).MBT_SetMatrix(output_matrix) : c.MBT_SetMatrix(output_matrix))))
\ : (c.Is420 ? c.ConvertToYUV420(matrix=matrix).ConvertBits(bitdepth).PropCopy(c).MBT_SetSDRColor(output_matrix, mbt_color_range)
\ : (c.Is422 ? c.ConvertToYUV422(matrix=matrix).ConvertBits(bitdepth).PropCopy(c).MBT_SetSDRColor(output_matrix, mbt_color_range)
\ : (c.Is444 ? c.ConvertToYUV444(matrix=matrix).ConvertBits(bitdepth).PropCopy(c).MBT_SetSDRColor(output_matrix, mbt_color_range) : c.MBT_SetSDRColor(output_matrix, mbt_color_range))))
}
function MBT_ToSDR(clip c, float "hdr_peak", float "sdr_peak", string "method", float "exposure", bool "chroma_correction", string "output_matrix")
{
transfer = MBT_GetTransferCode(c)
primaries = MBT_GetPrimariesCode(c)
color_range = MBT_GetRangeCode(c)
hdr_mode = transfer == 18 ? 2 : 0
Assert(transfer == 16 || transfer == 18, "MBT_ToSDR requires PQ or HLG input.")
Assert(!c.IsRGB, "MBT_ToSDR currently requires planar YUV input.")
Assert(primaries == 9,
\ "MBT_ToSDR currently requires Rec2020 primaries. P3 HDR needs mastering-primary support.")
method = Default(method, "MPC")
Assert(
\ method == "MPC" || method == "Hable" || method == "Mobius" || method == "Reinhard" || method == "ACES" || method == "BT2446C",
\ "MBT_ToSDR method must be MPC, Hable, Mobius, Reinhard, ACES, or BT2446C.")
chroma_correction = Default(chroma_correction, false)
hdr_peak = Default(hdr_peak, hdr_mode == 0 ? 10000.0 : 1000.0)
sdr_peak = Default(sdr_peak, 125.0)
output_matrix = Default(output_matrix, MBT_DefaultMatrix(c.Width, c.Height))
return method == "BT2446C"
\ ? c.MBT_ToSDR_BT2446C(hdr_mode, color_range, hdr_peak, sdr_peak, chroma_correction, output_matrix)
\ : method == "MPC"
\ ? c.MBT_ToSDR_MPC(hdr_mode, color_range, hdr_peak, sdr_peak, exposure, output_matrix)
\ : c.MBT_ToSDR_RGB(hdr_mode, color_range, method, exposure, output_matrix)
}
function MBT_ToSDR_BT2446C(clip c, int hdr_mode, int color_range, float hdr_peak, float sdr_peak, bool chroma_correction, string output_matrix)
{
Assert(FunctionExists("ConvertYUVtoXYZ") && FunctionExists("ConvertXYZtoYUV") && FunctionExists("ConverXYZ_BT2446_C_HDRtoSDR"),
\ "MBT_ToSDR requires HDRTools. Load HDRTools before calling it.")
output_color = MBT_SDRColorForMatrix(output_matrix)
linear = c.ConvertYUVtoXYZ(Color=0, OutputMode=2, HDRMode=hdr_mode, OOTF=false, fullrange=color_range == 1)
linear = linear.ConverXYZ_BT2446_C_HDRtoSDR(ChromaC=chroma_correction, PQMode=hdr_mode == 0, Lhdr=hdr_peak, Lsdr=sdr_peak, pColor=0)
result = linear.ConvertXYZtoYUV(Color=output_color, OutputMode=MBT_YuvOutputMode(c), OOTF=false, fullrange=false, pColor=0)
return result.MBT_SetToSDRProperties(c, output_matrix)
}
function MBT_ToSDR_RGB(clip c, int hdr_mode, int color_range, string method, float "exposure", string "output_matrix")
{
Assert(FunctionExists("ConvertYUVtoLinearRGB") && FunctionExists("ConvertRGBtoXYZ") && FunctionExists("ConvertXYZtoYUV"),
\ "MBT_ToSDR requires HDRTools. Load HDRTools before calling it.")
Assert(
\ (method == "Hable" && FunctionExists("ConvertRGB_Hable_HDRtoSDR")) ||
\ (method == "Mobius" && FunctionExists("ConvertRGB_Mobius_HDRtoSDR")) ||
\ (method == "Reinhard" && FunctionExists("ConvertRGB_Reinhard_HDRtoSDR")) ||
\ (method == "ACES" && FunctionExists("ConvertRGB_ACES_HDRtoSDR")),
\ "MBT_ToSDR method is not provided by the loaded HDRTools plugin.")
linear = c.MBT_LinearHDR(hdr_mode, color_range)
scale = Defined(exposure) ? exposure : (method == "ACES" ? 1.0 : 2.0)
mapped = method == "Hable" ? linear.ConvertRGB_Hable_HDRtoSDR(exposure_R=scale)
\ : method == "Mobius" ? linear.ConvertRGB_Mobius_HDRtoSDR(exposure_R=scale)
\ : method == "Reinhard" ? linear.ConvertRGB_Reinhard_HDRtoSDR(exposure_R=scale)
\ : linear.ConvertRGB_ACES_HDRtoSDR(exposure_R=scale)
xyz = mapped.ConvertRGBtoXYZ(Color=1, OOTF=false, EOTF=false)
result = xyz.ConvertXYZtoYUV(Color=MBT_SDRColorForMatrix(output_matrix), OutputMode=MBT_YuvOutputMode(c), OOTF=false, fullrange=false, pColor=1)
return result.MBT_SetToSDRProperties(c, output_matrix)
}
function MBT_ToSDR_MPC(clip c, int hdr_mode, int color_range, float hdr_peak, float sdr_peak, float "exposure", string "output_matrix")
{
Assert(FunctionExists("ConvertYUVtoLinearRGB") && FunctionExists("ConvertRGBtoXYZ") && FunctionExists("ConvertXYZtoRGB") && FunctionExists("ConvertLinearRGBtoYUV"),
\ "MBT_ToSDR requires HDRTools. Load HDRTools before calling it.")
scale = Defined(exposure) ? exposure : hdr_peak / sdr_peak
output_color = MBT_SDRColorForMatrix(output_matrix)
linear = c.MBT_LinearHDR(hdr_mode, color_range)
xyz = linear.ConvertRGBtoXYZ(Color=1, OOTF=false, EOTF=false)
xyz = xyz.MBT_HableLuminance(scale)
rgb = xyz.ConvertXYZtoRGB(Color=output_color, OutputMode=0, OOTF=false, EOTF=false, pColor=1)
rgb = rgb.MBT_GamutMap(output_matrix)
result = rgb.ConvertLinearRGBtoYUV(Color=output_color, OutputMode=MBT_YuvOutputMode(c), OOTF=false, EOTF=true, fullrange=false)
return result.MBT_SetToSDRProperties(c, output_matrix)
}
function MBT_HableLuminance(clip xyz, float exposure)
{
luminance = CombinePlanes(xyz, planes="Y", source_planes="G", pixel_type="Y32")
mapped = luminance.Expr(MBT_HableExpression(exposure), format="Y32", clamp_float=true)
scale = Expr(luminance, mapped, "y x 0.000001 max /", format="Y32", clamp_float=false)
scale = MergeRGB(scale, scale, scale, pixel_type="RGBPS")
return Expr(xyz, scale, "x y *", "x y *", "x y *", format="RGBPS", clamp_float=false).PropCopy(xyz)
}
function MBT_HableExpression(float exposure)
{
return "x " + String(exposure) + " * T^ " + \
"T T 0.15 * 0.05 + * 0.004 + N^ " + \
"N T T 0.15 * 0.5 + * 0.06 + / 0.06666666666666667 - " + String(MBT_HableWhiteScale()) + " * 0 max 1 min"
}
function MBT_GamutMap(clip rgb, string matrix)
{
red = CombinePlanes(rgb, planes="Y", source_planes="R", pixel_type="Y32")
green = CombinePlanes(rgb, planes="Y", source_planes="G", pixel_type="Y32")
blue = CombinePlanes(rgb, planes="Y", source_planes="B", pixel_type="Y32")
luma = MBT_LumaExpression(matrix)
return Expr(
\ red, green, blue,
\ MBT_GamutExpression("x", luma),
\ MBT_GamutExpression("y", luma),
\ MBT_GamutExpression("z", luma),
\ format="RGBPS",
\ clamp_float=true
\).PropCopy(rgb)
}
function MBT_LumaExpression(string matrix)
{
return matrix == "Rec601" ? "x 0.299 * y 0.587 * + z 0.114 * +"
\ : matrix == "Rec2020" ? "x 0.2627 * y 0.678 * + z 0.0593 * +"
\ : "x 0.2126 * y 0.7152 * + z 0.0722 * +"
}
function MBT_GamutExpression(string channel, string luma)
{
return luma + " L^ " + \
"x y min z min N^ x y max z max X^ " + \
"X 1 > 1 L - X L - / 1 ? S^ " + \
"N 0 < L L N - / 1 ? T^ S T min S^ " + \
channel + " L - S * L +"
}
function MBT_HableWhiteScale()
{
# 1 / hable(4.8), using the normalized Hable curve constants.
return 1.7896902226524685
}
function MBT_SetToSDRProperties(clip c, clip source, string output_matrix)
{
global mbt_color_matrix = output_matrix
global mbt_color_primaries = MBT_PrimariesForMatrix(output_matrix)
global mbt_color_transfer = 1
global mbt_color_range = 0
return c.ConvertBits(source.BitsPerComponent).PropCopy(source).MBT_SetSDRColor(output_matrix, 0)
}
function DeShake(clip c, int "w", int "h", float "dar", float "crop", float "xcrop", float "ycrop", int "pos", bool "zoom", bool "rot", float "freq")