Log in

View Full Version : My attempt at writing a new shapener - SuperToon


Hadien
2nd February 2012, 09:03
I wanted to try and see if I could make a powerful sharpener that can run in realtime and this is what I came up with.

#SuperToon is my attempt to optimize/speed up the previous versions of mfToon, vmToon, etc.
#
# requires MaskTools V2.0 (at the time of writing this function the used version was V2.0a35)
# Removegrain(), Unfilter(), and Degrainmedian(), Repair() and Medianblur
# tested only in YV12, although I 'think' it could do other colorspaces it is reccomended to
# do this in YV12 or YUV2 due to the math involed in this filter.
# thanks to:
# mastrboy for suggestion to use Removegrain() instead of Undot()
# theProfileth for optimizations to use mode=3
# Chikuzen for error checking
# Didée for suggestion for mode =4.


function SuperToon(clip input, float "power", int "mode",
\ int "Nthr", int "Nstr", int "Ncap",
\ int "Lthr", int "Hthr", int "Lcap",
\ int "cont", bool "show")

{

power = default(power,.75) #values -255.0-255.0 will have impact
# however a range from 0.2-1.5 is usually
# the desired range. Positive values darken lines,
# negative values brighten them. 0 won't sharpen
# the video however SuperToon will still consume CPU.
# when setting the Nthr, NCap, Lcap, and pretty much any other
# setting, you can set this to an extreme value like 100, this way
# you can easily see (as if the edge mask was overlayed on the original)
# what supertoon will be darkening (whether it'd be the lines you want it
# to darken or the noise you didn't want it you detect)
# for mode = 3, Power works differently. it ranges from -100 to 100 and its no
# longer the "strength of darkening" but instead the "strength of smoothing +
# sharpening"

mode = default(mode,0) # ranges from 0-4; tells SuperToon to try different methods
# no mode is always better than the other, its just that one mode
# can fare better for one source and not another. so the user
# has the option of which mode is best for their source. if you
# choose a number outside of the range it will default to 0.
# mode =0: just applies a very simple edge detection mask with
# no effective noise detection.
# mode =1: creates an edge mask and filters out noise by finding
# the average of all neighboring pixels and determines
# if the current pixel is an "outlier" and not noise, meaning
# its an edge to darken.
# mode =2: silmilar to mode 1 but does things differently, it runs a sieve
# on all the neighboring pixels and then resets the edge mask
# to be the median of the results from that sieve.
# this method is alot slower than the others, but sometimes its
# the only one that can seem to pull it off that the others can't
# mode =3: does things completely differently in that it performs a "filtered"
# unfilter on the source. by filtered I mean it sharpens the lines while
# smoothing everything else. very effective at removing noise and thinning
# lines, and is very fast. its just not as "strong" at sharpening as I
# had hoped as the other modes.
# mode =4: works to ignore non-edge lines like shadow lines so it doesn't darken lines
# that weren't meant to be darkened. if you're getting something like black
# outlines around stars or snow, this mode is very good at preventing that.
# this mode doesn't run realtime for me (~20 fps for a 2.01 GHz single core)

Lthr = default(Lthr,0) # can range from 0-255, raising this value makes darkened lines thicker and helps
# with edge detection, but it'll usually give very very thick lines if you set this
# too high. 9 times out of 10 you will likey not have to change this from the default.

Hthr = default(Hthr,255) # can range from 0-255, lowering of this value has a stronger
# impact on line detection than the low threshold.
# most of the time the default is the best setting.

Lcap = default(Lcap,255) # brightness cap which tells the function to only darken pixels under a certain luma value
# this is mostly intended to give more control to modes 0-2. mode 3 doesn't use it, and mode 4
# shouldn't ever need it. setting it to 255 basiaclly disables this.

cont = default(cont,180) # pixels are darkened by an amount relative to their spatial luma change. pixels with very low luma
# change (like noise) is darkened very very slightly. pixels with large luma change (like a black line)
# are greatly darkened. while moderate change (like a shadowline) is partially darkened.
# this value controls the contrast of these changes. lowering this values makes it so that both low luma
# and high luma changes are subtracted by near the same amount. if you lower this value "power" will have
# to be raised to show a visible difference when used with Ncap, its iw very effective at removing noise.

Nthr = default(Nthr,4) # threshold to try to detect noise around the lines to be sharpened set this too high and
# it'll begin to confuse the lines you want sharpened as noise. this parameter works
# differently depending on the mode so certain in modes it works better in certain ranges.
# in nearly all modes its used to clip lower values from the generated mask (as noise). for
# modes 1 and 2 it used more to clean the noise from the lines. for 3 its used to filter between
# smoothing or sharpening. for mode 4 its used to overlay mode 0's mask onto mode 4's mask. lower numbers
# add more weight to Mode 0's mask.

Nstr = default(Nstr,0) # can range from -100 to 100. 0 will make this do nothing. This is
# usually used to blur the edgemask to try to smooth out residual
# noise. basically its a parameter that uses Unfliter() on the
# treated mask. negative values work great for reducing noise
# its fast and effective but values near the limits can make demolish lines
# or produce a small halo effect. positive values and help thin some
# lines which works great if the source doesn't present alot of noise.

Ncap = default(Ncap,30) # ranges 0-255 increasing it is the best (as far as filter
# speed) at preventing noise from being sharpened, however
# setting it too high and you won't be darkening any lines
# this setting helps determine how "sensitive" it should be when looking
# at the edge mask. if it looks like the entire picture is darker,
# then its likely this value it too low. 18 is a good starting point
# for most sources I've tried.

show = default(show,false) # if you want the edge mask returned instead of the sharpened
# result
powerF = min(100,max(1,Round(abs(power))))
Spow = string(power)
SHthr = string(Hthr/4.0)
SNthr = string(Nthr)
SNcap = string(Ncap)
SLcap = string(Lcap)

mask = mt_makediff(input.mt_expand(Hthr),input.mt_inpand(Lthr))

#mode=1 -> abs(y-x)/x>n/255.0? y: 128
#mode=2 -> expr= abs(y-x)<n && x>y ? 128: y - x + 128
mask = (mode==2) ? mask.mt_luts(mask,pixels=mt_square( 4 ), mode="med", expr="y x - abs "+SNthr+" < x y > & 128 y x + 128 - ?")
\ : (mode==1) ? mask.mt_lutf(mask,mode="avg",expr="y x - abs x / "+SNthr+" 255.0 / > y 128 ?")
\ : mask


#now this tries a completely different mechanic working more like a "filtered" sharpener AND smoother.
# very very fast but is still experimental...not effective
hard = mode==3 ?input.degrainmedian().unfilter(powerF,powerF).Removegrain(mode=1):input
soft = mode==3 ?input.degrainmedian().unfilter(-powerF,-powerF).Removegrain(mode=1):input
#"abs(x-y)>n?y:0"
#"x==0?y:x"
hard = mode==3 ?input.mt_lutxy(hard,"x y - abs "+SNthr+" > y 0 ?"):hard
hard = mode==3 ?hard.mt_lutxy(soft,"x 0 = y x ?"):hard
hard = mode==3 && Nstr!=0?hard.unfilter(Nstr,Nstr):hard


#mode 4 works to avoid shadow lines. since the current mask it generates is far too weak, I have it add
# a portion of the previously generated mask. as a result shadow lines aren't completely removed from
# this mask, however their strength is so weak Nthr, Ncap, Nstr, and cont can easily remove it.
#x - (y-128)/n + 128
mask = (mode==4) ?input.medianblurcw(2,0,0,0).mt_logic(input,"max").mt_makediff(input).mt_lutxy(mask,"x y 128 - "+SNthr+" / +")
\ :mask

#these 2 lines workd to clean all/most of the noise caught in the mask
#as well and reset it's levels so its white to black and not white to grey.
mask = ((Nstr!=0)?mask.unfilter(Nstr,Nstr):mask).Removegrain(mode=1)
mask = mask.levels(128+Nthr,1,255,0,cont).Removegrain(mode=1)

#residual Noise reduction regardless of mode. the Unfilter before doesn't
# actually remove the noise... but it makes it simpler for the sharp LUT below to see it and
# filter it out with NCap
#"y<n||x>L?x:x-(y-n)*p"
sharp = mode==3 ? hard:input.mt_lutxy(mask,"y "+SNcap+" < x "+SLcap+" > | x x y "+SNcap+" - "+Spow+" * - ?",u=2,v=2)

#if show is true and the mode is not 3 (since it doesn't use an edge mask) return the edge mask
# else return the sharpened video.
return show && mode !=3 ?mask.greyscale():sharp
}






