View Full Version : MaskTools blending modes - dodge, burn, etc, etc
raffriff42
17th April 2014, 04:53
Our discussion in this thread (http://forum.doom9.org/showthread.php?t=170464) got me interested in the various overlay modes you see in Photoshop etc, and if they can be done in Avisynth with MaskTools. Well, I knew they could, but I didn't know how to do it. Also, I wanted to check whether the published formulas for these modes were correct. I went to five sources...
Photoshop Blend Modes Explained (http://photoblogstop.com/photoshop/photoshop-blend-modes-explained)
Gimp: Layer Modes (http://docs.gimp.org/2.8/en/gimp-concepts-layer-modes.html)
Let's Learn Math - Photoshop Blend Modes (http://www.venture-ware.com/kevin/coding/lets-learn-math-photoshop-blend-modes/)
Insight into Photoshop 7.0 Blending Modes (http://dunnbypaul.net/blends/)
Blend modes - Wikipedia (http://en.wikipedia.org/wiki/Blend_modes)
...and transcribed their formulas to MaskTools LUT expressions as best I could. Some did not work at all - whether due to my transcription error or theirs, I don't know. I was able to get quite a few to work, though. Here they are in a script, together with a neat test routine that Interleave's all the blend modes, letting you do a quick comparison in frame jog.
https://www.dropbox.com/s/uyr5glpxe0a501x/mt_blend_sample_1-blend-small.jpg?raw=1 https://www.dropbox.com/s/4lofxnblb7rub0x/mt_blend_sample_2-linear-small.jpg?raw=1
https://www.dropbox.com/s/jjps4thwgwa14rr/mt_blend_sample_3-cdodge-small.jpg?raw=1 https://www.dropbox.com/s/0a0eci224qyo5e6/mt_blend_sample_4-divide-small.jpg?raw=1
First, the test script: #A = (any video or image source)
#B = (any video or image source)
## RGB32 or YV12 only
A = A.Crop(0, 0, -A.Width % 4, -A.Height % 4)
A = (A.IsRGB) ? A.ConvertToRGB32 : A.ConvertToYV12
## overlay size & format must match
B = B.BicubicResize(A.Width, A.Height)
B = (A.IsRGB) ? B.ConvertToRGB32 : B.ConvertToYV12
## OPTIONS
opacity=0.5
cmode=2 ## affects YV12 only (2=passthrough; 3=process)
showargs=true
## TEST
#@function uu_mt_blend_allmodes(clip A, clip B, float opacity,
##\ int cmode, bool "showargs")
#return uu_mt_blend_allmodes(A, B, opacity, cmode, showargs)
## compare effect of cmode (YV12 only)
return Interleave(
\ uu_mt_blend_allmodes(A, B, opacity, 2, showargs)
\ , uu_mt_blend_allmodes(A, B, opacity, 3, showargs)
\ )
/*
*/
## PRODUCTION
mode="blend"
#@function uu_mt_blend(clip C, clip D, float "opacity",
##\ string "mode", int "cmode", bool "showargs")
return uu_mt_blend(A, B, opacity, mode, cmode)
(continued...)
raffriff42
17th April 2014, 04:54
"uu_mt_blend.avsi" ### TABLE OF CONTENTS ###
#@function uu_mt_blend(clip C, clip D,
##\ float "opacity", string "mode",
##\ int "cmode", bool "showargs")
#@function uu_mt_blend_allmodes(clip A, clip B, float opacity,
##\ int cmode, bool "showargs")
#@function blendtest(clip A, clip B, float opacity,
##\ int cmode, bool showargs, string mode)
#@function uu_mt_rgblutxy(clip C, clip D,
##\ string Rexpr, string Gexpr, string Bexpr, bool "mt_polish",
##\ bool "showargs", int "showargswidth")
#@function uumod(int m, float f, bool "z")
#@function StrReplace(string base, string findstr, string repstr)
#@function SplitLines(string s, int "lastcol", int "cc")
#@function TrimLeftStr(String s)
# http://forum.doom9.org/showthread.php?t=170490
#######################################
### blend (overlay, layer) two clips with MaskTools
## (fka "UUOverlayMT")
##
## @ opacity - see Avisynth: /Overlay/
##
## @ mode - listed by broad category (see ref. [b] below)
## (normal blending modes)
## * "blend" (default)
## (darkening modes)
## * "darken"
## * "multiply"
## * "cburn" (/color burn/)
## * "lburn" (/linear burn/)
## * "subtract" (Avisynth /Overlay/ mode="subtract")
## (lightening modes)
## * "lighten"
## * "screen"
## * "cdodge" (/color dodge/)
## * "ldodge" (/linear dodge/ aka /add/)
## * "divide"
## (contrast modes)
## * "overlay"
## * "softlight"
## * "hardlight"
## * "vivid" (/vivid light/)
## * "pinlight"
## * "linear"
## * "gextract" (Gimp /GrainExtract/, Avisynth /Subtract/)
## * "gmerge" (Gimp /Grain Merge/)
## (inversion modes)
## * "exclude"
## (cancellation modes)
## * "absdiff"
## * "absdiff4" (absolute diff. x4)
## * "absdiff16" (absolute diff. x16)
##
## @ cmode (does not apply if C.IsRGB) (see MaskTools docs)
## -255..0 : all the pixels of the plane will be set to -x (-128 = gray)
## 1 : the plane will not be processed. Content will be invalid.
## 2 : the plane of the first input clip will be copied (the default)
## 3 : the plane will be processed with the processing the filter is designed to do.
## 4 : the plane of the second input clip will be copied.
##
## Source for the math:
## [b] Photoshop Blend Modes Explained
## http://photoblogstop.com/photoshop/photoshop-blend-modes-explained
## [g] Gimp: Layer Modes
## http://docs.gimp.org/2.8/en/gimp-concepts-layer-modes.html
## [m] Let's Learn Math - Photoshop Blend Modes
## http://www.venture-ware.com/kevin/coding/lets-learn-math-photoshop-blend-modes/
## [p] Insight into Photoshop 7.0 Blending Modes
## http://dunnbypaul.net/blends/
## [w] Blend modes
## http://en.wikipedia.org/wiki/Blend_modes
##
function uu_mt_blend(clip C, clip D,
\ float "opacity", string "mode", int "cmode", bool "showargs")
{
Assert(IsClip(C) && HasVideo(C),
\ "uu_mt_blend: clip 'C' invalid")
Assert(IsClip(D) && HasVideo(D),
\ "uu_mt_blend: clip 'D' invalid")
Assert(C.Width==D.Width && C.Height==D.Height,
\ "uu_mt_blend: clips not same size")
Assert(C.IsRGB32 || C.IsYV12, "uu_mt_blend: "
\ + "clip 'C' must be RGB32 or YV12")
Assert(D.IsRGB32 || D.IsYV12, "uu_mt_blend: "
\ + "clip 'D' must be RGB32 or YV12")
Assert(C.IsRGB==D.IsRGB, "uu_mt_blend: "
\ + "both clips must be RGB32, or both must be YV12")
opacity = Float(Min(Max(0, Default(opacity, 1)), 1))
mode = LCase(Default(mode, "blend"))
cmode = Default(cmode, 2)
showargs = Default(showargs, false)
/* [p][m][g] */
s = (mode=="absdiff") ? "abs($x - $y)"
\ : (mode=="absdiff4") ? "4 * abs($x - $y)"
\ : (mode=="absdiff16") ? "16 * abs($x - $y)"
\
\ /* [p][m][g] */
\ : (mode=="cburn") ? "($y<0.00025) ? 0 "
\ + ": (1 - (1-$x) / $y)"
\ /* [p][m][g] */
\ : (mode=="cdodge") ? "(abs(1-$y)<0.00025) ? 0 "
\ + ": $x / (1-$y)"
\ /* [p][m][w][g] */
\ : (mode=="darken") ? "min($x, $y)"
\
\ /* [p](revised) */
\ : (mode=="divide") ? "($y<0.00025) ? 1 : ($x / $y)"
\
\ /* [p] */
\ : (mode=="exclude") ? "0.5 - 2 * ($x-0.5) * ($y-0.5)"
\
\ /* [g] */
\ : (mode=="gextract") ? "$x - $y + 0.5"
\
\ /* [g] */
\ : (mode=="gmerge") ? "$x + $y - 0.5"
\
\ /* [p][g] (alt) */
\ : (mode=="hardlight") ? "($y > 0.5) "
\ + "? (1 - (1-$x) * (1 - 2 * ($y-0.5))) "
\ + ": (2 * $y * $x)"
\ /* [p][m] */
\ : (mode=="lburn") ? "$y + $x - 1"
\
\ /* [p][g]("add") */
\ : (mode=="ldodge") ? "$x + $y"
\
\ /* [w][g] */
\ : (mode=="lighten") ? "max($x, $y)"
\
\ /* [p] */
\ : (mode=="linear") ? "($y > 0.5) "
\ + "? ($x + (2 * ($y-0.5))) "
\ + ": ($x + 2 * $y - 1)"
\ /* [p][m] */
\ : (mode=="multiply") ? "$x * $y"
\
\ /* [b] */
\ : (mode=="overlay") ? "$x * ($x + (2*$y) * (1-$x))"
\
\ /* [m][w][p] */
\ : (mode=="pinlight") ? "($y > 0.5) "
\ + "? (max($x, 2 * ($y-0.5))) "
\ + ": (min($x, 2 * $y))"
\ /* [p][m][w] */
\ : (mode=="screen") ? "1 - ((1-$y) * (1-$x))"
\
\ /* [p] */
\ : (mode=="softlight") ? "($y > 0.5) "
\ + "? (1 - (1-$x) * (1 - ($y-0.5))) "
\ + ": ($x * ($y+0.5))"
\ /* [p][g] */
\ : (mode=="subtract") ? "$x - $y"
\
\ /* [p](revised) */
\ : (mode=="vivid") ? "($y > 0.5) "
\ + "? ((1-$x) / (1 * ($y-0.5))) "
\ + ": ($x / (1.005 - (2 * $y)))"
\
\ : (mode=="blend" || mode=="") ? "$y"
\
\ : Assert(false, "uu_mt_blend: mode argument not recognized")
s0 = s
## apply overall blend
s = "((1-$o) * $x) + ($o * (" + s + ")"
## scale input from 0-255 to 0-1;
s = s.StrReplace("$x", "(x/256)" )
\ .StrReplace("$y", "(y/256)")
\ .StrReplace("$o", String(opacity, "%0.3f"))
s = "256 * (" + s + ")"
sp = ("(" + s + ")").mt_polish()
## ...strings 's0' and 's' after this point used for 'showargs' only
R = (C.IsRGB) ? uu_mt_rgblutxy(C, D, sp, sp, sp)
\ : mt_lutxy(C, D, expr=sp, U=cmode, V=cmode)
return (showargs==false) ? R
\ : R.Subtitle(SplitLines(s0+"\n \n"+s+"\n \n"+sp, 70),
\ size=(R.Height*36.0/720),
\ text_color=$80c0c0c0,
\ lsp=0, align=7)
}
#######################################
function uu_mt_blend_allmodes(clip A, clip B, float opacity,
\ int cmode, bool "showargs")
{
showargs = Default(showargs, true)
frame_count = A.Framecount
A = A.SelectEvery(23, 0)
B = B.SelectEvery(23, 0)
Interleave(
\ /* blend */
\ blendtest(A, B, opacity, cmode, showargs, "blend"),
\ blendtest(A, B, opacity, cmode, showargs, "gmerge"),
\ blendtest(A, B, opacity, cmode, showargs, "linear"),
\ blendtest(A, B, opacity, cmode, showargs, "hardlight"),
\ blendtest(A, B, opacity, cmode, showargs, "softlight"),
\ blendtest(A, B, opacity, cmode, showargs, "pinlight"),
\ blendtest(A, B, opacity, cmode, showargs, "overlay"),
\ /* lighten */
\ blendtest(A, B, opacity, cmode, showargs, "lighten"),
\ blendtest(A, B, opacity, cmode, showargs, "screen"),
\ blendtest(A, B, opacity, cmode, showargs, "ldodge"),
\ blendtest(A, B, opacity, cmode, showargs, "cdodge"),
\ blendtest(A, B, opacity, cmode, showargs, "vivid"),
\ blendtest(A, B, opacity, cmode, showargs, "divide"),
\ /* darken */
\ blendtest(A, B, opacity, cmode, showargs, "cburn"),
\ blendtest(A, B, opacity, cmode, showargs, "lburn"),
\ blendtest(A, B, opacity, cmode, showargs, "subtract"),
\ blendtest(A, B, opacity, cmode, showargs, "gextract"),
\ blendtest(A, B, opacity, cmode, showargs, "multiply"),
\ blendtest(A, B, opacity, cmode, showargs, "darken"),
\ /* other */
\ blendtest(A, B, opacity, cmode, showargs, "exclude"),
\ blendtest(A, B, opacity, cmode, showargs, "absdiff"),
\ blendtest(A, B, opacity, cmode, showargs, "absdiff4"),
\ blendtest(A, B, opacity, cmode, showargs, "absdiff16")
\ )
return Trim(0, -frame_count)
}
#######################################
function blendtest(clip A, clip B, float opacity,
\ int cmode, bool showargs, string mode)
{
C = BlankClip(A)
try {
C = uu_mt_blend(A, B, mode=mode,
\ opacity=opacity, cmode=cmode, showargs=showargs)
}
catch(err_msg) {
C = A.Subtitle(
\ SplitLines(err_msg+"\n"+mode, 40),
\ size=(36.0*A.Height/720),
\ align=5, text_color=$ffffff, lsp=0)
}
StackHorizontal(
\ C.ConvertToRGB32(),
\ StackVertical(
\ A.ConvertToRGB32().BicubicResize(C.Width/2, C.Height/2),
\ B.ConvertToRGB32().BicubicResize(C.Width/2, C.Height/2)
\ ))
BicubicResize(C.Width, uumod(4, Float(Height)*C.Width/Width))
Addborders(0, uumod(2, 0.5*(C.Height-Height)), 0, 0)
Addborders(0, 0, 0, C.Height-Height)
#return Subtitle(mode+" ("+String(opacity, "%0.2f")+")",
return Subtitle(mode+" (opacity="+String(opacity, "%0.2f")+", cmode="+String(cmode)+")",
\ size=(Height*36.0/720),
\ align=7, text_color=$c0c0c0)
}
################# library routines #####################
#######################################
### RGB version of mt_lutxy
## (aka "UUmt_rgblutxy" & "UUmt_rgblutxy_d")
##
function uu_mt_rgblutxy(clip C, clip D,
\ string Rexpr, string Gexpr, string Bexpr,
\ bool "showargs", int "showargswidth")
{
Assert(C.IsRGB, "uu_mt_rgblutxy: source must be RGB")
showargs = Default(showargs, false)
wid = Max(25, Default(showargswidth, 60))
R = (C.IsRGB24)
\ ? MergeRGB(
\ mt_lutxy(C.ShowRed("YV12"), D.ShowRed("YV12"),
\ yexpr=Rexpr,
\ U=-128, V=-128),
\ mt_lutxy(C.ShowGreen("YV12"), D.ShowGreen("YV12"),
\ yexpr=Gexpr,
\ U=-128, V=-128),
\ mt_lutxy(C.ShowBlue("YV12"), D.ShowBlue("YV12"),
\ yexpr=Bexpr,
\ U=-128, V=-128)
\ )
\ : MergeARGB(
\ C.ShowAlpha("YV12"),
\ mt_lutxy(C.ShowRed("YV12"), D.ShowRed("YV12"),
\ yexpr=Rexpr,
\ U=-128, V=-128),
\ mt_lutxy(C.ShowGreen("YV12"), D.ShowGreen("YV12"),
\ yexpr=Gexpr,
\ U=-128, V=-128),
\ mt_lutxy(C.ShowBlue("YV12"), D.ShowBlue("YV12"),
\ yexpr=Bexpr,
\ U=-128, V=-128)
\ )
return (showargs==false) ? R
\ : R.Subtitle(SplitLines(Rexpr, wid), lsp=0, align=7)
\ .Subtitle(SplitLines(Gexpr, wid), lsp=0, align=4)
\ .Subtitle(SplitLines(Bexpr, wid), lsp=0, align=1)
}
##################################
## return argument 'f' as integer and ensure it is Mod 'm'
##
## @ m - mod value
## @ f - input (may be positive or negative)
## @ z - always mod towards zero after rounding (default true)
##
## Examples:
## uumod(2, 3.4) == 2
## uumod(2, 3.6) == 4
## uumod(2, -3.4) == -2
## uumod(2, -3.6) == -4
##
## uumod(2, 3) == 2
## uumod(2, 3, false) == 4
## uumod(2, -3) == -2
## uumod(2, -3, false) == -4
##
function uumod(int m, float f, bool "z")
{
m = Max(1, m)
mult = Default(z, true) ? -1 : 1
fsgn = Sign(f)
i = Round(Abs(f))
return fsgn * Max(0, i + mult * (i % m))
}
# http://avisynth.nl/index.php/HDColorBars
##################################
##
function StrReplace(string base, string findstr, string repstr)
{
pos = FindStr(base, findstr)
return (StrLen(findstr)==0) || (pos==0)
\ ? base
\ : StrReplace(
\ LeftStr(base, pos-1) + repstr +
\ MidStr(base, pos+StrLen(findstr)),
\ findstr, repstr)
}
##################################
## 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 /lsp/ argument)
##
## @ lastcol - maximum line length before breaking (default 80)
## @ cc - line count, for internal use; leave it alone
##
## On return, global variable /splitlines_count/
## is set to the number of lines in the string
## this will be needed if you want to bottom-align.
## (splitlines_count==1 means one line & no breaks)
##
## Suggest caller remove line breaks from source argument with:
## StrReplace(Chr(10), "")
## StrReplace(Chr(13), " ")
##
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)<lastcol) ? s :
#\ TrimLeftStr(LeftStr(s, lastcol)) + "\n"
#\ + SplitLines(MidStr(s, lastcol+1), lastcol, counter+1)
## a little smarter
p = FindStr(RevStr(LeftStr(s, lastcol)), " ")
return (StrLen(s)<lastcol)
\ ? TrimLeftStr(s)
\ : (p < 12)
\ ? TrimLeftStr(LeftStr(s, lastcol-p)) + "\n" +
\ SplitLines(MidStr(s, lastcol-p+1), lastcol, cc+1)
\ : TrimLeftStr(LeftStr(s, lastcol)) + "\n" +
\ SplitLines(MidStr(s, lastcol+1), lastcol, cc+1)
}
##################################
## SplitLines helper: trim spaces from left side of string
##
function TrimLeftStr(String s)
{
return (LeftStr(s, 1)==" ") ? TrimLeftStr(MidStr(s, 2)) : s
}
__END__
Reel.Deel
7th October 2015, 01:56
Hi raffriff42, I was wondering what's the RPN expression for lighten (2 clips only no mask)? I looked at the script but couldn't decipher it.
raffriff42
7th October 2015, 04:17
Simply "max(x, y)".mt_polish
WorBry
7th October 2015, 04:33
I put something similar together a few years back.
http://forum.doom9.org/showthread.php?p=1400434#post1400434
http://blog.niiyan.net/post/607381609/blendmtalpha3
It was my first stab at writing a "more-involved" function, and I relied heavily on Didee for the computations and Gavino for the script syntax. I use it every now and then, along with a couple of other functions I put together at the time, for grading and simulating 'filmic' looks:
http://blog.niiyan.net/post/556119444/stint-v1
http://blog.niiyan.net/post/666717637/selsahv3
I'm particularly fond of the "Softlight" mode (at relatively low opacity 0.2 - 0.3) for giving a little filmic punch to footage off my AVCHD camcorder (Canon HF-G10) and at higher opacity (around 0.8) it does a very nice job bringing to life recordings shot in Cine Mode, which has a fairly flat contrast profile. Can't say I've used the other modes that much. I contemplated adding another mode (not sure what you would call it) to create a reverse S-curve, for dampening down high contrast material, but SmoothAdjust is the ideal tool-set for that.
Anyhow, it's the wee hours here, so I'll do some testing with your function(s) tomorrow. Just glancing over your script though, would I be right in concluding that you've also managed to include chroma in the blend transformations? If so, excellent !!
raffriff42
7th October 2015, 12:53
Just glancing over your script though, would I be right in concluding that you've also managed to include chroma in the blend transformations? If so, excellent !!Thanks, well you can process the U & V channels (using the same LUT) with cmode=3, but this would look wrong for most effects - blend excepted. For proper color processing, RGB32 is supported.
(EDIT there was another recent "lighten" thread - here (http://forum.doom9.org/showthread.php?t=172475))
Reel.Deel
7th October 2015, 14:53
Simply "max(x, y)".mt_polish
Hmm, I wonder why showargs=true shows something a bit different:
256 * max((x/256.0), (y/256.0))
x 256.0 / y 256.0 / max 256 * 1.0 * x 1 1.0 - * + round
:confused:
Thanks, well you can process the U & V channels (using the same LUT) with cmode=3, but this would look wrong for most effects - blend excepted. For proper color processing, RGB32 is supported.
I was also under the impression that it processed chroma correctly, I guess not. Is this because chroma is centered around 128? Would be interesting to find out how Overlay (https://github.com/jeeb/avisynth/tree/92a7c76705662776ed2d8ee0c5726439ed084517/src/filters/overlay) does this internally, I know it only works in YV24 but in YUV nonetheless.
I'm particularly fond of the "Softlight" mode (at relatively low opacity 0.2 - 0.3) for giving a little filmic punch to footage off my AVCHD camcorder (Canon HF-G10) and at higher opacity (around 0.8) it does a very nice job bringing to life recordings shot in Cine Mode, which has a fairly flat contrast profile.
I often shoot with the Cinestyle (http://www.technicolor.com/en/solutions-services/cinestyle) profile on my Canon, will give it a try. Usually I use this function here: http://forum.doom9.org/showthread.php?t=162797
WorBry
7th October 2015, 14:59
Thanks, well you can process the U & V channels (using the same LUT) with cmode=3, but this would look wrong for most effects - blend excepted. For proper color processing, RGB32 is supported.
Yes, I included cmode=3 in the computations also, but with a caveat emptor that it is prone to result in "marked U and V shifts with undesirable colour casts".....unless of course you're looking for some unpredictable, wacky 'cross process' look.
For some reason, at the time, I was in this purist 'got to keep it all in YV12' mindset. Most of the AVISynth grading routines I use currently involve dipping into RGB at some point.
On that note, what would be really nice is if someone could come up with an easier way of importing (if not creating) RGB adjustment curves in AVISynth i.e. PS/AE acv/amp and Gimp curve files....maybe KDenlive (FreiOr) xml curves also, if that's at all possible.
For me, still the only reliable method is opening or creating acv files with the Virtual Dub Gradation Curves plugin, saving the processing settings and then copy pasting the mile-long curves configuration string into the AVISynth script via LoadVirtualDub plugin. Rather tedious when it comes to tweaking. I've never been able to get the (original) MaskTools RGBLut to work properly for importing amp files that have distinct curves for the R,G and B channels. Same goes for GiCoCu.
there was another recent "lighten" thread - here (http://forum.doom9.org/showthread.php?t=172475))
Thanks. I'll have a look at that also. Not much time for testing today.
WorBry
7th October 2015, 16:10
I often shoot with the Cinestyle (http://www.technicolor.com/en/solutions-services/cinestyle) profile on my Canon, will give it a try. Usually I use this function here: http://forum.doom9.org/showthread.php?t=162797
Thanks, yes, I tried the CineStyle LUT correction amp file with some (HF-G10) CineMode footage and the S-curve is way too strong. Clearly the gamma profile of the CineStyle mode available on Canon DSLR's is quite distinct from the CineMode that has been perpetuated on their Vixia/Legria line of HD camcorders. The received wisdom is that Cinemode, at least as it was first applied on the early HV20 (HDV) models, was an emulation of the Cine.V(Preset#8) used on the the Canon XH A1 and G1 models:
http://www.dvinfo.net/forum/canon-vixia-series-avchd-hdv-camcorders/96017-hv20-cine-mode-not-2.html#post693857
To what extent that holds for the current AVCHD models, I'm not sure, but my experience with CineMode on the HF-G10 is that you need a decent gamma lift and moderate S-curve to bring it to life without knocking out shadow and highlight details. That's where Softlight works well.
raffriff42
7th October 2015, 16:46
Hmm, I wonder why showargs=true shows something a bit different:
256 * max((x/256.0), (y/256.0))
x 256.0 / y 256.0 / max 256 * 1.0 * x 1 1.0 - * + round
:confused:I scaled internal working range from (0, 255) to (0.0, 1.0). Support for opacity adds some cruft as well.
WorBry
7th October 2015, 19:38
@raffriff42
Apologies - just noted that you wrote the original function 18 months ago :rolleyes:
Shame I didn't pick up on it earlier, but I don't visit these pages as avidly as I used to.
WorBry
8th October 2015, 04:53
I ran a few tests with some YV12 clips comparing (with mt_MakeDiff) the luma curves created (at full opacity) by the "equivalent' modes in uu_mt_blend and Blend_MT-alpha3 and there are differences in all three of the bi-phasic modes (Overlay, Hardlight, Softlight) and (L)Burn and L(Dodge).
What's interesting is that it would appear that:
Overlay in uu_mt_blend is the same as Softlight in Blend_MT-alpha3. The mt_lutxy expression I used for Softlight was derived from the formula used in Gimp.
mt_lutxy(c1, c2, "256 x - x y * 256 / * x 256 256 x - 256 y - * 256 / - * + 256 /", y=3, u=2, v=2)
Hardlight in uu_mt_blend is the same as Overlay in Blend_MT-alpha3. For Overlay I used Didee's "128-centred contrast multiply, commuted" expression:
mt_lutxy(c1,c2,"x 128 < y x * 128 / x 128 > 256 256 y - 128 x 128 - - abs * 128 / - y ? ?",U=2,V=2)
and for Hardlight I used the reverse (with respect to layers) "128-centred contrast multiply":
mt_lutxy(c1,c2,"y 128 < x y * 128 / y 128 > 256 256 x - 128 y 128 - - abs * 128 / - x ? ?",U=2,V=2)
If you trace (more trudge) through that forum thread I linked to, starting from here....
http://forum.doom9.org/showthread.php?p=1387067#post1387067
....you'll see I debated whether the "128-centred contrast multiply" expression represented Hardlight or Overlay and in the end I plumbed for the former on the basis of which clip is the top and which is the base. Reaching a satisfactory expression for Softlight took even longer; according to the Gimp formula, Softlight is essentially a composite of Dodge and Burn. Interesting that your Overlay mode gives the same result as Softlight mode in Blend_MT-alpha3, yet your Hardlight doesn't. When the top and base clips are identical, Overlay and Hardlight should give exactly the same result !
Anyhow, make of that what you will. I'm not particularly inclined to try and re-educate myself on Reverse Polish to figure it all out.
Cheers.
raffriff42
8th October 2015, 14:26
Yikes, I found some major bugs in my post #2.
1) replace sp = s.mt_polish() with sp = ("("+s+")").mt_polish()
(Not having those parens was completely ruining hardlight and a number of others. You can't have too many parens with mt_polish, amirite (http://forum.doom9.org/showthread.php?p=1666933#post1666933)?)
2) replace mt_lutxy(C, D, yexpr=sp, U=cmode, V=cmode) with mt_lutxy(C, D, expr=sp, U=cmode, V=cmode)
(cmode was not working before at all)
EDIT
3) replace .StrReplace("$y", "(y/256.0)" ) with .StrReplace("$y", "((1-$o)*x/256.0 + $o*y/256.0)")
(Opacity was not correct. This was an old fix, but the post was never updated.)
(Note opacity was still wrong. Finally fixed 12th December 2015)
Sorry about that! I made a number of minor tweaks to the debug output too. Post #2 is fixed now, I hope.
There seems to be a lot of different published formulas for hardlight etc -- I dunno, use whatever looks best to you :confused:
WorBry
8th October 2015, 16:09
I'm getting a syntax error on line #257 of your updated uu_mt_blend script
return Subtitle(mode+" ("+String(opacity, "%0.2f")+")",
raffriff42
8th October 2015, 16:31
whoops, delete that line (will fix the post again)
WorBry
8th October 2015, 16:54
Now nothing works :(
Using the same source YV12 clip for top and base layer, all of the modes (including straight 'blend') give a blank white screen at 1.0 opacity.
raffriff42
8th October 2015, 20:16
OK sorry, I had a line missing in the online version. Re-downloaded the online script, diffed it with my working copy & found the problem. All should be good now.
WorBry
9th October 2015, 14:25
Yes, it's up and working. Thanks :)
The same differences exist between uu_mt_blend and Blend_MT_alpha3 in the bi-phasic blend modes, but as you say:
There seems to be a lot of different published formulas for hardlight etc -- I dunno, use whatever looks best to you
Same goes for Softlight. In this description of the Gimp Softlight mode it even states that - "In some versions of GIMP, “Overlay” mode and “Soft light” mode are identical"
http://docs.gimp.org/en/gimp-concepts-layer-modes.html
As for Dodge and Burn, it appears that LBurn and LDodge in uu_mt_blend are the same as Subtractive and Additive in Blend_MT_alpha3.
Anyhow, since I do intend to use uu_mt_blend for RGB processing especially, I put together a table summarizing the equivalent blend modes (attached below), for reference.
Cheers.
Reel.Deel
11th October 2015, 15:47
Can a moderator please approve WorBry's attachment?
wonkey_monkey
11th October 2015, 16:08
Yikes, I found some major bugs in my post #2.
(You can't have too many parens with mt_polish, amirite?)
Cuh, kids these days. I blame the parens.
WorBry
11th October 2015, 16:33
Cuh, kids these days. I blame the parens.
That joke was parenthetic :D
Pending the attachment - the blend mode equivalencies, for YUV luma, that is:
uu_mt_blend Blend_MT_alpha3
------------------------------------------------
Overlay Softlight
Hardlight Overlay
Softlight No equivalent
No equivalent Hardlight
Multiply Multiply
Screen Screen
Lighten Lighten
Darken Darken
CBurn Burn
CDodge Dodge
LBurn Subtractive
LDodge Additive
raffriff42
12th October 2015, 12:13
That joke was parenthetic :DBad parening is no joke, it's a serious problem :D
(when I said that (http://forum.doom9.org/showthread.php?p=1742040#post1742040) about the "parens" (OK, if you insist, "parentheses"), I was thinking of Gavino's post here (http://forum.doom9.org/showthread.php?p=1666933#post1666933))
I like your names better actually.
Gavino
12th October 2015, 12:54
(when I said that (http://forum.doom9.org/showthread.php?p=1742040#post1742040) about the "parens" (OK, if you insist, "parentheses"), I was thinking of Gavino's post here (http://forum.doom9.org/showthread.php?p=1666933#post1666933))
I thought about that too when I first saw what you had done.
Even with the bug I discovered, it should never be necessary to add parens round the whole outer expression, but I suppose it does no harm just in case there is another similar problem with mt_polish(). As you say, you can never have too many!
wonkey_monkey
12th October 2015, 14:12
I was thinking of Gavino's post here (http://forum.doom9.org/showthread.php?p=1666933#post1666933))
Tricky, implementing forward conditionals like that - it goes against the spirit of RPN in a way. I think it needs a concept of endif as well as else ( : ) to make it unambiguous.
Edit: oh, it's an mt_polish thing. Still, I think it's ambiguous as originally written, so I'm not sure it's a bug.
WorBry
12th October 2015, 17:21
I've put uu_mt_blend to good use in some grading tests:
https://vimeo.com/142090498
Script - a bit clutzy but it does the job:
LoadPlugin("Path.......\DGDecodeIM.dll")
LoadPlugin("Path.......\SmoothAdjust.dll")
LoadPlugin("Path.......\Hue.dll")
#LoadVirtualdubplugin("Path.......\Hue\Hue.vdf", "VD_Hue_Sat", 1)
LoadPlugin("Path.......\Hue.dll")
LoadPlugin("Path.......\masktools2.dll")
loadplugin("Path.......\VariableBlur.dll")
Import("Path............\uu_mt_blend.avsi")
#
dgsourceim("Path.....\Test Clip.mts", engine=2)
AssumeFPS(30000,1001)
#Adjust YV12 luma levels:
SmoothLevels(16,1.03,255,16,255,chroma=0, limiter=0, TVRange=False,
\ preset="fullrange", LMode=2, brightSTR=100)
clp1=last
#Convert to RGB32 - (Gavino's method)
clp1.ConvertToYV24(chromaresample="point", interlaced=false)
MergeChroma(PointResize(width, height, 0, 1))
ConvertToRGB32(matrix="rec709", interlaced=false)
#Adjust Saturation/Hue with imported VDub 'Hue' (HSI model) filter
Desaturate red, yellow, magenta by 20%. Hue nudged +1.
#VD_Hue_Sat(1, 1, -20, 0, 41)
Hue(sat=0.8, channels="RYM", hue=1)
clp3=last
#Prepare blurred clip with inverted edge mask:
clp4=clp1.BinomialBlur(vary=1)# light Gauss blur
clp5=clp1.mt_edge(mode="sobel").Greyscale().mt_invert()#Inverted edge mask
mt_merge(clp1,clp4,clp5)# Apply masked blur
ConvertToYV24(chromaresample="point", interlaced=false)
MergeChroma(PointResize(width, height, 0, 1))
ConvertToRGB32(matrix="rec709", interlaced=false)
#VD_Hue_Sat(1, 1, -20, 0, 41)
Hue(sat=0.8, channels="RYM", hue=1)
clp6=last
#Overlay blurred clip:
clp3.uu_mt_blend(clp6, 0.6, "Overlay")# Equivalent to "Softlight" in Blend_MT_alpha3
uu_mt_blend(clp6,0.05, "Screen")
#Convert back to YV12
ConvertToYV12(chromaresample="point", matrix="rec709")
@raffriff42
Regarding that 2-pass blend, first with Overlay then Screen (highlighted blue). Any chance you could work into uu_mt_blend an option to internally apply a second blend operation to the first, as I did in Blend_MT_alpha3 ?
Cheers.
Edit: Didn't realize there is an AVISynth port of the VDub 'Hue' plugin. Changed script accordingly (green highlight)
raffriff42
14th October 2015, 19:06
Any chance you could work into uu_mt_blend an option to internally apply a second blend operation to the first, as I did in Blend_MT_alpha3 ?I'm sorry, but no. I don't see the advantage to the user, and since it doubles the complexity of the code, I see a definite DISadvantage for the author (me), as chances for bugs to arise are multiplied.
WorBry
14th October 2015, 20:18
Fair enough. Works great as is :)
raffriff42
19th December 2015, 02:54
I put together a table summarizing the equivalent blend modes (attached below), for reference.
(Note opacity was still wrong. Finally fixed 12th December 2015)My version looks very different in some modes after this fix. Better, to my eyes.
WorBry
19th December 2015, 19:08
Great, I'll have a look at it when I have a moment.
I can't say I've really done anything to 'refine' Blend_MT-alpha3, other than snaffling parts of your function to create an RGB version ;) I won't post it - pretty crudely strung together.
One thing I still like about Blend_MT-alpha3 though is that provision was made for setting separate opacities above and below the center point (128), which, for the bi-phasic blend modes especially, makes it possible to further modulate the shape of the curve - useful for shifting the bias to the mid-lows or mid-highs.
Of late I've been looking at possible (i.e. budget feasible) off-the-shelf alternatives to AVISynth for color correction and grading. HitFilm 3 Express (the free version) is one I'm looking at just now.
https://hitfilm.com/store/hitfilm-3-express
You're probably already aware of it.
Actually, combined with several of the paid add-on packs (Starter and Colorist) it creates quite a comprehensive color correction/grading suite for relatively little cost...up to a point; some things I can still do better (with greater precision) with AVISynth/VDub (selective color correction, for one). No videoscopes (even in the Pro version) other than a smallish RGB Histogram, and the output format options are, to say the least, limiting. But all of the 'common' blend modes are there, in RGB space that is.
WorBry
20th December 2015, 02:40
I've just tested the revised (12th Dec) uu_mt_blend. Using the same clip for the top and base layer again, and testing YUV luma only, the blend mode equivalencies now look like:
uu_mt_blend Blend_MT_alpha3
------------------------------------------------
Overlay Softlight
Hardlight Hardlight and Overlay*
Softlight No equivalent
Multiply Multiply
Screen Screen
Lighten Lighten
Darken Darken
CBurn Burn - slight difference
CDodge Dodge
LBurn Subtractive
LDodge Additive
* In Blend_MT_alpha3, Hardlight is just Overlay-commuted, so it is expected that they would give the same result in a 'self-blend' (top and base layers identical).
raffriff42
20th December 2015, 02:58
So is that good? It sounds good.
Thanks for mentioning HitFilm 3 Express, I'm watching the tutorials and it looks pretty amazing (3D compositing! Motion tracking!).
WorBry
20th December 2015, 16:40
So is that good? It sounds good.!
Yes, I think so.
As you might expect, I've run some further blend equivalency tests comparing the HitFilm Express 3 blend modes with uu_mt_blend and the modded version of Blend_MT_alpha3 version I knocked together for RGB.
To generate an RGB source clip for the tests I put the test YV12 clip (used in the YUV tests) through HitFilm Express 3 and exported as Uncompressed RGB (it was either that or PNG Image Sequence.....I opted for Uncomp RGB).
For the 'analysis' I converted the blend RGB outputs to YV12 (matrix = Rec709) and compared them with mt_makediff.
Here are the results:
http://s20.postimg.org/t8hcbke9l/Blend_Mode_Equivalencies2.jpg (http://postimg.org/image/t8hcbke9l/)
As you can see, in summary:
1. HitFilm and Blend_MT_alpha3_RGB tallied with respect to the Overlay and Hardlight modes - these modes being interchangeable when applied as a 'self blend' i.e. Overlay is Hardlight commuted. Hardlight in uu-mt-blend tallied with both Hardlight and Overlay in HitFilm, but uu-mt-blend's Overlay mode was distinct (see Softlight below).
2. HitFilm's Softlight didn't exactly match any mode, but was very close to Softlight in Blend_MT_alpha3-RGB, and of the 3 bi-phasic modes in uu-mt-blend was closest to Softlight (which in fact matched Blend_MT_alpha3-RGB's Overlay mode- as was seen in the YUV tests). So again, it's differences in the interpretation/formulation for Softlight that largely complicate the picture. As I commented earlier in the thread:
. In this description of the Gimp Softlight mode it even states that - "In some versions of GIMP, “Overlay” mode and “Soft light” mode are identical"
http://docs.gimp.org/en/gimp-concepts-layer-modes.html
I wonder then if you used one where “Overlay” mode and “Soft light” mode are identical" ?
Confusing eh?
3. And finally....Color Burn in Hitfilm matched Burn in Blend_MT_alpha3-RGB, and CBurn in uu-mt-blend was just slightly different, as it was in the YUV tests.
Thanks for mentioning HitFilm 3 Express, I'm watching the tutorials and it looks pretty amazing (3D compositing! Motion tracking!).
Pretty good isn't it?
WorBry
20th December 2015, 17:26
Re;
For the 'analysis' I converted the blend RGB outputs to YV12 (matrix = Rec709) and compared them with mt_makediff.
What's a valid method for comparing clips in RGB space btw ?
I tried using uu_mt_blend's absdiff modes but got results that were not so easy to compare visually.
StainlessS
20th December 2015, 18:40
Will builtin Subtract() not do instead of MT_MakeDiff() ?
Subtract for YUV centers Y @ 126 rather than 128, but @ 128 for RGB.
raffriff42
20th December 2015, 18:47
Very satisfying to see those results in post #32, Worby.
For comparing blend modes, try absdiff16, it shows a pretty small change. EDIT Subtract+Levels works well too.
A=ImageSource("E:\Data\Pictures\test-patts\lena_std2.png")
#[[choose one
B=A.Levels(0, 1.0, 255, 2, 255, coring=false) ## raise black by 2
#B=A.Levels(0, 1.0, 255, 0, 253, coring=false) ## lower white by 2
#B=A.Levels(0, 1.02, 255, 0, 255, coring=false) ## raise gamma by 0.02
#]]
#[[choose one
#R = Subtract(A, B) ## zero diff = gray
#R = Subtract(A, B).Levels(65, 1, 255-64, 0, 255, coring=false) ## exaggerate
#R = Overlay(A, B, mode="subtract") ## always darker
R = uu_mt_blend(A, B, 1.0, "absdiff16").ConvertToRGB24 ## zero diff = black
#]]
return StackVertical(
\ StackHorizontal(
\ A.BicubicResize(R.Width/2, R.Height/2).AddBorders(4,4,4,4)
\ , B.BicubicResize(R.Width/2, R.Height/2).AddBorders(4,4,4,4)
\ )
\ , R.AddBorders(8,4,8,4)
\ )
https://www.dropbox.com/s/jhdi1m8r0ng0e0n/blending-lena-subtract1s.jpg?raw=1 https://www.dropbox.com/s/jgp3l3ap77zsdn2/blending-lena-absdiff16s.jpg?raw=1
Subtract+Levels......................................................... absdiff16.......................................
StainlessS
20th December 2015, 18:55
EDIT: Further to post #34.
From Subtract Docs
About offset of luma range:
For YUV formats the valid Y range is from 16 to 235 inclusive and subtract takes this into account. This means that the following script
Subtract(any_clip, any_clip)
will result in a grey clip with luma = 126. For those that require a subtract function for pc_range YUV data use Overlay:
#Overlay(any_clip, any_clip, mode="Difference", pc_range=true) # grey clip with luma = 128
Overlay(clip1, clip2, mode="Difference", pc_range=true)
WorBry
21st December 2015, 00:03
I have a suggestion guys - we forget all about blend modes and raffriff42 puts up some other pics of Lena?
No? OK....I'm back on it then.
creaothceann
21st December 2015, 00:18
some other pics of Lena?
Sure. (http://www.cs.cmu.edu/~chuck/lennapg/)
WorBry
21st December 2015, 00:25
Flip, I honestly didn't realize what I was asking :o
WorBry
21st December 2015, 03:20
Moving right along.....
So, applied to the blend RGB outputs, Subtract(A, B) and
Subtract(A, B).Levels(65, 1, 255-64, 0, 255, coring=false) revealed no further differences.
But uu_mt_blend(A, B, 1.0, "absdiff16").ConvertToRGB24 picked up some very feint (trace) differences in some of the modes, as indicated in blue text below:
http://s29.postimg.org/owyed19tf/Blend_Mode_Equivalencies3.jpg (http://postimg.org/image/owyed19tf/)
Significant ? For me, not really. Of more interest is what formulation HitFilm uses for Softlight.
vBulletin® v3.8.11, Copyright ©2000-2026, vBulletin Solutions Inc.