Log in

View Full Version : SmoothAdjust [v3.20 - Update 2015/11/15]


Pages : 1 2 3 4 5 [6] 7 8 9 10

Dogway
30th June 2011, 08:52
It would be cool if you added lsb_in feature for inputting 16bits stacked clips. Besides correcting chroma in debug, in case it is a bug.

edit: Also there seems to be a bug in UV planes with smoothcurve16. It is less red than default smoothcurve. Maybe it is related to dither, who knows...

LaTo
3rd July 2011, 15:18
It would be cool if you added lsb_in feature for inputting 16bits stacked clips. Besides correcting chroma in debug, in case it is a bug.

edit: Also there seems to be a bug in UV planes with smoothcurve16. It is less red than default smoothcurve. Maybe it is related to dither, who knows...
Can you post the curve that you used? I will try to reproduce the problem...

Dogway
3rd July 2011, 16:57
#~ SmoothCurve(\
#~ ycurve="0-9;16-30;128-128;233-220;255-255",\
#~ ucurve="0-16;255-240",\
#~ vcurve="0-16;255-240",\
#~ interp=50,smooth=10,show=false,limiter=false)

SmoothCurve16(\
ycurve="0-2304;4096-7680;32768-32768;59648-56320;65535-65535",\
ucurve="0-4096;65535-61440",\
vcurve="0-4096;65535-61440",\
interp=50,smooth=10,show=false,limiter=false)

LaTo
3rd July 2011, 17:10
#~ SmoothCurve(\
#~ ycurve="0-9;16-30;128-128;233-220;255-255",\
#~ ucurve="0-16;255-240",\
#~ vcurve="0-16;255-240",\
#~ interp=50,smooth=10,show=false,limiter=false)

SmoothCurve16(\
ycurve="0-2304;4096-7680;32768-32768;59648-56320;65535-65535",\
ucurve="0-4096;65535-61440",\
vcurve="0-4096;65535-61440",\
interp=50,smooth=10,show=false,limiter=false)

255 in 16 bit is 65280, so your conversion will never be the same as 8 bit.

If you want exactly the same, add 65280-65280 before 65535-65535.
It works?

PS: Why "Besides correcting chroma in debug, in case it is a bug."? What is the problem?

Dogway
3rd July 2011, 17:24
Im just doing what the readme suggests:
For example: SmoothCurve("0-255;128-128;255-0") --> SmoothCurve16("0-65535;32768-32768;65535-0")

If I change it to 65280 I get an error: *curve have not a correct synthax!

The debug graph for UV planes is different from yours.
http://i212.photobucket.com/albums/cc35/Dogway/Misc/239.png