here are some screen caps of before/after of it at work:
http://screenshotcomparison.com/comparison/106060/

and the parameters I used:

#1 Gurren Lagann Supertoon(power=.5,mode=0,nthr=8,ncap=40,nstr=10,cont=250)
#2 Soul Eater Supertoon(power=1,nthr=16,ncap=32,nstr=-70,cont=200)
#3 Full Metal Panic Supertoon(power=.75,Lthr=0,Nthr=4,Ncap=40,cont=230,nstr=20)
#4 & 5 Fooly Cooly Supertoon(power=.5,Nthr=24,Ncap=48,cont=230)


#4 and 5 where to show how well it can work with sources that had scenes with strong noise and weak noise

here's some new screen caps showing off the new mode=4
http://screenshotcomparison.com/comparison/107324
and the parameters I used for those screencaps:

#1 Soul Eater Supertoon(power=.75,mode=4,Lthr=0,nthr=3,nstr=-15,ncap=32,cont=200)
#2 Gurren Lagann Supertoon(power=.5,mode=4,nthr=4,ncap=40,nstr=-10,cont=200)


all the modes I used would play in realtime when I tested them, except for mode 2 which would run around 10 fps on my PC (2.01 Ghz single-core) and mode 4 (~20fps). Theres still alot of things I want to add and some problems I want to fix. but I wanted to go ahead and post what I've got so far. see if I could get some critiques.

