StainlessS
12th July 2012, 00:40
This moved here from another thread:
http://forum.doom9.org/showthread.php?p=1582243#post1582243
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
#include <windows.h>
#include "Avisynth.h"
// Helper function - exception protected wrapper, From Avisynth source, conditional.cpp
inline AVSValue GetVar(IScriptEnvironment* env, const char* name) {
try {return env->GetVar(name);} catch (IScriptEnvironment::NotFound) {} return AVSValue();
}
AVSValue __cdecl AveLuma(AVSValue args, void* user_data, IScriptEnvironment* env) {
PClip child = args[0].AsClip();
int n;
if(args[1].IsInt()) {n = args[1].AsInt(); }
else {
AVSValue cn = GetVar(env,"current_frame");
if (!cn.IsInt()) env->ThrowError("AveLuma: 'current_frame' only available in runtime scripts");
n = cn.AsInt();
}
const VideoInfo &vi = child->GetVideoInfo();
n = (n<0) ? 0 : (n>=vi.num_frames)?vi.num_frames-1:n; // Range limit frame n
PVideoFrame src = child->GetFrame(n,env);
int h,w;
const int pitch = src->GetPitch(PLANAR_Y); // PLANAR_Y no effect on non-Planar (equates to 0)
const int rowsize = src->GetRowSize(PLANAR_Y);
const int height = src->GetHeight(PLANAR_Y);
const BYTE *srcp = src->GetReadPtr(PLANAR_Y);
const double Pixels = (double)(height * vi.width);
__int64 acc = 0;
unsigned long sum = 0;
if(vi.IsYUV()) {
const int wstep = (vi.IsYUY2()) ? 2 : 1;
for(h=height ; --h >= 0 ; ) { // h is upside down to actual scan
// for (w=0 ; w < rowsize; w += wstep) { // w scans left to right
// Slightly faster on non-optimized compiler, about same on optimised compiler
for (w=rowsize ; (w -= wstep) >= 0; ) { // w scans right to left
sum += srcp[w];
}
if(sum & 0x80000000) {acc += sum;sum=0;} // avoid possiblilty of overflow
srcp += pitch;
}
} else {
// RGB to YUV-Y Conversion
int matrix = args[2].AsInt(0); // Default=0=REC601 : 1=REC709 : 2 = PC601 : 3 = PC709
if(matrix & 0xFFFFFFFC) env->ThrowError("AveLuma: RGB matrix 0 to 3 Only");
double Kr,Kb;
int Sy,offset_y;
if(matrix & 0x01) {Kr = 0.2126; Kb = 0.0722;} // 709 1 or 3
else {Kr = 0.2990; Kb = 0.1140;} // 601 0 or 2
if(matrix & 0x02) {Sy = 255 ; offset_y = 0;} // PC 2 or 3
else {Sy = 219 ; offset_y = 16;} // TV 0 or 1
const int shift = 15;
const int half = 1 << (shift - 1);
const double mulfac = double(1<<shift);
double Kg = 1.0 - Kr - Kb;
const int Srgb = 255;
const int Yb = int(Sy * Kb * mulfac / Srgb + 0.5); //B
const int Yg = int(Sy * Kg * mulfac / Srgb + 0.5); //G
const int Yr = int(Sy * Kr * mulfac / Srgb + 0.5); //R
const int OffyPlusHalf = (offset_y<<shift) + half;
const int wstep = (vi.IsRGB24()) ? 3 : 4;
for(h=height ; --h >= 0; ) { // h is same as actual RGB scan, bottom to top
// for (w=0 ; w < rowsize; w += wstep) { // w scans left to right
// Seems slightly faster on optimized compiler, nothing in it on non optimized.
for (w=rowsize ; (w -= wstep) >= 0; ) { // w scans right to left
sum += (srcp[w] * Yb + srcp[w+1] * Yg + srcp[w+2] * Yr + OffyPlusHalf) >> shift;
}
if(sum & 0x80000000) {acc += sum;sum=0;} // avoid possiblilty of overflow
srcp += pitch;
}
}
acc += sum;
return (float)((double)acc / Pixels);
}
extern "C" __declspec(dllexport) const char* __stdcall AvisynthPluginInit2(IScriptEnvironment* env) {
env->AddFunction("AveLuma", "c[n]i[matrix]i", AveLuma, 0);
return "`AveLuma' AveLuma plugin";
}
and test script
AviSource("D:\avs\avi\1.avi")
#Return AveLuma() # Show "AveLuma: 'current_frame' only available in runtime scripts"
V1=ScriptClip(""" Subtitle(String(AveLuma),Align=1) """ ) # Default current_frame
#V1=ScriptClip(""" Subtitle(String(AveLuma(current_frame)),Align=1) """ ) # current_frame explicitly supplied
#V1=ScriptClip(""" Subtitle(String(AveLuma(current_frame-100)),Align=1) """ ) # current_frame relative
V2=ShowChannels() # for comparison
StackVertical(V1,V2)
Takes an optional frame number (else current_frame).
Works just fine.
Another test script: Demos in YV12, YUY2, Y8, RGB24, RGB32, YV16, YV24 etc
(RGB mode, converts and returns RGB to YUV Luma)
Added Matrix arg
The result (Subtitled Average Accumulated Luma) is valid on the final frame.
AviSource("D:\avs\avi\1.avi").trim(0,0999)
# Set to ConvertToRGB matrix
COL = 0 MATRIX = "rec601" # Default 0=REC601
#COL = 1 MATRIX = "rec709"
#COL = 2 MATRIX = "pc601"
#COL = 3 MATRIX = "pc709"
#ConvertToYV12()
#ConvertToYUY2()
#ConvertToY8()
#ConvertToYV16()
#ConvertToYV24()
#ConvertToRGB24(matrix=MATRIX)
#ConvertToRGB32(matrix=MATRIX)
Sum = 0.0
SumHi=0.0 # Hi part Extra precision (need for long scans)
SumLo=0.0 # Lo part Extra precision
GScript("""
for(n=0,FrameCount()-1) {
# Sum = Sum + AveLuma(n,matrix=COL) # Were gonna use extra precision instead
SumLo = SumLo + AveLuma(n,matrix=COL)
SumHi = SumHi + floor(SumLo / 256.0) # Hi part, Extra Precision
SumLo = frac(SumLo / 256.0) * 256.0 # Lo part, Extra Precision
}
""")
# Sum=Sum/Float(FrameCount()) # Were gonna use extra precision instead
Sum = (SumHi / Float(FrameCount()) * 256.0) + (SumLo / Float(FrameCount()))
ConvertToYV12(matrix=MATRIX) # For Display and Showchannels info
s=showchannels() # Show eg Ave Luma for frame and Accumulated Ave luma for all frames visited
s.Subtitle(String(Sum),Align=1)
Found this thread by Gavino and added the GetVar helper function.
http://forum.doom9.org/showthread.php?p=1394689&highlight=current_frame#post1394689
EDIT: Added, YUY2, RGB and v2.6 planar modes. Renamed to AveLuma
EDIT: Added matrix arg: 0=rec601 : 1=rec709 : 2=pc601 : 3=pc709
EDIT: Minor mods
EDIT: Accumulator now 64 bit.
AveLuma(clip , int "n", int "matrix") : n defaults to current_frame : matrix defaults=0=rec601
EDIT: About 10% slower than AverageLuma (in above GScript test) as no SSE, BUT, supports optional frame number
(or eg current_frame-2), YUY2 and RGB with conversion to Luma.
EDIT: ShowChannels() Available via MediaFire in sig
EDIT: Seems that this thread subject is/was almost identical to example from Avisynth Docs:
http://avisynth.org/mediawiki/Filter_SDK/Non-clip_sample
http://forum.doom9.org/showthread.php?p=1582243#post1582243
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
#include <windows.h>
#include "Avisynth.h"
// Helper function - exception protected wrapper, From Avisynth source, conditional.cpp
inline AVSValue GetVar(IScriptEnvironment* env, const char* name) {
try {return env->GetVar(name);} catch (IScriptEnvironment::NotFound) {} return AVSValue();
}
AVSValue __cdecl AveLuma(AVSValue args, void* user_data, IScriptEnvironment* env) {
PClip child = args[0].AsClip();
int n;
if(args[1].IsInt()) {n = args[1].AsInt(); }
else {
AVSValue cn = GetVar(env,"current_frame");
if (!cn.IsInt()) env->ThrowError("AveLuma: 'current_frame' only available in runtime scripts");
n = cn.AsInt();
}
const VideoInfo &vi = child->GetVideoInfo();
n = (n<0) ? 0 : (n>=vi.num_frames)?vi.num_frames-1:n; // Range limit frame n
PVideoFrame src = child->GetFrame(n,env);
int h,w;
const int pitch = src->GetPitch(PLANAR_Y); // PLANAR_Y no effect on non-Planar (equates to 0)
const int rowsize = src->GetRowSize(PLANAR_Y);
const int height = src->GetHeight(PLANAR_Y);
const BYTE *srcp = src->GetReadPtr(PLANAR_Y);
const double Pixels = (double)(height * vi.width);
__int64 acc = 0;
unsigned long sum = 0;
if(vi.IsYUV()) {
const int wstep = (vi.IsYUY2()) ? 2 : 1;
for(h=height ; --h >= 0 ; ) { // h is upside down to actual scan
// for (w=0 ; w < rowsize; w += wstep) { // w scans left to right
// Slightly faster on non-optimized compiler, about same on optimised compiler
for (w=rowsize ; (w -= wstep) >= 0; ) { // w scans right to left
sum += srcp[w];
}
if(sum & 0x80000000) {acc += sum;sum=0;} // avoid possiblilty of overflow
srcp += pitch;
}
} else {
// RGB to YUV-Y Conversion
int matrix = args[2].AsInt(0); // Default=0=REC601 : 1=REC709 : 2 = PC601 : 3 = PC709
if(matrix & 0xFFFFFFFC) env->ThrowError("AveLuma: RGB matrix 0 to 3 Only");
double Kr,Kb;
int Sy,offset_y;
if(matrix & 0x01) {Kr = 0.2126; Kb = 0.0722;} // 709 1 or 3
else {Kr = 0.2990; Kb = 0.1140;} // 601 0 or 2
if(matrix & 0x02) {Sy = 255 ; offset_y = 0;} // PC 2 or 3
else {Sy = 219 ; offset_y = 16;} // TV 0 or 1
const int shift = 15;
const int half = 1 << (shift - 1);
const double mulfac = double(1<<shift);
double Kg = 1.0 - Kr - Kb;
const int Srgb = 255;
const int Yb = int(Sy * Kb * mulfac / Srgb + 0.5); //B
const int Yg = int(Sy * Kg * mulfac / Srgb + 0.5); //G
const int Yr = int(Sy * Kr * mulfac / Srgb + 0.5); //R
const int OffyPlusHalf = (offset_y<<shift) + half;
const int wstep = (vi.IsRGB24()) ? 3 : 4;
for(h=height ; --h >= 0; ) { // h is same as actual RGB scan, bottom to top
// for (w=0 ; w < rowsize; w += wstep) { // w scans left to right
// Seems slightly faster on optimized compiler, nothing in it on non optimized.
for (w=rowsize ; (w -= wstep) >= 0; ) { // w scans right to left
sum += (srcp[w] * Yb + srcp[w+1] * Yg + srcp[w+2] * Yr + OffyPlusHalf) >> shift;
}
if(sum & 0x80000000) {acc += sum;sum=0;} // avoid possiblilty of overflow
srcp += pitch;
}
}
acc += sum;
return (float)((double)acc / Pixels);
}
extern "C" __declspec(dllexport) const char* __stdcall AvisynthPluginInit2(IScriptEnvironment* env) {
env->AddFunction("AveLuma", "c[n]i[matrix]i", AveLuma, 0);
return "`AveLuma' AveLuma plugin";
}
and test script
AviSource("D:\avs\avi\1.avi")
#Return AveLuma() # Show "AveLuma: 'current_frame' only available in runtime scripts"
V1=ScriptClip(""" Subtitle(String(AveLuma),Align=1) """ ) # Default current_frame
#V1=ScriptClip(""" Subtitle(String(AveLuma(current_frame)),Align=1) """ ) # current_frame explicitly supplied
#V1=ScriptClip(""" Subtitle(String(AveLuma(current_frame-100)),Align=1) """ ) # current_frame relative
V2=ShowChannels() # for comparison
StackVertical(V1,V2)
Takes an optional frame number (else current_frame).
Works just fine.
Another test script: Demos in YV12, YUY2, Y8, RGB24, RGB32, YV16, YV24 etc
(RGB mode, converts and returns RGB to YUV Luma)
Added Matrix arg
The result (Subtitled Average Accumulated Luma) is valid on the final frame.
AviSource("D:\avs\avi\1.avi").trim(0,0999)
# Set to ConvertToRGB matrix
COL = 0 MATRIX = "rec601" # Default 0=REC601
#COL = 1 MATRIX = "rec709"
#COL = 2 MATRIX = "pc601"
#COL = 3 MATRIX = "pc709"
#ConvertToYV12()
#ConvertToYUY2()
#ConvertToY8()
#ConvertToYV16()
#ConvertToYV24()
#ConvertToRGB24(matrix=MATRIX)
#ConvertToRGB32(matrix=MATRIX)
Sum = 0.0
SumHi=0.0 # Hi part Extra precision (need for long scans)
SumLo=0.0 # Lo part Extra precision
GScript("""
for(n=0,FrameCount()-1) {
# Sum = Sum + AveLuma(n,matrix=COL) # Were gonna use extra precision instead
SumLo = SumLo + AveLuma(n,matrix=COL)
SumHi = SumHi + floor(SumLo / 256.0) # Hi part, Extra Precision
SumLo = frac(SumLo / 256.0) * 256.0 # Lo part, Extra Precision
}
""")
# Sum=Sum/Float(FrameCount()) # Were gonna use extra precision instead
Sum = (SumHi / Float(FrameCount()) * 256.0) + (SumLo / Float(FrameCount()))
ConvertToYV12(matrix=MATRIX) # For Display and Showchannels info
s=showchannels() # Show eg Ave Luma for frame and Accumulated Ave luma for all frames visited
s.Subtitle(String(Sum),Align=1)
Found this thread by Gavino and added the GetVar helper function.
http://forum.doom9.org/showthread.php?p=1394689&highlight=current_frame#post1394689
EDIT: Added, YUY2, RGB and v2.6 planar modes. Renamed to AveLuma
EDIT: Added matrix arg: 0=rec601 : 1=rec709 : 2=pc601 : 3=pc709
EDIT: Minor mods
EDIT: Accumulator now 64 bit.
AveLuma(clip , int "n", int "matrix") : n defaults to current_frame : matrix defaults=0=rec601
EDIT: About 10% slower than AverageLuma (in above GScript test) as no SSE, BUT, supports optional frame number
(or eg current_frame-2), YUY2 and RGB with conversion to Luma.
EDIT: ShowChannels() Available via MediaFire in sig
EDIT: Seems that this thread subject is/was almost identical to example from Avisynth Docs:
http://avisynth.org/mediawiki/Filter_SDK/Non-clip_sample