yours (http://latoninf.free.fr/d9/SA/compare/NEW.png)

LaTo
3rd July 2011, 18:29
Im just doing what the readme suggests:
For example: SmoothCurve("0-255;128-128;255-0") --> SmoothCurve16("0-65535;32768-32768;65535-0")

If I change it to 65280 I get an error: *curve have not a correct synthax!
Try:
SmoothCurve16(\
ycurve="0-2304;4096-7680;32768-32768;59648-56320;65280-65280;65535-65535",\
ucurve="0-4096;65535-61440",\
vcurve="0-4096;65535-61440",\
interp=50,smooth=10,show=false,limiter=false)


The debug graph for UV planes is different from yours.
http://i212.photobucket.com/albums/cc35/Dogway/Misc/239.png


yours (http://latoninf.free.fr/d9/SA/compare/NEW.png)
It's the debug histogram of avisynth 2.6.x

Dogway
4th July 2011, 02:02
Thank you, but result is the same (in other sources too). Does it work with you?

edit: smoothcurve16 doesn't have any benefit over default smoothcurve, right (quality wise)? I realised I dont need it since after that Im processing in 8b. In any case it's still worth a look on why that happens.

LaTo
4th July 2011, 12:20
Thank you, but result is the same (in other sources too). Does it work with you?

Oups, sorry... Try:
SmoothCurve16(\
ycurve="0-2304;4096-7680;32768-32768;59648-56320;65280-65280;65535-65280",\
ucurve="0-4096;65280-61440;65535-61440",\
vcurve="0-4096;65280-61440;65535-61440",\
interp=50,smooth=10,show=false,limiter=false)

edit: smoothcurve16 doesn't have any benefit over default smoothcurve, right (quality wise)? I realised I dont need it since after that Im processing in 8b. In any case it's still worth a look on why that happens.
Yes, it has no benefit if your processing chain is 8bits.

Dogway
4th July 2011, 15:06
Oups, sorry... Try:
Are you theorizing, or are you getting instead right results? It doesn't work for me either.
You know the insights of SmoothAdjust so you'll probably want to start matching smoothcurve internal dither algorithm with Dither's one, and start from there for testing, and discard either SmoothAdjust or Dither. Although I have just tested a simple resize with Dither vs Spline36 and chroma is alright, so it doesn't look like its fault. (It could be the way it "records"/deals chroma to LSB, cretindesalpes should have more to say on this...)

By the way, overriden parameter in smoothcurve16 are.... "dither" only? Maybe it would be a good idea to delete that option.

LaTo
4th July 2011, 17:53
Are you theorizing, or are you getting instead right results? It doesn't work for me either.
It *should* work, but I don't use 16 bits... So I can't verify.


You know the insights of SmoothAdjust so you'll probably want to start matching smoothcurve internal dither algorithm with Dither's one, and start from there for testing, and discard either SmoothAdjust or Dither. Although I have just tested a simple resize with Dither vs Spline36 and chroma is alright, so it doesn't look like its fault.
I haven't checked Dither's code because I don't care (*joke*) :p
Seriously, I don't think that the problem is the dithering.
(Have you tried with "dither=-1"?)


(It could be the way it "records"/deals chroma to LSB, cretindesalpes should have more to say on this...)
Maybe, in SmoothAdjust I use that:
msb[x] = (BYTE)( smooth[x]>>8 );
lsb[x] = (BYTE)( smooth[x] );
It should be correct...


By the way, overriden parameter in smoothcurve16 are.... "dither" only? Maybe it would be a good idea to delete that option.
:confused:

Dogway
4th July 2011, 18:15
Sorry sorry, seems that the last example did work. I don't understand still why but its ok. :p

If I use ditherpost (mandatory) after smoothcurve16 or at the end of the 16b chain for dithering, there's no point on having the "dither" parameter in smoothcurve16, just wondering

LaTo
4th July 2011, 18:32
Sorry sorry, seems that the last example did work. I don't understand still why but its ok. :p
No problem, it's great that everything works :)

If I use ditherpost (mandatory) after smoothcurve16, there's no point on having the "dither" parameter in smoothcurve16, just wondering
Yes, you can disable it (unless you have a full 16bits chain with a 16bits display ^^)

LaTo
4th July 2011, 18:41
v2.00 beta6:
added SmoothCustom

Todo-list:
- improve debug view
- fix all bugs
Download here: > SmoothAdjust v2.00 beta6 < (http://latoninf.free.fr/d9/SA/SmoothAdjust-v2.00beta6.zip)


SmoothCustom is considered "alpha", I can't test everything alone.
So if some people are motivated, here is what to do:
- find expressions that crash
- find expressions that produce a syntax error
- find expressions that output a false result
- find expressions that work but which should not
:thanks:


PS: All informations about SmoothCustom are in the readme.

LaTo
14th July 2011, 15:03
v2.00 beta7:
improved debug view
tweaked smoothing

Todo-list:
- fix all bugs
Download here: > SmoothAdjust v2.00 beta7 < (http://latoninf.free.fr/d9/SA/SmoothAdjust-v2.00beta7.zip)

:devil:

leeperry
14th July 2011, 15:35
:thanks: for the new build!

Dogway
14th July 2011, 20:17
Thanks LaTo!
Is it possible to implement lsb_in, for reading stacked 16b?
I normally put color correction at the end of the chain, that's why

LaTo
14th July 2011, 20:27
Thanks LaTo!
Is it possible to implement lsb_in, for reading stacked 16b?
I normally put color correction at the end of the chain, that's why

Yes it's already implemented...
Simply use SmoothLevels16(), SmoothTweak16(), SmoothCurve16() and SmoothCustom16().
;)

Dogway
14th July 2011, 20:34
Yay! Thanks you \(^o^)/

jmac698
27th July 2011, 20:23
Hey, this is great, I'll have to test it. Can you add avg and std and atan2 to the functions in smoothcustom?
Even better, correlation :)

Dogway
29th July 2011, 04:11
LaTo, since long Ive struggling trying to convert PC values to TV values. Normally this is a workflow:
SmoothLevels(preset="pc2tv",smooth=10,limiter=0)

sat=0.0
Cb=0.0
Cr=-0.0
SmoothCurve(\
ycurve="0-0;16-16;30-32;60-50;116-95;190-150;228-208;236-236;255-255",\
ucurve="0-"+string(128*(-sat))+";"+string(int(128-(128*Cb)))+"-"+string(128+(128*Cb))+";255-"+string(255-128*(-sat))+" ",\
vcurve="0-"+string(128*(-sat))+";"+string(int(128-(128*Cr)))+"-"+string(128+(128*Cr))+";255-"+string(255-128*(-sat))+" ",\
smooth=10,show=true,limiter=false)
Is it possible to do everything in one step inside smoothcurve? What operations should I do to convert the ycurve values to PClevels?

I also observed that the first value must be an integer in order to work...

Another issue is the graphs in the debug view, they change position depending on input values length. It makes it hard to compare 2 different graphs to check the changes.

Thanks for all the hard work.

atra dies
3rd August 2011, 08:26
Is it wrong to use smoothlevels16 on linear y values?

Blacks wash out much earlier on than in ylevelsg, even before gamma=2 whilst ylevels can go up to 9 with this dark movie. I am trying to replicate ylevels with chroma=0.

smoothlevels16(input_low=0,gamma=1.2,input_high=65535,output_low=0,output_high=65535,chroma=0,lmode=0 or 3,ecurve=1,dither=-1,smooth=0,limiter=0)

LaTo
4th August 2011, 09:23
Hey, this is great, I'll have to test it. Can you add avg and std and atan2 to the functions in smoothcustom?
Even better, correlation :)
Yes I will add some functions in the next version.
No problem/bug/crash in SmoothCustom?

I also observed that the first value must be an integer in order to work...
ex: SmoothCurve("a-b;c-d;e-f")
In this expression "a", "c" and "e" must be an integer.


Another issue is the graphs in the debug view, they change position depending on input values length. It makes it hard to compare 2 different graphs to check the changes.
I will add a parameter "to fix" the graph position.


Is it wrong to use smoothlevels16 on linear y values?

Blacks wash out much earlier on than in ylevelsg, even before gamma=2 whilst ylevels can go up to 9 with this dark movie. I am trying to replicate ylevels with chroma=0.
It's because ylevelsS or ylevelsG doesn't follow a linear conversion.
Simply use Lmode=1 or 2 in SmoothLevels for the same behaviour.
On top of that you can add protect=16 (or protect=4096 for 16bits) which protects the black parts.

Dogway
4th August 2011, 11:31
Thanks LaTo, by chance wouldn't you know what operations requires converting one value from PC levels to TV levels?

for example in PC levels I want to convert the value of 30 to 50 ("30-50"), what is the equivalent in TV levels?

SmoothCurve(\
ycurve="0-0;30-50;255-255",\
smooth=10,show=true,limiter=false)


SmoothCurve(\
ycurve="0-16;30-x;255-235",\
smooth=10,show=true,limiter=false)

atra dies
5th August 2011, 02:17
It's because ylevelsS or ylevelsG doesn't follow a linear conversion.
Simply use Lmode=1 or 2 in SmoothLevels for the same behaviour.
On top of that you can add protect=16 (or protect=4096 for 16bits) which protects the black parts.

Well I tried to replicate that with version beta 5, lmode3 (3 = limit conversion on bright areas (apply conversion @100% at luma=0 & @0% at luma=255)), ecurve 1, and chroma=0 which is why I found the result surprising. Should they have behaved similarly? I didn't want to protect anything cause I added borders later to avoid brightening them. Or is ylevelsg supposed to limit on the dark, not the bright? (going to check it now)

Edit: I think ylevelsg does something similar to lmode 1 with a low ecenter thereby protecting the blacks. Would be nice to know the exact center of the curve.

leeperry
21st August 2011, 02:08
hi LaTo, is there anything I'm overlooking here please?

http://thumbnails50.imagebam.com/14598/25a218145975896.jpg (http://www.imagebam.com/image/25a218145975896) http://thumbnails28.imagebam.com/14598/ba61ed145975893.jpg (http://www.imagebam.com/image/ba61ed145975893) http://thumbnails43.imagebam.com/14598/d20570145975894.jpg (http://www.imagebam.com/image/d20570145975894)

It works fine w/ 1.33 DAR, but w/ 1.5(untouched SAR) I get this error msg :(

LaTo
21st August 2011, 09:28
hi LaTo, is there anything I'm overlooking here please?

http://thumbnails50.imagebam.com/14598/25a218145975896.jpg (http://www.imagebam.com/image/25a218145975896) http://thumbnails28.imagebam.com/14598/ba61ed145975893.jpg (http://www.imagebam.com/image/ba61ed145975893) http://thumbnails43.imagebam.com/14598/d20570145975894.jpg (http://www.imagebam.com/image/d20570145975894)

It works fine w/ 1.33 DAR, but w/ 1.5(untouched SAR) I get this error msg :(

Maybe because you use multithreading, height[480]/threads[4] = 120

leeperry
21st August 2011, 13:52
Ah, you nailed it! Any chance of allowing 120 as a minimum instead of 128? http://forum-images.hardware.fr/images/perso/zytradance.gif

LaTo
2nd September 2011, 19:08
v2.00 beta8:
added "scale" parameter for debug view
relaxed minimum frame size (128 for AVX & 64 for SSE2)
added sinh, cosh & tanh in SmoothCustom

Todo-list:
- fix all bugs
Download here: > SmoothAdjust v2.00 beta8 < (http://latoninf.free.fr/d9/SA/SmoothAdjust-v2.00beta8.zip)

:)

leeperry
2nd September 2011, 20:49
I was eagerly hoping for a new build w/ more better colors, but this will do :D

:thanks: for the new build, and especially the lower minimum frame size requirement.

jmac698
2nd September 2011, 22:45
added sinh, cosh & tanh in SmoothCustom

Great, but could you add avg, std, atan2 at some point, as per my original request :)
avg(a,b)=(a+b)/2, std=standard deviation of the pixels, atan2(a,b) like atn

LaTo
3rd September 2011, 11:01
Great, but could you add avg, std, atan2 at some point, as per my original request :)
avg(a,b)=(a+b)/2, std=standard deviation of the pixels, atan2(a,b) like atn

The parser doesn't recognize multiple arguments in function, so I can't add these for now.

Dogway
5th September 2011, 11:19
Thanks LaTo, by chance wouldn't you know what operations requires converting one value from PC levels to TV levels?

I answer myself, well cretindesalpes gave me the TVtpPC formula, from there I could know the PCtoTV range conversion formula.

TV to PC: ((x-16)/219)*255
PC to TV: ((x*219)/255)+16

So I guess this line (in smoothcontrast):
"0-0;16-16;74-"+string(74-55*contrast)+";128-128;185-"+string(185+55*contrast)+";235-235;255-255"
should be:
"0-0;16-16;71-"+string(71-55*contrast)+";126-126;181-"+string(181+55*contrast)+";236-236;255-255"
?

Does chroma stays the same in TV range?:
"0-"+string(128*(-sat))+";255-"+string(255-128*(-sat))+" "

Gavino
5th September 2011, 11:24
Does chroma stays the same in TV range?
Chroma in TV range is 16-240, so the conversion formulas are:
TV to PC: ((x-16)/224)*255
PC to TV: ((x*224)/255)+16

Dogway
5th September 2011, 11:27
Thanks Gavino. I know that chroma changes when range conversion. Im refering to the contrast formula in TV range as opposed to the same formula for PC range. Should the above formula be good to go with TV range as well? Im guessing so, but just wanted some confirmation on the codes... I can't match smoothtweak saturation with that code in smoothcontrast

Dogway
17th September 2011, 12:07
Ok, since nobody confirmed me anything it's time to disclose the work so you can debug and correct me whatever I did wrong.


function SmoothContrast(clip c, float "contrast", float "sat", string "range", string "local", int "median", bool "lsb",\
int "smooth",int "dither", int "interp", bool "limit", bool "show"){

contrast = default(contrast, 0.0) # [-1.0 to +1.0]
sat = default(sat, contrast) # [-1.0 to +1.0] Saturation. By default contrast is also applied to the UV planes.
range = default(range, "TV") # TV or PC. Luma range of your source, probably TV as SmoothAdjust only accepts yuv
smooth = default(smooth, 10) # same as SmoothAdjust; strength of the smoothing
dither = default(dither, 50) # same as SmoothAdjust; fuzziness of the dither. -1 = off
interp = default(interp, 100) # same as SmoothAdjust; curve interpolation curve.
lsb_in = default(lsb, false) # 16 bits I/O. For using in a 16 bits stacked MSB+LSB chain
limit = default(limit, false) # Clamp the output to 16-235 for luma & 16-240 for chroma (TVRange)
show = default(show, false) # Show debug. Doesn't work when using "local" mode.

#local # "local" allows to focus only on the gamma/saturation of the dark or bright parts of the image.
# \ Use "dark" to process dark areas, or "bright" for bright areas.

m = Defined(median) ? (lsb_in ? median*256 : median) : (range=="tv" ? (lsb_in ? 32256 : 126) : (lsb_in ? 32768 : 128))
mf = m*1.000
# median defines the luma center point of your source to process contrast from,
# \ or the luma threshold where you want to process gamma/sat from in the "local" mode.

loc = Defined(local) ? true : false
loc ? Assert(!show, "Debug view cannot be shown in Local mode") : nop



# Variables

b=range=="tv" ? (lsb_in ? 4096 : 16) : 0
w=range=="tv" ? (lsb_in ? 60416 : 236) : (lsb_in ? 65535 : 256)
Cb=(b+mf)/2
Cw=(w+mf)/2
Chb=(mf-b)/2
Chw=(w-mf)/2
Cbi=int(Cb)
Cwi=int(Cw)


# Strings

no = lsb_in ? nop : "0-0;255-255"
Ytv = lsb_in ? nop : "0-0;16-16;"+string(Cbi)+"-"+string(Cb-Chb*contrast)+";"+string(m)+"-"+string(m)+";"+string(Cwi)+"-"+string(Cw+Chw*contrast)+";235-235;255-255"
Ypc = lsb_in ? nop : "0-0;"+string(Cbi)+"-"+string(Cb-Chb*contrast)+";"+string(m)+"-"+string(m)+";"+string(Cwi)+"-"+string(Cw+Chw*contrast)+";255-255"
UV = lsb_in ? nop : "0-"+string(128*(-sat))+";255-"+string(255-128*(-sat))+" "

no16 = !lsb_in ? nop : "0-0;65280-65280;65535-65280"
Ytv16 = !lsb_in ? nop : "0-0;4096-4096;"+string(Cbi)+"-"+string(Cb-Chb*contrast)+";"+string(m)+"-"+string(m)+";"+string(Cwi)+"-"+string(Cw+Chw*contrast)+";60160-60160;65280-65280;65535-65280"
Ypc16 = !lsb_in ? nop : "0-0;"+string(Cbi)+"-"+string(Cb-Chb*contrast)+";"+string(m)+"-"+string(m)+";"+string(Cwi)+"-"+string(Cw+Chw*contrast)+";65280-65280;65535-65280"
UV16 = !lsb_in ? nop : "0-"+string(32768*(-sat))+";65280-"+string(65280-32768*(-sat))+";65535-"+string(65280-32768*(-sat))+" "

Chroma = sat == 0.0 ? false : true
UV = !Chroma ? (lsb_in ? no16 : no) : (lsb_in ? UV16 : UV)
Ytv = contrast == 0.0 ? (lsb_in ? no16 : no) : (lsb_in ? Ytv16 : Ytv)
Ypc = contrast == 0.0 ? (lsb_in ? no16 : no) : (lsb_in ? Ypc16 : Ypc)


# Color Correction

c
s =
\ (range=="TV") ? ( lsb_in ? SmoothCurve16(Ycurve=Ytv16,Ucurve=UV16,Vcurve=UV16,smooth=0, dither=0, show=show, limiter=limit, interp=interp) :
\ SmoothCurve (Ycurve=Ytv, Ucurve=UV, Vcurve=UV, smooth=smooth,dither=dither,show=show, limiter=limit, interp=interp)) :
\ (range=="PC") ? ( lsb_in ? SmoothCurve16(Ycurve=Ypc16,Ucurve=UV16,Vcurve=UV16,smooth=0, dither=0, show=show, limiter=limit, interp=interp) :
\ SmoothCurve (Ycurve=Ypc, Ucurve=UV, Vcurve=UV, smooth=smooth,dither=dither,show=show, limiter=limit, interp=interp)) :
\ Assert(false, "Unrecognised range: "+string(range))



# Selective Gamma/Saturation Mode

Chroma = Chroma ? 3 : 2
msk = loc ? mt_binarize(lsb_in ? last.ditherpost(mode=6) : c,lsb_in ? m/256 : m,upper=
\ (local=="dark") ? true :
\ (local=="bright") ? false :
\ Assert(false, "Unrecognised Local mode")) : nop

loc ? (lsb_in ? Dither_merge16_8(s,msk,luma=true) : mt_merge(s,msk,U=Chroma,V=Chroma,luma=true)) : s}

cretindesalpes
17th September 2011, 15:39
w=range=="tv" ? (lsb_in ? 60416 : 236) : (lsb_in ? 65535 : 256)
TV white is 235.000 (60160 in 16 bits), not 236 or 235.999.
PC white is 255.000 on a 8-bit scale, not 256 or 255.999.

Also, use floating point data for all your intermediate calculations (m, Cb, Cw, Chb, Chw) to avoid accumulating rounding errors.

Regarding the chroma correction, I'm not sure the saturation can be mapped directly on the contrast. Generally, the contrast operation is performed in the RGB colorspace, so you'd need at least different multipliers for the U and V channels, and these would depend on the conversion matrix.

Chroma in TV range is 16-240, so the conversion formulas are:
TV to PC: ((x-16)/224)*255
PC to TV: ((x*224)/255)+16
Not exactly, pure grey should stay grey:
TV to PC: ((x-128)/224)*255+128
PC to TV: (((x-128)*224)/255)+128

Dogway
18th September 2011, 01:51
I updated the function.

256 is for the calculations, I still keep the white limit to 255 (changed TV's to 235 tho)
The string for a PC range as I have it now is:
"0-0;64-"+string(64-64*contrast)+";128-128;192-"+string(192+64*contrast)+";255-255"
If I change 256 to 255 then it would become:
"0-0;64-"+string(64-64*contrast)+";128-128;191-"+string(191.5+63.5*contrast)+";255-255"
If this is correct then Id change it.

About the chroma, you are right. It is an RGB thing. It would anyways feel akward to change contrast only on luma.
As for now saturation is processed evenly in all places, unlike contrast where towards the mid point and extremes of the curves are untouched, so there's a mismatch here.
What I just have thought is on creating a diff mask to "get" this contrast luma curve, and mask it to saturation. Saturation strength can be scaled correctly upon MAX saturation,
don't know if this is correct, but it can be a workaround. About matrix, I just know nothing : /
edit: I think I had a mental fart, I tested over photoshop and it seems saturation increases even where luma doesn't change in contrast. So it was correct, maybe as you say due to this being another way of processing saturation (and the only one in avisynth) and not curves in rgb, the strength of it can be different. I don't know about saturation differences on different matrix. This is only be going to be processed on yuv material I/O, but probably you mean whether it is 601 or 709. Maybe that's why I can't match smoothtweak saturation (results are near but not same)


edit: An elegant way to mask out only chroma? (Although I still don't know how to mask out chroma, not this is not related in anything to the contrast thinking; is wrong)

This is the difference mask in 16 bit of original clip vs the contrasted clip

msk=Dither_lutxy8(c.ditherpost(mode=6,u=-128,v=-128),s.ditherpost(mode=6,u=-128,v=-128),yexpr="x 256 * y 256 * - 32768 +")

Im struggling to mask out only the chroma planes over the contrasted clip.
edit: ok, I think I solved it, using the function YToUV() for the second example

Dither_merge16_8(s,msk.ditherpost(mode=6,u=-128,v=-128),luma=true) # This line merges UV but luma plane too
Dither_merge16(s,msk,y=4,u=3,v=3) # This line reads the UV planes of the mask for the chroma merging


BTW LaTo, ecenter has a problem on smoothlevels16, or so it seems, the curve gets a pinch...

LaTo
24th September 2011, 18:55
Ok, since nobody confirmed me anything it's time to disclose the work so you can debug and correct me whatever I did wrong.

Sorry I don't have much free time these days due to a new job, I will try to take a look later ;)


BTW LaTo, ecenter has a problem on smoothlevels16, or so it seems, the curve gets a pinch...
Already fixed in the next release!

LaTo
25th September 2011, 16:09
v2.00 beta9:
added TVrange parameter
added full decimals support in SmoothCurve
rewritted the parser in SmoothCurve (more robust)
changed defaults in SmoothLevels
fixed a bug with Ecenter in SmoothLevels16
added "pi" constant in SmoothCustom
cleaned the code

Todo-list:
- fix all bugs
Download here: > SmoothAdjust v2.00 beta9 < (http://latoninf.free.fr/d9/SA/SmoothAdjust-v2.00beta9.zip)

This release should be the last one before v2.00 final :cool:

LaTo
25th September 2011, 16:16
@Dogway: Here is a corrected version of your SmoothContrast using beta9:

function SmoothContrast( clip c, float "contrast", float "sat", string "range", string "local", int "median", bool "bits16", \
int "smooth", int "dither", int "interp", bool "limit", bool "show" )
{

contrast = default(contrast, 0.0) # [-1.0 to +1.0]
sat = default(sat, contrast) # [-1.0 to +1.0] Saturation. By default contrast is also applied to the UV planes.
range = default(range, "TV") # TV or PC. Luma range of your source, probably TV as SmoothAdjust only accepts yuv
smooth = default(smooth, 100) # same as SmoothAdjust; strength of the smoothing. 0 = off
dither = default(dither, 50) # same as SmoothAdjust; fuzziness of the dither. -1 = off
interp = default(interp, 100) # same as SmoothAdjust; curve interpolation curve. 0 = linear
bits16 = default(bits16, false) # 16 bits I/O. For using in a 16 bits stacked MSB+LSB chain
limit = default(limit, false) # Clamp the output to 16-235 for luma & 16-240 for chroma (TVRange)
show = default(show, false) # Show debug. Doesn't work when using "local" mode.

median = default(median, 128) # median defines the luma center point of your source to process contrast from,
# or the luma threshold where you want to process gamma/sat from in the "local" mode.

local = default(local, "default") # "local" allows to focus only on the gamma/saturation of the dark or bright parts of the image.
# Use "dark" to process dark areas, or "bright" for bright areas.

loc_assert = (local=="default") ? true : (local=="dark") ? true : (local=="bright") ? true : false
Assert(loc_assert, "Unrecognised Local mode")

loc_assert = ( (local=="dark") ? true : (local=="bright") ? true : false ) && ( show ) ? false : true
Assert(loc_assert, "Debug view cannot be shown in Local mode")

ran_assert = (range=="TV") ? true : (range=="PC") ? true : false
Assert(ran_assert, "Unrecognised Range")


# Variables

m = (!bits16) ? float(median) : float(median*256)
b = (!bits16) ? 0 : 0
w = (!bits16) ? 255 : 65280
Cb = (b+m)/2.0
Cw = (w+m)/2.0
Chb = (m-b)/2.0
Chw = (w-m)/2.0


# Strings

no = (!bits16) ? "0-0;255-255"
\ : "0-0;65535-65535"

Y = (!bits16) ? "0-0;"+string(Cb)+"-"+string(Cb-Chb*contrast)+";"+string(m)+"-"+string(m)+";"+string(Cw)+"-"+string(Cw+Chw*contrast)+";255-255"
\ : "0-0;"+string(Cb)+"-"+string(Cb-Chb*contrast)+";"+string(m)+"-"+string(m)+";"+string(Cw)+"-"+string(Cw+Chw*contrast)+";65280-65280;65535-65280"

UV = (!bits16) ? "0-"+string(128*(-sat))+";255-"+string(255-128*(-sat))
\ : "0-"+string(32768*(-sat))+";65280-"+string(65280-32768*(-sat))+";65535-"+string(65280-32768*(-sat))

Y = (contrast==0.0) ? no : Y
UV = (sat ==0.0) ? no : UV


# Color Correction

s = (!bits16) ? c.SmoothCurve ( Ycurve=Y, Ucurve=UV, Vcurve=UV, TVrange=(range=="TV")?2:0, smooth=smooth, dither=dither, show=show, limiter=limit, interp=interp )
\ : c.SmoothCurve16( Ycurve=Y, Ucurve=UV, Vcurve=UV, TVrange=(range=="TV")?2:0, smooth=smooth, dither=dither, show=show, limiter=limit, interp=interp )


# Selective Gamma/Saturation Mode

msk = (local=="dark") ? mt_binarize( (!bits16)?c:c.ditherpost(mode=6), (!bits16)?int(m):int(m/256.0), upper=true )
\ : (local=="bright") ? mt_binarize( (!bits16)?c:c.ditherpost(mode=6), (!bits16)?int(m):int(m/256.0), upper=false )
\ : nop()

return (local=="default") ? s : ( (!bits16) ? c.mt_merge(s, msk, U=(sat!=0.0)?3:2, V=(sat!=0.0)?3:2, luma=true) : c.Dither_merge16_8(s, msk, luma=true) )

}

leeperry
25th September 2011, 16:56
muchos :thanks: for the new build!

Dogway
25th September 2011, 18:20
Wow thanks, so many things, I appreciate your help. Now it looks much more tidier.
I realised a few things, like that now input value can be decimal(?), and TVrange is now a setting. In the help it says "Convert the expressions to TVrange(16-235/16-240) before applying conversion", does it reinterpret the input values, or it runs a range conversion before the current processing? aka, is it a 1 pass or 2 pass workflow?

I think you missed changing the variables since now its referenced as PC values.
# Variables

m = (!bits16) ? float(median) : float(median*256)
w = (!bits16) ? 255 : 65280
Cb = (m) /2.0
Cw = (w+m)/2.0
Chb = Cb
Chw = (w-m)/2.0

But Im not sure, this was my main question in last post after cretindesalpes reply, for calculations would you use 256 or 255?
Its the difference between using 256:
"0-0;64-"+string(64-64*contrast)+";128-128;192-"+string(192+64*contrast)+";255-255"
or 255:
"0-0;64-"+string(64-64*contrast)+";128-128;191-"+string(191.5+63.5*contrast)+";255-255"

I realise the "local" mode for gamma is not to be used much, that's why I started fiddling with smoothlevels lmode. But the option is still there, for completion, I will warn of its countereffects.

edit: I studied the new TVRange parameter, its hard to understand because it does different things in the different functions. For example in smoothlevels you set tvrange=true, but still the syntax for tvrange inputs must be "16,1.0,235,16,235". About smoothcurve I don't think tvrange=2 does the same I had before. You still process below 16 and above 235 values, and thus the curve also gets affected, unlike smoothlevels where gamma is correctly processed between 16-235 (default syntax). Probably for v2.00 final you could add also an option for HD and SD concerning chroma as cretindesalpes indicated.
http://img849.imageshack.us/img849/728/tvrange2.th.png (http://img849.imageshack.us/img849/728/tvrange2.png) http://img571.imageshack.us/img571/3526/tvrange0.th.png (http://img571.imageshack.us/img571/3526/tvrange0.png)
In this pic the pixel x=563,y=32 which the original luma value is 40 converts to 54 in the first pic, and 59 in the second.

LaTo
26th September 2011, 10:30
Wow thanks, so many things, I appreciate your help. Now it looks much more tidier.
I realised a few things, like that now input value can be decimal(?), and TVrange is now a setting. In the help it says "Convert the expressions to TVrange(16-235/16-240) before applying conversion", does it reinterpret the input values, or it runs a range conversion before the current processing? aka, is it a 1 pass or 2 pass workflow?

I think you missed changing the variables since now its referenced as PC values.
# Variables

m = (!bits16) ? float(median) : float(median*256)
w = (!bits16) ? 255 : 65280
Cb = (m) /2.0
Cw = (w+m)/2.0
Chb = Cb
Chw = (w-m)/2.0

But Im not sure, this was my main question in last post after cretindesalpes reply, for calculations would you use 256 or 255?
Its the difference between using 256:
"0-0;64-"+string(64-64*contrast)+";128-128;192-"+string(192+64*contrast)+";255-255"
or 255:
"0-0;64-"+string(64-64*contrast)+";128-128;191-"+string(191.5+63.5*contrast)+";255-255"
Yes, you're right, it's a mistake. You should use 0 & 255 for calculations (and not 16 - 235).

edit: I studied the new TVRange parameter, its hard to understand because it does different things in the different functions. For example in smoothlevels you set tvrange=true, but still the syntax for tvrange inputs must be "16,1.0,235,16,235".
Yes, it's different between functions:
- SmoothLevels: only affects limiting part (see Lmode in readme, last line)
- SmoothTweak: only affects contrast
- SmoothCurve/SmoothCustom: remap the curve/expression to the 16-235 range

About smoothcurve I don't think tvrange=2 does the same I had before. You still process below 16 and above 235 values, and thus the curve also gets affected, unlike smoothlevels where gamma is correctly processed between 16-235 (default syntax).
It's because there is no "one way" of doing this, the only fully correct & safest way is to drop out 0-16 & 235-255 informations (see TVrange=1)
Another way is to leave extra-range pixels as-is. (Work great for gamma adjustement, but most of the time it's a *very big* mistake)
The last way is to interpolate extra-range values from the 16-235 curve, this is the best most of time (see TVrange=2)

http://img849.imageshack.us/img849/728/tvrange2.th.png (http://img849.imageshack.us/img849/728/tvrange2.png) http://img571.imageshack.us/img571/3526/tvrange0.th.png (http://img571.imageshack.us/img571/3526/tvrange0.png) In this pic the pixel x=563,y=32 which the original luma value is 40 converts to 54 in the first pic, and 59 in the second.
Your 1rst curve is wrong, 40-60 are remapped with TVrange in the second curve so it's no longer the same (become 27.95-51.23).
Should be something like: "0-0;16-16;50.35-67.53;235-235;255-255" for the 1rst one.

Probably for v2.00 final you could add also an option for HD and SD concerning chroma as cretindesalpes indicated.
More precisely?

Dogway
26th September 2011, 12:10
I understand now, so it seems values get remapped instead of a 2 pass workflow. What is good about it is I can still use the old syntax with 16-16 when I want to precisely tweak the values. I was specially confused on what TVrange does on smoothcurve, now is a bit more clear.

Generally, the contrast operation is performed in the RGB colorspace, so you'd need at least different multipliers for the U and V channels, and these would depend on the conversion matrix.

I think he implied that saturation depends on conversion matrix, so probably chroma has to be treated differently in strength and in U and V whether it is SD, or HD.

Thanks for all, and sorry to bother so much :o


edit:
I fixed a few things like
-Input correction for matching TV values in median
-Default median now is 127.5 (255/2)
-When processing saturation only, a gradual mask is applied now in local mode
-Now 'show' works in local mode, the used mask is shown.
-plus more few little tweaks.

function SmoothContrast( clip c, float "contrast", float "sat", float "median", string "range", string "local", bool "lsb", \
int "smooth", int "dither", int "interp", bool "limit", bool "show" )
{

contrast = default(contrast, 0.0) # [-1.0 to +1.0]
sat = default(sat, contrast) # [-1.0 to +1.0] Saturation. By default contrast is also applied to the UV planes.
range = default(range, "TV") # TV or PC. Luma range of your source, probably TV as SmoothAdjust only accepts yuv
smooth = default(smooth, 20) # same as SmoothAdjust; strength of the smoothing. 0 = off
dither = default(dither, 100) # same as SmoothAdjust; fuzziness of the dither. -1 = off
interp = default(interp, 100) # same as SmoothAdjust; curve interpolation curve. 0 = linear
lsb = default(lsb, false) # 16 bits I/O. For using in a 16 bits stacked MSB+LSB chain
limit = default(limit, false) # Clamp the output to 16-235 for luma & 16-240 for chroma (TVRange)
show = default(show, false) # Show debug. Doesn't work when using "local" mode.

local = default(local, "default") # "local" allows to focus only on the gamma/saturation of the dark or bright parts of the image.
# Use "dark" to process dark areas, or "bright" for bright areas.

median = Defined(median)?(range=="TV" && contrast!= 0.0\
? ((median-16)/219.0)*255:median):127.5# median defines the luma center point of your source to process contrast from,
# or the luma threshold where you want to process gamma/sat from in the "local" mode.


# Error Report

loc_assert = (local=="default") ? true : (local=="dark") ? true : (local=="bright") ? true : false
Assert(loc_assert, "Unrecognised Local mode")

ran_assert = (range=="TV") ? true : (range=="PC") ? true : false
Assert(ran_assert, "Unrecognised Range")


# Variables

m = (!lsb) ? median : median*256
w = (!lsb) ? 255 : 65280
Cb = (m) /2.0
Cw = (w+m)/2.0
Chw = (w-m)/2.0


# Strings

no = (!lsb) ? "0-0;255-255"
\ : "0-0;65535-65535"

Y = (!lsb) ? "0-0;"+string(Cb)+"-"+string(Cb-Cb*contrast)+";"+string(m)+"-"+string(m)+";"+string(Cw)+"-"+string(Cw+Chw*contrast)+";255-255"
\ : "0-0;"+string(Cb)+"-"+string(Cb-Cb*contrast)+";"+string(m)+"-"+string(m)+";"+string(Cw)+"-"+string(Cw+Chw*contrast)+";65280-65280;65535-65280"

UV = (!lsb) ? "0-"+string(127.5*(-sat))+";255-"+string(255-127.5*(-sat))
\ : "0-"+string(32640*(-sat))+";65280-"+string(65280-32640*(-sat))+";65535-"+string(65280-32640*(-sat))

Y = (contrast==0.0) ? no : Y
UV = (sat ==0.0) ? no : UV


# Color Correction

s = (!lsb) ? c.SmoothCurve ( Ycurve=Y, Ucurve=UV, Vcurve=UV, TVrange=(range=="TV")?2:0, smooth=smooth, dither=dither, show=local!="default"?false:show, limiter=limit, interp=interp )
\ : c.SmoothCurve16( Ycurve=Y, Ucurve=UV, Vcurve=UV, TVrange=(range=="TV")?2:0, smooth=smooth, dither=dither, show=local!="default"?false:show, limiter=limit, interp=interp )


# Selective Gamma/Saturation Mode


c16 = local!="default" ? (!lsb ? c : c.ditherpost(mode=6)) : nop

mskstr = local=="dark" ? (range=="TV" ? "x "+string(median)+" > 0 255 x 16 - 255 "+string(median)+" 16 - / * - ?" :
\ "x "+string(median)+" > 0 255 x 255 "+string(median)+" / * - ?") :
\ local=="bright" ? (range=="TV" ? "x "+string(median)+" > 255 x 16 - 255 "+string(median)+" 16 - / * ? " :
\ "x "+string(median)+" > 255 x 255 "+string(median)+" / * ? ") : nop


msk = (local=="dark") ? (contrast!=0.0 ? mt_binarize( c16, (!lsb)?int(m):int(m/256.0), upper=true, U=-128,V=-128) :
\ mt_lut(c16, mskstr,U=-128,V=-128)) :
\ (local=="bright") ? (contrast!=0.0 ? mt_binarize( c16, (!lsb)?int(m):int(m/256.0), upper=false, U=-128,V=-128) :
\ mt_lut(c16, mskstr,U=-128,V=-128)) : nop

local=="default" ? s : (show ? msk.smoothlevels(preset="pc2tv",chroma=0,smooth=0,dither=-1).subtitle("This is 'local' debug 'show' mode\n" + " Shows the mask (as TVrange for display)\n" + " used for the selective Gamma/Saturation",lsp=40,size=12,y=10,x=10)
\ : (!lsb ? c.mt_merge(s, msk, U=(sat!=0.0)?3:2, V=(sat!=0.0)?3:2, luma=true) : c.Dither_merge16_8(s, msk, luma=true)))}

LaTo
4th October 2011, 07:23
beta9 has a strange bug. After reload a script in virtual dub it shows an error message in call: SmoothCurve("0-0;128-132;255-237") about incorrect parameter of TVrange. I tried different values, but the message repeats again. Sorry, I cannot use your beta 9, because I need to often reload the script. The error message is annoyed. Beta 8 works fine. I use 2.5.8 MT (2.6 MT is not compatible with Deshaker)

Can you post your script and the exact error message?

What did you do exactly to get this bug? (because I can't reproduce it now)

LaTo
4th October 2011, 14:46
The message is: Avisynth open failure: 'TVrange' has not a correct value! [0;1;2].
SetMTMode(5,4)
Import("C:\Soft\Video\bin\HDToolboxCore.avsi")
Import("video\avsi\20110619_14088.avsi")
ClipSource(video=true, audio=true) #reading over dss2()
SetMTMode(2)
PreTrim()
PostTrim()
RangeNEXtoTV() #here is the call of SmoothCurve("0-0;128-132;255-237")

It is an x64 edition, and ICL version of your plugin. I open the script in vdub, make changes in 20110619_14088.avsi in function PreTrim() and reload vdub to see changes (trim unwanted chunks). Just File>Reopen video file, or F2. And I see the message box, close it. And all works fine after, but after F2 I see the message again. I dont know what is the difference between first load of vdub and reload a file, but the message is only when I reload the script. The first open is correct.

Also checked it in x86 version. It works fine.
So the x86 beta9 works? Have you tried with x64 beta9 MSVC?

What is the content of RangeNEXtoTV()?
Try to turn off MT before SmoothCurve, it's internally multi-threaded.

LaTo
4th October 2011, 15:19
LaTo, I have different scripts where RangeNEXtoTV() is used. The code from my previous message is one of the batch generated files, where it is used. And I need in multithreading and 64 bit code in other scripts. So I cannot switch to 32 bit and disable MT. Sorry, cannot test it now. I have started a very long processing, that will be finished tomorrow. As completed, I will try to experiment.

The function is simple:
function RangeNEXtoTV(clip c) {
c.SmoothCurve("0-0;128-132;255-237")
}

The error message pointed to this function. Yes, beta9 x86 ICL works without this message box.

OK, so it's only with x64. When you can, use the x64 MSVC version from beta9. It's maybe a compiler bug.

edit: Ok I got it, fixed. Thanks for report!

sven_x
9th October 2011, 20:58
I'm currently working on a tool that will allow to
a) generate an overall gradiaton curve from a given Virtualdub Filter Chain
b) simulate that curve with SmoothCurve in RGB color space

The goal is to transfer color adjustments for old films with faded colors that work fairly good in Virtualdub. Old film consists of RGB color layers, that's why a correction of faded colors is easier done in RGB color space.

The first part a) is already working. gradationtestavs.zip (http://www.engon.de/audio/avisynth/gradationtestavs.zip)
http://www.engon.de/audio/vhs4/restauration-example1.jpg
http://www.engon.de/audio/avisynth/gradation_old_film.png
Old film and corrected version in Virtualdub,
The effect of the same filter chain to a grey scale and the overall gradiation curve, computed from this output

Part b) can be acchieved with a combination of separated smoothcurves of R G and B that are merged back into one clip. Doing so you also get a nice combinated RGB gradiation curve with show=true in all three layers.

http://www.engon.de/audio/avisynth/smoothgradiationrgb00.png

Here are my requests:

The process might gain some speed when there will be an option to disable processing of U and V in SmoothCurve, because in this case each of the three instances of smoothcurve will calculate Y only.
It will be easier to reconstruct the gradiation curve a), wenn the SmoothCurve gradiation curve will be displayed with a 4x4 grid, as it is seen in the first image.


Thank you very much

LaTo
9th October 2011, 21:09
Here are my requests:

The process might gain some speed when there will be an option to disable processing of U and V in SmoothCurve, because in this case each of the three instances of smoothcurve will calculate Y only.
It will be easier to reconstruct the gradiation curve a), wenn the SmoothCurve gradiation curve will be displayed with a 4x4 grid, as it is seen in the first image.

Thank you very much

1. It's already a feature, only modified planes are processed.
2. Why not, good idea!

sven_x
10th October 2011, 12:51
SmoothGradationRGB is a 5 point gradation curve in RGB color space.
It provides a 5-point Gradation curve Lo - P0 - Mid - P1 - Hi with global adjustments for Point 0, Mid and Point 1 and R,G,B offset for each point.
Free adjustment of position and offset for P0 and P1.
You can see all 3 curves blendet into the Y curve diagram when ShowRGB=true (example in previous post).

So the function enables individual settings of Min and Max levels and all kinds of S-Kurves for each Color.

The avisynth script for AvsP has sliders that can adjust a RGB gradation curve and add individual offsets for each color with a second set of sliders.

Here's the Script.
#Input ................................................................................................

#SmoothGradationRGB ................................................................................................
# SmoothGradationRGB is a 5 point gradation curve in RGB color space
# This is a 5-point Gradiation Curve Lo - P0 - Mid - P1 - Hi with global adjustments for Point 0, Mid and Point 1 and R,G,B offset for each point
# Free adjustment of position and RGB offset for P0 and P1.
# you can see all 3 curves blendet into the Y curve diagram when ShowRGB=true
# v0.5

loadplugin("J:\plugins\SmoothAdjust.dll")

[<separator="SmoothGradationRGB">]
po1 = [<"Point 1 lights", 180, 255, 214>] #position of point 1
offs1 = [<"Offset 1 (2)", -32, 32, -5>]
offs1R = [<"Offset 1R (2)", -32, 32, 14>]
offs1G = [<"Offset 1G (2)", -32, 32, -10>]
offs1B = [<"Offset 1B (2)", -32, 32, -3>]

midRGB=[<"MidRGB (0)", -64, 64, 0>]
midR=[<"MidR (0)", -64, 64, -25>]
midG=[<"MidG (0)", -64, 64, 0>]
midB=[<"MidB (0)", -64, 64, 24>]

po0 = [<"Point 0 dark (8)", 0, 75, 64>] #position of point 0
offs0 = [<"Offset 0 (2)", -32, 32, 18>] # y offset for point 0
offs0R = [<"Offset 0R (0)", -32, 32, -8>] # additional yR offset for point 0
offs0G = [<"Offset 0G (2)", -32, 32, -5>]
offs0B = [<"Offset 0B (2)", -32, 32, 10>]

HiR=[<"HiR (255)", 222, 254, 254>] # max level
HiG=[<"HiG (255)", 222, 254, 235>]
HiB=[<"HiB (255)", 222, 254, 254>]
LoR=[<"LoR (0)", 1, 32, 1>] #min level
LoG=[<"LoG (0)", 1, 32, 13>]
LoB=[<"LoB (0)", 1, 32, 1>]

SmoothRGB=[<"SmoothRGB (100)", 0, 200, 0>]

TVrange=[<"InputTVrangeRGB (0)", 0, 2, 0>]
LimiterRGB=select([<"LimiterRGBout (0)", 0, 1, 0>],false,true)
ShowRGB=select([<"Show RGB (0)", 0, 1, 1>],false,true)
OutRGB=select([<"RGB out (on)", 0, 1, 1>],false,true)

converttoRGB

R = ShowRed(pixel_type="YV12") .Scurve(Lo=LoR, po0=po0, offs0=offs0+offs0R, mid=midRGB+midR, po1=po1, offs1=offs1+offs1R, Hi=HiR, limiter=limiterRGB,Show=showRGB,TVrange=TVrange, Smooth=SmoothRGB)
G = ShowGreen(pixel_type="YV12") .Scurve(Lo=LoG, po0=po0, offs0=offs0+offs0G, mid=midRGB+midG, po1=po1, offs1=offs1+offs1G, Hi=HiG, limiter=limiterRGB,Show=showRGB,TVrange=TVrange, Smooth=SmoothRGB)
B = ShowBlue(pixel_type="YV12") .Scurve(Lo=LoB, po0=po0, offs0=offs0+offs0B, mid=midRGB+midB, po1=po1, offs1=offs1+offs1B, Hi=HiB,limiter=limiterRGB,Show=showRGB,TVrange=TVrange, Smooth=SmoothRGB)

last=MergeRGB(R,G,B)
last= (OutRGB==true) ? last : last.ConvertToYv12()
return last#.info()


function Scurve(clip "clip", int "Lo", int "po0", int "offs0", int "mid", int "po1", int "offs1", int "Hi", bool "show", bool "limiter", int "TVrange", int "smooth")
{
# interp=2 bicubic interpolation default
po0 = default(po0, 24)
offs0 = default(offs0, 0)
po1 = default(po1, 230)
offs1 = default(offs1, 0)
Lo = default(Lo, 1) # shift of mid point to dark/lights [-1.0 to +1.0]
mid = default(mid, 0) # shift of mid point to dark/lights [-1.0 to +1.0]
Hi = default(Hi, 1) # shift of mid point to dark/lights [-1.0 to +1.0]

show = default(show, false) # show curve
limiter = default(limiter, false) # clamp to tv levels

x0=string(po0)
y0=string(po0+offs0)
x1=string(po1)
y1=string(po1+offs1)

return clip.SmoothCurve(Ycurve="0-0; "+string(Lo)+"-1; "+string(y0)+"-"+string(x0)+"; "+string(128+mid)+"-128;" + string(y1)+ "-" +string(x1)+"; "+string(Hi)+"-254 ;255-255"\
,Limiter=limiter,show=show,TVrange=TVrange, smooth=smooth)
}

New Version: Version 0.5b (http://www.engon.de/audio/avisynth/SmoothGradiationRGB.avs), added debug mode
Gradationtestavs (http://www.engon.de/audio/avisynth/gradationtestavs.zip) to generate overall gradation curve from a Virtualdub Filter chain

USAGE
1. Reset all R, G and B offsets to 0.
2. Set the overall shape (for instance an S curve that darkens bright areas and lifts shadows).
It may help to change input to grey values to get a better visual impression of the result.
3. Add individual offsets to each point for R, G and B.

See SmoothAdjust readme for the rest of the SmoothCurve options that are used here.

It throws a few exceptions when parameters cross a certain range. LaTo, can you please have a look at this effect?