Edit: added more screencaps and updated code.

thebigmunch
2nd February 2012, 16:49
For your images, you should be able to right-click on the video in the AvsP preview and choose Save Image As to get proper screenshots.

Hadien
2nd February 2012, 22:23
I know how to post a full screenshot, It just didn't occur to me to do so. heres another screencap at full resolution
http://i253.photobucket.com/albums/hh77/Koensayrbtls3/Project%20Renderings/SuperToonTestGL.png

TheProfileth
2nd February 2012, 23:28
Since this is not a plugin or a new compiled version of avisynth I think this fits more in the avisynth usage section rather than the avisynth development section.
Just saying :o

Hadien
2nd February 2012, 23:40
well then I was confused. the description for this forum seemed to imply the creation of new filters, functions and such

while the other forum gave me the impression for learning how to code in avisynth, troubleshooting, etc.

TheProfileth
2nd February 2012, 23:45
Ah well they mean things like dll filters and such but yeah no big deal just make a new thread in the other forum and maybe use real screenshots too lol but yeah I will look forward to testing and giving feedback on this filter in there.
Even better still use this site to compare
http://screenshotcomparison.com/
should be better for everyone and more convenient too

Guest
3rd February 2012, 15:26
Moved this thread to Avisynth Usage and deleted the crosspost.

mastrboy
3rd February 2012, 17:24
you should replace undot() with removegrain(mode=1)

Hadien
4th February 2012, 05:08
Thanks neuron, I updated my post with suggested changes to the original code and relinked the screencaps I posted in the other thread

Chikuzen
7th February 2012, 10:57
I got an error "Script error: expected `:'"
maybe, this will fix.

--- supertoon.orig.avs 2012-02-07 18:52:06.108082300 +0900
+++ supertoon.fix.avs 2012-02-07 18:51:09.442841300 +0900
@@ -114,7 +114,7 @@
#"x==0?y:x"
hard = mode==3 ?input.mt_lutxy(hard,"x y - abs "+SNthr+" > y 0 ?"):hard
hard = mode==3 ?hard.mt_lutxy(soft,"x 0 = y x ?"):hard
-hard = mode==3 ?(Nstr!=0)?hard.unfilter(Nstr,Nstr):hard
+hard = mode==3 && (Nstr!=0)?hard.unfilter(Nstr,Nstr):hard

#"y<n||x>L?x:x-(y-n)*p"
sharp = mode==3 ? hard:input.mt_lutxy(mask,"y "+SNcap+" < x "+SLcap+" > | x x y "+SNcap+" - "+Spow+" * - ?",u=2,v=2)

Hadien
7th February 2012, 13:50
yeah I noticed that error, and thats the same fix I did. I haven't been able to post the proper fix lately due to my PC crashing a lot recently (a capacitor blew on my mobo).

I've been trying to wrap my head around a way to subtract the shadow lines from my edgemasks or have it so that the edgemasks don't pick them up in the first place. I don't think shadow lines should be darkened however the way I have my edge detection set up it'll confuse them as lines that should be darkened.

thinking of writing a mask using lut or convolution that detects only shadow lines and then subtract that mask from the edgemask. but not exactly sure on how to write it up.

