View Full Version : Deathray - OpenCL GPU accelerated spatial/temporal non-local means de-noising
Jawed
27th July 2016, 17:34
I could make a 64-bit version, but I don't know what you could use it for, since Deathray only works with Avisynth and that's 32-bit only. Isn't it?
How would you use a 64-bit version?
captaiŋadamo
27th July 2016, 17:53
Avisynth+ has had 64-bit builds for a while now.
Jawed
27th July 2016, 18:40
Does Deathray work with Avisynth+ ?
Groucho2004
27th July 2016, 18:55
Does Deathray work with Avisynth+ ?Sure, no problem.
captaiŋadamo
27th July 2016, 20:20
Does Deathray work with Avisynth+ ?
Wouldn't see why not. One of the main selling points is plugin compatibility.
Magik Mark
28th July 2016, 01:01
I guess there's only one way to find out
Is it possible to have a template similar to light, medium, and strong as a starting point?
Sent from my iPhone using Tapatalk
Jawed
28th July 2016, 19:32
I believe that I would have to create a new version of Deathray to be able to support 64-bit Avisynth+, because I would be linking against Avisynth+'s 64-bit lib. Instead of Avisynth's 32-bit lib.
These are two completely different pieces of software.
I have no idea how much work that involves.
Jawed
28th July 2016, 21:30
I guess there's only one way to find out
Is it possible to have a template similar to light, medium, and strong as a starting point?
Start here:
deathray(hy=0.3, huv=0.3, ty=0, tuv=0, s=1, x=1, l=false, c=false, z=true, b=true) # light
If you don't have colour noise, set huv=0.
Set hy as low as 0.1 or as high as you like. Similar for huv.
Set ty to 3 or 5 if you want to try temporal denoising (lots of frames). Set tuv similarly if you have colour noise:
deathray(hy=0.5, huv=0.5, ty=3, tuv=3, s=1, x=1, l=false, c=false, z=true, b=true) # medium
Try x=2 (don't use higher) for another way to increase strength:
deathray(hy=1, huv=1, ty=5, tuv=5, s=1, x=2, l=false, c=false, z=true, b=true) # heavy
I don't think there's any reason to go beyond 5 for ty and tuv.
I prefer MDegrain3.
By far my favourite is Deathray2 making a calm clip for MDegrain3 with a very light post-denoise by Deathray. That combination is on a whole other level.
ultim
28th July 2016, 22:32
I believe that I would have to create a new version of Deathray to be able to support 64-bit Avisynth+, because I would be linking against Avisynth+'s 64-bit lib. Instead of Avisynth's 32-bit lib.
These are two completely different pieces of software.
I have no idea how much work that involves.
The headers are the same for both x32 and x64, and a plugin never needs to link with the Avisynth .lib at all. So the extra effort is as small as just compiling with the x64 toolchain (doing an extra build) without any code changes, assuming that your own code is x64-compatible.
Jawed
28th July 2016, 22:59
The headers are the same for both x32 and x64, and a plugin never needs to link with the Avisynth .lib at all. So the extra effort is as small as just compiling with the x64 toolchain (doing an extra build) without any code changes, assuming that your own code is x64-compatible.
Thanks, you're right I had a look and the external libs include OpenCL and AMD APP, but not Avisynth.
I will have a quick play...
Jawed
28th July 2016, 23:49
My avisynth.h has the following line 347:
void* operator new(unsigned size);
And when I try to compile for 64-bit I get the error message:
error C2821: first formal parameter to 'operator new' must be 'size_t'
So, I've changed it to:
void* operator new(size_t size);
Which compiles. I have no idea if that's broken it :eek: (I'm assuming that the 64-bit avisynth uses the same fix...)
Now, anyone who'd like to experiment, go here:
https://github.com/JawedAshraf/Deathray/tree/x64-experimental
and right-click the Deathray.dll file and save the target file.
So if you're feeling brave, see what happens when you use this experimental 64-bit release. I don't know how to test it...
(I have written other non-Avisynth 64-bit DLLs that work fine (using OpenCL), so I'm pretty hopeful about this.)
Groucho2004
28th July 2016, 23:55
You should use the latest AVS+ header from here (https://github.com/AviSynth/AviSynthPlus/tree/MT/avs_core/include).
Jawed
29th July 2016, 00:06
OK, tomorrow night, nearly my bedtime.
Magik Mark
29th July 2016, 04:54
Thanks for the preset. It makes our testing a lot easier. By the way,. How will you classify video grain? Luminance noise? Will the light setting be able to take care of this?
Sent from my iPhone using Tapatalk
Jawed
29th July 2016, 09:37
Thanks for the preset. It makes our testing a lot easier. By the way,. How will you classify video grain? Luminance noise? Will the light setting be able to take care of this?
I made an error in the heavy preset:
deathray(hy=1, huv=3, ty=5, tuv=5, s=1, x=2, l=false, c=false, z=true, b=true) # heavy
huv should be set to 1. I've corrected that.
There is no classification of video grain or luminance noise.
I'm afraid these "presets" are literally just starting points.
Also, bear in mind that apart from a very light preset and as a pre-processor, I don't use Deathray. I certainly wouldn't use it as the sole de-noiser. Except for a quick and dirty de-noise.
Jawed
29th July 2016, 23:11
You should use the latest AVS+ header from here (https://github.com/AviSynth/AviSynthPlus/tree/MT/avs_core/include).
Thanks.
As I expected, there was quite a bit more effort involved than simply using the new header. The commits show the gory detail.
So, I've updated the DLL. See post 111 for instructions on getting this DLL. I haven't tested it.
Have fun!
Groucho2004
29th July 2016, 23:37
As I expected, there was quite a bit more effort involved than simply using the new header. The commits show the gory detail
Looking at the commits, it's pretty much the usual things required for updating AVS2.5 filters to the AVS2.6 interface.
Thanks for the update, I'll give it a whirl soon.
spiralout
21st January 2023, 20:46
Anybody get the 64bit Deathray working?
Jawed
7th March 2025, 16:16
Recently, I fixed a memory leak issue with Deathray2 and then earlier today, Jamaika started to submit some pull requests for it.
It's been years since I've been on here. And it was, until a few weeks ago, years since I wrote/used an Avisynth script or even did any video encoding. So, I'm out of touch. Apparently I didn't make an official Deathray2 thread.
So, for the time being I'm going to make this the Deathray2 thread.
You can download it here:
https://github.com/JawedAshraf/Deathray2/blob/master/Deathray2.dll
which is from:
https://github.com/JawedAshraf/Deathray2
This is the readme:
Deathray2
=========
An Avisynth plug-in filter for spatial/temporal non-local means de-noising.
Created by Jawed Ashraf - Deathray@cupidity.f9.co.uk
Installation
============
Copy the Deathray2.dll to the "plugins" sub-folder of your installation of
Avisynth.
De-installation
===============
Delete the Deathray2.dll from the "plugins" sub-folder of your installation of
Avisynth.
Compatibility
=============
The following software configurations are known to work:
- Avisynth 2.5.8 and 2.6 MT (SEt's)
The following hardware configurations are known to work:
- AMD HD 7770
- AMD HD 7970
Known non-working hardware:
- ATI cards in the 4000 series or earlier
- ATI cards in the 5400 series
Video:
- Deathray2 is compatible solely with 8-bit planar formatted video. It has
been tested with YV12 format.
Usage
=====
Deathray2 separates the video into its 3 component planes and processes each
of them independently. This means some parameters come in two flavours: luma
and chroma.
Filtering can be adjusted with the following parameters, with the default
value for each in brackets:
hY (1.0) - strength of de-noising in the luma plane.
Cannot be negative.
If set to 0 Deathray2 will not process the luma plane.
hUV (1.0) - strength of de-noising in the chroma planes.
Cannot be negative.
If set to 0 Deathray2 will not process the chroma planes.
tY (0) - temporal radius for the luma plane.
Limited to the range 0 to 64.
When set to 0 spatial filtering is performed on the
luma plane. When set to 1 filtering uses the prior,
current and next frames for the non-local sampling
and weighting process. Higher values will increase
the range of prior and next frames that are included.
tUV (0) - temporal radius for the chroma planes.
Limited to the range 0 to 64.
When set to 0 spatial filtering is performed on the
chroma planes. When set to 1 filtering uses the prior,
current and next frames for the non-local sampling
and weighting process. Higher values will increase
the range of prior and next frames that are included.
s (1.0) - sigma used to generate the gaussian weights.
Limited to values of at least 0.1.
The kernel implemented by Deathray2 uses 7x7-pixel
windows centred upon the pixel being filtered.
For a 2-dimensional gaussian kernel sigma should be
approximately 1/3 of the radius of the kernel, or less,
to retain its gaussian nature.
Since a 7x7 window has a radius of 3, values of sigma
greater than 1.0 will tend to bias the kernel towards
a box-weighting. i.e. all pixels in the window will
tend towards being equally weighted. This will tend to
reduce the selectivity of the weighting process and
result in relatively stronger spatial blurring.
x (1) - factor to expand sampling.
Limited to values in the range 1 to 4.
By default Deathray2 spatially samples 49 windows
centred upon the pixel being filtered, in a 7x7
arrangement. x increases the sampling range in
multiples of the kernel radius.
Since the kernel radius is 3, setting x to 2 produces
a sampling area of 13x13, i.e. 169 windows centred
upon the target pixel. Yet higher values of x such as
3 or 4 will result in 19x19 or 25x25 sample windows.
l (false) - redundant option to be removed
c (true) - redundant option to be removed
z (false) - redundant option to be removed
b (false) - option non-functional
a (8) - alpha sample set size.
limited to values in the range 8 to 128, with values
rounded down to the nearest multiple of 8.
Deathray2 sorts the samples in order to exclude the
worst samples. This improves detail retention while
enabling strong filtering.
Avisynth MT
===========
Deathray2 is not thread safe. This means that only a single instance of
Deathray2 can be used per Avisynth script. By extension this means that
it is not compatible with any of the multi-threading modes of the
Multi Threaded variant of Avisynth.
Use:
SetMTMode(5)
before a call to Deathray2 in the Avisynth script, if multi-threading
is active in other parts of the script.
Multiple Scripts Using Deathray2
================================
The graphics driver is thread safe. This means it is possible to have
an arbitrary number of Avisynth scripts calling Deathray2 running on a
system.
e.g. 2 scripts could be encoding, another could be running in a media player
and another could be previewing individual frames in AvsP or VirtualDub.
Eventually video memory will probably run out, even though it's virtualised.
Usage warnings:
It does not denoise the area just inside the four borders of the frame.
It is not a drop-in replacement for Deathray as the arguments it supports are different.
It only works with 32-bit Avisynth on Windows.
The reason I didn't officially release it, years ago, was that it wasn't finished. It was excellent for my own use, but needed some tidying. And laziness prevailed.
I'm publishing officially now merely because there may be some interest and because I fixed that memory leak, which was a foolish mistake by me.
Jawed
7th March 2025, 17:25
I wrote Deathray2 inspired by a concept in photography software called DxO for image denoising, in which a threshold is used to reject non-local sample weights, when performing non-local means denoising.
Deathray2 sorts the weights that are produced during non-local analysis and then uses the "best" of those weights for the final denoising. The count of weights is specified with the parameter "a".
Surprisingly this works fairly well. In theory the weights, themselves, should produce this "sorted by quality" characteristic, but in practice non-local means denoising tends to be blurry spatially and blotchy temporally. Sorting the weights makes it less ugly, though you can still produce a blurry mess if you really want.
I don't use Deathray or Deathray2 for video denoising, because it's not that good - I don't care how fast it is. Instead I use Deathray2 to create a calm clip, which I then feed in to MDegrain, part of MVTools.
Jawed
7th March 2025, 17:28
SadisticCalm is the function I have created that uses Deathray2 to produce a calm clip. Notice that it creates an "apron" which solves the problem with denoising near the four borders of the frame.
function SadisticCalm(clip source, float "hY", float "hUV", int "tY", int "tUV", int "x", int "a")
{ # Creates a calm clip using Deathray2, to be consumed by MVTools for denoising
hY = Default(hY, 1000) # Deathray2 luma strength
hUV = Default(hUV, 1000) # Deathray2 chroma strength
tY = Default(tY, 0) # Deathray2 luma temporal radius
tUV = Default(tUV, 0) # Deathray2 chroma temporal radius
x = Default(x, 1) # Deathray2 sample expand factor
a = Default(a, 40) # Deathray2 alpha sample count
SetMTMode(2)
corners = source.FlipHorizontal().FlipVertical()
edges = source.FlipVertical()
sides = source.FlipHorizontal()
apron = StackVertical(StackHorizontal(corners, edges, corners), StackHorizontal(sides, source, sides), StackHorizontal(corners, edges, corners))
apron = apron.crop(source.width - 16, source.height - 16, source.width + 32, source.height + 32, true)
SetMTMode(5)
calm = apron.deathray2(hy=hY, huv=hUV, ty=tY, tuv=tUV, s=1, x=x, a=a) # Deathray2 will eventually get its own apron capability
SetMTMode(2)
calm.crop(16,16,-16,-16,true) # remove apron
}
I use SeT's multi-threading Avisynth, hence the SetMTMode commands.
I use this function for the actual denoising, based upon the calm clip produced by SadisticCalm:
function MDegrainNCalm(clip source, clip calm, int temporal, int "blksize", int "overlap", int "sharp", int "thSAD", int "thSCD1", bool "RefineMotion")
{ # Motion compensated denoiser for progressive source clip
# Uses MVTools2
blksize = default(blksize,16) # blksize value (4, 8 or 16)
overlap = default(overlap,blksize/2) # overlap value (0 to half blksize)
sharp = default(sharp,2) # 0=bilinear softest, 1=bicubic, 2=Wiener sharpest
thSAD = default(thSAD,300) # higher risks motion ghosting and swimming, lower risks blotchy denoising
thSCD1 = default(thSCD1, thSad>300 ? 3*thSAD/4 : 300) # Scene change detection. Defaults to thSAD*3/4 when more than 300 because bad blocks on scene change affect more frames, becoming easier to see
RefineMotion = default(RefineMotion,true) # true means MRecalculate will be used to improve motion vectors
halfblksize = blksize/2 # MRecalculate works with half block size
halfoverlap = overlap/2 # Halve the overlap to suit the halved block size
halfthSAD = thSAD/2 # MRecalculate uses a more strict thSAD, which defaults to 150 (half of function's default of 300)
dct=5
source = source.assumeframebased() # MSuper pel=2 is faster with this
calm_super = calm.MSuper(pel=2, hpad=blksize, vpad=blksize, sharp=sharp)
source_super = source.MSuper(pel=2, hpad=blksize, vpad=blksize, sharp=sharp,levels=1)
recalculate = calm.MSuper(pel=2, hpad=blksize, vpad=blksize, sharp=sharp,levels=1)
all_vectors = MAnalyse (calm_super, multi=true, blksize=blksize, overlap=overlap, delta=temporal, dct=dct)
all_vectors = RefineMotion ? MRecalculate(recalculate, all_vectors, tr=temporal, blksize=halfblksize, overlap=halfoverlap, thSAD=halfthSAD) : all_vectors
MDegrainN(source, source_super, all_vectors, temporal, thSAD=thSAD, thSCD1=thSCD1)
}
That's using Ferenc's amazing MVTools2 MDegrainN, which makes me very happy.
Jawed
7th March 2025, 17:58
So this is an example of how I might use Deathray2:
setmemorymax(2048)
setmtmode(5,16) # I have 16 cores, 32 threads on my CPU
FFVideoSource("something", colorspace="YV12")
original=last
calm=SadisticCalm()
SetMTMode(2)
original.MDegrainNCalm(calm, 5, thsad=175)
The results are generally spectacular.
In reality I run multiple scripts one after the other, passing a lossless-encoded video into each successive script. This is my solution to the limited memory of 32-bit Avisynth :) So I encode first with SadisticCalm on its own in a script and then feed the output video into a script that solely runs MDegrainNCalm to do the denoising.
There are three parameters to play with:
Deathray2 strength, using "hY" and "hUV": 1 to 5 is safe; 10-1000 is strong; 10000 to 100000 is getting fairly blurry.
Deathray2 alpha set size "a", 8 or 16 is quite limited, 40 is the strongest I recommend for spatial Deathray2.
MDegrainN thSAD from 50 to 300. More than 300 indicates that you should be setting hY and/or hUV to higher values or increasing alpha set size up to a=40.
I don't see any real value in temporal denoising with Deathray2 when using it to make a calm clip, it's just slow. If you're going to try it, then start with tY=5 and tUV=5. Alpha set size "a" can be set to values in the range 48 to 128 to see good results.
When Deathray2 is used for spatial denoising the difference between a=40 and a=48 is shocking. 48 really spoils the quality (when combined with MVTools denoising) and I recommend against it.
The parameter "x" should be left at 1. I've never found value in raising it.
[I posted a few times in succession because of the very long lines of code in the previous post]
vBulletin® v3.8.11, Copyright ©2000-2026, vBulletin Solutions Inc.