Log in

View Full Version : adding grain on h264 in ffdshow ?


Pages : [1] 2

pitch.fr
20th September 2008, 20:24
hi there,

I already use LSF+Ulevels() in realtime in ffdshow...it looks awesome :D

but a lot of h264 lack cinema grain badly :(

like the new FAST & FURIOUS trailer :
http://movies.apple.com/movies/universal/fastandfurious/fastandfurious-tlr1_h720p.mov

or the SPEED RACER bluray :(

anyhow, I've found an old masktools v1 script from Didée called "GrainFactory" that looks really great!

the FAST & FURIOUS trailer looks awesome with (3,5,100,100) :eek:

and adding grain also deblocks really well, it seems to increase the picture depth :)

you can set a different grain level for mid and high/low tones to simulate real movie silver grain......but it's sucking a helluvalot of CPU time :(

benchmarking in VDUB lowers from 88fps to 48....which gives problems on high bitrate 1.78 HD :(

anyone knows a similar script that might be usable in realtime in ffdshow ?

Addgrain is too static I think, it's not realistic at all.

or anyone would be up to the task of converting it to masktools v2 please please please ?

I'm an AVS n00b, not quite a coder :(

here's a version fixed by Didée this morning(picture was two notches too bright before the fix) and modded to spline36 by me :

function GrainFactory(clip last,int "grain1_strength",int "grain2_strength",int "grain1_sharpness",int "grain2_sharpness")
{

grain1_strength=default(grain1_strength,5)
grain2_strength=default(grain2_strength,2)
grain1_sharpness=default(grain1_sharpness,75)
grain2_sharpness=default(grain2_sharpness,75)

#grain1_strength = 7 # [ 0 - ???] strength of grain / for mid-tone areas
grain1_size = 1.15 # [0.5 - 4.0] size of grain / for mid-tone areas

#grain2_strength = 0 # [ 0 - ???] strength of grain / for dark & bright areas
grain2_size = 0.8 # [0.5 - 4.0] size of grain / for dark & bright areas

#grain1_sharpness = 75 # [ 0 - 100] very soft (0) to very sharp(100)
#grain2_sharpness = 75 # [ 0 - 100] grain definition

grain1_texture = 0 # [ 0 - 100] strength of "material texture" appearance
grain2_texture = 0 # [ 0 - 100] (in fact: 'bump' effect)

ontop_grain = 0 # [ 0 - ???] additional grain to put on top of prev. generated grain

temp_avg = 20 # [ 0 - 100] percentage of noise's temporal averaging

brightness_bias = 0.96 # [0.5 - 2.0] bias for dark|bright --> midtone fading

#-----------------------------------------------------------------------------------
o = last
ox = o.width
oy = o.height
sx1 = m4(ox/grain1_size)
sy1 = m4(oy/grain1_size)
sx1a = m4((ox+sx1)/2.0)
sy1a = m4((oy+sy1)/2.0)
sx2 = m4(ox/grain2_size)
sy2 = m4(oy/grain2_size)
sx2a = m4((ox+sx2)/2.0)
sy2a = m4((oy+sy2)/2.0)
b1 = grain1_sharpness/(-50.0) + 1.0
b2 = grain2_sharpness/(-50.0) + 1.0
b1a = b1/2.0
b2a = b2/2.0
c1 = (1.0-b1)/2.0
c2 = (1.0-b2)/2.0
c1a = c1/2.0
c2a = c2/2.0
t1a = string(grain1_texture)
t1b = string(100-grain1_texture)
t2a = string(grain2_texture)
t2b = string(100-grain2_texture)
tmpavg = temp_avg/100.0

#-----------------------------------------------------------------------------------

ADD_DIFF = "x y - 128 +"
BUMPKERNEL1 = T1a + " 0 0 0 " + T1b + " 0 0 0 0"
BUMPKERNEL2 = T2a + " 0 0 0 " + T2b + " 0 0 0 0"
CENTER2MAX_BIAS = "x 120 - abs "+string(brightness_bias)+" ^ 2.5 *"

#-----------------------------------------------------------------------------------

grainlayer1 = blankclip(o, width=sx1, height=sy1, color_yuv=$808080) .AddGrain(grain1_strength, 0, 0)

grainlayer1 = (grain1_texture==0)
\ ? grainlayer1
\ : yv12lutxy(grainlayer1,grainlayer1.DEdgeMask(0,255,0,255, BUMPKERNEL1), ADD_DIFF)

grainlayer1 = (grain1_size == 1.0) ? grainlayer1
\ : (grain1_size < 0.75) ? grainlayer1.spline36resize(sx1a,sy1a, b1a, c1a).spline36resize(ox,oy, b1a,c1a)
\ : grainlayer1.spline36resize(ox,oy, b1,c1)

#-----------------------------------------------------------------------------------

grainlayer2 = blankclip(o, width=sx2, height=sy2, color_yuv=$808080) .AddGrain(grain2_strength, 0, 0)

grainlayer2 = (grain2_texture==0)
\ ? grainlayer2
\ : yv12lutxy(grainlayer2,grainlayer2.DEdgeMask(0,255,0,255, BUMPKERNEL1), ADD_DIFF)

grainlayer2 = (grain2_size == 1.0) ? grainlayer2
\ : (grain2_size < 0.75) ? grainlayer2.spline36resize(sx2a,sy2a, b2a, c2a).spline36resize(ox,oy, b2a,c2a)
\ : grainlayer2.spline36resize(ox,oy, b2,c2)

#-----------------------------------------------------------------------------------

grainlayer = MaskedMerge(grainlayer1, grainlayer2, o.yv12lut(CENTER2MAX_BIAS), U=1,V=1)

grainlayer = (temp_avg==0) ? grainlayer
\ : grainlayer.mergeluma(grainlayer.temporalsoften(1,255,255,255,2), tmpavg)
grainlayer = (ontop_grain==0) ? grainlayer
\ : grainlayer.AddGrain(ontop_grain,0,0)

#-----------------------------------------------------------------------------------

result = yv12lutxy(o, grainlayer, ADD_DIFF, U=2,V=2)

return result
}
#-----------------------------------------------------------------------------------
function m4(float x) {return( x<16?16:int(round(x/4.0)*4)) }
#===================================================================================

:thanks: for your help!

Didée
20th September 2008, 21:05
Ah, a public thread. That's better.


It's not a gamma issue. It's a plain +2 shift on all pixels, caused by a RGB<>YUV mismatch.

See those lines in GrainFactory:
#-----------------------------------------------------------------------------------

grainlayer1 = blankclip(o, width=sx1, height=sy1, color=$808080) .AddGrain(grain1_strength, 0, 0)

...

#-----------------------------------------------------------------------------------

grainlayer2 = blankclip(o, width=sx2, height=sy2, color=$808080) .AddGrain(grain2_strength, 0, 0)

...

Problem is that color=X in blankclip interprets X as an RGB color, even if the input is YUV. Now, RGB=$808080 is YUV=$7E8080. Consequently, the grainlayers are based on [126,128,128] grey clips, where it should be [128,128,128].

Easiest way is to use "color_yuv":
#-----------------------------------------------------------------------------------

grainlayer1 = blankclip(o, width=sx1, height=sy1, color_yuv=$808080) .AddGrain(grain1_strength, 0, 0)

...

#-----------------------------------------------------------------------------------

grainlayer2 = blankclip(o, width=sx2, height=sy2, color_yuv=$808080) .AddGrain(grain2_strength, 0, 0)

...
It's an old function (almost forgot it), and I'm not sure if the "color_yuv" verb already existed in Avisynth at that time. Also, it looks like that function should be converted to MaskTools v2 ... I get grey hair when I see functions still using the old v1 MaskTools. <g>


Adapting to MaskTools v2 is no prob at all, but there's no speed gain to expect. It's the inpand/expand/inflate/deflate filters that got a big speedup from v1 -> v2, but those aren't even used here. Some filters are even a bit slower in v2 ... e.g. MaskedMerge -> mt_merge is a bit slower now, and this filter IS used in the script. :p

The most reasonable speedup would be to set temp_avg to zero, to disable any temporal filtering. The value for temp_avg has to be changed manually in the script, it's not a parameter. To help a little more, you could set grain2_size from 0.8 to 1.0, avoiding one resize operation.
(Note that I didn't even make this *function* ... I once put together a plain script on request of a user, the surrounding function code was made by someone else. It's easy to see ... several things in the function are either hardcoded and/or silently disabled ... that's not the way that I usually make my functions) ;)

If it's still too slow with temp_avg=0 and grain2_size=1.0, then you've to use plain AddGrain (the ffdshow ones, then).

Or, another one: Mug Funky once suggested to apply blurred grain, to make it a little more film-ish. Don't have his script anymore, but it's easy to do ... like so:

# make some blurred grain
x1 = 15 # strenght of grain, 0 ~ 255
x2 = 0.66 # strength of grain blurring, 0.0 ~ 1.0
mt_makediff(mt_makediff(last,last.AddGrain(x1)).blur(x2),U=2,V=2)

In a nutshell:
a) converting to MaskTools v2 would be good as a matter of principle, but it won't help the speed.
b) for realtime usage, don't use monster scripts that were made for offline processing. Reflect about what you need barely, then use the most simple approach to get there.

pitch.fr
20th September 2008, 21:13
ok, thanks for your help Didée once more :)

well I love watching movies with LSF/Ulevels/GrainFactory/DDCC :D

that's why I got an o/c Q6600 in the first place :p

how would your blurry grain thingie work btw ? should I create a function out of it ?

Yeah, IanB told me I should understand all the scripts I'm using, and try to make an all-in-one to avoid wasting CPU.

thing is, it works fine as it is, except for that new GrainFactory(good name BTW :D )

ok back for some more benchmarks :

LSF/Ulevels on 4 threads
=88fps

+GrainFactor
=48 fps

+grain2_size=1
=55 fps

+temp_avg=0
=52 fps

+grain2_size=1 / temp_avg=0
=62 fps

I don't think I wanna turn off temporal smoothing, it doesn't look as good when it's disabled..

I'd like to try with grain2_size=1 but AVS gives me an error msg : "Clip's size must be the same (line 86)"

that's line 86 :

grainlayer = MaskedMerge(grainlayer1, grainlayer2, o.yv12lut(CENTER2MAX_BIAS), U=1,V=1)

I had to put back grain2_size=0.8....any idea of a fix please ? :thanks:

PS : anyway DDCC is the real CPU hog...when Haali will offer PS gamut conversion in his renderer, I will have plenty of CPU cycles available again :D

Sagekilla
20th September 2008, 23:19
Erm, do you use SetMTMode() or MT() by any chance? If you have a quadcore those would help..

pitch.fr
21st September 2008, 00:10
I do, don't worry ;)

IanB helped me to balance all these scripts nicely :)

MT("""
LimitedSharpenFaster(ss_x=1.1,ss_y=1.1,strength=40)
ULevels(preset="tv2pc")
GrainFactory(3,5,100,100)
""",4)

never managed to get SetMTMode working in ffdshow, though.

Sagekilla
21st September 2008, 05:23
If I'd wager a guess, that has to do with the fact that all the filters added to the ffdshow filter chain are probably added after the source filter. SetMTMode only likes being used when it's the first thing called, otherwise it borks if it's not the first to load.

Leak
21st September 2008, 16:27
If I'd wager a guess, that has to do with the fact that all the filters added to the ffdshow filter chain are probably added after the source filter. SetMTMode only likes being used when it's the first thing called, otherwise it borks if it's not the first to load.
That can be fixed by unchecking "Add ffdshow video source" and adding a call to ffdshow_source() at the appropriate place...

But it'll also need a call to Distributor() at the end, I suppose...

np: Digital Mystikz - Conference (Soul Jazz Records Singles 2006-2007 (Disc 1))

pitch.fr
21st September 2008, 16:42
thanks for clearing that up Leak :)

how would that take place in this script please ?

MT("""
LimitedSharpenFaster(ss_x=1.1,ss_y=1.1,strength=40)
ULevels(preset="tv2pc")
GrainFactory(3,5,100,100)
""",4)
Distributor()

Leak
21st September 2008, 17:04
how would that take place in this script please ?
Don't ask me, I'm using vanilla AviSynth 2.5.8 RCsomething... no need to use any multithreading when DVD decoding plus AviSynth filtering uses half a core on my Core 2 Quad...

np: Subway - 44110 (Soul Jazz Records Singles 2006-2007 (Disc 1))

pitch.fr
21st September 2008, 17:13
so it should added in the .avsi files ?

anyhow MT() works fine ;)