TheProfileth
10th February 2012, 03:21
I've been trying to wrap my head around a way to subtract the shadow lines from my edgemasks or have it so that the edgemasks don't pick them up in the first place. I don't think shadow lines should be darkened however the way I have my edge detection set up it'll confuse them as lines that should be darkened.

thinking of writing a mask using lut or convolution that detects only shadow lines and then subtract that mask from the edgemask. but not exactly sure on how to write it up.
Well off the top of my head, use 2 masks, 1 for sharpening and the other for darkening. On the darkening mask you could try blurring the image that is to be edge masked. And by not blurring the sharpening mask the sharpening should still be correct and the darkening should be more correct (hopefully) because it will not pick up weaker lines (such as shadows hopefully) or you could do something with thickening/thinning the edgemask to try and cull the shadows that had be included, but I would try the first method before doing that as it seems much simpler :D

Hadien
10th February 2012, 08:27
ugh I hate the fact that if you type a long post and quick reply it suddenly forgets who you are since you are signed out. so you press the back button to find your whole post you wrote up is lost.

the blurring it mostly meant to reduce/prevent noise from being detected and darkened, its never strong enough to effect shadowlines. infact if you check Supertoon's Mask (via show=true) you can see that it can detect shadow lines just as clearly as edge lines. to supertoon, it sees edges and shadowlines as actual lines, and it'll darken everything it confirms as a line (for now, I want to make it more specific).

I think the biggest difference between an edge line and a shadow line is that an edge line is usually one unique, darker color (within a threshold of course) that lies as a boundary between 2 other usually brighter colors. shadow lines are usually 2 different colors pushed together without a darker line splitting the two. so what I would want to do is tell my edge mask to look at its neighboring pixels in the input clip which weren't masked and determine if they were the same color as the pixel that were masked (again within a certain threshold)

what I have tried is overlay the edgemask onto the input (making edges white), then perform an inpand or two. afterwards I'd make a comparision between this overlay and the input stating that if the input pixel is darker than the same pixel in the overlay, its an edge. I'd turn that result into my new mask and tried applying it. While it did mostly demolish the shadow lines, it also broke up much of the actual edges, and somehow made certain other areas darken which weren't edges (notably i motion areas where things got motion blurred). I feel I'm on the right track here, But I think I need to update Masktools (current version is V2.0a35) and see if theres a better function to use here. mt_luts is the only neighboring pixel lut I have and it performs the expression on each pixel before doing the mode (I want it to do that in reverse)

Didée
10th February 2012, 17:43
A median filter is another possibility to "clamp out" lines, while not reacting to boundary-edges of uniform areas.

> vid.repair(vid.medianblur(2,0,0),1).mt_logic(vid,"max",U=2,V=2)

or maybe FastLineDarken-style?

> vid.repair(vid.mt_expand().mt_expand().mt_inpand(),1,0) # number of ex/inpands acc. to line thickness that shall be catched

TheProfileth
11th February 2012, 00:48
Didee would the dithertools box filter be of any use here? As it is certainly much faster than any median filter (as far as I know)
Cause here is a application of it I came up with a while ago after seeing someone ask for a mean filter
Function vmean (clip v,int "r",int "rc")
{
#variable mean
r = default(r, 4)
rc= default(rc, r*2)
v.Dither_convert_8_to_16 ()
Dither_box_filter16 (radius=r, y=3, u=2, v=2)
Dither_box_filter16 (radius=rc, y=2, u=3, v=3) # for 4:2:0 chroma subsampling
DitherPost (mode=-1,stacked=true)
}

Didée
11th February 2012, 04:45
A box filter is a mean filter. For the problem posed, mean filtering is exactly what needs to be avoided.

TheProfileth
11th February 2012, 05:41
A box filter is a mean filter. For the problem posed, mean filtering is exactly what needs to be avoided.
Ok that is what I thought, I just wanted to make sure :o

Hadien
11th February 2012, 11:17
@Didée:
thanks for that fantastic "clamping" suggestion. I tried it out and while the mask it gave me was still kinda weak (it prevented those shadow lines but would sometimes demolish other lines) I instead played with it some more and I believe I came across a pretty good solution. the mask itself was still weak so what I did was add mode=0's mask back onto this weak mask (mode 0's mask values were greatly reduced to help hide its shadow lines). Together with the noise reductions I already have in place (Nthr, Ncap, Nstr, and cont parameters) the shadow lines in mode 4's mask (mode 4 is the new mode to remove these shadow lines) were made so weak that Supertoon could pass them off as simple noise to filter out. speed-wise mode 4 runs about 20 fps (again on a single core 2.01 Ghz). It feels like I could make some speed improvements to it but I'll worry about that tomorrow

