Log in

View Full Version : SpotLess - Temporal denoise


Pages : 1 2 3 4 5 6 7 [8]

StainlessS
1st April 2025, 07:47
If distortion is around the edges, try adding padding of maybe 8 or 16 around distorted edges (before call to Spotless).
Padding()
http://forum.doom9.org/showthread.php?p=1596804#post1596804
Then chop (crop) it off afterwards.

Bluedraft
2nd April 2025, 01:35
Hello StainlessS, the distortion appears throughout the whole image, see

Before Spotless
https://i.ibb.co/pBsSpkQT/1.png (https://ibb.co/pBsSpkQT)

After Spoless
https://i.ibb.co/prXSMZkP/2.png (https://ibb.co/prXSMZkP)

StainlessS
2nd April 2025, 12:14
Try take a look at JohnMeyer post #20 for tips.

I think that there was some kind of mod of Spotless which tried to restore some removed spots, I've spent about an hour looking for it but cant find it.
Maybe somebody knows what I was looking for.

Selur
2nd April 2025, 15:38
Maybe somebody knows what I was looking for.
You probably looking for: https://forum.doom9.org/showthread.php?t=182831 (SpotLess + DeltaRestore)

Cu Selur

Ps.: DeSpot, with tweaked settings, might be the better filter for such a source.

StainlessS
2nd April 2025, 20:39
Thanks Selur, that looks to be what I was looking for.
Have added links at head of Spotless thread to that thread + SpotRemover
[film transfer] Remove dirt but keep grain? :- https://forum.doom9.org/showthread.php?t=182831
SpotRemover - clean video from spots:- https://forum.doom9.org/showthread.php?t=182407

