687 lines
32 KiB
Plaintext
687 lines
32 KiB
Plaintext
# media-batch-tools Avisynth+ helpers.
|
|
#
|
|
# This file is imported by generated hidden source scripts. Keep functions here
|
|
# small and dependency-light unless the visible scripts explicitly document the
|
|
# required plugins.
|
|
|
|
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.
|
|
# 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).\
|
|
propSet("mbt_frame_duration_seconds", mbt_frame_duration_seconds).\
|
|
propSet("mbt_frame_duration", mbt_frame_duration)
|
|
"""
|
|
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)
|
|
return AudioDub(video, audio)
|
|
}
|
|
|
|
function MBT_Drop(clip c, int start, int "end")
|
|
{
|
|
return MBT_SetDrop(c, 1, start, end)
|
|
}
|
|
|
|
function MBT_Undrop(clip c, int start, int "end")
|
|
{
|
|
return MBT_SetDrop(c, 0, start, end)
|
|
}
|
|
|
|
function MBT_SetDrop(clip c, int value, int start, int "end")
|
|
{
|
|
end = Default(end, -1)
|
|
end = (end < 0) ? start - end - 1 : end
|
|
runtime = """
|
|
(current_frame >= """ + String(start) + """ && current_frame <= """ + String(end) + """) ? \
|
|
last.propSet("mbt_drop_frame", """ + String(value) + """) : last
|
|
"""
|
|
return c.ScriptClip(runtime)
|
|
}
|
|
|
|
function MBT_DropEvery(clip c, int cycle, int "offset0", int "offset1", int "offset2", int "offset3", int "offset4", int "offset5", int "offset6", int "offset7", int "offset8", int "offset9", int "offset10", int "offset11", int "offset12", int "offset13", int "offset14", int "offset15")
|
|
{
|
|
return MBT_SetDropEvery(c, 1, cycle, offset0, offset1, offset2, offset3, offset4, offset5, offset6, offset7, offset8, offset9, offset10, offset11, offset12, offset13, offset14, offset15)
|
|
}
|
|
|
|
function MBT_UndropEvery(clip c, int cycle, int "offset0", int "offset1", int "offset2", int "offset3", int "offset4", int "offset5", int "offset6", int "offset7", int "offset8", int "offset9", int "offset10", int "offset11", int "offset12", int "offset13", int "offset14", int "offset15")
|
|
{
|
|
return MBT_SetDropEvery(c, 0, cycle, offset0, offset1, offset2, offset3, offset4, offset5, offset6, offset7, offset8, offset9, offset10, offset11, offset12, offset13, offset14, offset15)
|
|
}
|
|
|
|
function MBT_SetDropEvery(clip c, int value, int cycle, int "offset0", int "offset1", int "offset2", int "offset3", int "offset4", int "offset5", int "offset6", int "offset7", int "offset8", int "offset9", int "offset10", int "offset11", int "offset12", int "offset13", int "offset14", int "offset15")
|
|
{
|
|
expr = "false"
|
|
expr = Defined(offset0) ? expr + " || (current_frame % " + String(cycle) + " == " + String(offset0) + ")" : expr
|
|
expr = Defined(offset1) ? expr + " || (current_frame % " + String(cycle) + " == " + String(offset1) + ")" : expr
|
|
expr = Defined(offset2) ? expr + " || (current_frame % " + String(cycle) + " == " + String(offset2) + ")" : expr
|
|
expr = Defined(offset3) ? expr + " || (current_frame % " + String(cycle) + " == " + String(offset3) + ")" : expr
|
|
expr = Defined(offset4) ? expr + " || (current_frame % " + String(cycle) + " == " + String(offset4) + ")" : expr
|
|
expr = Defined(offset5) ? expr + " || (current_frame % " + String(cycle) + " == " + String(offset5) + ")" : expr
|
|
expr = Defined(offset6) ? expr + " || (current_frame % " + String(cycle) + " == " + String(offset6) + ")" : expr
|
|
expr = Defined(offset7) ? expr + " || (current_frame % " + String(cycle) + " == " + String(offset7) + ")" : expr
|
|
expr = Defined(offset8) ? expr + " || (current_frame % " + String(cycle) + " == " + String(offset8) + ")" : expr
|
|
expr = Defined(offset9) ? expr + " || (current_frame % " + String(cycle) + " == " + String(offset9) + ")" : expr
|
|
expr = Defined(offset10) ? expr + " || (current_frame % " + String(cycle) + " == " + String(offset10) + ")" : expr
|
|
expr = Defined(offset11) ? expr + " || (current_frame % " + String(cycle) + " == " + String(offset11) + ")" : expr
|
|
expr = Defined(offset12) ? expr + " || (current_frame % " + String(cycle) + " == " + String(offset12) + ")" : expr
|
|
expr = Defined(offset13) ? expr + " || (current_frame % " + String(cycle) + " == " + String(offset13) + ")" : expr
|
|
expr = Defined(offset14) ? expr + " || (current_frame % " + String(cycle) + " == " + String(offset14) + ")" : expr
|
|
expr = Defined(offset15) ? expr + " || (current_frame % " + String(cycle) + " == " + String(offset15) + ")" : expr
|
|
runtime = """
|
|
(""" + expr + """) ? last.propSet("mbt_drop_frame", """ + String(value) + """) : last
|
|
"""
|
|
return c.ScriptClip(runtime)
|
|
}
|
|
|
|
function MBT_Info(clip c, int "size", int "align")
|
|
{
|
|
# Overlay media-batch-tools frame properties for inspection while editing.
|
|
size = Default(size, 16)
|
|
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 + Chr(10) + \
|
|
"Color: " + matrix + ", " + transfer_name + ", " + range_name
|
|
Subtitle(last, text, size=""" + String(size) + """, align=""" + String(align) + """)
|
|
"""
|
|
return c.ScriptClip(runtime)
|
|
}
|
|
|
|
function Cropf(clip c, float l, float t, float w, float h)
|
|
{
|
|
return c.MBT_LinearResize(
|
|
\ Round(c.Width * w),
|
|
\ Round(c.Height * h),
|
|
\ c.Width * l,
|
|
\ c.Height * t,
|
|
\ c.Width * w,
|
|
\ c.Height * h
|
|
\)
|
|
}
|
|
|
|
function RotateCrop(clip c, float angle, float "dar")
|
|
{
|
|
dar = Default(dar, Float(c.Width) / Float(c.Height))
|
|
radians = angle * Pi() / 180.0
|
|
cosine = Abs(Cos(radians))
|
|
sine = Abs(Sin(radians))
|
|
|
|
crop_h = Min(
|
|
\ Float(c.Height),
|
|
\ Float(c.Width) / dar,
|
|
\ Float(c.Width) / (dar * cosine + sine),
|
|
\ Float(c.Height) / (dar * sine + cosine)
|
|
\)
|
|
crop_w = crop_h * dar
|
|
|
|
xalign = (c.Is420 || c.Is422) ? 2 : 1
|
|
yalign = c.Is420 ? 2 : 1
|
|
left = Ceil((c.Width - crop_w) / (2.0 * xalign)) * xalign
|
|
top = Ceil((c.Height - crop_h) / (2.0 * yalign)) * yalign
|
|
|
|
turned = c.Turn(angle=angle)
|
|
return turned.Crop(left, top, c.Width - 2 * left, c.Height - 2 * top)
|
|
}
|
|
|
|
function Resize(clip v, int "w", int "h", float "dar", float "xcrop", float "ycrop", int "pos", bool "linear", string "input_matrix", string "output_matrix")
|
|
{
|
|
# Resize with optional aspect-ratio crop.
|
|
# w/h: target size. dar: display aspect ratio when one dimension is omitted.
|
|
# xcrop/ycrop: percent cropped from each side before fitting. pos uses keypad
|
|
# placement: 1 top-left, 5 center, 9 bottom-right.
|
|
wo = v.Width
|
|
ho = v.Height
|
|
|
|
xcrop = Default(xcrop, 0.0) / 100.0
|
|
ycrop = Default(ycrop, 0.0) / 100.0
|
|
pos = Default(pos, 5)
|
|
pos = (pos < 1 || pos > 9) ? 5 : pos
|
|
|
|
dar = Default(dar, Float(wo) / Float(ho))
|
|
w = Defined(w) ? w : (Defined(h) ? Round(h * 0.5 * dar) * 2 : wo)
|
|
h = Defined(h) ? h : Round(w * 0.5 / dar) * 2
|
|
dar = Float(w) / Float(h)
|
|
|
|
cropped_w = (1.0 - 2.0 * xcrop) * wo
|
|
cropped_h = (1.0 - 2.0 * ycrop) * ho
|
|
|
|
crop_extra_w = (cropped_w / cropped_h > dar) ? (cropped_w - cropped_h * dar) : 0.0
|
|
crop_extra_h = (cropped_w / cropped_h < dar) ? (cropped_h - cropped_w / dar) : 0.0
|
|
|
|
crop_extra_t = crop_extra_h * ((pos <= 3) ? 0.0 : ((pos >= 7) ? 1.0 : 0.5))
|
|
crop_extra_b = crop_extra_h * ((pos >= 7) ? 0.0 : ((pos <= 3) ? 1.0 : 0.5))
|
|
crop_extra_l = crop_extra_w * ((pos % 3 == 1) ? 0.0 : ((pos % 3 == 0) ? 1.0 : 0.5))
|
|
crop_extra_r = crop_extra_w * ((pos % 3 == 0) ? 0.0 : ((pos % 3 == 1) ? 1.0 : 0.5))
|
|
|
|
crop_l = wo * xcrop + crop_extra_l
|
|
crop_t = ho * ycrop + crop_extra_t
|
|
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_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)
|
|
}
|
|
|
|
function MBT_LinearResize(clip src, int tw, int th,
|
|
\ float "src_left", float "src_top", float "src_width", float "src_height",
|
|
\ string "kernel", float "b", float "c", int "taps", float "p", int "bitdepth",
|
|
\ string "hkernel", string "vkernel", bool "linear", string "input_matrix", string "output_matrix")
|
|
{
|
|
uscale = "Spline36"
|
|
dscale = "CatmullRom"
|
|
|
|
hkernel = Default(hkernel , Default(kernel , (tw > src.Width ? uscale : dscale)))
|
|
vkernel = Default(vkernel , Default(kernel , (th > src.Height ? uscale : dscale)))
|
|
sl = Default(src_left , 0)
|
|
st = Default(src_top , 0)
|
|
sw = Default(src_width , src.Width)
|
|
sh = Default(src_height , src.Height)
|
|
b = Default(b , 1.0 / 3.0)
|
|
c = Default(c , 1.0 / 3.0)
|
|
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_color_matrix)
|
|
output_matrix = Default(output_matrix, MBT_DefaultOutputMatrix(tw, th))
|
|
|
|
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
|
|
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)
|
|
\ : (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)
|
|
}
|
|
|
|
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)
|
|
{
|
|
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"
|
|
\ : (matrix == 9) ? "Rec2020"
|
|
\ : (matrix == 10) ? "Rec2020"
|
|
\ : (matrix == 5) ? "Rec601"
|
|
\ : (matrix == 6) ? "Rec601"
|
|
\ : ""
|
|
}
|
|
|
|
function MBT_MatrixCodeForConvert(string matrix)
|
|
{
|
|
return (matrix == "Rec601") ? "601"
|
|
\ : (matrix == "Rec709") ? "709"
|
|
\ : (matrix == "Rec2020") ? "2020"
|
|
\ : matrix
|
|
}
|
|
|
|
function MBT_MatrixInt(string matrix)
|
|
{
|
|
return (matrix == "Rec709") ? 1
|
|
\ : (matrix == "Rec2020") ? 9
|
|
\ : (matrix == "Rec601") ? 6
|
|
\ : 2
|
|
}
|
|
|
|
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_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).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")
|
|
{
|
|
# Stabilize through MVTools + DePan functions. Those plugins must be loaded
|
|
# in Avisynth before this function is called.
|
|
crop = Default(crop, 2.5)
|
|
xcrop = Default(xcrop, crop)
|
|
ycrop = Default(ycrop, crop)
|
|
zoom = Default(zoom, true)
|
|
rot = Default(rot, true)
|
|
freq = Default(freq, 1.0)
|
|
|
|
vectors = c.MSuper().MAnalyse(isb=false, dct=5)
|
|
mdata = MDepan(c, vectors, thscd1=1000, thscd2=1000, zoom=zoom, rot=rot, error=255)
|
|
stabilized = DePanStabilize(
|
|
\ c,
|
|
\ data=mdata,
|
|
\ dxmax=c.Width * xcrop / 100.0,
|
|
\ dymax=c.Height * ycrop / 100.0,
|
|
\ rotmax=3.0,
|
|
\ mirror=15,
|
|
\ cutoff=freq
|
|
\)
|
|
return (Defined(w) || Defined(h) || Defined(dar)) ? Resize(stabilized, w, h, dar, xcrop, ycrop, pos) : stabilized
|
|
}
|
|
|
|
function FixContrast(clip c, int "low", int "high")
|
|
{
|
|
# Map input luma range to limited-range video luma.
|
|
low = Default(low, 16)
|
|
high = Default(high, 235)
|
|
return c.Levels(low, 1.0, high, 16, 235, coring=false)
|
|
}
|