Spatio-Temporal Limited Median filter for grain removal

Plugin for Avisynth 2.5
Version 0.8.2
Copyright (C)2004-2006 Alexander G. Balakhnin (aka Fizick).

DeGrainMedian plugin is mainly for film grain removal, but may be used for general denoising.
It uses some spatio-temporal limited median method.


DeGrainMedian(clip, int "limitY", int "limitUV", int "mode", bool "interlaced", bool "norow" )

Filter Parameters

first parameter as always is input clip or default last,

limitY (0 to 255, default=4) -limit of pixel luma correction,

limitUV (0 to 255, default=6) - limit of pixel chroma correction,

mode (0 to 5, default=1) - operation mode (the more, the weaker)

interlaced (true or false, default=false) - process clip as interlaced

norow (true or false, default=false) - do not use same row in spatial filter

Using for grain and noise removal

Load plugin, adjust parameters value for your clip.

Set limitY to typical value about 2-8 (compromise between denoising and bluring).

It seems, that bigger values of limitUV for chroma (4-15) are often quite safe.

Set mode parameter to bigger value if you found picture as oversmoothed (try!).

The more mode number, the bigger limit values is safe.

It seems, that mode=1 or 2 is good compromise, but it is dependent from source.

With mode=5 the result changes are invisible, but some of "hot" pixels are removed.

Option norow=true is useful on video with horizontal noise stripes , such as analog TV or VHS captures.

Simple sample scripts (use correct file path)

Subtle filtering but useful for hot pixels removal:


Two-stage filtering for more full noise removal:


Strong filtering for big grain removal:


Filtering for horizontal noise stripes removal:

DeGrainMedian(limitY=5,limitUV=7,mode=1, norow=true)

Technical details

The plugin is mostly based on two ideas, used at two stages of processing.

First stage - detecting

The first idea is from STMedianFilter plugin by Tom Barry
I also use part of its great optimized code and put some part of its nice doc (edited a little) here:

"STMedianFilter is a (slightly motion compensated) spatial/temporal median filter. It fairly very fine grained, using only adjacent pixels in space and time, so it looks at the adjacent 26 locations to filter each location. It filters both luma and chroma....
...A simple median filter is just a clipping step where a value is set to not extend past the high and low of its neighbors.
(probably more strictly: median is middle member of ranked (ordered by value) series - Fizick).
For instance, if you had 3 pixels in a row that had the values <5,8,7> you could clip the center one to not be outside the low of 5 or the high of 7, so you would set it to 7.
Now imagine you had a small 3x3 video screen, like one surface of a Rubiks cube. Imagine the previous frame was the bottom layer of the cube, the current frame was the middle layer, and the next frame was the top.
Then the current center pixel would be right in the center of the Rubics cube and there would be 13 ways you could draw a line through it and a pair of two nearest neighbors.
What I (and I - Fizick) did was to compare each of those pairs of neighbors to see which pair was most agreeing on value. I (and I - Fizick) used that pair to clip the value of the center pixel." (The end of Tom Barry citation).

Very interesting RemoveGrain plugin by Kassandro in mode=9 works similar to STMedianFilter at this stage, but in spatial plane only (not in temporal). It than changes pixel to this value of neighbors (by minimax). But it can not remove middle sized grain from my bad film source.
RemoveGrain is considered by Kassandro as prefilter for RemoveDirt, he says in doc:
"If grain is too crude, RemoveGrain can only partially remove it or cannot remove it at all."
( The end of Kassandro citation)
It seems, that it is mostly due to lack of temporal information from neighbor frames.

Since version 0.2, I add to DeGrainMedian also some other more safe operation modes 1-4(later 5) (code borrowed from RemoveGrain).
We consider the same 3x3x3 cube.
New pixel candidate value (newp) is clipped by values of neighbor pixels from pair (bound1 and bound2).
But mode1-mode5 use more safe criterion (weight) of optimal pixel pair,
We take into account also the difference of new value from central pixel old value (oldp) .

