Welcome to Doom9's Forum, THE in-place to be for everyone interested in DVD conversion.

Before you start posting please read the forum rules. By posting to this forum you agree to abide by the rules.

 

Go Back   Doom9's Forum > Capturing and Editing Video > Avisynth Development

Reply
 
Thread Tools Search this Thread Display Modes
Old 18th May 2009, 16:12   #1  |  Link
GilGalaad
Registered User
 
Join Date: Dec 2008
Posts: 9
photohop high pass sharpener ported to avisynth

sharpening, in artistic photography, it's a fundamental step in postprocessing, it compensates the natural softness of shots with high aperture, and it's one of the most important valutation parameters for a "good shot". that's why is vastly discussed on forums.
there are many ways to implement it. one of the most easy, fast and reliable one, it's achieved using a small and understimated photoshop filters, known as "high pass filter", or "accentua passaggio" in the obscene italian translation.
in video encoding too, oftwe we complain about lack of detail, excessive softness of images, or excessive grain, and removal of that causes even less detail.
i've seen many shapeners around, and all of them do their job pretty good, but sometimes they are difficult to use, plenty of options, and requires external plugins. moreover i think none of them uses the simple high pass filter method. so i tried to implement it by myself (and maybe i've done something already available...in this case, forgive this post), mostly because i love to use code written by me. it's simple, it uses only internal avisynth function, does not requires dll's.
let's see the script and the syntax:

Code:
function HighPassSharpen(clip c, int mode, float radius)
{
	v1 = c.binomialblur(radius,radius)
	v2 = v1.invert()
	v3 = merge(c,v2)
	v3 = v3.greyscale()
	v4 = overlay(c,v3,mode="hardlight")

	v5 = (mode==0) ? v3 : v4
	return v5
}
parameters are quite self-explanatory, the first is the clip to process, the second tells to work in "debug" mode (value 0) or "release" mode (1) , the third specifies the strenght of the effect (accepted values from 1 to 9).
let's see with some screenshot how to proceed, beginning from the original vob:



not much to say, beautiful image, but the source dvd is not so good, bitrate seems too low, especially in aerial views of the sea (water is a killer for mpeg)
let's call the shapening function in preview mode, and we get this:



wich is the same that we get in photoshop with the application of the high pass filter. the resulting screen is almost completely grey, with item's edges "traced" more decisely. technically speaking (you can google about it), to achieve a more sharped image we can start with a blurred image, inverted, and overlapped on the first, with 50% opacity. the more the blur, the more the sharpening. gaussian blur has standard deviation as parameter (the famous sigma), photoshop semplify the problem, using the pixel radius as parameter. avisynth does not use exactly the same function, neither the same parameter, at even at the maximum value, the blurring is relatively small (1/3 pixel). happily, gaussian blur is additive (not linearly, tough). so reiterating the call multiple times, we can obtain a good approximation of a wider range gaussian blur.
my script accepts numers from 1 to 9 (0 is valid too, and it return the original image, cosuming cpu times. this can be useful to make a rapid check between processed and unprocessed image, without commenting other part of the script). value 1 corresponds to 1/3 pixel, value 3 (recomended) corresponds to 1 pixel, growing up not linearly.
preview mode is useful to make a fine tuning of the "radius" value, you can evaluate immediately which edges will be traced and how much, you can start from a low setting and rise it step by step up to the desider threshold, enphatizing the detail, but preventing the noise to be taken in account.

so when you have finished you can use mode=1 and look at the final result:



of course you already know that, but i'll say it anyway. sharpening an image is a way to "guess" detailt not present in the original, that's why this must be done with caution, setting the value too high will produce a "plastic" effect, oversharpness, aliasing. and will amplify the noise, even create it, sometimes.
you will notice in this screen, some compression artifacts in the left part. we can think to apply a strong denoising, to flatten the blocks, but that will flatten the detail too, and then recover the crispness with the high pass filtering.