Didée
30th September 2008, 12:28
Regarding the "GrainFactory" script - I've had a funny time to find and address all the faults, cracks and wrong twists in that script. In a sense, the script as a whole is just ONE BIG BUG from start to end. :)
Probably that is why originally I had only posted it as a plain linear script, as a suggestion upon some user request (on neuron2's forum, iirc). Function-izing was not done by me ... ;)

A cleaned version will be posted shortly (today or tomorrow).

pitch.fr
30th September 2008, 12:49
sounds great, thanks Didee!

I just wish it wasn't sucking so much CPU, but well nothing's free in this world.....except for your scripts that is :D

if you can also try w/ AddGrain 1.0 please, coz the newer versions make crazy memory leaks in MT..

maybe you could also add a third level of grain ? but in realtime that would put my o/c Q6600 to its kness I guess...

from what I understand silver film grain has different shapes depending on the ISO it was shot on, as SoulHunter explained here : http://forum.doom9.org/showpost.php?p=841700&postcount=12

I think you said BlockBuster() might be a better candidate than AddGrain() because it's luma aware, and can change shapes accordingly ?

to me the reference for grain is 21 grams in HD.....it was shot on very high ISO and the grain is simply mindblowing :eek:

it's hard to make proper stills of grainy movies, but you get the idea....the picture is fully deblocked and looks true to life :

http://pix.nofrag.com/2/7/8/0262b5fd390c82e80db20fe8d110ctt.jpg (http://pix.nofrag.com/2/7/8/0262b5fd390c82e80db20fe8d110c.html)

http://pix.nofrag.com/7/f/b/17586c8519de6e50e6faf1834f770tt.jpg (http://pix.nofrag.com/7/f/b/17586c8519de6e50e6faf1834f770.html)

Didée
1st October 2008, 12:25
Using 3 different grain characteristics for dark/medium/bright would be an obvious thing to do. But that requires creating 3 layers, and subsequently using 2 times masked merge ... bad conditions for realtime usage on HD sources.

In clients like e.g. Vdub, I can *not* confirm any memory leakage. Not with AddGrain, and not with AddGrainC. (didn't try in ffdshow, though.)

Answer to all (PM'ed) questions about using PointResize: "No."

I think you said BlockBuster() might be a better candidate than AddGrain() because it's luma aware, and can change shapes accordingly ?
Can't remember to have said sth like that. Blockbuster isn't "luma aware", and it doesn't change shapes.

---

GrainFactory Changelog:

- function GrainFactory(clip last,...) {... o=last ... }

That's an outrageous construct, "Last" is not a free variable name.
(Spelled a curse, the creator will always line up in the longest and slowest queue until EOL.)
Changed to sane syntax, not using a "custom LAST"

- exposed *all* possible variables as parameters (instead only those that the function maker happend to like personally.)

- Fixed erroneous luma offset. (color=X -> color_yuv=X in BlankClip())

- Made correct versions for both MaskTools-v2 and old MaskTools-v1: GrainFactory_MT1 / GrainFactory_MT2()
MaskTools-v2 version is generally preferred. MaskTools-v1 version could be slightly faster (please test),
which might justify keeping the v1 version for realtime usage.

-- For the MaskTools-v1 version: Added possibility of internal padding. Target: multi-threading with MT().
When grainX_size=1.0, padding seems needed to not make MaskTools-v1 stumble over the dimensions of
the frame slices. (MaskTools-v2 don't have this problem.)

- using temp_avg now is slightly faster. (don't process chroma in TemporalSoften)

- changed brightness_bias from 0.0~2.0 float range to 0~200 int range.
On some systems, the mixing process did not work at all. (string() function could convert decimal point to
decimal comma, confusing lutxy, resulting in a black mask, resulting in grain-mixing not functioning:
grainlayer2 had no effect at all on affected systems)

- 2-step resizing was used for small grainsizes, where it should be used for big grainsizes. Fixed.

-- fixed wrong b/c coefficient calculation for 2-step resizing

- added possibility to use only grain1 (with brightness_bias=0) or only grain2 (with brightness_bias=256).

- more small internal changes


Script containing both MT1 and MT2 version of GrainFactory:
function GrainFactory_MT1(clip clp,int "grain1_strength",int "grain2_strength",int "grain1_sharpness",int "grain2_sharpness", float "grain1_size", float "grain2_size",
\ int "grain1_texture", int "grain2_texture", int "temp_avg", float "brightness_bias", int "ontop_grain",
\ int "modH", int "modV")
{

grain1_strength = default( grain1_strength, 8 ) # [ 0 - ???] strength of grain / for mid-tone areas
grain2_strength = default( grain2_strength, 15 ) # [ 0 - ???] strength of grain / for dark & bright areas
grain1_sharpness = default( grain1_sharpness, 66 ) # [ 0 - 100] sharpness of grain / for mid-tone areas (NO EFFECT when grain1_size=1.0 !!)
grain2_sharpness = default( grain2_sharpness, 66 ) # [ 0 - 100] sharpness of grain / for dark & bright areas (NO EFFECT when grain2_size=1.0 !!)
grain1_size = default( grain1_size, 1.00 ) # [0.5 - 4.0] size of grain / for mid-tone areas
grain2_size = default( grain2_size, 1.41 ) # [0.5 - 4.0] size of grain / for dark & bright areas
grain1_texture = default( grain1_texture, 0 ) # [ 0 - 100] strength of "material texture" appearance
grain2_texture = default( grain2_texture, 0 ) # [ 0 - 100] (in fact: 'bump' effect)
temp_avg = default( temp_avg, 0 ) # [ 0 - 100] percentage of noise's temporal averaging
brightness_bias = default( brightness_bias, 96 ) # [50 - 200] bias for dark|bright --> midtone fading
ontop_grain = default( ontop_grain, 0 ) # [ 0 - ???] additional grain to put on top of prev. generated grain

modH = default( modH, 0 ) # use "4" when using MT(splitvertical=TRUE)
modV = default( modV, 4 ) # needed for MT() (default:splitvertical=false) in combination with MaskTools v1.x


#-----------------------------------------------------------------------------------

ox0 = clp.width
oy0 = clp.height
padH = modH<1 ? 0 : (modH-(ox0%modH))%modH
padV = modV<1 ? 0 : (modV-(oy0%modV))%modV
o = padH==0&&padV==0 ? clp : clp.addborders(0,0,padH,padV)
ox = o.width()
oy = o.height()
sx1 = m4(ox/float(grain1_size))
sy1 = m4(oy/float(grain1_size))
sx1a = m4((ox+sx1)/2.0)
sy1a = m4((oy+sy1)/2.0)
sx2 = m4(ox/float(grain2_size))
sy2 = m4(oy/float(grain2_size))
sx2a = m4((ox+sx2)/2.0)
sy2a = m4((oy+sy2)/2.0)
b1 = grain1_sharpness/(-50.0) + 1.0
b2 = grain2_sharpness/(-50.0) + 1.0
b1a = b1/2.0
b2a = b2/2.0
c1 = (1.0-b1)/2.0
c2 = (1.0-b2)/2.0
c1a = (1.0-b1a)/2.0
c2a = (1.0-b2a)/2.0
t1a = string(grain1_texture)
t1b = string(100-grain1_texture)
t2a = string(grain2_texture)
t2b = string(100-grain2_texture)
tmpavg = temp_avg/100.0



#-----------------------------------------------------------------------------------

ADD_DIFF = "x y - 128 +"
BUMPKERNEL1 = T1a + " 0 0 0 " + T1b + " 0 0 0 0"
BUMPKERNEL2 = T2a + " 0 0 0 " + T2b + " 0 0 0 0"
CENTER2MAX_BIAS = "x 120 - abs "+string(brightness_bias)+" 100 / ^ 2.5 *"

#-----------------------------------------------------------------------------------

grainlayer1 = blankclip(o, width=sx1, height=sy1, color_yuv=$808080) .AddGrainC(grain1_strength, 0,0,0)

grainlayer1 = (grain1_texture==0)
\ ? grainlayer1
\ : yv12lutxy(grainlayer1,grainlayer1.DEdgeMask(0,255,0,0, BUMPKERNEL1,U=1,V=1), ADD_DIFF,U=1,V=1)

grainlayer1 = (grain1_size == 1.0 || sx1==ox && sy1==oy) ? grainlayer1
\ : (grain1_size > 1.75) ? grainlayer1.bicubicresize(sx1a,sy1a, b1a,c1a).bicubicresize(ox,oy, b1a,c1a)
\ : grainlayer1.bicubicresize(ox,oy, b1,c1)

#-----------------------------------------------------------------------------------

grainlayer2 = blankclip(o, width=sx2, height=sy2, color_yuv=$808080) .AddGrainC(grain2_strength, 0,0,0)

grainlayer2 = (grain2_texture==0)
\ ? grainlayer2
\ : yv12lutxy(grainlayer2,grainlayer2.DEdgeMask(0,255,0,0, BUMPKERNEL2,U=1,V=1), ADD_DIFF,U=1,V=1)

grainlayer2 = (grain2_size==1.0 || sx2==ox && sy2==oy) ? grainlayer2
\ : (grain2_size > 1.75) ? grainlayer2.bicubicresize(sx2a,sy2a, b2a,c2a).bicubicresize(ox,oy, b2a,c2a)
\ : grainlayer2.bicubicresize(ox,oy, b2,c2)

#-----------------------------------------------------------------------------------

grainlayer = (brightness_bias >255) ? grainlayer2
\ : (brightness_bias < 2) ? grainlayer1
\ : MaskedMerge(grainlayer1, grainlayer2, o.yv12lut(CENTER2MAX_BIAS), U=1,V=1)

grainlayer = (temp_avg==0) ? grainlayer
\ : grainlayer.mergeluma(grainlayer.temporalsoften(1,255,0,255,2), tmpavg)
grainlayer = (ontop_grain==0) ? grainlayer
\ : grainlayer.AddGrainC(ontop_grain,0,0)

#-----------------------------------------------------------------------------------

result = yv12lutxy(o, grainlayer, ADD_DIFF, U=2,V=2)
result = ox0==ox&&oy0==oy ? result : result.crop(0,0,-padH,-padV)

return( result)
}
#-----------------------------------------------------------------------------------
function m4(float x) {return( x<16?16:int(round(x/4.0)*4)) }
#===================================================================================



function GrainFactory_MT2(clip clp,int "grain1_strength",int "grain2_strength",int "grain1_sharpness",int "grain2_sharpness", float "grain1_size", float "grain2_size",
\ int "grain1_texture", int "grain2_texture", int "temp_avg", float "brightness_bias", int "ontop_grain" )
{

grain1_strength = default( grain1_strength, 8 ) # [ 0 - ???] strength of grain / for mid-tone areas
grain2_strength = default( grain2_strength, 15 ) # [ 0 - ???] strength of grain / for dark & bright areas
grain1_sharpness = default( grain1_sharpness, 66 ) # [ 0 - 100] sharpness of grain / for mid-tone areas (NO EFFECT when grain1_size=1.0 !!)
grain2_sharpness = default( grain2_sharpness, 66 ) # [ 0 - 100] sharpness of grain / for dark & bright areas (NO EFFECT when grain2_size=1.0 !!)
grain1_size = default( grain1_size, 1.00 ) # [0.5 - 4.0] size of grain / for mid-tone areas
grain2_size = default( grain2_size, 1.41 ) # [0.5 - 4.0] size of grain / for dark & bright areas
grain1_texture = default( grain1_texture, 0 ) # [ 0 - 100] strength of "material texture" appearance
grain2_texture = default( grain2_texture, 0 ) # [ 0 - 100] (in fact: 'bump' effect)
temp_avg = default( temp_avg, 0 ) # [ 0 - 100] percentage of noise's temporal averaging
brightness_bias = default( brightness_bias, 96 ) # [50 - 200] bias for dark|bright --> midtone fading
ontop_grain = default( ontop_grain, 0 ) # [ 0 - ???] additional grain to put on top of prev. generated grain


#-----------------------------------------------------------------------------------

o = clp
ox = o.width
oy = o.height
sx1 = m4(ox/float(grain1_size))
sy1 = m4(oy/float(grain1_size))
sx1a = m4((ox+sx1)/2.0)
sy1a = m4((oy+sy1)/2.0)
sx2 = m4(ox/float(grain2_size))
sy2 = m4(oy/float(grain2_size))
sx2a = m4((ox+sx2)/2.0)
sy2a = m4((oy+sy2)/2.0)
b1 = grain1_sharpness/(-50.0) + 1.0
b2 = grain2_sharpness/(-50.0) + 1.0
b1a = b1/2.0
b2a = b2/2.0
c1 = (1.0-b1)/2.0
c2 = (1.0-b2)/2.0
c1a = (1.0-b1a)/2.0
c2a = (1.0-b2a)/2.0
T1a = string(int(grain1_texture*1.28))
T1b = string(128-int(grain1_texture*1.28))
T2a = string(int(grain2_texture*1.28))
T2b = string(128-int(grain2_texture*1.28))
tmpavg = temp_avg/100.0

#-----------------------------------------------------------------------------------

BUMPKERNEL1 = T1a + " 0 0 0 " + T1b + " 0 0 0 0 128"
BUMPKERNEL2 = T2a + " 0 0 0 " + T2b + " 0 0 0 0 128"
CENTER2MAX_BIAS = "x 120 - abs "+string(brightness_bias)+" 100 / ^ 2.5 *"

#-----------------------------------------------------------------------------------

grainlayer1 = blankclip(o, width=sx1, height=sy1, color_yuv=$808080) .AddGrainC(grain1_strength, 0,0,0)

grainlayer1 = (grain1_texture==0)
\ ? grainlayer1
\ : mt_makediff(grainlayer1,grainlayer1.mt_edge(BUMPKERNEL1, 0,255,0,0, U=1,V=1 ), U=1,V=1)

grainlayer1 = (grain1_size == 1.0 || sx1==ox && sy1==oy) ? grainlayer1
\ : (grain1_size > 1.5) ? grainlayer1.bicubicresize(sx1a,sy1a, b1a,c1a).bicubicresize(ox,oy, b1a,c1a)
\ : grainlayer1.bicubicresize(ox,oy, b1,c1)

#-----------------------------------------------------------------------------------

grainlayer2 = blankclip(o, width=sx2, height=sy2, color_yuv=$808080) .AddGrainC(grain2_strength, 0,0,0)

grainlayer2 = (grain2_texture==0)
\ ? grainlayer2
\ : mt_makediff(grainlayer2,grainlayer2.mt_edge(BUMPKERNEL2, 0,255,0,0, U=1,V=1 ), U=1,V=1)

grainlayer2 = (grain2_size==1.0 || sx2==ox && sy2==oy) ? grainlayer2
\ : (grain2_size > 1.5) ? grainlayer2.bicubicresize(sx2a,sy2a, b2a,c2a).bicubicresize(ox,oy, b2a,c2a)
\ : grainlayer2.bicubicresize(ox,oy, b2,c2)

#-----------------------------------------------------------------------------------

grainlayer = (brightness_bias >255) ? grainlayer2
\ : (brightness_bias < 1) ? grainlayer1
\ : mt_merge(grainlayer1, grainlayer2, o.mt_lut(CENTER2MAX_BIAS), U=1,V=1)

grainlayer = (temp_avg==0) ? grainlayer
\ : grainlayer.mergeluma(grainlayer.temporalsoften(1,255,0,255,2), tmpavg)
grainlayer = (ontop_grain==0) ? grainlayer
\ : grainlayer.AddGrainC(ontop_grain,0,0,0)

#-----------------------------------------------------------------------------------

result = o.mt_makediff(grainlayer, U=2,V=2)

return( result )
}

pitch.fr
1st October 2008, 12:35
oh cool, thanks! gonna try it right now :)

so by "no" you mean PointResize won't show any visible difference over spline/bicubic ?

w/ AddGrainC 1.1(newer versions make crazy memory leaks in ffdshow) and this test script in VDUB :

SetMemoryMax(512) # Adjust value as required

ColorBars(1280, 720, pixel_type="YV12") # Size and pixel type as appropriate

# Insert Code under test here
MT("GrainFactory(3,5,100,100)",4)

# Throw away most of the image we are not testing VD's i/o
Crop(0, 0, 16, 16)


MT("GrainFactory(3,5,100,100)",4)
=141 with AddGrain 1.0 | 142 w/ AddGrainC 1.1(139 if changing AddGrain to AddGrainC in the script)

MT("GrainFactory_MT1(3,5,100,100)",4)
=208

MT("GrainFactory_MT2(3,5,100,100)",4)
=233

maybe I'll change grainsize2 to something smaller ?

but anyhow benchmarks look promising :eek:

Didée
1st October 2008, 13:22
Whoops. 228/141 is a 60% speedup ... not bad.

Using Pointresize for size-sampling of the added grain pretty much defeats the purpose of what the sampling is supposed to visually achieve. That's what I meant with "no".

Two times the exactly-same grain pattern (grainsize 1.75) ... left: Point / right: sharp bicubic :

http://img149.imageshack.us/img149/8928/grainpointbicubicjh8.th.png (http://img149.imageshack.us/my.php?image=grainpointbicubicjh8.png)http://img149.imageshack.us/images/thpix.gif (http://g.imageshack.us/thpix.php)

Blargh. Pointresize looks ugly. If you're in need for speed so badly, then you can simply use a generic noise generator just as well.

pitch.fr
1st October 2008, 13:38
wow, ok I'll just set it to spline36 then :D

you've changed the grain sizes from 1.15/0.8 to 1.0/1.41 to avoid one resize....but wouldn't that look any better to set grainsize2 to 0.8 or 0.65 ? I'll experiment :)

spline36 GrainF is 120fps, spline36 GrainF_MT2 is 220 :)

my full LSF/Ulevels/GrainF/convertrgb32 script is 52 fps w/ or w/o AddGrain, 37 w/ GrainF and 43 w/ GrainF_MT2(LSF and all GrainF in spline)

Didée
1st October 2008, 18:38
Grainsize1/2 is not a supersampling factor in the way like e.g. ss_x/y in LimitedSharpenFaster. Instead it refers to what the name says: to the size of grain. With grainsize=1.0, you get that kind of grain like when using AddGrain() directly. With smaller numbers like 0.8, you get smaller grain particles. With bigger numbers like 2.0, you get bigger grain particles.
Since film grain usually appears more coarse in dark areas, compared to more fine grain in the brighter midtones, the defaults are now set to reflect just that. Previously the sizes were reversed: very fine grain in the darks & brights, more coarse in the midtones.
If you liked the old settings more ... set the parameters to your liking. That's what parameters are for.


LSF and all GrainF in spline
Question to you:
Please explain what are the benefits of modding GrainFactory to use SplineXResize instead of BicubicResize. I'm curious to hear the reasoning. :)

pitch.fr
1st October 2008, 18:49
oh OK, makes sense....they were reversed in the first version, but 1.4 seems too BIG for bright areas(eg open sky)

I will check on my DLP pj this evening coz on my 19" CRT I got a hard time seeing 0.65 grain size :D

Ideally we'd need 3 grain sizes I think for supreme grainness :)

well you just showed me that pointresize is terribly blocky compared to bicubic.
and bicubic(except in -1.0) is known not to be as sharp as spline36/lanczos4 ?
I got plenty of horse power, and I want VERY sharp grain so it doesn't ruin the sharpness of my HD movies :)

actually my pj has a full mineral glass lens so the grainier the merrier...it gives a crazy analog feel in 24Hz with Reclock :eek:

PS : I don't really see what the "grain_texture" do, but no biggy

CruNcher
2nd October 2008, 01:23
Hmm Didée wouldn't it possible to record the original Grain Layer using mvdegrain and adding it back @ playback (instead of creating a artificial one) ?
so degrain with mvdegrain use the difference between the degrained and grained and regrain the compressed one with it or add it back @ playback, this way also the temporal information of the grainlayer could be preserved i think. Of course the recording of the whole layer wouldn't be necessary but i guess this way it could be easier to analyze different film stocks visual results and trying to match them as close as possible randomly generated. The final dream would be a selection box in ffdshow where you select the GrainLayer you want out of a Film Stock like Database :)

Sagekilla
2nd October 2008, 03:17
Cruncher, I believe that would be possible.. It'd be like this, in pseudocode:

To encoder
source = last
degrain = source.MVDegrain()

grain = Overlay(source,degrain,mode="difference")
GrainFile(mode="output", file="grain.info")

output = degrain
Return(output)


From Decoder

source = last
GrainInfo = GrainFile(mode="input", file="grain.info")


You'd have to find a way to maybe do some averaging over a 5 second sample (with scenecut detection, cause grain might change then) and then include a little bit of data on how to vary the data for each new frame in that 5 second interval, so it's not static grain. Otherwise if you did it on a per frame basis, the file would be HUGE

That's what I came up in my head, probably not the best idea but it could work. You'd just have to deal with -massive- information files that detail how the grain is supposed to look like. If it came to that, I'd just go with FGO and include a few kbps of data on how to add grain back inside of the stream. Sort of like Spectral Band Replication for AAC.

Didée
2nd October 2008, 12:02
@ CruNcher: We're talking about burning wood here. You are talking about vacuum energy. Yes, vacuum energy is much more fertile, no doubt. Problem is that it hasn't been implemented yet. Burning wood works right here, right now.


@ pitch.fr: I used bicubic for good resons. Look at this chart:

http://img522.imageshack.us/img522/4250/grainsamplingvariantssv6.th.png (http://img522.imageshack.us/my.php?image=grainsamplingvariantssv6.png)http://img522.imageshack.us/images/thpix.gif (http://g.imageshack.us/thpix.php)

In brief: Using any other resizer in GrainFactory() ...

... offers no benefit at all

... is slower (though not much)

... breaks the "sharpness" settings (they're not functional anymore, then)


When will people finally learn that amongst Avisynth's resizers there are no "bad / good / better / best" ones. A 10mm spanner is neither better nor worse than a 16mm spanner. The trick is you have to use the one that fits to the screw.

pitch.fr
2nd October 2008, 12:22
are you sure we can draw so many conclusions from your test charts :confused:

you use "bicubicresize" in your original script, so is it -0.6/-0.75/-1 ?

is it also a bad idea to upgrade LSF with spline36 ?

anyway, I guess you agree that the same grainsize for bright and dark areas is a problem ? you don't wanna have huge grains in open skies for instance :confused:

and what is "grain_texture" supposed to do ? :D

Didée
2nd October 2008, 13:25
are you sure we can draw so many conclusions from your test charts

The picture is only for visualation, and to proof the theory. Think about what is actually processed in the script: a greyscale map with per-pixel noise (zero mean gaussian noise). The point is that there is basically no correlation between any of the generated noise pixels. When there is no spatial correlation, then the "advantages" of the higher-order sampling algorithms don't bite anything. Bicubic deals kind of perfectly for the given purpose, AND it has the advantage that the "crispness" can be controlled via the bicubc b+c parameters. Ringing is no issue at all in this context - you can't see any ringing in the noise.
The chart should give the idea that whatever any of the other resizers do achieve, can be achieved by bicubic just as well - and even *more*, if you're after "sharp" grain.

Of course, if you feel better when filling your lawnmower with super-duper-$$$$-120octane gasoline, then by all means do so. My lawnmower gets only standard gasoline, and it runs very well.


you use "bicubicresize" in your original script, so is it -0.6/-0.75/-1 ?

bicubic( 1.0,0.00) -> grainX_sharpness = 0
bicubic( 0.0,0.50) -> grainX_sharpness = 50
bicubic(-0.5,0.75) -> grainX_sharpness = 75
bicubic(-1.0,1.00) -> grainX_sharpness = 100


is it also a bad idea to upgrade LSF with spline36 ?
For LSF it's another story. There, it's actual image content that gets resized, with lots of correlation between pixels. Therefore, for LSF the more specific sampling algorithms make a difference indeed. (unless you use ss_x = ss_y = 1.0, since then the internal resizer is not used at all.)


and what is "grain_texture" supposed to do ?
It's meant to make realtime usage impossible, and to force global warming.

Soulhunter
2nd October 2008, 14:48
Good to see this function alive again! Remembers me my Gain generation procedure in Photoshop... Gaussian Noise -> Gaussian Blur -> UnsharpMask (With higher blur radius for low luma parts, lower blur radius for high luma parts, more sharpening for high luma parts, less sharpening for teh low luma parts etc...). Hmm, how about a Grain3 param for this function? Having separate params for low, mid and high luma would be advantageous imo! :]


Bye

pitch.fr
2nd October 2008, 15:17
bicubic( 1.0,0.00) -> grainX_sharpness = 0
bicubic( 0.0,0.50) -> grainX_sharpness = 50
bicubic(-0.5,0.75) -> grainX_sharpness = 75
bicubic(-1.0,1.00) -> grainX_sharpness = 100
ok, thanks for the explanation :cool:

I didn't realize the sharpness setting was playing around with the bicubic coeff :rolleyes:

For LSF it's another story. There, it's actual image content that gets resized, with lots of correlation between pixels. Therefore, for LSF the more specific sampling algorithms make a difference indeed. (unless you use ss_x = ss_y = 1.0, since then the internal resizer is not used at all.)
right!! well I used to run ss1.1, but it was a tad too soft to my taste.....I like very sharp details, even if that means jaggies once in a while....and with ss1.1 there was indeed a CPU% difference, that doesn't exist anymore with ss1.0 :rolleyes:

It's meant to make realtime usage impossible, and to force global warming.
geez, I'm gonna stay far away from it then :D

how about a Grain3 param for this function? Having separate params for low, mid and high luma would be advantageous imo!

agreed, that would be the icing on the cake http://forum.slysoft.com/images/smilies/agreed.gif

foxyshadis
3rd October 2008, 06:24
Hmm Didée wouldn't it possible to record the original Grain Layer using mvdegrain and adding it back @ playback (instead of creating a artificial one) ?
so degrain with mvdegrain use the difference between the degrained and grained and regrain the compressed one with it or add it back @ playback, this way also the temporal information of the grainlayer could be preserved i think. Of course the recording of the whole layer wouldn't be necessary but i guess this way it could be easier to analyze different film stocks visual results and trying to match them as close as possible randomly generated. The final dream would be a selection box in ffdshow where you select the GrainLayer you want out of a Film Stock like Database :)

It's actually pretty easy to keep track of grain removed. Just record the MSE of every denoised frame to the original with Compare, feed that with some arithmetic manipulation into AddGrain. Had it working, but it's terribly slow since it has to reinitialize addgrain for every frame. Never implemented it in Avisynth because I really wanted to get it into ffdshow, but I lost steam a long time ago. Eventually lost the script when a driver crash wiped out my avsp session. >( That's the breaks sometimes.

As for addgrainC, I don't see how it could be leaking memory unless using it with MT somehow causes its pointers to be overwritten. I'd expect it to crash a lot in that case.

pitch.fr
3rd October 2008, 09:39
As for addgrainC, I don't see how it could be leaking memory unless using it with MT somehow causes its pointers to be overwritten. I'd expect it to crash a lot in that case.
hi foxy,

here's AddGrainC 1.1 with 4 thread in ffdshow(1st value is RAM usage, second one is virtual memory usage) :

http://pix.nofrag.com/8/c/3/bf6492c0b8afaa9538e9f8e946ddd.png

here's AddGrainC 1.4 with 4 thread in ffdshow on the same exact movie, with the same exact config :

http://pix.nofrag.com/7/d/c/c51f3396c096bcfc9e72ac3d5a2e5.png

if I use SetMemoryMax(256), 1.0/1.1 will obey but newer versions won't

with 8 threads, it crashes in VDUB for me(it's bypassed), uses +1.6Gb of RAM in ffdshow/MPC..then crashes MPC(also tried with other players)

tests were conducted on XP SP3, with MT 0.7 and the latest ffdshow/MPC on a Q6600

Didée
3rd October 2008, 14:38
@ foxyshadis: That's too simplistic for what CruNcher has in mind. That way you'll "only" get noise with varying sigma per frame, but still it'll be the "same kind" of noise all over the frame. The idea, however, was to have noise varying acc. to local brighness, and/or acc. to local amount of R/G/B component. That's a bit more complex.

pitch.fr
3rd October 2008, 14:55
any chance you could add a third layer please Didee ?

so we get real simulation of dark/mid/bright areas grain :)

I'm about to get myself a new DFI mobo to o/c my Q6600 even further, so bring it on :D

Didée
3rd October 2008, 21:47
Here you go. Look into the default() section to inform yourself about the parameters - I decided to shorten parameters' names for less typing effort when using named values in the function call. The zone transitions get specified by th1/th2 and th3/th4.

The structure of script & parameters should be kind of obvious ... in case you come to the idea of needing "dark dark" + "bright dark" and/or "dark/medium/bright medium" etc. zones, you can do it on your own. ;)

function GrainFactory3(clip clp,int "g1str",int "g2str",int "g3str",int "g1shrp",int "g2shrp",int "g3shrp",
\ float "g1size", float "g2size", float "g3size",
\ int "g1tex", int "g2tex", int "g3tex", int "temp_avg", int "ontop_grain",
\ int "th1", int "th2", int "th3", int "th4")
{

g1str = default( g1str, 13 ) # [ 0 - ???] strength of grain / for dark areas
g2str = default( g2str, 15 ) # [ 0 - ???] strength of grain / for midtone areas
g3str = default( g3str, 25 ) # [ 0 - ???] strength of grain / for bright areas
g1shrp = default( g1shrp, 60 ) # [ 0 - 100] sharpness of grain / for dark areas (NO EFFECT when g1size=1.0 !!)
g2shrp = default( g2shrp, 66 ) # [ 0 - 100] sharpness of grain / for midtone areas (NO EFFECT when g2size=1.0 !!)
g3shrp = default( g3shrp, 80 ) # [ 0 - 100] sharpness of grain / for bright areas (NO EFFECT when g3size=1.0 !!)
g1size = default( g1size, 1.50 ) # [0.5 - 4.0] size of grain / for dark areas
g2size = default( g2size, 1.20 ) # [0.5 - 4.0] size of grain / for mid-tone areas
g3size = default( g3size, 0.90 ) # [0.5 - 4.0] size of grain / for bright areas
g1tex = default( g1tex, 0 ) # [ 0 - 100] strength of "material texture" appearance
g2tex = default( g2tex, 0 ) # [ 0 - 100] (in fact: 'bump' effect)
g3tex = default( g3tex, 0 ) # [ 0 - 100] for dark / midtone / bright grain
temp_avg = default( temp_avg, 0 ) # [ 0 - 100] percentage of noise's temporal averaging
th1 = default( th1, 24 ) # start of dark->midtone mixing zone
th2 = default( th2, 56 ) # end of dark->midtone mixing zone
th3 = default( th3, 128 ) # start of midtone->bright mixing zone
th4 = default( th4, 160 ) # end of midtone->bright mixing zone
ontop_grain = default( ontop_grain, 0 ) # [ 0 - ???] additional grain to put on top of prev. generated grain


#-----------------------------------------------------------------------------------

o = clp
ox = o.width
oy = o.height
sx1 = m4(ox/float(g1size))
sy1 = m4(oy/float(g1size))
sx1a = m4((ox+sx1)/2.0)
sy1a = m4((oy+sy1)/2.0)
sx2 = m4(ox/float(g2size))
sy2 = m4(oy/float(g2size))
sx2a = m4((ox+sx2)/2.0)
sy2a = m4((oy+sy2)/2.0)
sx3 = m4(ox/float(g3size))
sy3 = m4(oy/float(g3size))
sx3a = m4((ox+sx3)/2.0)
sy3a = m4((oy+sy3)/2.0)

b1 = g1shrp/(-50.0) + 1.0
b2 = g2shrp/(-50.0) + 1.0
b3 = g3shrp/(-50.0) + 1.0
b1a = b1/2.0
b2a = b2/2.0
b3a = b3/2.0
c1 = (1.0-b1)/2.0
c2 = (1.0-b2)/2.0
c3 = (1.0-b3)/2.0
c1a = (1.0-b1a)/2.0
c2a = (1.0-b2a)/2.0
c3a = (1.0-b3a)/2.0
T1a = string(int(g1tex*1.28))
T1b = string(128-int(g1tex*1.28))
T2a = string(int(g2tex*1.28))
T2b = string(128-int(g2tex*1.28))
T3a = string(int(g3tex*1.28))
T3b = string(128-int(g3tex*1.28))
th1str = string(th1)
th2str = string(th2)
th3str = string(th3)
th4str = string(th4)
tmpavg = temp_avg/100.0

#-----------------------------------------------------------------------------------

BUMPKERNEL1 = T1a + " 0 0 0 " + T1b + " 0 0 0 0 128"
BUMPKERNEL2 = T2a + " 0 0 0 " + T2b + " 0 0 0 0 128"
BUMPKERNEL3 = T3a + " 0 0 0 " + T3b + " 0 0 0 0 128"

#-----------------------------------------------------------------------------------

grainlayer1 = blankclip(o, width=sx1, height=sy1, color_yuv=$808080) .AddGrainC(g1str, 0,0,0)

grainlayer1 = (g1tex==0)
\ ? grainlayer1
\ : mt_makediff(grainlayer1,grainlayer1.mt_edge(BUMPKERNEL1, 0,255,0,0, U=1,V=1 ), U=1,V=1)

grainlayer1 = (g1size == 1.0 || sx1==ox && sy1==oy) ? grainlayer1
\ : (g1size > 1.5) ? grainlayer1.bicubicresize(sx1a,sy1a, b1a,c1a).bicubicresize(ox,oy, b1a,c1a)
\ : grainlayer1.bicubicresize(ox,oy, b1,c1)

#-----------------------------------------------------------------------------------

grainlayer2 = blankclip(o, width=sx2, height=sy2, color_yuv=$808080) .AddGrainC(g2str, 0,0,0)

grainlayer2 = (g2tex==0)
\ ? grainlayer2
\ : mt_makediff(grainlayer2,grainlayer2.mt_edge(BUMPKERNEL2, 0,255,0,0, U=1,V=1 ), U=1,V=1)

grainlayer2 = (g2size==1.0 || sx2==ox && sy2==oy) ? grainlayer2
\ : (g2size > 1.5) ? grainlayer2.bicubicresize(sx2a,sy2a, b2a,c2a).bicubicresize(ox,oy, b2a,c2a)
\ : grainlayer2.bicubicresize(ox,oy, b2,c2)

#-----------------------------------------------------------------------------------

grainlayer3 = blankclip(o, width=sx3, height=sy3, color_yuv=$808080) .AddGrainC(g3str, 0,0,0)

grainlayer3 = (g3tex==0)
\ ? grainlayer3
\ : mt_makediff(grainlayer3,grainlayer3.mt_edge(BUMPKERNEL2, 0,255,0,0, U=1,V=1 ), U=1,V=1)

grainlayer3 = (g3size==1.0 || sx3==ox && sy3==oy) ? grainlayer3
\ : (g3size > 1.5) ? grainlayer3.bicubicresize(sx3a,sy3a, b3a,c3a).bicubicresize(ox,oy, b3a,c3a)
\ : grainlayer3.bicubicresize(ox,oy, b3,c3)

#-----------------------------------------------------------------------------------

grainlayer = grainlayer1.mt_merge(grainlayer2, o.mt_lut("x "+th1str+" < 0 x "+th2str+" > 255 255 "+th2str+" "+th1str+" - / x "+th1str+" - * ? ?"), U=1,V=1)
\ .mt_merge(grainlayer3, o.mt_lut("x "+th3str+" < 0 x "+th4str+" > 255 255 "+th4str+" "+th3str+" - / x "+th3str+" - * ? ?"), U=1,V=1)

grainlayer = (temp_avg==0) ? grainlayer
\ : grainlayer.mergeluma(grainlayer.temporalsoften(1,255,0,255,2), tmpavg)
grainlayer = (ontop_grain==0) ? grainlayer
\ : grainlayer.AddGrainC(ontop_grain,0,0,0)

#-----------------------------------------------------------------------------------

result = o.mt_makediff(grainlayer, U=2,V=2)

return( result )

#-----------------------------------------------------------------------------------
function m4(float x) {return( x<16?16:int(round(x/4.0)*4)) }
#===================================================================================}

pitch.fr
3rd October 2008, 21:50
awesome Didee, you're the man http://forum.slysoft.com/images/smilies/agreed.gif

gonna try it right now with :

GrainFactory3(6,5,3,100,100,100,1.50,1,0.70,0,0,0,0,0,24,56,128,160)

EDIT : it looks damn good, thank you so much http://forum.slysoft.com/images/smilies/rock.gif

it brings back the grain h264 stole from us :D

MT("GrainFactory_MT2(3,5,100,100,1.0,0.7,0,0,0,96,0)",4)
=152fps

MT("GrainFactory3(7,5,3,100,100,100,1.30,1,0.70,0,0,0,0,0,24,56,128,160)",4)
=112

MT("GrainFactory(3,5,100,100)",4) (1.15/0.8 default sizes and no temporal thingie)
=119

pitch.fr
3rd October 2008, 22:20
apparently you need to add these lines at the end of the GrainF3 script to be standalone from the MT1/MT2 :

#-----------------------------------------------------------------------------------
function m4(float x) {return( x<16?16:int(round(x/4.0)*4)) }
#===================================================================================

Didée
3rd October 2008, 22:27
Ah yes, the modulo function accidentially was left out during copy/paste. Thanks for noting. It's now included in the script above.

pitch.fr
3rd October 2008, 22:35
I think I've edited after you read it, so here it is again :

GrainF3 gives less FPS than the original GrainF script, yet it's fully usable in realtime on my 3.4Ghz Q6600

apparently there's 2 different problems with using an AVS script in ffdshow :
1-fps, of course
2-but more importantly latency.....and the latency seems far better with MT2 than MT1 :)

my CPU load is 55% with a 1.78 20mbit 720p h264 movie, and LSF/Ulevels/GrainF + gamut conversion through AVIshader + ConvertToRGB32 :cool:

mikeytown2
4th October 2008, 01:23
I added GrainFactory to the wiki http://avisynth.org/mediawiki/External_filters#Effects
Should I change the link to point to GrainFactory3? Does it need a new entry in the wiki? Some guidance would be nice.


GrainFactory3 got it's closing "}" commented out

pitch.fr
4th October 2008, 10:15
I think I like the same grain size for dark and mid areas(1), and then finer grain for bright ones(0.7)

big grain sizes for dark ones look rather distracting :D

at least it's good that we can set different strengths http://forum.slysoft.com/images/smilies/agreed.gif

Soulhunter
4th October 2008, 11:15
@pitch.fr

How about larger diameter but less sharpness, maybe also lower strength? To my experience grain often looks a bit blurry in dark areas... :]

pitch.fr
4th October 2008, 14:23
good point, but then it makes the picture a bit blurry, doesn't it ?

I think I might settle down to

GrainFactory3(6,5,3,100,100,100,1,1,0.70,0,0,0,0,0,24,56,128,160)

I wonder if that'd be easily doable to have GrainF_MT2 using the same settings for dark/mid, and separate ones for bright ?

Didée
4th October 2008, 15:32
More and more it deteriorates to a foolish act ... you realize that with grainsizeX=1.0 you get exactly the output of AddGrain()? A few more such requests, and it'll be a huge placebo function that does the same as a simple AddGrain() would do ... :rolleyes:

With th1=0 and th2=0, this mod (http://www.mediafire.com/?ks02mmfmiaw) uses layer2 for both dark & midtones.

Know why usually I don't react to any such "realtime function" requests? Most of the time it creates "more simplification" and "even more simplification", up to the point where the sophisticated idea you once had started with has turned into a thing so primitive and ugly that one should be ashamed about. And truly, we're on that way here.

pitch.fr
4th October 2008, 16:41
cool, thanks again Didee!

well the whole idea is to have big grain in dark stuff, and very fine in bright ones.

maybe a 50/50 would be fine after all ? or at least better than having the same size in dark/bright, and different in mid.

but I've done my tests on a 19" CRT mainly, I'll look more into it this evening on the pj.

I'm not asking for 50/50 stuff because it sucks too much ressources, but simply coz huge grain in dark areas looks a bit distracting and very small one a bit weak.

I'll try to play around w/ the sharpness as SoulHunter said.

and this script is great, even for non realtime use I think :cool:

Didée
4th October 2008, 19:13
It's not clear what exactly you mean with 50/50 stuff. Please be more verbose on this.

What can be done with the function as-is, one can have a 50/50 mix of "grain1" and "grain2" for the combined dark+midtone area, with "grain3" blended in for bright areas: set "th1=-9999,th2=9999", and th3/th4 as usual.

pitch.fr
4th October 2008, 19:55
oops, sorry I just meant to have big grain from 0% to 50% brightness, then smaller grain from 50% to 100%

but if I read you correctly, there's a mixing zone at this point ?

the scale goes from 0 to 200/256 ?

maybe it'd be good to have big grain up to 40%, mixing zone up to 60%, and fine grain up to 100% then ?

Didée
4th October 2008, 21:20
Oh, that simple indeed. With ~3a, you got all of these at your hands already. Basic arithmetics.


oops, sorry I just meant to have big grain from 0% to 50% brightness, then smaller grain from 50% to 100%
is achieved with GrainFactory3a(th1=0,th2=0,th3=127,th4=128) grain1 settings do not apply. grain2 settings are used for the dark half, grain3 settings for the bright half.

-----

maybe it'd be good to have big grain up to 40%, mixing zone up to 60%, and fine grain up to 100% then ?
is achieved with GrainFactory3a( th1=0,th2=0, th3=255*40/100, th4=255*60/100) # 40%~60% for PC-ranged (0-255) input

GrainFactory3a( th1=0,th2=0, th3=16+(235-16)*40/100, th4=16+(235-16)*60/100) # 40%~60% for TV-ranged (16-235) input grainX settings as above.

-----

the scale goes from 0 to 200/256 ?
The "scale" is 0 to 255. The ranges tell at which luma value the zones start or end. It's not that hard.

pitch.fr
4th October 2008, 21:41
cool, thanks..gonna give it a shot!

well BT709 YV12 is also 16-235 I think ?!

only the new xvYCC(Extended Gamut YCC) is 0-255.....but there isn't any xvYCC material available yet ?!

OK, it's the regular 0-255, I thought AVS was using some other scale.......silly me :rolleyes:

well ideally doing 2 types of grain was also a way to save CPU time, but I guess using ~3a is the same as using ~_MT2 from what you told me :p

Didée
4th October 2008, 22:45
well ideally doing 2 types of grain was also a way to save CPU time, but I guess using ~3a is the same as using ~_MT2 from what you told me :p
Of course they're not the same. The old one used a hyperbola-like mask, centered around Y=120. The new use sliding linear ranges.

Seriously, it starts getting boring. It's all about creating grain a bit like this or a bit like that, then using luma-based masks to combine them. That's rather basic stuff, and more than enough examples have been given how to do it. If more groundbreaking ideas come up how some noise could be thrown at a source, make the exercise. (If LUT is too strange, then levels() deals very well too.)

For the moment, I consider this case closed. Too trivial to put more (of my) time into it.

pitch.fr
4th October 2008, 23:05
muchas gracias for your awesome scripts and your swift replies Didee! http://forum-images.hardware.fr/images/perso/haha%20jap.gif

pitch.fr
30th October 2008, 21:36
just a lil'bump, these scripts from Didee look so friggin' awesome on HD in ffdshow..that'd be too bad that they'd fall down in the forum archives :eek:

adding LSF is literally the icing on the cake :D

Didée 4 President, now!

Adub
31st October 2008, 01:42
Why don't you add it to the wiki then? I'd do it myself, but I am getting swamped with midterms right now.

McCauley
1st February 2009, 23:08
Why don't you add it to the wiki then? I'd do it myself, but I am getting swamped with midterms right now.
Done (http://avisynth.org/mediawiki/GrainFactory3).

Regards
McCauley

McCauley
12th March 2009, 17:38
Is there a specific reason why Grainfactory3 uses integer values for the grain strength instead of float?
How about defining a default multiplier for the strength values? Since the default ratio seems to be quite good it would be:
g1str=X
g2str=X*1.15
g3str=X*1.66

Regards
McCauley