Log in

View Full Version : Problems with scripted Unsharp Mask


mf
27th April 2003, 20:46
I've been reading up on Unsharp Masking, and found that it should be possible to script it in AVISynth (not having found any strong enough unsharp mask filters). From what I've read, this should do the trick (with that I mean produce something comparable to unsharp mask, of course this alone won't do "the trick", it's just a start):

orig = AVISource("NARUTO.14.avi")
mask = orig.Blur(1.58).Blur(1.58).Blur(1.58).Blur(1.58). \
Blur (1.58).Blur(1.58).Tweak(0.0, 0.0, -30.0, 0.7)

Subtract(orig, mask)
And this does give me an unsharped masked image, but with strange black spots on light areas. The source is YV12, if I convert to YUY2 I get the same, if I convert to RGB32 I have to leave out the tweak (will only work on YUV), and I get a strange gray-centered image (I'll post screenshots soon). If I convert to YUY2 before Tweak() and convert to RGB32 afterwards again, I get really funky colors (I'll really post screenshots!). When I don't use Tweak() in YV12, the chroma actually lags behind :eek: a few frames.

This is really getting creepy. :D

mf
27th April 2003, 21:12
Ok, here's the screenshots.

YV12, no Tweak():
http://mf.onthanet.com/unsharpmask/yv12.png
YV12, Tweak():
http://mf.onthanet.com/unsharpmask/yv12-tweak.png
RGB32, no Tweak():
http://mf.onthanet.com/unsharpmask/rgb.png
RGB32, Tweak():
http://mf.onthanet.com/unsharpmask/rgb-tweak.png

Guest
28th April 2003, 05:21
Ah, one of my favorite subjects...

First, you can't do this the way you are trying to do it with Avisynth as it is today. I won't comment on your code, because it is not a correct unsharp masking operation. I will say that Subtract() isn't doing what you want (the 50% grey thing!) and you are getting clipped and/or saturated. The tweak operation is not part of the standard unsharp mask operation and I don't see the 2*orig factor anywhere (see below).

Let's consider the basic unsharp mask operation:

out = 2*in - blurred_in

This is one case of out = a*in + (1-a)*blurred_in, with a = 2.
Avisynth cannot represent 2*in without saturating. And Subtract() is not a real subtraction, although you might get around it by subtracting off the 50% grey. But the real showstopper is the first point. You need more range than 0-255 to do this calculation.

All is not lost, however. The blur can be a simple box blur, or a circular gaussian with defined standard deviation, or etc., etc. But in all cases it can be represented by a convolution matrix. Then the remaining math gives you a final convolution matrix that implements the unsharp mask operation.

For example, for a simple 3x3 box blur you'd get this:

AVISource("in.avi")
ConvertToRGB32()
GeneralConvolution(0, "
-1 -1 -1
-1 17 -1
-1 -1 -1")

Run it. You'll see that it works nicely.

You can change the center value of the kernel to adjust the strength. If it is 8, you get a Laplacian. If it is 9, you get the well-known (very strong) sharpening kernel. As you raise it above 9, the sharpening gets weaker. There you have a fast unsharp mask filter with adjustable strength. What more could you ask for? :)

So unsharp masking really just reduces to standard kernel sharpening. Lots of people think it is something special or different but it isn't. It's quite funny to hear naive people say things like, "I don't want a sharpening kernel, I want an unsharp masker." I don't want ketchup on my fries but make it Heinz.

Here is an unsharp mask kernel using a gaussian blur:

AVISource("in.avi")
ConvertToRGB32()
GeneralConvolution(0, "
0 0 -1 0 0
0 -8 -21 -8 0
-1 -21 299 -21 -1
0 -8 -21 -8 0
0 0 -1 0 0")

mf
28th April 2003, 12:08
Thanks! Well actually I don't really need an unsharp masker, I just need something that can sharpen way stronger than any sane person would need. The method is irrelevant, although I've noticed that unsharp mask is the only one I've gotten as strong as I want it :). But anyway, in Paint Shop Pro I have been able to get a (somehow impaired) home-made unsharp mask by subtracting a less-contrast less-brightness blurred version of the original off the original, and then maximizing the levels, but knowing next to nothing on the subject I'll just assume the reasons you gave make this impossible in avisynth :). Thanks for the script example, I'll try it. Now on for coffee.

Guest
28th April 2003, 13:50
I remembered that you can do a real subtract using Layer(). Also, by scaling down by two first and then up by two after [using Levels() or Tweak(cont=)], you might solve the range problem:

out/2 = in - blurred_in/2
out = 2*out

But this would be cumbersome and slow and would be equivalent to the kernel approach, so why bother?

mf
28th April 2003, 13:59
Err, nice, but this doesn't really sharpen. It just brightens the picture while leaving the black, or something like that. I do remember experimenting with general convolution and getting sharpening results, so I'll test around a bit.

Guest
28th April 2003, 14:07
>Err, nice, but this doesn't really sharpen.

Do you mean the first script with the 3x3 sharpening kernel I gave?

What center value did you use.

It is impossible that it did not sharpen unless you are using a version of Avisynth with a broken GeneralConvolution. Please elaborate on your environment and findings.

mf
28th April 2003, 14:17
Originally posted by neuron2
>Err, nice, but this doesn't really sharpen.

Do you mean the script with the sharpening kernel I gave?

What center value did you use.

It is impossible that it did not sharpen unless you are using a version of Avisynth with a broken GeneralConvolution. Please elaborate on your environment and findings.
I copied your exact script snippet, and I have the latest AviSynth 2.5.1 from sourceforge.

This is my result:
http://mf.onthanet.com/unsharpmask/genconv.png

Ok, as I downloaded the latest AVISynth, I found the above YV12 colorspace problem has been resolved (baka me, I should use up-to-date versions) :)!

I now found this works as a nice unsharp mask (sorry, couldn't resist trying it out):
orig = AVISource("NARUTO.14.avi")
mask = orig.Blur(1.58).Blur(1.58).Blur(1.58).Blur(1.58).Blur(1.58).Blur(1.58)
sharp = Subtract(orig, mask).ConvertToYUY2()
origyuy = orig.ConvertToYUY2()
origyuy.MergeLuma(Layer(origyuy, sharp, "mul", 255).Levels(0, 1.0, 100, 0, 255))
:D