Mode=0 of DeGrainMedian (similar to mode=9 of RemoveGrain) uses weight=|bound1 -bound2|, it is strongest.
Mode=1 of DeGrainMedian (more strong than mode=8 of RemoveGrain) uses weight=|oldp - newp|+4* |bound1 - bound2|
Mode=2 of DeGrainMedian (similar to mode=8 of RemoveGrain) uses weight=|oldp - newp|+2* |bound1 - bound2|
Mode=3 of DeGrainMedian (similar to mode=7 of RemoveGrain) uses weight=|oldp - newp|+ |bound1 - bound2|
Mode=4 of DeGrainMedian (similar to mode=6 of RemoveGrain) uses weight=2*|oldp - newp|+ |bound1 - bound2|
Mode=5 of DeGrainMedian (similar to mode=5 of RemoveGrain) uses weight=|oldp - newp|, it is weakest.

norow option excludes horizontal pixels pair of same frame, so filter considers 12 pairs only.

Second stage - filtration

STMedianFilter at second stage don't filter pixel if it would change it more than some threshold.
(More correctly, it firstly makes temporal detecting and thresholding,
with following spatial detecting and thresholding)

The most denoisers also do not filter pixels with big noise (big luma differences).
But both such single pixels are very visible on flat smoothed regions,
and group of them forms very annoying edge artifacts.

Moreover, pure temporal denoisers often produce ghosting even for not very big thresholds.

DeGrainMedian at this second stage uses other (different) method of pixel processing.

Here I use second idea, borrowed from Dust plugin by Steady. See part of Dust doc here:

Sets the strength of the temporal filtering. (How much it can change the original pixel)." (The end of Steady citation).

So, if filtered pixel would be changed more than some threshold (limit),
it will NOT be restored to original in DeGrainMedian,
but will be changed by LIMITED value.
DeGrainMedian plugin filters ALL pixels, but with limited strength.
Therefore, all grain and strikes are (fully or partially) smoothed.

Filter almost don't produce edge artifacts.
Ghosting is also minimal, since it is automatically switched to spatial smoothing as more nearest pair by values.
You may select one of 6 modes of operation, from strong (but smoothed) to weak (but sharp).

So, DeGrainMedian have a speed of STMedianFilter (true) and strength of Dust (true by limit value :-)!
It is almost not joke :-).

It can greatly increase the compession degree.

What is bad, really?

1. Some blurring (you want denoising without it ? :-) .
2. It can not produce flat fully smooth picture, some noise always remains (you like oversmoothed and blocked clips ?).
3. Motion compensation is limited to 1 pixel value (of course, it is NOT as Dust, but what about external motion compensation ?).

Seriously, it is not super filter and still experimental, but for my noisy grainy films, it makes quite good results.
Firstly I developed it as prefilter (to Vaguedenoiser etc), but it seems now, that sometimes it can be used alone...
Probably, it can help somebody else. That is why I release it.

Some note. With limitY=255, DeGrainMedian must have the same (or similar) output
as STMedianFilter with thresholds=255 (and with the same artifacts), but it is not true now,
probably due to some bug or bugs :-(

Edit: It is true now - it was a bug in STMedianFilter, I fixed it in v 1.0.3.

Combining usage

It must be applied before other denoising filters.
Sometimes I use two DeGrainMidian filters in script with lower limits for better noise removal.
The better results for films restoration within Avisynth can be achieved with my global motion compensation plugin DePan.

Features and limitations of current version

  1. Works only in YV12 and YUY2 color format.
  2. Requires a P-III, Athlon, or higher. Needs SSEMMX (Integer SSE) support.
  3. No scenechange detector in current version. But I do not see any ghosting - filter is switched to spatial pixel pairs.
  4. First and last line (line pair for interlaced) of every frame are not filtered.
  5. First and last 8 (YV12) or 4 (YUY2) pixels in every row are filtered with more simple (same columns) method.
  6. Chroma filtering in YUY2 mode is also in same columns only.
  7. First and last frames are not filtered
  8. Tested with Avisynth 2.5.5.
  9. It is very fast filter.

More info

See doom9 forum special thread:


I use free MS VC++ Toolkit 2003 with MS Platform SDK.
Use make file "makefile" with command:


(note: copy lost nmake.exe and cvtres.exe from Platform SDK directory Bin\win64 to Bin ).


This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

Documentation is distributed under CreativeCommons BY-SA 3.0 license.

If you like my plugins, please consider to make some donation.

Version changes:

Download DeGrainMedian version 0.8.2

Return to main page