following you can see the image denoised with mvdegrain3 (sad 400), and then sharped with my script (wich has to be applied always after denoising). it's necessary to remind that sharpening is the opposite of flattening in terms of compressibility too, a sharpened imaged is always bigger than the denoised one, and often is even bigger of the original unprocessed.





if you prefer, you can modify the calls of blur(1.58) with blur(1.58, mmx=false). avisynth manual reports the latter as a slower but more precise algorythm. to be hones, i couldn't see the difference with my eyes, so i leaved it this way.
always in manual, it's suggested the value of 1.0, for repeated calls of the function, i used 1.58 because the gaussian blur can be executed in 2-passes, horizontally and vertically, and then mixed together, this give exactly the same result, with lesser computation. the suggested value 1.0 seems to do it in a not symmetrical way, so i avoided it.

some trials on anime material showed that the even in the lighter setting, this seems to be too much, and creates excessive halo and aliasing. i hope you can fine tune the blur value to find an optimal value, but i'm not a master of anime encoding, so i can't really help.

i always appreciate comments, positive and negative ones, and feedback if you want to try this script.
i am the first to say that the code can be writted much much better. there's no check on out-of-range parameters, the flux control sucks, but avisynth is no c++, and my graduee is near, i don't have much time to spend on hobbies.
and, i repeat, i'm not sure this work has not been done by anyone else, so please forgive me if i bothered you with a well known solution.

update to 1.1: changed the blur function, the range parameter now seems to have sense. you need the variableblur plugin. tnx to gargalash, gavino and neuron2.
update to 1.2 changed overlay into merge, corrected the shift to black, global luminance is definitely closer to the original. tnx to didée.

Last edited by GilGalaad; 10th June 2009 at 09:37. Reason: update
GilGalaad is offline   Reply With Quote
Old 19th May 2009, 12:53   #2  |  Link
le_canz
Registered User
 
Join Date: Sep 2007
Location: Franche-Comté, France
Posts: 19
Hi,

Your script lacks "" in the beginning :

Code:
function HighPassSharpen(clip c, int "mode", int "radius")
I'm still quite new with scripting too, but it looks like an interesting method

Unfortunately it generates a lot of ghosting with strong settings (I tried it with a simple DV source, uncompressed)

Maybe more skilled people here will be able to make it better
le_canz is offline   Reply With Quote
Old 19th May 2009, 15:29   #3  |  Link
GilGalaad
Registered User
 
Join Date: Dec 2008
Posts: 9
"" are needed only if you want to mark a parameter as optional. since there's no control and no defaults, i don't want anything optional
as i suggested, you should use it at lower settings. anyway avisynth does not implement blending methods like photoshop does, haloing, aliasing and some chroma differences are the unwanted result of stronger settings.
in the future, i might want to write some code to make a dll, because i know the potential of this method applied to photography. btw thanks for your comment
GilGalaad is offline   Reply With Quote
Old 19th May 2009, 16:03   #4  |  Link
le_canz
Registered User
 
Join Date: Sep 2007
Location: Franche-Comté, France
Posts: 19
It's strange, there was an error when I tried this function without ""

Adding it resolved the issue.

Edit : forget this, the problem was from another part of my script....

Last edited by le_canz; 19th May 2009 at 16:45.
le_canz is offline   Reply With Quote
Old 19th May 2009, 16:46   #5  |  Link
Gargalash
Registered User
 
