View Full Version : Using the output of a filter in the same filter (recursion?)
omion
15th June 2011, 20:24
I'm trying to write a simple deduplication filter, but I'm fairly new to AviSynth development (and object-oriented C++...).
What I want to do is use the output of one frame for other frames, using the AviSynth cache of that frame if available. I tried to do something like this:
MyFilter::GetFrame(frame_num, env) {
if(same(child->GetFrame(frame_num), child->GetFrame(frame_num - 1))) {
GetFrame(frame_num - 1);
} else {
complex_calculation();
}
}
but the recursive GetFrame call will not use the cached AviSynth copy. Is there a way to tell AviSynth to check the cache first?
My current workaround is to have an array in the filter object, and store any frame that I think may be used again. But I think it would be more efficient to use the existing cache, if at all possible. Does that sound doable?
IanB
15th June 2011, 23:11
Your call to
GetFrame(frame_num - 1)
can be considered
this->GetFrame(frame_num - 1)
what you really want is
parent->GetFrame(frame_num - 1)
but the problem is at the time your filter is instantiated, parent (with a cache instance) does not exist yet.
The usual solution is to create 2 filters instances with a cache between them
MyFilter::GetFrame(frame_num, env) {
if(same(grandchild->GetFrame(frame_num), grandchild->GetFrame(frame_num - 1))) {
child->GetFrame(frame_num - 1);
} else {
complex_calculation();
}
}
You need to do some gymnastics in your filter create routine to be able to "know" both the child and grandchild pointers and jam a cache between them.
...
PClip a = args[1].AsClip(); // this becomes the grandchild
PClip b= new MyFilter2(a, ...., env);
PClip c = new Cache(b, env); // this becomes the child and probably will need to use an env->Invoke()
PClip d = new MyFilter1(c, ...., env);
...
Gavino
16th June 2011, 00:03
...
PClip a = args[1].AsClip(); // this becomes the grandchild
Normally, this would be args[0].
PClip c = new Cache(b, env); // this becomes the child and probably will need to use an env->Invoke()
For more details, see here (http://avisynth.org/mediawiki/Filter_SDK/Env_Invoke#Caching_frames_from_Invoked_filters).
omion
16th June 2011, 15:01
Many thanks, you two! I got a simple filter done by making two classes and calling them one after the other with a cache in between. The cache seems to work perfectly for small ranges, although backtracking with large ranges (where the first filter is only called 1/10 frames, for example) causes the frames to be recalculated much of the time.
Unfortunately, I realized that I couldn't use this method for my real filter since I simplified a bit too much with my posted example. The problem is that complex_calculation() is actually based on the number of frames found the same. My actual filter is more like this:
figure out which frames around frame_num are very similar to frame_num. Call the range (same_min_num .. same_max_num)
if a result was already found in my_cache[same_min_num], use it
if not, calculate an average between the data from same_min_num to same_max_num, and store it in my_cache[same_min_num]
My complex_calculation() is actually the averaging operation, which averages a variable number of frames. There seems to be a loop with splitting this up into two filters:
The averaging needs to be in the first filter of the two, in order for its output to be caught by the InternalCache.
The calculation for similarity must be in the second of the two filters, since it needs to be able to take advantage of the cached frames.
Which frames get averaged by the first filter depend on the range found by the second!
I could get around this by copying the similarity code to both of the filters, but that would end up doing it twice (and it's not exactly free either...)
In the end, I think I'll stick with my manual frame-array cache. It seems like a bit of a hack, but it's currently working fairly well.
vBulletin® v3.8.5, Copyright ©2000-2012, Jelsoft Enterprises Ltd.