summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--rotord/src/graph.cpp5
-rw-r--r--rotord/src/graph.h1
-rw-r--r--rotord/src/nodes_filters.h251
-rw-r--r--rotord/src/nodes_maths.h45
-rw-r--r--rotord/src/nodes_transform.h68
-rw-r--r--rotord/src/rendercontext.cpp45
-rw-r--r--rotord/src/rotor.cpp260
-rw-r--r--rotord/src/rotor.h869
8 files changed, 418 insertions, 1126 deletions
diff --git a/rotord/src/graph.cpp b/rotord/src/graph.cpp
index 8881a5f..5f63a0b 100644
--- a/rotord/src/graph.cpp
+++ b/rotord/src/graph.cpp
@@ -24,7 +24,6 @@ Node* Graph::find_node(const string &type){
}
return nullptr; //can be tested against
};
-/*
bool Graph::signal_render(string &signal_xml,const float framerate) {
if (find_node("signal_output")) {
Signal_output *signal_output=dynamic_cast<Signal_output*>(find_node("signal_output"));
@@ -34,7 +33,6 @@ bool Graph::signal_render(string &signal_xml,const float framerate) {
return false;
}
-*/
bool Graph::print_features(xmlIO &XML,string &node){
if (nodes.find(node)!=nodes.end()){
if (dynamic_cast<Audio_processor*>(nodes[node])){
@@ -218,7 +216,8 @@ bool Graph::video_render(const string &output_filename,const float framerate,int
if (usingaudio) {delete a;};
}
- exporter.encodeFrame(i->RGBdata,true); //final keyframe;
+
+ //exporter.encodeFrame(i->RGBdata,true); //final keyframe;
exporter.finishRecord();
diff --git a/rotord/src/graph.h b/rotord/src/graph.h
index 7ab7962..06f7970 100644
--- a/rotord/src/graph.h
+++ b/rotord/src/graph.h
@@ -4,6 +4,7 @@
#include "Poco/StringTokenizer.h"
#include "rotor.h"
+#include "nodes_signals.h"
#include "nodes_audio_analysis.h"
diff --git a/rotord/src/nodes_filters.h b/rotord/src/nodes_filters.h
index 7491ff1..2c9cea1 100644
--- a/rotord/src/nodes_filters.h
+++ b/rotord/src/nodes_filters.h
@@ -68,258 +68,7 @@ namespace Rotor {
float size;
cv::Mat hsv,hsv1;
};
- class Luma_levels: public Image_node {
- public:
- Luma_levels(){
- create_image_input("image input","Image input");
- create_parameter("black_in","number","input black point","Input black point",0.0f,0.0f,1.0f);
- create_parameter("white_in","number","input white point","Input white point",1.0f,0.0f,1.0f);
- create_parameter("gamma","number","gamma level","Gamma",1.0f,0.0f,10.0f);
- create_parameter("black_out","number","output black point","Output black point",0.0f,0.0f,1.0f);
- create_parameter("white_out","number","output white point","Output white point",1.0f,0.0f,1.0f);
- title="Luma levels";
- description="Remap luma values of image";
- LUT=nullptr;
- UID="4e500576-2d0b-11e3-b234-cf74b6a122e4";
- };
- Luma_levels(map<string,string> &settings):Luma_levels() {
- base_settings(settings);
- }
- ~Luma_levels(){if (LUT) { delete[] LUT;} };
- void generate_LUT(){
- //can check here if anything has changed
- if (LUT) delete[] LUT;
- LUT=new unsigned char[256];
- float fltmax=(255.0f/256.0f);
- for (int i=0;i<256;i++){
- LUT[i]=(unsigned char)(((pow(min(fltmax,max(0.0f,(((((float)i)/256.0f)-parameters["black_in"]->value)/(parameters["white_in"]->value-parameters["black_in"]->value)))),(1.0/parameters["gamma"]->value))*(parameters["white_out"]->value-parameters["black_out"]->value))+parameters["black_out"]->value)*255.0f);
- }
- }
- void apply_LUT(const Image& in){
- apply_LUT(in,image);
- }
- void apply_LUT(const Image& in,Image &out){ //facility to apply to other images for inherited classes
- out.setup(in.w,in.h);
- for (int i=0;i<out.w*out.h*3;i++){
- out.RGBdata[i]=LUT[in.RGBdata[i]];
- }
- }
- Image *output(const Frame_spec &frame){
- Image *in=image_inputs[0]->get(frame);
- if (in){
- generate_LUT();
- apply_LUT(*in);
- }
- return &image;
- }
- Luma_levels* clone(map<string,string> &_settings) { return new Luma_levels(_settings);};
- protected:
- unsigned char *LUT;
- };
- class Echo_trails: public Luma_levels {
- //draw trail frames additively that fade off over time
- //the hard thing here is how to cache frames, if its done cleverly it could have no impact when
- //used linearly
- //Image needs to overload operator+
- //need a clever data structure to cache frames - maybe a map of Image pointers
-
- //we know the frames we want to overlay as offsets ie -25,-20,-15,-10,-5
- //do we keep 25 frames loaded in order to benefit? 25 PAL frames is 60MB so probably so
- //OK so:
- //make a new set of pointers
- //identify if any of the new pointers can inherit old frames
- //delete unneeded old frames
- //load new frames
- //do the calculations
-
- //new set of pointers? or track frames by absolute frame number?
- //with relative pointers and switching frames, could use auto_ptr?
-
- //this cache mechanism should maybe be inheritable too?
- //it could be hugely beneficial to only do the LUT once?
- //although maybe the way to do the fading is to have a LUT for each frame?
-
- //or is it actually best to use alpha keying after all!
- public:
- Echo_trails(){
- //calls base class constructor first
- create_parameter("number","number","number of echoes","Number echoes",25.0f);
- create_parameter("fadeto","number","amount that echoes fade out","Fadout amount",1.0f,0.0f,1.0f);
- create_attribute("mode","blend mode for echoes","Blend mode","screen",{"screen","wrap"});
- title="Echo trails";
- description="Draw trail frames additively that fade off over time";
- UID="5b1ab684-2d0b-11e3-8fa2-970be8c360dd";
- };
- Echo_trails(map<string,string> &settings):Echo_trails() {
- base_settings(settings);
- lastframe=-1;
- }
- ~Echo_trails(){
- for (auto i:images) delete i.second;
- };
- Image *output(const Frame_spec &frame){
- //check if cache is valid
- if (images.size()){
- if (frame.w!=image.w||frame.h!=image.h){ //or framerate changed?
- //clear cache and start over
- images.clear();
- lastframe=-1;
- //calculate frame interval
- //interval=(int)(((duration/number)*frame.framerate)+0.5);
- //total=interval*number;
- }
- }
- int thisframe=frame.frame();
- //iterate cache and throw out any obsolete frames
- auto i = std::begin(images);
- while (i != std::end(images)) {
- // check if the image is in the range we need
- if (thisframe-(*i).first>(int)parameters["number"]->value||thisframe-(*i).first<0) {
- delete (*i).second;
- i = images.erase(i);
- }
- else
- ++i;
- }
- //if frame has already been calculated just return it
- if (thisframe!=lastframe) {
- Image *in=image_inputs[0]->get(frame);
- if (in) {
- generate_LUT();
- //need a better strategy here, should be able to get each image once
- //copy incoming image **writable
- image=*(in);
- images[thisframe]=new Image(frame.w,frame.h);
- apply_LUT(image,*(images[thisframe]));
- for (int i=1;i<(int)parameters["number"]->value;i++){
- //check echo frame isn't at negative time
- int absframe=thisframe-i;
- if (absframe>-1){
- //check if image is in the cache
- if (images.find(absframe)==images.end()){
- images[absframe]=new Image(frame.w,frame.h);
- Frame_spec wanted=Frame_spec(absframe,frame.framerate,frame.duration,frame.w,frame.h);
- apply_LUT(*(((Image_node*)image_inputs[0]->connection)->get_image_output(wanted)),*(images[absframe]));
- }
- if (fless(1.0f,parameters["fadeto"]->value)){
- float amount=(((parameters["number"]->value-i)/parameters["number"]->value)*(1.0f-parameters["fadeto"]->value))+(1.0f-parameters["fadeto"]->value);
- Image *temp=*images[absframe]*amount;
- if (attributes["mode"]->value=="screen") {
- image+=*temp;
- }
- else {
- image.add_wrap(*temp);
- }
- delete temp;
- }
- else {
- if (attributes["mode"]->value=="screen") image+=*(images[absframe]);
- else image=image.add_wrap(*(images[absframe]));
- }
- }
- }
- //for (int i=0;i<frame.w*frame.h*3;i++){
- // image->RGBdata[i]=LUT[in->RGBdata[i]];
- //}
- lastframe=thisframe;
- }
- }
- return &image;
- }
- Echo_trails* clone(map<string,string> &_settings) { return new Echo_trails(_settings);};
- protected:
- int interval,total,lastframe; //number of frames between displayed echoes
- unordered_map<int,Image*> images;
- };
- class RGB_levels: public Image_node {
- public:
- RGB_levels(){
- create_image_input("image input","Image input");
- create_parameter("red_black_in","number","Red input black-point","Red input black-point",0.0f,0.0f,1.0f);
- create_parameter("red_white_in","number","Red input white-point","Red input white-point",1.0f,0.0f,1.0f);
- create_parameter("red_gamma","number","Red gamma level","Red gamma",1.0f,0.01f,10.0f);
- create_parameter("red_black_out","number","Red output black point","Red output black point",0.0f,0.0f,1.0f);
- create_parameter("red_white_out","number","Red output white point","Red output white point",1.0f,0.0f,1.0f);
- create_parameter("green_black_in","number","Green input black point","Green input black point",0.0f,0.0f,1.0f);
- create_parameter("green_white_in","number","Green input white point","Green input white point",1.0f,0.0f,1.0f);
- create_parameter("green_gamma","number","Green gamma level","Green gamma",1.0f,0.01f,10.0f);
- create_parameter("green_black_out","number","Green output black point","Green output black point",0.0f,0.0f,1.0f);
- create_parameter("green_white_out","number","Green output white point","Green output white point",1.0f,0.0f,1.0f);
- create_parameter("blue_black_in","number","Blue input black point","Blue input black point",0.0f,0.0f,1.0f);
- create_parameter("blue_white_in","number","Blue input white point","Blue input white point",1.0f,0.0f,1.0f);
- create_parameter("blue_gamma","number","Blue gamma level","Blue gamma",1.0f,0.01f,10.0f);
- create_parameter("blue_black_out","number","Blue output black point","Blue output black point",0.0f,0.0f,1.0f);
- create_parameter("blue_white_out","number","Blue output white point","Blue output white point",1.0f,0.0f,1.0f);
- title="RGB levels";
- description="Remap RGB values of image";
- LUT=nullptr;
- UID="68522cba-2d0b-11e3-8767-8f3c605e9bed";
- };
- RGB_levels(map<string,string> &settings):RGB_levels() {
- base_settings(settings);
- }
- ~RGB_levels(){
- if (LUT) {
- for (int i=0;i<3;i++) {
- delete[] LUT[i];
- }
- delete[] LUT;
- }
- };
- void generate_LUT(){
- //can check here if anything has changed
- if (LUT) {
- for (int i=0;i<3;i++) {
- delete[] LUT[i];
- }
- delete[] LUT;
- }
- LUT=new unsigned char*[3];
- for (int i=0;i<3;i++){
- LUT[i]=new unsigned char[256];
- }
- float fltmax=(255.0f/256.0f);
- for (int i=0;i<256;i++){
- LUT[0][i]=(unsigned char)(((\
- pow(min(fltmax,max(0.0f,(((((float)i)/256.0f)-parameters["red_black_in"]->value)/(parameters["red_white_in"]->value-parameters["red_black_in"]->value))))\
- ,(1.0/parameters["red_gamma"]->value))\
- *(parameters["red_white_out"]->value-parameters["red_black_out"]->value))+parameters["red_black_out"]->value)*255.0f);
- LUT[1][i]=(unsigned char)(((\
- pow(min(fltmax,max(0.0f,(((((float)i)/256.0f)-parameters["green_black_in"]->value)/(parameters["green_white_in"]->value-parameters["green_black_in"]->value))))\
- ,(1.0/parameters["green_gamma"]->value))\
- *(parameters["green_white_out"]->value-parameters["green_black_out"]->value))+parameters["green_black_out"]->value)*255.0f);
- LUT[2][i]=(unsigned char)(((\
- pow(min(fltmax,max(0.0f,(((((float)i)/256.0f)-parameters["blue_black_in"]->value)/(parameters["blue_white_in"]->value-parameters["blue_black_in"]->value))))\
- ,(1.0/parameters["blue_gamma"]->value))\
- *(parameters["blue_white_out"]->value-parameters["blue_black_out"]->value))+parameters["blue_black_out"]->value)*255.0f);
- }
- }
- void apply_LUT(const Image& in){
- apply_LUT(in,image);
- }
- void apply_LUT(const Image& in,Image &out){ //facility to apply to other images for inherited classes
- for (int i=0;i<out.w*out.h;i++){
- out.RGBdata[i*3]=LUT[0][in.RGBdata[i*3]];
- 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);
- if (in){
- generate_LUT();
- apply_LUT(*in);
- }
- return &image;
- }
- RGB_levels* clone(map<string,string> &_settings) { return new RGB_levels(_settings);};
- protected:
- unsigned char **LUT;
- };
}
diff --git a/rotord/src/nodes_maths.h b/rotord/src/nodes_maths.h
index 13947a0..07f111b 100644
--- a/rotord/src/nodes_maths.h
+++ b/rotord/src/nodes_maths.h
@@ -28,26 +28,28 @@ namespace Rotor {
}
Comparison* clone(map<string,string> &_settings) { return new Comparison(_settings);};
const float output(const Time_spec &time) {
- switch (attributes["operator"]->intVal) {
- case COMPARISON_Equal:
- return fequal(parameters["value"]->value,inputs[0]->get(time))?1.0f:0.0f;
- break;
- case COMPARISON_Not_equal:
- return fequal(parameters["value"]->value,inputs[0]->get(time))?0.0f:1.0f;
- break;
- case COMPARISON_Greater:
- return fgreater(parameters["value"]->value,inputs[0]->get(time))?1.0f:0.0f;
- break;
- case COMPARISON_Less:
- return fless(parameters["value"]->value,inputs[0]->get(time))?1.0f:0.0f;
- break;
- case COMPARISON_Greater_or_equal:
- return fgreater_or_equal(parameters["value"]->value,inputs[0]->get(time))?1.0f:0.0f;
- break;
- case COMPARISON_Less_or_equal:
- return fless_or_equal(parameters["value"]->value,inputs[0]->get(time))?1.0f:0.0f;
- break;
- }
+ if (inputs[0]->connection) {
+ switch (attributes["operator"]->intVal) {
+ case COMPARISON_Equal:
+ return fequal(parameters["value"]->value,inputs[0]->get(time))?1.0f:0.0f;
+ break;
+ case COMPARISON_Not_equal:
+ return fequal(parameters["value"]->value,inputs[0]->get(time))?0.0f:1.0f;
+ break;
+ case COMPARISON_Greater:
+ return fgreater(parameters["value"]->value,inputs[0]->get(time))?1.0f:0.0f;
+ break;
+ case COMPARISON_Less:
+ return fless(parameters["value"]->value,inputs[0]->get(time))?1.0f:0.0f;
+ break;
+ case COMPARISON_Greater_or_equal:
+ return fgreater_or_equal(parameters["value"]->value,inputs[0]->get(time))?1.0f:0.0f;
+ break;
+ case COMPARISON_Less_or_equal:
+ return fless_or_equal(parameters["value"]->value,inputs[0]->get(time))?1.0f:0.0f;
+ break;
+ }
+ }
return 0.0f;
}
@@ -248,6 +250,9 @@ namespace Rotor {
uint32_t seed;
private:
};
+
}
+
+
#endif
diff --git a/rotord/src/nodes_transform.h b/rotord/src/nodes_transform.h
index 624ad21..99f553e 100644
--- a/rotord/src/nodes_transform.h
+++ b/rotord/src/nodes_transform.h
@@ -186,6 +186,74 @@ namespace Rotor {
private:
Image still;
};
+#define MIRROR_horiz 1
+#define MIRROR_vert 2
+#define MIRROR_horizR 3
+#define MIRROR_vertR 4
+ class Mirror: public Image_node {
+ public:
+ Mirror(){
+ create_image_input("image input","Image input");
+ create_attribute("mode","Mirror mode","Mirror mode","horiz",{"horiz","vert","horizR","vertR"});
+ title="Mirror";
+ description="Mirror video across a central axis";
+ UID="1fed5b26-2d0a-11e3-9901-4b5ea78a005d";
+ };
+ Mirror(map<string,string> &settings):Mirror() {
+ base_settings(settings);
+ };
+ ~Mirror(){ };
+ Mirror* clone(map<string,string> &_settings) { return new Mirror(_settings);};
+ Image *output(const Frame_spec &frame){
+ Image *in=image_inputs[0]->get(frame);
+ if (in){
+ //copy incoming image **writable
+ image=(*in);
+ //could be more efficient here by only copying once
+ switch (attributes["mode"]->intVal) {
+ case MIRROR_horiz:
+ for (int i=0;i<image.w/2;i++){
+ for (int j=0;j<image.h;j++){
+ for (int k=0;k<3;k++){
+ image.RGBdata[(((j*image.w)+((image.w/2)+i))*3)+k]=image.RGBdata[(((j*image.w)+((image.w/2)-i))*3)+k];
+ }
+ }
+ }
+ break;
+ case MIRROR_vert:
+ for (int i=0;i<image.w;i++){
+ for (int j=0;j<image.h/2;j++){
+ for (int k=0;k<3;k++){
+ image.RGBdata[((((image.h/2+j)*image.w)+i)*3)+k]=image.RGBdata[((((image.h/2-j)*image.w)+i)*3)+k];
+ }
+ }
+ }
+ break;
+ case MIRROR_horizR:
+ for (int i=0;i<image.w/2;i++){
+ for (int j=0;j<image.h;j++){
+ for (int k=0;k<3;k++){
+ image.RGBdata[(((j*image.w)+((image.w/2)-i))*3)+k]=image.RGBdata[(((j*image.w)+((image.w/2)+i))*3)+k];
+ }
+ }
+ }
+ break;
+ case MIRROR_vertR:
+ for (int i=0;i<image.w;i++){
+ for (int j=0;j<image.h/2;j++){
+ for (int k=0;k<3;k++){
+ image.RGBdata[((((image.h/2-j)*image.w)+i)*3)+k]=image.RGBdata[((((image.h/2+j)*image.w)+i)*3)+k];
+ }
+ }
+ }
+ break;
+ }
+ return &image;
+ }
+ return nullptr;
+ }
+ private:
+ };
}
#endif
diff --git a/rotord/src/rendercontext.cpp b/rotord/src/rendercontext.cpp
index 47fa498..86331b4 100644
--- a/rotord/src/rendercontext.cpp
+++ b/rotord/src/rendercontext.cpp
@@ -240,49 +240,6 @@ void Render_context::session_command(const Session_command& command,xmlIO& XML,H
//should interrupt whatever is happening?
//before begining to load from xml
if (state==IDLE) { //eventually not like this
- /*
- if (Poco::File(graph_dir+command.body).exists()) {
- string graph_filename=graph_dir+command.body;
- if (graph.loadFile(graph_filename,media_dir)) {
- status=HTTPResponse::HTTP_OK;
- //XML.addValue("patchbay",graph.toString());
- //XML.loadFromBuffer(graph.toString());
- XML=graph.xml;
- //the graph could actually contain an xml object and we could just print it here?
- //or could our nodes even be subclassed from xml nodes?
- //the graph or the audio could load first- have to analyse the audio with vamp after the graph is loaded
- //for now the graph must load 1st
- if (graph.audio_loaded) {
- add_queue(Session_task(command.uid,ANALYSE_AUDIO));
- status=HTTPResponse::HTTP_OK;
- logger.information("Starting audio analysis for graph: "+command.commands[0]);
- XML.addValue("status","Starting audio analysis for graph: "+command.commands[0]);
- }
- }
- else {
- status=HTTPResponse::HTTP_INTERNAL_SERVER_ERROR; //~/sources/poco-1.4.6-all/Net/include/Poco/Net/HTTPResponse.h
- logger.error("ERROR: graph not loaded: check file");
- XML.addValue("error","graph not loaded: check file");
- }
- }
- else if (graph.load(command.body,media_dir)) {
- status=HTTPResponse::HTTP_OK;
- logger.information("Loaded graph from http PUT body");
- XML.addValue("status","Loaded graph from PUT body");
- if (graph.audio_loaded) {
- add_queue(Session_task(command.uid,ANALYSE_AUDIO));
- status=HTTPResponse::HTTP_OK;
- logger.information("Starting audio analysis for graph: "+command.commands[0]);
- XML.addValue("status","Starting audio analysis for graph: "+command.commands[0]);
- }
- }
- else {
- status=HTTPResponse::HTTP_INTERNAL_SERVER_ERROR; //~/sources/poco-1.4.6-all/Net/include/Poco/Net/HTTPResponse.h
- logger.error("ERROR: graph unreadable");
- XML.addValue("error","graph unreadable");
- }
- */
-
if (command.body!="") {
graph_filename="";
graph_body="";
@@ -346,7 +303,7 @@ void Render_context::session_command(const Session_command& command,xmlIO& XML,H
// framerate=toFloat(command.id);
//}
string signal_xml;
- if (false) { //graph.signal_render(signal_xml,framerate)){
+ if (graph.signal_render(signal_xml,framerate)){
status=HTTPResponse::HTTP_OK;
logger.information("rendering signal to xml");
XML.addValue("signal",signal_xml); //this doesn't work >> pseudo xml
diff --git a/rotord/src/rotor.cpp b/rotord/src/rotor.cpp
index 77d6d0f..444d861 100644
--- a/rotord/src/rotor.cpp
+++ b/rotord/src/rotor.cpp
@@ -1,4 +1,8 @@
#include "rotor.h"
+
+#include "nodes_signals.h"
+#include "nodes_channels.h"
+#include "nodes_source.h"
#include "nodes_audio_analysis.h"
#include "nodes_maths.h"
#include "nodes_drawing.h"
@@ -15,7 +19,7 @@ Node_factory::Node_factory(){
//this can be hard coded also
//
- categories["signals"]=vector<Rotor::Node*>();
+ categories["signals"]=vector<Node*>();
add_type("time",new Time(),categories["signals"]);
add_type("track_time",new Track_time(),categories["signals"]);
add_type("at_track_time",new At_track_time(),categories["signals"]);
@@ -23,7 +27,7 @@ Node_factory::Node_factory(){
add_type("signal_output",new Signal_output());
add_type("testcard",new Testcard());
//
- categories["channels"]=vector<Rotor::Node*>();
+ categories["channels"]=vector<Node*>();
add_type("invert",new Invert(),categories["channels"]);
add_type("monochrome",new Monochrome(),categories["channels"]);
add_type("blend",new Blend(),categories["channels"]);
@@ -33,7 +37,7 @@ Node_factory::Node_factory(){
add_type("rgb_levels",new RGB_levels(),categories["channels"]);
add_type("luma_levels",new Luma_levels(),categories["channels"]);
- categories["source"]=vector<Rotor::Node*>();
+ categories["source"]=vector<Node*>();
add_type("signal_colour",new Signal_colour(),categories["source"]);
add_type("signal_greyscale",new Signal_greyscale(),categories["source"]);
add_type("shape",new Shape(),categories["source"]);
@@ -42,26 +46,26 @@ Node_factory::Node_factory(){
add_type("still_image",new Still_image(),categories["source"]);
add_type("video_loader",new Video_loader(),categories["source"]);
- categories["distort"]=vector<Rotor::Node*>();
+ categories["distort"]=vector<Node*>();
add_type("mirror",new Mirror(),categories["distort"]);
add_type("transform",new Transform(),categories["distort"]);
- categories["editing"]=vector<Rotor::Node*>();
+ categories["editing"]=vector<Node*>();
add_type("video_cycler",new Video_cycler(),categories["editing"]);
add_type("video_output",new Video_output(),categories["editing"]);
add_type("act_segmenter",new Act_segmenter(),categories["editing"]);
- categories["audio"]=vector<Rotor::Node*>();
+ categories["audio"]=vector<Node*>();
add_type("audio_analysis",new Audio_analysis(),categories["audio"]);
- categories["maths"]=vector<Rotor::Node*>();
+ categories["maths"]=vector<Node*>();
add_type("comparison",new Comparison(),categories["maths"]); //TODO: alias to symbols
add_type("arithmetic",new Arithmetic(),categories["maths"]); //TODO: alias to symbols
add_type("bang",new Is_new_integer(),categories["maths"]);
add_type("on_off",new On_off(),categories["maths"]);
add_type("random",new Random(),categories["maths"]);
- categories["fx"]=vector<Rotor::Node*>();
+ categories["fx"]=vector<Node*>();
add_type("blur",new Blur(),categories["fx"]);
add_type("vhs",new VHS(),categories["fx"]);
add_type("echo_trails",new Echo_trails(),categories["fx"]);
@@ -96,3 +100,243 @@ float Parameter::get(const Time_spec& time){ //gets input and updates variable
}
return value;
}
+bool Node_factory::list_node(const string &t,xmlIO XML){
+ for (auto& type: type_map) {
+ if (type.first==t) {
+ list_node(type.second,XML);
+ return true;
+ }
+ }
+ XML.addValue("error","Node /"+t+"/ not found");
+};
+void Node_factory::list_node(Rotor::Node* type,xmlIO XML,int i=0){
+ XML.addTag("node");
+ XML.addAttribute("node","type",type->type,i);
+ XML.addAttribute("node","inputs",type->duplicate_inputs?"expandable":"fixed",i);
+ XML.addAttribute("node","title",type->title,i);
+ XML.addAttribute("node","description",type->description,i);
+ XML.addAttribute("node","UID",type->UID,i);
+ if (dynamic_cast<Signal_node*> (type)!=nullptr) XML.addAttribute("node","output","signal",i);
+ if (dynamic_cast<Image_node*> (type)!=nullptr) XML.addAttribute("node","output","image",i);
+ XML.pushTag("node",i);
+ //if (type->description!="") {
+ // XML.addTag("description");
+ // XML.setValue("description",type->description,0);
+ //}
+ int j=0;
+ for (auto& input: type->inputs) {
+ XML.addTag("signal_input");
+ XML.addAttribute("signal_input","title",input->title,j);
+ XML.addAttribute("signal_input","description",input->description,j);
+ j++;
+ }
+ j=0;
+ if (dynamic_cast<Image_node*> (type)!=nullptr) {
+ for (auto& input: (dynamic_cast<Image_node*>(type))->image_inputs) {
+ XML.addTag("image_input");
+ XML.addAttribute("image_input","title",input->title,j);
+ XML.addAttribute("image_input","description",input->description,j);
+ j++;
+ }
+ }
+ j=0;
+ for (auto& parameter: type->parameters) {
+ XML.addTag("parameter");
+ XML.addAttribute("parameter","name",parameter.first,j);
+ XML.addAttribute("parameter","type",parameter.second->type,j);
+ XML.addAttribute("parameter","title",parameter.second->title,j);
+ XML.addAttribute("parameter","description",parameter.second->description,j);
+ XML.addAttribute("parameter","value",parameter.second->value,j);
+ XML.addAttribute("parameter","min",parameter.second->min,j);
+ XML.addAttribute("parameter","max",parameter.second->max,j);
+ j++;
+ }
+ j=0;
+ for (auto& attribute: type->attributes) {
+ XML.addTag("attribute");
+ XML.addAttribute("attribute","name",attribute.first,j);
+ XML.addAttribute("attribute","title",attribute.second->title,j);
+ XML.addAttribute("attribute","description",attribute.second->description,j);
+ XML.addAttribute("attribute","value",attribute.second->value,j);
+ if (attribute.second->vals.size()){ //document attribute enumeration
+ XML.addAttribute("attribute","type","enum",j);
+ XML.pushTag("attribute",j);
+ int k=0;
+ for (auto val: attribute.second->vals){
+ XML.addTag("option");
+ XML.addAttribute("option","value",val,k);
+ k++;
+ }
+ XML.popTag();
+ }
+ else XML.addAttribute("attribute","type","string",j);
+ j++;
+ }
+ XML.popTag();
+}
+void Node_factory::list_nodes(xmlIO XML){
+ int i=0;
+ for (auto& type: type_map) {
+ if (type.second->description!="") { //blank description = internal/ testing node
+ list_node(type.second,XML,i);
+ i++;
+ }
+ }
+}
+void Node_factory::list_categories(xmlIO XML){
+ int i=0;
+ for (auto& category: categories) {
+ XML.addTag("category");
+ XML.addAttribute("category","name",category.first,i);
+ XML.pushTag("category",i);
+ int j=0;
+ for (auto& node: category.second){
+ list_node(node,XML,j);
+ j++;
+ }
+ XML.popTag();
+ i++;
+ }
+}
+void Node_factory::list_categories(Json::Value &JSON){
+ JSON["categories"]=Json::arrayValue;
+ for (auto& _category: categories) {
+ Json::Value category;
+ category["name"]=_category.first;
+ category["nodes"]=Json::arrayValue;
+ for (auto& _node: _category.second){
+ Json::Value node;
+ node["type"]=_node->type;
+ node["title"]=_node->title;
+ node["inputs"]=_node->duplicate_inputs?"expandable":"fixed";
+ if (dynamic_cast<Signal_node*> (_node)!=nullptr) node["output"]="signal";
+ if (dynamic_cast<Image_node*> (_node)!=nullptr) node["output"]="image";
+ node["description"]=_node->description;
+ node["UID"]=_node->UID;
+ if (_node->inputs.size()){
+ node["signal_inputs"]=Json::arrayValue;
+ for (auto& input: _node->inputs) {
+ Json::Value signal_input;
+ signal_input["title"]=input->title;
+ signal_input["description"]=input->description;
+ node["signal_inputs"].append(signal_input);
+ }
+ }
+ if (dynamic_cast<Image_node*> (_node)!=nullptr) {
+ if ((dynamic_cast<Image_node*>(_node))->image_inputs.size()){
+ node["image_inputs"]=Json::arrayValue;
+ for (auto& input: (dynamic_cast<Image_node*>(_node))->image_inputs) {
+ Json::Value image_input;
+ image_input["title"]=input->title;
+ image_input["description"]=input->description;
+ node["image_inputs"].append(image_input);
+ }
+ }
+ }
+ if (_node->parameters.size()){
+ node["parameters"]=Json::arrayValue;
+ for (auto& param: _node->parameters) {
+ Json::Value parameter;
+ parameter["name"]=param.first;
+ parameter["type"]=param.second->type;
+ parameter["title"]=param.second->title;
+ parameter["description"]=param.second->description;
+ parameter["value"]=param.second->value;
+ parameter["min"]=param.second->min;
+ parameter["max"]=param.second->max;
+ node["parameters"].append(parameter);
+ }
+ }
+ if (_node->attributes.size()){
+ node["attributes"]=Json::arrayValue;
+ for (auto& attr: _node->attributes) {
+ Json::Value attribute;
+ attribute["name"]=attr.first;
+ attribute["title"]=attr.second->title;
+ attribute["description"]=attr.second->description;
+ attribute["value"]=attr.second->value;
+ if (attr.second->vals.size()){ //document attribute enumeration
+ attribute["type"]="enum";
+ attribute["options"]=Json::arrayValue;
+ for (auto val: attr.second->vals){
+ attribute["options"].append(val);
+ }
+ }
+ else attribute["type"]="string";
+ node["attributes"].append(attribute);
+ }
+ }
+ category["nodes"].append(node);
+ }
+ JSON["categories"].append(category);
+ }
+}
+void Node_factory::list_nodes(Json::Value &JSON){
+ JSON["nodeslist"]=Json::arrayValue;
+ for (auto& type: type_map) {
+ if (type.second->description!="") { //blank description = internal/ testing node
+ Json::Value node;
+ node["type"]=type.first;
+ node["title"]=type.second->title;
+ node["inputs"]=type.second->duplicate_inputs?"expandable":"fixed";
+ if (dynamic_cast<Signal_node*> (type.second)!=nullptr) node["output"]="signal";
+ if (dynamic_cast<Image_node*> (type.second)!=nullptr) node["output"]="image";
+ node["description"]=type.second->description;
+ node["UID"]=type.second->UID;
+ if (type.second->inputs.size()){
+ node["signal_inputs"]=Json::arrayValue;
+ for (auto& input: type.second->inputs) {
+ Json::Value signal_input;
+ signal_input["title"]=input->title;
+ signal_input["description"]=input->description;
+ node["signal_inputs"].append(signal_input);
+ }
+ }
+ if (dynamic_cast<Image_node*> (type.second)!=nullptr) {
+ if ((dynamic_cast<Image_node*>(type.second))->image_inputs.size()){
+ node["image_inputs"]=Json::arrayValue;
+ for (auto& input: (dynamic_cast<Image_node*>(type.second))->image_inputs) {
+ Json::Value image_input;
+ image_input["title"]=input->title;
+ image_input["description"]=input->description;
+ node["image_inputs"].append(image_input);
+ }
+ }
+ }
+ if (type.second->parameters.size()){
+ node["parameters"]=Json::arrayValue;
+ for (auto& param: type.second->parameters) {
+ Json::Value parameter;
+ parameter["name"]=param.first;
+ parameter["type"]=param.second->type;
+ parameter["title"]=param.second->title;
+ parameter["description"]=param.second->description;
+ parameter["value"]=param.second->value;
+ parameter["min"]=param.second->min;
+ parameter["max"]=param.second->max;
+ node["parameters"].append(parameter);
+ }
+ }
+ if (type.second->attributes.size()){
+ node["attributes"]=Json::arrayValue;
+ for (auto& attr: type.second->attributes) {
+ Json::Value attribute;
+ attribute["name"]=attr.first;
+ attribute["title"]=attr.second->title;
+ attribute["description"]=attr.second->description;
+ attribute["value"]=attr.second->value;
+ if (attr.second->vals.size()){ //document attribute enumeration
+ attribute["type"]="enum";
+ attribute["options"]=Json::arrayValue;
+ for (auto val: attr.second->vals){
+ attribute["options"].append(val);
+ }
+ }
+ else attribute["type"]="string";
+ node["attributes"].append(attribute);
+ }
+ }
+ JSON["nodeslist"].append(node);
+ }
+ }
+} \ No newline at end of file
diff --git a/rotord/src/rotor.h b/rotord/src/rotor.h
index ec9a193..0a81b36 100644
--- a/rotord/src/rotor.h
+++ b/rotord/src/rotor.h
@@ -7,6 +7,43 @@ Definitions of base classes and types for rotor rendering graph
-------------------------*/
+/*------------------------
+
+Aims
+
+-realtime, what does this entail?
+
+ patchbay must be working
+
+ incremental graph editing - examine new graph and make alterations
+
+ window manager
+
+ network stream? rtp?
+
+ realtime architecture - maybe a tiny amount of buffering
+
+ framerate limiting
+
+-undefined number of message types - a mssage is a pointer to a struct
+
+-documentation embedded in nodes
+
+-------------------------*/
+
+/*------------------------
+
+Requirements
+
+-stretch a video to fit a segment
+
+ either use a signal as a playhead {seconds|stretched
+
+ or deliver segment information with a signal
+
+-------------------------*/
+
+
#include <unordered_map>
#include <deque>
#include <math.h>
@@ -290,126 +327,6 @@ namespace Rotor {
int channels,bits,samples,rate;
};
//actual nodes-------------------------------------------------
- class Time: public Signal_node {
- public:
- Time(){
- title="Time";
- description="Outputs the time in seconds as a signal";
- UID="432b0d1e-2d09-11e3-a8b9-e3affcfd2b31";
- };
- Time(map<string,string> &settings): Time() {
- base_settings(settings);
- };
- Time* clone(map<string,string> &_settings) { return new Time(_settings);};
- const float output(const Time_spec &time) {
- return time.time;
- }
- };
- class Track_time: public Signal_node {
- public:
- Track_time(){
- title="Track time";
- description="Outputs the fraction of the track as a signal";
- UID="5892933e-2d09-11e3-8f2e-47c1defdb1d7";
- };
- Track_time(map<string,string> &settings): Track_time() {
- base_settings(settings);
- };
- Track_time* clone(map<string,string> &_settings) { return new Track_time(_settings);};
- const float output(const Time_spec &time) {
- return time.time/time.duration;
- }
- };
- class At_track_time: public Signal_node {
- public:
- At_track_time(){
- create_signal_input("signal","Signal Input");
- create_parameter("time","number","Track time to evaluate","Time",0.0f);
- title="@Track time";
- description="Gets input from a different point in the track";
- UID="6a3edb9c-2d09-11e3-975c-df9df6d19f0a";
- };
- At_track_time(map<string,string> &settings): At_track_time() {
- base_settings(settings);
- };
- At_track_time* clone(map<string,string> &_settings) { return new At_track_time(_settings);};
- const float output(const Time_spec &time) {
- Time_spec t=Time_spec(parameters["time"]->value*time.duration,time.framerate,time.duration);
- return inputs[0]->get(t);
- }
- };
- class Signal_output: public Signal_node {
- public:
- Signal_output(){
- create_signal_input("signal","Signal Input");
- title="Signal output";
- description="Outputs a signal to xml for testing";
- UID="74773190-2d09-11e3-ae26-7f2bb9af632c";
- };
- Signal_output(map<string,string> &settings): Signal_output() {
- base_settings(settings);
- };
- Signal_output* clone(map<string,string> &_settings) { return new Signal_output(_settings);};
- bool render(const float duration, const float framerate,string &xml_out);
- const float output(const Time_spec &time) {
- return inputs[0]->get(time);
- }
- };
- class Testcard: public Image_node {
- public:
- Testcard(){
- //internal testing node only
- };
- Testcard(map<string,string> &settings): Testcard() {
- base_settings(settings);
- };
- ~Testcard(){};
- Testcard* clone(map<string,string> &_settings) { return new Testcard(_settings);};
- Image *output(const Frame_spec &frame){
- float hs=(255.0f/frame.h);
- for (int i=0;i<frame.h;i++){
- for (int j=0;j<frame.w;j++){
- image.RGBdata[(i*frame.w+j)*3]=(uint8_t)((int)((i+(frame.time*25.0f)*hs))%255);
- image.RGBdata[((i*frame.w+j)*3)+1]=(uint8_t)((int)((j+(frame.time*100.0f)*hs))%255);
- image.RGBdata[((i*frame.w+j)*3)+2]=(uint8_t)(0);
- //image->Adata[i*frame.w+j]=(uint8_t)255;
- //image->Zdata[i*frame.w+j]=(uint16_t)512; //1.0 in fixed point 8.8 bits
- }
- }
- return &image;
- }
- private:
-
- };
- class Invert: public Image_node {
- public:
- Invert(){
- create_image_input("Image to invert","Image input");
- create_parameter("invert","number","Invert when greater than 0.0","Negative",1.0f,0.0f,1.0f);
- title="Negative";
- description="Inverts the input picture";
- UID="8676c25c-2d09-11e3-80a7-db36c774523c";
- };
- Invert(map<string,string> &settings) :Invert() {
- base_settings(settings);
- };
- ~Invert(){};
- Invert* clone(map<string,string> &_settings) { return new Invert(_settings);};
- Image *output(const Frame_spec &frame){
- Image *in=image_inputs[0]->get(frame);
- if (in) {
- if (parameters["invert"]->value>0.0f){
- for (int i=0;i<in->w*in->h*3;i++) {
- image.RGBdata[i]=255-in->RGBdata[i];
- }
- return &image;
- }
- return in;
- }
- return nullptr;
- }
- private:
- };
#define CYCLER_cut 1
#define CYCLER_mix 2
#define CYCLER_abs 1
@@ -487,439 +404,6 @@ namespace Rotor {
}
Video_cycler* clone(map<string,string> &_settings) { return new Video_cycler(_settings);};
};
- class Signal_colour: public Image_node {
- public:
- Signal_colour(){
- create_signal_input("Selector","Selector input");
- create_attribute("palette","palette list of web colours","Colour palette","000000");
- title="Signal colour";
- description="Cycles through a palette of background colours according to selector signal";
- UID="a2183fe0-2d09-11e3-9a64-538ee2cf40bc";
- };
- Signal_colour(map<string,string> &settings):Signal_colour() {
- base_settings(settings);
- for (int i=0;i<attributes["palette"]->value.size()/6;i++){
- palette.push_back(Colour(attributes["palette"]->value.substr(i*6,6)));
- }
- prevcol=-1;
- };
- ~Signal_colour(){};
- 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?
- 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;
- }
- Signal_colour* clone(map<string,string> &_settings) { return new Signal_colour(_settings);};
- private:
- vector<Rotor::Colour> palette;
- int prevcol;
- };
- class Signal_greyscale: public Image_node {
- //Draws signal bars in greyscale
- public:
- Signal_greyscale(){
- create_signal_input("Signal","Signal input");
- title="Signal greyscale";
- description="Renders signal level as greyscale background";
- UID="ae91b8a0-2d09-11e3-aa7d-8b7f1ef1a439";
- };
- Signal_greyscale(map<string,string> &settings):Signal_greyscale() {
- base_settings(settings);
- prevcol=-1;
- };
- ~Signal_greyscale(){};
- Image *output(const Frame_spec &frame){
- uint8_t col=((uint8_t)(inputs[0]->get((Time_spec)frame)*255.0f));
- if (col!=prevcol){ //how about when starting a new render?
- for (int i=0;i<image.w*image.h*3;i++){
- image.RGBdata[i]=col;
- }
- prevcol=col;
- }
- return &image;
-
- }
- Signal_greyscale* clone(map<string,string> &_settings) { return new Signal_greyscale(_settings);};
- private:
- uint8_t prevcol;
- };
- #define ARITHMETIC_plus 1
- #define ARITHMETIC_minus 2
- #define ARITHMETIC_multiply 3
- #define ARITHMETIC_divide 4
- #define ARITHMETIC_modulo 5
- class Image_arithmetic: public Image_node {
- public:
- Image_arithmetic(){
- create_image_input("image input","Image input");
- create_parameter("value","number","Value or signal for operation","Value",1.0f);
- create_attribute("operator","operator for image","Operator","+",{"+","-","*","/"});
- title="Image arithmetic";
- description="Performs arithmetic on an image with a signal or value";
- UID="bc3b633e-2d09-11e3-86b2-7fbba3d71604";
- };
- Image_arithmetic(map<string,string> &settings):Image_arithmetic() {
- base_settings(settings);
- }
- ~Image_arithmetic(){};
- Image *output(const Frame_spec &frame){
- Image *in=image_inputs[0]->get(frame);
- if (in){
- switch (attributes["operator"]->intVal) {
- case ARITHMETIC_plus:
- image=(*in); //could be poss without copy?
- image+=parameters["value"]->value;
- break;
- case ARITHMETIC_minus:
- image=(*in);
- image-=parameters["value"]->value;
- break;
- case ARITHMETIC_multiply:
- image=(*in);
- image*=parameters["value"]->value;
- break;
- case ARITHMETIC_divide:
- image=(*in);
- image/=parameters["value"]->value;
- break;
- }
- }
- return &image;
- }
- Image_arithmetic* clone(map<string,string> &_settings) { return new Image_arithmetic(_settings);};
- private:
- };
- #define BLEND_blend 1
- #define BLEND_screen 2
- #define BLEND_multiply 3
- #define BLEND_alpha 4
- #define BLEND_wrap 5
- #define BLEND_xor 6
- class Blend: public Image_node {
- public:
- Blend(){
- create_image_input("image input 1","Image input 1");
- create_image_input("image input 2","Image input 2");
- create_parameter("amount","number","amount to blend input 2","Blend amount",0.5f,0.0f,1.0f);
- create_attribute("mode","Blend mode","Blend mode","blend",{"blend","screen","multiply","alpha","wrap","xor"});
- title ="Blend";
- description="Blend images in various modes";
- UID="12ed7af0-2d0a-11e3-ae32-2b44203b93c9";
- };
- Blend(map<string,string> &settings):Blend() {
- base_settings(settings);
- };
- ~Blend(){};
- Blend* clone(map<string,string> &_settings) { return new Blend(_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) {
- image=*(in1);
- switch(attributes["mode"]->intVal){
- case BLEND_screen:
- image+=(*in2);
- break;
- case BLEND_multiply:
- image*=(*in2);
- break;
- case BLEND_xor:
- image^=(*in2);
- break;
- case BLEND_alpha:
- image=image.alpha_blend(*in2);
- break;
- case BLEND_wrap:
- image=image.add_wrap(*in2);
- break;
- case BLEND_blend: //has to be last because of initialser of *in? go figure
-
- image*=(1.0f-parameters["amount"]->value);
- /* //problem here with leak
- //opencv handles not being released
- Image *in=(*in2)*parameters["amount"]->value;
- image+=(*in);
- delete in;
- */
- in=(*in2); //removed allocator
- in*=parameters["amount"]->value;
- image+=in;
- break;
- }
- return &image;
- }
- //if there aren't 2 image inputs connected just return the first
- return in1;
- }
- return nullptr;
- }
- private:
- Image in;
- };
- #define MIRROR_horiz 1
- #define MIRROR_vert 2
- #define MIRROR_horizR 3
- #define MIRROR_vertR 4
- class Mirror: public Image_node {
- public:
- Mirror(){
- create_image_input("image input","Image input");
- create_attribute("mode","Mirror mode","Mirror mode","horiz",{"horiz","vert","horizR","vertR"});
- title="Mirror";
- description="Mirror video across a central axis";
- UID="1fed5b26-2d0a-11e3-9901-4b5ea78a005d";
- };
- Mirror(map<string,string> &settings):Mirror() {
- base_settings(settings);
- };
- ~Mirror(){ };
- Mirror* clone(map<string,string> &_settings) { return new Mirror(_settings);};
- Image *output(const Frame_spec &frame){
- Image *in=image_inputs[0]->get(frame);
- if (in){
- //copy incoming image **writable
- image=(*in);
- //could be more efficient here by only copying once
- switch (attributes["mode"]->intVal) {
- case MIRROR_horiz:
- for (int i=0;i<image.w/2;i++){
- for (int j=0;j<image.h;j++){
- for (int k=0;k<3;k++){
- image.RGBdata[(((j*image.w)+((image.w/2)+i))*3)+k]=image.RGBdata[(((j*image.w)+((image.w/2)-i))*3)+k];
- }
- }
- }
- break;
- case MIRROR_vert:
- for (int i=0;i<image.w;i++){
- for (int j=0;j<image.h/2;j++){
- for (int k=0;k<3;k++){
- image.RGBdata[((((image.h/2+j)*image.w)+i)*3)+k]=image.RGBdata[((((image.h/2-j)*image.w)+i)*3)+k];
- }
- }
- }
- break;
- case MIRROR_horizR:
- for (int i=0;i<image.w/2;i++){
- for (int j=0;j<image.h;j++){
- for (int k=0;k<3;k++){
- image.RGBdata[(((j*image.w)+((image.w/2)-i))*3)+k]=image.RGBdata[(((j*image.w)+((image.w/2)+i))*3)+k];
- }
- }
- }
- break;
- case MIRROR_vertR:
- for (int i=0;i<image.w;i++){
- for (int j=0;j<image.h/2;j++){
- for (int k=0;k<3;k++){
- image.RGBdata[((((image.h/2-j)*image.w)+i)*3)+k]=image.RGBdata[((((image.h/2+j)*image.w)+i)*3)+k];
- }
- }
- }
- break;
- }
- return &image;
- }
- return nullptr;
- }
- private:
- };
- class Monochrome: public Image_node {
- public:
- Monochrome(){
- create_image_input("image input","Image input");
- title="Monochrome";
- description="Render video greyscale";
- UID="2c3cb12e-2d0a-11e3-a46b-a34e44493cef";
- };
- Monochrome(map<string,string> &settings):Monochrome() {
- base_settings(settings);
- };
- ~Monochrome(){
- };
- Monochrome* clone(map<string,string> &_settings) { return new Monochrome(_settings);};
- Image *output(const Frame_spec &frame){
- Image *in=image_inputs[0]->get(frame);
- if (in){
- for (int i=0;i<image.w;i++){
- for (int j=0;j<image.h;j++){
- uint8_t luma=0;
- for (int l=0;l<3;l++) luma+=pixels.mono_weights[l][in->RGBdata[(((j*image.w)+i)*3)+l]];
- for (int k=0;k<3;k++) image.RGBdata[(((j*image.w)+i)*3)+k]=luma;
- }
- }
- return &image;
- }
- return nullptr;
- }
- private:
- };
- class Alpha_merge: public Image_node {
- public:
- Alpha_merge(){
- create_image_input("image input","Image input");
- create_image_input("alpha input","Alpha input");
- title="Alpha merge";
- description="Alpha merge two images";
- UID="3f5e3eee-2d0a-11e3-8679-1374154a9fa8";
- };
- Alpha_merge(map<string,string> &settings):Alpha_merge() {
- base_settings(settings);
- };
- ~Alpha_merge(){};
- Alpha_merge* clone(map<string,string> &_settings) { return new Alpha_merge(_settings);};
- Image *output(const Frame_spec &frame){
- Image *in1=image_inputs[0]->get(frame);
- if (in1){
- //copy incoming image **writable
- Image *in2=image_inputs[1]->get(frame);
- if (in2) {
- image=(*in1);
- image.alpha_merge(*in2);
- return &image;
- }
- //if there aren't 2 image inputs connected just return the first
- return in1;
- }
- return nullptr;
- }
- private:
- };
- class Difference_matte: public Image_node {
- public:
- Difference_matte(){
- create_image_input("image input","Image input");
- create_image_input("background input","Background input");
- 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;
- UID="4db4d2c8-2d0a-11e3-b08b-7fb00f8c562a";
- };
- Difference_matte(map<string,string> &settings):Difference_matte() {
- base_settings(settings);
- };
- ~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"){
- cv::cvtColor(lutmask, image.rgb, CV_GRAY2RGB);
- }
- else image.alpha_from_cv(lutmask);
- return &image;
-
-
-
- }
- //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,filtmask,lutmask;
- cv::Mat hsv1,hsv2;
- uint8_t *LUT;
- };
#define VIDEOFRAMES_frame 1
#define VIDEOFRAMES_blend 2
#define VIDEOTIME_play 1
@@ -930,6 +414,10 @@ namespace Rotor {
class Video_loader: public Image_node {
public:
Video_loader(){
+ create_signal_input("playhead","Playhead");
+ //floating point control of playback time
+ //if signal is connected it overrides normal playback
+ //time_mode dictates whether control is seconds, or duration
create_parameter("speed","number","video playback speed","Speed",1.0f,0.0f,0.0f);
create_parameter("framerate","number","framerate override","Frame rate",0.0f,0.0f,0.0f);
create_attribute("filename","name of video file to load","File name","");
@@ -972,13 +460,28 @@ namespace Rotor {
float clipframerate=(parameters["framerate"]->value==0.0f?player.get_framerate():parameters["framerate"]->value);
float clipspeed=(clipframerate/frame.framerate)*parameters["speed"]->value;
float wanted;
- switch (attributes["frame_mode"]->intVal){
- case VIDEOTIME_play:
- wanted=fmod(frame.time*frame.framerate*clipspeed,(float)player.get_number_frames());
- break;
- case VIDEOTIME_stretch:
- wanted=fmod((frame.time/frame.duration)*((float)player.get_number_frames())*clipspeed,(float)player.get_number_frames());
- break;
+ if (inputs[0]->connection) {
+ //using playhead
+ //should speed affect it?
+ //if you want absolute control then you just want absolute control?
+ switch (attributes["frame_mode"]->intVal){
+ case VIDEOTIME_play:
+ wanted=fmod(inputs[0]->get((Time_spec)frame)*frame.framerate*clipspeed,(float)player.get_number_frames());
+ break;
+ case VIDEOTIME_stretch:
+ wanted=fmod(fmod(inputs[0]->get((Time_spec)frame),1.0f)*((float)player.get_number_frames())*clipspeed,(float)player.get_number_frames());
+ break;
+ }
+ }
+ else {
+ switch (attributes["frame_mode"]->intVal){
+ case VIDEOTIME_play:
+ wanted=fmod(frame.time*frame.framerate*clipspeed,(float)player.get_number_frames());
+ break;
+ case VIDEOTIME_stretch:
+ wanted=fmod((frame.time/frame.duration)*((float)player.get_number_frames())*clipspeed,(float)player.get_number_frames());
+ break;
+ }
}
if (attributes["frame_mode"]->intVal==VIDEOFRAMES_blend){
if (((int)wanted)!=lastframe){
@@ -1158,246 +661,12 @@ namespace Rotor {
}
return NULL;
};
- bool list_node(const string &t,xmlIO XML){
- for (auto& type: type_map) {
- if (type.first==t) {
- list_node(type.second,XML);
- return true;
- }
- }
- XML.addValue("error","Node /"+t+"/ not found");
- };
- void list_node(Rotor::Node* type,xmlIO XML,int i=0){
- XML.addTag("node");
- XML.addAttribute("node","type",type->type,i);
- XML.addAttribute("node","inputs",type->duplicate_inputs?"expandable":"fixed",i);
- XML.addAttribute("node","title",type->title,i);
- XML.addAttribute("node","description",type->description,i);
- XML.addAttribute("node","UID",type->UID,i);
- if (dynamic_cast<Signal_node*> (type)!=nullptr) XML.addAttribute("node","output","signal",i);
- if (dynamic_cast<Image_node*> (type)!=nullptr) XML.addAttribute("node","output","image",i);
- XML.pushTag("node",i);
- //if (type->description!="") {
- // XML.addTag("description");
- // XML.setValue("description",type->description,0);
- //}
- int j=0;
- for (auto& input: type->inputs) {
- XML.addTag("signal_input");
- XML.addAttribute("signal_input","title",input->title,j);
- XML.addAttribute("signal_input","description",input->description,j);
- j++;
- }
- j=0;
- if (dynamic_cast<Image_node*> (type)!=nullptr) {
- for (auto& input: (dynamic_cast<Image_node*>(type))->image_inputs) {
- XML.addTag("image_input");
- XML.addAttribute("image_input","title",input->title,j);
- XML.addAttribute("image_input","description",input->description,j);
- j++;
- }
- }
- j=0;
- for (auto& parameter: type->parameters) {
- XML.addTag("parameter");
- XML.addAttribute("parameter","name",parameter.first,j);
- XML.addAttribute("parameter","type",parameter.second->type,j);
- XML.addAttribute("parameter","title",parameter.second->title,j);
- XML.addAttribute("parameter","description",parameter.second->description,j);
- XML.addAttribute("parameter","value",parameter.second->value,j);
- XML.addAttribute("parameter","min",parameter.second->min,j);
- XML.addAttribute("parameter","max",parameter.second->max,j);
- j++;
- }
- j=0;
- for (auto& attribute: type->attributes) {
- XML.addTag("attribute");
- XML.addAttribute("attribute","name",attribute.first,j);
- XML.addAttribute("attribute","title",attribute.second->title,j);
- XML.addAttribute("attribute","description",attribute.second->description,j);
- XML.addAttribute("attribute","value",attribute.second->value,j);
- if (attribute.second->vals.size()){ //document attribute enumeration
- XML.addAttribute("attribute","type","enum",j);
- XML.pushTag("attribute",j);
- int k=0;
- for (auto val: attribute.second->vals){
- XML.addTag("option");
- XML.addAttribute("option","value",val,k);
- k++;
- }
- XML.popTag();
- }
- else XML.addAttribute("attribute","type","string",j);
- j++;
- }
- XML.popTag();
- }
- void list_nodes(xmlIO XML){
- int i=0;
- for (auto& type: type_map) {
- if (type.second->description!="") { //blank description = internal/ testing node
- list_node(type.second,XML,i);
- i++;
- }
- }
- }
- void list_categories(xmlIO XML){
- int i=0;
- for (auto& category: categories) {
- XML.addTag("category");
- XML.addAttribute("category","name",category.first,i);
- XML.pushTag("category",i);
- int j=0;
- for (auto& node: category.second){
- list_node(node,XML,j);
- j++;
- }
- XML.popTag();
- i++;
- }
- }
- void list_categories(Json::Value &JSON){
- JSON["categories"]=Json::arrayValue;
- for (auto& _category: categories) {
- Json::Value category;
- category["name"]=_category.first;
- category["nodes"]=Json::arrayValue;
- for (auto& _node: _category.second){
- Json::Value node;
- node["type"]=_node->type;
- node["title"]=_node->title;
- node["inputs"]=_node->duplicate_inputs?"expandable":"fixed";
- if (dynamic_cast<Signal_node*> (_node)!=nullptr) node["output"]="signal";
- if (dynamic_cast<Image_node*> (_node)!=nullptr) node["output"]="image";
- node["description"]=_node->description;
- node["UID"]=_node->UID;
- if (_node->inputs.size()){
- node["signal_inputs"]=Json::arrayValue;
- for (auto& input: _node->inputs) {
- Json::Value signal_input;
- signal_input["title"]=input->title;
- signal_input["description"]=input->description;
- node["signal_inputs"].append(signal_input);
- }
- }
- if (dynamic_cast<Image_node*> (_node)!=nullptr) {
- if ((dynamic_cast<Image_node*>(_node))->image_inputs.size()){
- node["image_inputs"]=Json::arrayValue;
- for (auto& input: (dynamic_cast<Image_node*>(_node))->image_inputs) {
- Json::Value image_input;
- image_input["title"]=input->title;
- image_input["description"]=input->description;
- node["image_inputs"].append(image_input);
- }
- }
- }
- if (_node->parameters.size()){
- node["parameters"]=Json::arrayValue;
- for (auto& param: _node->parameters) {
- Json::Value parameter;
- parameter["name"]=param.first;
- parameter["type"]=param.second->type;
- parameter["title"]=param.second->title;
- parameter["description"]=param.second->description;
- parameter["value"]=param.second->value;
- parameter["min"]=param.second->min;
- parameter["max"]=param.second->max;
- node["parameters"].append(parameter);
- }
- }
- if (_node->attributes.size()){
- node["attributes"]=Json::arrayValue;
- for (auto& attr: _node->attributes) {
- Json::Value attribute;
- attribute["name"]=attr.first;
- attribute["title"]=attr.second->title;
- attribute["description"]=attr.second->description;
- attribute["value"]=attr.second->value;
- if (attr.second->vals.size()){ //document attribute enumeration
- attribute["type"]="enum";
- attribute["options"]=Json::arrayValue;
- for (auto val: attr.second->vals){
- attribute["options"].append(val);
- }
- }
- else attribute["type"]="string";
- node["attributes"].append(attribute);
- }
- }
- category["nodes"].append(node);
- }
- JSON["categories"].append(category);
- }
- }
- void list_nodes(Json::Value &JSON){
- JSON["nodeslist"]=Json::arrayValue;
- for (auto& type: type_map) {
- if (type.second->description!="") { //blank description = internal/ testing node
- Json::Value node;
- node["type"]=type.first;
- node["title"]=type.second->title;
- node["inputs"]=type.second->duplicate_inputs?"expandable":"fixed";
- if (dynamic_cast<Signal_node*> (type.second)!=nullptr) node["output"]="signal";
- if (dynamic_cast<Image_node*> (type.second)!=nullptr) node["output"]="image";
- node["description"]=type.second->description;
- node["UID"]=type.second->UID;
- if (type.second->inputs.size()){
- node["signal_inputs"]=Json::arrayValue;
- for (auto& input: type.second->inputs) {
- Json::Value signal_input;
- signal_input["title"]=input->title;
- signal_input["description"]=input->description;
- node["signal_inputs"].append(signal_input);
- }
- }
- if (dynamic_cast<Image_node*> (type.second)!=nullptr) {
- if ((dynamic_cast<Image_node*>(type.second))->image_inputs.size()){
- node["image_inputs"]=Json::arrayValue;
- for (auto& input: (dynamic_cast<Image_node*>(type.second))->image_inputs) {
- Json::Value image_input;
- image_input["title"]=input->title;
- image_input["description"]=input->description;
- node["image_inputs"].append(image_input);
- }
- }
- }
- if (type.second->parameters.size()){
- node["parameters"]=Json::arrayValue;
- for (auto& param: type.second->parameters) {
- Json::Value parameter;
- parameter["name"]=param.first;
- parameter["type"]=param.second->type;
- parameter["title"]=param.second->title;
- parameter["description"]=param.second->description;
- parameter["value"]=param.second->value;
- parameter["min"]=param.second->min;
- parameter["max"]=param.second->max;
- node["parameters"].append(parameter);
- }
- }
- if (type.second->attributes.size()){
- node["attributes"]=Json::arrayValue;
- for (auto& attr: type.second->attributes) {
- Json::Value attribute;
- attribute["name"]=attr.first;
- attribute["title"]=attr.second->title;
- attribute["description"]=attr.second->description;
- attribute["value"]=attr.second->value;
- if (attr.second->vals.size()){ //document attribute enumeration
- attribute["type"]="enum";
- attribute["options"]=Json::arrayValue;
- for (auto val: attr.second->vals){
- attribute["options"].append(val);
- }
- }
- else attribute["type"]="string";
- node["attributes"].append(attribute);
- }
- }
- JSON["nodeslist"].append(node);
- }
- }
- }
+ bool list_node(const string &t,xmlIO XML);
+ void list_node(Rotor::Node* type,xmlIO XML,int i=0);
+ void list_nodes(xmlIO XML);
+ void list_nodes(Json::Value &JSON);
+ void list_categories(xmlIO XML);
+ void list_categories(Json::Value &JSON);
private:
unordered_map<string,Node*> type_map;
unordered_map<string,vector<Rotor::Node*> > categories;