### basic utility functions; most require AVS+ ## v0.41 2017-05 raffriff42 ## v0.41 bugfix 6 add remove_gamma, restore_gamma; fix sc8f bug; new EvalShow args. ## v0.41 bugfix 8 remove unused scnf(), add ColorbarsHD2(clip T, ...) ## v0.41 bugfix 9 SplitLines: preserve existing line breaks ## v0.41 bugfix 10 vdubFM, MatchColorFormat ## v0.41 bugfix 11 misc. improvements ## v0.41 bugfix 12 ScaleSize gamma optional; improved gamma filters ### CONFIG SECTION ## vector graticule(s): full name = + + ".png" ## ## Example actual image path: "E:\images\misc\Vectorscope_graticule-box-Rec709.png" ## (in which case you would set to "E:\images\misc\") ## #global vector_graticule_image_base = pathMisc + "Vectorscope_graticule-box-" ####################################################################################### ### TABLE OF CONTENTS ### http://avisynth.nl/index.php/User:Raffriff42/Utils-r41_Quick_Reference ####################################################################################### ### STRING FUNCTIONS ##### Split long lines for Subtitle line wrap; #@ function SplitLines(string s, int "lastcol", int "cc") ##### SplitLines helper: trim spaces from left side of string #@ function TrimLeft(string s) #@ function GetNameFromPath(string path) #@ function GetParentFolder(string path) ### NUMERIC FUNCTIONS ##### return argument as integer and ensure it is modulo #@ function _mod(int m, float f) ##### format a Float as a String with (by default) 2 decimals #@ function StrinF(float f, int "decimals") ##### Hex() with leading "0" if less than 2 chars long #@ function Hex2(int i) ##### bugfixed IsFloat #@ function IsReallyFloat(val v) ##### given Y, U, V [, A], return an Avisynth color_yuv #@ function MakeYUVColor(float y, float u, float v, float "a") ##### given R, G, B [, A], return an Avisynth color (AVS-) #@ function MakeRGBColor(float r, float "g", float "b", float "a") ##### given Avisynth color, set its transparency (AVS-) #@ function transparent_color(float a, int color) ####### scale "normal" float arguments to ##### ColorYUV's "gain_x", "gamma_x" & "cont_x" (AVS*) #@ function f2c(float f) ##### MatchAudioFormat helper: Greatest Common Divisor #@ function _gcd(int x, int y) ##### given new height, calulate new width, preserving aspect ratio #@ function CalcWidth(clip C, int new_hgt, int "mod", int "lim") ##### given new width, calulate new height, preserving aspect ratio #@ function CalcHeight(clip C, int new_wid, int "mod", int "lim") ### DEEP COLOR ARGUMENT SCALING FUNCTIONS ##### return TV-black level for given bit depth #@ function getTVBlack(int bits) ##### return integer fullscale value for given bit depth (mostly internal use) #@ function getFullscale(int bits) ##### scale a value from one fullscale range to another #@ function scff(int fs_in, int fs_out, float f, bool "cx") ##### scale a value from one bit depth to another #@ function scbf(int bits_in, int bits_out, float f, bool "cx") ##### scale a value from one bit depth to another; clamp output #@ function scbx(int bits_in, int bits_out, float f) ##### scale a value from one bit depth to another; string result #@ function scbs(int bits_in, int bits_out, float f, int "decimals") ##### scale an 8-bit value for target clip #@ function sc8f(clip T, float f, bool "cx") ##### scale an 8-bit value for target clip ; clamp output #@ function sc8x(clip T, float f) ##### scale an 8-bit value for target clip ; string result #@ function sc8s(clip T, float f, int "decimals") ### UTILITY FILTERS ##### Convert from anything to planar RGB(A) #@ function ToRGB(clip C, string "matrix", int "bits_out", clip "A") ##### Convert from anything to YUV(A)444 #@ function To444(clip C, string "matrix", int "bits_out", clip "A") ##### Convert from anything to best equivalent 16-bit version #@ function To16bit(clip C, clip "A") ##### Convert from anything to 'best' (v2.6.x compatible) equivalent 8-bit version #@ function To8bit(clip C, bool "dither") ##### make changes needed to display on vdubFM (VirtualDub FilterMod) #@ function ToVdubFM(clip C, bool "ditherto8bit") ##### Match color format of source C to template T (AVS+) #@ function MatchColorFormat(clip C, clip T, string "matrix", bool "matchbits", bool "dither") ##### Match audio properties of source C to template T #@ function MatchAudioFormat(clip C, clip T, bool "allowresample") ##### transport RGB in YV24: G=>Y, B=>U, R=>V (AVS+) #@ function GBR2YUV(clip C, bool "yuva") ##### transport YV24 in RGB: Y=>G, U=>B, V=>R (AVS+) #@ function YUV2GBR(clip C, clip "A", bool "rgb32") ##### PC->TV #@ function PC2TV(clip C) ###### TV->PC #@ function TV2PC(clip C) ##### Stack clip horizontally until it is at least wide #@ function stack_to_wid(clip C, int wid, clip "R", bool "flip") ##### Stack clip vertically until it is at least high #@ function stack_to_hgt(clip C, int hgt, clip "R", bool "flip") ##### Stack clip until it is x #@ function stack_to_size(clip C, int wid, int hgt, bool "flip") ##### remove gamma transfer function (if bit depth > 8) #@ function remove_gamma(clip C, bool enable) ##### apply standard gamma transfer function (if bit depth > 8) #@ function restore_gamma(clip C, bool enable) ### COLOR AND OVERLAY FILTERS ##### scale 0-255 Levels arguments to current bit depth (AVS+) #@ function LevelsPlus(clip C, ##\ int input_low, float gamma, int input_high, ##\ int output_low, int output_high, ##\ bool "coring", bool "dither") ##### Overlay with support for parameter ##### and adding borders/letterboxing to inset clip #@ function OverlayAligned(clip base, clip over, ##\ int "x", int "y", clip "mask", float "opacity", string "mode", ##\ bool "greymask", string "output", bool "ignore_conditional", ##\ bool "pc_range", int "align", int "borderwidth", int "bordercolor") ### TIMELINE FILTERS ##### "slip" a clip in time. #@ function Slip(clip C, int offset) ##### change speed over a wide range ##### with frame blending in fast forward by default. #@ function ChangeSpeed( ##\ clip C, float factor, bool "pitchfix", bool "noblend") #@ function ChangeAudioSpeed( ##\ clip C, float factor, bool "pitchfix") ### CROP, RESIZE AND TRANSFORM FILTERS ##### Crop with =true by default; optionally enforce Mod #@ function Cropa(clip C, int x, int y, int wid, int hgt, bool "align", int "mod") ##### show a diagnostic string on Crop failure; optionally enforce Mod #@ function Cropd(clip C, int x, int y, int wid, int hgt, bool "align", int "mod") ### switch (or fade) between three Resize clips depending on scale factor (AVS+) #@ function ScaleSize(clip C, float factor, int mod, ##\ string "sm", string "med", string "lg", ##\ float "thrSm", float "thrLg", bool "fade", bool "ident", bool "gamma") #@ function ScaleSize(clip C, float fwid, float fhgt, int mod, ##\ string "sm", string "med", string "lg", ##\ float "thrSm", float "thrLg", bool "fade", bool "ident", bool "gamma") ##### crop to size with smooth animated pan & tilt, expressed as percentage (center @ pan=0, tilt=0) #@ function CropPanTilt(clip C, int wid, int hgt, float "pan", float "tilt") ##### crop to size with smooth animated pan & tilt, expressed as pixels (center @ off_x=0, off_y=0) #@ function CropShift(clip C, int wid, int hgt, float "off_x", float "off_y") ##### symmetrically crop or expand a clip to ensure it is a certain size, ##### with size forced to mod-4, mod-8 etc #@ function CropEx(clip C, float wid, float hgt, int "mod", ## int "border", string "mode", bool "debug") #@ function AddRepeatBorders(clip C, int "bdrx", int "bdry") #@ function Padding(clip C, int left, int top, int right, int bottom) #@ function MirrorBorders(clip C, int left, int top, int right, int bottom) #@ function BlurBorders(clip C, int left, int top, int right, int bottom) ### shift a clip up-down and left-right (sub-pixel precision); #@ function Shift(clip C, float offh, float offv) ##### shift Chroma relative to Luma #@ function ShiftUV(clip C, float "ux", float "uy", float "vx", float "vy") ##### shift and/or resize Red and Blue relative to Green ## @ rxd, ryd, bxd, byd - "delta" width & height #@ function ShiftRedBlue(clip C, ##\ float "rx", float "ry", ##\ float "bx", float "by", ##\ float "rxd", float "ryd", ##\ float "bxd", float "byd") ### DEBUGGING FILTERS ##### for clip identification, show a short text string, very large and semitransparent. #@ function bigsub(clip C, string msg, float "sizeMult", bool "enable") ##### execute a script snippet; show script & result on screen #@ function EvalShow(clip C, string s, float "size", int "align", float "opacity") ##### show a mask clip overlaid on another clip for visualization #@ function ShowMask(clip C, clip M, String "mode", float "opacity") ## @ mode (""|"luma"|"cyan") default = "" ##### ColorbarsHD2 with clip properties adjusted to match template T #@ function ColorbarsHD2(clip T, float "tonedb") ##### ColorbarsHD with optional RGB & deep color output; size & audio level changes ##### (1280x720, TV-range, 29.97 fps, 1 hour long, audio tone @ -18dB, 48kz float) #@ function ColorbarsHD2(int "width", int "height", bool "rgb_out", ##\ int "bits_out", int "bits_gen", bool "dither", float "tonedb") ##### return grayscale ramp clip w/ same specs as template clip (AVS+) #@ function Grayramp(clip T, int "height", bool "dither") ##### return grayscale ramp (cropped out from ColorbarsHD2) #@ function Grayramp(int "width", int "height", bool "rgb_out", int "bits_out", bool "dither") ##### show waveform + vectorscope; accepts RGB #@ function VideoScopes(clip C, bool "shrink", bool "vector", string "matrix", bool "parade") ##### print color channel statistics on the screen #@ function Analyze(clip C) ##### overlay a user-supplied histogram clip #@ function HistogramOverlay(clip B, clip F, bool "autocrop", int "align", ##\ int "x", int "y", int "wid", int "hgt", int "margin", ##\ float "opacity", string "mode", bool "pc_range") ####### Classic Histogram, waveform on top #@ function HistogramTurn(clip C, bool "parade") ##### 8-bit Histogram for deep color #@ function Histogram8(clip C, string "mode", float "factor") ##### show original & 3 channels in quad split #@ function ShowChannels(clip C, bool "analyze") ##### ShowFrameNumber with support for opacity and "align" ##### ("scroll" always false unless align<0) #@ function ShowFrameNumberAligned( ##\ clip C, bool "scroll", int "offset", ##\ float "x", float "y", string "font", int "size", ##\ int "text_color", int "halo_color", ##\ float "font_width", float "font_angle", ##\ float "opacity", int "align") ##### make side-by-side split clip (normally, for comparison or preview purposes) #@ function StackTwoAcrossCropped(clip A, clip B, clip "S", ##\ int "border", float "pan", int "wid", ##\ string "titleA", string "titleB", int "align") ####################################################################################### ### STRING FUNCTIONS ####################################################################################### ################################## ### Split long lines for [[Subtitle]] line wrap; ## tries to split on word boundaries ## with a /very/ basic algorithm. ## ## Example: ## | Subtitle(SplitLines(longstring, 80), lsp=0) ## (remember to add the argument) ## ## @ lastcol - maximum line length before breaking (default 80) ## @ cc - line count, for internal use; leave it alone ## ## For paragraph text, you may want to remove line breaks from source argument with: ## | ReplaceStr(Chr(10), "") ## | ReplaceStr(Chr(13), " ") ## By default, SplitLines preserves existing line breaks. ## ## On return, global variable *splitlines_count* ## is set to the number of lines after splitting; ## this will be needed if you want to bottom-align. ## NOTE only valid if line breaks have been removed as shown above. ## ## version 1.1 2017-05-15 preserve existing line breaks ## function SplitLines(string s, int "lastcol", int "cc") { lastcol = Default(lastcol, 80) cc = Default(cc, 1) global splitlines_count = cc ## dumb split: no word breaks #return (StrLen(s)0) \ ? SplitLines(LeftStr(s, n+0), lastcol) + SplitLines(MidStr(s, n+1), lastcol) \ : (StrLen(s) as integer and ensure it is modulo ## ## @ m - mod value ## (if negative, round UP, else round down) ## @ f - input (may be positive or negative) ## function _mod(int m, float f) { x = (m>0) ? -1 : 1 m = Max(1, Abs(m)) sg = Sign(f) i = Round(Abs(f)) return sg * Max(0, i + x * (i % m)) } ####################################### ### format a Float as a String with (by default) 2 decimals ## function StrinF(float f, int "decimals") { decimals = Min(Max(0, Default(decimals, 2)), 8) return String(f, "%0."+String(decimals)+"f") } ################################## ### Hex() with leading "0" if less than 2 chars long ## function Hex2(int i) { s = Hex(i) return (StrLen(s)==1) ? "0"+s : s } # http://forum.videohelp.com/threads/382601-CropResize-Script?p=2478303#post2478303 ################################## ### bugfixed [[IsFloat]] ### see http://avisynth.nl/index.php/Internal_functions#IsFloat ## function IsReallyFloat(val v) { s = "" try { s = Hex(v) [* fails on float arguments! *] } catch(err_msg) { s = "true" } return (IsFloat(v)) \ ? (s=="true") [* s<>"true" if Hex succeeds *] \ : false [* eliminate clips, strings, booleans *] } ####################################### ### given , , [, ] return an Avisynth [[color_yuv]] (AVS*) ## ## @ a - 0.0 = transparent, 1.0 = opaque. ## function MakeYUVColor(float y, float u, float v, float "a") { y = Min(Max(0, Round(y)), 255) u = Min(Max(0, Round(u)), 255) v = Min(Max(0, Round(v)), 255) a = 255 - Min(Max(0, Round(Default(a, 1.0) * 256.0)), 255) return (a*$1000000) + (y*$10000) + (u*$100) + v } ####################################### ### given , , [, ] return an Avisynth color (AVS*) ## ## @ r, g, b - 0..255 ## @ a - 0.0 = transparent, 1.0 = opaque. ## function MakeRGBColor(float r, float "g", float "b", float "a") { r = Min(Max(0, Round(r)), 255) ## thanks Gavino g = Min(Max(0, Round(Default(g, r))), 255) b = Min(Max(0, Round(Default(b, g))), 255) a = 255 - Min(Max(0, Round(Default(a, 1.0) * 256.0)), 255) return (a*$1000000) + (r*$10000) + (g*$100) + b } ####################################### ## given Avisynth color, set its transparency (AVS*) ## @ a - 0.0 = transparent, 1.0 = opaque. ## function transparent_color(float a, int color) { a = 255 - Min(Max(0, Round(a * 256.0)), 255) return BitOr(a*$1000000, BitAnd(color, $00ffffff)) } ####################################### ### scale "normal" float arguments to ### [[ColorYUV]]'s , & (AVS*) ## function f2c(float f) { return Round((f - 1.0) * 256.0) } # http://users.telenet.be/darnley/avisynth/mine.html ################################## ### MatchAudioFormat helper: Greatest Common Divisor ## (called by MatchAudioFormat to help find if SSRC should be called) ## function _gcd(int x, int y) { t = x % y x = y y = t return (y>0) ? _gcd(x, y) : x } ############################### ### calculate new width, given height, for preserving aspect ratio ## ## @ mod - new height & width will be divisible by this number ## (allowed values 1|2|4|8|16|32, default 2) ## @ lim - result forced to >= (default==) ## ## ex. [[Spline64Resize]](CalcWidth(720), 720).[[CropEx]](1280, 720) ## function CalcWidth(clip C, float fhgt, int "mod", int "lim") { mdd = Max(1, Default(mod, 2)) lim = Max(mdd, Default(lim, mdd)) Assert(IsClip(C) && C.hasVideo, \ "CalcWidth: invalid source clip") Assert(fhgt>0.001, \ "CalcWidth: must be > 0") Assert((mdd==1||mdd==2||mdd==4||mdd==8||mdd==16||mdd==32), \ "CalcWidth: argument not one of (1|2|4|8|16|32)") aspect = Float(C.Width) / Float(C.Height) wid = Round((fhgt*aspect)/mdd)*mdd return Max(lim, wid) } ############################### ### calculate new height, given width, for preserving aspect ratio ## ## @ mod - new height & width will be divisible by this number ## (allowed values 1|2|4|8|16|32, default 2) ## @ lim - result forced to >= (default==) ## ## ex. [[Spline64Resize]](1280, CalcHeight(1280)).[[CropEx]](1280, 720) ## function CalcHeight(clip C, float fwid, int "mod", int "lim") { mdd = Max(1, Default(mod, 2)) lim = Max(mdd, Default(lim, mdd)) Assert(IsClip(C) && C.hasVideo, \ "CalcHeight: invalid source clip") Assert(fwid>0, \ "CalcHeight: must be > 0") Assert((mdd==1||mdd==2||mdd==4||mdd==8||mdd==16||mdd==32), \ "CalcHeight: argument not one of (1|2|4|8|16|32)") aspect = Float(C.Width) / Float(C.Height) hgt = Round((fwid/aspect)/mdd)*mdd return Max(lim, hgt) } ####################################################################################### ### DEEP COLOR ARGUMENT SCALING FUNCTIONS ####################################################################################### ####################################### ### return TV-black level for given bit depth ## function getTVBlack(int bits) { return (bits==8) ? 16 \ : (bits==10) ? 643 \ : (bits==12) ? 256 \ : (bits==14) ? 1024 \ : (bits==16) ? 4096 \ : (bits==32) ? 16.0/255.0 \ : Assert(false, \ "getTVBlack: not one of (8|10|12|14|16|32)") } # https://forum.doom9.org/showthread.php?p=1804992#post1804992 ####################################### ### return integer fullscale value for given bit depth ## function getFullscale(int bits) { return (bits==8) ? 255 \ : (bits==10) ? 1023 \ : (bits==12) ? 4095 \ : (bits==14) ? 16383 \ : (bits==16) ? 65535 \ : (bits==32) ? 1 \ : Assert(false, \ "getFullscale: not one of (8|10|12|14|16|32)") } ## ('scff' short for 'SCale with Fullscale param, Float result') ####################################### ### scale a value from one fullscale range to another ## ## @ fs_in - the fullscale value in the range being converted from ## @ fs_out - the fullscale value in the range being converted to ## @ cx - if true, clamp output to [0..] ## @ returns float if not clamped; ## else returns int for int formats, float for float ## function scff(int fs_in, int fs_out, float f, bool "cx") { clamp = Default(cx, false) fin = Float( (fs_in==1) ? 256.0/255.0 : fs_in+1 ) fout = Float( (fs_out==1) ? 256.0/255.0 : fs_out+1 ) fr = (fs_in==fs_out) ? f : f * fout / fin ## unclamped result fr = (clamp==false) ? fr : Min(Max(0.0, fr), fs_out) fr = (clamp==false) ? fr : (fs_out==1) ? fr : Round(fr) return fr } ## ('scbf' short for 'SCale with Bits param, Float result') ####################################### ### scale a value from one bit depth to another ## ## @ bits_in - the bit depth being converted from ## @ bits_out - the bit depth being converted to ## @ cx - if true, clamp output ## @ returns float if not clamped; ## else returns int for int formats, float for float ## function scbf(int bits_in, int bits_out, float f, bool "cx") { return scff(getFullscale(bits_in), getFullscale(bits_out), f, cx) } ####################################### ### scale a value from one bit depth to another; clamp output ## ## @ bits_in - the bit depth being converted from ## @ bits_out - the bit depth being converted to ## @ returns int for int formats, float for float ## function scbx(int bits_in, int bits_out, float f) { return scbf(bits_in, bits_out, f, true) } ####################################### ### scale a value from one bit depth to another; string result ## ## @ bits_in - the bit depth being converted from ## @ bits_out - the bit depth being converted to ## @ returns String, unclamped ## function scbs(int bits_in, int bits_out, float f, int "decimals") { decimals = Min(Max(0, Default(decimals, 4)), 8) return String(scbf(bits_in, bits_out, f), "%0."+String(decimals)+"f") } ####################################### ### scale an 8-bit value for target clip ## ## @ T - clip with target bit depth (assume = 8) ## @ cx - if true, clamp output ## @ returns float if not clamped; ## else returns int for int formats, float for float ## function sc8f(clip T, float f, bool "cx") { return scbf(8, T.BitsPerComponent, f, cx) } ####################################### ### scale an 8-bit value for target clip ; clamp output ## ## @ T - clip with target bit depth ## @ returns int for int formats, float for float ## function sc8x(clip T, float f) { return scbf(8, T.BitsPerComponent, f, true) } ####################################### ### scale an 8-bit value for target clip ; string result ## @ T - clip with target bit depth (assume = 8) ## @ returns String, unclamped ## function sc8s(clip T, float f, int "decimals") { return scbs(8, T.BitsPerComponent, f, decimals) } ####################################################################################### ### UTILITY FILTERS ####################################################################################### ################################## ### Convert from anything to planar RGB(A) ## YUV>RGB conversions always done at 16-bit ## ## @ matrix - ignored if not converting from YUV; default "Rec601" ## @ bits_out - output bit depth, one of (8|10|12|14|16); default same as source ## @ A - if supplied, becomes the alpha channel of the resulting clip; ## overrides any existing alpha channel. ## * if is RGBA or YUVA, the A channel is used; ## * if YUV or Y-only, the Y channel is used; ## * else, is ignored. ## ## Return planar RGB(A), max 16-bit (no float output) ## function ToRGB(clip C, string "matrix", int "bits_out", clip "A") { C = (C.BitsPerComponent==32) \ ? C.ConvertBits(16) \ : C matrix = Default(matrix, "Rec601") bits = C.BitsPerComponent bits_out = Min(Default(bits_out, bits), 16) A = (IsClip(A) && HasVideo(A) && A.HasAlpha) \ ? A.ExtractA \ .ConvertBits(bits_out) \ : (IsClip(A) && HasVideo(A) && (A.IsYUV || A.IsY)) \ ? A.ExtractY \ .ConvertBits(bits_out) \ : (C.HasAlpha) \ ? C.ExtractA \ .ConvertBits(bits_out) \ : Nop R = (C.IsRGB) \ ? C.ConvertToPlanarRGB \ .ConvertBits(bits_out) \ : C.ConvertBits(16) \ .ConvertToYUV444 \ .ConvertToPlanarRGB(matrix=matrix) \ .ConvertBits(bits_out) return (IsClip(A)) \ ? CombinePlanes(R.ExtractR, R.ExtractG, R.ExtractB, A, \ planes="RGBA") \ : R } ################################## ### Convert from anything to YUV(A)444 ## RGB>YUV conversions always done at 16-bit ## ## @ matrix - ignored if not converting from RGB; default "Rec601" ## @ bits_out - output bit depth, one of (8|10|12|14|16); default same as source ## @ A - if supplied, becomes the alpha channel of the resulting clip; ## overrides any existing alpha channel. ## * if is RGBA or YUVA, the A channel is used; ## * if YUV or Y-only, the Y channel is used; ## * else, is ignored. ## ## Return YUV(A)444, max 16-bit (no float output) ## function To444(clip C, string "matrix", int "bits_out", clip "A") { C = (C.BitsPerComponent==32) \ ? C.ConvertBits(16) \ : C matrix = Default(matrix, "Rec601") bits = C.BitsPerComponent bits_out = Min(Default(bits_out, bits), 16) A = (IsClip(A) && HasVideo(A) && A.HasAlpha) \ ? A.ExtractA \ .ConvertBits(bits_out) \ : (IsClip(A) && HasVideo(A) && (A.IsYUV || A.IsY)) \ ? A.ExtractY \ .ConvertBits(bits_out) \ : (C.HasAlpha) \ ? C.ExtractA \ .ConvertBits(bits_out) \ : Nop R = (C.IsYUV) \ ? C.ConvertBits(16) \ .ConvertToYUV444 \ .ConvertBits(bits_out) \ : C.ConvertToPlanarRGB \ .ConvertBits(16) \ .ConvertToYUV444(matrix=matrix) \ .ConvertBits(bits_out) return (IsClip(A)) \ ? CombinePlanes(R.ExtractY, R.ExtractU, R.ExtractV, A, "YUVA") \ : R } ################################## ### Convert from anything to best equivalent 16-bit version ## no RGB>YUV conversions ## ## @ A - if supplied, becomes the alpha channel of the resulting clip; ## overrides any existing alpha channel. ## * if is RGBA or YUVA, the A channel is used; ## * if YUV or Y-only, the Y channel is used; ## * else, is ignored. ## ## Returns: ## any RGB(A) -> RGB(A)P16 ## any 444 -> YUV(A)444P16 ## any 422 -> YUV(A)422P16 ## any 420 -> YUV(A)420P16 ## any Yxx -> Y16 / YUVA420P16 // if supplied, return YUVA420P16 ## YV411 -> YUV(A)420P16 // alpha from if supplied ## function To16bit(clip C, clip "A") { A = (IsClip(A) && HasVideo(A) && A.HasAlpha) \ ? A.ExtractA \ .ConvertBits(16) \ : (IsClip(A) && HasVideo(A) && (A.IsYUV || A.IsY)) \ ? A.ExtractY \ .ConvertBits(16) \ : (C.HasAlpha) \ ? C.ExtractA \ .ConvertBits(16) \ : Nop R = (C.IsRGB) \ ? C.ConvertToPlanarRGB \ .ConvertBits(16) \ : (C.Is444) \ ? C.ConvertToYUV444 \ .ConvertBits(16) \ : (C.Is422 || C.IsYUY2) \ ? C.ConvertToYUV422 \ .ConvertBits(16) \ : (C.Is420 || C.IsYV411) \ ? C.ConvertToYUV420 \ .ConvertBits(16) \ : (C.IsY) \ ? (IsClip(A)) \ ? C.ConvertToYUV420 \ .ConvertBits(16) \ : C.ConvertToY \ .ConvertBits(16) \ : Assert(false, \ "To16bit: unrecognized color format") return (R.IsRGB) \ ? (IsClip(A)) \ ? CombinePlanes(R.ExtractR, R.ExtractG, R.ExtractB, A, "RGBA") \ : R \ : (R.IsYUV) \ ? (IsClip(A)) \ ? CombinePlanes(R.ExtractY, R.ExtractU, R.ExtractV, A, "YUVA") \ : R \ : Assert(false, \ "To16bit: internal error") } ################################## ### Convert from anything to 'best' (v2.6x compatible) equivalent 8-bit version ## no RGB>YUV conversions ## ## @ dither - if true, add dither when applicable; default false ## ## Returns: ## any RGBA -> RGB32 ## any RGB -> RGB24 ## any 444 -> YV24 ## any 422 -> YUY2 ## any 420 -> YV12 ## any Yxx -> Y8 ## YV411 (no change) ## (no YUVA output, so save your alpha channel elsewhere) ## function To8bit(clip C, bool "dither") { bits = C.BitsPerComponent dither = Default(dither, false) dith = (dither && (bits>8) && (bits<32)) ? 0 : -1 R = (C.IsRGB) \ ? (C.HasAlpha) \ ? C.ConvertBits(8, dither=dith) \ .ConvertToRGB32 \ : C.ConvertBits(8, dither=dith) \ .ConvertToRGB24 \ : (C.Is444) \ ? C.RemoveAlphaPlane \ .ConvertBits(8, dither=dith) \ .ConvertToYV24 \ : (C.Is422) \ ? C.RemoveAlphaPlane \ .ConvertBits(8, dither=dith) \ .ConvertToYUY2 \ : (C.IsYUY2) \ ? C \ : (C.Is420) \ ? C.RemoveAlphaPlane \ .ConvertBits(8, dither=dith) \ .ConvertToYV12 \ : (C.IsY) \ ? C.RemoveAlphaPlane \ .ConvertBits(8, dither=dith) \ .ConvertToY8 \ : (C.IsYV411) \ ? C \ : Assert(false, \ "To8bit: unrecognized color format") return R } ################################## ### make changes needed to display on [vdubFM]* (VirtualDub FilterMod) (AVS+) ## *[vdubFM: https://sourceforge.net/projects/vdfiltermod/] ## ## @ ditherto8bit - if true, reduce bit depth to 8 bit with dithering if applicable; ## default false ## ## vdubFM does not currently support all AVS+ color spaces; ## ToVdubFM does minimally-destructive translation ## per docs, vdubFM accepts: ## 10-bit 422 (v210 interleaved) ## 16-bit 422, 444, 420 (planar) ## 16-bit RGBA ## this filter returns: ## 8-bit formats unchanged, except YV411->YV420 ## 10-bit 422 (planar, not interleaved) ## 16-bit 422 (planar) ## 16-bit RGBA ## ## version 2017-04-29 raffriff42 ## version 2017-05-16 ditherto8bit option; bugfixes after more thorough testing ## version 2017-05-22 16-bit 444 not dislaying, so falling back to 422 ## function ToVdubFM(clip C, bool "ditherto8bit") { dit8 = Default(ditherto8bit, false) bits = C.BitsPerComponent dith = (bits > 8 && bits < 32) ? 0 : -1 Assert(IsClip(C), \ "ToVdubFM: clip 'C' appears to be invalid") Assert(HasVideo(C), \ "ToVdubFM: clip 'C' appears not to contain video") Assert(C.Height>0 && C.Width>0, \ "ToVdubFM: clip 'C' appears to be zero width or height") CY = (C.HasAlpha) ? C.RemoveAlphaPlane : C R = (C.IsRGB24) ? C \ : (C.IsRGB32) ? C \ : (C.IsYV24) ? C \ : (C.IsYUY2) ? C \ : (C.IsYV12) ? C \ : (C.IsYV411) ? C.ConvertToYV12 \ : (C.IsY8) ? C \ \ : (C.IsRGB && C.NumComponents==4) \ ? (bits==8) ? C .ConvertToRGB32 \ : (bits==16) ? C .ConvertToRGB64 \ : (dit8) ? C.ConvertBits(8, dither=dith).ConvertToRGB32 \ : C.ConvertBits(16) .ConvertToRGB64 \ \ : (C.IsRGB && C.NumComponents==3) \ ? (bits==8) ? C .ConvertToRGB24 \ : (bits==16) ? C .ConvertToRGB64 \ : (dit8) ? C.ConvertBits(8, dither=dith).ConvertToRGB24 \ : C.ConvertBits(16) .ConvertToRGB64 \ [* 16bit: ConvertToRGB48 ng *] \ : (C.Is444) \ ? (bits==8) ? CY .ConvertToYV24 \ : (bits==16) ? CY .ConvertToYUV422 \ : (dit8) ? CY.ConvertBits(8, dither=dith).ConvertToYV24 \ : CY.ConvertBits(16) .ConvertToYUV422 \ [* 16bit: ConvertToYUV444 ng ???? *] \ : (C.Is422) \ ? (bits==8) ? CY .ConvertToYUY2 \ : (bits==10) ? CY .ConvertToYUV422 \ : (dit8) ? CY.ConvertBits(8, dither=dith).ConvertToYUY2 \ : CY.ConvertBits(16) .ConvertToYUV422 \ : (C.Is420) \ ? (bits==8) ? CY .ConvertToYV12 \ : (bits==10) ? CY .ConvertToYUV422 \ : (dit8) ? CY.ConvertBits(8, dither=dith).ConvertToYV12 \ : CY.ConvertBits(16) .ConvertToYUV422 \ [* 16bit: ConvertToYUV420 ng *] \ : (C.IsY) \ ? (bits==8) ? C .ConvertToY8 \ : (bits==10) ? C .ConvertToYUV422 \ : (dit8) ? C.ConvertBits(8, dither=dith) .ConvertToY8 \ : C.ConvertBits(16) .ConvertToYUV422 \ [* 16bit: ConvertToY ng ? *] \ : Assert(false, \ "ToVdubFM: internal error: clip 'C' has unrecognized pixel type") Assert(IsClip(R), \ "ToVdubFM: internal error: clip 'R' invalid") Assert(HasVideo(R), \ "ToVdubFM: internal error: clip 'R' has no video") Assert(R.Height>0 && R.Width>0, \ "ToVdubFM: internal error: clip 'R' zero width or height") return R } ################################## ### Match color format of source to template T (AVS+) ## (for ease of splicing, interleaving etc) ## ## @ matrix - ignored if not converting between RGB<>YUV; default "Rec601" ## @ matchbits - if true (default) forces bit depth to match ## @ dither - if true, add dither when down-converting bit depth; default false ## ## version 2017-05-16 bugfixes after more thorough testing ## function MatchColorFormat(clip C, clip T, string "matrix", bool "matchbits", bool "dither") { Assert(IsClip(C) && HasVideo(C), \ "MatchColorFormat: clip invalid") Assert(IsClip(T) && HasVideo(T), \ "MatchColorFormat: clip invalid") matrix = Default(matrix, "Rec601") matchbits = Default(matchbits, true) dither = Default(dither, false) bits = C.BitsPerComponent bits_out = T.BitsPerComponent dith = (dither && (bits>8) && (bits<32) && (bits_outRGB C = (matchbits && (bits>.ConvertToY8 *] \ [* required to avoid a possible H-shift problem (as of v2.58) *] \ : (T.Is444) \ ? (C.IsRGB) \ ? C.ConvertToYUV444(matrix=matrix) \ : C.ConvertToYUV444 \ : (T.Is422) \ ? (C.IsRGB) \ ? C.ConvertToYUV422(matrix=matrix) \ : C.ConvertToYUV422 \ : (T.Is420) \ ? (C.IsRGB) \ ? C.ConvertToYUV420(matrix=matrix) \ : C.ConvertToYUV420 \ : (T.IsY) \ ? (C.IsRGB) \ ? C.ConvertToY(matrix=matrix) \ : C.ConvertToY \ : Assert(false, \ "MatchColorFormat: unrecognized template format") try { R = (matchbits && (R.BitsPerComponent>bits_out)) \ ? R.ConvertBits(bits_out, dither=dith) \ : R } catch(err_msg2) { Assert(false, \ "MatchColorFormat: internal error 2: " + err_msg2) } Assert(!matchbits || (R.BitsPerComponent==bits_out), \ "MatchColorFormat: internal error 3") return (T.HasAlpha) \ ? (R.HasAlpha) ? R : R.AddAlphaPlane \ : (!R.HasAlpha) ? R : R.RemoveAlphaPlane } ################################## ### Match audio properties of source to template ## (for ease of splicing, interleaving etc) ## Properties matched: ## number of channels (see note), bit depth, ## sample rate (if allowresample=true) ## ## AudioChannels handling: ## if has no audio, returns .KillAudio; ## if has no audio, copies silenced audio from ; ## if and have the same channel count, no change is made; ## if has 1 channel, returns .ConvertToMono; ## if has 2 channels and has 1, returns with split mono; ## otherwise, an error is raised (manual intervention is needed) ## ## @ allowresample - if true, resample audio to match template if needed; ## if false (the default), use [[AssumeSampleRate]], ## which may affect audio/video sync. ## function MatchAudioFormat(clip C, clip T, bool "allowresample") { Assert(IsClip(C), \ "MatchAudioFormat: clip invalid") Assert(IsClip(T), \ "MatchAudioFormat: clip invalid") allowresample = Default(allowresample, false) D = (T.HasAudio==false) ? C.KillAudio \ : (C.HasAudio==false) ? C.AudioDub(T.Loop).Amplify(0) \ : (C.AudioChannels==T.AudioChannels) ? C \ : (C.AudioChannels==2 && T.AudioChannels==1) ? C.ConvertToMono \ : (C.AudioChannels==1 && T.AudioChannels==2) ? C.MergeChannels(C, C) \ : Assert(false, \ "MatchAudioFormat: channels > 2") ## use AssumeSampleRate if: ## (1) pitch shift less than (arbitrary) 2% ## (2) change in duration less than (arbitrary) 25 msec dur = (C.HasAudio) ? C.AudioDuration : 0.0 Tfr = (T.HasAudio) ? T.AudioRate : 0 Cfr = (C.HasAudio) ? C.AudioRate : 0 Tff = Float(Tfr) Cff = Float(Cfr) asok = (allowresample==false) ? true \ : (Tfr<1 || Cfr<1) ? false \ : (Abs(1.0 - Tff / Cff) <= 0.02) && \ (Abs(dur - (dur * Tff / Cff)) < 0.025) ## else, use SSRC if sample rate ratio is allowed: ## http://avisynth.nl/index.php/SSRC frqgcd = (Tfr<1 || Cfr<1) ? 1 : _gcd(Cfr, Tfr) fs1 = (Tfr > Cfr) ? (Cfr / frqgcd) : (Tfr / frqgcd) ssok = (frqgcd>1) && ((fs1==1) || ((fs1 % 2)==0) || ((fs1 % 3)==0)) ## ...else, use ResampleAudio rsok = (Tfr>=1 && Cfr>=1) D = (Cfr==Tfr) ? D \ : (asok) ? D.AssumeSampleRate(Tfr) \ : (ssok) ? D.ConvertAudioToFloat.SSRC(Tfr) \ : (rsok) ? D.ConvertAudioToFloat.ResampleAudio(Tfr) \ : D D = (Tfr<1 || Cfr<1) ? D \ : (D.IsAudioFloat==T.IsAudioFloat) && (D.AudioBits==T.AudioBits) ? D \ : (T.IsAudioFloat) ? D.ConvertAudioToFloat \ : (T.AudioBits==32) ? D.ConvertAudioTo32bit \ : (T.AudioBits==24) ? D.ConvertAudioTo24bit \ : (T.AudioBits==16) ? D.ConvertAudioTo16bit \ : (T.AudioBits==8) ? D.ConvertAudioTo8bit \ : Assert(false, \ "MatchAudioFormat: unrecognized sample format") return D } ####################################### ### transport RGB in YV24: G=>Y, B=>U, R=>V (AVS+) ## function GBR2YUV(clip C, bool "yuva") { Assert(C.IsRGB, \ "GBR2YUV: source must be RGB") yuva = Default(yuva, true) ## (AVS) #return YToUV(C.ShowBlue("YV12"), C.ShowRed("YV12"), C.ShowGreen("YV12")) ## (AVS+) return (C.NumComponents==4 && yuva) \ ? CombinePlanes(C.ExtractG, C.ExtractB, C.ExtractR, C.ExtractA, "YUVA") \ : CombinePlanes(C.ExtractG, C.ExtractB, C.ExtractR, "YUV") } ####################################### ### transport YV24 in RGB: Y=>G, U=>B, V=>R (AVS+) ## ## @ A - if supplied, provides the alpha data to merge into the output; ## YUV format required; alpha is taken from the Y (luma) channel. ## @ rgb32 - if true (the default), output pixel type = RGB32, else it is RGB24. ## function YUV2GBR(clip C, clip "A", bool "rgb32") { Assert(C.IsYV24 || C.Is444, \ "YUV2GBR: source must be 4:4:4") Assert(!IsClip(A) || A.IsYUV, \ "YUV2GBR: alpha source must be YUV") rgb32 = Default(rgb32, true) ## (AVS) #return (IsClip(A) && rgb32) #\ ? MergeARGB(A, C.VToY, C.ConvertToY8, C.UToY) #\ : MergeRGB(C.VToY, C.ConvertToY8, C.UToY, #\ pixel_type=(rgb32)?"RGB32":"RGB24") ## (AVS+) return (IsClip(A) && rgb32) \ ? CombinePlanes(C.ExtractY, C.ExtractU, C.ExtractV, A.ExtractY, "GBRA") \ : (C.NumComponents==4 && rgb32) \ ? CombinePlanes(C.ExtractY, C.ExtractU, C.ExtractV, C.ExtractA, "GBRA") \ : CombinePlanes(C.ExtractY, C.ExtractU, C.ExtractV, "GBR") } ############################## ## PC->TV ## ## version 2017-05-28 hackish but seems ok ## function PC2TV(clip C) { apc = 219.25/256.0 apc2 = 219.0/256.0 bpc = C.sc8f(16) cpc = 224.0/256.0 return (C.IsRGB) \ ? C.RGBAdjust( \ apc, apc, apc, apc, \ bpc, bpc, bpc, bpc, dither=false) \ : C.ColorYUV( \ gain_y=f2c(apc2), \ off_y=16.8, \ cont_u=f2c(cpc), \ cont_v=f2c(cpc)) [* ## alternative return (C.IsRGB) \ ? C.To444("Rec709").ToRGB("PC.709") \ : C.ToRGB("PC.709").To444("Rec709") *] } ############################## ### TV->PC ## ## version 2017-05-28 hackish but seems ok ## function TV2PC(clip C) { atv = 256.0/219.25 btv = C.sc8f(-16.0*atv) ctv = 256.0/224.0 return (C.IsRGB) \ ? C.RGBAdjust( \ atv, atv, atv, atv, \ btv, btv, btv, btv, dither=false) \ : C.ColorYUV( \ gain_y=f2c(atv), \ off_y=(-16.5*atv), \ cont_u=f2c(ctv), \ cont_v=f2c(ctv)) [* ## alternative (C.IsRGB) \ ? C.To444("PC.709").ToRGB("Rec709") \ : C.ToRGB("Rec709").To444("PC.709") *] } ############################## ### Stack clip horizontally until it is at least wide ## function stack_to_wid(clip C, int wid, clip "R", bool "flip") { R = Default(R, C) flip = Default(flip, false) C2 = (flip) ? C.FlipHorizontal : C return (R.Width >= wid) ? R \ : stack_to_wid(C, wid, StackHorizontal(R, C2)) } ############################## ### Stack clip vertically until it is at least high ## function stack_to_hgt(clip C, int hgt, clip "R", bool "flip") { R = Default(R, C) flip = Default(flip, false) C2 = (flip) ? C.FlipVertical : C return (R.Height >= hgt) ? R \ : stack_to_hgt(C, hgt, StackVertical(R, C2)) } ############################## #### Stack clip until it is x ## function stack_to_size(clip C, int wid, int hgt, bool "flip") { flip = Default(flip, false) C ## Last stack_to_wid(wid, flip=flip) stack_to_hgt(hgt, flip=flip) Crop(0, 0, wid, hgt) return Last } ################################## ### remove gamma transfer function (if bit depth > 8) for linear-light processing ## improves appearance of certain filters like Resize; ## must call restore_gamma before final output ## ## @ mode - select desired gamma transform method ## mode 0: sRGB (default; requires MaskTools v2.2.x) ## mode 1: straight 2.2 gamma, no linear segment ## (small round-trip errors in dark areas at < 16-bit) ## @ enable - default true; if false, do nothing (bypass) ## function remove_gamma(clip C, bool "enable", int "mode") { mode = Default(mode, 0) enable = Default(enable, true) #### sRGB to linear #### (x <= 0.04045) ? (x / 12.92) : (((x + 0.055) / 1.055)^2.4) sxp = "x 255 @F / 0.04045 <= x 255 @F / 12.92 / x 255 @F / 0.055 + 1.055 / 2.4 ^ ? 255 @F * " return (enable==false || C.BitsPerComponent==8) ? C \ : (mode==1) ? \ (C.IsRGB) [* straight 2.2 gamma, no linear segment *] \ ? C.LevelsPlus(0, 1/2.2, 255, 0, 255) [* ng (testing round-trip in 10-bit) *] \ : C.LevelsPlus(0, 1/2.2, 255, 0, 255) [* ok except where noted *] \ : [* default *] \ (C.IsRGB) [* sRGB to linear *] \ ? Eval("""C.GBR2YUV.mt_lut(yexpr=sxp, uexpr=sxp, vexpr=sxp, chroma="process").YUV2GBR""") \ : Eval("""C.mt_lut(yexpr=sxp, chroma="copy")""") } ################################## ### apply standard gamma transfer function (if bit depth > 8) ## only call this if gamma was previously removed ## ## @ mode - select desired gamma transform method ## mode 0: sRGB (default; requires MaskTools v2.2.x) ## mode 1: straight 2.2 gamma, no linear segment ## (small round-trip errors in dark areas at < 16-bit) ## @ enable - default true; if false, do nothing (bypass) ## function restore_gamma(clip C, bool "enable", int "mode") { enable = Default(enable, true) mode = Default(mode, 0) #### linear to sRGB #### (x <= 0.0031308) ? (x * 12.92) : (((x^(1.0 / 2.4)) * 1.055) - 0.055) sxp = "x 255 @F / 0.0031308 <= x 255 @F / 12.92 * x 255 @F / 1 2.4 / ^ 1.055 * 0.055 - ? 255 @F * " return (enable==false || C.BitsPerComponent==8) ? C \ : (mode==1) ? \ (C.IsRGB) [* straight 2.2 gamma, no linear segment *] \ ? C.LevelsPlus(0, 2.2, 255, 0, 255) \ : C.LevelsPlus(0, 2.2, 255, 0, 255) \ : [* default *] \ (C.IsRGB) [* linear to sRGB *] \ ? Eval("""C.GBR2YUV.mt_lut(yexpr=sxp, uexpr=sxp, vexpr=sxp, chroma="process").YUV2GBR""") \ : Eval("""C.mt_lut(yexpr=sxp, chroma="copy")""") } ####################################################################################### ### COLOR AND OVERLAY FILTERS ####################################################################################### ################################## ### scale 0-255 [[Levels]] arguments to current bit depth (AVS+) ## ## @ coring - unlike 'legacy' Levels, now defaults to false ## function LevelsPlus(clip C, \ int input_low, float gamma, int input_high, \ int output_low, int output_high, \ bool "coring", bool "dither") { coring = Default(coring, false) return C.Levels( \ C.sc8x(input_low), \ gamma, \ C.sc8x(input_high), \ C.sc8x(output_low), \ C.sc8x(output_high), \ coring, dither) } ################################## ### [[Overlay]] with support for parameter ### supports adding borders to, or letterboxing, inset clip ## ## @ align - @see [[Subtitle]] ("number pad notation") ## @ borderwidth - @see [[AddBorders]]/[[Letterbox]] (Letterbox if less than zero) ## @ bordercolor - @see AddBorders ## function OverlayAligned(clip base, clip over, \ int "x", int "y", clip "mask", float "opacity", string "mode", \ bool "greymask", string "output", bool "ignore_conditional", \ bool "pc_range", int "align", int "borderwidth", int "bordercolor") { x = Default(x, 0) y = Default(y, 0) opc = Default(opacity, 1.0) aln = Default(align, 7) bdr = Default(borderwidth, 0) clr = Default(bordercolor, 0) x = (aln==3 || aln==6 || aln==9) ? x + base.Width - over.Width \ : (aln==2 || aln==5 || aln==8) ? x + (base.Width/2) - (over.Width/2) \ : x y = (aln==1 || aln==2 || aln==3) ? y + base.Height - over.Height \ : (aln==4 || aln==5 || aln==6) ? y + (base.Height/2) - (over.Height/2) \ : y over = (bdr <= 0) ? over [* L T R B *] \ : (aln==1) ? over.AddBorders( 0, bdr, bdr, 0, clr) \ : (aln==2) ? over.AddBorders(bdr, bdr, bdr, 0, clr) \ : (aln==3) ? over.AddBorders(bdr, bdr, 0, 0, clr) \ : (aln==4) ? over.AddBorders( 0, bdr, bdr, bdr, clr) \ : (aln==5) ? over.AddBorders(bdr, bdr, bdr, bdr, clr) \ : (aln==6) ? over.AddBorders(bdr, bdr, 0, bdr, clr) \ : (aln==8) ? over.AddBorders(bdr, 0, bdr, bdr, clr) \ : (aln==9) ? over.AddBorders(bdr, 0, 0, bdr, clr) \ : over.AddBorders( 0, 0, bdr, bdr, clr) ## (aln==7) bdr = -bdr over = (bdr <= 0) ? over [* T B L R *] \ : (aln==1) ? over.Letterbox(bdr, 0, 0, bdr, clr) \ : (aln==2) ? over.Letterbox(bdr, 0, bdr, bdr, clr) \ : (aln==3) ? over.Letterbox(bdr, 0, bdr, 0, clr) \ : (aln==4) ? over.Letterbox(bdr, bdr, 0, bdr, clr) \ : (aln==5) ? over.Letterbox(bdr, bdr, bdr, bdr, clr) \ : (aln==6) ? over.Letterbox(bdr, bdr, bdr, 0, clr) \ : (aln==8) ? over.Letterbox( 0, bdr, bdr, bdr, clr) \ : (aln==9) ? over.Letterbox( 0, bdr, bdr, 0, clr) \ : over.Letterbox( 0, bdr, 0, bdr, clr) ## (aln==7) return (opc<0.001) ? base \ : Overlay(base, over, x, y, mask, opc, mode, \ greymask, output, ignore_conditional, pc_range) } ####################################################################################### ### TIMELINE FILTERS ####################################################################################### ####################################### ### "slip" (advance or delay) a clip in time. ## ## @ C - clip to be advanced or delayed ## @ offset - if positive, clip is advanced; ## if negative, clip is delayed ## function Slip(clip C, int offset) { lenTrim = (offset > 0) ? offset : 0 lenPad = (offset < 0) ? -offset : 0 C = (lenPad==0) \ ? C \ : C.Trim(0, -1).Loop(lenPad) + C.Trim(0, C.Framecount-lenPad) C = (lenTrim==0) \ ? C \ : C.Trim(lenTrim, 0) + C.Trim(C.Framecount-lenTrim, -1).Loop(lenTrim) return C } ####################################### ### change speed over a wide range, ### with frame blending in fast forward by default. ## ## @ factor - 0.33 for 1/3 speed, etc; ## min = 0.001; max = min([[FrameCount]], 1000) ## (if > FrameCount, duration would be 0) ; ## if argument is out of range, an error occurs. ## ## @ pitchfix - if true, maintain audio pitch with speed ## (default false; allow pitch to rise or fall) ## ## @ noblend - if true, never blend frames (default false) ## ## @ version 1.0 24-Feb-2014 raffriff42 ## function ChangeSpeed( \ clip C, float factor, bool "pitchfix", bool "noblend") { Assert(factor>=0.001, \ "ChangeSpeed bad argument: factor<0.001") Assert(factor<=1000.0, \ "ChangeSpeed bad argument: factor>1000") Assert(factor<=C.FrameCount, \ "ChangeSpeed bad argument: factor>FrameCount") pitchfix = Default(pitchfix, false) noblend = Default(noblend, false) AS = C.KillAudio.AssumeFPS(C.FrameRate * factor) ASI = C.KillAudio.AssumeFPS(C.FrameRate / factor) temprad = Round(0.5 * factor) TS = (factor<1.0 || temprad==0 || noblend) \ ? AS \ : AS.Overlay( \ AS.TemporalSoften(temprad, 255, 255, 48, 2), \ opacity=0.7) R = (factor<1.5 && noblend==false) \ ? AS.ConvertFPS(C) \ : TS.ChangeFPS(C.Framerate, linear=(factor<10.0)) return (Abs(factor-1.0) < (0.5/C.FrameCount)) ? C \ : (C.HasAudio==false) ? R \ : R.AudioDub( C.ChangeAudioSpeed(factor, pitchfix) ) } ####################################### ### audio only: change speed over a wide range ## ## @ factor - 0.33 for 1/3 speed, etc; ## min = 0.01; max < 1000; ## if argument is out of range, an error occurs. ## ## @ pitchfix - if true, maintain audio pitch with speed ## (default false; allow pitch to rise or fall) ## ## @ version 1.0 11-Apr-2017 ~ split from [[ChangeSpeed]] ## function ChangeAudioSpeed( \ clip C, float factor, bool "pitchfix") { Assert(C.HasAudio, \ "ChangeAudioSpeed bad argument: source has no audio") Assert(factor>=0.01, \ "ChangeAudioSpeed bad argument: factor < 0.01") Assert(factor<=1000.0, \ "ChangeAudioSpeed bad argument: factor > 1000") A = Default(pitchfix, false) \ ? C.ConvertAudioToFloat.TimeStretch(tempo = factor*100.0) \ : C.ConvertAudioToFloat.TimeStretch(rate = factor*100.0) R = (Abs(factor-1.0) < 0.0001) ? C \ : (C.AudioDuration/factor < 0.001) ? C.AudioTrim(0.0, length=0.0) \ : (C.AudioBits==32) ? A.ConvertAudioTo32bit \ : (C.AudioBits==24) ? A.ConvertAudioTo24bit \ : (C.AudioBits==16) ? A.ConvertAudioTo16bit \ : (C.AudioBits==8) ? A.ConvertAudioTo8bit \ : A ## (==float) return (factor > 0.99) ? R \ : R.AudioTrim(0.0, length=C.AudioDuration) } ####################################################################################### ### CROP, RESIZE AND TRANSFORM FILTERS ####################################################################################### ################################################## ### [[Crop]] with true by default; optionally enforce [[Mod]] ### function Cropa(clip C, int x, int y, int wid, int hgt, bool "align", int "mod") { align = Default(align, true) m = Default(mod, 1) x = _mod(m, x) y = _mod(m, y) wid = _mod(m, wid) hgt = _mod(m, hgt) return C.Crop(x, y, wid, hgt, align) } ################################################## ### show a diagnostic string on [[Crop]] failure; optionally enforce [[Mod]] ### function Cropd(clip C, int x, int y, int wid, int hgt, bool "align", int "mod") { align = Default(align, false) m = Default(mod, 1) # widmod = (C.IsRGB || C.IsYV24) ? 1 : 2 # hgtmod = (C.IsFrameBased) # \ ? (C.IsRGB || C.IsYV24 || C.IsYUY2) ? 1 : 2 # \ : (C.IsRGB || C.IsYV24 || C.IsYUY2) ? 2 : 4 x = _mod(m, x) y = _mod(m, y) wid = _mod(m, wid) hgt = _mod(m, hgt) # Assert(wid % widmod == 0, # \ "(Cropd) width not a multiple of " + String(widmod)) # Assert(hgt % hgtmod == 0, # \ "(Cropd) height not a multiple of " + String(hgtmod)) try { return C.Crop(x, y, wid, hgt, align) } catch(err_msg) { ss = ", " args = ss + "x: " + String(x) \ + ss + "y: " + String(y) \ + ss + "wid: " + String(wid) \ + ss + "hgt: " + String(hgt) \ + ss + "x+wid: " + String(x+wid) \ + ss + "y+hgt: " + String(y+hgt) \ + ss + "C.Width: " + String(C.Width) \ + ss + "C.Height: " + String(C.Height) Assert(false, err_msg + " (Cropd)"+args ) } } ################################## ### switch (or fade) between three [[Resize]] clips depending on scale factor (AVS+) ## ## @ factor - 0.5 for 50% size, 1.2 for 120%, etc ## @ mod - force new size modulo; if <= 0, it is calculated as follows: ## (mod=1 if source is RGB or YUV 4:4:4 or Y-only; else mod=2) ## @ sm - resizer for downscaling smaller than aprox. 0.5x ## @ med - resizer for subtle size adjustments ## @ lg - resizer for upscaling greater than aprox. 2x ## (default "sm" is gamma-aware BicubicResize(b=-0.5, c=0.25)) ## (default "med" is gamma-aware Spline64Resize) ## (default "lg" is gamma-aware GaussResize) ## @ thrSm - scale factor below which the "small" resizer is used; default 0.65 ## @ thrLg - scale factor above which the "large" resizer is used; default 1.85 ## @ fade - if true, crossfade between small & medium, and between medium & large ## (might be good for very long zooms, but watch out for H- or V-shift issues) ## @ ident - if true, show an on-screen identifier for the small, medium & large clips ## @ gamma - if true and if bit depth > 8, do linear-light processing ## (may retain more highlights but slightly slower); default true ## ## (minimum size on output is x ) ## function ScaleSize(clip C, float factor, int mod, \ string "sm", string "med", string "lg", \ float "thrSm", float "thrLg", bool "fade", bool "ident", bool "gamma") { return C.ScaleSize( \ (factor*C.Width), (factor*C.Height), mod, \ sm, med, lg, thrSm, thrLg, fade, ident, gamma) } ################################## ### switch (or fade) between three [[Resize]] clips depending on scale factor (AVS+) ## ## @ fwid - new width ## @ fhgt - new height ## (if <= 0, it is calculated from and aspect ratio) ## (if <= 0, it is calculated from and aspect ratio) ## (if both <= 0, an error is raised) ## @ mod - force new size modulo; if <= 0, ## =1 if source is RGB or YUV 4:4:4 or Y-only; else =2 ## @ sm - resizer for downscaling smaller than (see below) ## @ med - resizer for subtle size adjustments ## @ lg - resizer for upscaling greater than (see below) ## If supplied, MUST be name of a filter with generic [[Resize]] signature: ## | filter(clip, int target_width, int target_height, ## | float src_left, float src_top, float src_width, float src_height) ## (default ="ScaleSize_small", gamma-aware [[BicubicResize]](b=-0.5, c=0.25)) ## (default ="ScaleSize_medium", gamma-aware [[Spline64Resize]]) ## (default ="ScaleSize_large", gamma-aware [[GaussResize]](p=50)) ## @ thrSm - scale factor below which the resizer is used; default 0.65 ## @ thrLg - scale factor above which the resizer is used; default 1.33 ## @ fade - if true, crossfade between small & medium, and between medium & large ## (the idea being to avoid a sudden shift in appearance at the transition) ## (might be good for very long zooms, but watch out for H- or V-shift issues) ## @ ident - if true, show an on-screen identifier for the small, medium & large clips ## @ gamma - if true and if bit depth > 8, do linear-light processing ## (may retain more highlights but slightly slower); default true ## ## (minimum size on output is x ) ## ## version 2.0 2017-04-16 raffriff42 ## version 2.1 2017-05-6 auto-nogamma ## version 2.2 2017-05-11 optional user-supplied resize functions ## function ScaleSize(clip C, float fwid, float fhgt, int mod, \ string "sm", string "med", string "lg", \ float "thrSm", float "thrLg", bool "fade", bool "ident", bool "gamma") { Assert(fwid>0.0 || fhgt>0.0, \ "ScaleSize: either or must be > 0") Assert(C.Height>0 && C.Width>0, \ "ScaleSize: source has zero pixels width or height") mdd = Max((C.IsRGB || C.IsY || C.Is444) ? 1 : 2, mod) sm = Default(sm, "ScaleSize_small") med = Default(med, "ScaleSize_medium") lg = Default(lg, "ScaleSize_large") thrSm = Default(thrSm, 0.65) thrLg = Default(thrLg, 1.33) fade = Default(fade, false) idnt = Default(ident, false) gamma = Default(gamma, true) forx = Float(C.Width) fory = Float(C.Height) aspc = forx/fory ## aspect ratio fwid = (fwid>=0.0) ? fwid : fhgt*aspc ## calc from aspect if needed fhgt = (fhgt>=0.0) ? fhgt : fwid/aspc fx = fwid/C.Width ## scale factor: x fy = fhgt/C.Height ## scale factor: y fxy = Pow(fx*fy, 0.5) ## scale factor: geometric mean mwid = Max(mdd, Round(fwid/mdd)*mdd) ## modulo-restricted width, height mhgt = Max(mdd, Round(fhgt/mdd)*mdd) adjX = (Float(mwid)-fwid)/fx ## pre-crop (crop before resize) adjY = (Float(mhgt)-fhgt)/fy debug = false dbg = (!debug) ? "" \ : "ScaleSize:\n" \ + String(mdd, "mod= %0.0f\n") \ + String(C.Width, "in_wid= %0.0f\n") \ + String(C.Height, "in_hgt= %0.0f\n") \ + String(fwid, "arg_wid= %0.2f\n") \ + String(fhgt, "arg_hgt= %0.2f\n") \ + String(fx, "fx= %0.2f\n") \ + String(fy, "fy= %0.2f\n") \ + String(fxy, "scalefactor= %0.2f\n") \ + String(mwid, "out_wid= %0.0f\n") \ + String(mhgt, "out_hgt= %0.0f\n") \ + String(mwid-fwid, "raw adjX= %+0.2f\n") \ + String(mhgt-fhgt, "raw adjY= %+0.2f\n") \ + String(adjX, "scaled adjX= %+0.2f\n") \ + String(adjY, "scaled adjY= %+0.2f\n") src_L = -adjX/2.0 src_T = -adjY/2.0 src_W = Float(C.Width)+adjX src_H = Float(C.Height)+adjY ## "callback functions" CL = Eval(lg + "(C, mwid, mhgt, src_L, src_T, src_W, src_H, gamma)") CM = Eval(med + "(C, mwid, mhgt, src_L, src_T, src_W, src_H, gamma)") CS = Eval(sm + "(C, mwid, mhgt, src_L, src_T, src_W, src_H, gamma)") CL = (idnt) ? CL.bigsub("L#-") : CL CM = (idnt) ? CM.bigsub("#M#") : CM CS = (idnt) ? CS.bigsub("-#S") : CS ## switch (or fade) between scaling methods depending on scale factor: R = (fade && fxy > 1.2 * thrLg) ? CL \ : (fade && fxy > 0.8 * thrLg) ? Merge(CM, CL, fxy-thrLg) \ : (fade && fxy > 1.2 * thrSm) ? CM \ : (fade && fxy > 0.8 * thrSm) ? Merge(CS, CM, 2*fxy-thrSm) \ : (fxy > thrLg) ? CL \ : (fxy > thrSm) ? CM \ : CS R = (!debug) ? R \ : R.Subtitle(dbg, size=12, x=R.Width*3/8, y=R.Height*1/16, lsp=0) return R } ## ScaleSize "callback functions" - user-overridable: ## { { { ############################### ### for downscaling smaller than aprox. 0.5x function ScaleSize_small(clip C, \ int mwid, int mhgt, \ float src_L, float src_T, \ float src_W, float src_H, bool "gamma") { gamma = Default(gamma, false) G = (gamma) ? C.remove_gamma : C R = G.BicubicResize(mwid, mhgt, -0.5, 0.25, \ src_L, src_T, src_W, src_H) return (gamma) ? R.restore_gamma : R } ############################### ### for subtle size adjustments function ScaleSize_medium(clip C, \ int mwid, int mhgt, \ float src_L, float src_T, \ float src_W, float src_H, bool "gamma") { gamma = Default(gamma, false) G = (gamma) ? C.remove_gamma : C R = G.Spline64Resize(mwid, mhgt, \ src_L, src_T, src_W, src_H) return (gamma) ? R.restore_gamma : R } ############################### ### for upscaling greater than aprox. 2x ### (if substituting nnedi3 etc, watch out for image shift issues) ## function ScaleSize_large(clip C, \ int mwid, int mhgt, \ float src_L, float src_T, \ float src_W, float src_H, bool "gamma") { gamma = Default(gamma, false) G = (gamma) ? C.remove_gamma : C R = G.GaussResize(mwid, mhgt, \ src_L, src_T, src_W, src_H, p=50) return (gamma) ? R.restore_gamma : R } ## } } } # http://forum.doom9.org/showthread.php?p=1737682#post1737682 ################################## ### crop to size with smooth animated pan & tilt, expressed as percentage (center @ pan=0, tilt=0) ## related: [[CropShift]] ## ## @ pan - -1=left, 0=center, +1=right; [default 0] ## @ tilt - -1=up, 0=center, +1=down; [default 0] ## @ title - short identifier string for debugging (used in eg, [[StackTwoAcrossCropped]]) ## @ align - see [[Subtitle]]: align ## function CropPanTilt(clip C, int wid, int hgt, float "pan", float "tilt", \ string "title", int "align") { bdrWid = (wid - C.Width) bdrHgt = (hgt - C.Height) xctr = Min(Max(-1.0, Default(pan, 0.0)), 1.0) yctr = Min(Max(-1.0, Default(tilt, 0.0)), 1.0) bdrLt = Abs(Float(bdrWid) / 2.0) bdrLt = bdrLt + bdrLt * xctr bdrLt = Sign(bdrWid) * bdrLt bdrTp = Abs(Float(bdrHgt) / 2.0) bdrTp = bdrTp + bdrTp * yctr bdrTp = Sign(bdrHgt) * bdrTp align = Default(align, 7) (bdrLt<=0 && bdrTp<=0) ? Nop \ : Assert(false, "CropPanTilt: clip too small for target size") C = C.Spline36Resize(wid, hgt, -bdrLt, -bdrTp, wid, hgt) return (!defined(title)) ? C \ : C.Subtitle(String(title), align=align, size=C.Height/24) } ##----> see also function Offset ################################## ### crop to size with smooth animated pan & tilt, expressed as pixels (center @ off_x=0, off_y=0) ## related: CropPanTilt ## ## @ off_x <0=left, 0=center, >0=right; [default 0] ## @ off_y <0=up, 0=center, >0=down; [default 0] ## function CropShift(clip C, int wid, int hgt, float "off_x", float "off_y") { off_x = Default(off_x, 0.0) off_y = Default(off_y, 0.0) bdrWid = (wid - C.Width) bdrHgt = (hgt - C.Height) bdrLt = Abs(Float(bdrWid) / 2.0) bdrLt = bdrLt - off_x bdrLt = Sign(bdrWid) * bdrLt bdrTp = Abs(Float(bdrHgt) / 2.0) bdrTp = bdrTp - off_y bdrTp = Sign(bdrHgt) * bdrTp (bdrLt<=0 && bdrTp<=0) ? Nop \ : Assert(false, "CropShift: clip too small for target size") return C.Spline36Resize(wid, hgt, -bdrLt, -bdrTp, wid, hgt) } # https://forum.doom9.org/showthread.php?p=1739635#post1739635 ### fka UUSize4 ################################## ### symmetrically crop or expand a clip to ensure it is a certain size, ### with size forced to mod-4, mod-8 etc ## ## @ wid, hgt - new desired width & height (will be rounded to nearest ) ## @ mod - [[Mod]] value; one of (1|2|4|8|16|32); default 2 ## (if negative, round UP, else round down) ## @ border - border color; default 0 (black border) ## (if -1, mode="extend"; if -2, mode="mirror"; if -3, mode="blur") ## @ mode - expand method: ("extend"|"mirror"|"blur"|"border") default/fallback="border" ## (overrides "border" argument for those who prefer this syntax) ## @ debug - show internal variables ## ## EXAMPLES: ## black borders ## | CropEx(x, y) ## | CropEx(x, y, mode="border") ## color borders ## | CropEx(x, y, border=color_red) ## extend edges ## | CropEx(x, y, mode="extend") ## mirror borders ## | CropEx(x, y, mode="mirror") ## function CropEx(clip C, float wid, float hgt, int "mod", \ int "border", string "mode", bool "debug") { #return ColorBars(width=1280, height=720) mode = Default(mode, "border") mdd = Default(mod, 2) border = Default(border, 0) dbug = Default(debug, false) #mult = Default(z, true) ? -1 : 1 mult = (mdd>0) ? -1 : 1 mdd = Abs(mdd) Assert((mdd==1||mdd==2||mdd==4||mdd==8||mdd==16||mdd==32), \ "CropEx: argument not one of (1|2|4|8|16|32)") border = Min(Max(-3, border), $ffffff) wid = Round(wid) hgt = Round(hgt) out_wid = wid + mult * (wid % mdd) out_hgt = hgt + mult * (hgt % mdd) C ## Last==C bdrX = out_wid - Width bdrY = out_hgt - Height bdrLt = Ceil(Float(bdrX) / 2.0) bdrTp = Ceil(Float(bdrY) / 2.0) bdrLt = bdrLt - (bdrLt % 2) bdrTp = bdrTp - (bdrTp % 2) bdrRt = (bdrX - bdrLt) bdrBt = (bdrY - bdrTp) mode = (mode=="extend") ? mode \ : (mode=="mirror") ? mode \ : (border==-1) ? "extend" \ : (border==-2) ? "mirror" \ : (border==-3) ? "blur" \ : "border" dbg="CropEx:" +String(mdd, " mod= %0.0f") dbg=dbg+"\n"+String(C.Width, "in_wid= %0.0f") +String(C.Height, " in_hgt= %0.0f") dbg=dbg+"\n"+String(wid, "arg_wid= %0.0f") +String(hgt, " arg_hgt= %0.0f") dbg=dbg+"\n"+String(bdrX, "bdrX= %+0.0f") +String(bdrY, " bdrY= %+0.0f") dbg=dbg+"\n"+String(bdrLt, "bdrLt= %+0.0f") +String(bdrTp, " bdrTp= %+0.0f") dbg=dbg+"\n"+String(bdrRt, "bdrRt= %+0.0f") +String(bdrBt, " bdrBt= %+0.0f") dbg=dbg+"\n"+String(out_wid, "out_wid= %0.0f") +String(out_hgt, " out_hgt= %0.0f") dbg=dbg+"\n mode = '"+mode+"'" +String(border, " border= %0.0f") (mode=="extend") ? padding( \ Max(0, bdrLt), Max(0, bdrTp), \ Max(0, bdrRt), Max(0, bdrBt)) \ : (mode=="mirror") ? MirrorBorders( \ Max(0, bdrLt), Max(0, bdrTp), \ Max(0, bdrRt), Max(0, bdrBt)) \ : (mode=="blur") ? BlurBorders( \ Max(0, bdrLt), Max(0, bdrTp), \ Max(0, bdrRt), Max(0, bdrBt)) \ : AddBorders( \ Max(0, bdrLt), Max(0, bdrTp), \ Max(0, bdrRt), Max(0, bdrBt), border) Crop( \ ((bdrLt < 0) ? -bdrLt : 0), \ ((bdrTp < 0) ? -bdrTp : 0), \ ((bdrRt < 0) ? bdrRt : out_wid), \ ((bdrBt < 0) ? bdrBt : out_hgt)) return (!dbug) ? Last : Subtitle(dbg, align=5, lsp=0) } # http://forum.doom9.org/showthread.php?p=1596804#post1596804 ################################## function AddRepeatBorders(clip C, int "bdrx", int "bdry") { bdrx = Max(0, Default(bdrx, 16)) bdry = Max(0, Default(bdry, 16)) bdrl = bdrx bdrr = bdrx bdrt = bdry bdrb = bdry w = c.Width h = c.Height return C.PointResize( \ w+bdrl+bdrr, \ h+bdrt+bdrb, \ -bdrl, -bdrt, \ w+bdrl+bdrr, h+bdrt+bdrb ) } # http://forum.doom9.org/showthread.php?p=1596804#post1596804 ################################## function Padding(clip C, int left, int top, int right, int bottom) { left = Max(0, left) top = Max(0, top) right = Max(0, right) bottom = Max(0, bottom) w = C.Width h = C.Height C.PointResize( \ w+left+right, h+top+bottom, \ -left, -top, \ w+left+right, h+top+bottom) } # http://forum.doom9.org/showthread.php?p=1739635#post1739635 ################################## function MirrorBorders(clip C, int left, int top, int right, int bottom) { bdrLt = Max(0, left ) bdrTp = Max(0, top ) bdrRt = Max(0, right ) bdrBt = Max(0, bottom) C ## Last==c (bdrLt==0) ? Last : StackHorizontal( \ stack_to_wid(bdrLt, flip=true) \ .Crop(0, 0, bdrLt, 0) \ .FlipHorizontal, \ Last) (bdrRt==0) ? Last : StackHorizontal( \ Last, \ stack_to_wid(bdrRt, flip=true) \ .Crop(Width-bdrRt, 0, bdrRt, 0) \ .FlipHorizontal) (bdrTp==0) ? Last : StackVertical( \ stack_to_hgt(bdrTp, flip=true) \ .Crop(0, 0, 0, bdrTp) \ .FlipVertical, \ Last) (bdrBt==0) ? Last : StackVertical( \ Last, \ stack_to_hgt(bdrBt, flip=true) \ .Crop(0, Height-bdrBt, 0, bdrBt) \ .FlipVertical) return Last } # https://forum.doom9.org/showthread.php?p=1807908#post1807908 ################################## function BlurBorders(clip C, int left, int top, int right, int bottom) { wid = C.Width + left + right hgt = C.Height + top + bottom ## heavy 'gaussian blur' ## tuned to a fixed, compromise radius w = Max(4, wid/16 - (wid/16 % 4)) h = Max(4, hgt/16 - (hgt/16 % 4)) B = C.BilinearResize(w, h) \ .GaussResize(wid, hgt, p=19) return B.Overlay(C, x=left, y=top) } ##----> see also function CropShift ################################## ### shift a clip up-down and left-right (with sub-pixel precision); ### this results in repeated edge pixels ## function Shift(clip C, float offh, float offv) { C BilinearResize(Width, Height, -offh, -offv, Width, Height) return Last } ##----> see also http://avisynth.nl/images/ChromaShiftSP.avsi ################################## ### shift Chroma relative to Luma (AVS+) ## (YUV4xx, YUVA4xx, YUV4xxPx, YUVA4xxPx) ## function ShiftUV(clip C, float "ux", float "uy", float "vx", float "vy") { Assert(C.IsYUV || C.IsYUVA, \ "ShiftUV: source must be YUV(A)") ux = Float(Default(ux, 0)) uy = Float(Default(uy, 0)) vx = Float(Default(vx, ux)) vy = Float(Default(vy, uy)) U = C.ExtractU V = C.ExtractV U = U.BilinearResize(U.Width, U.Height, -ux, -uy, U.Width, U.Height) V = V.BilinearResize(V.Width, V.Height, -vx, -vy, V.Width, V.Height) return (C.NumComponents==3) \ ? CombinePlanes(C.ExtractY, U, V, "YUV") \ : CombinePlanes(C.ExtractY, U, V, C.ExtractA, "YUVA") } # https://forum.doom9.org/showthread.php?p=1803247#post1803247 ################################## ### shift and/or resize Red and Blue relative to Green (AVS+) ## (RGB24, RGB32, RGB48, RGB64, RGB10/12/14/16/32, RGBA10/12/14/16/32) ## ## @ rx, ry, bx, by - ± "shift" red & blue position ## @ rxd, ryd, bxd, byd - ± "delta" width & height for advanced convergence ## function ShiftRedBlue(clip C, \ float "rx", float "ry", \ float "bx", float "by", \ float "rxd", float "ryd", \ float "bxd", float "byd") { Assert(C.IsRGB, \ "ShiftRedBlue: source must be RGB") rx = Float(Default(rx, 0)) ry = Float(Default(ry, 0)) bx = Float(Default(bx, 0)) by = Float(Default(by, 0)) rxd = Float(Default(rxd, 0)) ryd = Float(Default(ryd, 0)) bxd = Float(Default(bxd, 0)) byd = Float(Default(byd, 0)) R=C.ExtractR.Spline64Resize(C.Width, C.Height, -rx, -ry, C.Width-rxd, C.Height-ryd) B=C.ExtractB.Spline64Resize(C.Width, C.Height, -bx, -by, C.Width-bxd, C.Height-byd) return (C.NumComponents==3) \ ? CombinePlanes(R, C.ExtractG, B, "RGB") \ : CombinePlanes(R, C.ExtractG, B, C.ExtractA, "RGBA") } ####################################################################################### ### DEBUGGING FILTERS ####################################################################################### ################################## ### for clip identification, show a semitransparent text string, very large by default ## ## @ sizeMult - adjust size if needed (default 1.0 means text size = .Height) ## @ enable - if true, bypass this filter (useful with a 'master' control variable) ## function bigsub(clip C, string msg, float "sizeMult", bool "enable") { enable = Default(enable, true) sizeMult = Default(sizeMult, 1.0) return (enable==false) ? C : \ C.Subtitle(msg, align=2, size=Round(sizeMult * Float(C.Height)), \ text_color=$aaffff00, halo_color=$ff000000) } ################################## ### [[Eval]] a script snippet; show script & its return value (clip or nonclip) ## ## @ size, align - cf. [[Subtitle]] ## @ opacity - cf. [[Overlay]] ## ## version 2017-05-17 remove 'bypass', add ## function EvalShow(clip C, string s, float "size", int "align", float "opacity") { size = Default(size, (C.Width+C.Height)/100.0) align = Default(align, 7) opacity = Default(opacity, 0.7) text = transparent_color(opacity, color_yellow) halo = transparent_color(opacity, color_black) ss = s.ReplaceStr(Chr(10), "\n").ReplaceStr(Chr(13), " ") ## ss: 'show string' C r = Eval(s) return IsClip(r) \ ? r.Subtitle(ss, \ size=size, align=align, text_color=text, halo_color=halo, lsp=0) \ : Subtitle(ss + " = \n" + String(r), \ size=size, align=align, text_color=text, halo_color=halo, lsp=0) } ################################## ### show a mask clip overlaid on another clip for visualization ## ## @ mode - one of (""|"luma"|"cyan"); default "" (all channels in Add mode) ## @ opacity - cf. [[Overlay]] ## function ShowMask(clip C, clip M, String "mode", float "opacity") { mode = Default(mode, "") opacity = Float(Default(opacity, 0.7)) MM = MergeRGB(BlankClip(M), M, M).RGBAdjust(2, 2, 2) MM = (C.IsYV12==false) ? MM : MM.ConvertToYV12 MM = (C.IsYUY2==false) ? MM : MM.ConvertToYUY2 MM = (C.IsRGB24==false) ? MM : MM.ConvertToRGB24 MM = (C.IsRGB32==false) ? MM : MM.ConvertToRGB32 CY = C.Overlay(MM, mode="Add", opacity=0.8*opacity) CL = C.Overlay(M, mode="Luma", opacity=0.6*opacity) CA = C.Overlay(M, mode="Add", opacity=0.9*opacity) return (StrCmpi(mode,"cyan")==0) ? CY \ : (StrCmpi(mode,"luma")==0) ? CL \ : CA } ################################## ### ColorbarsHD2 with clip properties adjusted to match template ## ## @ tonedb - level of audio tone in dB; default -18.0 ## function ColorbarsHD2(clip T, float "tonedb") { return ColorbarsHD2(T.Width, T.Height, T.IsRGB, T.BitsPerComponent, tonedb=tonedb) \ .MatchColorFormat(T) \ .MatchAudioFormat(T) } ################################## ### [[ColorbarsHD]] with optional RGB & deep color output; size & audio level changes ### (default 1280x720, TV-range, 29.97 fps, 1 hour long, audio tone @ -18dB, 48kz float) ## ## @ width - default 1280 (ColorbarsHD default was 1288) ## @ height - default 720 ## @ rgb_out - if true, return full-range RGB32 (8bit) or PlanarRGBA; ## else (default) return TV-range YV24 (8bit) or YUV444 ## @ bits_out - output bit depth, one of (8|10|12|14|16); default 8 ## @ bits_gen - generated bit depth, one of (8|10|12|14|16); default bits_out ## @ dither - if true, add dither when down-converting from to ; default false ## @ tonedb - level of audio tone in dB; default -18.0 (was 0.0) ## ## @ version 1.1 2017-04-28 raffriff42 ## @ version 1.2 2017-05-15 add bits_gen, tonedb ## function ColorbarsHD2(int "width", int "height", bool "rgb_out", \ int "bits_out", int "bits_gen", bool "dither", float "tonedb") { wid = Default(width, 1280) hgt = Default(height, 720) rgb = Default(rgb_out, false) bto = Default(bits_out, 8) btg = Default(bits_gen, bto) dth = (Default(dither, false) && (bto < btg)) ? 0 : -1 tdb = Min(Default(tonedb, -18.0), 0.0) Assert(bto==8||bto==10||bto==12||bto==14||bto==16, \ "ColorbarsHD2: not one of (8|10|12|14|16)") Assert(btg==8||btg==10||btg==12||btg==14||btg==16, \ "ColorbarsHD2: not one of (8|10|12|14|16)") R = ColorbarsHD(wid, height, pixel_type="YUV444P"+String(btg)).AmplifyDB(tdb) R = (rgb==true) ? R.ConvertToPlanarRGBA(matrix="Rec709") : R R = (bto != btg) ? R.ConvertBits(bto, dither=dth) : R R = (bto==8 && rgb==true) ? R.ConvertToRGB32 : R R = (bto==8 && rgb==false) ? R.ConvertToYV24 : R return R } ################################## ### return grayscale ramp clip w/ same specs as template clip (AVS+) ## ## @ height - default .Height ## @ dither - if true, add dither when down-converting to ; default false ## function Grayramp(clip T, int "height", bool "dither") { hgt = Default(height, T.Height) bits = Max(10, T.BitsPerComponent) ## @ width - one of (512|1024|2048|4096) wid = (T.Width > (4096+2048)/2) ? 4096 \ : (T.Width > (2048+1024)/2) ? 2048 \ : (T.Width > (1024+512)/2) ? 1024 \ : 512 return Grayramp(wid, hgt, rgb_out=T.IsRGB, bits_out=bits, dither=dither) \ .BilinearResize(T.Width, hgt) \ .MatchColorFormat(T) \ .MatchAudioFormat(T) } ################################## ### return grayscale ramp (cropped out from [[ColorbarsHD2]]) ### (1024x180, TV-range, 29.97 fps, 1 hour long, audio 48kz float, silent) ## ## @ width - one of (512|1024|2048|4096); default 1024 ## @ height - default 180 ## @ rgb_out - if true, return full-range RGB32 (8bit) or PlanarRGBA; ## else (default) return tv-range YV24 (8bit) or YUV444 ## @ bits_out - output bit depth, one of (8|10|12|14|16); default 8 ## @ dither - if true (default), add dither for 8bit output ## ## @ version 1.1 raffriff42 2017-04-28 ## function Grayramp(int "width", int "height", bool "rgb_out", int "bits_out", bool "dither") { wid = Default(width, 1024) hgt = Default(height, 180) bto = Default(bits_out, 8) dth = Default(dither, true) Assert(wid==512||wid==1024||wid==2048||wid==4096, \ "Grayramp: not one of (512|1024|2048|4096)") ## adjust initial size so cropped grayramp is correct final size w2 = (wid==4096) ? 5466 \ : (wid==2048) ? 2732 \ : (wid==1024) ? 1366 \ : 680 ## wid==512 #@ function ColorbarsHD2(int "width", int "height", bool "rgb_out", ## int "bits_out", int "bits_gen", bool "dither", float "tonedb") R = ColorbarsHD2(w2, 720, rgb_out, bto, Max(10, bto), dither, -999.0) ## crop out the grayramp (with fine adhsustments where needed) R = (wid==4096) ? R.Crop(685, 480, -685, -180) \ : (wid==2048) ? R \ : (wid==1024) ? R.Crop(172, 480, -172, -180).AddBorders(2, 0, 0, 0, $0) \ : R.Crop( 85, 480, -84, -180).AddBorders(0, 0, 1, 0, $ffffff) ## wid==512 return R.stack_to_hgt(hgt) \ .BilinearResize(R.Width, hgt) } # http://forum.videohelp.com/threads/381327-Free-vectorscope-for-windows-64-bit?p=2467250#post2467250 ################################## ### show video waveform + vectorscope; many little enhancements; accepts both YUV & RGB ## ## @ shrink - if true (default) shrink clip+scopes & add side borders so output size = input ## if false, height increases by 256px ## @ vector - if true (default) show vectorscope [Histogram("color2")] ## else, show waveform [Histogram("classic"), rotated 90deg] only ## @ matrix - if source is RGB and =true, use this matrix; default "Rec709" ## otherwise is ignored (does not affect waveform or main image) ## @ parade - if true, show RGB waveforms side-by-side; ## else, show RGB waveforms superimposed (default true) ## ## ## version 2017-05-09 support RGB, deep color ## version 2017-05-18 matrix argument ## function VideoScopes(clip C, bool "shrink", bool "vector", string "matrix", bool "parade") { shrink = Default(shrink, true) vector = Default(vector, true) matrix = Default(matrix, "Rec709") parade = Default(parade, true) vidbits = C.BitsPerComponent wid = C.Width hgt = C.Height dth = (vidbits>8 && vidbits<32) ? 0 : -1 C = (shrink) ? C.ScaleSize(-1, hgt-256, 4) : C R = (C.IsRGB) \ ? StackVertical( \ C.KillAudio.ConvertBits(8, dither=dth).TurnRight \ ._HistRGB(parade) \ .LevelsPlus(24, 2.5, 235, 0, 255) \ .TurnLeft.ConvertBits(vidbits).MatchColorFormat(C) \ , C) \ : StackVertical( \ C.KillAudio.ConvertBits(8, dither=dth).TurnRight \ ._Hist2.Crop(C.Height, 0, 0, 0) \ .LevelsPlus(24, 2.5, 235, 0, 255) \ .TurnLeft.ConvertBits(vidbits) [* .Grayscale *] \ , C) R = (shrink) \ ? R.CropEx(wid, hgt) \ : R R = (vector) \ ? R.HistogramOverlay(C._Vector(matrix), \ align=9, opacity=(shrink ? 1.0 : 0.7)) \ : R return (C.HasAudio) \ ? R.AudioDub(C) \ : R ####################################### ### eliminate high, low and center highlighting from classic Histogram; ### no effect if explicit 'legacy' argument is specified. ## ## @ mode - one of (classic2|classic|levels|color|color2|luma|audiolevels ## |Stereo|StereoY8|StereoOverlay|''); default "classic2" ## @ factor - only valid with ="levels"; see [[Histogram]] ## @ keeplines - if true, keep one bright line at 16 & 235 (false recommended for RGB) ## function _Hist2(clip C, string "mode", float "factor", bool "keeplines") { mode = Default(mode, "classic2") keep = Default(keeplines, true) H = (mode=="classic2") \ ? C.Histogram \ : C.Histogram(mode, factor) #return H ## bypass further processing for testing ## we are cropping by odd numbers R = H.ConvertToYV24 return (mode!="classic2") ? H \ : StackHorizontal( \ R.Crop(0, 0, C.Width, 0), [* base clip *] \ R.Crop(C.Width, 0, (keep?15:16), 0) [* low zone (shaded) *] \ .ColorYUV(off_y=(-100), off_u=(128-20), off_v=(128-160)), \ R.Crop(C.Width+(keep?15:16), 0, (keep?93:92), 0), [* low-mid 1 *] \ R.Crop(C.Width+108, 0, 2, 0).Grayscale, [* low-mid 2 (green tint) *] \ R.Crop(C.Width+110, 0, 1, 0) [* mid (green line) *] \ .ColorYUV(off_y=(-84), off_u=(128-160), off_v=(128-16)), \ R.Crop(C.Width+111, 0, 2, 0).Grayscale, [* high mid 1 (green tint) *] \ R.Crop(C.Width+113, 0, (keep?124:123), 0), [* high mid 2 *] \ R.Crop(C.Width+(keep?237:236), 0, 0, 0) [* high zone (shaded) *] \ .ColorYUV(off_y=(-100), off_u=(128-20), off_v=(128-160)) \ ).MatchColorFormat(C) } ############################# ## classic Histogram for RGB ## ## @ parade - if true, show RGB waveforms side-by-side; ## else, show RGB waveforms superimposed (default true) ## @ keeplines - if true, keep one bright line at 16 & 235 (false recommended for RGB) ## function _HistRGB(clip C, bool "parade", bool "keeplines") { parade = Default(parade, true) keep = Default(keeplines, false) R = C.ExtractR._Hist2(keeplines=keep).Crop(C.Width, 0, 0, 0) G = C.ExtractG._Hist2(keeplines=keep).Crop(C.Width, 0, 0, 0) B = C.ExtractB._Hist2(keeplines=keep).Crop(C.Width, 0, 0, 0) R = (!parade) ? R : R.BicubicResize(R.Width, _mod(4, R.Height/3.0)) G = (!parade) ? G : G.BicubicResize(G.Width, _mod(4, G.Height/3.0)) B = (!parade) ? B : B.BicubicResize(B.Width, _mod(4, B.Height/3.0)) R = R.Levels(0, 1.0, 230, 0, 255, coring=false) B = B.Levels(8, 1.0, 180, 0, 255, coring=false) R = (!parade) ? R : R.AddBorders(0, 0, 0, (C.Height-R.Height)) G = (!parade) ? G : G.AddBorders(0, (C.Height-G.Height)/2, 0, (C.Height-G.Height)/2) B = (!parade) ? B : B.AddBorders(0, (C.Height-B.Height), 0, 0) return CombinePlanes(R, G, B, "RGB") } ############################# ## YUV/RGB vectorscope function _Vector(clip C, string matrix) { vidbits = C.BitsPerComponent dth = (vidbits>8 && vidbits<32) ? 0 : -1 Last = (C.IsRGB) \ ? C.ConvertToYV12(matrix=matrix) \ : C #.ConvertToYV12 ConvertBits(8, dither=dth) Histogram("color2") #hist_color_labels f = vector_graticule_image_base + matrix + ".png" G = Exist(f) ? ImageSource(f, pixel_type="RGB32") : 0 IsClip(G) ? Overlay(G, mask=G.ExtractA, x=C.Width, y=0) : Nop ConvertBits(vidbits) return Last } } ################################## ### print color channel statistics on the screen ## (AVS+ 8, 10, 12, 14, 16 bit; no float as of r2455) function Analyze(clip C) { return (C.IsRGB) \ ? C.RGBAdjust(analyze=true) \ : C.ColorYUV(analyze=true) } ################################## ### overlay a user-supplied [[Histogram]] clip ## ## @ autocrop - if true, assume a standard 256x256 ## Histogram @ top right ## (default true; use false for VD_clrtools) ## @ align - cf. [[Subtitle]] (default 7, top left) ## @ x, y - if supplied, override ## @ wid, hgt - resize the histogram (default 256, 256) ## @ opacity, mode, pc_range ## - cf. [[Overlay]] ## ## Example: (for this example, Last=RGB32) ## | CY12 = Last.ConvertToYV12(matrix="Rec709") ## | HistogramOverlay(CY12.Histogram("levels"), hgt=128, ## | \ x=8, y=8) ## | HistogramOverlay(CY12.Histogram("color2"), ## | \ x=8, y=8+128+8) ## ## @ raffriff42 Aug-2014 ## function HistogramOverlay(clip B, clip F, bool "autocrop", int "align", \ int "x", int "y", int "wid", int "hgt", int "margin", \ float "opacity", string "mode", bool "pc_range") { autocrop = Default(autocrop, true) F = (autocrop==false) ? F \ : F.Cropd(F.Width-256, 0, 256, 256) wid = Default(wid, 256) hgt = Default(hgt, 256) F = F.BicubicResize(wid, hgt) align = Default(align, -1) margin = Min(Max(-4, Default(margin, 8)), 32) x = Default(x, \ ( (align==1||align==4||align==7) ? margin \ : (align==2||align==5||align==8) ? (B.Width/2) - (wid/2) \ : (align==3||align==6||align==9) ? B.Width - (wid+margin) \ : margin) \ ) y = Default(y, \ ( (align==7||align==8||align==9) ? margin \ : (align==4||align==5||align==6) ? (B.Height/2) - (hgt/2) \ : (align==1||align==2||align==3) ? B.Height - (hgt+margin) \ : margin) \ ) pc_range = Default(pc_range, false) mode = Default(mode, "blend") opacity = Default(opacity, 1.0) return B.Overlay(F, x, y, opacity=opacity, mode=mode, pc_range=pc_range) #\ .hist_color_labels(x, y) } ################################## ### Classic [[Histogram]], waveform on top ## ## version 2017-05-17 support RGB ## ## @ parade - if true, show RGB waveforms side-by-side; ## else, show RGB waveforms superimposed (default true) ## function HistogramTurn(clip C, bool "parade") { parade = Default(parade, true) return C.VideoScopes(shrink=false, vector=false, parade=parade) } ################################## ### 8-bit [[Histogram]] for deep color ## function Histogram8(clip C, string "mode", float "factor") { vidbits = C.BitsPerComponent dth = (vidbits>8 && vidbits<32) ? 0 : -1 return (vidbits==8) \ ? C.Histogram(mode, factor) \ : StackHorizontal(C, \ C.ConvertBits(8, dither=dth) \ .Histogram(mode, factor) \ .Crop(C.Width, 0, 0, 0) \ .ConvertBits(vidbits) \ ) } ################################## ### show original & 3 channels in quad split ### C (full color) & G (or Y) channel on top, ### with B (or U) & R (or V) below (AVS+) ## ## @ analyze - if true, print color channel statistics ## ## see also: https://forum.doom9.org/showthread.php?p=1676821 ## (show U+/U-, V+/V- seperately) ## function ShowChannels(clip C, bool "analyze") { analyze = Default(analyze, false) return (C.IsRGB) \ ? C._showChannelsRGB(analyze) \ : C._showChannelsYUV(analyze) ################################## ### show quad split ### C (full color) & G channel on top, ### with B & R below (AVS+) ## function _showChannelsRGB(clip C, bool "analyze") { Assert(C.IsRGB, \ "ShowChannelsRGB: source must be RGB") analyze = Default(analyze, false) vidbits = C.BitsPerComponent dth = (vidbits>8 && vidbits<32) ? 0 : -1 C BicubicResize(Width/2, Height/2) G = ExtractG.ConvertBits(8, dither=dth).ConvertToRGB32 R = ExtractR.ConvertBits(8, dither=dth).ConvertToRGB32 B = ExtractB.ConvertBits(8, dither=dth).ConvertToRGB32 (analyze) ? Last.Analyze : Last ConvertBits(8, dither=dth).ConvertToRGB32 StackVertical( \ StackHorizontal(Last, G), \ StackHorizontal(B, R) \ ) Subtitle( \ "C G\nB R", align=5, \ size=Height/16, \ text_color=$e0e0e0, lsp=0) return MatchColorFormat(C) } ################################## ### show quad split: ### C (full color) & Y channel on top, ### with U & V below (AVS+) ## function _showChannelsYUV(clip C, bool "analyze") { Assert(C.IsYUV, \ "ShowChannelsYUV: source must be YUV") analyze = Default(analyze, false) C ConvertToYUV420 O = Last U = UtoY V = VtoY BicubicResize(Width/2, Height/2) Y = Grayscale StackVertical( \ StackHorizontal((analyze ? Last.Analyze : Last), Y), \ StackHorizontal(U, V) \ ) Subtitle( \ "C Y\nU V", align=5, \ size=Height/16, \ text_color=$e0e0e0, lsp=0) return MatchColorFormat(O) } } ################################## ### [[ShowFrameNumber]] with support for and ### ( argument always false unless <0) ## ## @ opacity - @see [[Overlay]] ## @ align - @see [[Subtitle]] ("number pad notation") default 2 ## function ShowFrameNumberAligned( \ clip C, bool "scroll", int "offset", \ float "x", float "y", string "font", int "size", \ int "text_color", int "halo_color", \ float "font_width", float "font_angle", \ float "opacity", int "align") { scr = Default(scroll, true) off = Default(offset, 0) x = Default(x, 0) y = Default(y, 0) fnt = Default(font, "Arial") siz = Default(size, 24*Round(C.Width/1024.0)) txt = Default(text_color, $ffff00) hal = Default(halo_color, $0) wid = Default(font_width, 0) ang = Default(font_angle, 0.0) opc = Default(opacity, 1.0) aln = Default(align, 2) number_width = 6*siz ## approximate width of frame number (assume avg. 6 digits) xa = (aln==3 || aln==6 || aln==9) ? x + C.Width - number_width \ : (aln==2 || aln==5 || aln==8) ? x + (C.Width/2) - (number_width/2) \ : x + siz ya = (aln==1 || aln==2 || aln==3) ? y + C.Height - siz \ : (aln==4 || aln==5 || aln==6) ? y + (C.Height/2) - (siz/2) \ : y + siz + siz/2 return (align<0) \ ? C.ShowFrameNumber(scroll=scr, offset=off, \ x=x, y=y, font=fnt, size=siz, \ text_color=transparent_color(opc, txt), \ halo_color=transparent_color(opc, hal), \ font_width=wid, font_angle=ang) \ : C.ShowFrameNumber(scroll=false, offset=off, \ x=xa, y=ya, font=fnt, size=siz, \ text_color=transparent_color(opc, txt), \ halo_color=transparent_color(opc, hal), \ font_width=wid, font_angle=ang) } ### fka UUStackTwoAcrossCropped ################################## ### make side-by-side split clip; crop input images to fit in original width ## (normally, for comparison or preview purposes) ## ## @ S - sound source [default = ] ## @ border - border subtracted from clips [default = 8] ## @ pan - range = -1.0 to +1.0: ## -1.0=left, 0.0=center, +1.0=right; [default = 0.0] ## @ wid - output width [default = input width] ## @ titleA, B - short identifier strings for debugging ## @ align - see [[Subtitle]]: align ## ## @version 1.0 28-May-2012 raffriff42 ## @version 1.1 02-Apr-2014 (@ wid) ## function StackTwoAcrossCropped(clip A, clip B, clip "S", \ int "border", float "pan", int "wid", \ string "titleA", string "titleB", int "align") { S = Default(S, A) inWid = Default(wid, A.Width) inHgt = Min(A.Height, B.Height) border = _mod(4, Default(border, 8)) border2 = Int(border / 2) pan = Min(Max(-1.0, Float(Default(pan, 0.0))), 1.0) * 0.5 cropWid = _mod(2, Float(inWid) / 2.0) align = Default(align, 7) ## no default for labels A = A.CropPanTilt(cropWid-border2, inHgt, pan, 1.0, titleA, align) B = B.CropPanTilt(cropWid-border2, inHgt, pan, 1.0, titleB, align) A = A.AddBorders(0, 0, border2, 0) B = B.AddBorders(border2, 0, 0, 0) R = StackHorizontal(A, B).CropEx(inWid, inHgt) return (S.IsClip==false) ? R \ : (S.AudioChannels==0) ? R \ : AudioDub(R, S) } __END__