Welcome to Doom9's Forum, THE in-place to be for everyone interested in DVD conversion. Before you start posting please read the forum rules. By posting to this forum you agree to abide by the rules. |
|
|
Thread Tools | Search this Thread | Display Modes |
12th April 2010, 16:02 | #1 | Link |
Registered User
Join Date: Jan 2004
Location: Here, there and everywhere
Posts: 1,197
|
Selective desaturation in YV12 for given luma range: possible?
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.
__________________
Nostalgia's not what it used to be Last edited by WorBry; 12th April 2010 at 16:10. |
12th April 2010, 18:21 | #2 | Link |
Registered User
Join Date: Apr 2002
Location: Germany
Posts: 5,389
|
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. Code:
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 Code:
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)
__________________
- We´re at the beginning of the end of mankind´s childhood - My little flickr gallery. (Yes indeed, I do have hobbies other than digital video!) |
12th April 2010, 19:47 | #3 | Link |
Registered User
Join Date: May 2009
Location: Hungary
Posts: 79
|
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/ |
12th April 2010, 20:56 | #4 | Link |
Registered User
Join Date: Apr 2002
Location: Germany
Posts: 5,389
|
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.
__________________
- We´re at the beginning of the end of mankind´s childhood - My little flickr gallery. (Yes indeed, I do have hobbies other than digital video!) |
12th April 2010, 21:57 | #5 | Link |
Registered User
Join Date: Jan 2004
Location: Here, there and everywhere
Posts: 1,197
|
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.
__________________
Nostalgia's not what it used to be |
14th April 2010, 04:53 | #6 | Link |
Registered User
Join Date: Jan 2004
Location: Here, there and everywhere
Posts: 1,197
|
@Didee,
OK, I got this far with the second approach: Code:
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) Code:
ExprI="x "+string(Low)+" > x "+string(High)+" < | y 128 - "+string(sat)+" * 128 + y ?" Any chance you could help me out with this? I am trying and learning, honest
__________________
Nostalgia's not what it used to be Last edited by WorBry; 14th April 2010 at 05:24. |
14th April 2010, 06:20 | #7 | Link |
ekTOMBE STUDIOS
Join Date: Dec 2005
Location: Cuba
Posts: 254
|
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.
__________________
So, it works or not??? |
14th April 2010, 08:52 | #8 | Link | |
Avisynth language lover
Join Date: Dec 2007
Location: Spain
Posts: 3,431
|
Quote:
But why change the order (except perhaps for clarity)? You could just replace the first 'y' by the new expression. |
|
14th April 2010, 13:02 | #9 | Link |
Registered User
Join Date: Apr 2002
Location: Germany
Posts: 5,389
|
Code:
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)
__________________
- We´re at the beginning of the end of mankind´s childhood - My little flickr gallery. (Yes indeed, I do have hobbies other than digital video!) |
14th April 2010, 13:13 | #10 | Link | |||
Registered User
Join Date: Jan 2004
Location: Here, there and everywhere
Posts: 1,197
|
Quote:
Quote:
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 ) . Thanks Didee. Now to apply it.....more food for thought. BTW: Quote:
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
__________________
Nostalgia's not what it used to be Last edited by WorBry; 14th April 2010 at 14:12. |
|||
14th April 2010, 17:31 | #13 | Link |
Registered User
Join Date: Jan 2004
Location: Here, there and everywhere
Posts: 1,197
|
Well, application of the roll-off gradient to the mt_merge approach is straightforward enough, even for me:
Code:
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) } Edit: Figured it. Function modified accordingly.
__________________
Nostalgia's not what it used to be Last edited by WorBry; 14th April 2010 at 19:12. |
14th April 2010, 18:49 | #14 | Link | |
Avisynth language lover
Join Date: Dec 2007
Location: Spain
Posts: 3,431
|
Quote:
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. |
|
14th April 2010, 19:11 | #15 | Link |
Registered User
Join Date: Jan 2004
Location: Here, there and everywhere
Posts: 1,197
|
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.
__________________
Nostalgia's not what it used to be Last edited by WorBry; 14th April 2010 at 19:18. |
16th April 2010, 03:48 | #16 | Link |
Registered User
Join Date: Jan 2004
Location: Here, there and everywhere
Posts: 1,197
|
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: Code:
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)
__________________
Nostalgia's not what it used to be Last edited by WorBry; 16th April 2010 at 03:59. |
16th April 2010, 08:36 | #17 | Link | |
Avisynth language lover
Join Date: Dec 2007
Location: Spain
Posts: 3,431
|
Quote:
Code:
# [0] <-white-> [p1] <-gradient-> [p2] <-black-> [p3] <-gradient-> [p4] <-white-> [255] # Exclusive However, if you still want to keep your meaning of the parameters, then Code:
gradhi = "x "+p3+" < 255 1 x "+p3+" - "+p4+" "+p3+" - / - * " # Not right !! Code:
gradhi = "x "+p3+" < 255 x "+p4+" - "+p3+" "+p4+" - / * " # 255*(x-p4)/(p3-p4) |
|
16th April 2010, 15:40 | #18 | Link | ||
Registered User
Join Date: Jan 2004
Location: Here, there and everywhere
Posts: 1,197
|
Quote:
Quote:
Code:
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) http://rapidshare.com/files/37657940..._Mask.jpg.html I always get that hard black/white boundary at the high set point.
__________________
Nostalgia's not what it used to be |
||
16th April 2010, 16:20 | #19 | Link | |
Avisynth language lover
Join Date: Dec 2007
Location: Spain
Posts: 3,431
|
Ah, yes, you also need to change the comparison to get the logic to work:
Code:
gradlo = "x "+p1+" <= 255 x "+p1+" - "+p2+" "+p1+" - / * " Quote:
|
|
16th April 2010, 17:33 | #20 | Link | ||
Registered User
Join Date: Jan 2004
Location: Here, there and everywhere
Posts: 1,197
|
Quote:
Quote:
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.
__________________
Nostalgia's not what it used to be Last edited by WorBry; 16th April 2010 at 17:49. |
||
Thread Tools | Search this Thread |
Display Modes | |
|
|