diff options
| -rw-r--r-- | rotord/src/graph.cpp | 5 | ||||
| -rw-r--r-- | rotord/src/graph.h | 1 | ||||
| -rw-r--r-- | rotord/src/nodes_filters.h | 251 | ||||
| -rw-r--r-- | rotord/src/nodes_maths.h | 45 | ||||
| -rw-r--r-- | rotord/src/nodes_transform.h | 68 | ||||
| -rw-r--r-- | rotord/src/rendercontext.cpp | 45 | ||||
| -rw-r--r-- | rotord/src/rotor.cpp | 260 | ||||
| -rw-r--r-- | rotord/src/rotor.h | 869 |
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 ℑ - } - 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 ℑ - } - 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 ℑ - } - 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 ℑ + } + 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 ℑ - } - 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 ℑ - } - 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 ℑ - } - 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 ℑ - - } - 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_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 ℑ - } - //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 ℑ - } - 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 ℑ - } - 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 ℑ - } - //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 ℑ - - - - } - //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; |
