View Full Version : How to apply strong blur
MysteryX
17th May 2017, 06:33
Let's say I have a mask that I binarize. Now I want to soften the border, say, blur and spread to 15 pixels all around.
How do I do that? Blur has a maximum value of 1.58, and I'd have to call it 10 times in a row to get a decent result. GBlur has a maximum value of 8 and doesn't help much more.
What better option is there?
raffriff42
17th May 2017, 07:28
I used VariableBlur (http://avisynth.nl/index.php/External_filters#Blurring)'s GaussianBlur for this kind of thing, until it crashed too once often, at which point (2013) I hacked up this serviceable workalike: basically downsize-blur-upsize.##################################
### Gaussian blur - simplified version.
##
## @ rad - blur radius
##
function GaussianBlur2(clip C, float rad)
{
rad = Max(0.0, rad)
w0 = C.Width
h0 = C.Height
w1 = Round(w0/rad)
h1 = Round(h0/rad)
B = C.BilinearResize(
\ Min(Max(4, w1 + (w1 % 2)), w0),
\ Min(Max(4, h1 + (h1 % 2)), h0))
B = B.Blur(1.0).Blur(1.0)
return (rad<0.01) ? C
\ : (rad<1.2) ? C.Blur(rad)
\ : (B.Width>8 && B.Height>8)
\ ? B.GaussResize(w0, h0, p=19)
\ : B.BilinearResize(w0, h0)
}
dipje
17th May 2017, 08:05
I know it's vapoursynth not avisynth, but the bilateral plugin has a gaussian blur with variable radius .
MysteryX
17th May 2017, 19:23
uh... there's no simple answer to this so simple task!?
Any reason why Blur is limited to 1.58 and can't do 20 or 30?
dipje
17th May 2017, 20:39
I'm just guessing , but I guess because it's implemented as a 3x3 or 5x5 convolution? (Like I said , guessing ). Means you can never go higher in radius or past a certain mix of the pixels.
raffriff42
17th May 2017, 21:03
A little GaussianBlur2 (https://forum.doom9.org/showthread.php?p=1807156#post1807156) demo:
https://www.dropbox.com/s/ldaaqy799q21kgg/GaussianBlur2-01.jpg?raw=1
https://www.dropbox.com/s/evmdpz6fzmywqiv/GaussianBlur2-03.jpg?raw=1
https://www.dropbox.com/s/o65lpa6rheoc0nf/GaussianBlur2-06.jpg?raw=1
https://www.dropbox.com/s/x65kxqhd1b6w4lw/GaussianBlur2-20.jpg?raw=1
https://www.dropbox.com/s/b55xg1g202fwuc4/GaussianBlur2-50.jpg?raw=1
wonkey_monkey
17th May 2017, 21:10
Not bad... but the blockiness is pretty evident.
uh... there's no simple answer to this so simple task!?
Try Variableblur as suggested by raffriff42 - I've never known it to crash.
Or you can try boxblur:
http://rationalqm.us/boxblur/boxblur.html
If you want a reasonable approximation to a gaussian blur, call it 3 or 4 times.
MysteryX
17th May 2017, 21:43
This script is working just fine actually
Katie Boundary
21st May 2017, 00:33
uh... there's no simple answer to this so simple task!?
Any reason why Blur is limited to 1.58 and can't do 20 or 30?
3x3 convolution matrix. A radius of 1.0 is equivalent to the classic gaussian matrix:
1 2 1
2 4 2
1 2 1
A radius of 1.58 is equivalent to this:
1 1 1
1 1 1
1 1 1
...which is the flattest blur that you can get in a 3x3 matrix.
manono
21st May 2017, 00:45
I usually use MedianBlur (http://avisynth.nl/index.php/MedianBlur) for blurring, usually to create feathering for masks.
vcmohan
21st May 2017, 06:35
FQPlus plugin has a function F2QBlur. One can either use built in constructs or specify by a text file desired blur coefficients. You can get as much blur as you want.
raffriff42
21st May 2017, 20:44
I see the problem with my script. Thanks for the suggestions, will look them all over ASAP.
raffriff42
22nd May 2017, 01:37
I tried variableblur/GaussianBlur, virtualdub/BoxBlur, my script above, and V.C.Mohan's modPlus/GBlur.
After comparing with equal apparent blur (at various strengths),
* GaussianBlur looks very good, but it takes a LONG time to initialize each new blur radius
* BoxBlur looks also looks very good, maybe a little better.
* my GaussianBlur2 script did look blocky, as mentioned, but it has been fixed! (see post #2 (https://forum.doom9.org/showthread.php?p=1807156#post1807156), changes in blue)
* GBlur is limited to a small blur radius and cannot be tested at blur radius > 8.
All in all, I like BoxBlur (edit: caveats below). It only works in RGB mode, but the other filters look better in RGB anyway.
Here's the function I used to port it to AviSynth:# http://rationalqm.us/boxblur/boxblur.html
## You need to set this global variable to something that works
global pathVdub = "E:\VirtualDubPlugins\"
##################################
### fast box, triangle or cubic (gaussian) blur.
##
## @ C - RGB only.
## @ power - 0=disabled; 1=box; 2=quadratic; 3=cubic (gaussian; default)
##
function VD_BoxGaussBlur(clip C, int radius, int "power", float "opacity")
{
LoadVirtualdubPlugin(pathVdub+"BoxBlur\BoxBlur.vdf", "_BoxBlur")
power = Default(power, 3)
opacity = Default(opacity, 1.0)
return C._BoxBlur(
\ Max(0, radius),
\ Min(Max(0, power), 3),
\ Min(Max(0, Round(opacity * 1000.0)), 1000)
\ )
}
MysteryX
22nd May 2017, 03:39
Thanks raffriff42. Your approach is still the only one that doesn't have extra dependencies.
btw your inline comment is breaking your script
raffriff42
22nd May 2017, 04:15
Thanks MysteryX. Fixed the comment.
Gavino
22nd May 2017, 08:46
function VD_BoxGaussBlur(clip C, int radius, int "power", float "opacity")
{
LoadVirtualdubPlugin(pathVdub+"BoxBlur\BoxBlur.vdf", "_BoxBlur")
...
Wouldn't it be better to load the plugin outside the function rather than on every call?
raffriff42
22nd May 2017, 13:58
Wouldn't it be better to load the plugin outside the function rather than on every call?
I don't know... I have always thought so, but this time, I followed the style of VirtualDub_I (http://avisynth.nl/index.php/VirtualDub_I) on the wiki, which is also used in the example (https://forum.doom9.org/showthread.php?t=72634) linked from the Plugins (http://avisynth.nl/index.php/Plugins#LoadVirtualDubPlugin) page. It seems more convenient to put the load statement together with the filter definition, so I thought I would try it.
Loading the plugin within the function should make no difference for a single call to VD_BoxGaussBlur, but for many? I don't know. Let us test... calls = 1, preload, fps = 180
calls = 1, inline, fps = 180
calls = 10, preload, fps = 25
calls = 10, inline, fps = 25
So no difference in speed -- however, these tests revealed a new problem: no matter where the load statement was located, with 2 or more calls, VirtualDub crashed when closing the script.
EDIT if this is a bug report, I should give some details: Win8.1 x64; AVS+ r2455 x86; vdubFM build 38919 x86; simplest possible script (ImageSource, ConvertToRGB32, filter calls, return)
EDIT ...and speed-testing GaussianBlur2:## calls = 1, fps = 245
## calls = 10, fps = 41
MysteryX
22nd May 2017, 19:21
VirtualFilterDub still has more bugs then.
And if I remember, VirtualDub filters only work with RGB (at least for Deshaker) and if you're working with YUV, requires conversion back and forth.
jpsdr
23rd May 2017, 09:46
It's been at least over a decade than VDub filter SDK has improved and can work with planar YUV ;).
vcmohan
23rd May 2017, 12:43
I tried variableblur/GaussianBlur, virtualdub/BoxBlur, my script above, and V.C.Mohan's modPlus/GBlur.
[/code]
I suggested use of FQPlus/F2QBlur. A circular blur (as in out of Focus) or any other type input through a text file can be used. The radius can be as much as 1/ 8 th of frame smaller dimension.
raffriff42
24th May 2017, 07:31
A circular blur... or any other type input through a text file can be used. I'm sorry, that sounds like too much work for a simple blur. An interface which needs only a "radius" argument would be helpful.
vcmohan
24th May 2017, 13:12
I'm sorry, that sounds like too much work for a simple blur. An interface which needs only a "radius" argument would be helpful.
the script line would be
radius = xx
F2QBlur(psf = "focus", x = radius)
MysteryX
24th May 2017, 17:52
uh... Raffriff42 your method is screwing up the borders!
https://s13.postimg.org/ohrs3vfpv/Mask.png (https://postimg.org/image/ohrs3vfpv/)
raffriff42
24th May 2017, 18:02
All methods screw up the borders in slightly different ways. Vdub BoxBlur looked best to my eyes. I would like to know how to emulate it. It probably involves padding in some way. I played with mirrored borders but it made no difference. Feeling perplexed, I gave up...
raffriff42
25th May 2017, 01:29
vcmohan: very well, you asked for it. Here are the results of my tests. They are not flattering to F2QBlur at all. F2QBlur has a very uneven, non-Gaussian frequency response, not helped by accepting only interlaced sources. F2QBlur also fades to black around the edges.
I might have done the interlacing/deinterlacing improperly; if so please correct me. I was trying to follow the instructions given. Quoting the error message when usage is attempted without interlacing:F2QBlur:Input must be fieldseparated.
Please use either fieldbased() or assume framebased().separatefields().
https://www.dropbox.com/s/jayuhny2zg0v1np/GaussianBlurTest%202017-05-24-01.jpg?raw=1
https://www.dropbox.com/s/rrpugxybil3ewzm/GaussianBlurTest%202017-05-24-02.jpg?raw=1
https://www.dropbox.com/s/e81nama12e3f9zd/GaussianBlurTest%202017-05-24-03.jpg?raw=1
https://www.dropbox.com/s/m5m5xkimiw5cz0a/GaussianBlurTest%202017-05-24-04.jpg?raw=1
-----------------------------------------------
https://www.dropbox.com/s/e75c61t5ich9er6/GaussianBlurTest%202017-05-24-05.jpg?raw=1
https://www.dropbox.com/s/l9r5ykrw1wrey3m/GaussianBlurTest%202017-05-24-06.jpg?raw=1
https://www.dropbox.com/s/lyo8c7kgm21ynh6/GaussianBlurTest%202017-05-24-07.jpg?raw=1
-----------------------------------------------
https://www.dropbox.com/s/c5lij9jcvnfzaa3/GaussianBlurTest%202017-05-24-08.jpg?raw=1
https://www.dropbox.com/s/y08cta9dts81xzw/GaussianBlurTest%202017-05-24-09.jpg?raw=1
https://www.dropbox.com/s/j9be7e80nwb0x6h/GaussianBlurTest%202017-05-24-10.jpg?raw=1
vcmohan
25th May 2017, 13:55
Thanks for testing. F2QBlur is not a Gaussian blur. It is a blur that an out of focus camera spherical lens produces. A progressive image is required for filtering. Interleaved produces motion artifacts. Thats the reason for separating fields.
I did not fully understand how the grey images were derived. If they are the psf s derived then they show some features outside the intended blur psf. If I can understand it, I can try to correct. Off hand they appear to be the signature of the hanning window and frame geometry used .
shekh
25th May 2017, 18:51
I was intrigued by VariableBlur' algorithms, especially the claims to produce arbitrary blur without much increasing cpu cost.
Eventually got to test it so sharing my results
all somewhat visually similar
binomialBlur(varY=100,varC=100,Y=3,V=3,U=3) = 220ms/frame
gaussianBlur(varY=30,varC=30,Y=3,V=3,U=3,border=3) = 282ms/frame
Gauss blur (5.0) = 30ms/frame
Box blur^3 (5.0) = 20ms/frame
Well this is disappointing, I expected something else.
MysteryX
25th May 2017, 20:00
I can't believe how difficult this task is; but it is as it is.
Raffriff42, I'd suggest calling your method RaffBlur to distinguish it from the other methods (since there are already 2 different methods called GBlur)
raffriff42
25th May 2017, 20:48
Interleaved produces motion artifacts. Thats the reason for separating fields.I see, so I didn't need to actually separate fields. AssumeFrameBased.SeparateFields.F2QBlur(psf="focus", x=24).Weave ## the way I did the tests above
AssumeFieldBased.F2QBlur(psf="focus", x=24).AssumeFrameBased ## better (H & V blur more equal)
By the way, if anyone wants the original test patterns, I got the pretty face from wikipedia (she is called Hacker-Pschorr Oktoberfest Girl (https://en.wikipedia.org/w/index.php?search=Hacker-Pschorr+Oktoberfest+Girl&title=Special:Search&profile=images)), the star chart (radial lines) from bealecorner.org (http://www.bealecorner.org/red/test-patterns/) and the zone plate (concentric rings) from zone plate generator (http://www.mrpinhole.com/zp.php) - try wavelength=550, focal length=100, rings=64, opacity=sinusoidal.
MysteryX
26th May 2017, 05:32
All methods screw up the borders in slightly different ways. Vdub BoxBlur looked best to my eyes. I would like to know how to emulate it. It probably involves padding in some way. I played with mirrored borders but it made no difference. Feeling perplexed, I gave up...
If you add a black border all around, then it would have no reason to screw up the borders; and then you crop. For when it touches the border, however, it would fade to black. I think you needs to pad by repeating the border pixel, so when the mask touches, the padding is white in that area, and black elsewhere.
Any reason why this wouldn't work?
raffriff42
26th May 2017, 11:09
>Any reason why this wouldn't work?
Had been thinking about this and did some testing. I compared black, gray, extended and mirror borders with a variety of images and blur radii. It turns out that, while extending pixels is best on most images, there are some pathological cases, such as the image below. For reference, I added a thin black line showing the final image bounds after cropping out the border:
https://www.dropbox.com/s/z5inqbx2b3515fd/GaussianBlurTest%202017-05-26-02.jpg?raw=1
Strangely enough, it doesn't matter how wide the border is (tested at 0.5x - 4x blur radius)
Heavily blurring the border instead seems to work fairly well (nothing is perfect) everywhere, and that is what I suggest.
https://www.dropbox.com/s/74bz9elvuko3bpb/GaussianBlurTest%202017-05-26-03.jpg?raw=1
# <source...>
blur_rad=12
BlurBorders(blur_rad/2)
# <blur function under test...>
Crop(blur_rad/2, blur_rad/2, -blur_rad/2, -blur_rad/2)
return Last
##################################
### simple blurred-borders effect, <bdr> pixels wide on all sides
##
function BlurBorders(clip C, int bdr)
{
wid = C.Width + 2 * bdr
hgt = C.Height + 2 * bdr
## heavy 'gaussian blur'
## tuned to a fixed, compromise radius
w = Max(4, wid/16 - (wid/16 % 4))
h = Max(4, hgt/16 - (hgt/16 % 4))
B = C.BilinearResize(w, h)
\ .GaussResize(wid, hgt, p=19)
return B.Overlay(C,
\ x=(B.Width/2)-(C.Width/2),
\ y=(B.Height/2)-(C.Height/2))
}
...so now you gotta blur before you blur! Yo dawg, I heard you like to blur...
vcmohan
27th May 2017, 07:55
I have been thinking about heavy blur. F2QBlur focus, x = 24 means that a radius of circle of 24 is used. Normally in a boxblur all blur coefficients are equal, while in gauss blur they taper away. In the 24 pixel circle there are over 1800 pixels . In case of boxblur each weight is about 1/ 1800 and in case of 0 to 255 range of image the contribution of any single pixel is negligible. In fact if a dirac function is input, output of this type of blur is zero. In case of gaussian types the contribution of outer parts of circle are practically near zero. By allowing high standard deviation gaussblur coefficients will gradually approximate to a boxblur.
The signature of a process (here F2qBlur or boxblur or Gaussblur) can be obtained with an input of Dirac pulse only. All other patterns super impose other artifacts. However in our usual images limited to a range of 0 to 255 such large circular blur filter can not be tested. Unfortunately even though floating point format input data is available, while viewing the result it is converted to the 0 to 255 range and so all accuracy is lost.
There is one other thing I am intrigued about. When such heavy blur is applied what is the point in looking at minor variations of result?
MysteryX
27th May 2017, 15:52
raffriff42, could you post the full script including both passes?
and as a side-question, how do you do unidirectional grossian blur?
MysteryX
27th May 2017, 16:26
Also Riffraff, I don't think your script should call Blur for lower radius because yours uses a completely different scale. I just tried 40x Blur(1.58) and yours still has WAY more blur with GaussianBlur2(6)
MysteryX
27th May 2017, 18:11
I don't think Directional Blur would hard to implement. For each pixel, take the average of the 10 pixels on the right.
Unless there already exists a way of doing so?
Gavino
27th May 2017, 18:18
I just tried 40x Blur(1.58) and yours still has WAY more blur with GaussianBlur2(6)
Ah, but because of the limits of 8-bit precision, repeated application of Blur() soon reaches the point where further iterations have no effect.
See here.
raffriff42
27th May 2017, 18:51
I don't think Directional Blur would hard to implement. For each pixel, take the average of the 10 pixels on the right.
Unless there already exists a way of doing so?Below is the full version of my function; the first one was cut down for simplicity.
This one does support unidirectional blur.
Named "GaussianBlur42" as a compromise to your suggestion of "RaffBlur."
(also I played around with mt_hysterisis as I had suggested earlier (https://forum.doom9.org/showthread.php?p=1807785#post1807785), but you seem to be doing better with blur)
https://www.dropbox.com/s/5z2wytqz71iyfiy/GaussianBlur42-H.png?raw=1
https://www.dropbox.com/s/pyuca2t4uzc9qm1/GaussianBlur42-V.png?raw=1
(EDIT output is smoother in AVS+ with 16-bit source)
EDIT new version with gamma awareness (retains highlights a little better, off by default)
Requires remove_gamma & restore_gamma, included in Utils-r41.avsi (http://avisynth.nl/index.php/User:Raffriff42/Utils-r41_Quick_Reference).
(or comment out those lines)
##################################
### Emulate [[VariableBlur/GaussianBlur]] (AVS*)
## For YUV, effective chroma blur varies depending on source
## color subsampling - YUV444 has *more* chroma blur, others less.
##
## @ var - works like GaussianBlur's varY
## @ rad - blur radius (<var> rescaled); overrides <var>
## @ vvar - vertical; default same as horizontal
## @ vrad - vertical radius; overrides <vvar>; default same as horizontal
## @ gamma - if true and if bit depth > 8, do linear-light processing
## (retains more highlights)
## @ p - final [[GaussResize]] sharpness. Default 19
## (if > 25, blockiness; if < 15, loss of contrast)
##
## version 2013-10-23 raffriff42
## version 2014-05-31 discrete hor. and vert. args
## version 2017-05-21 bugfix: blockiness
## version 2017-05-27 gamma
##
function GaussianBlur42(clip C,
\ float "var", float "rad",
\ float "vvar", float "vrad",
\ bool "gamma", float "p")
{
var = Max(0.0, Float(Default(var, 1.0)))
rad = Max(1.0, Float(Default(rad, Pow(var, 0.5))))
var = Pow(Min(Max(0.0, rad), 60.0), 1.9) ## arbitrary max radius = 60
vvar = Max(0.0, Float(Default(vvar, var)))
vrad = Max(1.0, Float(Default(vrad, Pow(vvar, 0.5))))
vvar = Pow(Min(Max(0.0, vrad), 60.0), 1.9)
gamma = Default(gamma, false)
p = Default(p, 19)
w0 = C.Width
h0 = C.Height
w1 = Round(w0/rad)
h1 = Round(h0/vrad)
B = (gamma) ? C.remove_gamma : C
B = B.BilinearResize(
\ Min(Max(4, w1 + (w1 % 2)), w0),
\ Min(Max(4, h1 + (h1 % 2)), h0))
B = B.Blur(1.0).Blur(1.0)
B = (gamma) ? B.restore_gamma : B
return (var<0.01 && vvar<0.01) ? C
\ : (var<1.5 && vvar<1.5) ? C.Blur(0.85 * var, 0.85 * vvar)
\ : (B.Width>8 && B.Height>8) ? B.GaussResize(w0, h0, p=p)
\ : B.BilinearResize(w0, h0)
}
raffriff42
27th May 2017, 19:20
raffriff42, could you post the full script including both passes?Okay, cleaning it up a lot!
Requires CropEx, part of Utils-r41.avsi (http://avisynth.nl/index.php/User:Raffriff42/Utils-r41_Quick_Reference) (interesting repo of AVS+ utilities) (BTW, CropEx now supports mode="blur")wid=848
hgt=480
ImageSource(...)
## must be RGB or YUV444 due to 1 px Letterbox below
CropEx(wid, hgt)
rad=12
bdr=1*rad ## border width makes no difference (!)
Interleave(
\ CropEx(wid+bdr, hgt+bdr)
\ , CropEx(wid+bdr, hgt+bdr, border=color_gray)
\ , CropEx(wid+bdr, hgt+bdr, mode="extend")
\ , CropEx(wid+bdr, hgt+bdr, mode="mirror")
\ , BlurBorders(bdr/2, bdr/2, bdr/2, bdr/2)
\ )
Interleave(
\ Last,
\ GaussianBlur42(rad=rad)
\ ).Trim(0, -10)
Overlay(
\ Crop(bdr/2, bdr/2, -bdr/2, -bdr/2)
\ .LetterBox(1, 1, 1, 1),
\ x=(Width/2)-((Width-bdr)/2),
\ y=(Height/2)-((Height-bdr)/2))
return Last
## OUTPUT:
# frame 0: unblurred w/ black border
# frame 1: blurred w/ black border & border line
# frame 2: unblurred w/ gray border
# frame 3: blurred w/ gray border & border line
# frame 4: unblurred w/ extended border
# frame 5: blurred w/ extended border & border line
# frame 6: unblurred w/ mirror border
# frame 7: blurred w/ mirror border & border line
# frame 8: unblurred w/ blur border
# frame 9: blurred w/ blur border & border line
__END__
MysteryX
27th May 2017, 19:22
What's the difference between var and rad? Is it just 2 different scales to do the same thing?
One possible point of improvement: cancelling the gamma curve. I don't think it's necessary in my case but it would give more accurate results.
As for directional blending, here it is bidirectional so I'd do it twice. I would get better results if I did blending to the right, then blending to the left, and process them separately. The difference would probably be minor, but I'll lose some of the left and right edges by doing both together.
raffriff42
27th May 2017, 19:39
Judging by eye, var is approximately radē. It's kept for backwards compatibility with VariableBlur's function.
And what gamma curve? EDIT: oh, you mean the Pow() call.
Yes, I would put it in a wrapper the takes only rad and vrad.
MysteryX
27th May 2017, 19:57
And what gamma curve? EDIT: oh, you mean the Pow() call.
Look at gfunc param here
http://avisynth.nl/index.php/VariableBlur/GaussianBlur
I'm not sure exactly what it's doing... perhaps my pattern detection would benefit from doing something similar too to better detect higher/lower luma value shifts.
Strange, his gamma correction supports a bunch of weird gammas but not BT.601?
MysteryX
27th May 2017, 21:01
Worth noting that gamma adjustment will mostly lose its benefits in 8-bit, or perhaps cause banding.
Your function works a lot better in 16-bit -- perhaps you could do the 16-bit conversion automatically, then convert back to the original bitrate (in some cases dithering will help, but for masks, I don't really care). SMDegran script detects the Avisynth version and adjusts a few things based on the version.
raffriff42
27th May 2017, 21:10
Hold on, I deleted my last post, will repost in a minute. Yes, you need 16-bit.
raffriff42
27th May 2017, 21:30
(my deleted post with corrections)
I had never paid attention to the gamma arguments, as it seems resize-blur-resize works well enough.
Did a quick test and confirmed: linear-light (removing gamma, processing, restoring gamma) makes A LITTLE difference (preserving specular highlights a little better) when doing resize-blur-resize.
I experimented with linear-light scaling and definitely prefer it; I use it all the time now.
See aforementioned Utils-r41.avsi script (http://avisynth.nl/images/Utils-r41.avsi):##################################
### remove gamma transfer function (if bit depth > 8) for linear-light processing
## improves appearance of certain filters like Resize;
## must call restore_gamma before final output
##
## @ enable - if false, do nothing; default true
##
function remove_gamma(clip C, bool "enable")
{
enable = Default(enable, true)
return (enable==false || C.BitsPerComponent==8) ? C
\ : (C.IsRGB)
\ ? C.RGBAdjust(rg=1.0/2.2, gg=1.0/2.2, bg=1.0/2.2)
\ : C.LevelsPlus(0, 1/2.2, 255, 0, 255)
}
##################################
### apply standard gamma transfer function (if bit depth > 8)
## only call this if gamma was previously removed
##
## @ enable - if false, do nothing; default true
##
function restore_gamma(clip C, bool "enable")
{
enable = Default(enable, true)
return (enable==false || C.BitsPerComponent==8) ? C
\ : (C.IsRGB)
\ ? C.RGBAdjust(rg=2.2, gg=2.2, bg=2.2)
\ : C.LevelsPlus(0, 2.2, 255, 0, 255)
}
###############################
### for downscaling smaller than aprox. 0.5x
function ScaleSize_small(clip C,
\ int mwid, int mhgt,
\ float src_L, float src_T,
\ float src_W, float src_H)
{
R = C.remove_gamma
\ .BicubicResize(mwid, mhgt, -0.5, 0.25,
\ src_L, src_T, src_W, src_H)
\ .restore_gamma
return R
}
I will have to read VariableBlur's source though, I might learn something. I am doing "straight 2.2 gamma, no linear segment" but that may be the wrong choice.
MysteryX
29th May 2017, 04:25
Let me know what you discover by studying the source. I'm interesting in this as well.
Adding gamma correction to StripeMask might allow better detection of low/high brightness frames with weak contrast, which it currently has trouble detecting.
raffriff42
29th May 2017, 07:16
Let me know what you discover by studying the source. I'm interesting in this as well.
Adding gamma correction to StripeMask might allow better detection of low/high brightness frames with weak contrast, which it currently has trouble detecting.I have experimented with sRGB gamma compared to straight 2.2, and at 10-bit, sRGB is noticeably better in very dark areas, below around 30d (http://avisynth.nl/index.php/D_Notation) (straight gamma has severe banding down there). At 16-bit both are much better of course. For your purposes though, Levels, ColorYUV or RGBAdjust at 14 or 16 bits should be fine.
I found resamplehq (http://svn.int64.org/viewvc/int64/resamplehq/doc/index.html)/color.cpp gives a much more readable example of sRGB code than VariableBlur, so I worked from that. A code snippet showing my MaskTools adaptation is below. Not sure why or how mt_lut works in RGB, since it's not documented AFAIK; the version included in my Utils-r41.avsi (http://avisynth.nl/index.php/User:Raffriff42/Utils-r41_Quick_Reference) does plane-swapping RGB to YUV as extra insurance.
## simplified exerpt from Utils-r41.avsi (http://avisynth.nl/index.php/User:Raffriff42/Utils-r41_Quick_Reference)
##################################
function remove_gamma(clip C)
{
#### sRGB to linear
#### (x <= 0.04045) ? (x / 12.92) : (((x + 0.055) / 1.055)^2.4)
sxp = "x 255 @F / 0.04045 <= x 255 @F / 12.92 / x 255 @F / 0.055 + 1.055 / 2.4 ^ ? 255 @F * "
return (enable==false || C.BitsPerComponent==8) ? C
\ : (C.IsRGB) [* sRGB to linear (undocumented RGB behavior?) *]
\ ? C.mt_lut(yexpr=sxp, uexpr=sxp, vexpr=sxp, chroma="process")
\ : C.mt_lut(yexpr=sxp, chroma="copy")
}
##################################
function restore_gamma(clip C)
{
enable = Default(enable, true)
mode = Default(mode, 0)
#### linear to sRGB
#### (x <= 0.0031308) ? (x * 12.92) : (((x^(1.0 / 2.4)) * 1.055) - 0.055)
sxp = "x 255 @F / 0.0031308 <= x 255 @F / 12.92 * x 255 @F / 1 2.4 / ^ 1.055 * 0.055 - ? 255 @F * "
return (enable==false || C.BitsPerComponent==8) ? C
\ : (C.IsRGB) [* linear to sRGB (undocumented RGB behavior?) *]
\ ? C.mt_lut(yexpr=sxp, uexpr=sxp, vexpr=sxp, chroma="process")
\ : C.mt_lut(yexpr=sxp, chroma="copy")
}
MysteryX
29th May 2017, 15:23
In my case, there is nothing to gain in processing in 16-bit since we only look for high contrasts. 16-bit support will probably convert to 8-bit without dithering, process, and give the result back in the source bitrate.
I'm a bit confused. Does the gamma curve apply only to RGB or also to YUV? With ColorYUV, what I find is gamma_y. Should I simply apply a gamma of 2.2? It already would work better. I'm not worried about banding; only about having a linear scale to compare contrast changes on the full spectrum.
raffriff42
29th May 2017, 18:29
In YUV, the gamma curve is applied to the Y channel only. In RGB it's applied to all channels.
ColorYUV(gamma_y=f2c (http://avisynth.nl/index.php/ColorYUV#Parameter_Rescaling)(1/2.2)) (aprox. Y'UV to linear) and
ColorYUV(gamma_y=f2c(2.2)) (aprox. linear to Y'UV) works fine.
MysteryX
29th May 2017, 19:48
In YUV, the gamma curve is applied to the Y channel only. In RGB it's applied to all channels.
ColorYUV(gamma_y=f2c (http://avisynth.nl/index.php/ColorYUV#Parameter_Rescaling)(1/2.2)) (aprox. Y'UV to linear) and
ColorYUV(gamma_y=f2c(2.2)) (aprox. linear to Y'UV) works fine.
Thanks! It would have taken me a while to figure out the f2c thing.
2.2 is a serious gamma shift. I'm surprised, it's making a HUGE difference on my mask outputs! Seems to be a big improvement.
This looks right?
AVSValue sargs[2] = { input, ((1.0 / 2.2) - 1.0) * 256.0 };
const char *nargs[2] = { 0, "gamma_y" };
input = env->Invoke("ColorYUV", AVSValue(sargs, 2), nargs).AsClip();
kuchikirukia
2nd June 2017, 09:30
function remove_glasses
vBulletin® v3.8.11, Copyright ©2000-2026, vBulletin Solutions Inc.