View Full Version : plugin development: HistByLine
jmac698
14th August 2014, 20:19
Hi,
Getting back into writing plugins again - and I've lost all the files I've ever had so, starting over.
I modified and compiled DemosaicCPP. My test plugin simply copies a source to a destination clip, but the colour channels aren't being copied.
vi.pixel_type = VideoInfo::CS_YV12; // RGB would be CS_BGR32
--------
dest_frame = env->NewVideoFrame(vi);
dest_data = dest_frame->GetWritePtr();
dest_pitch = dest_frame->GetPitch();
dest_height = dest_frame->GetHeight();
--------
if(src_vi.IsYV12()) {
src_data = src_frame->GetReadPtr(PLANAR_Y);
src_pitch = src_frame->GetPitch(PLANAR_Y);
src_row_size= src_frame->GetRowSize(PLANAR_Y); // in bytes!
src_height = src_frame->GetHeight(PLANAR_Y);
src_inc = 1;
-------
unsigned int x, y;
for (y = 0; y < src_height; ++y) {
for (x = 0; x < src_row_size; ++x) {
dest_data[x] = src_data[x] + 1;
}
src_data += src_pitch;
// dest_data -= dest_pitch; // decrement in the case of RGB
dest_data += dest_pitch;
}
return dest_frame;
--------
colorbars(pixel_type="YV12")
Myplugin(mode=0)
The luma values are correct, but u/v/ values are all 0 (giving a greenish look). I thought it was because YV12 was planar so I just extended the loop to double (y<src_height*2), but that crashed. What is the proper way to iterate this?
Thanks
p.s. using avisynth+, if that makes any difference.
jackoneill
14th August 2014, 21:03
It's because you're not copying the chroma planes at all.
You need to do what you're doing two more times: for PLANAR_U and PLANAR_V. Of course you can't keep calling GetWritePtr, GetPitch and GetHeight without parameters like you do there because you'll end up copying the chroma over the luma, or something. You have to pass PLANAR_Y, etc to those functions as well.
Wilbert
14th August 2014, 22:25
http://avisynth.nl/index.php/Filter_SDK/InvertNeg
jmac698
15th August 2014, 03:19
I see what you're saying, thanks. Are all the Y* formats planar?
Gavino
15th August 2014, 08:56
All the Y* formats are planar, except for YUY2.
See http://avisynth.nl/index.php/Filter_SDK/Data_storage.
jmac698
18th August 2014, 12:45
So I fixed that, now I'm getting random pixel garbage in my output which varies by frame. What could cause that?
https://www.sendspace.com/file/58l5o5
Gavino
18th August 2014, 13:50
Perhaps you are writing your pixel data to the wrong place, or failing to write all pixels (or all pixel planes).
Post the code of your GetFrame() function here.
jmac698
18th August 2014, 14:00
ok, thanks for looking at it
// HistByLine plugin
//
// Written by jmac698
// Copyleft 8-14-2014
//
// You may use this plugin however you wish,
// and distribute it under the terms of the GNU GPL.
//
// HISTORY:
// - Alpha 0.3: mode 0 is implemented and works, returns YV12 output only
// - tested with inputs of YV12, YUY2, Y8
//
// Plugin parameters:
// ------------------
// HistByLine(clip, int mode)
//
// clip clip: input clip, can be YUY2, YV12 or Y8
// int mode (default: 0):
// 0 - if count>0 then luma=255
// 1 - scaled count with 255=1.0
// 2 - lsb of count
// 3 - msb of count
//
// output: RGB32 color image
//
// Based on DemosaicCPP by StainlessS
#include <Windows.h>
#include <stdlib.h>
#include <string.h>
#include "Avisynth.h"
// class HistByLineCPP
class HistByLineCPP : public GenericVideoFilter {
private:
// ---------- avisynth.h interface extension - begin ----------
// The following is missing from avisynth.h version 3 and is needed by this v2.5 plugin.
//
// From Avisynth.h Version 5
// CS_Shift_Sample_Bits = 16,
// CS_Sample_Bits_Mask = 7 << CS_Shift_Sample_Bits,
// CS_Sample_Bits_8 = 0 << CS_Shift_Sample_Bits,
//
// Planar match mask 1111.0000.0000.0111.0000.0111.0000.0111 // 0xF0070707
// Planar signature 10xx.0000.0000.00xx.0000.00xx.00xx.00xx // 0xCFFCFCCC, 0x80000000
// Planar filter mask 1111.1111.1111.1111.1111.1111.1100.1111 // 0xFFFFFFCF
//
enum {
V25_CS_Shift_Sample_Bits = 16,
V25_CS_Sample_Bits_Mask = (7 << V25_CS_Shift_Sample_Bits),
V25_CS_Sample_Bits_8 = (0 << V25_CS_Shift_Sample_Bits),
V25_CS_PLANAR_MASK = 0xF0070707,
V25_CS_PLANAR_FILTER = 0xFFFFFFCF,
V25_CS_Y = VideoInfo::CS_PLANAR | VideoInfo::CS_INTERLEAVED | VideoInfo::CS_YUV,
V25_CS_Y8 = V25_CS_Y | V25_CS_Sample_Bits_8
};
bool V25_IsY8(VideoInfo&vix) const { return (vix.pixel_type & V25_CS_PLANAR_MASK) == (V25_CS_Y8 & V25_CS_PLANAR_FILTER);}
// ---------- avisynth_c interface extension - end ----------
const int mode;
VideoInfo src_vi;
public:
HistByLineCPP(PClip _child,int _mode,IScriptEnvironment* env); // Constructor
~HistByLineCPP(); // Destructor
PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment* env); // GetFrame
};
// This is the constructor.
HistByLineCPP::HistByLineCPP(PClip _child,int _mode,IScriptEnvironment* env) :
GenericVideoFilter(_child),mode(_mode) {
if(!vi.IsYV12() && !vi.IsYUY2() && !V25_IsY8(vi)) // Synthesized for Y8
env->ThrowError("Input video format can be: YUY2, YV12, Y8");
src_vi = vi; // remember original source vi.
// we copied the clip type of the source, now we make some modifications
// Set up VideoInfo to return new colorspace YV12
vi.pixel_type = VideoInfo::CS_YV12; // RGB would be CS_BGR32
vi.width = 256; // only need width of 256 to hold histogram samples
}
// This is the destructor definition. This is called when the filter is destroyed.
HistByLineCPP::~HistByLineCPP()
{} // Empty Destructor
// GetFrame() Called by Avisynth to get frame n
PVideoFrame __stdcall HistByLineCPP::GetFrame(int n, IScriptEnvironment* env) {
// destination
PVideoFrame dest_frame;
BYTE* dest_data;
unsigned int dest_pitch;
unsigned int dest_height;
unsigned int dest_row_size;
unsigned int dest_uv_pitch; // for planar (yv12)
unsigned int dest_uv_height;
BYTE* dest_data_u;
BYTE* dest_data_v;
unsigned int dest_uv_row_size;
dest_frame = env->NewVideoFrame(vi);
dest_data = dest_frame->GetWritePtr(PLANAR_Y);
dest_pitch = dest_frame->GetPitch(PLANAR_Y);
dest_height = dest_frame->GetHeight(PLANAR_Y);
dest_row_size = dest_frame->GetRowSize(PLANAR_Y);
dest_uv_pitch = dest_frame->GetPitch(PLANAR_U);
dest_uv_height = dest_frame->GetHeight(PLANAR_U);
dest_data_u = dest_frame->GetWritePtr(PLANAR_U);
dest_data_v = dest_frame->GetWritePtr(PLANAR_V);
dest_uv_row_size = dest_frame->GetRowSize(PLANAR_U);
// source
PVideoFrame src_frame;
CONST BYTE* src_data;
unsigned int src_pitch;
unsigned int src_row_size; // in bytes!
unsigned int src_height;
unsigned int src_inc; // next pixel in row
src_frame = child->GetFrame(n, env);
if(src_vi.IsYV12()) {
src_data = src_frame->GetReadPtr(PLANAR_Y);
src_pitch = src_frame->GetPitch(PLANAR_Y);
src_row_size= src_frame->GetRowSize(PLANAR_Y); // in bytes!
src_height = src_frame->GetHeight(PLANAR_Y);
src_inc = 1;
} else if(src_vi.IsYUY2()) {
src_data = src_frame->GetReadPtr();
src_pitch = src_frame->GetPitch();
src_row_size= src_frame->GetRowSize(); // in bytes!
src_height = src_frame->GetHeight();
src_inc = 2;
} else if(V25_IsY8(src_vi)) {
src_data = src_frame->GetReadPtr();
src_pitch = src_frame->GetPitch();
src_row_size= src_frame->GetRowSize(); // in bytes!
src_height = src_frame->GetHeight();
src_inc = 1;
} else { // somethings very wrong
return dest_frame; // dest_frame is junk ???
}
unsigned int x, y;
// RGB is upside down
// dest_data += (dest_height - 1) * dest_pitch; // only needed if returning RGB
// make dest_frame luma 0
for (y = 0; y < dest_height; ++y) {
for (x = 0; x < dest_row_size; ++x) {
dest_data[x] = 0; // initialize luma of dest frame to 0
}
dest_data += dest_pitch;
}
// make dest_frame grey (for planar formats)
for (y = 0; y < dest_uv_height; ++y) {
for (x = 0; x < dest_uv_row_size; ++x) {
dest_data_u[x] = 128;
dest_data_v[x] = 128;
}
dest_data_u += dest_uv_pitch;
dest_data_v += dest_uv_pitch;
}
dest_data = dest_frame->GetWritePtr(PLANAR_Y); // reset dest_data
for (y = 0; y < src_height; ++y) {
for (x = 0; x < src_row_size; x += src_inc) {
// int mode (default: 0):
// 0 - if count>0 then luma=255
// 1 - scaled count with 255=1.0
// 2 - lsb of count
// 3 - msb of count
if (mode == 0) {
dest_data[src_data[x]] = 255; //assumes dest_data was initialized with 0's
}
}
src_data += src_pitch;
// dest_data -= dest_pitch; // decrement in the case of RGB
dest_data += dest_pitch;
}
return dest_frame;
}
AVSValue __cdecl Create_HistByLineCPP(AVSValue args, void* user_data, IScriptEnvironment* env) {
return new HistByLineCPP( // Arguments to the Filter class Constructor of HistByLineCPP class.
args[0].AsClip(), // Clip
args[1].AsInt(0), // mode
env
);
}
extern "C" __declspec(dllexport) const char* __stdcall AvisynthPluginInit2(IScriptEnvironment* env) {
env->AddFunction(
"HistByLineCPP", // The name of the filter in Avisynth.
"c[mode]i",
Create_HistByLineCPP, // The function Avisynth will call to create an instance of the filter.
0
);
// Arguments is a string that defines the types and optional names of the arguments for you filter.
// c - Video Clip
// i - Integer number
// f - Float number
// s - String
// b - boolean
// . - Any type (dot)
// Array Specifiers
// i* - Integer Array, zero or more
// i+ - Integer Array, one or more
// .* - Any type Array, zero or more
// .+ - Any type Array, one or more
// Etc
return "`HistByLineCPP' HistByLineCPP plugin"; // A freeform name of the plugin.
}
Gavino
18th August 2014, 14:23
dest_data[src_data[x]] = 255; //assumes dest_data was initialized with 0's
You cannot assume frame is initialised to zero - it is just random memory.
You need to uncomment the earlier code which initialises it explicitly.
Also, to allow for YUY2 source, x needs to be incremented by src_inc rather than via x++ in the inner loop.
jmac698
18th August 2014, 15:08
Awesome, all working now, code updated, maybe it'll be an example for someone :)
vBulletin® v3.8.11, Copyright ©2000-2026, vBulletin Solutions Inc.