View Single Post
Old 14th April 2011, 02:14   #13  |  Link
pbristow
Registered User
 
pbristow's Avatar
 
Join Date: Jun 2009
Location: UK
Posts: 263
Well, I have now firmly established that the key to separating these unwanted stripes from wanted details really is the smallness of the luma changes involved, rather than their verticality.

So, throwing caution to the wind and my trousers in the washing machine, I have spent today learning how to use the MT_lut... family of filters, and creating my own crude approximation to a "horizontal only SpatialSoften". Here it is, complete with explanatory comments (written mostly for my own benefit, as otherwise I keep forgetting what things do!).

N.B. I will probably change the name to something more sensible at some point. )

Code:
function RollMeOwn(Clip C, int "rad", int "thr", int "option")
{
	# By Paul Bristow (pbristow).
	# Performs selective horizontal averaging of luma, where variation is already low.
	# (Similar in concept to SpatialSoften, but cruder and only working in one dimension.)
	#
	# Works in two stages:
	# 1. First look at a horizontal region within +/- rad of the current pixel;
	#  - If the pixel's luma value is *very* close (difference less than half of thr) to the average in that region, then replace it with that average.
	# 2. Otherwise, look at a small region, +/- 0.5*rad of the pixel;
	#  - If the pixel's luma value is *fairly* close (difference less than thr) to the average in the smaller zone, then replace it with that average.
	#
	# This approach avoids excessively wide blurring near the edges of dark regions, while giving lots of blur within the dark regions. 

	rad = Default(rad, 4)
	thr_ = Default(thr, 3)
	THR=string(thr_)
	HALF_THR=string(thr_/2)
	option = Default(option, 0)

	# Create required blurring pattern, given the radius:
	Blurred = Rad == 1 ? 	C.Mt_Convolution(Horizontal=" 1 1 1 ", vertical = " 1 ", u=1, v=1) : \
		  Rad == 2 ? 	C.Mt_Convolution(Horizontal=" 1 1 1 1 1 ", vertical = " 1 ", u=1, v=1)   : \
		  Rad == 3 ? 	C.Mt_Convolution(Horizontal=" 1 1 1 1 1 1 1 ", vertical = " 1 ", u=1, v=1)  : \
		  Rad == 4 ?	C.Mt_Convolution(Horizontal=" 1 1 1 1 1 1 1 1 1 ", vertical = " 1 ", u=1, v=1) : \
		  Rad == 5 ?	C.Mt_Convolution(Horizontal=" 1 1 1 1 1 1 1 1 1 1 1 ", vertical = " 1 ", u=1, v=1) : \
		  Rad == 6 ?	C.Mt_Convolution(Horizontal=" 1 1 1 1 1 1 1 1 1 1 1 1 1 ", vertical = " 1 ", u=1, v=1) : \
		  Rad == 7 ?	C.Mt_Convolution(Horizontal=" 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 ", vertical = " 1 ", u=1, v=1) : \
		  Rad == 8 ?	C.Mt_Convolution(Horizontal=" 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 ", vertical = " 1 ", u=1, v=1) : \
		  C

	Blurred2 = Rad == 1 ? 	C.Mt_Convolution(Horizontal=" 1 2 1 ", vertical = " 1 ", u=1, v=1) : \
		  Rad == 2 ? 	C.Mt_Convolution(Horizontal=" 1 1 1 ", vertical = " 1 ", u=1, v=1)   : \
		  Rad == 3 ? 	C.Mt_Convolution(Horizontal=" 1 2 2 2 1 ", vertical = " 1 ", u=1, v=1)  : \
		  Rad == 4 ?	C.Mt_Convolution(Horizontal=" 1 1 1 1 1 ", vertical = " 1 ", u=1, v=1) : \
		  Rad == 5 ?	C.Mt_Convolution(Horizontal=" 1 2 2 2 2 2 1 ", vertical = " 1 ", u=1, v=1) : \
		  Rad == 6 ?	C.Mt_Convolution(Horizontal=" 1 1 1 1 1 1 1 ", vertical = " 1 ", u=1, v=1) : \
		  Rad == 7 ?	C.Mt_Convolution(Horizontal=" 1 2 2 2 2 2 2 2 1 ", vertical = " 1 ", u=1, v=1) : \
		  Rad == 8 ?	C.Mt_Convolution(Horizontal=" 1 1 1 1 1 1 1 1 1 ", vertical = " 1 ", u=1, v=1) : \
		  C

# Option 1: Decide based on similarity to pre-computed average (i.e. the blurred clip):
	OptA =	MT_Lutxy(C, Blurred,  		expr = " X Y - abs  "+THR+" <=  Y X ? " )

# Option 1b: Two-stage version:
	OptB =	MT_Lutxyz(C, Blurred, Blurred2, expr = " X Y - abs  "+HALF_THR+" <=  Y     X Z - abs  "+THR+" <=  Z X ?   ? " )

# Option1c: ...with added brightness threshold:
	OptC =	MT_Lutxyz(C, Blurred, Blurred2, expr = " Y 48 >=   X    X Y - abs  "+HALF_THR+" <=  Y     X Z - abs  "+THR+" <=  Z X ?   ?   ? " )

# Option1d: ...with added brightness threshold only applied to the wider blur situation:
	OptD =	MT_Lutxyz(C, Blurred, Blurred2, expr = " Y 48 <   X Y - abs  "+HALF_THR+" <=   &    Y     X Z - abs  "+THR+" <=  Z X ?   ? " )
# ... i.e.:	"If local brightness is less than 48 AND consistent over a wide area, then use the wider average; 
# 		 else, if brightness is consistent over the narrower area, use the narrower average; 
#		 otherwise leave the original alone.


# Option1e: Better selection method, based on max difference within the region of interest:.
# This prevents averaging too widely over regions with gradual gradients, or which are bordering a bright area 
# at one end and a dark area at the other (so the average is close to the centre value, but the extremes aren't);

	MaxDiffs  = mt_luts( C, C, mode = "max", pixels = mt_rectangle( rad, 1 ), expr = "x y - abs" )
	MaxDiffs2 = mt_luts( C, C, mode = "max", pixels = mt_rectangle( rad/2, 1 ), expr = "x y - abs" )
	# First lay in the narrow blurring where appropriate:
	C2 = MT_Lutxyz(C, Blurred2, MaxDiffs2, expr = "              Z      "+THR+"    <=           Y     X   ? " )
	# Now overlay the wider blurring where appropriate. (Wide trumps narrow, wherever both could apply.)
	OptE = MT_Lutxyz(C2, Blurred,  MaxDiffs,  expr = " Y 48 <       Z   "+HALF_THR+"  <=    &      Y     X   ? " )

# Option f: ...And for speed, an option with just the wider blurring:
	OptF = MT_Lutxyz(C,  Blurred,  MaxDiffs,  expr = " Y 48 <       Z   "+HALF_THR+"  <=    &      Y     X   ? " )

# Option g: ...And one with just the narrower blurring:
	OptG = MT_Lutxyz(C,  Blurred2, MaxDiffs2, expr = "              Z      "+THR+"    <=           Y     X   ? " )


# Choose and return final result:
	return  option == 1 ? OptA :\
		option == 2 ? OptB :\
		option == 3 ? OptC :\
		option == 4 ? OptD :\
		option == 5 ? OptE :\
		option == 6 ? OptF :\
		option == 7 ? OptG :\
		C
}
Gosh, I am proud!

Using option=7 (with rad=5, thr=4) in the context of my overall script (which includes things like yadif, TemporalSoften(1,5,6) and SoftLevels to apply the gamma curve), I get 5.0fps, which is the speed goal I was aiming for. The results are at least as good as I got with SpatialSoften(3,3,3) running at only 3.3 fps. For tougher cases I can use option 5, which uses 2 passes and completely nukes the stripes with no ill effects at all... although it only runs at 3fps.

Since I own the camera that produces these weird effects (and it is definitely the CCD that's doing it: I noticed that on a shot where I used the camera's image stabiliser, when the camera moves the lines start to shift a moment later, and drift back into their original position once the camera settles), I think I'll be needing this function quite a lot! (That's what you get for buying 2nd hand, I guess... )

If anyone can offer tuning tips to make it run any faster, without sacrificing effectiveness, please do. In the meantime, I can get back to running those overnight clean-up jobs, knowing that each one will at least be finished by next lunchtime!

Last edited by pbristow; 14th April 2011 at 02:44. Reason: (just fixing typos)
pbristow is offline   Reply With Quote