since it's getting late/early here I post a full code update and new comparison screen-caps when I wake up in a few hours.

TheProfileth
11th February 2012, 23:56
You should be using medianblur(2,0,0) rather than medianblurcw(2,0,0,0) as it is (about) twice as fast and does the same thing in this situation.
That hopefully should allow you to get realtime playback.

Hadien
12th February 2012, 00:55
thanks for the tip and you're right its does greatly speed up that mode to play real-time. I initially thought the center weight version would prove more roboust if I would have it not count the center pixel by setting it to 0. when I changed it back I saw no visual change (though I do remember a change when I first tried it, maybe in the end after adding the difference mask and overlaying the previous mask that got balanced out) but yeah, definite speed boost.

I'm pondering if I should add a hook to the radius variable in median blur cause it might be that a radius of 2 won't always help for every instance.

Besides that, what is everyone's opinion of this sharpener? would you ever see yourself using it? do you like where its going? I'd like to hear some feedback on this.

mandarinka
12th February 2012, 01:13
My personal opinion is that this sort of filtering is a horrible thing to do in the first place, so if you excuse me...

TheProfileth
12th February 2012, 01:28
thanks for the tip and you're right its does greatly speed up that mode to play real-time. I initially thought the center weight version would prove more roboust if I would have it not count the center pixel by setting it to 0. when I changed it back I saw no visual change (though I do remember a change when I first tried it, maybe in the end after adding the difference mask and overlaying the previous mask that got balanced out) but yeah, definite speed boost.

I'm pondering if I should add a hook to the radius variable in median blur cause it might be that a radius of 2 won't always help for every instance.

Besides that, what is everyone's opinion of this sharpener? would you ever see yourself using it? do you like where its going? I'd like to hear some feedback on this.
Adding the option to change the radius from like 1-3 would be good. BTW for radius 1 removegrain(4,0,0) is faster than medianblur(1,0,0) as removegrain is better optimized. And btw medianblur(2,0,0) literally is the exact same thing as medianblurcw(2,0,0,0) because the center pixel is weighted no differently in both. On the other hand medianblurcw(2,0,0,1) is different as the center pixel gets more weight.
My opinion is that the sharpener while fast is too haphazard in its approach if it were to be used for anything other than realtime playback enhancement. The results in general are too aliased though I do like the darkening.
One thing though here is an odd bug when using mode= 3. Look at the area near the edges.
http://eypic.net/images/meganenaka.png
There is a black box around most of the image but not all of it.
Also in general it seems that when you set lcap to 0 you get no sharpening as well as no line darkening instead of just no line darkening which is quiet odd.
Edit:
My personal opinion is that this sort of filtering is a horrible thing to do in the first place, so if you excuse me...
lol Mandi calm down, constructive criticism remember?

BTW here is a sharpener that I threw together recently that is about 10% faster than yours
http://screenshotcomparison.com/comparison/107415
http://screenshotcomparison.com/comparison/107414
http://screenshotcomparison.com/comparison/107416
While I prefer nlsharp over supertoon in this case they both are not good enough for my tastes

Now this is more my speed
http://screenshotcomparison.com/comparison/107422
http://screenshotcomparison.com/comparison/107423
Well the motion compensation involved in this sharpening does have a price,
it goes at about 5fps compared to 32fps for nlsharp and 29.5 fps for supertoon

All in all, ultimately if you want this to be used by more people I think making the line darkening less attached to the sharpening is a good idea as well as trying to find a way to limit aliasing, cause mode 4 is (for me atleast ) the only mode worth actually even attempting to use.

But those are just my opinions and I still think that working on this is atleast good practice for if you want to do something different later.

Hadien
12th February 2012, 16:21
Nice work for nlsharp. And going back to your comparisions you can see the problem I'm still facing with mode 4. In that it loves to over darken corners (a result of the medianblur). medianblur even gets the sharp corners of shadow lines. Going by your screencap look at the corner of that shadowline on the girls sleeve, you can see it clearly by setting the power to 1000 (common method I use to see exactly what pixels supertoon is touching as they are all set to black), another thing the medianblur method can't detect for me is when 2 actual lines meet at/near perpendicular angles.