coolgit
19th April 2025, 02:11
Hello, I'm trying to use spotless to clean some scenes but using it by default it ends affecting the image in some parts (it generate distortion in the image), I tried playing a little with some settings but couldn't get any better (probably because I not understand very well what I'm doing). I uploaded a sample of the scene, any help will be greatly appreciated.

https://drive.google.com/file/d/1FQvveK0k9CdU9sf1FzyLwGiqcQQK8D4U/view?usp=sharing

This is my standard to go for removing defects and keeping the quality intact. ThSAD should go no higher than 2000. Any more the image gets distorted. In some case i run spotless twice which is better than increasing ThSAD.

SpotLess(BlkSz=12, OLap=8/2, pel=2, Tm=false, Bblur=0.0, ThSAD=1000, RadT=1)

rgr
8th March 2026, 15:43
This is my standard to go for removing defects and keeping the quality intact. ThSAD should go no higher than 2000. Any more the image gets distorted. In some case i run spotless twice which is better than increasing ThSAD.

SpotLess(BlkSz=12, OLap=8/2, pel=2, Tm=false, Bblur=0.0, ThSAD=1000, RadT=1)

Whether on default or these settings -- I see that it can't handle flashes (left - org, right - SpotLess).

But in other cases -- pretty good.
https://gcdnb.pbrd.co/images/2lTDElnKRYhA.png?o=1

coolgit
13th March 2026, 19:27
Flashes are rare and better to copy the original flash frames back in. In fact flashes creates havoc for many filters.

johnmeyer
14th March 2026, 02:38
Because flashes are so bad for other filters, if I have a film or video which has them, I have a simple script which compares the current frame to both the adjacent frames using a YDiff comparison. This blows up in only one direction at scene changes, but when there is a bright flash, like a photographer's flash, it will replace that frame with either a duplicate of the previous frame or use motion interpolation to synthesize a new frame.

Finding individual "bad" frames in video; save frame number; or repair (https://forum.doom9.org/showthread.php?t=174104)

rgr
19th March 2026, 18:30
Because flashes are so bad for other filters, if I have a film or video which has them, I have a simple script which compares the current frame to both the adjacent frames

Yes, I know about that filter. But I want to keep the flashes.

StainlessS
19th March 2026, 20:11
Yes, I know about that filter. But I want to keep the flashes.

You can 'pluck' out your flash frame/s using FrameSel(),
process with whatever temporal filter (Spotless), then replace the plucked out frames
from whence they came [ FrameRep() ]

FrameSel:- https://forum.doom9.org/showthread.php?t=167971

EDIT: From the FrameSel() thread:

Post #2 of 4.

2) FrameRep

FrameRep(Clip c,Clip c2 int F1, ... , int Fn, string 'scmd',string 'cmd', bool 'show', bool 'ver',bool "reject",bool "debug")

Video:- All Valid colorspaces

Audio:- Returns source clip c audio.

Args identical to FrameSel, with exception of the compulsory 2nd clip arg which is a clip of frames to replace into the 1st source clip,
and also does not have an Ordered argument, as it is ALWAYS Ordered, and FrameRep, does not have the Extract arg.

See FrameSel() description for other arguments.

FrameRep() is complimentary to FrameSel, whereas FrameSel extracts frames, FrameRep replaces or reseats them (from the c2 clip) back from
whence they came (into the c clip) and using the EXACT same arguments and commands (whether directly supplied or in string command or
file command). If you wish to extract some frames and later replace back into the source clip, you MUST use the default Ordered=True in
FrameSel or the frames may not be replaced back into the positions they originally came from.

The number of Unique Selected frames in the combined frames commands MUST EQUAL the number of frames in the c2 clip (after taking Reject
into account), or it WILL throw an error, (will not know where to put the remainder or frame shortage). This is the reason you should
use the same frame commands in both FrameSel and FrameRep so that there will be an identical number of unique frames and Ordered
sequential. If Debug is set true, it will show a little info in DebugView (Google), eg number of unique frames in a command set.

# ----------------

Here, Example to do some simple editing on selected frames, try changing REJECT to True.

Colorbars.Trim(0,-10).ShowFrameNumber() # 10 frames
sCmd="2;5;6" # Frames in SCmd string
REJECT=False # False=modify SCmd frames : True=modify frames NOT in SCmd
SELECTED=FrameSel(scmd=sCMD,ordered=TRUE,reject=REJECT,debug=true) # We are going to use FrameRep, must use ORDERED=True (OR REJECT=True)
SELECTED=Invert(SELECTED) # Do some editing
FrameRep(Last,SELECTED,scmd=sCMD,reject=REJECT,debug=true) # Put edited frames back into original Last clip (audio from Last)
return Last

# ----------------

Example use when NOT selecting/replacing in exact same position:
Lets say you have a clip with a bad frame before each scene change and you want to replace these bad frames with the frame immediately
before it.

C=Colorbars().Trim(0,-50).ShowFrameNumber() # 50 frames (0 -> 49)
BADFRAMES = "10;20;30;40" # 4 bad frames in source clip
PREBAD = " 9;19;29;39" # 4 frames immediately before bad frames
REPAIR=FrameSel(C,scmd=PREBAD) # Get the 4 frames we will duplicate
Return FrameRep(C,REPAIR,scmd=BADFRAMES) # fix bad frames with REPAIR clip from PREBAD frames

Will produce a duplicate at frames 10, 20, 30, and 40 in source clip.

When used as above you must ensure that eg BADFRAMES and PREBAD have the same number of unique frames and sequential or results
may not be as expected (ie use Ordered).

# ----------------

Same Example again using Extract arg:-

C=Colorbars().Trim(0,-50).ShowFrameNumber() # 50 frames (0 -> 49)
BADFRAMES = "10;20;30;40" # 4 bad frames in source clip
EXTRACT=FrameSel(C,scmd=BADFRAMES,ordered=True,extract=3) # Extract Previous, Target and Next frames for each Target frame
REPAIR=EXTRACT.SelectEvery(3,0) # Select each frame previous to Target bad frames, 1st of each triplet
#REPAIR=REPAIR.Invert() # So we can easily see repaired frames
Return FrameRep(C,REPAIR,scmd=BADFRAMES) # Fix bad frames with REPAIR clip using exact same BADFRAMES spec.

# ----------------

Example scripts to show use with a paint program for manual frame repair.

# Extract Script
C=Colorbars().Trim(0,-50).ShowFrameNumber.ConvertToRGB24 # 50 frames (0 -> 49), RGB24
BADFRAMES = "10;20;30;40" # 4 bad frames in source clip
EDITCLIP=FrameSel(C,scmd=BADFRAMES,ordered=True) # Extract BAD frames
EDITCLIP.ImageWriter(file="Pic%06d.bmp",type="bmp",info=true) # Write bitmaps to edit
return Last
# Need to play all way through for ImageWriter to work properly.

# Here, EDIT bad frames in paint program

# Replace Script, after editing
C=Colorbars().Trim(0,-50).ShowFrameNumber.ConvertToRGB24 # 50 frames (0 -> 49), RGB24
BADFRAMES = "10;20;30;40" # 4 bad frames in source clip
END = 3 # unique_bad_frames - 1 (number of frames in BADFRAMES-1)
REPAIRED=ImageReader(file="Pic%06d.bmp",end=END)
#REPAIRED=REPAIRED.Invert() # Hilite repaired frames, easier to see
Return FrameRep(C,REPAIRED,scmd=BADFRAMES,show=true) # Fix bad frames with REPAIRED clip using exact same BADFRAMES spec.
# NOTE, you need to tell ImageReader the last frame number to read,
# the default for End is 1000, so imagereader will read in 4 frames (0 to 3) and 997 duplicates,
# FrameRep would throw an error as it does not know what to do with the unwanted 997 duplicate frames that were 'helpfully' appended.
# The 1st script, ImageWriter(Info=True) arg, shows the FrameNumber and name for each frame, and the number shown on the last frame
# can be assigned to END in 2nd script, shows eg "Frame 3 written to: D:\Pic000003.bmp".
# The Last frame number shown by Info=True, will be correct even if BADFRAMES list (in eg string or command file) has duplicates
# as they will be sorted and only unique frames delivered by FrameSel when Ordered=True is used, eg
# BADFRAMES="40;30;20;10;20;30;40", if used in 1st script FrameSel will select exactly the same frames as BADFRAMES="10;20;30;40",
# ie 4 frames and in sequential order (Ordered=True).
# ----------------

Example scripts to show use with a paint/rotoscope program that can read in bad frames together with previous and next frames,
and manually fix frames using image data from previous or next frames to repair damage.
# Extract Script
C=Colorbars().Trim(0,-50).ShowFrameNumber.ConvertToRGB24 # 50 frames (0 -> 49), RGB24
BADFRAMES = "10;20;30;40" # 4 bad frames in source clip
EDITCLIP=FrameSel(C,scmd=BADFRAMES,ordered=True,extract=3) # Extract Previous, Target and Next frames for each BAD frame
EDITCLIP.ImageWriter(file="Pic%06d.bmp",type="bmp",info=true) # Write bitmaps to edit
return Last

# Here, EDIT in Triple frame paint/rotoscope editing program

# Replace Script, after editing triplets
C=Colorbars().Trim(0,-50).ShowFrameNumber.ConvertToRGB24 # 50 frames (0 -> 49), RGB24
END = 11 # unique_bad_frames * Extract - 1
BADFRAMES = "10;20;30;40" # 4 bad frames in source clip
EDITCLIP=ImageReader(file="Pic%06d.bmp",end=END)
REPAIRED=EDITCLIP.SelectEvery(3,1) # Select repaired BAD frames, 2nd of each triplet
#REPAIRED=REPAIRED.Invert() # Hilite repaired frames, easier to see
Return FrameRep(C,REPAIRED,scmd=BADFRAMES,show=true) # Fix bad frames with REPAIRED clip using exact same BADFRAMES spec.


In 1st example above, selected frames were EDITED/Changed and then replaced back where they came from,
You, would want to extract the flash frames, change(spotless) the entire source clip and then replace extracted flash frames back into the spotless'ed clip.

EDIT: making small changes to 1st example [Untested]

Colorbars.Trim(0,-10).ShowFrameNumber() # 10 frames
sCmd="2;5;6" # Frames in SCmd string
REJECT=False # False=modify SCmd frames : True=modify frames NOT in SCmd
SELECTED=FrameSel(scmd=sCMD,ordered=TRUE,reject=REJECT,debug=true) # We are going to use FrameRep, must use ORDERED=True (OR REJECT=True)

#SELECTED=Invert(SELECTED) # Do some editing
Last=Invert(Last) # Do some editing to original source clip [Or use Spotless or whatever instead of invert]

FrameRep(Last,SELECTED,scmd=sCMD,reject=REJECT,debug=true) # Put SELECTED [eg Flash] frames back into EDITED Last clip (audio from Last)
return Last


EDIT: You could also use Clipclop for similar functionality.

johnmeyer
13th June 2026, 01:55
I'm doing some film transfer and restoration for the first time in several years. I'd used SpotLess before, but hadn't done a lot of testing until now. On these Super 8 and 8mm transfers I'm doing now, it did a better job than the old RemoveDirtMC.

The reason for posting is that I almost went back to RemoveDirtMC after I discovered some really horrible morphing artifacts on a girl's face in a swimming pool. I've seen this stuff before when using MAnalyze motion estimation for frame interpolation. On a hunch, I changed TrueMotion=False to TrueMotion=True.

Problem solved.

I changed the defaults in my script and will not be tempted to go back to TrueMotion=False.

I searched this entire thread to see if anyone had tested this. One person did test it, but never reported on the results because he was testing other things as well.

StainlessS
13th June 2026, 10:10
Thanks for the report John.

From first post,


Tm, TrueMotion Default True. Some folk swear truemotion=false is better.

Glob, Default True (True v1.06, was same as Tm, true almost always better), Allow set MAnalyse(global) independently of TrueMotion.
From MvTools2 docs for MAnalyse,
global
Estimate global motion (at every level) and use it as an additional predictor.
Only pan shift is estimated (no zoom and rotation).
Use false to disable, use true to enable.


Perhaps you had already changed defaults in your version script.

johnmeyer
13th June 2026, 16:11
I'm not sure why I had "false" in my script, other than the comment you put in your script the "some people" (whoever they are) preferred false. I just wanted people to know that the TrueMotion setting does make a difference in this script.

BTW, thanks for both SpotLessS (as I spell it) and also GamMac. I used them both yesterday in my restoration work. I still can't get GamMac to always produce better color with these old films, so I do one restoration with GamMac, and one without, and then cut between those in my NLE to get the best color. The one without GamMac already has the color correction that I do manually in my NLE, so the colors have already been corrected as best I can.

StainlessS
13th June 2026, 18:49
Pre v1.06, Glob default was same as tm, if your used script version was prior v.1.06, and you manually changed to default tm=false (because of the 'some 'people' comment),
then Glob default would be same as your default tm of false, that would probably not be very good, [Note Glob comment, "true almost always better"].

DTL
17th June 2026, 21:41
"I just wanted people to know that the TrueMotion setting does make a difference in this script."

This script uses the very very simple example of ME with the old mvtools2 plugin. Also the difference is not only with truemotion preset switch but also with some footage + mvtools ME settings used. This is why some other users may report with different footage the better results may be with TrueMotion=false.

More advanced way of ME used in QTGMC script (different prefiltering of the input to MAnalyse + up to 2 stages of block size refining + refining search around all predictors (including temporal predictor). These features are implemented in ver 3.392s https://github.com/realfinder/AVS-Stuff/blob/Community/avs%202.6%20and%20up/QTGMC.avsi . I think someone can export the ME part from QTGMC and make it into separate script function like MAnalysePlus(all ME arguments from QTGMC) and return value as MVs clip to be used as script function in the other scripts without copy of many text from QTGMC.

Other possible solution is to use exports of MV clips from QTGMC - https://github.com/realfinder/AVS-Stuff/blob/ff6c8682f82e073d2775564c8f96ad8e496ffc8b/avs%202.6%20and%20up/QTGMC.avsi#L753 . But they are in the format of a single MV clip for single time offset (not in the format of multi MV clip). So easy enough may be created a version of Spotless with RadT up to 6.

Example for RadT=1:

Function SpotLess_QTGMC_ME_RadT1(clip c,int "ThSAD",int "ThSAD2",int "pel",bool "chroma", int "BlkSz",Int "Olap",bool "tm",Bool "glob",
\ Float "bBlur", clip "dc" ) {
ThSAD = Default(ThSAD,255*(8/2 * 8/2)) # SAD threshold at radius 1. Here is max'ed out at 255 * 1/4 of 8*8 block <ie 4080> (MvTools Default 10,000 is nearly OFF, maybe way Too High).
# Maybe above 255*8/2*8/2 still a bit high, maybe 128*(8/2 * 8/2) Better <ie 2048>. For General noise perhaps somethimg like 24 * (8 * 8) <ie 1536>.
ThSAD2 = Default(ThSAD2,ThSAD-(8*8)) # SAD threshold at radius RadT(a bit less than ThSAD).
Pel = Default(pel,2) # Default 2. 1, 2, or 4. Maybe set 1 for HD+. (1=precision to pixel, 2=precision to half pixel, 4=quarter pixel)
Tm = Default(tm,True) # TrueMotion, Some folk swear MAnalyse(truemotion=false) is better.
Chroma = Default(chroma,True) # MAnalyse chroma arg. If set to true, use chroma in block matching.
Glob = Default(glob,True) # Default True, Allow set MAnalyse(global) independently of TrueMotion.
# Only 1 Level required where not MAnalyse-ing.
sup_rend = c.MSuper(pel=pel, sharp=2,Levels=1)
QTGMC(c,InputType=1, GlobalNames="QTGMC_SL", preset="slow", SubPel=Pel, BlockSize=16, Overlap=8, TrueMotion=Tm, ChromaMotion=Chroma, GlobalMotion=Glob, \
Refinemotion=true, RefineMotionAll=true, search=3,searchparam=4, trymany=true, RefineMotionSearch=3, RefineMotionRadius=8, PNew=5)
#QTGMC(c,InputType=1, GlobalNames="QTGMC_SL", preset="slow", SubPel=Pel, BlockSize=32, Overlap=16, TrueMotion=Tm, ChromaMotion=Chroma, GlobalMotion=Glob, Refinemotion=true, Refinemotion2=true, \
#temporal=true, RefineMotionAll=true, search=3,searchparam=4, trymany=true, RefineMotionSearch=3, RefineMotionRadius=8, PNew=5)

mc_f1=c.MCompensate(sup_rend, QTGMC_SL_fVec1, thSad=ThSAD, thSad2=ThSAD2)
mc_b1=c.MCompensate(sup_rend, QTGMC_SL_bVec1, thSad=ThSAD, thSad2=ThSAD2)
Interleave(mc_b1, c, mc_f1)
MedianBlurTemporal(radiusY=0,radiusU=0,radiusV=0,temporalradius=1) # Temporal median blur only [not spatial]
SelectEvery(3,1) # Return middle frame
}


Lots more settings for ME to play with. But I still can not get ME result matching close enough the simple SpotLess version. May be prefiltering in QTGMC make this difference.