Join Date: Nov 2008
Posts: 67
GilGalaad,
I really like this so far, thank you for this. Here is the problem I encountered:
I experience a color shift. It "looks like" the hue is shifted towards yellow (it's what I see, don't know what's going on). The color space I'm working in (in Avsp) is YV12.
I used a .d2v source from a PAL DVD.

The way it works, it seems to sharpen a lot what is already a clear edge (halo, gosthing...) but it does very well at 4. When I put a stronger setting, looking at the highpass, almost no "softer" edges are added, only the more visible ones get even more sharpening So it doesn't sharpen more edges, it only sharpens more what is already there.
I don't know much, but here is an idea: something that can analyse the highpass results and adapt sharpening to sharpen less on the higher contrast edges and more on the lower contrast edges.

I tried HQDering before sharpening as well but it had no effect on the strong edges.
Gargalash is offline   Reply With Quote
Old 19th May 2009, 19:12   #6  |  Link
GilGalaad
Registered User
 
Join Date: Dec 2008
Posts: 9
@gargalash: i've noticed the shift.
to be honest, i think it's a problem concerning the "blending mode" used here -> v4 = overlay(c,v3,mode="softlight"), because until the "grey" frame, the result is not distinguishable.
avisynth's softlight is quite different from photoshop's softlight.
i am not an expert avisynth scripter, and the manual is pretty trivial, i am not even sure if ad adaptive approach is doable.

i will experiment, but i think that for a decent result, i have to write a dll. but i don't know if i will have the time and the skill for it.
the point of this thread is mainly to show an alternative method for sharpening wich is clean, fast, and proved to give good results in other applications.

edit: i've modified slyghtly the script. i made the "gray" frame really grey, with greyscale(). the color shifting seems gone. of course the average luma goes down a bit, this is result of a more contrasted image, but the chroma is untouched. verified with coloyuv(analyze=true)

Last edited by GilGalaad; 19th May 2009 at 21:09.
GilGalaad is offline   Reply With Quote
Old 19th May 2009, 21:27   #7  |  Link
Gargalash
Registered User
 
Join Date: Nov 2008
Posts: 67
Great, I will keep testing it out! Thanks
Gargalash is offline   Reply With Quote
Old 27th May 2009, 20:39   #8  |  Link
Gargalash
Registered User
 
Join Date: Nov 2008
Posts: 67
GilGalaad,
At tried something in photoshop with the High Pass filter. By using a big radius value (60) I get an interesting contrasting.
I duplicate a layer, apply high pass with radius 60 on the top layer soft light blend it. You can try with any image, you will see what I mean.

I would like to get this effect with your script. Radius doesn't mean the same thing in your script. Do you think you can modify it to get this effect in avisynth? I tried to understand how to do it, but I have a lack of knowledge that stops me. Thanks!
Gargalash is offline   Reply With Quote
Old 28th May 2009, 09:10   #9  |  Link
GilGalaad
Registered User
 
Join Date: Dec 2008
Posts: 9
as i said, there is direct but not LINEAR correspondence between the radius of a gaussian blur (photoshop raius) and avisynth blur. the formula is not known exactly. you can achieve almost any radius in avisynth simply repeating the blur function many times. the radius parameter of my fuction should be intended as "repetitions" of the avisynth blur, and should be coded with a for cycle to allow an arbitrary number (instead of this not elegant form). but avisynth does not support reiteration, and recursion is slow, ram consuming and with a crazy syntax.
my suggestion is to find an optimal value by yourself. do the blurring in photoshop, then open avisynth and repeat the call of blur many times until you get something very close. probably for a radius of 60 pixels this mean repeating the function 200 times. you can figure what this mean in cpu time, but that's the only way avisynth allows.
as i said, i'd like to have the time to implement this in a dll, would be a final workaround for all those limitations.
GilGalaad is offline   Reply With Quote
Old 28th May 2009, 12:24   #10  |  Link
Gavino
Avisynth language lover
 
Join Date: Dec 2007
Location: Spain
Posts: 3,431
Quote:
Originally Posted by GilGalaad View Post
recursion is slow, ram consuming and with a crazy syntax.
I have to disagree with you on all three counts.
There is no 'run-time' penalty in time or memory for using recursion. Any overhead is 'compile-time' only, ie during script loading, and is scarcely noticeable for simple functions with reasonable depths of recursion. The resulting filter graph is exactly the same as if you had unwound the recursion 'by hand'.

As for the syntax, it may look strange if you haven't used recursion before, but it becomes quite natural once you understand it. Your function can be written simply using a recursive 'helper' function, eg
Code:
function RepeatBlur(clip c, int n) {
  n > 0 ? c.Blur(1.58).RepeatBlur(n-1) : c
}
Then the code at the start of your function
Code:
v1 = (radius == 1) ? c.blur(1.58) : c
v1 = (radius == 2) ? c.blur(1.58).blur(1.58) : v1
...
v1 = (radius == 9) ? c.blur(1.58).blur(1.58).blur(1.58)... : v1
can be replaced simply by c.RepeatBlur(radius). This might even be faster to load since there is less text to parse, and of course now the radius is no longer limited to 9.
Gavino is offline   Reply With Quote
Old 28th May 2009, 14:19   #11  |  Link
Guest
Guest
 
Join Date: Jan 2002
Posts: 21,901
Common misconception:

Repeated blurs do not increase the radius. Repeating blurs with the the Blur() filter gives only a better and better approximation of a Gaussian blur with a fixed width.

For a truly variable radius, you need something like this:

http://neuron2.net/boxblur/boxblur.html

Last edited by Guest; 28th May 2009 at 14:21.
Guest is offline   Reply With Quote
Old 28th May 2009, 15:29   #12  |  Link
Gargalash
Registered User
 
Join Date: Nov 2008
Posts: 67
I experienced neuron2's clarification before asking how to get a larger radius. I had modified the function and realized that 60 compared to probably 1000 repeated blurs did not change anything. There was no modification starting at something like 12 repetitions.

Thanks all for your comments.
neuron2, I will be playing with your boxblur later today.

Last edited by Gargalash; 28th May 2009 at 20:00. Reason: typo
Gargalash is offline   Reply With Quote
Old 28th May 2009, 16:08   #13  |  Link
GilGalaad
Registered User
 
Join Date: Dec 2008
Posts: 9
@gavino: tnx for the syntax help but, forgive me, any syntax that does not allow any flow control except ternary operator...is crazy, imho, and i can say for sure that i have use recursion before. but tnx again for the example, it's clear and simple.

@neuron2: interesting point, and tnx for the clarification. but the misconception comes from this assert in the avisynth help page:
"If you want a large-radius Gaussian blur, I recommend chaining several copies of Blur(1.0) together. (Anybody remember Pascal's triangle?)"

Last edited by GilGalaad; 28th May 2009 at 16:08. Reason: typo
GilGalaad is offline   Reply With Quote
Old 28th May 2009, 17:03   #14  |  Link
Guest
Guest
 
Join Date: Jan 2002
Posts: 21,901
That just shows that the misconception is as common as I said.

In a very limited sense it is correct in that the tails of the gaussian are pushed out as it is iterated (but it rapidly converges), but that's not what people usually mean by a larger radius.

Last edited by Guest; 28th May 2009 at 17:06.
Guest is offline   Reply With Quote
Old 28th May 2009, 20:30   #15  |  Link
Gavino
Avisynth language lover
 
Join Date: Dec 2007
Location: Spain
Posts: 3,431
Quote:
Originally Posted by neuron2 View Post
Repeated blurs do not increase the radius. Repeating blurs with the the Blur() filter gives only a better and better approximation of a Gaussian blur with a fixed width.
That's very interesting. I have also been misled by the statement in the docs.

When you say a fixed width, do you mean fixed independent of the parameter to Blur(), or that for a given parameter value, it converges to a Gaussian whose radius depends on the parameter.

In other words if Blur(x).Blur(x).Blur(x)... converges to Gaussian(r), does r depend on x or is it fixed?
If the first case, do you know what is the relationship between r and x, and if the second, what is the fixed radius?
Gavino is offline   Reply With Quote
Old 28th May 2009, 20:59   #16  |  Link
Guest
Guest
 
Join Date: Jan 2002
Posts: 21,901
It's related to the size of the blur kernel, which is fixed at 3x3. I suppose the variance of the gaussian is 3 or some function of 3.
Guest is offline   Reply With Quote
Old 29th May 2009, 18:46   #17  |  Link
Wilbert
Moderator
 
Join Date: Nov 2001
Location: Netherlands
Posts: 6,364
I will correct the docs when i've time.
Wilbert is offline   Reply With Quote
Old 1st June 2009, 01:06   #18  |  Link
Gavino
Avisynth language lover
 
Join Date: Dec 2007
Location: Spain
Posts: 3,431
Quote:
Originally Posted by neuron2 View Post
Repeated blurs do not increase the radius. Repeating blurs with the the Blur() filter gives only a better and better approximation of a Gaussian blur with a fixed width.
At first sight, this is a surprising result.

Theory shows that repeated convolution of any blur filter converges to a Gaussian blur, but also states that the radius of the blur keeps growing proportionally to the square root of the number of repetitions n. Or, to put it another way, the 'variance' (=radius^2) is directly proportional to n. So in principle, repeated applications of Blur() should be capable of (eventually) producing a Gaussian blur of any desired radius.

Blur(1.58) [actually, Blur(log(3)/log(2))] has a (1/3, 1/3, 1/3) kernel which can be shown to have a variance of 2/3.
Hence, repeating it 6 times gives a good approximation to a Gaussian of radius 2 [=sqrt(6*2/3)]. In theory, you should be able to create any radius by repeating it enough times, although (because of the square root factor) the number of repetitions required becomes impractical for larger radii. For example, 150 would be required to get a radius of 10, while to get a radius of 30 would require 1350 iterations.

However, even these numbers are beyond the reach of the Blur() filter. Simple tests confirm neuron2's statement, showing that beyond a certain number of repetitions (about 100), there is no further change in the image, putting an upper limit on the radius that can be reached (about 8).

So what is behind this limit? I believe it has to do with the limits of 8-bit pixel precision.
As you add more repetitions of the filter, each one has a smaller and smaller effect, though in theory it never reaches zero. However, once the point is reached where the effect of the next filter application is a difference too small to be seen in 8 bits, any further iterations have no effect.

As neuron2 suggests, the practical way to construct a Gaussian blur is to start with a wider box blur, such as neuron2's own filter, for which only a small number of repetitions is required to give any desired Gaussian.
For example, BoxBlur(r, 1, 1000) has a variance of r*(r+1)/3, so 6 repetitions (or 2 applications of BoxBlur(r, 3, 1000)) approximates a Gaussian of radius sqrt(2r(r+1)).

Last edited by Gavino; 1st June 2009 at 01:08.
Gavino is offline   Reply With Quote
Old 1st June 2009, 03:04   #19  |  Link
Guest
Guest
 
Join Date: Jan 2002
Posts: 21,901
Thanks for the clarification, Gavino. As you say, the radius does grow slowly but it's impractical for getting wide kernels.
Guest is offline   Reply With Quote
Old 10th June 2009, 08:05   #20  |  Link
GilGalaad
Registered User
 
Join Date: Dec 2008
Posts: 9
i updated the script in the first post, using variableblur.
now it doesn't require recursion, it's faster, and radius makes sense. binomialblur can be replaced by a true gaussianblur but for the range i'm using for HD material (radius 1-3) i can't see a real difference (and manual says that, in this range, binomial is a a very good approximation of gaussian)
i preferred variableblur to neuron2's boxblur, since boxblur is a virtualdub plugin and a rgb colorspace conversion should be necessary to make it work (or am i wrong?).
to be honest i'm pretty happy of the results, i'm posting a screenshot from Robin Hood, where is cleary visible the detail gain in the skin and in the sweat.
thank you everyone for testing, corrections and suggestions


Last edited by GilGalaad; 10th June 2009 at 08:07. Reason: typo
GilGalaad is offline   Reply With Quote
Reply

Thread Tools Search this Thread
Search this Thread:

Advanced Search
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT +1. The time now is 20:47.


Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2024, vBulletin Solutions Inc.