View Full Version : Development of taverage
jmac698
8th July 2012, 06:37
Hi,
I'm making a 'simple' filter to average all pixels up to the current frame.
First of all, it's going to be quite slow for random access. I'd need to keep a persistent running sum buffer, and maybe even a history of the last frame number requested.
But for now, I'll just add all frames from 0 to n.
Does this code look right?
PVideoFrame __stdcall INTERNALNAME::GetFrame(int n, IScriptEnvironment* env) {
PVideoFrame dst; // Safe pointer to Video Frame
PVideoFrame srcf;
GETFRAME(n,dst); // get read only source frame n (range limited by macro)
env->MakeWritable(&dst); // We are calling Avisynth here via env.
const int width = vi.width; // In pixels, (why not exposed via PVideoFrame eg dst->GetWidth() ?)
const int height = dst->GetHeight(PLANAR_Y);
const int rowsize = dst->GetRowSize(PLANAR_Y); // In bytes (eg, About pixel width * 3 in RGB24 [mod whatever])
const int pitch = dst->GetPitch(PLANAR_Y);
int x,y,w;
int f;
unsigned long int sum;
int pitchf;
if(vi.IsPlanar() || vi.IsYUY2()) {
// Planar or YUY2
unsigned char *dp = dst->GetWritePtr(PLANAR_Y);
//unsigned char *dpf;
// We are only going to alter Y, we do not touch chroma.
const int wstep = (vi.IsPlanar()) ? 1 : 2; // 2 for YUY2 or 1 for any Planar.
for (y=0; y < height; ++y) {
for (x = w = 0; w < rowsize; w += wstep, ++x) {
sum = 0;
for (f=0; f <= n; ++f) {// for all frames up to current,
GETFRAME(f,srcf);// get a pointer 'srcf' to structure of frame f
pitchf = srcf->GetPitch(PLANAR_Y);// the pitch of this frame, which may vary by frame
unsigned char *dpf = srcf->GetWritePtr(PLANAR_Y);// dpf is a pointer to the data of the src frame
sum+=dpf[w + y * pitchf];//add the char at dpf+w to sum
}
dp[w] = sum/(n+1); // write the average to the current dst frame
}//for (x = w = 0; w < rowsize; w += wstep, ++x)
dp += pitch; // Add the pitch of one line (in bytes) to the dest.
}//for (y=0; y < height; ++y)
}// if planar
ps it'll crash, there's a wrong pointer
Wilbert
8th July 2012, 16:35
You can calculate the average of a frame from the frame itself and the average of the previous frames.
If your frame consists of one pixel (for simplicity), say x[j]. The average of frame n is calculated as follows:
M[n] = (x[0] + ... + x[n-1] + x[n]) / (n+1) = (x[0] + ... + x[n-1]) / n * (n / (n+1)) + x[n] / (n+1) = M[n-1] * (n / (n+1)) + x[n] / (n+1)
with M[0] = x[0]
jmac698
8th July 2012, 21:34
Wilbert,
Thanks for the insight! Unfortunately I wrote my message too quickly. What I'm looking for is the average per pixel, temporally only. And yes, instead of keeping a running sum I can keep a moving average, and even subtract or add the effect of a single frame to it, but the precision requirements remain the same, and an int is always going to have more mantissa bits than a float. This has been done in script, and it was found that precision effects were visible. Somewhat related, I discovered a bug in avisynth color conversion because repeated conversions made the image look yellowish!
Where I'm stuck is, I don't quite get the pointer syntax and I think there's something wrong with my calculation, thus leading to a crash.
StainlessS
8th July 2012, 22:40
I don't on initial inspection see any real problems except this line:
unsigned char *dpf = srcf->GetWritePtr(PLANAR_Y);// dpf is a pointer to the data of the src frame
You only need a read pointer here, not a write one, ie
const unsigned char * dpf = srcf->GetReadPtr(PLANAR_Y);// dpf is a pointer to the data of the src frame
EDITED:
Don't know if lots of new writable frames, within a single GetFrame() could cause a crash here,
try the fix and report back.
jmac698
9th July 2012, 05:12
Ok, it seems it boils down to srcf->GetWritePtr(PLANAR_Y) is crashing in avisynth.h 399 because refcount>1. I don't actually need *writable* frames, but I just didn't know how else to get a pointer to other frames. Just on a wild guess I tried GetReadPtr, and that function exists. So the lesson learned seems to be that you can only write to one frame at a time?
And poof, like magic it works!
But... it's extremely slow. For 3 frames it's a million calculations, which is taking a noticable fraction of a second on my Atom box. So the next idea is, to keep a sum buffer persistently between calls, which can be modified only if the current frame # is +-1 of the last called frame#, otherwise it's calculated from scratch again, meaning that linear playing forward or backward can be optimized to calculating one frame.
Release
http://www.sendspace.com/filegroup/9Jp55ub7vo%2BWcMacRzuyCQ
jmac698
9th July 2012, 07:37
Ahh, I see that you had already suggested GetReadPtr, I didn't catch that.
I looked at your lifelong example, but I don't think it's what I want. How can I make a simple array once on the first call of the plugin, sticks around between multiple calls, and which only gets destroyed when the script ends?
TheFluff
9th July 2012, 12:27
I assume that by "sticks around between multiple calls" you mean multiple calls to GetFrame() and not multiple calls to the exported function itself.
First off, Avisynth calls the function you specify in AvisynthPluginInit2() when your filter is instantiated. You can't get frames yet at this point (I think? never tried it), but you do have access to VideoInfo and the arguments you were called with, so just put whatever you need to initialize in the filter constructor and make the creation function call it with the appropriate arguments. Put any data you want to be persistent between calls to GetFrame in a class member variable; if necessary, allocate memory for it in the constructor and deallocate it in the destructor.
edit: StainlessS' lifelong example seems to be pretty much exactly what you want, by the way.
jmac698
9th July 2012, 20:16
I'm sure I misunderstood Lifelong, but I didn't look a it carefully. I will play with it. If that works, the taverage should be sped up quite a bit.
Thanks.
StainlessS
9th July 2012, 22:13
If you want LifeLong data to be eg unsigned long then:
in Class: (DEFINITON)
unsigned long * LifeLong;
In constructor: (ALLOCATION AND INITIALIZATION)
int wid=vi.width;
int hit=vi.height;
LifeLong = new unsigned long[wid*hit]; // (unsigned long *) malloc(wid*hit * sizeof(LifeLong[0]));
//check for NULL
for(int i=0;i<(wid*hit);LifeLong[i]=0,++i); // Init
In GetFrame(): (USAGE)
unsigned long * sum = LifeLong;
wid=vi.width;
for(y= etc
for(w= etc // EDIT: for (x = w = 0; w < rowsize; w += wstep, ++x)
sum[w] += etc // EDIT: Actually this should be sum[x]+= etc, as in sum[x] += dpf[w + y * pitchf] for your example
} //end for w
sum +=wid; // Next line
} // end for y
In Destructor: (FREEING/DESTRUCTION)
if(LifeLong != NULL) delete [] LifeLong; // 'free(LifeLong);'
Sorted. ;)
EDITED:
I shall modify the CPP for C Progger's thing to use LifeLong in GetFrame, as the usage is not obvious.
StainlessS
11th July 2012, 20:01
First off, Avisynth calls the function you specify in AvisynthPluginInit2() when your filter is instantiated. You can't get frames yet at this point (I think? never tried it), but you do have access to VideoInfo and the arguments you were called with
I was a little curious about this as I possibly intend to do something similar in near future, so
EDIT: TEXT MOVED HERE (Simple Runtime Filter) :-
http://forum.doom9.org/showthread.php?p=1582296#post1582296
jmac698
11th July 2012, 22:01
So you're accessing avisynth runtime vars in C++ code? That's cool.
But you're main point was that you don't need a getframe function?
StainlessS
12th July 2012, 01:04
So you're accessing avisynth runtime vars in C++ code? That's cool.
But you're main point was that you don't need a getframe function?
Yep thats what I was curious about, moved post to new thread and updated a little.
What I really want is to figure out how the
writefilefirst thing works so as to eg scan a number of frames
at intervals to establish eg AutoCropping/field order/pulldown
detection, but within the startup stage and before rendering
starts, so avoiding two pass requirement.
EDIT: Actually, Nope, we still used the GetFrame() function but of the previous filter in the chain, it's
just that we did not create any filter of our own that has a GetFrame() for the following filter to call.
jmac698
12th July 2012, 01:10
Oh! I know something :) There's a compile time and render time aspect to Avisynth. You can use this to do a sort of 2 pass. This must be the C++ internals side of things that you've discovered. For example you can read a pixel in compile time, but only once, then use it later in the script. The Avisynth docs contain a description of how compile time works.
StainlessS
12th July 2012, 02:57
I'm going scavenge from WriteFileFirst, probably easiest way. If needed to return multiple values,
then would need to perhaps return a string using some kind of separator character.
To ensure that the prescan is actually performed, we may need to ensure that some subsequent
operation used the resulting string or some other value derived from it
eg, x = x + length(s) - length(s)
where x is some arg to a later filter.
Here the CVS command to get at the v2.6 on CVS, dont ask me what you do with it, I figured it
out at the time, but no idea how now. (Which reminds me, IanB just posted a CVS update so
I'm going to update [using TortoiseCVS, which simplfes things, right click-"update"] in Windows Explorer).
You will find it handy to have the source, you've always got something to emulate if there is no documentation
for whatever you are trying to do.
cvs -d :pserver:anonymous@avisynth2.cvs.sourceforge.net:/cvsroot/avisynth2 checkout -P avisynth
StainlessS
12th July 2012, 03:53
Just a suggestion that you may wish to totally ignore,
dont know if you did actually implement the "Scan from start" where previous frame visited
is not n - 1, but what I did in the ShowChannels() plug is to define the function to show
eg average luma for current frame and accumulated average luma "For All Frames Visited So Far".
That way there would be no real long delay if user messed about in the timeline, and they would just
get what they "deserved". If however you insist on not breaking from initial definition, you might also
want to consider doing a 'sum -= etc' where Last_pos == n + 1 (assuming that you did not implement that already).
jmac698
12th July 2012, 04:17
Well I realized it's better to think of it as 'cache ranges', you could have a single buffer which represents the precomputed mean from n1 to n2, or in fact multiple buffers. To find the mean for a different range, m1 to m2, find which buffers fall within range, even partially, and find the best way to compute the new range. You can also specify a limit, to say show only the mean of only 50 frames before the current frame. Buffers would be replaced FILO style.
If a buffer is partially in range, it would have to be more than halfway in or else the cost of "undoing" the range outside would outweigh the cost of calculating the partial range from scratch.
I could show the mean of random frames, but I think my limiter will work ok.
StainlessS
12th July 2012, 06:15
Is there any real need for random access?
If not then it will probably be less irksome to show for frames visited rather than a slip of the
hand meaning scanning a significant number of frames with a long timeout (and user wondering if its crashed).
Many plugs require forward only scanning, else unpredictable results, on the whole I think Mrs User would
prefer forward only scanning to long timeouts. During rendering, you are only going to be scanning forward anyway.
PS, the sum -= etc thing would mean you could scan forward/backwards ok, just not jump about like a madman.
EDIT: the 'only show mean of prev 50 frames' might be a new plugin, or additional mode.
EDIT: You could of course check your results using ShowChannels on two copies of the source clip
the original accumulated ave should be the same as the current frame ave of taverage clip.
(StackVertical(V1,V2))
Gavino
21st July 2012, 16:57
(Been offline for a couple of weeks, so still catching up with recent threads.)
Ok, it seems it boils down to srcf->GetWritePtr(PLANAR_Y) is crashing in avisynth.h 399 because refcount>1. I don't actually need *writable* frames, but I just didn't know how else to get a pointer to other frames. Just on a wild guess I tried GetReadPtr, and that function exists. So the lesson learned seems to be that you can only write to one frame at a time?
GetWritePtr() will return a null (and hence unusable) pointer if the frame is not writable. This will happen if the frame was created by a different filter instance (eg the parent filter). Only one write pointer to a given frame can exist at a time.
Use GetReadPtr() if you only need read access.
Or use env->MakeWritable() to create a new writable copy of the frame, if that's what you need.
StainlessS
22nd July 2012, 07:33
Welcome back Gavino, trust you had a restful holiday. :)
We did miss you so. ;)
vBulletin® v3.8.11, Copyright ©2000-2025, vBulletin Solutions Inc.