View Full Version : Selective desaturation in YV12 for given luma range: possible?
WorBry
12th April 2010, 16:02
I'm trying to find a method of desaturating a YV12 source (PAL DV or FFDShow-HuffYV12) for a defined luma range. Purpose: primarily to be able to selectively desaturate highlights, mid-tones, shadows etc.
I've tried every combination of Levels, MaskTools and Swap (UVtoY etc) I can think of, without success...chroma always gets scrambled (like my brain).
I am sure it can be done (more easily) in RGB, but, if at all possible, I'd like to stay in YV12, without color space conversion.
Seems that the key lies in the ability to apply transformations, for a given pixel, between luma(Y) and chroma (u,v) planes i.e. given transformation A on luma results in transformation B on chroma, whilst not affecting the delicate uv color information in the rest of the image.
Is this possible ?
Cheers.
Didée
12th April 2010, 18:21
The "easy-but-complicated" way is to create a luma mask, then merge-in a desaturated copy by that mask.
The "complicated-but-easy" way is to use mt_lutxy, and do the transformation directly.
source = whatever
pale = source.tweak(sat=0.5)
lmask = source.mt_lut("x 50 < x 100 > | 0 255 ?") # a primitive lut - affect only Y=[50,100], without roll-off)
result = mt_merge(source,pale,lmask,luma=true,Y=2,U=3,V=3) # without roll-off, "lmask.blur(1)" is probably more nice
source = whatever
source.reduceby2()
result = YtoUV(last,last).mt_lutxy(source,"x 50 < x 100 > | y y 128 - 2 / 128 + ?",Y=4,U=3,V=3)
Those are the black-or-white primitves - either leave a pixel unchanged, or desaturate it by [fixed_value]. Making a soft roll-off for the range boundaries requires only some refinement of the LUTs. Which is left "as an exercise for the reader". :p
julius666
12th April 2010, 19:47
You should try these filters:
Virtualdub filters:
http://fdump.narod.ru/rgb.htm
http://fdump.narod.ru/equalizer.htm
Avisnyth filters:
http://expsat.sourceforge.net/
Didée
12th April 2010, 20:56
The Vdub's work in RGB, don't they? The task was to avoid that YUV>RGB>YUV conversion ...
ExpSat does not have Luma<>Chroma linking.
WorBry
12th April 2010, 21:57
Thanks Didee.
Aha, so that's how you create a mt_lut mask for a defined luma range - I'd been trying to figure that out. I'd been making some progress, using a 'difference' approach (mt_makediff and mt_adddiff) with greyscaled mt_luts and blankclips, but I was sure there had to be a more direct solution.
Will experiment some more with your scripts.
Cheers.
WorBry
14th April 2010, 04:53
@Didee,
OK, I got this far with the second approach:
clp = last
#
Sat=1.0 # (Default) saturation. Desat < 1.0 > Increased sat
Sat= Sat <0.0 ? 0.0 : Sat
Low= 0 # (Default) lower 'chroma' range point
Low= Low <0.0 ? 0.0 : Low >255 ? 255 : Low
High= 255 # (Default) upper 'chroma' range point
High= High <0.0 ? 0.0 : High >255 ? 255 : High
#
clp.reduceby2()
#
Expr="x "+string(Low)+" < x "+string(High)+" > | y y 128 - "+string(sat)+" * 128 + ?"
result = YtoUV(last,last).mt_lutxy(clp,expr=Expr,Y=4,U=3,V=3)
Return(result)
But I cant, for the life of me, figure out how to do the soft roll off at the range boundaries. I imagine it involves extending the mt_lutxy expression line, via an 'otherwise' argument, to include a second expression that defines a progressive increase in saturation (from set SAT value to 1.0) over a suitable overshoot range...maybe (Low - 10) and (High + 10). The only way I can see being able to do that is by changing the first expression to:
ExprI="x "+string(Low)+" > x "+string(High)+" < | y 128 - "+string(sat)+" * 128 + y ?"
Which leaves an 'otherwise' argument open for a second string expression (replacing the terminal `otherwise y`), but that excludes the actual low and high luma range set points; > = seems to throw up an access violation in the reverse polish notation.
Any chance you could help me out with this? I am trying and learning, honest :rolleyes:
OvejaNegra
14th April 2010, 06:20
i think i have the same problem, more or less, i have a source with the highlights (or the whites) tinted with cyan.
I tryed with tweak and a color selection but it kills all the similar colors.
Using photshop and desaturating the highlights worked, and now im trying to reproduce that with avisynth.
i will try the Didée scripts to see what happens.
Gavino
14th April 2010, 08:52
... changing the first expression to:
ExprI="x "+string(Low)+" > x "+string(High)+" < | y 128 - "+string(sat)+" * 128 + y ?"
Which leaves an 'otherwise' argument open for a second string expression (replacing the terminal `otherwise y`), but that excludes the actual low and high luma range set points; > = seems to throw up an access violation in the reverse polish notation.
You need ">=", not "> =". Also, by negating the conditions, you need to change the "|" to "&".
But why change the order (except perhaps for clarity)? You could just replace the first 'y' by the new expression.
Didée
14th April 2010, 13:02
c = colorbars().converttoyv12()
c = c.mt_lutspa(relative=true,expr="x 255 *").greyscale()
# [0] <--black--> [p1] <--gradient--> [p2] <--white--> [p3] <--gradient--> [p4] <--black--> [255]
p1 = 40
p2 = 80
p3 = 120
p4 = 160
p1=string(p1) p2=string(p2) p3=string(p3) p4=string(p4)
blak = "x "+p1+" < x "+p4+" > | 0 "
wite = "x "+p2+" >= x "+p3+" <= & 255 "
gradlo = "x "+p2+" < 255 x "+p1+" - "+p2+" "+p1+" - / * "
gradhi = "x "+p3+" > 255 1 x "+p3+" - "+p4+" "+p3+" - / - * "
expr = blak + wite + gradlo + gradhi + "x ? ? ? ?"
c.mt_lut(expr).greyscale()
interleave(c,last)
WorBry
14th April 2010, 13:13
You need ">=", not "> =". Also, by negating the conditions, you need to change the "|" to "&".
Ah, yes you're right. Thanks. I thought operators needed to be separated by at least a space.
But why change the order (except perhaps for clarity)? You could just replace the first 'y' by the new expression.
Cos' then it gets too convoluted for my monochromatic mind, but I'll try that also.
Thankfully the mt_infix function (reverse polish to infix notation), that Manao added in MaskTools-v2.0a37, helps arithmetically desaturated numbskulls like me to decipher the work of others, but I sure wish there was more documentation available on MaskTools usage, by way of defined examples.
Edit: Ah, Didee's post bumped mine. See, I would never have thought of that approach. Was getting hung up on arithmetic progressions (radioactive decay curves, law of diminishing returns etc :p) . Thanks Didee. Now to apply it.....more food for thought.
BTW:
i think i have the same problem, more or less, i have a source with the highlights (or the whites) tinted with cyan.
I tryed with tweak and a color selection but it kills all the similar colors.
Using photshop and desaturating the highlights worked, and now im trying to reproduce that with avisynth.
i will try the Didée scripts to see what happens.
Yes, it works quite well in such situations. Adding the 'roll-off' refinement, should help to avoid chroma zoning at strong/full desaturation.
Actually, I can see more applications for this, once I've got the script down, one being a modification of the Tint function for greater control over the selective application of color tints.
http://avisynth.org/oldwiki/index.php?page=Tint
Gavino
14th April 2010, 13:49
I thought operators needed to be separated by at least a space.
They do - but "<=" is a single operator.
Writing it as "< =" makes it appear as two, confusing the hell out of the reverse polish interpreter.
WorBry
14th April 2010, 14:06
Got it.
WorBry
14th April 2010, 17:31
Well, application of the roll-off gradient to the mt_merge approach is straightforward enough, even for me:
Function SelSat (clip clp, float "Sat", int "Luma_Low", int "Low_OS",
\ int "Luma_High", int "High_OS")
{
# Allows selective control of saturation for a defined luma range.
# Merely assembled by WorBry from component functions kindly provided by Didee.
# Requires YV12 clp input and assumes full (pc) luma range 0-255.
# Requires MaskTools v2
#Defaults:
Sat = default(Sat, 1.0) # Desaturation 0.0 < 1.0 > Increased saturation
Luma_Low = default(Luma_Low, 0.0) # Lower luma range point
Luma_High = default(Luma_High, 255) # Upper luma range point
Low_OS = default(Low_OS, 0.0) # Lower over-shoot (roll-off) point for Luma_Low
High_OS = default(High_OS,255) # Upper over-shoot (roll-off) point for Luma_high
#
#Limits:
Sat = Sat <0.0 ? 0.0 : Sat
Luma_Low = Luma_Low <0.0 ? 0.0 : Luma_Low >255 ? 255 : Luma_Low
Low_OS = Low_OS <0.0 ? 0.0 : Low_OS >Luma_Low ? Luma_Low : Low_OS
Luma_High = Luma_High <0.0 ? 0.0 : Luma_High >255 ? 255 : Luma_High
High_OS = High_OS <Luma_High ? Luma_High : High_OS >255 ? 255 : High_OS
#
pale = clp.tweak(sat=Sat) # Saturation control clip.
#Create luma mask with low/high range points and roll-off gradient at boundaries:
# [0] <-black-> [p1] <-gradient-> [p2] <-white-> [p3] <-gradient-> [p4] <-black-> [255]
p1 = Low_OS
p2 = Luma_Low
p3 = Luma_High
p4 = High_OS
p1=string(p1) p2=string(p2) p3=string(p3) p4=string(p4)
blak = "x "+p1+" < x "+p4+" > | 0 "
wite = "x "+p2+" >= x "+p3+" <= & 255 "
gradlo = "x "+p2+" < 255 x "+p1+" - "+p2+" "+p1+" - / * "
gradhi = "x "+p3+" > 255 1 x "+p3+" - "+p4+" "+p3+" - / - * "
expr = blak + wite + gradlo + gradhi + "x ? ? ? ?"
lmask= clp.mt_lut(expr).greyscale()
#
# Apply luma mask:
result = mt_merge(clp,pale,lmask,luma=true,Y=2,U=3,V=3)
Return(result)
}
I guess the Low_OS and High_OS limit control should be refined to ensure that Low_OS is never higher than Luma_Low and High_OS is never lower than Luma_High. Not sure how to do that.
Edit: Figured it. Function modified accordingly.
Gavino
14th April 2010, 18:49
I guess the Low_OS and High_OS limit control should be refined to ensure that Low_OS is never higher than Luma_Low and High_OS is never lower than Luma_High. Not sure how to do that.
Use Assert (http://avisynth.org/mediawiki/Internal_functions/Control_functions), eg Assert(Low_OS <= Luma_Low, " ... ")
You could also use Assert to totally reject values outside of 0-255 instead of just clamping them.
BTW I think it would be more logical to have the parameters (apart from Sat) as ints, not floats, as luma values are discrete.
WorBry
14th April 2010, 19:11
Thanks. Yes, my oversight. Changed the floats to int.
Will come back on the 'Assert' way, when I've thought about it. Works, as is for now.
Cheers.
WorBry
16th April 2010, 03:48
Didee,
I thought it would be helpful to include an option for excluding the selected luma range e.g. if want to desaturate the shadows and highlights, but not the mid-tones.
Mere inversion of the luma mask (with mt_invert) works OK for the Luma_Low and Luma_High points, but of course the boundary roll-offs are then mis-placed.
So, I've tried to modify the mask mt_lut expressions instead:
c = colorbars().converttoyv12()
c = c.mt_lutspa(relative=true,expr="x 255 *").greyscale()
# [0] <-black-> [p1] <-gradient-> [p2] <-white-> [p3] <-gradient-> [p4] <-black-> [255] # Inclusive
# [0] <-white-> [p2] <-gradient-> [p1] <-black-> [p4] <-gradient-> [p3] <-white-> [255] # Exclusive
p1 = 100 # Low_OS
p2 = 60 # Luma_Low
p3 = 180 # Luma_High
p4 = 140 # High_OS
p1=string(p1) p2=string(p2) p3=string(p3) p4=string(p4)
#Inclusive:
#blak = "x "+p1+" < x "+p4+" > | 0 "
#wite = "x "+p2+" >= x "+p3+" <= & 255 "
#gradlo = "x "+p2+" < 255 x "+p1+" - "+p2+" "+p1+" - / * "
#gradhi = "x "+p3+" > 255 1 x "+p3+" - "+p4+" "+p3+" - / - * "
#expr = blak + wite + gradlo + gradhi + "x ? ? ? ?"
#Exclusive:
blak = "x "+p1+" > x "+p4+" < & 0 "
wite = "x "+p2+" <= x "+p3+" >= | 255 "
gradlo = "x "+p2+" > 255 x "+p1+" - "+p2+" "+p1+" - / * "
gradhi = "x "+p3+" < 255 1 x "+p3+" - "+p4+" "+p3+" - / - * " # Not right !!
expr = blak + wite + gradlo + gradhi + "x ? ? ? ?"
c.mt_lut(expr).greyscale()
interleave(c,last)
return(last)
The blak, wite and gradlo expressions seem to work OK, but gradhi I just can not get right. Would appreciate your help.
Gavino
16th April 2010, 08:36
Mere inversion of the luma mask (with mt_invert) works OK for the Luma_Low and Luma_High points, but of course the boundary roll-offs are then mis-placed.# [0] <-black-> [p1] <-gradient-> [p2] <-white-> [p3] <-gradient-> [p4] <-black-> [255] # Inclusive
# [0] <-white-> [p2] <-gradient-> [p1] <-black-> [p4] <-gradient-> [p3] <-white-> [255] # Exclusive
p1 = 100 # Low_OS
p2 = 60 # Luma_Low
p3 = 180 # Luma_High
p4 = 140 # High_OS
It seems more logical to me to keep the roll-offs outside the specified range, ie
# [0] <-white-> [p1] <-gradient-> [p2] <-black-> [p3] <-gradient-> [p4] <-white-> [255] # Exclusive
Then you could simply invert the previous mask.
However, if you still want to keep your meaning of the parameters, then
gradhi = "x "+p3+" < 255 1 x "+p3+" - "+p4+" "+p3+" - / - * " # Not right !!
should be
gradhi = "x "+p3+" < 255 x "+p4+" - "+p3+" "+p4+" - / * " # 255*(x-p4)/(p3-p4)
WorBry
16th April 2010, 15:40
It seems more logical to me to keep the roll-offs outside the specified range, ie
# [0] <-white-> [p1] <-gradient-> [p2] <-black-> [p3] <-gradient-> [p4] <-white-> [255] # Exclusive
Then you could simply invert the previous mask.
Yes, but in that case the over-shoot set points (OS_Low, OS_High) are within the 'selected' domains defined by the Luma_Low and Luma_High set points i.e. the roll-off's begin within the white's, when they should roll-off from the Luma_Low and Luma_High set points into the black.
However, if you still want to keep your meaning of the parameters, then
gradhi = "x "+p3+" < 255 1 x "+p3+" - "+p4+" "+p3+" - / - * " # Not right !!
should be
gradhi = "x "+p3+" < 255 x "+p4+" - "+p3+" "+p4+" - / * " # 255*(x-p4)/(p3-p4)
Tried that before, and it's not right either; unless, of course, I've got the other expressions wrong as well. Example, using the above set points:
c = colorbars().converttoyv12()
c = c.mt_lutspa(relative=true,expr="x 255 *").greyscale()
# [0] <--black--> [p1] <--gradient--> [p2] <--white--> [p3] <--gradient--> [p4] <--black--> [255] # Inclusive
# [0] <--white--> [p2] <--gradient--> [p1] <--black--> [p4] <--gradient--> [p3] <--white--> [255] # Exclusive
p1 = 100
p2 = 60
p3 = 180
p4 = 140
p1=string(p1) p2=string(p2) p3=string(p3) p4=string(p4)
#Exclusive:
blak = "x "+p1+" > x "+p4+" < & 0 "
wite = "x "+p2+" <= x "+p3+" >= | 255 "
gradlo = "x "+p2+" > 255 x "+p1+" - "+p2+" "+p1+" - / * "
gradhi = "x "+p3+" < 255 x "+p4+" - "+p3+" "+p4+" - / * " # 255*(x-p4)/(p3-p4)
expr = blak + wite + gradlo + gradhi + "x ? ? ? ?"
c.mt_lut(expr).greyscale()
return(last)
Result:
http://rapidshare.com/files/376579400/Test_GSGrad_Exclusive_Mask.jpg.html
I always get that hard black/white boundary at the high set point.
Gavino
16th April 2010, 16:20
Ah, yes, you also need to change the comparison to get the logic to work:
gradlo = "x "+p1+" <= 255 x "+p1+" - "+p2+" "+p1+" - / * "
Yes, but in that case the over-shoot set points (OS_Low, OS_High) are within the 'selected' domains defined by the Luma_Low and Luma_High set points i.e. the roll-off's begin within the white's, when they should roll-off from the Luma_Low and Luma_High set points into the black.
I still see that as more consistent. For one thing, it preserves the relationship OS_Low <= Luma_Low <= Luma_High <= OS_High. But of course, it's your function so you should specify it the way that makes sense to you. :)
WorBry
16th April 2010, 17:33
Ah, yes, you also need to change the comparison to get the logic to work:
gradlo = "x "+p1+" <= 255 x "+p1+" - "+p2+" "+p1+" - / * "
Yes, that looks right. Thanks.
I still see that as more consistent. For one thing, it preserves the relationship OS_Low <= Luma_Low <= Luma_High <= OS_High. But of course, it's your function so you should specify it the way that makes sense to you. :)
Well, for sure, in changing from 'Inclusive' to 'Exclusive' modes, the Low_OS and High_OS points, need to be moved accordingly, but I guess I can set up their defaults, at least, to account for this.
In practice though, I cant see anyone using this merely wanting to flip back and to between the modes; 'Inclusive' is for one purpose, 'Exclusive' for another. I guess it could be refined further to allow selection of multiple ranges, but the mt_lut computation for that would be way too zany for me.
Cheers.
WorBry
19th April 2010, 21:28
OK, if anyone is interested (:rolleyes:), I`ve updated the function:
- Since it uses Tweak, thought I might as well add Hue as well. Can therefore be used to create and off-set color tints, but I prefer to use other functions for that. Quite useful though for highlighting selected areas in Preview (see below)
- Accordingly, changed function name to SelSah.
- Added the Exclude mode option.
- Added a Preview option. Displays the original clip, output and two luma mask views, one with the original chroma and the other the `tweaked`chroma.
I`ve a suspicion that the script flow is a little cumbersome in parts (maybe around the Include-Exclude mode switches), but it does the job....and rather well. ;)
Function SelSah_v1 (clip clp, float "Sat", float "Hue", int "Luma_Low", int "Luma_High", int "Low_OS", int "High_OS",
\ String "Mode", bool "Preview")
{
# 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:
# 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 *
# Mode - Determines whether the selected luma range is "Included" or "Excluded".
# 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)
Mode = default(Mode, "Include")
Luma_Low = default(Luma_Low, 0.0)
Luma_High = default(Luma_High, 255)
Low_OS = (Mode=="Include") ? default(Low_OS, Luma_Low - 20)
\ : (Mode=="Exclude") ? default(Low_OS, Luma_Low + 20) : Low_OS
High_OS = (Mode=="Include") ? default(High_OS, Luma_High + 20)
\ : (Mode=="Exclude") ? default(High_OS, Luma_High - 20) : High_OS
Preview = default(Preview, False)
#
#Limits:
Sat = Sat <0.0 ? 0.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 = (Mode=="Include") && Low_OS <0.0 ? 0.0
\ : (Mode=="Include") && Low_OS >Luma_Low ? Luma_Low
\ : (Mode=="Exclude") && Low_OS <Luma_Low ? Luma_Low
\ : (Mode=="Exclude") && Low_OS > High_OS ? ((Luma_Low + Luma_High)/2)
\ : Low_OS
High_OS = (Mode=="Include") && High_OS <Luma_High ? Luma_High
\ : (Mode=="Include") && High_OS >255 ? 255
\ : (Mode=="Exclude") && High_OS >Luma_High ? Luma_High
\ : (Mode=="Exclude") && High_OS < Low_OS ? ((Luma_Low + Luma_High + 2)/2)
\ : High_OS
#
#Create Saturation and Hue control clip:
twclp = clp.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 mode" 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 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 = (Mode=="Include") ? twclp.mt_lut(expr=exprI)
\ : (Mode=="Exclude") ? twclp.mt_lut(expr=ExprE) : lmask
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 = (Mode=="Include") ? clp.mt_lut(expr=exprI)
\ : (Mode=="Exclude") ? clp.mt_lut(expr=ExprE): lmcc
lmccs = lmcc.Subtitle("Luma Mask + Original Chroma").LanczosResize(640,360)
#
Clpout = StackVertical(clps,outs)
Masks = StackVertical(lmccs, lmcts)
Prev = StackHorizontal(Clpout,Masks)
Final = (Preview == False) ? Output
\ : (Preview == True ) ? Prev
\ : Final
#
Return(Final)
}
Gavino
20th April 2010, 00:32
Good job, WorBry.
I`ve a suspicion that the script flow is a little cumbersome in parts (maybe around the Include-Exclude mode switches)
That could be simplified a bit if you changed the mode from string into a boolean ('exclude', say). Then instead of, for example
lmask = (Mode=="Include") ? twclp.mt_lut(expr=exprI)
\ : (Mode=="Exclude") ? twclp.mt_lut(expr=ExprE) : lmask
you could just write
lmask = twclp.mt_lut(expr = (exclude ? ExprE : ExprI))
Final = (Preview == False) ? Output
\ : (Preview == True ) ? Prev
\ : Final
The third case is unnecessary as a boolean can only ever be true or false.
Final = Preview ? Prev : Output
WorBry
20th April 2010, 03:22
Thanks Gavino. I thought there might be some things that could be trimmed down. This is a good learning exercise for me. Will look at incorporating your suggestions. Cheers.
Edit: Changes incorporated:
Function SelSah_v1 (clip clp, float "Sat", float "Hue", int "Luma_Low", int "Luma_High", int "Low_OS", int "High_OS",
\ bool "Exclude", bool "Preview")
{
# 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:
# 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)
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 = clp.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 ? clp.mt_lut(expr=ExprE) : clp.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)
}
WorBry
28th April 2010, 00:05
Thought I would apply the 'luma range' approach to Actionman133's Tint function
http://avisynth.org/oldwiki/index.php?page=Tint
which I had in any case modified to use MaskTools v2.
Here it is......Stint :p:
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 designed to favour 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.
#
# Defaults:
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)
}
The usage is quite self explantory from the description and parameter definitions.
Any comments and suggestions for improvement are welcome.
WorBry
5th June 2010, 02:46
Updated SelSah to incorporate the saturation and hue range control parameters (minSat, MaxSat, startHue, endHue, interp) introduced in Tweak v2.58. Reason:
http://forum.doom9.org/showthread.php?p=1405480#post1405480
Function SelSah_v3 (clip clp, float "Sat", float "Hue", int "Luma_Low", int "Luma_High", int "Low_OS", int "High_OS",
\ Float "minSat", float "maxSat", float "startHue", float "endHue", float "interp", clip "clp2",
\ bool "Exclude", bool "Preview")
{
# 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. Tweak should be v2.58 or higher.
#
# 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. Desaturation 0.0 =< 1.0 > Increased saturation. See Tweak 2.58 doc
# Hue - Hue. Range -180 to +180. Default 0.0. See Tweak 2.58 doc
# startHue - See Tweak 2.58 doc for details. Range 0 - <360. Default 0
# endHue - See Tweak 2.58 doc for details. Range >0 - 360. Default 360
# minSat - See Tweak 2.58 doc for details. Range 0 - <150 ; must be < MaxSat. Default 0
# maxSat - See Tweak 2.58 doc for details. Range >0 -150. Default 150
# interp - See Tweak 2.58 doc for details. Range 0 - 32. Default 16
# Luma_Low - Lower luma range point. Range 0 - 255* Default 0
# Luma_High - Upper luma range point. Range 0 - 255* Default 255
# Low_OS - Lower over-shoot point. Provides soft roll-off at lower range boundary. Range 0 - 255*
# Default is Luma_Low+20 for normal (inclusive) range mode and Luma_low-20 for Exclude mode.
# High_OS - Upper over-shoot point. Provides soft roll-off at upper range boundary. Range 0 - 255*
# Default is Luma_High-20 for normal (inclusive) mode and Luma_High+20 for Exclude mode.
# Exclude True/False. True excludes the range selected by the luma set points. False (default) includes the selected range.
# Preview - True/False. True gives preview of input clip, output and luma masks with original and output chroma.
# Default is false
#
# *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)
startHue = default(startHue, 0)
endHue = default(endHue, 360)
minSat = default(minSat, 0)
maxSat = default(maxSat, 150)
interp = default(interp, 16)
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
#
#Limits for startHue, endHue, minSat and maxSat set by Tweak
#
#Create Saturation and Hue control clip:
twclp = clp2.tweak(sat=Sat, hue=Hue, startHue=startHue, endHue=endHue,
\ minSat=minSat, maxSat=maxSat, interp=interp, 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)
}
bxyhxyh
22nd October 2013, 18:05
I did little tweak, added contrast
It is intended to use with MDegrain which oversmooths dark area.
Function SelSah_v3_mod (clip clp, float "Cont", float "Sat", float "Hue", int "Luma_Low", int "Luma_High", int "Low_OS", int "High_OS",
\ Float "minSat", float "maxSat", float "startHue", float "endHue", float "interp", clip "clp2",
\ bool "Exclude", bool "Preview")
{
# Extends Tweak to allow selective control of saturation, hue and contrast 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. Tweak should be v2.58 or higher.
#
# 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.
# Cont - Contrast. Decreased contrast 0.0 <= 1.0 > Increased contrast. See Tweak 2.58 doc
# Sat - Saturation. Desaturation 0.0 =< 1.0 > Increased saturation. See Tweak 2.58 doc
# Hue - Hue. Range -180 to +180. Default 0.0. See Tweak 2.58 doc
# startHue - See Tweak 2.58 doc for details. Range 0 - <360. Default 0
# endHue - See Tweak 2.58 doc for details. Range >0 - 360. Default 360
# minSat - See Tweak 2.58 doc for details. Range 0 - <150 ; must be < MaxSat. Default 0
# maxSat - See Tweak 2.58 doc for details. Range >0 -150. Default 150
# interp - See Tweak 2.58 doc for details. Range 0 - 32. Default 16
# Luma_Low - Lower luma range point. Range 0 - 255* Default 0
# Luma_High - Upper luma range point. Range 0 - 255* Default 255
# Low_OS - Lower over-shoot point. Provides soft roll-off at lower range boundary. Range 0 - 255*
# Default is Luma_Low+20 for normal (inclusive) range mode and Luma_low-20 for Exclude mode.
# High_OS - Upper over-shoot point. Provides soft roll-off at upper range boundary. Range 0 - 255*
# Default is Luma_High-20 for normal (inclusive) mode and Luma_High+20 for Exclude mode.
# Exclude True/False. True excludes the range selected by the luma set points. False (default) includes the selected range.
# Preview - True/False. True gives preview of input clip, output and luma masks with original and output chroma.
# Default is false
#
# *Note: the luma set points are limited with respect to each other to ensure correct orientation and avoid overlap.
#
#Defaults:
Cont = default(Cont, 1.0)
Sat = default(Sat, 1.0)
Hue = default(Hue, 0.0)
startHue = default(startHue, 0)
endHue = default(endHue, 360)
minSat = default(minSat, 0)
maxSat = default(maxSat, 150)
interp = default(interp, 16)
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:
Cont = Cont < 0.0 ? 0.0 : Cont > 10.0 ? 10.0 : Cont
Sat = Sat <0.0 ? 0.0 : Sat > 10.0 ? 10.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
#
#Limits for startHue, endHue, minSat and maxSat set by Tweak
#
#Create Saturation and Hue control clip.
twclp = clp2.tweak(cont=Cont, sat=Sat, hue=Hue, startHue=startHue, endHue=endHue, minSat=minSat, maxSat=maxSat, interp=interp, 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=3,U=3,V=3)
#
#PREVIEW set-up
clps = Preview == false ? nop : clp.LanczosResize(640,360).Subtitle("Original Clip")
outs = Preview == false ? nop : output.LanczosResize(640,360).Subtitle("Final Output")
#Luma Mask with chroma from tweaked clp
lmcts = Preview == false ? nop : lmask.LanczosResize(640,360).Subtitle("Luma mask + Tweaked Chroma")
#Luma Mask with chroma from original clp
lmccs = Preview == false ? nop : Exclude ? clp2.mt_lut(expr=ExprE).LanczosResize(640,360).Subtitle("Luma Mask + Original Chroma")
\ : clp2.mt_lut(expr=exprI).LanczosResize(640,360).Subtitle("Luma Mask + Original Chroma")
#Create composite view
Prev = Preview == false ? nop : StackHorizontal(StackVertical(clps,outs), StackVertical(lmccs, lmcts))
#
return Preview == false ? Output : Prev
}
I haven't tested it on different sources, different settings or different filters. But it was very effective on this case.
Source
https://dl.dropboxusercontent.com/u/58215671/source.png
Normal MDegrain3 thsad=150
https://dl.dropboxusercontent.com/u/58215671/150%20normal%20mdegrain3.png
Tweak(cont=2.3).RemoveGrain(11) as prefilter and MDegrain3 thsad=150
https://dl.dropboxusercontent.com/u/58215671/150%20cont2.3%20mdegrain3.png
Selsah_v3_mod(luma_low=0, luma_high=80, cont=2.3).RemoveGrain(11) as prefilter and MDegrain3 150
https://dl.dropboxusercontent.com/u/58215671/150%20selsah%20mdegrain3.png
WorBry
23rd October 2013, 19:08
Glad you found use for it. I haven't done any 'filmic look' processing for a while - which was largely what it was created for.
vBulletin® v3.8.11, Copyright ©2000-2025, vBulletin Solutions Inc.