View Full Version : New!! - Bleach Bypass filter
actionman133
10th May 2006, 09:19
Inspired by my attempts to create a glow/bloom filter, and stumbling upon a tutorial about imitating a bleach bypass in AfterEffects, I decided to take a shot at creating the filter in AVISynth...
Previews can be found here....
http://actionman133.isa-geek.net/.../bleachbypass.html (http://actionman133.isa-geek.net:8080/preview/bleachbypass.html)
I should give credit Didée for writing FastGaussBlur (), which is used to provide a mask for some subtle blooming....
The settings are as follows:
BleachBypass (clip, output, preview)
output determines which image comes out.
0 returns the source image
1 returns the draft bleach bypass, no bloom applied
2 returns the full bypass, with blooming incorporated (default)
preview will compare the original frame to the filtered frame.
0 returns the fully filtered frame (default)
1 returns half the image as the source, the other half as filtered.
2 presents all the used images for debugging purposes
WorBry
24th March 2010, 05:05
An old post I know, but anyone know where I might find this 'Bleach Bypass' filter (the above link is dead) ?
I'm not so much interested in the bloom/glow aspect, more in acheiving that 'dramatic-earthy-almost metallic/pewter' type effect.
WorBry
28th March 2010, 05:43
OK, no takers, and I see Actionman133 hasnt been around the forum for a while.
So I'm putting together a method based on bits and pieces from the 'film-look' thread (I know, film-look....yawn, ho-hum) and various After Effects/PS/Gimp recipes.
I'm want to process entirely in YV12 and found this (MaskTools based) YV12Overlay function in the 'film look' thread, which in part meets my needs:
http://forum.doom9.org/showthread.php?p=641432#post641432
Adapted it like so:
function yv12overlay(clip a,clip b,float "luma_opacity", float "chroma_desat")
{
yExpr="x "+string(luma_opacity)+" * y 1 "+string(luma_opacity)+" - * +"
uvExpr="x "+string(chroma_desat)+" * y 1 "+string(chroma_desat)+" - * +"
return mt_lutxy(a,b,yexpr=yExpr, uexpr=uvExpr, vexpr=uvExpr, Y=3, U=3, V=3)
}
Where:
Clip a = original source clip (Pal DV 25p progressive anamorphic)
Clip b = greyscale of source clip to which has been applied a 'filmic' S-curve (luma only), using the Levels > Curve function in FFDShow (encoded HuffYuv-YV12).
In this way I can vary both the degree of contrast modulation (introduced by the overlayed luma S-curve) and chroma desaturation. I know there are other things to consider, like simulated film-grain, glow, tint (maybe) etc, which I might look at later, but as a basic method I'm quite pleased with the results.
This said, several of the AE/PS/Gimp recipes that I've come across recommend using a 'multiply' mode for merging the layered clips/images, which really gives a better representation of the contrast 'look' I am after.
I can do this in AVISynth using the (internal core) Layer filter, but that means converting to YUY2.
I'm sure the same can be done in YV12 with Masktools. Could anyone advise how I might modify the mt_Lutxy expressions in above YV12Overlay to acheive this, or else suggest an alternative....maybe mt_merge with an appropriate mask?
Many thanks.
WorBry
29th March 2010, 04:37
If anyone is interested :rolleyes:, actually now that I've examined the 'multiply' blending approach (using Layer filter in YUY2) with a wider range of clips, I realize that it's best suited for well and evenly exposed shots (outdoors on sunny days etc), that in, any case, could benefit from some darkening. For indoor and other unevenly lit shots, I find it over-darkens and loses detail in shadows rather too much. Maybe for digitized film images and professional-level digital camcorders and still cameras with a wide dynamic range, its a different story. But generally, with my consumer level DV camcorder (Panasonic GS400) I find that I can get more acceptable results by adjusting the overlayed luma S-curve at the lower end. So, I'll stick with my existing YV12Overlay method for now.
This said, it would be nice to have a YV12 overlay function that incorporates the various layer blending modes (merge operations) commonly found in imaging software. The AVSynth core Overlay filter can do all of them, and Layer has some. Again, I'm sure all of this is possible in YV12 with Mask Tools. I'd have a go at putting one together myself if someone could help with the respective mt_lut expression calcs.
Nuff said.
One other thing, I've based my workflow on the assumption that the Levels>Curves function in FFDShow can work with YV12 without changing the color format - is this so, or does it convert to RGB and back?
Didée
29th March 2010, 12:08
Oh well, the frame contrast issue, once more ...
A maybe-interesting reading reading as of recent: this thread (http://forum.doom9.org/showthread.php?t=153138), with emphasisis on this post (http://forum.doom9.org/showthread.php?p=1379790#post1379790).
For what it's worth, here's three simple MaskTools functions: Multiply, inverse Multiply, and "128-centered Contrast Multiply"
function mt_layer_mul(clip c1, clip c2)
{ mt_lutxy(c1,c2,"x y * 255 /",U=2,V=2) }
function mt_layer_invmul(clip c1, clip c2)
{ mt_lutxy(c1,c2,"256 256 x - 256 y - * 256 / -",U=2,V=2) }
function mt_layer_centermul(clip c1, clip c2)
{ mt_lutxy(c1,c2,"y 128 < x y * 128 / y 128 > 256 256 x - 128 y 128 - - abs * 128 / - x ? ?",U=2,V=2) }
Just to clarify once more ... whatever you do in this area, it always is only "F(x)" or "F(x,y)", with F being something arbitrary. Magic there is none. And splitting-up "F" into a multi-step filter chain won't introduce something new, it will just introduce rounding errors into something that could be done as a one-step filter. ( F(x) can always be achieved with one single mt_lut, and F(x,y) can always be achieved with one single mt_lutxy. )
WorBry
29th March 2010, 19:58
Thanks Obi-Wan. "Much to learn, I still have" ;)
So, would I be right in concluding that the Inverse Multiply function corresponds to, what is commonly referred to as 'screen' mode blending in AE/PS and the like?
And is the '128-centred-multiply' function essentially the same as 'overlay' mode, where 'multiply' is applied to values <128 and 'screen' to values >128 (cant get my head around the reverse polish syntax for the calc) ? If not, what would that mt_lutxy expression be?
Why 255 for the 'multiply' but 256 for the other two?
Edit: Tried 'overlay' blend mode in Gimp 2 with a frame grab. Rather different result to '128-centred multiply' - darkened shadows, lightened highlights. Actually, with lightly reduced opacity it gives a quite decent 'bleach by-pass' effect (at least in terms of what I am looking for), without resorting to pre/post curve adjustment. Makes sense, since it effectively amounts to an S-curve.
Didée
29th March 2010, 20:43
So, would I be right in concluding that the Inverse Multiply function corresponds to, what is commonly referred to as 'screen' mode blending in AE/PS and the like?
yepp
And is the '128-centred-multiply' function essentially the same as 'overlay' mode, where 'multiply' is applied to values <128 and 'screen' to values >128
yupp
Why 255 for the 'multiply' and 256 for the other two?
yipp ... erh, because division-by-zero usually should be avoided.
Edit: Tried 'overlay' mode in Gimp 2 with a frame grab. Rather different result to '128-centred multiply' - darkened shadows, lightened highlights (makes sense :rolleyes:).
I don't know what Gampi or Adobulus Photoshopulus are doing. My little "128-centered" lut effectively does an S-curve adjustment (as you yourself did note in 2nd quote above).
WorBry
29th March 2010, 21:36
I don't know what Gampi or Adobulus Photoshopulus are doing. My little "128-centered" lut effectively does an S-curve adjustment (as you yourself did note in 2nd quote above).
Ah, silly me, in the Gimp 'overlay' I hadnt restricted to luminance only, so the RGB color values were transformed as well. After correcting for this, the 128-centred-multiply function does indeed give an identical result.
Thanks.
WorBry
6th April 2010, 17:45
@Didee,
I've been experimenting with a few other 'blend modes'.
'Hardlight', as I understand, is simply a reversal (with respect to layers) of '128-centred multiply' (aka "Overlay"):
function mt_layer_hardlight(clip c1, clip c2) # commuted 128_Centred Multiply
{ return mt_lutxy(c1,c2,"x 128 < y x * 128 / x 128 > 256 256 y - 128 x 128 - - abs * 128 / - y ? ?",U=2,V=2) }
So, for a duplicate layer (Clip c1 and c2 the same), Hardlight and Overlay effectively give the same result.
"Soft light" appears to be a milder form of "Overlay", but I'm struggling to find a suitable formula, or at least one that easily translates to reverse polish. Furthermore, the various imaging suites appear to approach 'soft light' in different ways.
For example, I found this set of blend formulae for Photoshop:
http://www.nathanm.com/photoshop-blending-math/
But an attempt to translate 'Soft Light' formula using mt_polish gave just " y 128 < 2 x", which is clearly nonsense.
Any chance you could help me out (again) with a suitable mt_lutxy expression ? :)
Edit: Came across this earlier post of yours on 'Hardlight':
http://forum.doom9.org/showthread.php?p=703132#post703132
Well that kinda blows my notion of 'Hard Light' being a simple layer reversal of 'Overlay' out of the water.
BTW - in those 4 YV12Hardlight variants what is function of the 3-tiered (HL1) lut expressions? Is it to create an internal gradation of the effect, maybe to avoid discontinuance?
WorBry
21st April 2010, 17:09
BTW - in those 4 YV12Hardlight variants what is function of the 3-tiered (HL1) lut expressions? Is it to create an internal gradation of the effect, maybe to avoid discontinuance?
Aha, now that I've been getting my head into this Reverse Polish (or at least trying to), I realize that this is how to string together multiple 'if-then-else' expressions, incorporating, in this case opacity.
Adding opacity to the Multiply and Inverse Multiply (Screen) expressions is straightforward enough, but the 128-centred Multiply (Overlay) has me befuddled. I reckon it should be:
clp = last
Opac=1.0 #(0.0 - 1.0)
OL = " y 128 < x y 128 / "+String(Opac)+" * x 1 "+String(Opac)+" - * + "
OL = OL + " y 128 > 256 256 x - 128 y 128 - - abs * 128 / - "+String(Opac)+" * x 1 "+String(Opac)+" - * + "
OL = OL + "x ? ?"
Return mt_lutxy(clp,clp, OL, U=2,V=2)
but the result is not right. Cant figure what's wrong.
I could resort to:
Clp=last
Opac=1.0 # (0.0 - 1.0)
Over=mt_lutxy(Clp,Clp, "y 128 < x y * 128 / y 128 > 256 256 x - 128 y 128 - - abs * 128 / - x ? ?",U=2,V=2)
Expr = "y "+string(Opac)+" * x 1 "+string(Opac)+" - * +"
return mt_lutxy(Clp ,Over, Expr, U=2, V=2)
But I'm wondering if the two mt_lutxy calculations, in theory, introduce more 'rounding errors' than a single one?
Gavino
21st April 2010, 18:44
...
OL = " y 128 < x y 128 / "+String(Opac)+" * x 1 "+String(Opac)+" - * + "
...
but the result is not right. Cant figure what's wrong.
If I understand what you're doing (and I'm not sure I do), this should be
OL = " y 128 < x y * 128 / "+String(Opac)+" * x 1 "+String(Opac)+" - * + "
I'm wondering if the two mt_lutxy calculations, in theory, introduce more 'rounding errors' than a single one?
In theory yes, because each mt_lutxy rounds its results to integers. I don't know if it matters enough in practice.
WorBry
21st April 2010, 19:01
OL = " y 128 < x y * 128 / "+String(Opac)+" * x 1 "+String(Opac)+" - * + "
Oh my goodness. I was too focused on the if/else arguments to notice that simple omission :eek: :stupid:
Needless to say, thanks.
I'm working on routines for simulating 'film-looks', 'bleach-bypass' being one, and avoiding, as far as possible color space conversions and multi-stage layering (hence the SelSah 'selective saturation/hue' function that you helped me on).
WorBry
22nd April 2010, 21:36
Just realized, another benefit of including opacity in the Overlay mt_lutxy expression is that allows opacity to be separately adjusted for luma values below and above 128, whilst keeping the curve 128-centred. As a result, it is possible to alter the shape of the S-curve so that the effect is applied more strongly in the lighter areas than the darker areas, and visa-versa. That is very useful.
Check this:
colorbars().converttoyv12().mt_lutspa(relative=true,expr="x 255 *").greyscale()
clp=last
Opac1 = 1.0 # 0.0 - 1.00 Opacity (and so magnitude) of Overlay curve < 128
Opac2 = 1.0 # 0.0 - 1.00 Opacity (and so magnitude) of Overlay curve > 128
OL = " y 128 < x y * 128 / "+String(Opac1)+" * x 1 "+String(Opac1)+" - * + "
OL = OL + " y 128 > 256 256 x - 128 y 128 - - abs * 128 / - "+String(Opac2)+" * x 1 "+String(Opac2)+" - * + "
OL = OL + "x ? ?"
Over = mt_lutxy(clp,clp, OL, U=2,V=2)
clps = clp.Subtitle("Original").LanczosResize(640,360)
Overs = Over.Subtitle("Overlay (128-Centred Contrast Multiply)").LanczosResize(640,360)
StackVertical(clps,Overs)
WorBry
30th April 2010, 04:54
Didee,
Sorry to pester you again, but you really are the one to consult on such matters.
Your mt_lutxy computation for ‘128-Centred Contrast Multiply’ is somewhat aligned with the Photoshop formula for ‘Overlay’, except that the Photoshop model is not truly 128-centred. Both respect the top clip (c2) as the reference for deciding whether to ‘multiply’ (y <128 ) or ‘screen’ (y > 128). In this case, ‘Hardlight’, as per the Photoshop formula, should just be a simple question of swapping x and y in the ‘128-Centred Contrast Multiply’ computation , or else, swapping the clips themselves (which is effectively what Photoshop does with the layer channels) i.e.
#function mt_layer_hardlight(clip c1, clip c2) # commuted 128-Centred Contrast Multiply (Hardlight)
#{ return mt_lutxy(c2,c1,"y 128 < x y * 128 / y 128 > 256 256 x - 128 y 128 - - abs * 128 / - x ? ?",U=2,V=2) }
This then leaves me a bit confused about where your ‘Hardlight1’ formula sits, as it seems to respect c2 also, i.e. as applied without the opacity expression:
#function mt_layer_hardlight1(clip c1, clip c2) # based on Didee’s Hardlight-1
#{ return mt_lutxy(c1,c2,"y 128 < x x 128 / 128 y – abs * - x 255 x – 128 / 128 y – abs * + ?",U=2,V=2) }
Also I came across this reference which suggests that the opposite should apply i.e. that Overlay should respect c1 (x) and HardLight should respect c2 (y).
http://www.pegtop.net/delphi/articles/blendmodes/hardlight.htm
In fact, they propose that the terms Overlay and Hardlight, as they are applied in Photoshop, should be swapped to be more logical. Which I guess means respecting the terms as they apply to their photographic applications. Yet this is from a widely-webbed explanation of Photoshop blendmodes:
"Overlay multiplies or screens the colors, depending on the base color. Patterns or colors overlay the existing pixels while preserving the highlights and shadows of the base color. The base color is not replaced, but mixed with the blend color to reflect the lightness or darkness of the original color".
"Hard Light multiplies or screens the colors, depending on the blend color. The effect is similar to shining a harsh spotlight on the image. If the blend color (light source) is lighter than 50% gray, the image is lightened, as if it were screened. This is useful for adding highlights to an image. If the blend color is darker than 50% gray, the image is darkened, as if it were multiplied. This is useful for adding shadows to an image. Pure black or white results in pure black or white."
Again Overlay, by this definition, seems to respect the base layer (Clp c1) and HardLight the blend layer (Clp c2). Confusing.
Of course, if the clips are identical, it really shouldn’t make any difference.
Even so, really would value your opinion on this. In essence, if '128-centred Contrast Multiply' is tantamount to 'Overlay', what then is 128-centred Hardlight, or visa versa?
Cheers.
Edit:
Aha, so these are the Photoshop formulae from the horses mouth:
http://www.adobe.com/devnet/pdf/pdfs/blend_modes.pdf
Quote:
HardLight:
Multiplies or screens the colors, depending on the source color (Cs) value. The effect is similar to shining a harsh spotlight on the backdrop (Cb).
B(Cb, Cs) =
Multiply(Cb, 2 × Cs) if Cs ≤ 0.5
Screen (Cb, 2 x Cs -1) if Cs > 0.5
Overlay:
Multiplies or screens the colors, depending on the backdrop color value. Source colors overlay the backdrop while preserving its highlights and shadows. The backdrop color is not replaced but is mixed with the source color to reflect the lightness or darkness of the backdrop.
B(Cb, Cs) = HardLight(Cs, Cb)
Where (as I interpret it)
Cb = "Backdrop color", is equivalent to the 'base' layer (= Clp c1)
Cs = "Source color", is equivalent to the top (blend) layer (= Clp c2)
So, again this indicates that Hardlight respects the top clip values in deciding to Multiply (y <= 128) or Screen (y > 128), and that Overlay (being a layer reversal of Hardlight) respects the base clip (x) values.
On that basis, this interpretation of the Photoshop formulae, which I took to be de facto, is not actually the case:
http://www.nathanm.com/photoshop-blending-math/
Your "Contrast Multiply'" formula has the advantage of being 128 anchored, so avoiding potential 'luma to chroma' spill, but I'm still left wondering whether it should respect the c1 (x) values, rather the c2 (y) values, to be 'Overlay', in the Photoshop 'sense'.
I know there is the view that just because Adobe say 'this is the way it is' doesn't necessarily make it 'correct'. I'm just trying to find some consistency in the terminology as it applies to the desired/implied result. Why, one might say, if it does the job ("A Rose by any other name...etc")? Because in seeking to replicate effects based on Photoshop/AE recipes, it is important to be clear what a particular action does.
WorBry
1st May 2010, 06:05
Just did some tests with overlayed greyscale gradient patterns.
The '128-Centred Contrast Multiply' and "HardLight1" functions give indistinguishable patterns. The reasonable conclusion is that the former is actually a '128-Centred Hardlight', not Overlay, as such.
WorBry
3rd May 2010, 01:25
I know my ramblings on this out-of-vogue topic are about as exciting as Stephen Hawkings belated caution about the dangers of contacting extra-terrestrial life forms.
And I know that anyone stumbling upon this thread will likely be disappointed, as I was, that there is actually no ‘Bleach Bypass’ script to be found. I will be posting my offering shortly. Just tweaking the motion-compensated, super-resolution part :rolleyes:. In fact, my intent is to transmit it into deep outer space with the hope that any marauding nomads are turned back shrieking “Oh, no....Movie 300....way too much contrast’
Meanwhile, if there are any AVI-Synthesizing life forms tuned in, perhaps one might explain; on switching the Multiply and Inverse Multiply (Screen) functions to chroma mode (u=v=3), I wasn’t too surprised get a chroma shifted image; (minus shift) green-cast with Multiply and a (positive shift) mauve-cast with Screen (probably normal images for the average alien).
What surprised me though is that the ‘128-Centred Contrast Multiply’ function does appear produce a valid image in which color saturation is somewhat increased or decreased in parallel with the luma contrast. I suspect that has something to do with the fact that zero in YUV chroma scaling is 128 and that 128-CCM applies separate equations for y < and > 128.....'suspect' of course meaning that I havent got a clue. I wonder if is possible though to modify the Multiply and Screen functions so that they produce a valid chroma result...obviously without recourse to RGB ?
Edit: Forget the second part of that question. It wouldnt be of much value anyway. I'm just interested in understanding why 128-CCM actually gives a valid 'chroma mode' result when Multiply and Screen dont.
Edit2: Actually, now I've looked at the Histogram levels, it appears that what looked like a 'saturation' effect, is actually a broadening of the U and V profiles, so it's really more of a 'hue' effect, which figures because the multiply component will move some U and V values more into the negative and the screen component will move other values more into the positive, creating a balanced but broader mix of hues. That explains it. Quite an interesting effect, bordering almost on technicolor.
WorBry
4th May 2010, 16:23
OK, here’s my offering. Not exactly ‘Movie 300’, but more or less the ‘look’ I was aiming for. For my taste, I’d probably drop the pre-blur and post-sharpen. All processing is in YV12.
'Bleach Bypass' (Silver Retention)-like script
clpo = last # Assumes source is progressive/deinterlaced.
# Apply light Gauss blur to luma (maybe...me prefer not).
Clp2 = clpo.BinomialBlur(vary=2) # From tsp's VariableBlur filter
# Apply contrast S-curve to luma; slight bias to 'mid-to-highlights':
Blend_MT_alpha(Clpo, clp2=clp2, BLMode="Overlay", Opac1=0.7, Opac2=0.8)
# Desaturate (50%, across the scale in this case); shift hue a tad to reduce impact of tint on bright reds (see sample below)
SelSah_V2(0.5, -10, clp2=clpo)
# Apply tint (Sienna or whatever) favouring mid-tones.
Stint_v1($8E6B23, 0.35, 140, 180, 0, 210, clp2=clpo)
# And/or apply copperish tint to edges - gives metals 'antique' look (like Knights armour in below sample)
#Stint_v1($B87333, 0.3, EdgeM=true)
# Sharpen to taste (maybe....me prefer not)
LSFMod(smode=3,strength=90)
# Add Grain to taste..if you want. I know there's a GrainFactory_MT2 version.
GrainFactory3(g1str=2,g2str=3,g3str=5)
# Brighten if necssary (Maybe should incorporate in Blend_MT).
Ylevels(Gamma=1.05)
# Voila!! "Beach Bus-Pass"-like.
Need more 'bloom and gloom' ?. Maybe look at
http://forum.doom9.org/showthread.php?t=108814
As for the Blend_MT_alpha, SelSah_v2 and Stint_v1 functions. Here they be:
Blend_MT_alpha (....very alpha, but it does the job).
Edit: Use Blend_MT_Alpha3 now:
http://forum.doom9.org/showpost.php?p=1400434&postcount=42
function Blend_MT_alpha(clip clp1 ,clip "clp2", string "BLMode", int "LMode", int "CMode", float "Opac", float "Opac1", float "Opac2",
\ bool "BL2", string "BLMode2", float "SOpac", float "SOpac1", float "SOpac2", bool "Preview", bool "GSGrad")
{
# Assembled by WorBry. Thanks to Didee for the Multiply, Screen and '128-Centred Contrast Multiply' computations.
# DESCRIPTION:
# Blend_MT aims to emulate a number of the blend modes available in commercial imaging software (After Effects etc). Whilst
# the existing core filters, Layer and Overlay, incorporate some of these modes, both require color space conversion to process
# YV12 sources; Overlay accepts YV12 input, but internally converts to YUV 4:4:4 for processing. Blend_MT uses MaskTools v2
# only and so allows processing exclusively in YV12. The function was created as one of set of routines to assist in the
# simulation of certain "filmic looks", but perhaps has general application.
# Requires YV12 input, assumes full (pc) range 0-255 and assumes that the source is progressive (native or deinterlaced).
# Requires MaskTools v2 only.
# PARAMETER DEFINITIONS:
#
#Clp1 - Input clip that serves as the lower (base) clip in the specified blend operation.
#Clp2 - Input clip that serves as the upper (overlay) clip in the specified blend operation. If not defined, clp2
# defaults to clp1
#BLMode - The specific blend operation that will be applied to the clips. At default, the operation is applied to the
# luma plane only; chroma is retaind from Clp1. The currently available modes are:
# Blend - Simple merge operation. Note that several other so-called 'blend modes' may be acheived simply by modulating
# the LMode, CMode and Opacity settings (see below). Thus:
# 'Luminosity' mode retains the luma from clp2 and chroma from clp1. Blend mode, at default settings (LMode=3,
# Cmode=2, Opacity = 1.0), or otherwise LMode=4 and CMode=2, correspond to this.
# 'Chroma' or 'Color' mode retains the luma from clp1 and the chroma from clp2. LMode=2 and CMode=3 at full
# opacity (1.0), or LMode=2 and CMode=4, correspond to this.
# 'Average' mode is equivalent to 0.5 (50%) opacity.
#
# Multiply - Multiplies the corresponding pixel values of the two input clips and divides the result by 255. The outcome
# is always a darker composite image. If the input clips are identical, the transformation corresponds to an
# exponential curve.
#
# Screen - Has the opposite effect to Multiply. It multiples the inverted (complement) values. The outcome is always
# a lighter composite image.
#
# Hardlight - (128-Centred Contrast Multiply) Multiplies or screens the pixel values, depending on top clip (Clp2) values.
# The effect is likened to shining a harsh spotlight (as if Clp2) on the image (Clp1), hence the name. For Clp2
# values > 128 (50% grey) the image is screened, and so lightened; useful for adding highlights. For Clp2
# values < 128, the image is multiplied,and so darkened; useful for adding shadows. Pure black or white results
# in pure black or white. The computation differs from that used in After Effects and Photoshop, in that it is
# 128-centred; this avoids luma overflow. If Clp2 and Clp1 have identical pixel values, the result corresponds
# to a symetrical S-curve. This is useful for creating high contrast images characteristic of certain 'film looks'.
#
# Overlay - (128-Centred Contrast Multiply commuted). Multiplies or screens the pixel values, depending on the base clip
# values. For Clp1 pixel values > 128 the image is screened and for Clp1 values < 128 the image is multiplied.
# In this way patterns overlay the existing pixels while preserving the highlights and shadows of the base clip.
# The formulation, like Hardlight, is 128-centered. It follows that if Clp1 and Clp2 have identical pixel values,
# Overlay and Hardlight will produce the same result.
# Reflect - Gives the appearance of adding shiny objects. See the Greyscale gradient curve (GSGrad below) to see why. Use
# with caution and always wear sun glasses. Reduced opacity is recommended.
# Glow - The opposite of Reflect, but the same cautions apply.
#
# SoftLight - N/A YET-STILL WORKING ON A SUITABLE FORMULA. Darkens or lightens the colors, depending on top (Clp2) values.
# The effect is likened to shining a diffused spotlight (as if Clp2) on the image (Clp1). For clp2 values >128,
# the image is lightened as if it were 'dodged'. For Clp2 values <128 the image is darkened as if it were 'burned-in'.
# Pure black or white results in distinctly darker or lighter area but does not result in pure black or white.
#
# Darken - Takes the darkest pixel value from either clip. If the input clips have identical values, there is no change.
#
# Lighten - Takes the lightest pixel value from either clip. If the input clips have identical values, there is no change.
#
#LMode - Determines whether the luma plane (Y) will be processed by the blend operation (3) or retained from Clp1 (2)
# or Clp2 (4). Default is LMode=3.
#CMode - (2,3,4 or 0). Determines whether the chroma planes (U,V) will be processed by the blend operation (3), retained
# from Clp1 (2) retained Clp2 (4), or excluded i.e. greyscale (0). Note, aside from 'Blend' mode, Cmode=3 has
# limited application since the transformations result marked U and V shifts with undesirable color casts.
# However, when applied to the 'contrast' modes (HardLight, Overlay), with identical source clips, CMode=3 may
# produce an acceptable image in which the U and V profiles are broadened to create a richer mix of like hues.
# Depending on the source, this can take on a 'technicolor' film like appearance.
#BL2 - True/False. Whilst the strength of the BLMode operation can be somewhat regulated by opacity adjustment, it may
# be desirable to apply a second blend operation to the first. True, activates this option. Default is, False.
#BL2Mode - Selects the second blend mode operation. All of the blend modes available in BLMode are available in BL2Mode.
# The BLMode2 operation is applied with Clp1 as the base clp and the product of the first BLMode operation as the
# top clip. For simplicity, the LMode and CMode settings are kept the same as for BLMode.
#Opac - Sets the opacity of the BLMode product relative to Clp1. Range 0.0 - 1.0. At 1.0 (default) all of the blend
# product is represented.
#Opac1, Opac2 - Apply only to Overlay and HardLight. Opac1 sets the opacity of the BLMode product for Clp2 pixel values <128
# and Opac2 sets the opacity for Clp2 values >128. This makes it possible to alter, in some way, the vertical
# symetry of the S-curve. Range 0.0 - 1.0. If not separately defined, defaults to Opac value.
#SOpac - Sets the opacity of the BLMode2 product relative to Clp1. Range 0.0 - 1.0. At 1.0 (default) all of the blend
# product is represented.
#SOpac1, SOpac2- Apply to Hardlight and Overlay, SOpac1 sets the opacity of the BLMode2 product for BLMode pixel values <128
# and SOpac2 sets the opacity for Clp2 values >128. In this way it is possible to further manipulate the shape of
# the final curve. Range 0.0 - 1.0. If not separately defined, default to SOpac value.
#Preview - True/False. True gives a composite view of the input clips and final output. Default is False.
#GSGrad - True/False. True demonstrates the effect of the blend operation as applied to a greyscale gradient; histogram
# reveals the luma curve. Default is False.
#
#Defaults:
#clp2 = default(clp2, clp1)
BLMode = default(BLMode, "Blend")
LMode = default(LMode, 3)
CMode = default(Cmode, 2 )
Opac = default(Opac, 1.0)
Opac1 = default(Opac1, Opac)
Opac2 = default(Opac2, Opac)
BL2 = default(BL2, False)
BLMode2 = default(BLMode2, "Blend")
SOpac = default(SOpac, 1.0)
SOpac1 = default(SOpac1, SOpac)
SOpac2 = default(SOpac2, SOpac)
Preview = default(Preview, False)
GSGrad = default(GSGrad, False)
#
#Limits:
Opac = Opac <0.0 ? 0.0 : Opac >1.0 ? 1.0 : Opac
Opac1 = Opac1 <0.0 ? 0.0 : Opac1 >1.0 ? 1.0 : Opac1
Opac2 = Opac2 <0.0 ? 0.0 : Opac2 >1.0 ? 1.0 : Opac2
SOpac = SOpac <0.0 ? 0.0 : SOpac >1.0 ? 1.0 : SOpac
SOpac1 = SOpac1 <0.0 ? 0.0 : SOpac1 >1.0 ? 1.0 : SOpac1
SOpac2 = SOpac2 <0.0 ? 0.0 : SOpac2 >1.0 ? 1.0 : SOpac2
#Switch for CMode=0 greyscale:
CMode = (Cmode == 0) ? 128 : CMode
#Create greyscale gradients for GSGrad:
GSG = colorbars().converttoyv12().mt_lutspa(relative=true,expr="x 255 *").greyscale()
GSG = GSG.TurnLeft().LanczosResize(640,360)
Clp1 = (GSGrad == False) ? Clp1 : GSG
clp2 = default(clp2, clp1)
Clp2 = (GSGrad == False) ? Clp2 : GSG
# Create mt_lutxy expressions for the blend modes:
# Didee's '128-Centred Contrast Multiply' (HardLight):
HDL = " y 128 < x y * 128 / "+String(Opac1)+" * x 1 "+String(Opac1)+" - * + "
HDL = HDL + " y 128 > 256 256 x - 128 y 128 - - abs * 128 / - "+String(Opac2)+" * x 1 "+String(Opac2)+" - * + "
HDL = HDL + "x ? ?"
HDL2 = " y 128 < x y * 128 / "+String(SOpac1)+" * x 1 "+String(SOpac1)+" - * + "
HDL2 = HDL2 + " y 128 > 256 256 x - 128 y 128 - - abs * 128 / - "+String(SOpac2)+" * x 1 "+String(SOpac2)+" - * + "
HDL2 = HDL2 + "x ? ?"
#Didee's '128-centred contrast multiply' commuted (Overlay):
OVE = " x 128 < y x * 128 / "+String(Opac1)+" * x 1 "+String(Opac1)+" - * + "
OVE = OVE + " x 128 > 256 256 y - 128 x 128 - - abs * 128 / - "+String(Opac2)+" * x 1 "+String(Opac2)+" - * + "
OVE = OVE + "y ? ?"
OVE2 = " x 128 < y x * 128 / "+String(SOpac1)+" * x 1 "+String(SOpac1)+" - * + "
OVE2 = OVE2 + " x 128 > 256 256 y - 128 x 128 - - abs * 128 / - "+String(SOpac2)+" * x 1 "+String(SOpac2)+" - * + "
OVE2 = OVE2 + "y ? ?"
#Glow
GLO = "x 255 == x "+String(Opac)+" * x 1 "+String(Opac)+" - * + "
GLO = GLO + " 255 y y * 255 x - / min "+String(Opac)+" * x 1 "+String(Opac)+" - * + "
GLO = GLO + "?"
GLO2 = "x 255 == x "+String(SOpac)+" * x 1 "+String(SOpac)+" - * + "
GLO2 = GLO2 + " 255 y y * 255 x - / min "+String(SOpac)+" * x 1 "+String(SOpac)+" - * + "
GLO2 = GLO2 + "?"
#Reflect
REF = "y 255 == y "+String(Opac)+" * x 1 "+String(Opac)+" - * + "
REF = REF + " 255 x x * 255 y - / min "+String(Opac)+" * x 1 "+String(Opac)+" - * + "
REF = REF + "?"
REF2 = "y 255 == y "+String(SOpac)+" * x 1 "+String(SOpac)+" - * + "
REF2 = REF2 + " 255 x x * 255 y - / min "+String(SOpac)+" * x 1 "+String(SOpac)+" - * + "
REF2 = REF2 + "?"
#
Bl1Expr= (BLMode=="Blend") ? "y "+string(Opac)+" * x 1 "+string(Opac)+" - * +"
\ : (BLMode=="Multiply") ? "x y * 255 / "+String(Opac)+" * x 1 "+String(Opac)+" - * +"
\ : (BLMode=="Screen") ? "256 256 x - 256 y - * 256 / - "+String(Opac)+" * x 1 "+String(Opac)+" - * + "
\ : (BLMode=="Hardlight") ? HDL
\ : (BLMode=="Overlay") ? OVE
\ : (BLMode=="Glow") ? GLO
\ : (BLMode=="Reflect") ? REF
\ : (BLMode=="Lighten") ? "x y > x y "+String(Opac)+" * x 1 "+String(Opac)+" - * + ? "
\ : (BLMode=="Darken") ? "x y < x y "+String(Opac)+" * x 1 "+String(Opac)+" - * + ? "
\ : Bl1Expr
#
Bl2Expr= (BLMode2=="Blend") ? "y "+string(SOpac)+" * x 1 "+string(SOpac)+" - * +"
\ : (BLMode2=="Multiply") ? "x y * 255 / "+String(SOpac)+" * x 1 "+String(SOpac)+" - * + "
\ : (BLMode2=="Screen") ? "256 256 x - 256 y - * 256 / - "+String(SOpac)+" * x 1 "+String(SOpac)+" - * + "
\ : (BLMode2=="HardLight") ? HDL2
\ : (BLMode2=="Overlay") ? OVE2
\ : (BLMode2=="Glow") ? GLO2
\ : (BLMode2=="Reflect") ? REF2
\ : (BLMode2=="Lighten") ? "x y > x y "+String(SOpac)+" * x 1 "+String(SOpac)+" - * + ? "
\ : (BLMode2=="Darken") ? "x y < x y "+String(SOpac)+" * x 1 "+String(SOpac)+" - * + ? "
\ : Bl2Expr
#Apply blend operation:
BLOut = mt_lutxy(clp1,clp2, Bl1Expr, Y=LMode, U=CMode, V=CMode)
Output = (BL2 == False) ? BLOut : mt_lutxy(Clp1, BLOut, Bl2Expr, Y=LMode, U=CMode, V=CMode)
#Preview set-up:
Clp1s = Clp1.LanczosResize(640,360).Subtitle("Clp1 (Base)", size=14)
Clp2s = Clp2.LanczosResize(640,360).Subtitle("Clp2 (Top)", size=14)
Outs = Output.LanczosResize(640,360).Subtitle("Output", size=14)
Outgs = Output.Greyscale().LanczosResize(640,360).Subtitle("Output (Greyscale)", size=14)
Clpsv = StackVertical(Clp2s, Clp1s)
Outsv = StackVertical(Outs, Outgs)
Prev = StackHorizontal(Clpsv, Outsv)
#GSGrad view set-up:
GSGH = GSG.Histogram().Subtitle("Original")
GSGoH = Output.LanczosResize(640,360).Histogram().Subtitle("Blend operation applied")
GSGout = StackVertical(GSGH, GSGoH)
#Output
Final = (Preview == False && GSGrad == False) ? Output
\ : Preview ? Prev : GSGout
#
Return Final
}
Of course, it would be nice if there were a MaskTools based function that could generate a custom YV12 luma curve, but the mt_lut computations for that would be way, way beyond my capabilities.
Edit: Fixed BLMode2 bug
WorBry
4th May 2010, 16:24
.....and, SelSah_V2
Function SelSah_v2 (clip clp, float "Sat", float "Hue", int "Luma_Low", int "Luma_High", int "Low_OS", int "High_OS",
\ clip "clp2", bool "Exclude", bool "Preview")
{
#Description:
# Extends Tweak to allow selective control of saturation and hue over a defined luma range.
# Assembled by WorBry. MaskTools computations kindly provided by Didee. Thanks to Gavino for advice on script syntax.
# Requires YV12 clp input and assumes full (pc) luma range 0-255.
# Requires MaskTools v2.
#
# Parameters:
# Clp - Is the input clip that will be processed.
# Clp2 - Provides option to use a second clip with different luma curve for the saturation/hue control.
# Sat - Saturation. (As per Tweak) Desaturation 0.0 < 1.0 > Increased saturation
# Hue - Hue. (As per Tweak) Range -180 to +180
# Luma_Low - Lower luma range point. Range 0 - 255 *
# Luma_High - Upper luma range point. Range 0 - 255 *
# Low_OS - Lower over-shoot point. Provides soft roll-off at lower range boundary. Range 0 - 255 *
# High_OS - Upper over-shoot point. Provides soft roll-off at upper range boundary. Range 0 - 255 *
# Exclude True/False. True excludes the range selected by the luma set points. False (default)
# includes the selected range.
# Preview - True/False. True provides preview of original clip, output and luma masks with original
# and output chroma.
#*Note: the luma set points are limited with respect to each other to ensure correct orientation and avoid overlap.
#Defaults:
Sat = default(Sat, 1.0)
Hue = default(Hue, 0.0)
Exclude = default(Exclude, False)
Luma_Low = default(Luma_Low, 0.0)
Luma_High = default(Luma_High, 255)
Low_OS = Exclude ? default(Low_OS, Luma_Low + 20)
\ : default(Low_OS, Luma_Low - 20)
High_OS = Exclude ? default(High_OS, Luma_High - 20)
\ : default(High_OS, Luma_High + 20)
clp2 = default(clp2, clp)
Preview = default(Preview, False)
#
#Limits:
Sat = Sat <0.0 ? 0.0 : Sat
Hue = Hue <-180 ? -180 : Hue > 180 ? 180 : Hue
Luma_Low = Luma_Low <0.0 ? 0.0 : Luma_Low >255 ? 255 : Luma_Low
Luma_High = Luma_High <0.0 ? 0.0 : Luma_High >255 ? 255 : Luma_High
Low_OS = Exclude && Low_OS <Luma_Low ? Luma_Low
\ : Exclude && Low_OS > High_OS ? ((Luma_Low + Luma_High)/2)
\ : Low_OS <0.0 ? 0.0 : Low_OS >Luma_Low ? Luma_Low : Low_OS
High_OS = Exclude && High_OS >Luma_High ? Luma_High
\ : Exclude && High_OS < Low_OS ? ((Luma_Low + Luma_High + 2)/2)
\ : High_OS <Luma_High ? Luma_High : High_OS >255 ? 255 : High_OS
#
#Create Saturation and Hue control clip:
twclp = clp2.tweak(sat=Sat, hue=Hue, coring=false)
#
#Create luma mask with low/high range points and roll-off gradient at boundaries:
#"Include" [0]<-black->[Low_OS]<-gradient->[Luma_Low]<-white->[Luma_High]<-gradient->[High_OS]<-black->[255]
#"Exclude" [0]<-white->[Luma_Low]<-gradient->[Low_OS]<-black->[High_OS]<-gradient->[Luma_High]<-white->[255]
#
Low_OS = string(Low_OS) Luma_Low = String(Luma_Low)
Luma_High = String(Luma_High) High_OS = String(High_OS)
#Create "Include" range expression for mask:
blkI = "x "+Low_OS+" < x "+High_OS+" > | 0 "
whtI = "x "+Luma_Low+" >= x "+Luma_High+" <= & 255 "
grLI = "x "+Luma_Low+" < 255 x "+Low_OS+" - "+Luma_Low+" "+Low_OS+" - / * "
grHI = "x "+Luma_High+" > 255 1 x "+Luma_High+" - "+High_OS+" "+Luma_High+" - / - * "
exprI = blkI + whtI + grLI + grHI + "x ? ? ? ?"
#Create "Exclude" range expression for mask:
blkE = "x "+Low_OS+" >= x "+High_OS+" <= & 0 "
whtE = "x "+Luma_Low+" < x "+Luma_High+" > | 255 "
grLE = "x "+Low_OS+" <= 255 x "+Low_OS+" - "+Luma_Low+" "+Low_OS+" - / * "
grHE = "x "+Luma_High+" < 255 x "+High_OS+" - "+Luma_High+" "+High_OS+" - / * "
exprE = blkE + whtE + grLE + grHE + "x ? ? ? ?"
#Luma Mask (greyscale for output):
lmask = twclp.mt_lut(expr = (Exclude ? ExprE : ExprI))
lmaskg = lmask.Greyscale()
# Apply luma mask:
Output = mt_merge(clp,twclp,lmaskg,luma=true,Y=2,U=3,V=3)
#
#PREVIEW set-up
clps =clp.Subtitle("Original Clip").LanczosResize(640,360)
outs =output.Subtitle("Final Output").LanczosResize(640,360)
#Luma Mask with chroma from tweaked clp
lmcts = lmask.Subtitle("Luma mask + Tweaked Chroma").LanczosResize(640,360)
#Luma Mask with chroma from original clp
lmcc = Exclude ? clp2.mt_lut(expr=ExprE) : clp2.mt_lut(expr=exprI)
lmccs = lmcc.Subtitle("Luma Mask + Original Chroma").LanczosResize(640,360)
#Create composite view
Clpout = StackVertical(clps,outs)
Masks = StackVertical(lmccs, lmcts)
Prev = StackHorizontal(Clpout,Masks)
#
Final = Preview ? Prev : Output
#
Return(Final)
}
Stint_v1
Function Stint_v1 (clip clp, int color, float "Sat", int "Luma_Low", int "Luma_High", int "Low_OS", int "High_OS",
\ bool "Exclude", clip "clp2", bool "Blr", string "Blr_Type", float "Blr_rad",
\ bool "EdgeM", string "EMode", float "EThY1", float "EthY2",
\ bool "Grey", bool "Preview")
{
#
# Description:
#
# Based on the original 'Tint' function by actionman133. Modified by WorBry to use MaskTools v2 and provide a
# greater level of control. The input clip chroma is converted to luma and a luma mask is used to apply the desired
# color tint. The original Tint function was intended to favour the mid-tones. This updated version allows the tint
# to be applied to a defined 'luma range'. It is also possible to apply a (pre)blur and edge mask. These can assist
# in creating localized hues, characteristic of some 'film looks' e.g. "Bleach Bypass' (Silver Retention).
# The function Requires YV12 input and assumes and processes at full (pc) range (0-255); if input clip is 16-235
# (tv) range, pre-scale accordingly with scaling filter of choice (ColorYUV, Levels, YLevels, SmoothLevels etc).
# Requires MaskTools v2 and tsp's VariableBlur and MedianBlur.
#
# Parameters:
# Clp - Input clip to which the tint will be applied.
# Color - The chosen tint color. Uses hexadecimal color codes, preceded by $. Default is Sepia.
# Sat - Strength (saturation) of the tint. Range 0.0 - 1.0. Default 0.3
# Luma_Low - Lower luma range point. Range 0 - 255 *
# Luma_High - Upper luma range point. Range 0 - 255 *
# Low_OS - Lower over-shoot point. Provides soft roll-off at lower range boundary. Range 0 - 255 *
# High_OS - Upper over-shoot point. Provides soft roll-off at upper range boundary. Range 0 - 255 *
# Exclude - True/False. True excludes the selected range. Default is False.
# Clp2 - Option to use a second clip for creation of the luma mask. Useful for applying other
# pre-treatments that may influence the tint pattern - sharpening, altered luma curve etc.
# Blr - True/False. True applies a blur of a selected type. Default is False. The blur is applied
# prior to creation of the luma range control mask or edge mask.
# Blr_type - Blur type. The provided options are "binomial", "gauss", "average" (from VariableBlur) and
# "median". Default is "binomial".
# Blr_rad - Blur strength, as defined by the blur luma (y) radius (variance). Default is 3.0.
# EdgeM - True/False. True applies a luma edge mask (mt_edge). This replaces the luma range
# control mask, so range control is not available in this mode; the range is fixed at 0-255.
# Default is False.
# EMode - Edge mask Mode (3x3 convolution kernel). Default is "sobel". "min/max" can be useful for
# creating a more diffuse hue (halo) around edges, especially in combination with a (pre) blur.
# For other available modes, see MaskTools v2 documentation.
# EthY1 - Edge mask low luma threshold. Below this value, pixel is set to 0. Default is 10
# EthY2 - Edge mask upper luma threshold. Above this value, pixel is set to 255. Default is 10
# Grey - True/False. True applies the tint to the fully desaturated (greyscale) clip. Default is False.
# Preview - True/False. True provides a preview of the input clip, final output, greyscale clp with
# applied tint and mask. Default is False.
#
#*Note: the luma set points are limited with respect to each other to ensure correct orientation and avoid overlap.
#
Color = Default(Color, $5E2612) # Sepia
Sat = Default(Sat, 0.3)
Exclude = default(Exclude, False)
Luma_Low = default(Luma_Low, 0.0)
Luma_High = default(Luma_High, 255)
Low_OS = Exclude ? default(Low_OS, Luma_Low + 20)
\ : default(Low_OS, Luma_Low - 20)
High_OS = Exclude ? default(High_OS, Luma_High - 20)
\ : default(High_OS, Luma_High + 20)
clp2 = Default(clp2, clp)
EdgeM = Default(EdgeM, false)
EMode = Default(EMode, "sobel")
EthY1 = default(EthY1, 10)
EthY2 = default(EthY2, 10)
Blr = default(Blr, false)
Blr_type = default(Blr_type, "binomial")
Blr_rad = default(Blr_rad, 3.0)
Grey = default(Grey, false)
Preview = default(Preview, false)
#
#Limits:
Sat = Sat <0.0 ? 0.0 : Sat >1.0 ? 1.0 : Sat
Luma_Low = Luma_Low <0.0 ? 0.0 : Luma_Low >255 ? 255 : Luma_Low
Luma_High = Luma_High <0.0 ? 0.0 : Luma_High >255 ? 255 : Luma_High
Low_OS = Exclude && Low_OS < Luma_Low ? Luma_Low
\ : Exclude && Low_OS > High_OS ? ((Luma_Low + Luma_High)/2)
\ : Low_OS <0.0 ? 0.0 : Low_OS >Luma_Low ? Luma_Low : Low_OS
High_OS = Exclude && High_OS >Luma_High ? Luma_High
\ : Exclude && High_OS < Low_OS ? ((Luma_Low + Luma_High + 2)/2)
\ : High_OS <Luma_High ? Luma_High : High_OS >255 ? 255 : High_OS
#
#Create blankclip with chosen color
colbc = BlankClip (clp, color=color, pixel_type = "YV12")
#Create greyscale clp, if 'Grey" requested
clp = Grey? clp.greyscale() : clp
#Convert chroma from clp and blankclip to luma. Resulting images are half-size & greyscale.
clpu = clp.UtoY ()
clpv = clp.VtoY ()
colbcu = colbc.UtoY ()
colbcv = colbc.VtoY ()
#Create greyscale clip for luma mask; uses clp2 if defined, or clp by default
clp2g = clp2.Greyscale()
#Apply blur, if requested.
clp2gb = (Blr == False) ? clp2g
\ : (Blr_type == "binomial") ? clp2g.binomialblur(vary=Blr_rad)
\ : (Blr_type == "gauss") ? clp2g.gaussianblur(vary=Blr_rad)
\ : (Blr_type == "median") ? clp2g.MedianBlur(radiusy=Blr_rad)
\ : (Blr_type == "average") ? clp2g.averageblur(rady=Blr_rad)
\ : clp2g
#Apply edge mask, if requested (will replace the luma range control mask)
clp2em = (EdgeM == false) ? clp2gb
\ : clp2gb.mt_edge(mode=EMode, thy1=EthY1, thy2=EthY2, y=3, u=2, v=2)
#Prepare luma range control mask:
#"Include" mode [0]<-black->[Low_OS]<-gradient->[Low]<-white->[High]<-gradient->[High_OS]<-black->[255]
#"Exclude" mode [0]<-white->[Low]<-gradient->[Low_OS]<-black->[High_OS]<-gradient->[High]<-white->[255]
Low_OS = string(Low_OS)
Luma_Low = string(Luma_Low)
Luma_High = string(Luma_High)
High_OS = string(High_OS)
Sat = string(Sat)
#Create Include mode lut expression for mask:
blkI = "x "+Low_OS+" < x "+High_OS+" > | 0 "
whtI = "x "+Luma_Low+" >= x "+Luma_High+" <= & 255 "
grLI = "x "+Luma_Low+" < 255 x "+Low_OS+" - "+Luma_Low+" "+Low_OS+" - / * "
grHI = "x "+Luma_High+" > 255 1 x "+Luma_High+" - "+High_OS+" "+Luma_High+" - / - * "
exprI = blkI + whtI + grLI + grHI + "x ? ? ? ?"
#Create Exclude mode lut expression for mask:
blkE = "x "+Low_OS+" >= x "+High_OS+" <= & 0 "
whtE = "x "+Luma_Low+" < x "+Luma_High+" > | 255 "
grLE = "x "+Low_OS+" <= 255 x "+Low_OS+" - "+Luma_Low+" "+Low_OS+" - / * "
grHE = "x "+Luma_High+" < 255 x "+High_OS+" - "+Luma_High+" "+High_OS+" - / * "
exprE = blkE + whtE + grLE + grHE + "x ? ? ? ?"
#Create luma range control mask (or use edge mask, if requested)
LMask = (EdgeM == False) ? clp2em.mt_lut(expr = (Exclude ? ExprE : ExprI))
\ : clp2em
#Modify mask for tint strength scaling and resize to match chroma
LMask = LMask.mt_lut("255 x 255 / 1 "+Sat+" - - * ", y=3).Reduceby2()
# Apply color tint with mask; convert luma back to chroma and restore luma from original clp
clput = mt_merge (clpu, colbcu, LMask, Y=3, U=2, V=2)
clpvt = mt_merge (clpv, colbcv, LMask, Y=3, U=2, V=2)
Output = YtoUV (clput, clpvt, clp)
#
#Set-up Preview:
clps = clp.LanczosResize(640,360).Subtitle("Original Clip", Size=14)
outs = output.LanczosResize(640,360).Subtitle("Final Output", Size=14)
LMasks = LMask.LanczosResize(640,360).Subtitle("Luma Mask", Size=14)
#Tint applied to greyscale clp
clpgu = clp.Greyscale().UtoY ()
clpgv = clp.Greyscale().VtoY ()
clpgut = mt_merge (clpgu, colbcu, LMask, Y=3, U=2, V=2)
clpgvt = mt_merge (clpgv, colbcv, LMask, Y=3, U=2, V=2)
GsTin = YtoUV (clpgut, clpgvt, clp)
GsTins = GsTin.LanczosResize(640,360).Subtitle("Tint on Greyscale", Size=14)
#Create composite view
Clpout = StackVertical(clps,outs)
Mskgs = StackVertical(LMasks, GsTins)
Prev = StackHorizontal(Clpout,Mskgs)
#
Final = Preview ? Prev : Output
#
Return(Final)
}
Here are some samples. Source was a sample I had from a Panasonic HMC-41 AVCHD camcorder (720 50p). The uploaded clip is in FFDShow Huff YUV YV12 format.
Source clip:
http://www.mediafire.com/file/zztwyyujzmw/Sample%20720%2050p%20YV12.avi
Frame shots:
Original:
http://www.mediafire.com/file/lz2lnnmwmwy/Frame%200%20Original.png
http://www.mediafire.com/file/nwhgumtyzxy/Frame%2050%20Original.png
Processed:
http://www.mediafire.com/file/jhzyjngrzyu/Frame%200%20Bleach%20Bypass.png
http://www.mediafire.com/file/yel2t2yromn/Frame%200%20Bleach%20Bypass%20edge%20tinted.png
http://www.mediafire.com/file/ekdtmm0ljkm/Frame%2050%20Bleach%20Bypass.png
WorBry
4th May 2010, 22:01
Actually, applying the pre-blur through an mt_edge mask improves the result. Dont really want to add another 'step' with more rounding errors. Maybe I can work it into the Blend_MT function.
WorBry
5th May 2010, 16:34
I'm still trying to derive a suitable mt_lutyxy expression for 'Soft Light'
Reading around, the general consensus seems to be that the formula is a combination of "burn" and "dodge":
Softlight(x,y) = (((255 - x) * Rm) + (x * Rs))/255
Where:
Rm = x * y /255 # i.e. Multiply
Rs = (255 - ((255 - x) * (255 -y)))/255 # i.e. Inverse Multiply (Screen)
I've tried every which way to get a result (with a self blended greyscale gradient), but always end up with a total black or half white/half black image.
Anyone (more mathematically inclined) know how to resolve this, and the resulting (about) half-equations for y < 128 and 'else' (y => 128) ?
Gavino
5th May 2010, 16:50
Rs = (255 - ((255 - x) * (255 -y)))/255 # i.e. Inverse Multiply (Screen)
That doesn't look right, just from the point of view of range normalisation I think it should be
Rs = 255 - ((255 - x) * (255 -y))/255
WorBry
5th May 2010, 17:01
Yeah, I tried that also. Here's the reference:
http://books.google.ca/books?id=qxrKDP1jHr4C&pg=PA346&lpg=PA346&dq=soft+light+equation+255+128&source=bl&ots=2RBsxxHY9X&sig=Dd--lrA_tqu0xPqo4L8A5wrwsyE&hl=en&ei=uXXhS4jeN4S0lQeuh6n-AQ&sa=X&oi=book_result&ct=result&resnum=6&ved=0CBkQ6AEwBQ#v=onepage&q=soft%20light%20equation%20255%20128&f=false
Also these:
http://forensicphotoshop.blogspot.com/2007/09/blending-mode-math-part-2.html
http://www.pegtop.de/delphi/articles/blendmodes/softlight.htm
(ie. the Pegtop version, not Photoshop)
I get a bit stuck on the algebra in converting these F(a,b) formulae and code to 255-scaled equations, and even more so with the half (128-scaled) equations.
WorBry
5th May 2010, 18:43
Well, at least I'm now getting a curve with this:
SoftLight = (((255-x) * ((x * y)/255)) + (x * (255-((255-x) * (255 - y))/255))) / 255
Translated:
SoftLight = mt_lutxy(Clp1, Clp1, "255 y - x y * 255 / * y 255 255 x - 255 y - * 255 / - * + 255 / ", y=3, u=2, v=2)
Grayscale gradient (self blend) with Histogram curve
http://www.mediafire.com/file/vmh5tgmetzn/Softlight%20maybe.png
Two Grayscale gradients (at right angles) blended.
http://www.mediafire.com/file/wnmmgmmyjqm/Softlight%20maybe2.png
But I'm still not sure if it is right, and then how to convert to the half equations for <128 and =>128 ?
Edit: Ah, just got the 'respected' layer the 'wrong way' round when doing the translation. Like Overlay, should be (respecting (Clp1) x) :
SoftLight= mt_lutxy(Clp1, Clp2, "255 x - x y * 255 / * x 255 255 x- 255 y - * 255 / - * + 255 /", y=3, u=2, v=2)
I think that's right now.
WorBry
8th May 2010, 05:31
Realized that the referenced formulas for softlight are not quite correct. For a 0 - 255 scaled image all of the 255 should really be 256. Using 255, offsets the white (255) and grey (128) points marginally. Using 256 gives the correct values.
So:
SoftLight= mt_lutxy(Clp1, Clp2, "255 x - x y * 255 / * x255 255 x- 255 y - * 255 / - * + 255 /", y=3, u=2, v=2)
Should really be:
SoftLight= mt_lutxy(Clp1, Clp2, "256 x - x y * 256 / * x 256 256 x - 256 y - * 256 / - * + 256 /", y=3, u=2, v=2)
Makes you wonder why they didnt realize that when deriving the formula
As for a (128-centred) two-part equation, well I'm stumped. The best fit I can get is with :
SL = "x 128 < 256 x - x y * 128 / * 128 / "
SL = SL + "x 128 > x 256 256 x - 128 y - * 128 / - * 256 / "
SL = SL + "x ? ? "
Output = mt_lutxy(Clp1, Clp1, SL, y=3, u=2, v=2)
But the curve is not right - kinky around the mid-point (if you see what I mean):
http://www.mediafire.com/file/uytnomtzkdw/SoftLight%20128-centred%20attempt%202.png
Sure wish someone could help me on this.:o
Still, I'm quite pleased with the effect that the 'single line' Softlight result achieves. A bit like 'Overlay' but more subtle. Quite useful for enhancing a 'filmic look' without the high-contrast.
Edit: I was surprised to find that the (AVISynth core filter) Overlay gives the following curves for 'Hardlight' and 'Softlight"
http://www.mediafire.com/file/h20a2z2kl2e/Overlay%20%27Hardlight%27%20curve.png
http://www.mediafire.com/file/mn4qvmmujm2/Overlay%20%27Softlight%27%20curve.png
I was under the impression that Overlay uses the Photoshop formulae for its blend modes.
WorBry
9th May 2010, 04:25
Oh my giddy aunt, in the midst of the brain-racking, it seems I didnt see the wood for the trees. There is no need to derive separate 'half equations'. Wheras Overlay and Hardlight apply 'Multiply' to values < 128 and 'Screen' to values > 128, the 'Softlight' curve is in fact the net sum of two actions: ((256-x) * Multiply) + (x * Screen). As a result:
a) The same the equation applies to all values across the 0 - 255 scale, and
b) The equation is intrinsically '128-centred' (as long 255 in the original formula is replaced by 256).
In the Blend_MT_alpha function, I took advantage of the 'half-equations' for Hardlight and Overlay, making it possible to apply separate opacity settings for values < 128 and >128. In this way, the effect could be biased somewhat in favour of the shadows or highlights. I thought this might not be possible with 'Softlight', but it is now easily achieved.
WorBry
9th May 2010, 07:30
I've updated Blend_MT_alpha accordingly, with added 'Softlight', 'Dodge' and 'Burn' blend modes:
Edit: Also included 'Additive', 'Subtractive' and 'Difference' modes. Some might find them useful.
I see the function text is now too long and so have put the explanation of the 'Blend Modes' as an Appendix.
BTW- On further testing with pure black and white overlays, I note that setting disimilar opacity values for Opac1 and Opac2, as applied to the 'contrast' (bi-phasic) modes (Hardlight, Overlay, Softlight), does tend to introduce some undesirable luma effects. I'll look at this some more, but for now I wouldnt recommend using this option with non-identical source clips i.e. for anything other than 'self-blends'.
function Blend_MT_alpha2(clip clp1 ,clip "clp2", string "BLMode", int "LMode", int "CMode", float "Opac", float "Opac1", float "Opac2",
\ bool "BL2", string "BLMode2", float "SOpac", float "SOpac1", float "SOpac2", bool "Preview", bool "GSGrad")
{
# Assembled by WorBry. Thanks to Didee for the Multiply, Screen and '128-Centred Contrast Multiply' computations.
# DESCRIPTION:
# Blend_MT aims to emulate a number of the blend modes available in commercial imaging software (After Effects etc). Whilst
# the existing core filters, Layer and Overlay, incorporate some of these modes, both require color space conversion to process
# YV12 sources; Overlay accepts YV12 input, but internally converts to YUV 4:4:4 for processing. Blend_MT uses MaskTools v2
# only and so allows processing exclusively in YV12. The function was created as one of set of routines to assist in the
# simulation of certain "filmic looks", but perhaps has general application.
# Requires YV12 input, assumes full (pc) range 0-255 and assumes that the source is progressive (native or deinterlaced).
# Requires MaskTools v2 only.
# PARAMETER DEFINITIONS:
#
#Clp1 - Input clip that serves as the lower (base) clip in the specified blend operation.
#Clp2 - Input clip that serves as the upper (overlay) clip in the specified blend operation. If not defined, clp2
# defaults to clp1
#BLMode - The specific blend operation that will be applied to the clips. At default, the operation is applied to the
# luma plane only; chroma is retaind from Clp1. The currently available modes are Blend, Multiply, Screen,
# Hardlight, Overlay, Softlight, Burn, Dodge, Reflect, Glow, Darken, Lighten, Additive, Subtractive and
# Difference. See Appendix 1 for explanation of these modes.
#LMode - Determines whether the luma plane (Y) will be processed by the blend operation (3) or retained from Clp1 (2)
# or Clp2 (4). Default is LMode=3.
#CMode - (2,3,4 or 0). Determines whether the chroma planes (U,V) will be processed by the blend operation (3), retained
# from Clp1 (2) retained Clp2 (4), or excluded i.e. greyscale (0). Note, aside from 'Blend' mode, Cmode=3 has
# limited application since the transformations cause marked U and V shifts with undesirable color casts.
# However, when applied to the 'contrast' modes (HardLight, Overlay, Softlight), with identical source clips,
# CMode=3 may produce an acceptable image in which the U and V profiles are broadened to create a richer mix of
# like hues. Depending on the source, this can take on a 'technicolor' film like appearance.
#BL2 - True/False. Whilst the strength of the BLMode operation can be somewhat regulated by opacity adjustment, it may
# be desirable to apply a second blend operation to the first. True, activates this option. Default is, False.
#BL2Mode - Selects the second blend mode operation. All of the blend modes available in BLMode are available in BL2Mode.
# The BLMode2 operation is applied with Clp1 as the base clp and the product of the first BLMode operation as the
# top clip. For simplicity, the LMode and CMode settings are kept the same as for BLMode.
#Opac - Sets the opacity of the BLMode product relative to Clp1. Range 0.0 - 1.0. At 1.0 (default) all of the blend
# product is represented.
#Opac1, Opac2 - Apply only to Hardlight, Overlay and Softlight. Opac1 sets the opacity of the BLMode product for Clp2 pixel
# values <128 and Opac2 sets the opacity for Clp2 values >128. This makes it possible to alter, in some way,
# the symetry of the S-curve. Note though, this can introduce some undesirable luma effects with non-identical
# source clips. Range 0.0 - 1.0. If not separately defined, defaults to Opac value.
#SOpac - Sets the opacity of the BLMode2 product relative to Clp1. Range 0.0 - 1.0. At 1.0 (default) all of the blend
# product is represented.
#SOpac1, SOpac2- Apply to Hardlight, Overlay and Softlight. SOpac1 sets the opacity of the BLMode2 product for BLMode pixel
# values <128 and SOpac2 sets the opacity for values >128. In this way it is possible to further
# manipulate the shape of the curve. Range 0.0 - 1.0. If not separately defined, default to SOpac value.
#Preview - True/False. True gives a composite view of the input clips and final output. Default is False.
#GSGrad - True/False. True demonstrates the effect of the blend operation as applied to a greyscale gradient; histogram
# reveals the luma curve. Default is False.
#
#Defaults:
#clp2 = default(clp2, clp1)
BLMode = default(BLMode, "Blend")
LMode = default(LMode, 3)
CMode = default(Cmode, 2 )
Opac = default(Opac, 1.0)
Opac1 = default(Opac1, Opac)
Opac2 = default(Opac2, Opac)
BL2 = default(BL2, False)
BLMode2 = default(BLMode2, "Blend")
SOpac = default(SOpac, 1.0)
SOpac1 = default(SOpac1, SOpac)
SOpac2 = default(SOpac2, SOpac)
Preview = default(Preview, False)
GSGrad = default(GSGrad, False)
#
#Limits:
Opac = Opac <0.0 ? 0.0 : Opac >1.0 ? 1.0 : Opac
Opac1 = Opac1 <0.0 ? 0.0 : Opac1 >1.0 ? 1.0 : Opac1
Opac2 = Opac2 <0.0 ? 0.0 : Opac2 >1.0 ? 1.0 : Opac2
SOpac = SOpac <0.0 ? 0.0 : SOpac >1.0 ? 1.0 : SOpac
SOpac1 = SOpac1 <0.0 ? 0.0 : SOpac1 >1.0 ? 1.0 : SOpac1
SOpac2 = SOpac2 <0.0 ? 0.0 : SOpac2 >1.0 ? 1.0 : SOpac2
#Switch for CMode=0 greyscale:
CMode = (Cmode == 0) ? 128 : CMode
#Create greyscale gradients for GSGrad:
GSG = colorbars().converttoyv12().mt_lutspa(relative=true,expr="x 255 *").greyscale()
GSG = GSG.TurnLeft().LanczosResize(640,360)
Clp1 = (GSGrad == False) ? Clp1 : GSG
clp2 = default(clp2, clp1)
Clp2 = (GSGrad == False) ? Clp2 : GSG
# Create mt_lutxy expressions for the blend modes:
# Didee's '128-Centred Contrast Multiply' (HardLight):
HDL = " y 128 < x y * 128 / "+String(Opac1)+" * x 1 "+String(Opac1)+" - * + "
HDL = HDL + " y 128 > 256 256 x - 128 y 128 - - abs * 128 / - "+String(Opac2)+" * x 1 "+String(Opac2)+" - * + "
HDL = HDL + "x ? ?"
HDL2 = " y 128 < x y * 128 / "+String(SOpac1)+" * x 1 "+String(SOpac1)+" - * + "
HDL2 = HDL2 + " y 128 > 256 256 x - 128 y 128 - - abs * 128 / - "+String(SOpac2)+" * x 1 "+String(SOpac2)+" - * + "
HDL2 = HDL2 + "x ? ?"
#Didee's '128-centred contrast multiply' commuted (Overlay):
OVE = " x 128 < y x * 128 / "+String(Opac1)+" * x 1 "+String(Opac1)+" - * + "
OVE = OVE + " x 128 > 256 256 y - 128 x 128 - - abs * 128 / - "+String(Opac2)+" * x 1 "+String(Opac2)+" - * + "
OVE = OVE + "y ? ?"
OVE2 = " x 128 < y x * 128 / "+String(SOpac1)+" * x 1 "+String(SOpac1)+" - * + "
OVE2 = OVE2 + " x 128 > 256 256 y - 128 x 128 - - abs * 128 / - "+String(SOpac2)+" * x 1 "+String(SOpac2)+" - * + "
OVE2 = OVE2 + "y ? ?"
#Softlight: Based on Gimp and Delphi formula
SFL = " y 128 < 255 x - x y * 255 / * x 255 255 x - 255 y - * 255 / - * + 255 / "+String(Opac1)+" * x 1 "+String(Opac1)+" - * + "
SFL = SFL + " y 128 > 255 x - x y * 255 / * x 255 255 x - 255 y - * 255 / - * + 255 / "+String(Opac2)+" * x 1 "+String(Opac2)+" - * + "
SFL = SFL + " y ? ? "
SFL2 = " y 128 < 255 x - x y * 255 / * x 255 255 x - 255 y - * 255 / - * + 255 / "+String(SOpac1)+" * x 1 "+String(SOpac1)+" - * + "
SFL2 = SFL2 + " y 128 > 255 x - x y * 255 / * x 255 255 x - 255 y - * 255 / - * + 255 / "+String(SOpac2)+" * x 1 "+String(SOpac2)+" - * + "
SFL2 = SFL2 + "y ? ? "
#Glow
GLO = "x 255 == x "+String(Opac)+" * x 1 "+String(Opac)+" - * + "
GLO = GLO + " 255 y y * 255 x - / min "+String(Opac)+" * x 1 "+String(Opac)+" - * + "
GLO = GLO + "?"
GLO2 = "x 255 == x "+String(SOpac)+" * x 1 "+String(SOpac)+" - * + "
GLO2 = GLO2 + " 255 y y * 255 x - / min "+String(SOpac)+" * x 1 "+String(SOpac)+" - * + "
GLO2 = GLO2 + "?"
#Reflect
REF = "y 255 == y "+String(Opac)+" * x 1 "+String(Opac)+" - * + "
REF = REF + " 255 x x * 255 y - / min "+String(Opac)+" * x 1 "+String(Opac)+" - * + "
REF = REF + "?"
REF2 = "y 255 == y "+String(SOpac)+" * x 1 "+String(SOpac)+" - * + "
REF2 = REF2 + " 255 x x * 255 y - / min "+String(SOpac)+" * x 1 "+String(SOpac)+" - * + "
REF2 = REF2 + "?"
#
Bl1Expr = (BLMode=="Blend") ? "y "+string(Opac)+" * x 1 "+string(Opac)+" - * +"
\ : (BLMode=="Multiply") ? "x y * 255 / "+String(Opac)+" * x 1 "+String(Opac)+" - * +"
\ : (BLMode=="Screen") ? "256 256 x - 256 y - * 256 / - "+String(Opac)+" * x 1 "+String(Opac)+" - * + "
\ : (BLMode=="Hardlight") ? HDL
\ : (BLMode=="Overlay") ? OVE
\ : (BLMode=="Softlight") ? SFL
\ : (BLMode=="Burn") ? "255 255 x - 256 * y 1 + / - "+String(Opac)+" * x 1 "+String(Opac)+" - * + "
\ : (BLMode=="Dodge") ? " x 256 * 256 y - / "+String(Opac)+" * x 1 "+String(Opac)+" - * + "
\ : (BLMode=="Glow") ? GLO
\ : (BLMode=="Reflect") ? REF
\ : (BLMode=="Lighten") ? "x y > x y "+String(Opac)+" * x 1 "+String(Opac)+" - * + ? "
\ : (BLMode=="Darken") ? "x y < x y "+String(Opac)+" * x 1 "+String(Opac)+" - * + ? "
\ : (BLMode=="Additive") ? "x y + 255 > 255 x y + "+String(Opac)+" * x 1 "+String(Opac)+" - * + ? "
\ : (BLMode=="Subtractive") ? "x y + 256 - 0 < 0 x y + 256 - "+String(Opac)+" * x 1 "+String(Opac)+" - * + ? "
\ : (BLMode=="Difference") ? "x y - abs "+String(Opac)+" * x 1 "+String(Opac)+" - * + "
\ : Assert(false, "Invalid BLMode String")
#
Bl2Expr = (BLMode2=="Blend") ? "y "+string(SOpac)+" * x 1 "+string(SOpac)+" - * +"
\ : (BLMode2=="Multiply") ? "x y * 255 / "+String(SOpac)+" * x 1 "+String(SOpac)+" - * +"
\ : (BLMode2=="Screen") ? "256 256 x - 256 y - * 256 / - "+String(SOpac)+" * x 1 "+String(SOpac)+" - * + "
\ : (BLMode2=="Hardlight") ? HDL2
\ : (BLMode2=="Overlay") ? OVE2
\ : (BLMode2=="Softlight") ? SFL2
\ : (BLMode2=="Burn") ? "255 255 x - 256 * y 1 + / - "+String(SOpac)+" * x 1 "+String(SOpac)+" - * + "
\ : (BLMode2=="Dodge") ? " x 256 * 256 y - / "+String(SOpac)+" * x 1 "+String(SOpac)+" - * + "
\ : (BLMode2=="Glow") ? GLO2
\ : (BLMode2=="Reflect") ? REF2
\ : (BLMode2=="Lighten") ? "x y > x y "+String(SOpac)+" * x 1 "+String(SOpac)+" - * + ? "
\ : (BLMode2=="Darken") ? "x y < x y "+String(SOpac)+" * x 1 "+String(SOpac)+" - * + ? "
\ : (BLMode2=="Additive") ? "x y + 255 > 255 x y + "+String(SOpac)+" * x 1 "+String(SOpac)+" - * + ? "
\ : (BLMode2=="Subtractive") ? "x y + 256 - 0 < 0 x y + 256 - "+String(SOpac)+" * x 1 "+String(SOpac)+" - * + ? "
\ : (BLMode2=="Difference") ? "x y - abs "+String(SOpac)+" * x 1 "+String(SOpac)+" - * + "
\ : Assert(false, "Invalid BLMode String")
#Apply blend operation:
BLOut = mt_lutxy(clp1,clp2, Bl1Expr, Y=LMode, U=CMode, V=CMode)
Output = (BL2 == False) ? BLOut : mt_lutxy(Clp1, BLOut, Bl2Expr, Y=LMode, U=CMode, V=CMode)
#Preview set-up:
Clp1s = Clp1.LanczosResize(640,360).Subtitle("Clp1 (Base)", size=14)
Clp2s = Clp2.LanczosResize(640,360).Subtitle("Clp2 (Top)", size=14)
Outs = Output.LanczosResize(640,360).Subtitle("Output", size=14)
Outgs = Output.Greyscale().LanczosResize(640,360).Subtitle("Output (Greyscale)", size=14)
Clpsv = StackVertical(Clp2s, Clp1s)
Outsv = StackVertical(Outs, Outgs)
Prev = StackHorizontal(Clpsv, Outsv)
#GSGrad view set-up:
GSGH = GSG.Histogram().Subtitle("Original")
GSGoH = Output.LanczosResize(640,360).Histogram().Subtitle("Blend operation applied")
GSGout = StackVertical(GSGH, GSGoH)
#Output
Final = (Preview == False && GSGrad == False) ? Output
\ : Preview ? Prev : GSGout
#
Return Final
}
WorBry
11th May 2010, 10:30
........Appendix 1. Explanation of the 'Blend Modes':
# BLEND_MT_ALPHA2
# APPENDIX 1
# EXPLANATION OF BLEND MODES
# Blend - Simple merge operation. Note that several other so-called 'blend modes' may be acheived simply by modulating
# the LMode, CMode and Opacity settings (see below). Thus:
# 'Luminosity' mode retains the luma from clp2 and chroma from clp1. Blend mode, at default settings (LMode=3,
# Cmode=2, Opacity = 1.0), or otherwise LMode=4 and CMode=2, correspond to this.
# 'Chroma' or 'Color' mode retains the luma from clp1 and the chroma from clp2. LMode=2 and CMode=3 at full
# opacity (1.0), or LMode=2 and CMode=4, correspond to this.
# 'Average' mode is equivalent to 0.5 (50%) opacity.
#
# Multiply - Multiplies the corresponding pixel values of the two input clips and divides the result by 255. The outcome
# is always a darker composite image. If the input clips are identical, the transformation corresponds to an
# exponential curve.
#
# Screen - Has the opposite effect to Multiply. It multiples the inverted (complement) values. The outcome is always
# a lighter composite image.
#
# Hardlight - (128-Centred Contrast Multiply) Multiplies or screens the pixel values, depending on top clip (Clp2) values.
# The effect is likened to shining a harsh spotlight (as if Clp2) on the image (Clp1), hence the name. For Clp2
# values > 128 (50% grey) the image is screened, and so lightened; useful for adding highlights. For Clp2
# values < 128, the image is multiplied,and so darkened; useful for adding shadows. Pure black or white results
# in pure black or white. The computation differs from that used in After Effects/Photoshop, in that it is
# 128-centred; this avoids luma overflow. If Clp2 and Clp1 have identical pixel values, the result corresponds
# to a symetrical S-curve. This is useful for creating high contrast images characteristic of certain 'film looks'.
#
# Overlay - (128-Centred Contrast Multiply commuted). Multiplies or screens the pixel values, depending on the base clip
# values. For Clp1 pixel values > 128 the image is screened and for Clp1 values < 128 the image is multiplied.
# In this way patterns overlay the existing pixels while preserving the highlights and shadows of the base clip.
# The formulation, like Hardlight, is 128-centered. It follows that if Clp1 and Clp2 have identical pixel values,
# Overlay and Hardlight will produce the same result.
# SoftLight - Produces a similar effect to Overlay. In this case, "Multiply" and "Screen" derivatives are combined.
# The composite effect is likened to shining a diffused spotlight (as if Clp2) on the image (Clp1). When Clp1
# and Clp2 have identical values, the resulting S-curve that is milder than that produced by Overlay. This is
# very useful for enhancing a generic 'filmic look' without the 'high contrast'.
#
# Burn - Decreases the contrast to make the top clip reflect the base clip. The darker the bottom layer, the more this
# influnces the mix. Blending with white produces no difference. Blending with black gives black. The effect is
# similar to changing the 'black point'.
#
# Dodge - Decreases the contrast to make the base reflect the top clip: the brighter the top clip, the more this
# influences the mix. Blending with white gives white. Blending with black does not change the image.
# The effect is similar to changing the 'white point'.
#
# Reflect - Gives the appearance of adding shiny objects. See the Greyscale gradient curve (GSGrad option) to see why. Use
# with caution and always wear sun glasses. Reduced opacity is recommended.
#
# Glow - The opposite of Reflect, but the same cautions apply.
#
# Darken - Takes the darkest pixel value from either clip. If the input clips have identical values, there is no change.
#
# Lighten - Takes the lightest pixel value from either clip. If the input clips have identical values, there is no change.
#
# Additive - Adds the pixel values from Clp1 and Clp2. Sum values >255 are truncated to pure white.
#
# Subtractive- Similar to Additive mode, except that 256 is subtracted from the sum pixel value. Resulting values <0 are
# truncated to pure black.
#
# Difference - Takes the absolute difference between in the clips i.e. subtracts Clp2 value from Clp1 value, or visa-versa
# to always give a positive value. As a result, overlay with pure white inverts the base image, and overlay with
# pure black produces no change, as all values are 0. The primary use is for aligning two images with an identical
# visual element.
Gavino
11th May 2010, 12:57
Glad to see you got it working satisfactorily in the end.
One thing still puzzles me:
Realized that the referenced formulas for softlight are not quite correct. For a 0 - 255 scaled image all of the 255 should really be 256. Using 255, offsets the white (255) and grey (128) points marginally. Using 256 gives the correct values.
If the formulae are derived using a range of 0.0 to 1.0 which is then scaled to 0-255, I don't see how the number 256 can arise.
If 0 is white and 255 is black, isn't (50%) grey 127.5 rather than 128 anyway?
(Perhaps your 128-centred formulae should really be 127.5 centred? :devil:)
WorBry
12th May 2010, 04:51
Well that’s a good point. I guess the reason why I was looking for a 128-centred formula was for consistency with Didee’s ‘128-Centred Contrast Multiply’ (Hardlight) and it’s mirror – Overlay. Exactly why Didee chose 128 as the centre point rather than 127.5, I am not entirely sure. No doubt there is a valid reason and he could answer that point more accurately than I.
My understanding is that the ‘centred approach’ allows the ‘Multiply’ and ‘Screen’ components to be applied ‘cleanly’ to values <128 and >128 respectively, so avoiding conflict and the potential for luma overflow. The conventional (i.e. Photoshop-based) two-part formulas for Hardlight and Overlay all take the ‘If x> 128 (or <128) then.....Else’ type approach. In that case, what then is the centre point? It can’t be 128 or 127.5.
The Softlight formula (at least the one I have taken) however, is a different situation because it represents the sum of two opposing actions across the entire 0-255 scale, so there is not the same need to set a centre point – as far as I can tell, the equation does that itself. In fact, I find that if I do set the centre point at 128 (in like manner to `128-Centred Contrast Multiply) this seems to upset the balance. This is apparent when overlaying an image with pure white or black – resulting in pits of black in light areas and visa-versa. In this case, resorting to the ’If-Then-Else’ approach, or not partitioning the formula at all, seems to resolve this problem.
I would have to agree though, that if 127.5 is logically taken to be the true` centre (grey point), then the ’original` Softlight formula (i.e. using 255`s instead of 256`s) is perfectly valid – computing 127.5 (as if a self-blend i.e. x values = y values) results in 127.5 exactly. So I think I will leave it that way unless there is a compelling argument for forcing a 128 centre point; I suspect not.
I`ve amended the SoftLight formula in Blend_MT_alpha2 accordingly.....although keeping a 'If x < 128 then...else' format to allow separate Opac1 and Opac2 settings.
Didée
12th May 2010, 11:34
"128" is the common center point used for the [0,255] range. Blame the restrictions of 8-bit colorspaces.
In any case, you need a neutral element for the do-nothing case. Using 127.5 as center is not good because you cannot do "nothing". And when you're going to use the result of mt_makediff(this,that) as the modifier clip, this one is 128-centered, too. Even worse: when the blend formula is centered to 127.5, then you can not *correctly* use the result of mt_makediff() as modifier clip: in that case, it will introduce a drift, because 128 would be supposed to do nothing.
Also, don't forget to think out of the box - all these blending modes are not only useful for just modifying the overall frame contrast. You could also use them e.g. for sharpening tasks, and such. For which the presence of a discretely available center element is mandatory.
WorBry
12th May 2010, 15:38
Thanks for your explanation. In that case, I remain stumped on a suitable '128-Centred' formula for Softlight.
Based on the 'original' formula :
Result= (((255-x) * ((x * y)/255)) + (x * (255-((255-x) * (255 - y))/255))) / 255
i.e.
mt_lutxy(Clp1, Clp2, "255 x - x y * 255 / * x 255 255 x - 255 y - * 255 / - * + 255 /", y=3, u=2, v=2)
Then, either:
SFL = " x 128 < 255 x - x y * 255 / * x 255 255 x - 255 y - * 255 / - * + 255 / "+String(Opac1)+" * x 1 "+String(Opac1)+" - * + "
SFL = SFL + " x 128 > 255 x - x y * 255 / * x 255 255 x - 255 y - * 255 / - * + 255 / "+String(Opac2)+" * x 1 "+String(Opac2)+" - * + "
SFL = SFL + " x ? ? "
mt_lutxy(clp1,clp2, SFL, y=3, U=2, V=2)
or
SFL = " x 128 < 256 x - x y * 256 / * x 256 256 x - 256 y - * 256 / - * + 256 / "+String(Opac1)+" * x 1 "+String(Opac1)+" - * + "
SFL = SFL + " x 128 > 256 x - x y * 256 / * x 256 256 x - 256 y - * 256 / - * + 256 / "+String(Opac2)+" * x 1 "+String(Opac2)+" - * + "
SFL = SFL + " x ? ? "
mt_lutxy(clp1,clp2, SFL, y=3, U=2, V=2)
....both give this sort of pitting/stippling when a greyscale image is overlayed with pure black or white.....which I dont think should happen.
http://www.mediafire.com/file/2wzwgmtoi5y/Softlight%20with%20white%20overlay.png
http://www.mediafire.com/file/m3ino2mnmlu/Softlight%20with%20black%20overlay.png
Converting to a 'If x<128 then, this......or Else (everything else)' resolves it (which is how I have it now in Blend_MT) but then it is not '128-centred' in terms of a neutral anchor point?
Maybe, the SoftLight formula is a different case (for the reasons I suggested above) or is it possible to derive a workable half-equation solution (for x<128 and x>128) that doesnt upset the balance? I couldnt find one, and so resorted to using the 'full' formula for both halves.
Didée
12th May 2010, 16:15
Obviously your LUT strings are somehow skewed. Assuming that the 1st clip is the "base" clip and the 2nd clip is the "modifier" clip, you would check for y>/<128, not x. Value 128 on the base clip doesn't need any special handling. Value 128 on the overlay clip is which needs to be checked.
WorBry
12th May 2010, 17:42
For sure, that fixes it, but then it's back to this issue of which clip the blend should be referencing/respecting. All of the descriptions for SoftLight I have seen say that (like Overlay) it references (and so reflects the characteristics - highlights, shadows) of the base clip e.g blending with black or white (as the top layer) doesnt result in pure black or white (as Hardlight does). But then I'm thinking, if it does the intended job, which it does, what the heck. So, I'll take your advice on the 'using y instead of x'. Thanks.
Modified the SoftLight formula in Blend_MT_alpha2 (post# 26) accordingly.
Loadus
13th May 2010, 06:48
I once tried to emulate a filmlook by using softlight/hardlight/overlay formulas, and got them stitched into a GPU shader filter (well, actually, Phaeron did the code, not me) :D
The core calculation for softlight was:
A = 1st layer (you can use colorgradients or grayscale on this)
B = 2nd layer (ie. background)
C = resulting composite
if (A < 0.5)
C = (2 * A - 1) * (B - B * B) + B;
else
C = (2 * A - 1) * (sqrt(B) - B) + B;
Find the discussion here:
http://forums.virtualdub.org/index.php?act=ST&f=7&t=14597&st=0
The shader and the code:
http://www.loadusfx.net/virtualdub/filmfxguide.htm
In the examples I used it to just slightly colorgrade the video, but you can crank up the volume and do some mad stuff with it.
Version 1.7.3 is based on a cineon style exposure, and it's here:
http://www.loadusfx.net/virtualdub/Film%20FX%20v1.7.3.fx
The code on my part is a bit goofy, I haven't cleaned it up + the terms are odd. But it's a tweaker's heaven with the sliders and all. I hope you can get some help from those. ^^
WorBry
13th May 2010, 16:18
Thanks for your input. I was beginning to wonder if anyone was interested in film-looks anymore.
If I'm not mistaken (always a possibilty), your Softlight code is based on the Photoshop formula, of which there seem to be various interpretations...some maybe incorrect. I do, at some point, intend to derive a workable mt_lutxy equation based on the Photoshop formula, to see how it compares with the 'Gimp-based' equation that I am using (references in post#22 ); it's interesting to note though that in some versions of Gimp (mine included) they used the same formula for Overlay and SoftLight - I understand this has now been changed.
Your VDub Shader filter looks interesting (bearing in mind that this is a sub-forum for AVISynth useage). Of course, there are lots of things you can do with RGB, in terms of chroma, that are not so easy to replicate in YUV colorspace. My objective was to process entirely in YV12, without intermediate color space conversions, and mimimize, as far as possible, multi-stage layering (as it relates to the respective luma and chroma planes).
The 'bleach' component of your filter is of interest, and I'll look at that some more.
I did experiment quite a bit with a single 'contrast curve-desaturation' blend stage (using a greyscale overlay clip), but decided to keep them separate. That way you can independently control the strength of the luma contrast curve and the degree of chroma desaturation. In fact the SelSah (Selective Saturation & Hue) function that I put together allows you to restrict the saturation control to a defined luma range. Plus, you can choose which clip you want to use for 'controlling' the saturation, which might be the original clip or the blend product. This is relevant, because if one chooses to 'partially desaturate' (not a good term I know), the pattern will be influenced by the shape of the luma curve (that's just the way the function works)...which can be put to some advantage.
Out of interest how do you install your filter? I've never used this type of VDub filter.
Edit: Well I can see some instruction for installation in the virtualdub.org forum thread, but where do I find the GPUFilter vdf?
Edit: Could you just clarify your definitions for the layers. As I understand your terminology:
A = First Layer appears refer to the image ('modifier' as Didee more aptly puts it) that will be overlayed on top of the base image.
B = Second Layer (i.e. background) I take to mean the base image.
If so, this seems contrary to convention (in terms of 'layer' orientation), although consistent with Didee's assertion that it should be the 'modifier' clip that is referenced in partitioned equation. As you might have noted from my foregoing posts, I get hung up on this issue, not least because there are (supposed) Photoshop formulae out there that appear to have got things mixed up (for some modes). This, oft referred to, source being a case in point:
http://www.nathanm.com/photoshop-blending-math/
CruNcher
14th May 2010, 01:04
Ehmm you should be able to use that Shader with AviShader for Avisynth :)
http://forum.doom9.org/showthread.php?s=&threadid=87295
WorBry
14th May 2010, 02:10
But it's still RGB though, isnt it? Believe me, if there was an AVISynth filter for generating custom curves (natively) in YV12, I likely wouldnt have gone down this blend mode road; 'possible', of course, with mighty Masktools v2, but way, way way, beyond my capabilities.
Still looking for this ethereal GPUFilter vdf plugin.
If it's relevant, I'm running a GeForce 8400M GS card, Vista (SP2), Core2Duo CPU, DirectX 10.0. Dont really want to mess with the registry ('missing' dll's etc) if that's what it invloves.....I still have the scars.
Edit:
Loadus. Actually, from the information in your 'Film Fx' filter guide, I'm already pretty familiar with the processes involved. What intrigues me is the 'bleaching' effect. OK, I understand that in the 'normal process' adjustments are made in a greyscale copy and that this is applied to the original image with a Softlight blend. For the 'bleaching' component it is stated that:
"The bleaching in the shader is done by reversing the effect of the shader. When bleaching, all the curves and gamma sliders are reversed. When the bleach is set to null (the slider is in the center) the EffectCurves and EffectGamma have no effect....
....The effect layer of the shader is a grayscale copy of the original image and it is blended to the original using "SoftLight" blending, common in image editing programs. All the EffectCurves and EffectGamma's are done to that grayscale effectlayer".
I'm wondering then if the 'bleaching' really just amounts to an opacity adjustment of the final Softlight blend?
Loadus
15th May 2010, 18:29
What intrigues me is the 'bleaching' effect.
The SoftLight formula used in my code is from Paint Shop Pro (I guess it's "SoftLight Legacy" or similar in Photoshop) - basically an Overlay calculation that doesn't allow highlight tones from clipping over (ie. classic SoftLight that I use in almost all photo editing).
The bleach effect is done by making the overlay image negative/inversed. If you then apply a blur into the overlay layer (say, radius 20 on a 720 x 480 clip) you'll get a tonemapping effect, similar to the 'HDR' photostuff that peeps do. ^^
EDIT:
If you're still looking for the filter, it's here: http://www.virtualdub.org/beta/gpufilter-0.6.zip
WorBry
15th May 2010, 20:35
Thanks for your reply.
The bleach effect is done by making the overlay image negative/inversed.
Ah, OK, gotcha. Easy to do with Blend_MT also.
...... If you then apply a blur into the overlay layer (say, radius 20 on a 720 x 480 clip) you'll get a tonemapping effect, similar to the 'HDR' photostuff that peeps do. ^^
Reduce global contrast, whilst preserving local contrast......pseudo 'extended dynamic range' ; interesting.....from a 'non-peep' perspective :cool: :D . I shoot most of my DV stuff in (Panasonic) 'Procinema' mode' which, apparently, uses some electronic jiggery-pokery in the signal amplification to extend the dynamic range (as claimed). Actually, it's a pretty good starting point for some of this 'film-look' stuff, aside from the pseudo-progressive (frame) motion, which I find a little too jittery, and the pesky aliasing to deal with. Waiting for further improvements in the 50p/60p and low-light capability of AVCHD cams before I make the jump to HD.
EDIT:
If you're still looking for the filter, it's here: http://www.virtualdub.org/beta/gpufilter-0.6.zip
Thanks. It installed/loaded OK (no complaints about missing dll's etc). I'll play around some more. Would be nice if there was an option to reset the FilmFx to default settings.
Gavino
16th May 2010, 10:52
Are the formulae for Additive and Subtractive correct?
\ : (BLMode=="Lighten") ? "x y > x y "+String(Opac)+" * x 1 "+String(Opac)+" - * + ? "
\ : (BLMode=="Darken") ? "x y < x y "+String(Opac)+" * x 1 "+String(Opac)+" - * + ? "
\ : (BLMode=="Additive") ? "x y + 255 > 255 x y + "+String(Opac)+" * x 1 "+String(Opac)+" - * + ? "
\ : (BLMode=="Subtractive") ? "x y + 256 - 0 < 0 x y + 256 - "+String(Opac)+" * x 1 "+String(Opac)+" - * + ? "
\ : (BLMode=="Difference") ? "x y - abs "+String(Opac)+" * x 1 "+String(Opac)+" - * + "
\ : Bl1Expr
Note that the opacity is only applied on one branch of the conditionals. That doesn't matter for Lighten and Darken, since the result is unchanged, but not for Additive and Subtractive - is this intended? Should it not be
(BLMode=="Additive") ? "x y + 255 > 255 x y + ? "+String(Opac)+" * x 1 "+String(Opac)+" - * + " etc
I think it would make the formulae more readable and checkable if you defined the opacity expressions as variables, eg
Opacity = String(Opac)+" * x 1 "+String(Opac)+" - * + "
instead of repeating them everywhere.
Also, if you call the function with an unrecognised BLMode, it gives the error "I don't know what Bl1Expr means".
Better would be to replace the final Bl1Expr in the code above with something like
Assert(false, "Invalid BLMode String")
WorBry
17th May 2010, 03:03
Thanks for your comments. All suggestions for improvement are most welcome.
Are the formulae for Additive and Subtractive correct?
For sure, the formulae per se are 'correct'.
Note that the opacity is only applied on one branch of the conditionals. That doesn't matter for Lighten and Darken, since the result is unchanged, but not for Additive and Subtractive - is this intended? Should it not be
(BLMode=="Additive") ? "x y + 255 > 255 x y + ? "+String(Opac)+" * x 1 "+String(Opac)+" - * + " etc
For the Opacity component of the mt_lutxy expression strings, I took the same as approach as Didee's earlier Hardlight functions:
http://forum.doom9.org/showpost.php?p=703132&postcount=2
And taking, for example, 'Additive':
(x + y ) > 255 ? 255 : (( x + y) * "+String(Opac)+" ) + (x * (1 - "+String(Opac)+" ))
....seems right to me and mt_polish (using a arbitary value instead of "+String(Opac)+" ) literally translates this to:
x y + 255 > 255 x y + "+String(Opac)+" * x 1 "+String(Opac)+" - * + ?
i.e. puts the ? ('If' condition) at the end of the string (stack?), which is how it is now in Blend_MT.
Reverse translation (mt_infix) of:
"x y + 255 > 255 x y + ? "+String(Opac)+" * x 1 "+String(Opac)+" - * + "
literally gives:
((((x + y) > 255 )) ? 255 : (x + y)) * "+String(Opac)+" ) + (x * (1 - "+String(Opac)+" )))
Which seems to be the same thing. Both '?' options work and give the same result, so I am not sure, which is the 'more correct' ?
I think it would make the formulae more readable and checkable if you defined the opacity expressions as variables, eg
Opacity = String(Opac)+" * x 1 "+String(Opac)+" - * + "
instead of repeating them everywhere.
Yes, I've been thinking of ways to cut down on the repetition, and will have a look at that, bearing in mind that there are six opacity determinants in all (Opac, Opac1, Opac2, SOpac, SOpac1, SOpac2).
I did at one point (posts #10 and #11) consider applying the blend opacity through a second mt_lutxy expression, but would that (obviously) would mean two lut calcs (each with their own rounding errors) instead of one, and make it more difficult to work in separate opacity determinants for <128 and >128 with the Hardlight, Softlight and Overlay modes.
Also, if you call the function with an unrecognised BLMode, it gives the error "I don't know what Bl1Expr means".
Better would be to replace the final Bl1Expr in the code above with something like
Assert(false, "Invalid BLMode String")
Quite agree. Added it. Thanks.
WorBry
17th May 2010, 04:32
I think it would make the formulae more readable and checkable if you defined the opacity expressions as variables, eg
Opacity = String(Opac)+" * x 1 "+String(Opac)+" - * + "
instead of repeating them everywhere.
So, I guess you are suggesting something like........(edited)
Edit: Script modified to incorporate changes as suggested by Gavino (below). Now Blend_MT_alpha3. The Appendix 1 'Explanation of the Blend Modes' (Post# 27) remains unchanged.
Edit2: Corrected Overlay and Softlight expressions, and changed the base clip for the BLMode2 operation from Clp1 to the BLMode product (as per post# 46 below)
function Blend_MT_alpha3(clip clp1 ,clip "clp2", string "BLMode", int "LMode", int "CMode", float "Opac", float "Opac1", float "Opac2",
\ bool "BL2", string "BLMode2", float "SOpac", float "SOpac1", float "SOpac2", bool "Preview", bool "GSGrad")
{
# Assembled by WorBry. Thanks to Didee for the Multiply, Screen and '128-Centred Contrast Multiply' computations, and to
# Gavino for valuable advice on syntax.
# DESCRIPTION:
# Blend_MT aims to emulate a number of the blend modes available in commercial imaging software (After Effects etc). Whilst
# the existing core filters, Layer and Overlay, incorporate some of these modes, both require color space conversion to process
# YV12 sources; Overlay accepts YV12 input, but internally converts to YUV 4:4:4 for processing. Blend_MT uses MaskTools v2
# only and so allows processing exclusively in YV12. The function was created as one of set of routines to assist in the
# simulation of certain "filmic looks", but perhaps has general application.
# Requires YV12 input, assumes full (pc) range 0-255 and assumes that the source is progressive (native or deinterlaced).
# Requires MaskTools v2 only.
# PARAMETER DEFINITIONS:
#
#Clp1 - Input clip that serves as the lower (base) clip in the specified blend operation.
#Clp2 - Input clip that serves as the upper (overlay) clip in the specified blend operation. If not defined, clp2
# defaults to clp1
#BLMode - The specific blend operation that will be applied to the clips. At default, the operation is applied to the
# luma plane only; chroma is retaind from Clp1. The currently available modes are Blend, Multiply, Screen,
# Hardlight, Overlay, Softlight, Burn, Dodge, Reflect, Glow, Darken, Lighten, Additive, Subtractive and
# Difference. See Appendix 1 for explanation of these modes.
#LMode - Determines whether the luma plane (Y) will be processed by the blend operation (3) or retained from Clp1 (2)
# or Clp2 (4). Default is LMode=3.
#CMode - (2,3,4 or 0). Determines whether the chroma planes (U,V) will be processed by the blend operation (3), retained
# from Clp1 (2) retained Clp2 (4), or excluded i.e. greyscale (0). Note, aside from 'Blend' mode, Cmode=3 has
# limited application since the transformations cause marked U and V shifts with undesirable color casts.
# However, when applied to the 'contrast' modes (HardLight, Overlay, Softlight), with identical source clips,
# CMode=3 may produce an acceptable image in which the U and V profiles are broadened to create a richer mix of
# like hues. Depending on the source, this can take on a 'technicolor' film like appearance.
#BL2 - True/False. Whilst the strength of the BLMode operation can be regulated by opacity adjustment, it may
# be desirable to apply a second blend operation to the first. True, activates this option. Default is, False.
#BL2Mode - Selects the second blend mode operation. All of the blend modes available in BLMode are available in BL2Mode.
# The BLMode2 operation is applied as a 'self-blend' i.e. the BLMode product is the top and base clip. For
# simplicity, the LMode and CMode settings are kept the same as for BLMode.
#Opac - Sets the opacity of the BLMode product relative to Clp1. Range 0.0 - 1.0. At 1.0 (default) all of the blend
# product is represented.
#Opac1, Opac2 - Apply only to Hardlight, Overlay and Softlight. Opac1 sets the opacity of the BLMode product for pixel
# values <128 and Opac2 sets the opacity for values >128. This makes it possible to alter, in some way,
# the symetry of the S-curve. Note though, this can introduce some undesirable luma effects with non-identical
# source clips. Range 0.0 - 1.0. If not separately defined, defaults to Opac value.
#SOpac - Sets the opacity of the BLMode2 product relative to the BLMode product. Range 0.0 - 1.0. At 1.0 (default) all
of the BLMode2 product is represented.
#SOpac1, SOpac2- Apply to Hardlight, Overlay and Softlight. SOpac1 sets the opacity of the BLMode2 product for pixel
# values <128 and SOpac2 sets the opacity for values >128. In this way it is possible to further
# manipulate the shape of the curve. Range 0.0 - 1.0. If not separately defined, default to SOpac value.
#Preview - True/False. True gives a composite view of the input clips and final output. Default is False.
#GSGrad - True/False. True demonstrates the effect of the blend operation as applied to a greyscale gradient; histogram
# reveals the luma curve. Default is False.
#
#Defaults:
#clp2 = default(clp2, clp1)
BLMode = default(BLMode, "Blend")
LMode = default(LMode, 3)
CMode = default(Cmode, 2 )
Opac = default(Opac, 1.0)
Opac1 = default(Opac1, Opac)
Opac2 = default(Opac2, Opac)
BL2 = default(BL2, False)
BLMode2 = default(BLMode2, "Blend")
SOpac = default(SOpac, 1.0)
SOpac1 = default(SOpac1, SOpac)
SOpac2 = default(SOpac2, SOpac)
Preview = default(Preview, False)
GSGrad = default(GSGrad, False)
#
#Limits:
Opac = Opac <0.0 ? 0.0 : Opac >1.0 ? 1.0 : Opac
Opac1 = Opac1 <0.0 ? 0.0 : Opac1 >1.0 ? 1.0 : Opac1
Opac2 = Opac2 <0.0 ? 0.0 : Opac2 >1.0 ? 1.0 : Opac2
SOpac = SOpac <0.0 ? 0.0 : SOpac >1.0 ? 1.0 : SOpac
SOpac1 = SOpac1 <0.0 ? 0.0 : SOpac1 >1.0 ? 1.0 : SOpac1
SOpac2 = SOpac2 <0.0 ? 0.0 : SOpac2 >1.0 ? 1.0 : SOpac2
#Switch for CMode=0 greyscale:
CMode = (Cmode == 0) ? 128 : CMode
#Create greyscale gradients for GSGrad:
GSG = colorbars().converttoyv12().mt_lutspa(relative=true,expr="x 255 *").greyscale()
GSG = GSG.TurnLeft().LanczosResize(640,360)
Clp1 = (GSGrad == False) ? Clp1 : GSG
clp2 = default(clp2, clp1)
Clp2 = (GSGrad == False) ? Clp2 : GSG
# Create mt_lutxy expressions for the blend modes:
Opac = " "+String(Opac)+" * x 1 "+String(Opac)+" - * + "
Opac1 = " "+String(Opac1)+" * x 1 "+String(Opac1)+" - * + "
Opac2 = " "+String(Opac2)+" * x 1 "+String(Opac2)+" - * + "
SOpac = " "+String(SOpac)+" * x 1 "+String(SOpac)+" - * + "
SOpac1 = " "+String(SOpac1)+" * x 1 "+String(SOpac1)+" - * + "
SOpac2 = " "+String(SOpac2)+" * x 1 "+String(SOpac2)+" - * + "
# Didee's '128-Centred Contrast Multiply' (HardLight):
HDL = " y 128 < x y * 128 / " + Opac1
HDL = HDL + " y 128 > 256 256 x - 128 y 128 - - abs * 128 / - " + Opac2
HDL = HDL + " x ? ? "
HDL2 = " y 128 < x y * 128 / " + SOpac1
HDL2 = HDL2 + " y 128 > 256 256 x - 128 y 128 - - abs * 128 / - " + SOpac2
HDL2 = HDL2 + " x ? ? "
#Didee's '128-centred contrast multiply' commuted (Overlay):
OVE = " x 128 < x y * 128 / " + Opac1
OVE = OVE + " x 128 > 256 256 x - 128 y 128 - - abs * 128 / - " + Opac2
OVE = OVE + " y ? ? "
OVE2 = " x 128 < x y * 128 / " + SOpac1
OVE2 = OVE2 + " x 128 > 256 256 x - 128 y 128 - - abs * 128 / - " + SOpac2
OVE2 = OVE2 + " y ? ? "
#Softlight: Based on Gimp and Delphi formula
SFL = " y 128 < 255 x - x y * 255 / * x 255 255 x - 255 y - * 255 / - * + 255 / " + Opac1
SFL = SFL + " y 128 > 255 x - x y * 255 / * x 255 255 x - 255 y - * 255 / - * + 255 / " + Opac2
SFL = SFL + " x ? ? "
SFL2 = " y 128 < 255 x - x y * 255 / * x 255 255 x - 255 y - * 255 / - * + 255 / " + SOPac1
SFL2 = SFL2 + " y 128 > 255 x - x y * 255 / * x 255 255 x - 255 y - * 255 / - * + 255 / " + SOPac2
SFL2 = SFL2 + " x ? ? "
#
Bl1Expr = (BLMode=="Blend") ? "y " + Opac
\ : (BLMode=="Multiply") ? "x y * 255 / " + Opac
\ : (BLMode=="Screen") ? "256 256 x - 256 y - * 256 / - " + Opac
\ : (BLMode=="Hardlight") ? HDL
\ : (BLMode=="Overlay") ? OVE
\ : (BLMode=="Softlight") ? SFL
\ : (BLMode=="Burn") ? "255 255 x - 256 * y 1 + / - " + Opac
\ : (BLMode=="Dodge") ? " x 256 * 256 y - / " + Opac
\ : (BLMode=="Glow") ? "x 255 == x 255 y y * 255 x - / min ? " + Opac
\ : (BLMode=="Reflect") ? "y 255 == y 255 x x * 255 y - / min ? " + Opac
\ : (BLMode=="Lighten") ? "x y > x y ? " + Opac
\ : (BLMode=="Darken") ? "x y < x y ? " + Opac
\ : (BLMode=="Additive") ? "x y + 255 > 255 x y + ? " + Opac
\ : (BLMode=="Subtractive") ? "x y + 256 - 0 < 0 x y + 256 - ? " + Opac
\ : (BLMode=="Difference") ? "x y - abs " + Opac
\ : Assert(false, "Invalid BLMode String")
#
Bl2Expr = (BLMode2=="Blend") ? "y " + SOpac
\ : (BLMode2=="Multiply") ? "x y * 255 / " + SOpac
\ : (BLMode2=="Screen") ? "256 256 x - 256 y - * 256 / - " + SOpac
\ : (BLMode2=="Hardlight") ? HDL2
\ : (BLMode2=="Overlay") ? OVE2
\ : (BLMode2=="Softlight") ? SFL2
\ : (BLMode2=="Burn") ? "255 255 x - 256 * y 1 + / - " + SOpac
\ : (BLMode2=="Dodge") ? "x 256 * 256 y - / " + SOpac
\ : (BLMode2=="Glow") ? "x 255 == x 255 y y * 255 x - / min ? " + SOpac
\ : (BLMode2=="Reflect") ? "y 255 == y 255 x x * 255 y - / min ? " + SOpac
\ : (BLMode2=="Lighten") ? "x y > x y ? " + SOpac
\ : (BLMode2=="Darken") ? "x y < x y ? " + SOpac
\ : (BLMode2=="Additive") ? "x y + 255 > 255 x y + ? " + SOpac
\ : (BLMode2=="Subtractive") ? "x y + 256 - 0 < 0 x y + 256 - ? " + SOpac
\ : (BLMode2=="Difference") ? "x y - abs " + SOpac
\ : Assert(false, "Invalid BLMode String")
#Apply blend operation:
BLOut = mt_lutxy(clp1,clp2, Bl1Expr, Y=LMode, U=CMode, V=CMode)
Output = (BL2 == False) ? BLOut : mt_lutxy(BLOut, BLOut, Bl2Expr, Y=LMode, U=CMode, V=CMode)
#Preview set-up:
Clp1s = Clp1.LanczosResize(640,360).Subtitle("Clp1 (Base)", size=14)
Clp2s = Clp2.LanczosResize(640,360).Subtitle("Clp2 (Top)", size=14)
Outs = Output.LanczosResize(640,360).Subtitle("Output", size=14)
Outgs = Output.Greyscale().LanczosResize(640,360).Subtitle("Output (Greyscale)", size=14)
Clpsv = StackVertical(Clp2s, Clp1s)
Outsv = StackVertical(Outs, Outgs)
Prev = StackHorizontal(Clpsv, Outsv)
#GSGrad view set-up:
GSGH = GSG.Histogram().Subtitle("Original")
GSGoH = Output.LanczosResize(640,360).Histogram().Subtitle("Blend operation applied")
GSGout = StackVertical(GSGH, GSGoH)
#Output
Final = (Preview == False && GSGrad == False) ? Output
\ : Preview ? Prev : GSGout
#
Return Final
}
Gavino
17th May 2010, 09:33
And taking, for example, 'Additive':
(x + y ) > 255 ? 255 : (( x + y) * "+String(Opac)+" ) + (x * (1 - "+String(Opac)+" ))
....seems right to me
But this way, if x+y >255, the opacity is ignored and 255 is returned directly instead of being blended with the original.
Reverse translation (mt_infix) of:
"x y + 255 > 255 x y + ? "+String(Opac)+" * x 1 "+String(Opac)+" - * + "
literally gives:
((((x + y) > 255 )) ? 255 : (x + y)) * "+String(Opac)+" ) + (x * (1 - "+String(Opac)+" )))
Which seems to be the same thing.
No, it's not the same. The difference is in the brackets I have highlighted in red.
Here the opacity is applied to the entire conditional expression, including the 255 part.
Gavino
17th May 2010, 09:37
So, I guess you are suggesting something like:
\ : (BLMode2=="Additive") ? "x y + 255 > 255 x y + ? " + SOpac
\ : (BLMode2=="Subtractive") ? "x y + 256 - 0 < 0 x y + 256 - ? " + SOpac
Exactly. Now it's much more obvious whether the opacity is being applied in the right place. And now Additive and Subtractive are correct.
WorBry
17th May 2010, 14:40
Yes, you are right. :rolleyes: Silly thing is, I did consider this implication with the Glow and Reflect modes and resolved it this way:
GLO = "x 255 == x " + Opac
GLO = GLO + " 255 y y * 255 x - / min " + Opac
GLO = GLO + "?"
Even so, for consistency, I've modified those also.
Significant changes in all, so above script is now Blend_MT_alpha3......Wow...Gasp!
As my old neurosurgery professor used to remark "What he lacks in accuracy he makes up for in enthusiasm" :p
WorBry
18th May 2010, 18:50
On further testing I find that the 'Overlay' expression is also not quite right. 'Overlay' is merely a commutation (reversal) of 'Hardlight', with respect to the layers. As such, it is sufficient to change only the 'reference' clip in the '128-Centred Contrast Multiply' (Hardlight) formula. Otherwise the half equations for < 128 > are not changed. This was not apparent with simple 'self-blends', but became obvious on further testing of 'double-blends' and non-identical overlays.
So, what was:
OVE = " x 128 < y x * 128 / " + Opac1
OVE = OVE + " x 128 > 256 256 y - 128 x 128 - - abs * 128 / - " + Opac2
OVE = OVE + "y ? ?"
Should be:
OVE = " x 128 < x y * 128 / " + Opac1
OVE = OVE + " x 128 > 256 256 x - 128 y 128 - - abs * 128 / - " + Opac2
OVE = OVE + "y ? ? "
Also, the terminal (128 anchor) in the Softlight formula should be x and not y.
Updated Blend_MT_alpha3 (in post# 42) accordingly.
Edit: Also changed the BLMode2 operation so that it uses the BLMode product as base clip, instead of Clp1 (i.e. is now a 'self-blend'). This is the more usual application in AE/PS/Gimp 'recipes' that involve multiple blends. Of course, the same can be acheived with a repeat pass of Blend_MT if desired.
WorBry
20th May 2010, 17:20
Now that I've got the 'luma' component of these blends sorted (I hope), I've been experimenting with some (maybe acheivable) chroma effects (I know...YUV chroma....not for the faint hearted...why not just do it in RGB?). To this end I want to create some (YV12) U and V reference spectra . Figured I could do this using a greyscale gradient and YtoUV, like so:
GSG = colorbars().converttoyv12().mt_lutspa(relative=true,expr="x 255 *").Greyscale().LanczosResize(640,640)
GSG2 = GSG.Reduceby2()
NGry2 = GSG.mt_lut("x 128 ", y=3).Reduceby2()
Uspec = YtoUV(GSG2, NGry2)
VSpec = YtoUV(NGry2, GSG2)
Return Interleave(USpec, VSpec)
But with both spectra, there is obviously some clipping/scale issue going on. Any idea how to resolve?
Gavino
20th May 2010, 18:17
But with both spectra, there is obviously some clipping/scale issue going on.
Why do you think it's wrong?
Looks OK to me, as you can see by adding Histogram("levels").
I also changed the code to the simpler
BlankClip(100, 640, 640, pixel_type="YV12")
USpec = mt_lutspa(uexpr="x 255 *", Y=-128, U=3, V=-128)
VSpec = mt_lutspa(vexpr="x 255 *", Y=-128, U=-128, V=3)
Return Interleave(USpec, VSpec)
and got identical results.
Didée
20th May 2010, 18:51
Of the various histogram-pictures that you have posted, each and every single one shows clipping. For evaluation, you probably want to add a final
ConvertToRGB32(matrix="PC.601")
before returning.
WorBry
20th May 2010, 19:15
Thanks for your replys.
With both methods, I'm still seeing the clipping/banding:
With Histogram 'level's:
http://www.mediafire.com/file/zzmtmizimhd/U_Spectrum.png
http://www.mediafire.com/file/q4ufzmm43vm/V_Spectrum.png
With final ConvertToRGB32(matrix="PC.601"):
http://www.mediafire.com/file/nvq0nwotjoz/U_Spectrum%20RGB32.png
http://www.mediafire.com/file/kdmzdzogdil/V_Spectrum%20RGB32.png
Didée
20th May 2010, 19:26
I'm not quite sure what you are seeing, to me those pics look good. Perhaps it is simply a display issue - what kind of display are you using? TFT panel? Possible to make a true screenshot with a camera?
WorBry
20th May 2010, 20:07
I'm seeing a lighter vertical band in the upper range (right side) of both spectra. Thought it might be some VDub quirk, but if you are not seeing it in the upoaded pics, maybe it is my display. I'm on my laptop. Unfortunately, I dont have a camera with me. Screen capture predictably shows the same. I'll play around with my display settings meanwhile. Cheers.
Edit: Here's a composite U & V spectrum (VSpec rotated left 45 deg). If you cant see the cross-banding on that one, it must surely be my display.
http://www.mediafire.com/file/mfzyrjztdjy/UV_Composite_Spectrum.png
Gavino
20th May 2010, 20:31
I think I understand what you're referring to now.
Taking the USpec for example, and looking at the RGB values in AvsP, the blue ranges from 0 to 255, but the variation is confined to a central band with bands of constant 0 on the left and constant 255 on the right. This is irrespective of adding ConvertToRGB32("PC.601") or not, although this probably changes the relative sizes of the three regions.
I think the explanation is that you have proper U and V gradients (as the histograms show), but on conversion to RGB you get clipping on the ranges of B (for U) or R (for V). This is because the range of U (and V) which produces valid RGB also depends on the value of Y, as can be seen from the formulae on this page (http://avisynth.org/mediawiki/Color_conversions).
WorBry
20th May 2010, 20:53
If that's the explanation - then what to do, or should I just take it that the spectra are valid in the context of YV12 processing, if not RGB display?
Edit: Cant find any adjustment to my laptop display that makes a difference. Tried coring (16 - 235) the source greyscale gradient, and the output U and V spectra (using Levels, which also clips the chroma) - same thing. On converting the U and V spectra back to luma, there's no clipping of the luma gradient. So I'm inclined to think you are right about the 'Y' factor, as it relates to RGB rendering of UV chroma.
That's why it also is important to avoid luma 'overflow' (rippling) in the blend formulae - luma aberration translates to colour aberration.
WorBry
24th May 2010, 06:56
@Gavino,
I replied to your post (new thread) on the subject of 'RGB invalid' YUV chroma:
http://forum.doom9.org/showthread.php?p=1402323#post1402323
vBulletin® v3.8.5, Copyright ©2000-2012, Jelsoft Enterprises Ltd.