Log in

View Full Version : KeepGrain Function


VideoMilk78
20th September 2025, 02:20
I've been toying around with mRD_RestoreGrain for quite some time after finding it in this thread (https://forum.doom9.org/showthread.php?t=182831) however the issue was that after restoring grain there would be discoloration and artifacts around where the dirt had been. I came up with this script that expands the area around the spot slightly and puts the clean version where dirt was leaving the rest of the grain structure completely original.
/*
KeepGrain — a modification and improvement of mRD_RestoreGrain, developed by VideoMilk78.

Requirements:
Avisynth+ 3.7.4
masktools2.dll
OverlayPlus.avsi
ExpandMask (included)
mRD_RestoreGrain (included)

Usage:
KeepGrain(Clip "source",Clip "clean",Int "expand",int "blur")

Parameters:
Source, unfiltered film source
Clean, source cleaned with a dirt removal filter of choice
Expand, expands the restore mask
Blur, softens the edges of restore mask
*/

Function KeepGrain(Clip "source",Clip "clean",Int "expand",int "blur") { #https://forum.doom9.org/showthread.php?p=2022883#post2022883
Expand=default(expand,4)
Blur=default(Blur,expand/4)
Grain=clean.mRD_RestoreGrain(source,40,40)
mask=mt_lutxy(source, grain, expr="x y - abs 0 > 255 0 ?", u=1, v=1).ExpandMask(expand,blur)
OverlayPlus(source,clean,mask=mask,chroma=true)
}

Function mRD_RestoreGrain(clip rd, clip o, float "str1", float "str2") { #https://forum.doom9.org/showthread.php?p=1943371#post1943371
sisavs26 = !(VersionNumber() < 2.60)
str1 = string(default(str1, 10.0))
str2 = string(default(str2, 20.0))
expr = sisavs26 ? " x range_half - abs "+str1+" scalef min "+str2+" scalef x range_half - abs - min 0 max x range_half - x range_half - abs 1 max / * range_half + " : " x 128 - abs "+str1+" min "+str2+" x 128 - abs - min 0 max x 128 - x 128 - abs 1 max / * 128 + "
Return sisavs26 ? mt_makediff(o, rd).mt_lut(expr, use_expr=2).mt_adddiff(rd, chroma="copy second") : mt_makediff(o, rd).mt_lut(expr).mt_adddiff(rd, chroma="copy second")
}

function ExpandMask(clip mask, int amount, float "blur") {
blur = Default(blur, 0)

for (i = 1, amount) {
mask = mask.mt_expand(chroma="-128")
}
if (blur > 0) {
mask = mask.mt_inflate()
}

return mask
}

Arx1meD
22nd September 2025, 19:53
Clip "clean" can be cleaned with any filter?

VideoMilk78
22nd September 2025, 22:31
Clip "clean" can be cleaned with any filter?

Yes, now keep in mind if the dirt is in a place motion then a harsh filter like spotless will still have artifacts.

I have been using it with RemoveDirtMC(20) and this does a great job with small spots.

VideoMilk78
11th November 2025, 23:59
V1.01
See first post

VideoMilk78
19th November 2025, 00:17
V1.02
See first post


I don't see where else to go from here, as I'm already obtaining amazing results with V1.02. I want a function that can take the grain of the source and make grain based off of that to put in the filled areas, but so far I haven't seen any filter that really suits my interests.

StainlessS
19th November 2025, 11:01
Milky,
I have not tried the function, but, is there an extra '}' character at the end of the the OP post [EDIT: in the code block], I think it might throw an error as it is.

VideoMilk78
19th November 2025, 14:36
Thanks for pointing this out, it's been fixed.
Curious to see what you think of KeepGrain and if you have any suggestions :D

StainlessS
19th November 2025, 17:38
Sorry, I'm a bit busy for the next month or so.
I just really pop by for a few moments to have a qwik peep at posts.

Frank62
20th November 2025, 12:10
Great! Thanks for this!!

Selur
20th November 2025, 17:10
Side note: I made a quick attempt to port KeepGrain to Vapoursynth: https://pastebin.com/QWUyAr1t

Cu Selur

VideoMilk78
20th November 2025, 18:15
Thanks Selur, I wonder if I could make this work on VapourSynth MAC arm64? I have no experience with VapourSynth but as I only have a mac it would be more convenient than running Avisynth in a windows 10 emulator.

Selur
20th November 2025, 19:54
The script doesn't need anything that doesn't come with Vapoursynth itself and optionally uses llvmexpr or akarin, so I don't see a reason why it shouldn't work on MacOS if Vaoursynth is installed.