hmm...running back to that over darkening the corners problem. I can run a max clip of mask 4 to mask 0. Afterall, mask 4 is meant to detect less, not more. the other problem where it can't detect certain actual lines should have been fixed when mask 0 gets added, but since the lines look "peppered" to me in the end result prehaps a mt_deflate().mt_inflate() can help even out the strength of those lines (as well as help with noise reduction).

Even when I first wrote it up I never got mode 3 to work as I had wanted it to. It was mainly meant to be a fast, subtle sharpener and smoother at the same time. Currently it determines which pixel to use from either the sharpening version or smoothing version based on how much of a luma change there is between the sharp and the original. Now I'm thinking that I should use mode 0 as a mask to mode 3 sharp/smoothing, just like I'm doing for mode 4. since mode 3 won't actually darken shadow lines (more accurately put it won't smooth shadow lines). Or to provide more control, have 2 types of modes, a masking mode and a sharpening mode. if I think of it that way I actually have 5 masking modes (one of which, mode=3 I'll likely throw out as it seems useless) and 2 sharpening modes (one using the filtered unfilters, and the other using the mask's luma to determine darkening amount)

Aliasing has always been a problem for me. Usually to me I would take it as a hint that I either need to decrease nstr or my power is set far too high. but sometimes setting power too low wouldn't give satisfying results so I would up the power then apply a very weak blur(), unfilter, or even an antialias() to fix. But those are poor ways to avoid the problem, so I have been thinking up ways to prevent it, as I think it would end up making the script far faster if you prevent it first rather than add in filters to fix it.

Didée
12th February 2012, 17:10
the problem I'm still facing with mode 4. In that it loves to over darken corners (a result of the medianblur)
Sure. A median blur cuts corners. Now, guess why I had suggested median()+repair()?

vid.repair(vid.medianblur(2,0,0),1) # or ,12


Aliasing has always been a problem for me. Usually to me I would take it as a hint that I either need to decrease nstr or my power is set far too high. but sometimes setting power too low wouldn't give satisfying results so I would up the power then apply a very weak blur(), unfilter, or even an antialias() to fix.
If aliasing is a result of strength-of-the-operation, or a result of "limiting", then it's another problem.
But some of the aliasing could be produced by the median filtering in itself (the result often is rather "picewise" than "continuous") ... in which case it is very effective to use a median-mean-alias: calculate both median and mean, get the diff's of both, build the alias via repair [ meandiff.repair(mediandiff,12) ], then apply.

jinkazuya
14th February 2012, 23:36
Just wonder where to get the repair() function or plugin?

Didée
15th February 2012, 00:11
Here: http://tinyurl.com/6p6ysce

Bloax
15th February 2012, 07:05
o u

This is a nice place too. (http://home.pages.at/kassandro/RemoveGrain/RemoveGrain.zip)

ryrynz
23rd February 2012, 08:03
in which case it is very effective to use a median-mean-alias: calculate both median and mean, get the diff's of both, build the alias via repair [ meandiff.repair(mediandiff,12) ], then apply.

I'm currently running a script in real time for anime content which includes Seesaw but have to keep the strength of it down to avoid aliasing.
Is the above method an option that can be used by me? Any chance of an example script which I could test that would bring out all that detail seesaw brings out so well and avoid the aliasing?

Didée
23rd February 2012, 08:49
It is unlikely to do "the same" as SeeSaw, and do it "better" as SeeSaw, and do it "same fast" or even-faster. If you want the operation to be more and more and evenmore specific, you need to do more and more and evenmore intermediate steps.


SeeSaw uses an "absolute" image-based alias:

sharp1 = video.sharpen(gauss)
sharp2 = video.sharpen(median)
sharp = sharp1.repair(sharp2,1)


A more restrained way is to not do it absolute, but rather relative (limit the difference)

sharp1 = video.sharpen(gauss)
sharp2 = video.sharpen(median)
sharp1D= mt_makediff(video,sharp1)
sharp2D= mt_makediff(video,sharp2)
DD = sharp1D.repair(sharp2D,1)
sharp = sharp1.mt_makediff(DD,U=2,V=2)


Or very similar, using the minblur() function I had posted a few times:

mt_lutxy(video,video.minblur(n), x x y - +",U=2,V=2)


Relative limiting is more safe, less aliasing, but also less sharpening. In particular, broad edges don't get any sharpening anymore. (Unless they contained halos to begin with.)

See first paragraph. If you want it to be filed, you need to rasp. :)