PDA

View Full Version : Discrepencies in output when calling the same function twice


PigOnWing
8th January 2009, 18:21
I've encountered some strange behavior with multiple instances of the same function being called from the same script.

First, I'll describe some behavior that I did manage to solve, thus understanding what was being done wrongly on my part. Then, forward to what I don't yet understand.

The issue began with me trying to compare result of several methods of smoothers, and creating a function for PeachSmoother (http://avisynth.org.ru/docs/english/externalfilters/peachsmoother.htm). The function was first conceived thus:


function VHSPeachclean (clip PcleanC) {
CleanE = PcleanC.AssumeTFF.SeparateFields().SelectEven().ConvertToYUY2(). \
PeachSmoother(NoiseReduction=50, Stability=25, DoSpatial=false).ConvertToYV12()
CleanO = PcleanC.AssumeTFF.SeparateFields().SelectOdd().ConvertToYUY2(). \
PeachSmoother(NoiseReduction=50, Stability=25, DoSpatial=false).ConvertToYV12()
return Interleave(CleanE,CleanO).AssumeFieldBased().AssumeTFF().Weave()
}
(The arguments for PeachSmoother are based upon the suggested ones in Wilbert & Ivo's article (http://www.geocities.com/wilbertdijkhof/analog_comp/comparison.htm), though tempered down a bit).

Then I wanted to change the stage where the colour conversion is taking place (PeachSmoother works in YUY2 only). Thus the new code was:


function VHSPeachcleanNew (clip PcleanC) {
CleanE = PcleanC.ConvertToYUY2().AssumeTFF.SeparateFields().SelectEven(). \
PeachSmoother(NoiseReduction=50, Stability=25, DoSpatial=false).ConvertToYV12()
CleanO = PcleanC.ConvertToYUY2().AssumeTFF.SeparateFields().SelectOdd(). \
PeachSmoother(NoiseReduction=50, Stability=25, DoSpatial=false).ConvertToYV12()
return Interleave(CleanE,CleanO).AssumeFieldBased().AssumeTFF().Weave()
}
Upon comparing the two clips vie SeeTheDifference (http://avisynth.org/mediawiki/SeeTheDifference), I noticed a discrepancy between the two results: eventually I spotted the culprit when I noticed that ConvertToYUY2 should have been explicitly said to treat the interlaced video - an issue which is documented here (http://avisynth.org/mediawiki/Interlaced_fieldbased) and here (http://avisynth.org/mediawiki/Sampling).

Thus, the correct function should have been (and now indeed is):


function VHSPeachcleanNew (clip PcleanC) {
CleanE = PcleanC.ConvertToYUY2(true).AssumeTFF.SeparateFields().SelectEven(). \
PeachSmoother(NoiseReduction=50, Stability=25, DoSpatial=false).ConvertToYV12()
CleanO = PcleanC.ConvertToYUY2(true).AssumeTFF.SeparateFields().SelectOdd(). \
PeachSmoother(NoiseReduction=50, Stability=25, DoSpatial=false).ConvertToYV12()
return Interleave(CleanE,CleanO).AssumeFieldBased().AssumeTFF().Weave()
}
(ConvertTo/X (http://avisynth.org/mediawiki/Convert) has a single boolean argument, hence it is not necessary to explicitly state "interlaced=true").

Now, indeed,
SeeTheDifference(last.VHSPeachClean,last.VHSPeachCleanNew)
shows a clean result, with no discrepancies between the two.


So far so logical; however, when I wanted to continue the chain of filters, in order to compare more cumulative settings, I noticed that upon calling the same function twice - I saw discrepancies again. Thus,
SeeTheDifference(last.VHSPeachCleanNew,last.VHSPeachCleanNew)
does show discrepancies between the two clips!

At first I thought it might be due to the fact that calling the function twice might cause a clash between both instances using the same variable names; however, the same variable name is being used both in VHSPeachCleanOld and VHSPeachCleanNew, and the result gives consistent, clean output. Even when I duplicated the function itself, and created two identical separate functions (namely VHSPeachCleanOne and VHSPeachCleanTwo), once they are being called with SeeTheDifference there seems to be a discrepancy between the output of the two.

Any thoughts?


-- Piggie


Versions used:
AviSynt v2.5.8
PeachSmoother v1.0c [2003-08-01]
VirtualDub v1.8.8

neuron2
8th January 2009, 18:34
Provide the complete AVS script with all functions, including SeeTheDifference().

PigOnWing
8th January 2009, 19:07
Gladly, and thanks for the quick attention. :-)


# Loading Extra Plugins
Load_Stdcall_Plugin("C:\Program Files\Multimedia\Authoring\MeGUI\tools\yadif\yadif.dll")


# Comparing Videos - taken directly from AviSynth.org

function SeeTheDifference (clip v1, clip v2) {

# If Videos start at different frames
frameadjust = 0

# Videos to compare: (v1 is original, v2 is encoded or whatever)
# v1 = AviSource("original.avi",false).trim(frameadjust,0)
# v2 = AviSource("encoded.avi",false)
sub = v1.subtract(v2)
substrong = sub.levels(122,1,132,0,255)

return StackVertical(StackHorizontal(v1.subtitle("original"),v2.subtitle("encoded")),StackHorizontal(sub.subtitle("Difference"),substrong.subtitle("Difference amplified")))
}



# VHS Noise Reduction using PeachSmoother as a Temporal Smoother

function VHSPeachcleanOld (clip PcleanC) {
CleanE = PcleanC.AssumeTFF.SeparateFields().SelectEven().ConvertToYUY2().PeachSmoother(NoiseReduction=50, Stability=25, DoSpatial=false).ConvertToYV12()
CleanO = PcleanC.AssumeTFF.SeparateFields().SelectOdd().ConvertToYUY2().PeachSmoother(NoiseReduction=50, Stability=25, DoSpatial=false).ConvertToYV12()
return Interleave(CleanE,CleanO).AssumeFieldBased().AssumeTFF().Weave()
}

function VHSPeachcleanOne (clip PcleanC) {
CleanE = PcleanC.ConvertToYUY2(true).AssumeTFF.SeparateFields().SelectEven().PeachSmoother(NoiseReduction=50, Stability=25, DoSpatial=false).ConvertToYV12()
CleanO = PcleanC.ConvertToYUY2(true).AssumeTFF.SeparateFields().SelectOdd().PeachSmoother(NoiseReduction=50, Stability=25, DoSpatial=false).ConvertToYV12()
return Interleave(CleanE,CleanO).AssumeFieldBased().AssumeTFF().Weave()
}

function VHSPeachcleanTwo (clip PcleanC) {
CleanE = PcleanC.ConvertToYUY2(true).AssumeTFF.SeparateFields().SelectEven().PeachSmoother(NoiseReduction=50, Stability=25, DoSpatial=false).ConvertToYV12()
CleanO = PcleanC.ConvertToYUY2(true).AssumeTFF.SeparateFields().SelectOdd().PeachSmoother(NoiseReduction=50, Stability=25, DoSpatial=false).ConvertToYV12()
return Interleave(CleanE,CleanO).AssumeFieldBased().AssumeTFF().Weave()
}



Vid=DGDecode_Mpeg2Source("E:\DVD Rips from LG Recorder (After Cutting)\Das Meiningen Hoftheater - MPEG-2 - 720x576 (PAL) - 4~3.d2v",info=3).ColorMatrix(hints=true)
# Aud=BassAudioSource("E:\DVD Rips from LG Recorder (After Cutting)\Das Meiningen Hoftheater - MPEG-2 - 720x576 (PAL) - 4~3.ac3")
Vid # AudioDub(Vid,Aud)


SeeTheDifference(VHSPeachCleanOld(),VHSPeachCleanTwo())


SeeTheDifference(VHSPeachCleanOld(),VHSPeachCleanOne()) ==> Results in a no-difference result. Both functions used should give equal results, chained in a different order.

SeeTheDifference(VHSPeachCleanOne(),VHSPeachCleanOne()) ==> Shows discrepancies between the two clips, even though the same function is being called.

SeeTheDifference(VHSPeachCleanOne(),VHSPeachCleanTwo()) ==> Shows discrepancies between the two clips, even though the two functions are identical.


Please take notice that both functions are not present in the original script, as they are loaded from AVSI files residing in AVISynth's plugin directory (as is PeachSmoother).

"Vid" and "Aud" are being provided for quick switch between tasks that I need Audio for, and tasks that I don't.

Yes, the aforementioned behavior happens with different sources as well.

-- Piggie

neuron2
8th January 2009, 20:51
I removed ColorMatrix and PeachSmoother and the problem does not occur. Often, in such cases, a filter is using a global variable instead of a per-instance variable, and that causes one to interfere with the other. In this case, PeachSmoother would be implicated.

PigOnWing
8th January 2009, 22:56
Thanks - I do partly understand your meaning.

I understand PeachSmoother is the culprit, however I'm trying to understand what does it do, which is "culpriting".

I tried separating all variables in the three instances (Old, One & Two), so I end up with up to Nine variables defined (six called upon) - PCleanOld/One/Two, CleanEOld/One/Two etc. Thus, in my logic, I completely detach all interaction between the different calls to the same or identical functions. Same behavior as before.

I understand that what you may imply, is that PeachSmoother itself is using a global variable for all instances where it is being called, regardless of the ones I'm stating, and the multiple calls to that variable may interfer with one another. That's a fair call: but if that is so, how does that explain that when comparing the "Old" and "New" methods, it does present a clean output? According to that logic, a clean output should have actually been detected only if both sources were indeed different, and in such a manner that their difference would be consistent enough so that the discrepancy caused by pointing at the same global would always be corrected to the exact output as the result from the other function call. Am I making sense?

What am I missing?

neuron2
8th January 2009, 23:36
I've had a look at PeachSmoother's source code and it's riddled with globals. There's no way multiple instances can function without potentially interfering with each other. The result will be undefined and will depend on the specific invocations and ordering of operations.

Sagekilla
8th January 2009, 23:49
Which leads me to ask: Why not just do:
Source
AssumeTFF()
SeparateFields()
ConvertToYUY2()
PeachSmoother()
Weave()

That looks like it (should) do the same exact thing.

Gavino
9th January 2009, 00:28
Source
AssumeTFF()
SeparateFields()
ConvertToYUY2()
PeachSmoother()
Weave()
Two things wrong here:
- ConvertToYUY2() will cause chroma shift if applied to field-based clip - need to convert back before Weave;
- PeachSmoother is spatio-temporal filter so should not be applied to field-based clip, which has vertical displacement between alternate frames.

Sagekilla
9th January 2009, 00:35
Ah, I see now. Seems like only possible way would be a bob -> reinterlace then.

PigOnWing
9th January 2009, 07:47
@ Sagekilla & Gavino: you are both correct, and the settings have been made in order to deal with exactly what you refer to.

The use of SeparateFields() have been done exactly in order to deal with two Frame-Based, non-interlaced clip, rather than with interlaced ones (even though the spatial processing of PeachSmoother is disabled); The ConvertToYUY2 phase is being done with (interlace=true) when done before the SeparateFields() function.

So both points have indeed been addressed.


@ neuron2 - thanks again; so I understand that indeed the collision between globals done internally by PeachSmoother is what causes the said behaviour. However, do ou have an idea how come it doesn't cause such a collision when called upon with comparing the different chains? I thought it might be due to PeachSoother receiving inputs with different characteristics, however even though it is chained differently, once getting to the PeachSmoother stage, both methods feed it with similar clips: it always gets a YUY2-SeperatedField clip. So as far as it goes, same collision should have occurred.

It's not an error which bugs my mind, it's an error which doesn't always happen. :-)

-- Piggie

PigOnWing
9th January 2009, 08:14
P.S.
The practical solution is, of course, instead of using
SeeTheDifference(VHSPeachClean(),VHSPeachClean())
to use

VHSPeachClean()
SeeTheDifference(Last,Last)

In which case the function isn't being called twice, but only the resulting clip (I would assume that will also translate to faster processing).

I am still intrigued as to what cause the logical collision - or rather, what causes a logical collision when there's a double-call to PeachSmoother using an identical chain, that doesn't occur when the chain is arranged in a different manner.

While this gives a practical solution should I want to test other functions chained after that point, it renders any testing of pre-chained filters useless, as one cannot tell whether any changes seen are due to such usage or due to the aforementioned discrepancy. Back to frame 0.


-- Piggie

stickboy
9th January 2009, 10:56
Ah, I see now. Seems like only possible way would be a bob -> reinterlace then.Or use my JDL_UnfoldFieldsVertical/JDL_FoldFieldsVertical functions (http://avisynth.org/stickboy/).

Leak
9th January 2009, 13:20
I am still intrigued as to what cause the logical collision - or rather, what causes a logical collision when there's a double-call to PeachSmoother using an identical chain, that doesn't occur when the chain is arranged in a different manner.
AviSynth filters are, as you probably noticed, DLLs.

Each DLL is loaded only once, no matter how often you use the filter(s) contained therein.

The problem here is that a DLL itself can save state in the form of global variables (i.e. there exists exactly one instance of the data) while every instance of the filter(s) used, as they are C++ objects, can also store their own state - the latter is what must be used to make sure the filter instances don't interfere with each other.

The problem with PeachSmoother is that it stores state information that would have to be stored for each instance that you use in your AviSynth script (i.e. for each call to VHSPeachClean) in global variables of the DLL instead, so there exists only one global copy of that information. So each instance of VHSPeachClean overwrites the state of all the other instances of VHSPeachClean, and that's why things go awry here.

Obviously, the author of PeachSmoother didn't fully grasp the concept of filter programming...

neuron2
9th January 2009, 14:56
@PigOnWing

As I said, the behavior will be undefined. Undefined can include working correctly (for some fortuitous invocations). :)

PigOnWing
9th January 2009, 20:15
@stickboy: I actually was influenced by your functions when creating my own functions quoted above. Kudos for that. :-)

@Leak: Yup, I (surprising myself) understand the problem. However, as far as I go, I would still not call anything regarding PeachSmooth's author, simply because I wouldn't allow myself to speak anything about programming when I know nothing about it. :-) It's too much of a mystery to me.

@Neuron2: Thanks again. I'm now ceasing my attempts of making a non-workie work, and instead trying to approach achieving the same goal in a different manner.


The original goal was to check the differences in different order of chaining several smoothers (used as general denoisers). I wanted to check the results I'll get from chaining FluxSmooth before and after PeachSmoother.

In order to overcome the limitation calling PeachSmoother only once in order to get consistent results, I'm currently using this approach:


# Calling to a routine similar to VHSPeachClean above, separating and re-weaving the video into two field-based clips, FluxSmoothing them and re-weaving)
VHSFluxClean()

# Stacking the FluxSmoothed result alongside the original state clip
StackHorizontal(Last,Vid)

# PeachSmoothing the double-width result
VHSPeachClean()

# Checking the difference between the two halves, applying FluxSmooth to the part that wasn't Flux'ed previously.
SeeTheDifference(Last.Crop(0,0,-Width/2,0),Last.Crop((Width/2),0,0,0).VHSFluxClean())


Does that make any sense?

-- Piggie

P.S.
Yes, the amplified result shows some differences. No, I cannot really say they actually stand out. Cannot even claim they sit in.

Gavino
10th January 2009, 12:50
Does that make any sense?
Yes, it sort of makes sense - IIUC you are comparing FluxSmooth().PeachSmoother() with PeachSmoother().FluxSmooth() in a clever way to avoid calling PeachSmoother twice.

But your VHSPeachClean() function already calls PeachSmoother() twice, and it's not clear if this works properly. Perhaps the only reliable way to use PeachSmoother is to deinterlace as Sagekilla suggested or use stickboy's idea.

neuron2
10th January 2009, 15:39
Perhaps the only reliable way to use PeachSmoother is to deinterlace as Sagekilla suggested or use stickboy's idea. Better yet, modify the filter to avoid using globals. The source code is available.

PigOnWing
12th January 2009, 15:46
D'oh!... :rolleyes:

Yes, well... ahem... uh... Yup.
Didn't notice the double-call to the filter from within the script.
Indeed. So basically, comparing the results of one function to another isn't any good, as the resulting clip from a single function isn't reliable for what it is.

Back to frame 0.
The following function has been created, implementing stickboy's Fold/Unfold concept (re-written in shorthand, with less options):


function VHSPeachcleanFold (clip PcleanC) {
PcleanC.ConvertToYUY2(true).AssumeTFF.SeparateFields()
StackVertical(SelectEven(),SelectOdd()).PeachSmoother(NoiseReduction=50, Stability=25, DoSpatial=false).ConvertToYV12()
Interleave(Crop(0,0,0,Height()/2),Crop(0,Height()/2,0,0)).AssumeFieldBased().AssumeTFF().Weave()
}

This, actually, does give clean results when called as
SeeTheDifference(VHSPeachCleanFold(),VHSPeachCleanFold())

However, according to SeeTheDifference it gives different results than both VHSPeachCleanOld & VHSPeachCleanNew, suggesting that if this does indeed gives logical, consistent, reliable results, neither of the previous ones did...


As to retouching the filter's code: Unfortunately, I know pretty much nothing about C, except that I understand that its syntax style is adopted in many scripting languages (so I might think in C's logic by writing scripts, without knowing it). Bt as it did get me curious, even if I won't be able to do anything with it - where can I find PeachSmoother's Source code? I didn't locate it at WarpSharpEnterprise's page, nor in a Google search (albeit not a too thorough one).

Thanks all,

-- Piggie


P.S.
Are there are already some known opinions / already made tests in regard to denoiser filter combination and recommended order? The main articles I located sofar are Scintilla's page (http://aquilinestudios.org/avsfilters/index.html) and a thread with that aims at compression ratio/speed factors (http://forum.doom9.org/showthread.php?t=51181), but nothing regarding combination.

stickboy
12th January 2009, 21:52
BTW, please get my username right. Thanks. ;)

neuron2
13th January 2009, 01:46
where can I find PeachSmoother's Source code? See here:

http://neuron2.net/misc/PeachSmoother.zip

neuron2
13th January 2009, 03:45
Corrected due to well-pointed post below - apologies. It's still wrong. :)

PigOnWing
15th January 2009, 10:16
I thought Avisynth isn't case sensitive... :)
Corrected again.