diff --git a/README.md b/README.md index 479784bc099..41fdd709827 100644 --- a/README.md +++ b/README.md @@ -1112,6 +1112,28 @@ SLIC (Simple Linear Iterative Clustering) is a simple adaptation of k-means clus
             
+ + +
             
+ +
             
+ + + +#### Superpixel Mean + +Superpixel algorithms derive meaningful atomic primitives from dense grid images, which can then be used as inputs to other computer vision algorithms. + +One of the simplest such algorithm is mean, averaging the values in a superpixel, leading to a "mosaic-like" look. + +Achanta, Radhakrishna, et al. ["SLIC superpixels compared to state-of-the-art superpixel methods."](https://infoscience.epfl.ch/record/149300/files/SLIC_Superpixels_TR_2.pdf) IEEE transactions on pattern analysis and machine intelligence 34.11 (2012): 2274-2282. + + +
             
+ +
             
+ + ### Hdr diff --git a/src/plugins/opencv/nodes/superpixels/mean.cpp b/src/plugins/opencv/nodes/superpixels/mean.cpp new file mode 100644 index 00000000000..174a5d681d7 --- /dev/null +++ b/src/plugins/opencv/nodes/superpixels/mean.cpp @@ -0,0 +1,139 @@ +#include + +#include + +#include + +#include "frame.h" + +namespace { + +dependency_graph::InAttr a_in; +dependency_graph::InAttr a_superpixels; +dependency_graph::OutAttr a_out; + +float getFloat(const cv::Mat& in, int row, int col, int channel) { + if(in.depth() == CV_32F) + return in.ptr(row, col)[channel]; + return in.ptr(row, col)[channel]; +} + +long getLong(const cv::Mat& in, int row, int col, int channel) { + if(in.depth() == CV_8U) + return in.ptr(row, col)[channel]; + if(in.depth() == CV_8S) + return in.ptr(row, col)[channel]; + if(in.depth() == CV_16U) + return in.ptr(row, col)[channel]; + if(in.depth() == CV_16S) + return in.ptr(row, col)[channel]; + return in.ptr(row, col)[channel]; +} + +void setFloat(cv::Mat& in, int row, int col, int channel, float val) { + if(in.depth() == CV_32F) + in.ptr(row, col)[channel] = val; + else + in.ptr(row, col)[channel] = val; +} + +void setLong(cv::Mat& in, int row, int col, int channel, long val) { + if(in.depth() == CV_8U) + in.ptr(row, col)[channel] = val; + else if(in.depth() == CV_8S) + in.ptr(row, col)[channel] = val; + else if(in.depth() == CV_16U) + in.ptr(row, col)[channel] = val; + else if(in.depth() == CV_16S) + in.ptr(row, col)[channel] = val; + else + in.ptr(row, col)[channel] = val; +} + +dependency_graph::State compute(dependency_graph::Values& data) { + const cv::Mat& in = *data.get(a_in); + const cv::Mat& superpixels = *data.get(a_superpixels); + + if(in.rows != superpixels.rows || in.cols != superpixels.cols) + throw std::runtime_error("Input and superpixel size have to match."); + + if(superpixels.type() != CV_32SC1) + throw std::runtime_error("Only CV_32SC1 type supported on the superpixels input!"); + + // first of all, get the maximum index of the superpixels + int32_t maxIndex = 0; + for(int row=0; row(row, col)); + + cv::Mat out = cv::Mat::zeros(in.rows, in.cols, in.type()); + + if(in.depth() == CV_32F || in.depth() == CV_64F) { + // make the right sized accumulator and norm array + std::vector> vals(in.channels(), std::vector(maxIndex+1, 0.0f)); + std::vector norm(maxIndex+1, 0); + + // and accumulate the values + for(int row=0; row(row, col); + + for(int c=0;c(row, col); + setFloat(out, row, col, c, vals[c][index] / (float)norm[index]); + } + } + + else { + // make the right sized accumulator and norm array + std::vector> vals(in.channels(), std::vector(maxIndex+1, 0.0f)); + std::vector norm(maxIndex+1, 0); + + // and accumulate the values + for(int row=0; row(row, col); + + for(int c=0;c(row, col); + setLong(out, row, col, c, vals[c][index] / norm[index]); + } + } + + data.set(a_out, possumwood::opencv::Frame(out)); + + return dependency_graph::State(); +} + +void init(possumwood::Metadata& meta) { + meta.addAttribute(a_in, "in", possumwood::opencv::Frame(), possumwood::AttrFlags::kVertical); + meta.addAttribute(a_superpixels, "superpixels", possumwood::opencv::Frame(), possumwood::AttrFlags::kVertical); + meta.addAttribute(a_out, "out", possumwood::opencv::Frame(), possumwood::AttrFlags::kVertical); + + meta.addInfluence(a_in, a_out); + meta.addInfluence(a_superpixels, a_out); + + meta.setCompute(compute); +} + +possumwood::NodeImplementation s_impl("opencv/superpixels/mean", init); + +} diff --git a/src/plugins/opencv/nodes/superpixels/slic.cpp b/src/plugins/opencv/nodes/superpixels/slic.cpp index ddc89171532..a4f72d352c8 100644 --- a/src/plugins/opencv/nodes/superpixels/slic.cpp +++ b/src/plugins/opencv/nodes/superpixels/slic.cpp @@ -1,7 +1,5 @@ #include -#include - #include #include diff --git a/toolbars/06_opencv/91_superpixel_mean.png b/toolbars/06_opencv/91_superpixel_mean.png new file mode 100644 index 00000000000..b140383b183 Binary files /dev/null and b/toolbars/06_opencv/91_superpixel_mean.png differ diff --git a/toolbars/06_opencv/91_superpixel_mean.psw b/toolbars/06_opencv/91_superpixel_mean.psw new file mode 100644 index 00000000000..ff9535021a5 --- /dev/null +++ b/toolbars/06_opencv/91_superpixel_mean.psw @@ -0,0 +1,368 @@ +{ + "connections": [ + { + "in_node": "slic_0", + "in_port": "frame", + "out_node": "color_0", + "out_port": "out_frame" + }, + { + "in_node": "network_0", + "in_port": "frame", + "out_node": "concat_0", + "out_port": "out_frame" + }, + { + "in_node": "mean_0", + "in_port": "in", + "out_node": "image_0", + "out_port": "frame" + }, + { + "in_node": "concat_0", + "in_port": "in_frame_1", + "out_node": "image_0", + "out_port": "frame" + }, + { + "in_node": "color_0", + "in_port": "in_frame", + "out_node": "image_0", + "out_port": "frame" + }, + { + "in_node": "concat_0", + "in_port": "in_frame_2", + "out_node": "mean_0", + "out_port": "out" + }, + { + "in_node": "mean_0", + "in_port": "superpixels", + "out_node": "slic_0", + "out_port": "superpixels" + } + ], + "description": "### Superpixel Mean\n\nSuperpixel algorithms derive meaningful atomic primitives from dense grid images, which can then be used as inputs to other computer vision algorithms.\n\nOne of the simplest such algorithm is mean, averaging the values in a superpixel, leading to a \"mosaic-like\" look. \n\nAchanta, Radhakrishna, et al. [\"SLIC superpixels compared to state-of-the-art superpixel methods.\"](https://infoscience.epfl.ch/record/149300/files/SLIC_Superpixels_TR_2.pdf) IEEE transactions on pattern analysis and machine intelligence 34.11 (2012): 2274-2282.", + "nodes": { + "color_0": { + "blind_data": { + "type": "possumwood::NodeData", + "value": { + "x": 317.258239746094, + "y": 483.830322265625 + } + }, + "name": "color", + "ports": { + "mode": "BGR2Lab" + }, + "type": "opencv/color" + }, + "concat_0": { + "blind_data": { + "type": "possumwood::NodeData", + "value": { + "x": 128.066879272461, + "y": 1018.80303955078 + } + }, + "name": "concat", + "ports": { + "mode": "Horizontal", + "separation": 0 + }, + "type": "opencv/concat" + }, + "image_0": { + "blind_data": { + "type": "possumwood::NodeData", + "value": { + "x": 148.766784667969, + "y": 301.283843994141 + } + }, + "name": "image", + "ports": { + "filename": "$EXAMPLES/cae.wisc.edu_images/cameraman.png" + }, + "type": "opencv/capture/image" + }, + "mean_0": { + "blind_data": { + "type": "possumwood::NodeData", + "value": { + "x": 256.573883056641, + "y": 859.926391601562 + } + }, + "name": "mean", + "type": "opencv/superpixels/mean" + }, + "network_0": { + "blind_data": { + "type": "possumwood::NodeData", + "value": { + "x": 180.262115478516, + "y": 1222.83801269531 + } + }, + "connections": [ + { + "in_node": "draw_0", + "in_port": "vertex_data", + "out_node": "background_0", + "out_port": "vertex_data" + }, + { + "in_node": "program_0", + "in_port": "fragment_shader", + "out_node": "fragment_shader_0", + "out_port": "shader" + }, + { + "in_node": "metadata_0", + "in_port": "frame", + "out_node": "input_0", + "out_port": "data" + }, + { + "in_node": "opencv_texture_0", + "in_port": "frame", + "out_node": "input_0", + "out_port": "data" + }, + { + "in_node": "split_vec2u_0", + "in_port": "vec", + "out_node": "metadata_0", + "out_port": "size" + }, + { + "in_node": "viewport_0", + "in_port": "in_uniforms", + "out_node": "opencv_texture_0", + "out_port": "out_uniforms" + }, + { + "in_node": "draw_0", + "in_port": "program", + "out_node": "program_0", + "out_port": "program" + }, + { + "in_node": "unsigned_1", + "in_port": "value", + "out_node": "split_vec2u_0", + "out_port": "x" + }, + { + "in_node": "unsigned_0", + "in_port": "value", + "out_node": "split_vec2u_0", + "out_port": "y" + }, + { + "in_node": "opencv_texture_0", + "in_port": "in_uniforms", + "out_node": "unsigned_0", + "out_port": "out_uniforms" + }, + { + "in_node": "unsigned_0", + "in_port": "in_uniforms", + "out_node": "unsigned_1", + "out_port": "out_uniforms" + }, + { + "in_node": "program_0", + "in_port": "vertex_shader", + "out_node": "vertex_shader_0", + "out_port": "shader" + }, + { + "in_node": "draw_0", + "in_port": "uniforms", + "out_node": "viewport_0", + "out_port": "out_uniforms" + } + ], + "name": "network", + "nodes": { + "background_0": { + "blind_data": { + "type": "possumwood::NodeData", + "value": { + "x": 378.493469238281, + "y": 846.5810546875 + } + }, + "name": "background", + "type": "render/vertex_data/background" + }, + "draw_0": { + "blind_data": { + "type": "possumwood::NodeData", + "value": { + "x": 597.115966796875, + "y": 727.212646484375 + } + }, + "name": "draw", + "type": "render/draw" + }, + "fragment_shader_0": { + "blind_data": { + "type": "possumwood::NodeData", + "value": { + "x": 168.251327514648, + "y": 736.723999023438 + } + }, + "name": "fragment_shader", + "ports": { + "source": "#version 130 \n \nout vec4 color; \n \nin vec2 uv; \n\nuniform sampler2D image;\n\nvoid main() { \n\tif(uv.y < 0.0 || uv.y > 1.0)\n\t\tcolor = vec4(0,0,0,1);\n\telse\n\t\tcolor = vec4(texture(image, vec2(uv.x, 1.0-uv.y))); \n} \n" + }, + "type": "render/fragment_shader" + }, + "input_0": { + "blind_data": { + "type": "possumwood::NodeData", + "value": { + "x": 17.3097133636475, + "y": -133.207290649414 + } + }, + "name": "frame", + "type": "input" + }, + "metadata_0": { + "blind_data": { + "type": "possumwood::NodeData", + "value": { + "x": 165.910888671875, + "y": 117.083778381348 + } + }, + "name": "opencv_metadata", + "type": "opencv/metadata" + }, + "opencv_texture_0": { + "blind_data": { + "type": "possumwood::NodeData", + "value": { + "x": 562.957153320312, + "y": 428.043426513672 + } + }, + "name": "opencv_texture", + "ports": { + "mode": "Linear", + "name": "image" + }, + "type": "render/uniforms/opencv_texture" + }, + "program_0": { + "blind_data": { + "type": "possumwood::NodeData", + "value": { + "x": 374.836456298828, + "y": 625.601623535156 + } + }, + "name": "program", + "type": "render/program" + }, + "split_vec2u_0": { + "blind_data": { + "type": "possumwood::NodeData", + "value": { + "x": 386.837554931641, + "y": 175.306381225586 + } + }, + "name": "split_vec2u", + "type": "maths/split_vec2u" + }, + "unsigned_0": { + "blind_data": { + "type": "possumwood::NodeData", + "value": { + "x": 570.666015625, + "y": 243.660018920898 + } + }, + "name": "unsigned height", + "ports": { + "name": "image_height" + }, + "type": "render/uniforms/unsigned" + }, + "unsigned_1": { + "blind_data": { + "type": "possumwood::NodeData", + "value": { + "x": 567.202209472656, + "y": 58.0921211242676 + } + }, + "name": "unsigned width", + "ports": { + "name": "image_width" + }, + "type": "render/uniforms/unsigned" + }, + "vertex_shader_0": { + "blind_data": { + "type": "possumwood::NodeData", + "value": { + "x": 197.651947021484, + "y": 524.369689941406 + } + }, + "name": "vertex_shader", + "ports": { + "source": "#version 130 \n \nin vec3 P; // position attr from the vbo \n \nuniform mat4 iProjection; // projection matrix \nuniform mat4 iModelView; // modelview matrix \n\nuniform uint image_width;\nuniform uint image_height;\nuniform vec2 iResolution;\n\nout vec2 uv; // uv texturing parameters\n \nvoid main() {\n\t// compute the aspect ratio from image width and height\n\tfloat aspect = float(image_width) / float(image_height);\n\n\t// compute the screen aspect ratio\n\tfloat screen_aspect = iResolution.x / iResolution.y;\n\n\tgl_Position = vec4(P.x, P.y, 0, 1);\n\n\t// UV parameters are just untransformed world-space position\n\tuv = vec2(P/2.0);\n\tuv.y *= aspect / screen_aspect;\n\tuv += 0.5;\n} \n" + }, + "type": "render/vertex_shader" + }, + "viewport_0": { + "blind_data": { + "type": "possumwood::NodeData", + "value": { + "x": 597.060607910156, + "y": 600.486389160156 + } + }, + "name": "viewport", + "type": "render/uniforms/viewport" + } + }, + "type": "network" + }, + "slic_0": { + "blind_data": { + "type": "possumwood::NodeData", + "value": { + "x": 295.862609863281, + "y": 633.991455078125 + } + }, + "name": "slic_superpixels", + "ports": { + "filter": "connected components, final step", + "iterations": 10, + "spatial_bias": 30.0, + "target_pixel_count": 500 + }, + "type": "opencv/superpixels/slic" + } + }, + "scene_config": { + "end_time": 5.0, + "fps": 24.0, + "start_time": 0.0 + }, + "ui_geometry": "AdnQywADAAAAAAAAAAAAAAAAB38AAAQTAAAAAAAAABQAAASlAAACnAAAAAACAAAAB4AAAAAAAAAAGAAAB38AAAQT", + "ui_state": "AAAA/wAAAAD9AAAAAgAAAAAAAAJCAAADbPwCAAAAAfsAAAAKAGcAcgBhAHAAaAEAAAB1AAADbAAAAJgBAAADAAAAAQAAAagAAANs/AIAAAAC+wAAABQAcAByAG8AcABlAHIAdABpAGUAcwEAAAB1AAAB2wAAAHYBAAAD+wAAAAwAZQBkAGkAdABvAHIBAAACUQAAAZAAAACqAQAAAwAAA5QAAANsAAAABAAAAAQAAAAIAAAACPwAAAAA" +} \ No newline at end of file diff --git a/toolbars/06_opencv/91_superpixel_mean_screenshot.png b/toolbars/06_opencv/91_superpixel_mean_screenshot.png new file mode 100644 index 00000000000..ca991adbdec Binary files /dev/null and b/toolbars/06_opencv/91_superpixel_mean_screenshot.png differ