summaryrefslogtreecommitdiff
path: root/rotord/src
diff options
context:
space:
mode:
Diffstat (limited to 'rotord/src')
-rw-r--r--rotord/src/graph.cpp13
-rw-r--r--rotord/src/nodes_filters.h11
-rw-r--r--rotord/src/nodes_maths.h10
-rwxr-xr-xrotord/src/rotor.cpp1
-rwxr-xr-xrotord/src/rotor.h171
5 files changed, 177 insertions, 29 deletions
diff --git a/rotord/src/graph.cpp b/rotord/src/graph.cpp
index 139873f..5871b7b 100644
--- a/rotord/src/graph.cpp
+++ b/rotord/src/graph.cpp
@@ -43,6 +43,9 @@ bool Graph::video_render(const string &output_filename,const string &audio_filen
}
if (find_node("video_output")) {
Video_output *video_output=dynamic_cast<Video_output*>(find_node("video_output"));
+ for (auto f: find_nodes("video_feedback")){
+ (dynamic_cast<Video_feedback*>(f))->set_feedback(&(video_output->image));
+ }
return video_output->render(duration,framerate,output_filename,audio_filename,progress,outW,outH);
}
@@ -167,10 +170,10 @@ bool Graph::parseJson(string &data,string &media_path){
for (int l=0;l<jnodes[i]["parameters"].size();l++){
string parameter=jnodes[i]["parameters"][l]["name"].asString();
- /*
+
if (nodes[nodeID]->parameters.find(parameter)!=nodes[nodeID]->parameters.end()) {
- string val=jnodes[i]["parameters"][l]["value"].asString();
- if (val!="") nodes[nodeID]->parameters.find(parameter)->second->value=ofToFloat(val);
+ float val=jnodes[i]["parameters"][l]["value"].asFloat();
+ nodes[nodeID]->parameters.find(parameter)->second->value=val;
string fromID=jnodes[i]["parameters"][l]["from"].asString();
if (nodes.find(fromID)!=nodes.end()) {
if (!nodes[nodeID]->parameters[parameter]->connect(nodes[fromID])){
@@ -181,8 +184,8 @@ bool Graph::parseJson(string &data,string &media_path){
}
else if (fromID!="") cerr << "Rotor: linking parameter " << parameter << " of node: '" << nodeID << "', cannot find target '" << fromID << "'" << endl;
}
- else cerr << "Rotor: cannot find parameter input '" << parameter << "' of "<<settings["type"]<<" "<< nodeID << endl;
- */
+ else cerr << "Rotor: cannot find parameter '" << parameter << "' of "<<settings["type"]<<" "<< nodeID << endl;
+
}
//
diff --git a/rotord/src/nodes_filters.h b/rotord/src/nodes_filters.h
index 0cc1d77..4e2826f 100644
--- a/rotord/src/nodes_filters.h
+++ b/rotord/src/nodes_filters.h
@@ -12,7 +12,7 @@ namespace Rotor {
create_parameter("size","number","Blur window size","Size",1.0f);
create_image_input("image input","Image input");
};
- Blur(map<string,string> &settings) {
+ Blur(map<string,string> &settings):Blur() {
base_settings(settings);
};
~Blur(){
@@ -48,7 +48,7 @@ namespace Rotor {
base_settings(settings);
}
~Luma_levels(){if (LUT) { delete[] LUT;} };
- void generate_LUT(){
+ void generate_LUT(){
//can check here if anything has changed
if (LUT) delete[] LUT;
LUT=new unsigned char[256];
@@ -227,7 +227,7 @@ namespace Rotor {
delete[] LUT;
}
};
- void generate_LUT(){
+ void generate_LUT(){
//can check here if anything has changed
if (LUT) {
for (int i=0;i<3;i++) {
@@ -264,6 +264,9 @@ namespace Rotor {
out.RGBdata[i*3+1]=LUT[1][in.RGBdata[i*3+1]];
out.RGBdata[i*3+2]=LUT[2][in.RGBdata[i*3+2]];
}
+ if (!in.alpha.empty()){
+ out.alpha=in.alpha;
+ }
}
Image *output(const Frame_spec &frame){
Image *in=image_inputs[0]->get(frame);
@@ -280,4 +283,4 @@ namespace Rotor {
}
-#endif \ No newline at end of file
+#endif
diff --git a/rotord/src/nodes_maths.h b/rotord/src/nodes_maths.h
index eb685eb..760040d 100644
--- a/rotord/src/nodes_maths.h
+++ b/rotord/src/nodes_maths.h
@@ -61,12 +61,14 @@ namespace Rotor {
#define ARITHMETIC_cos 8
#define ARITHMETIC_ease 9
#define ARITHMETIC_jolt 10
+#define ARITHMETIC_floor 11
+#define ARITHMETIC_2pow 12
class Arithmetic: public Signal_node {
public:
Arithmetic(){
create_signal_input("signal","Signal");
create_parameter("value","number","Value or signal for operation","Value",1.0f);
- create_attribute("operator","operator for image","Operator","+",{"+","-","*","/","%","^","sin","cos","ease","jolt"});
+ create_attribute("operator","operator for image","Operator","+",{"+","-","*","/","%","^","sin","cos","ease","jolt","floor","2pow"});
title="Arithmetic";
description="Performs arithmetic on a signal with a signal or value";
};
@@ -116,6 +118,12 @@ namespace Rotor {
case ARITHMETIC_jolt:
return ((1.0-parameters["value"]->value)*in)+(parameters["value"]->value*(0.5f+((sin((fmod(in,1.0f)+1.0f)*M_PI))*0.5f)));
break;
+ case ARITHMETIC_floor:
+ return floor(in);
+ break;
+ case ARITHMETIC_2pow:
+ return pow(2,in);
+ break;
}
}
}
diff --git a/rotord/src/rotor.cpp b/rotord/src/rotor.cpp
index 8883a71..8b52301 100755
--- a/rotord/src/rotor.cpp
+++ b/rotord/src/rotor.cpp
@@ -43,6 +43,7 @@ Node_factory::Node_factory(){
//video nodes
add_type("video_loader",new Video_loader());
add_type("video_output",new Video_output());
+ add_type("video_feedback",new Video_feedback());
}
bool Signal_input::connect(Node* source) {
connection=dynamic_cast<Signal_node*>(source);
diff --git a/rotord/src/rotor.h b/rotord/src/rotor.h
index 2549683..da1ac8a 100755
--- a/rotord/src/rotor.h
+++ b/rotord/src/rotor.h
@@ -257,7 +257,7 @@ namespace Rotor {
lut=nullptr;
};
~LUT(){if (lut) { delete[] lut;} };
- void generate(float black_in,float white_in,float black_out,float white_out,float gamma){
+ void generate(float black_in,float white_in,float black_out,float white_out,float gamma){
//can check here if anything has changed
if (lut) delete[] lut;
lut=new unsigned char[256];
@@ -434,14 +434,14 @@ namespace Rotor {
Image *output(const Frame_spec &frame){
if (palette.size()) {
int col=((int)inputs[0]->get((Time_spec)frame))%palette.size();
- if (col!=prevcol){ //how about when starting a new render?
+ //if (col!=prevcol){ //how about when starting a new render?
for (int i=0;i<image.w*image.h;i++){
image.RGBdata[i*3]=palette[col].r;
image.RGBdata[i*3+1]=palette[col].g;
image.RGBdata[i*3+2]=palette[col].b;
}
prevcol=col;
- }
+ //}
return &image;
}
return nullptr;
@@ -679,6 +679,11 @@ namespace Rotor {
}
private:
};
+ #define TRANSFORM_nearest 1
+ #define TRANSFORM_linear 2
+ #define TRANSFORM_area 3
+ #define TRANSFORM_cubic 4
+ #define TRANSFORM_lanczos 5
class Transform: public Image_node {
//what is the best coordinate system to use?
//origin: corner or centre
@@ -693,6 +698,7 @@ namespace Rotor {
create_parameter("originY","number","Y transformation origin","Origin Y",0.5f);
create_parameter("rotation","number","Rotation about origin","Rotation",0.0f);
create_parameter("scale","number","Scale about origin","Scale",1.0f);
+ create_attribute("filter","Filtering mode","Filter mode","linear",{"nearest","linear","area","cubic","lanczos"});
title="Transform";
description="Apply 2D transformation";
};
@@ -705,6 +711,25 @@ namespace Rotor {
Image *output(const Frame_spec &frame){
Image *in=image_inputs[0]->get(frame);
if (in){
+ int filtmode;
+ switch(attributes["filter"]->intVal){
+ case TRANSFORM_nearest:
+ filtmode=cv::INTER_NEAREST;
+ break;
+ case TRANSFORM_linear:
+ filtmode=cv::INTER_LINEAR;
+ break;
+ case TRANSFORM_area:
+ filtmode=cv::INTER_AREA;
+ break;
+ case TRANSFORM_cubic:
+ filtmode=cv::INTER_CUBIC;
+ break;
+ case TRANSFORM_lanczos:
+ filtmode=cv::INTER_LANCZOS4;
+ break;
+ }
+
float tX=parameters["transformX"]->value;
float tY=parameters["transformY"]->value;
float oX=parameters["originX"]->value;
@@ -732,7 +757,7 @@ namespace Rotor {
dstTri[i].y=srcTri[i].y+(tY*in->h);
}
trans_mat=getAffineTransform( srcTri, dstTri );
- warpAffine( in->rgb, inter.rgb, trans_mat, inter.rgb.size(), cv::INTER_LINEAR, cv::BORDER_WRAP);
+ warpAffine( in->rgb, inter.rgb, trans_mat, inter.rgb.size(), filtmode, cv::BORDER_WRAP);
// Compute rotation matrix
@@ -742,7 +767,8 @@ namespace Rotor {
rot_mat = getRotationMatrix2D( centre, r, s );
// Do the transformation
//
- warpAffine( inter.rgb, image.rgb, rot_mat, image.rgb.size(), cv::INTER_LINEAR, cv::BORDER_WRAP);
+
+ warpAffine( inter.rgb, image.rgb, rot_mat, image.rgb.size(), filtmode, cv::BORDER_WRAP);
//BORDER_WRAP
//INTER_NEAREST - a nearest-neighbor interpolation
@@ -793,49 +819,123 @@ namespace Rotor {
Difference_matte(){
create_image_input("image input","Image input");
create_image_input("background input","Background input");
- create_parameter("threshold","number","Difference threshold","Threshold",0.05f,0.0f,1.0f);
+ create_parameter("threshold","number","Difference threshold","Threshold",0.2f,0.0f,1.0f);
+ create_parameter("feather","number","Feather width","Feather",0.1f,0.0f,1.0f);
+ create_parameter("weight_h","number","H component weight","Weight H",0.5f,0.0f,1.0f);
+ create_parameter("weight_s","number","S component weight","Weight S",0.5f,0.0f,1.0f);
+ create_parameter("weight_v","number","V component weight","Weight V",0.5f,0.0f,1.0f);
+ create_parameter("blursize","number","Blur size","Blur size",2.0f,0.0f,10.0f);
create_attribute("mode","Output {image|alpha}","output mode","alpha",{"image","alpha"});
title="Difference matte";
description="Create an alpha channel using a background reference picture";
+ LUT=nullptr;
};
Difference_matte(map<string,string> &settings):Difference_matte() {
base_settings(settings);
};
- ~Difference_matte(){};
+ ~Difference_matte(){if (LUT) delete[] LUT;};
Difference_matte* clone(map<string,string> &_settings) { return new Difference_matte(_settings);};
Image *output(const Frame_spec &frame){
Image *in1=image_inputs[0]->get(frame);
if (in1){
Image *in2=image_inputs[1]->get(frame);
if (in2) {
-
+ generate_LUT();
+
+ /*
cv::cvtColor(in1->rgb,greyfg,CV_RGB2GRAY);
cv::cvtColor(in2->rgb,greybg,CV_RGB2GRAY);
cv::absdiff(greyfg,greybg,greyDiff);
//parameters["threshold"]->value
cv::threshold(greyDiff,mask,parameters["threshold"]->value,255,CV_THRESH_BINARY); //int block_size=3, double param1=5); //int blockSize, int offset=0,bool invert=false, bool gauss=false);
-
+
//cv::adaptiveThreshold(greyDiff,mask,255,CV_ADAPTIVE_THRESH_GAUSSIAN_C,CV_THRESH_BINARY, 3,5); //int block_size=3, double param1=5); //int blockSize, int offset=0,bool invert=false, bool gauss=false);
+ */
+
+ cv::cvtColor(in1->rgb, hsv1, CV_RGB2HSV);
+ cv::cvtColor(in2->rgb, hsv2, CV_RGB2HSV);
+
+ mask.create(frame.h,frame.w,CV_8UC1);
+ lutmask.create(frame.h,frame.w,CV_8UC1);
+
+ //get euclidean distance in HSV space
+ int dist,d;
+ uint8_t m;
+
+ float weights[3] = {parameters["weight_h"]->value,parameters["weight_s"]->value,parameters["weight_v"]->value};
+ float weight_total=255.0f/pow(pow(weights[0]*255,2)+pow(weights[1]*255,2)+pow(weights[2]*255,2),0.5);
+
+ for (int i=0;i<frame.w*frame.h;i++){
+ dist=0;
+ for (int j=0;j<3;j++){
+ d=((int)hsv1.data[i*3+j])-((int)hsv2.data[i*3+j]);
+ dist+=(d*d)*weights[j];
+ }
+ uint8_t id=(uint8_t)(sqrt((float)dist)*weight_total);
+ mask.data[i]=id;
+ }
+
+ /*
+
+ for (int i=0;i<frame.w*frame.h;i++){
+ dist=0;
+ for (int j=0;j<3;j++){
+ d=((int)hsv1.data[i*3+j])-((int)hsv2.data[i*3+j]);
+ dist+=(abs(d))*weights[j];
+ }
+ uint8_t id=(uint8_t)(((float)dist)/weight_total);
+ m=LUT[id];
+ mask.data[i]=m;
+ }
+ */
+
+ //cv::bilateralFilter(mask,filtmask, 4,8,2 );
+ //cv::GaussianBlur(mask,filtmask,cv::Size( 4, 4 ), 2, 2);
+
+ int ksize=max((ceil(parameters["blursize"]->value/2.0)*2)+1,1.0);
+ //nb this doesn't do the intended: create 'continuously variable' blur
+ cv::GaussianBlur(mask,filtmask,cvSize(ksize,ksize),parameters["blursize"]->value);
+
+
+ for (int i=0;i<frame.w*frame.h;i++){
+ lutmask.data[i]=LUT[filtmask.data[i]];
+ }
+
+
image=(*in1);
if (attributes["mode"]->value=="image"){
- image.setup_fromMat(mask);
+ cv::cvtColor(lutmask, image.rgb, CV_GRAY2RGB);
}
- else image.alpha_from_cv(mask);
+ else image.alpha_from_cv(lutmask);
return &image;
-
- //cv::cvtColor(in1->rgb, hsv1, CV_RGB2HSV);
- //cv::cvtColor(in2->rgb, hsv2, CV_RGB2HSV);
-
+
+
+
}
//if there aren't 2 image inputs connected just return the first
return in1;
}
return nullptr;
}
+ void generate_LUT(){
+ //can check here if anything has changed
+ //cerr<<"generating LUT: threshold "<<parameters["threshold"]->value<<", feather "<<parameters["feather"]->value<<endl;
+ if (LUT) delete[] LUT;
+ LUT=new uint8_t[256];
+ float fltmax=(255.0f/256.0f);
+ float minf=max(0.0f,parameters["threshold"]->value-(parameters["feather"]->value*0.5f));
+ float maxf=min(1.0f,parameters["threshold"]->value+(parameters["feather"]->value*0.5f));
+ for (int i=0;i<256;i++){
+ LUT[i]=(uint8_t)(min(1.0f,max(0.0f,((((float)i)/255.0f)-minf)/(maxf-minf)))*255.0f);
+ // cerr<<((int)LUT[i])<<" ";
+ }
+ //cerr<<endl;
+ }
private:
- cv::Mat greyfg,greybg,greyDiff,mask;
+ cv::Mat greyfg,greybg,greyDiff,mask,filtmask,lutmask;
cv::Mat hsv1,hsv2;
+ uint8_t *LUT;
};
#define VIDEOFRAMES_frame 1
#define VIDEOFRAMES_blend 2
@@ -878,7 +978,14 @@ namespace Rotor {
};
~Video_output(){ };
Image *output(const Frame_spec &frame){
- return image_inputs[0]->get(frame);
+ Image *in=image_inputs[0]->get(frame);
+ if (in){
+ //make copy of the image, for feedback
+ //optimise?
+ image=(*in);
+ return &image;
+ }
+ return nullptr;
};
Video_output* clone(map<string,string> &_settings) { return new Video_output(_settings);};
bool render(const float duration, const float framerate,const string &output_filename,const string &audio_filename,float& progress,int w,int h);
@@ -886,6 +993,32 @@ namespace Rotor {
private:
};
+ class Video_feedback: public Image_node {
+ public:
+ Video_feedback(){
+ title="Video feedback";
+ description="Repeats output of the last frame";
+ feedback=nullptr;
+ };
+ Video_feedback(map<string,string> &settings):Video_feedback() {
+ base_settings(settings);
+ };
+ ~Video_feedback(){ };
+ void set_feedback(Image *iptr){
+ feedback=iptr;
+ }
+ Image *output(const Frame_spec &frame){
+ if (feedback->RGBdata){
+ return feedback;
+ }
+ image.setup(frame.w,frame.h);
+ image.clear();
+ return &image;
+ };
+ Video_feedback* clone(map<string,string> &_settings) { return new Video_feedback(_settings);};
+ private:
+ Image *feedback;
+ };
//-------------------------------------------------------------------
class Node_factory{
public:
@@ -983,7 +1116,7 @@ namespace Rotor {
if (type.second->description!="") { //blank description = internal/ testing node
list_node(type,XML,i);
i++;
- }
+ }
}
}
void list_nodes(Json::Value &JSON){
@@ -1051,7 +1184,7 @@ namespace Rotor {
}
}
JSON["nodeslist"].append(node);
- }
+ }
}
}
private: