├── .gitignore ├── example-ImageSegmentation ├── example-ImageSegmentation.sln ├── example-ImageSegmentation.vcxproj ├── example-ImageSegmentation.vcxproj.filters ├── example-ImageSegmentation.vcxproj.user ├── icon.rc └── src │ ├── main.cpp │ ├── ofApp.cpp │ └── ofApp.h ├── libs └── egs │ ├── convolve.h │ ├── disjoint-set.h │ ├── filter.h │ ├── image.h │ ├── imconv.h │ ├── imutil.h │ ├── misc.h │ ├── pnmfile.h │ ├── segment-graph.h │ └── segment-image.h └── src ├── ofxImageSegmentation.cpp └── ofxImageSegmentation.h /.gitignore: -------------------------------------------------------------------------------- 1 | docs/ 2 | 3 | *.depend 4 | *.layout 5 | *.mode*v3 6 | *.pbxuser 7 | *.app* 8 | *.DS_* 9 | ._*.* 10 | 11 | .svn/ 12 | obj/ 13 | bin/ 14 | build/ 15 | !data/ 16 | xcuserdata/ 17 | 18 | ipch/ 19 | *.suo 20 | *.opensdf 21 | 22 | *.obj 23 | *.tlog 24 | *.sdf 25 | *.pdb 26 | *.idb 27 | *.pch 28 | 29 | 30 | *~.xml -------------------------------------------------------------------------------- /example-ImageSegmentation/example-ImageSegmentation.sln: -------------------------------------------------------------------------------- 1 | Microsoft Visual Studio Solution File, Format Version 12.00 2 | # Visual Studio 2012 3 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "openframeworksLib", "..\..\..\libs\openFrameworksCompiled\project\vs\openframeworksLib.vcxproj", "{5837595D-ACA9-485C-8E76-729040CE4B0B}" 4 | EndProject 5 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example-ImageSegmentation", "example-ImageSegmentation.vcxproj", "{7FD42DF7-442E-479A-BA76-D0022F99702A}" 6 | EndProject 7 | Global 8 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 9 | Debug|Win32 = Debug|Win32 10 | Release|Win32 = Release|Win32 11 | EndGlobalSection 12 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 13 | {5837595D-ACA9-485C-8E76-729040CE4B0B}.Debug|Win32.ActiveCfg = Debug|Win32 14 | {5837595D-ACA9-485C-8E76-729040CE4B0B}.Debug|Win32.Build.0 = Debug|Win32 15 | {5837595D-ACA9-485C-8E76-729040CE4B0B}.Release|Win32.ActiveCfg = Release|Win32 16 | {5837595D-ACA9-485C-8E76-729040CE4B0B}.Release|Win32.Build.0 = Release|Win32 17 | {7FD42DF7-442E-479A-BA76-D0022F99702A}.Debug|Win32.ActiveCfg = Debug|Win32 18 | {7FD42DF7-442E-479A-BA76-D0022F99702A}.Debug|Win32.Build.0 = Debug|Win32 19 | {7FD42DF7-442E-479A-BA76-D0022F99702A}.Release|Win32.ActiveCfg = Release|Win32 20 | {7FD42DF7-442E-479A-BA76-D0022F99702A}.Release|Win32.Build.0 = Release|Win32 21 | EndGlobalSection 22 | GlobalSection(SolutionProperties) = preSolution 23 | HideSolutionNode = FALSE 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /example-ImageSegmentation/example-ImageSegmentation.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | 14 | {7FD42DF7-442E-479A-BA76-D0022F99702A} 15 | Win32Proj 16 | emptyExample 17 | 18 | 19 | 20 | Application 21 | Unicode 22 | v110 23 | 24 | 25 | Application 26 | Unicode 27 | true 28 | v110 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | bin\ 42 | obj\$(Configuration)\ 43 | $(ProjectName)_debug 44 | true 45 | true 46 | 47 | 48 | bin\ 49 | obj\$(Configuration)\ 50 | false 51 | 52 | 53 | 54 | Disabled 55 | true 56 | EnableFastChecks 57 | %(PreprocessorDefinitions) 58 | MultiThreadedDebugDLL 59 | Level3 60 | EditAndContinue 61 | %(AdditionalIncludeDirectories);src;..\..\..\addons\ofxImageSegmentation\src;..\..\..\addons\ofxImageSegmentation\libs\egs;..\..\..\addons\ofxGui\src 62 | CompileAsCpp 63 | 64 | 65 | true 66 | Console 67 | false 68 | %(AdditionalDependencies) 69 | %(AdditionalLibraryDirectories) 70 | 71 | 72 | 73 | 74 | false 75 | %(PreprocessorDefinitions) 76 | MultiThreadedDLL 77 | Level3 78 | %(AdditionalIncludeDirectories);src;..\..\..\addons\ofxImageSegmentation\src;..\..\..\addons\ofxImageSegmentation\libs\egs;..\..\..\addons\ofxGui\src 79 | CompileAsCpp 80 | 81 | 82 | false 83 | false 84 | Console 85 | true 86 | true 87 | false 88 | %(AdditionalDependencies) 89 | %(AdditionalLibraryDirectories) 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | {5837595d-aca9-485c-8e76-729040ce4b0b} 131 | 132 | 133 | 134 | 135 | /D_DEBUG %(AdditionalOptions) 136 | 137 | 138 | 139 | -------------------------------------------------------------------------------- /example-ImageSegmentation/example-ImageSegmentation.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | src 6 | 7 | 8 | src 9 | 10 | 11 | addons\ofxImageSegmentation\src 12 | 13 | 14 | addons\ofxGui\src 15 | 16 | 17 | addons\ofxGui\src 18 | 19 | 20 | addons\ofxGui\src 21 | 22 | 23 | addons\ofxGui\src 24 | 25 | 26 | addons\ofxGui\src 27 | 28 | 29 | addons\ofxGui\src 30 | 31 | 32 | addons\ofxGui\src 33 | 34 | 35 | addons\ofxGui\src 36 | 37 | 38 | 39 | 40 | {d8376475-7454-4a24-b08a-aac121d3ad6f} 41 | 42 | 43 | {ad862210-66e5-485e-a7e8-7d6b74805e03} 44 | 45 | 46 | {074e14d7-dcb8-4e09-b53f-768f0586ca5b} 47 | 48 | 49 | {2cdcd2dd-870e-482a-ba84-8bcecdbcd00f} 50 | 51 | 52 | {42484516-3968-49c1-bb8c-3ffcf0bb9cf8} 53 | 54 | 55 | {3ac180b0-5bf3-4b03-b858-e93723f8910b} 56 | 57 | 58 | {f5024d85-19ef-499b-8665-0e6d15aed719} 59 | 60 | 61 | {52b68e32-9ae8-49a9-903a-321d9481b17e} 62 | 63 | 64 | 65 | 66 | src 67 | 68 | 69 | addons\ofxImageSegmentation\libs\egs 70 | 71 | 72 | addons\ofxImageSegmentation\libs\egs 73 | 74 | 75 | addons\ofxImageSegmentation\libs\egs 76 | 77 | 78 | addons\ofxImageSegmentation\libs\egs 79 | 80 | 81 | addons\ofxImageSegmentation\libs\egs 82 | 83 | 84 | addons\ofxImageSegmentation\libs\egs 85 | 86 | 87 | addons\ofxImageSegmentation\libs\egs 88 | 89 | 90 | addons\ofxImageSegmentation\libs\egs 91 | 92 | 93 | addons\ofxImageSegmentation\libs\egs 94 | 95 | 96 | addons\ofxImageSegmentation\src 97 | 98 | 99 | addons\ofxGui\src 100 | 101 | 102 | addons\ofxGui\src 103 | 104 | 105 | addons\ofxGui\src 106 | 107 | 108 | addons\ofxGui\src 109 | 110 | 111 | addons\ofxGui\src 112 | 113 | 114 | addons\ofxGui\src 115 | 116 | 117 | addons\ofxGui\src 118 | 119 | 120 | addons\ofxGui\src 121 | 122 | 123 | addons\ofxGui\src 124 | 125 | 126 | addons\ofxImageSegmentation\libs\egs 127 | 128 | 129 | 130 | 131 | 132 | -------------------------------------------------------------------------------- /example-ImageSegmentation/example-ImageSegmentation.vcxproj.user: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $(ProjectDir)/bin 5 | WindowsLocalDebugger 6 | 7 | 8 | $(ProjectDir)/bin 9 | WindowsLocalDebugger 10 | 11 | -------------------------------------------------------------------------------- /example-ImageSegmentation/icon.rc: -------------------------------------------------------------------------------- 1 | // Icon Resource Definition 2 | #define MAIN_ICON 102 3 | 4 | #if defined(_DEBUG) 5 | MAIN_ICON ICON "..\..\..\libs\openFrameworksCompiled\project\vs\icon_debug.ico" 6 | #else 7 | MAIN_ICON ICON "..\..\..\libs\openFrameworksCompiled\project\vs\icon.ico" 8 | #endif 9 | -------------------------------------------------------------------------------- /example-ImageSegmentation/src/main.cpp: -------------------------------------------------------------------------------- 1 | #include "ofMain.h" 2 | #include "ofApp.h" 3 | 4 | //======================================================================== 5 | int main( ){ 6 | ofSetupOpenGL(1280,720,OF_WINDOW); // <-------- setup the GL context 7 | 8 | // this kicks off the running of my app 9 | // can be OF_WINDOW or OF_FULLSCREEN 10 | // pass in width and height too: 11 | ofRunApp(new ofApp()); 12 | 13 | } 14 | -------------------------------------------------------------------------------- /example-ImageSegmentation/src/ofApp.cpp: -------------------------------------------------------------------------------- 1 | #include "ofApp.h" 2 | 3 | //-------------------------------------------------------------- setup(); 4 | void ofApp::setup(){ 5 | 6 | ofSetVerticalSync(true); 7 | ofSetCircleResolution(80); 8 | ofBackground(54, 54, 54); 9 | 10 | gui.setup("SEGMENTATION PARAMETERS"); 11 | gui.add(sigma.setup("SIGMA", segmentation.sigma, 0, 2.0)); 12 | gui.add(k.setup("K", segmentation.k, 0, 500)); 13 | gui.add(min_size.setup("MIN SIZE", segmentation.min, 0, 50)); 14 | 15 | videoInput.initGrabber(320,240); 16 | } 17 | 18 | //-------------------------------------------------------------- update(); 19 | void ofApp::update(){ 20 | 21 | segmentation.sigma = sigma; 22 | segmentation.k = k; 23 | segmentation.min = min_size; 24 | 25 | videoInput.update(); 26 | if(videoInput.isFrameNew()){ 27 | segmentation.segment(videoInput.getPixelsRef()); 28 | segmentedImage.setFromPixels(segmentation.getSegmentedPixels()); 29 | segmentedImage.update(); 30 | } 31 | } 32 | 33 | //-------------------------------------------------------------- draw(); 34 | void ofApp::draw(){ 35 | videoInput.draw(0,0); 36 | if(segmentedImage.isAllocated()){ 37 | segmentedImage.draw(videoInput.getWidth(),0); 38 | //draw all the little masks below 39 | ofImage image; 40 | for(int i = 0; i < segmentation.numSegments; i++){ 41 | image.setFromPixels(segmentation.getSegmentMask(i)); 42 | image.update(); 43 | image.draw(i*160,240,160,120); 44 | } 45 | } 46 | 47 | gui.draw(); 48 | } 49 | 50 | //-------------------------------------------------------------- KeyBoard Events 51 | void ofApp::keyPressed(int key){ 52 | 53 | } 54 | 55 | //-------------------------------------------------------------- 56 | void ofApp::keyReleased(int key){ 57 | 58 | } 59 | -------------------------------------------------------------------------------- /example-ImageSegmentation/src/ofApp.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ofMain.h" 4 | #include "ofxGui.h" 5 | #include "ofxImageSegmentation.h" 6 | 7 | class ofApp : public ofBaseApp{ 8 | 9 | public: 10 | void setup(); 11 | void update(); 12 | void draw(); 13 | 14 | void keyPressed(int key); 15 | void keyReleased(int key); 16 | 17 | ofVideoGrabber videoInput; 18 | ofxImageSegmentation segmentation; 19 | ofImage segmentedImage; 20 | 21 | ofxPanel gui; 22 | ofxSlider sigma; 23 | ofxSlider k; 24 | ofxSlider min_size; 25 | 26 | }; 27 | -------------------------------------------------------------------------------- /libs/egs/convolve.h: -------------------------------------------------------------------------------- 1 | #ifndef CONVOLVE_H 2 | #define CONVOLVE_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include "image.h" 8 | 9 | /* convolve src with mask. dst is flipped! */ 10 | static void convolve_even(image11 *src, image11 *dst, 11 | std::vector &mask) { 12 | int width = src->width(); 13 | int height = src->height(); 14 | int len = mask.size(); 15 | 16 | for (int y = 0; y < height; y++) { 17 | for (int x = 0; x < width; x++) { 18 | float sum = mask[0] * imRef(src, x, y); 19 | for (int i = 1; i < len; i++) { 20 | sum += mask[i] * 21 | (imRef(src, __max(x-i,0), y) + 22 | imRef(src, __min(x+i, width-1), y)); 23 | } 24 | imRef(dst, y, x) = sum; 25 | } 26 | } 27 | } 28 | 29 | /* convolve src with mask. dst is flipped! */ 30 | static void convolve_odd(image11 *src, image11 *dst, 31 | std::vector &mask) { 32 | int width = src->width(); 33 | int height = src->height(); 34 | int len = mask.size(); 35 | 36 | for (int y = 0; y < height; y++) { 37 | for (int x = 0; x < width; x++) { 38 | float sum = mask[0] * imRef(src, x, y); 39 | for (int i = 1; i < len; i++) { 40 | sum += mask[i] * 41 | (imRef(src, __max(x-i,0), y) - 42 | imRef(src, __min(x+i, width-1), y)); 43 | } 44 | imRef(dst, y, x) = sum; 45 | } 46 | } 47 | } 48 | 49 | #endif 50 | -------------------------------------------------------------------------------- /libs/egs/disjoint-set.h: -------------------------------------------------------------------------------- 1 | #ifndef DISJOINT_SET 2 | #define DISJOINT_SET 3 | 4 | // disjoint-set forests using union-by-rank and path compression (sort of). 5 | 6 | typedef struct { 7 | int rank; 8 | int p; 9 | int size; 10 | } uni_elt; 11 | 12 | class universe { 13 | public: 14 | universe(int elements); 15 | ~universe(); 16 | int find(int x); 17 | void join(int x, int y); 18 | int size(int x) const { return elts[x].size; } 19 | int num_sets() const { return num; } 20 | 21 | private: 22 | uni_elt *elts; 23 | int num; 24 | }; 25 | 26 | universe::universe(int elements) { 27 | elts = new uni_elt[elements]; 28 | num = elements; 29 | for (int i = 0; i < elements; i++) { 30 | elts[i].rank = 0; 31 | elts[i].size = 1; 32 | elts[i].p = i; 33 | } 34 | } 35 | 36 | universe::~universe() { 37 | delete [] elts; 38 | } 39 | 40 | int universe::find(int x) { 41 | int y = x; 42 | while (y != elts[y].p) 43 | y = elts[y].p; 44 | elts[x].p = y; 45 | return y; 46 | } 47 | 48 | void universe::join(int x, int y) { 49 | if (elts[x].rank > elts[y].rank) { 50 | elts[y].p = x; 51 | elts[x].size += elts[y].size; 52 | } else { 53 | elts[x].p = y; 54 | elts[y].size += elts[x].size; 55 | if (elts[x].rank == elts[y].rank) 56 | elts[y].rank++; 57 | } 58 | num--; 59 | } 60 | 61 | #endif 62 | -------------------------------------------------------------------------------- /libs/egs/filter.h: -------------------------------------------------------------------------------- 1 | #ifndef FILTER_H 2 | #define FILTER_H 3 | 4 | #include 5 | #include 6 | #include "image.h" 7 | #include "misc.h" 8 | #include "convolve.h" 9 | #include "imconv.h" 10 | 11 | #define WIDTH 4.0 12 | 13 | /* normalize mask so it integrates to one */ 14 | static void normalize(std::vector &mask) { 15 | int len = mask.size(); 16 | int i; 17 | float sum = 0; 18 | for (i = 1; i < len; i++) { 19 | sum += fabs(mask[i]); 20 | } 21 | sum = 2*sum + fabs(mask[0]); 22 | for (i = 0; i < len; i++) { 23 | mask[i] /= sum; 24 | } 25 | } 26 | 27 | /* make filters */ 28 | #define MAKE_FILTER(name, fun) \ 29 | static std::vector make_ ## name (float sigma) { \ 30 | sigma = __max(sigma, 0.01F); \ 31 | int len = (int)ceil(sigma * WIDTH) + 1; \ 32 | std::vector mask(len); \ 33 | for (int i = 0; i < len; i++) { \ 34 | mask[i] = fun; \ 35 | } \ 36 | return mask; \ 37 | } 38 | 39 | MAKE_FILTER(fgauss, exp(-0.5*square(i/sigma))); 40 | 41 | /* convolve image with gaussian filter */ 42 | static image11 *smooth(image11 *src, float sigma) { 43 | std::vector mask = make_fgauss(sigma); 44 | normalize(mask); 45 | 46 | image11 *tmp = new image11(src->height(), src->width(), false); 47 | image11 *dst = new image11(src->width(), src->height(), false); 48 | convolve_even(src, tmp, mask); 49 | convolve_even(tmp, dst, mask); 50 | 51 | delete tmp; 52 | return dst; 53 | } 54 | 55 | /* convolve image with gaussian filter */ 56 | image11 *smooth(image11 *src, float sigma) { 57 | image11 *tmp = imageUCHARtoFLOAT(src); 58 | image11 *dst = smooth(tmp, sigma); 59 | delete tmp; 60 | return dst; 61 | } 62 | 63 | /* compute laplacian */ 64 | static image11 *laplacian(image11 *src) { 65 | int width = src->width(); 66 | int height = src->height(); 67 | image11 *dst = new image11(width, height); 68 | 69 | for (int y = 1; y < height-1; y++) { 70 | for (int x = 1; x < width-1; x++) { 71 | float d2x = imRef(src, x-1, y) + imRef(src, x+1, y) - 72 | 2*imRef(src, x, y); 73 | float d2y = imRef(src, x, y-1) + imRef(src, x, y+1) - 74 | 2*imRef(src, x, y); 75 | imRef(dst, x, y) = d2x + d2y; 76 | } 77 | } 78 | return dst; 79 | } 80 | 81 | #endif 82 | -------------------------------------------------------------------------------- /libs/egs/image.h: -------------------------------------------------------------------------------- 1 | #ifndef IMAGE_H 2 | #define IMAGE_H 3 | 4 | #include 5 | 6 | template 7 | class image11 { 8 | public: 9 | /* create an image */ 10 | image11(const int width, const int height, const bool init = true); 11 | 12 | /* delete an image */ 13 | ~image11(); 14 | 15 | /* init an image */ 16 | void init(const T &val); 17 | 18 | /* copy an image */ 19 | image11 *copy() const; 20 | 21 | /* get the width of an image. */ 22 | int width() const { return w; } 23 | 24 | /* get the height of an image. */ 25 | int height() const { return h; } 26 | 27 | /* image data. */ 28 | T *data; 29 | 30 | /* row pointers. */ 31 | T **access; 32 | 33 | private: 34 | int w, h; 35 | }; 36 | 37 | /* use imRef to access image data. */ 38 | #define imRef(im, x, y) (im->access[y][x]) 39 | 40 | /* use imPtr to get pointer to image data. */ 41 | #define imPtr(im, x, y) &(im->access[y][x]) 42 | 43 | template 44 | image11::image11(const int width, const int height, const bool init) { 45 | w = width; 46 | h = height; 47 | data = new T[w * h]; // allocate space for image data 48 | access = new T*[h]; // allocate space for row pointers 49 | 50 | // initialize row pointers 51 | for (int i = 0; i < h; i++) 52 | access[i] = data + (i * w); 53 | 54 | if (init) 55 | memset(data, 0, w * h * sizeof(T)); 56 | } 57 | 58 | template 59 | image11::~image11() { 60 | delete [] data; 61 | delete [] access; 62 | } 63 | 64 | template 65 | void image11::init(const T &val) { 66 | T *ptr = imPtr(this, 0, 0); 67 | T *end = imPtr(this, w-1, h-1); 68 | while (ptr <= end) 69 | *ptr++ = val; 70 | } 71 | 72 | 73 | template 74 | image11 *image11::copy() const { 75 | image11 *im = new image11(w, h, false); 76 | memcpy(im->data, data, w * h * sizeof(T)); 77 | return im; 78 | } 79 | 80 | #endif 81 | -------------------------------------------------------------------------------- /libs/egs/imconv.h: -------------------------------------------------------------------------------- 1 | /* image conversion */ 2 | 3 | #ifndef CONV_H 4 | #define CONV_H 5 | 6 | #include 7 | #include "image.h" 8 | #include "imutil.h" 9 | #include "misc.h" 10 | 11 | #define RED_WEIGHT 0.299 12 | #define GREEN_WEIGHT 0.587 13 | #define BLUE_WEIGHT 0.114 14 | 15 | static image11 *imageRGBtoGRAY(image11 *input) { 16 | int width = input->width(); 17 | int height = input->height(); 18 | image11 *output = new image11(width, height, false); 19 | 20 | for (int y = 0; y < height; y++) { 21 | for (int x = 0; x < width; x++) { 22 | imRef(output, x, y) = (uchar) 23 | (imRef(input, x, y).r * RED_WEIGHT + 24 | imRef(input, x, y).g * GREEN_WEIGHT + 25 | imRef(input, x, y).b * BLUE_WEIGHT); 26 | } 27 | } 28 | return output; 29 | } 30 | 31 | static image11 *imageGRAYtoRGB(image11 *input) { 32 | int width = input->width(); 33 | int height = input->height(); 34 | image11 *output = new image11(width, height, false); 35 | 36 | for (int y = 0; y < height; y++) { 37 | for (int x = 0; x < width; x++) { 38 | imRef(output, x, y).r = imRef(input, x, y); 39 | imRef(output, x, y).g = imRef(input, x, y); 40 | imRef(output, x, y).b = imRef(input, x, y); 41 | } 42 | } 43 | return output; 44 | } 45 | 46 | static image11 *imageUCHARtoFLOAT(image11 *input) { 47 | int width = input->width(); 48 | int height = input->height(); 49 | image11 *output = new image11(width, height, false); 50 | 51 | for (int y = 0; y < height; y++) { 52 | for (int x = 0; x < width; x++) { 53 | imRef(output, x, y) = imRef(input, x, y); 54 | } 55 | } 56 | return output; 57 | } 58 | 59 | static image11 *imageINTtoFLOAT(image11 *input) { 60 | int width = input->width(); 61 | int height = input->height(); 62 | image11 *output = new image11(width, height, false); 63 | 64 | for (int y = 0; y < height; y++) { 65 | for (int x = 0; x < width; x++) { 66 | imRef(output, x, y) = imRef(input, x, y); 67 | } 68 | } 69 | return output; 70 | } 71 | 72 | static image11 *imageFLOATtoUCHAR(image11 *input, 73 | float min, float max) { 74 | int width = input->width(); 75 | int height = input->height(); 76 | image11 *output = new image11(width, height, false); 77 | 78 | if (max == min) 79 | return output; 80 | 81 | float scale = UCHAR_MAX / (max - min); 82 | for (int y = 0; y < height; y++) { 83 | for (int x = 0; x < width; x++) { 84 | uchar val = (uchar)((imRef(input, x, y) - min) * scale); 85 | imRef(output, x, y) = bound(val, (uchar)0, (uchar)UCHAR_MAX); 86 | } 87 | } 88 | return output; 89 | } 90 | 91 | static image11 *imageFLOATtoUCHAR(image11 *input) { 92 | float min, max; 93 | min_max(input, &min, &max); 94 | return imageFLOATtoUCHAR(input, min, max); 95 | } 96 | 97 | static image11 *imageUCHARtoLONG(image11 *input) { 98 | int width = input->width(); 99 | int height = input->height(); 100 | image11 *output = new image11(width, height, false); 101 | 102 | for (int y = 0; y < height; y++) { 103 | for (int x = 0; x < width; x++) { 104 | imRef(output, x, y) = imRef(input, x, y); 105 | } 106 | } 107 | return output; 108 | } 109 | 110 | static image11 *imageLONGtoUCHAR(image11 *input, long min, long max) { 111 | int width = input->width(); 112 | int height = input->height(); 113 | image11 *output = new image11(width, height, false); 114 | 115 | if (max == min) 116 | return output; 117 | 118 | float scale = UCHAR_MAX / (float)(max - min); 119 | for (int y = 0; y < height; y++) { 120 | for (int x = 0; x < width; x++) { 121 | uchar val = (uchar)((imRef(input, x, y) - min) * scale); 122 | imRef(output, x, y) = bound(val, (uchar)0, (uchar)UCHAR_MAX); 123 | } 124 | } 125 | return output; 126 | } 127 | 128 | static image11 *imageLONGtoUCHAR(image11 *input) { 129 | long min, max; 130 | min_max(input, &min, &max); 131 | return imageLONGtoUCHAR(input, min, max); 132 | } 133 | 134 | static image11 *imageSHORTtoUCHAR(image11 *input, 135 | short min, short max) { 136 | int width = input->width(); 137 | int height = input->height(); 138 | image11 *output = new image11(width, height, false); 139 | 140 | if (max == min) 141 | return output; 142 | 143 | float scale = UCHAR_MAX / (float)(max - min); 144 | for (int y = 0; y < height; y++) { 145 | for (int x = 0; x < width; x++) { 146 | uchar val = (uchar)((imRef(input, x, y) - min) * scale); 147 | imRef(output, x, y) = bound(val, (uchar)0, (uchar)UCHAR_MAX); 148 | } 149 | } 150 | return output; 151 | } 152 | 153 | static image11 *imageSHORTtoUCHAR(image11 *input) { 154 | short min, max; 155 | min_max(input, &min, &max); 156 | return imageSHORTtoUCHAR(input, min, max); 157 | } 158 | 159 | #endif 160 | -------------------------------------------------------------------------------- /libs/egs/imutil.h: -------------------------------------------------------------------------------- 1 | /* some image utilities */ 2 | 3 | #ifndef IMUTIL_H 4 | #define IMUTIL_H 5 | 6 | #include "image.h" 7 | #include "misc.h" 8 | 9 | /* compute minimum and maximum value in an image */ 10 | template 11 | void min_max(image11 *im, T *ret_min, T *ret_max) { 12 | int width = im->width(); 13 | int height = im->height(); 14 | 15 | T min = imRef(im, 0, 0); 16 | T max = imRef(im, 0, 0); 17 | for (int y = 0; y < height; y++) { 18 | for (int x = 0; x < width; x++) { 19 | T val = imRef(im, x, y); 20 | if (min > val) 21 | min = val; 22 | if (max < val) 23 | max = val; 24 | } 25 | } 26 | 27 | *ret_min = min; 28 | *ret_max = max; 29 | } 30 | 31 | /* threshold image */ 32 | template 33 | image11 *threshold(image11 *src, int t) { 34 | int width = src->width(); 35 | int height = src->height(); 36 | image11 *dst = new image11(width, height); 37 | 38 | for (int y = 0; y < height; y++) { 39 | for (int x = 0; x < width; x++) { 40 | imRef(dst, x, y) = (imRef(src, x, y) >= t); 41 | } 42 | } 43 | 44 | return dst; 45 | } 46 | 47 | #endif 48 | -------------------------------------------------------------------------------- /libs/egs/misc.h: -------------------------------------------------------------------------------- 1 | /* random stuff */ 2 | 3 | #ifndef MISC_H 4 | #define MISC_H 5 | 6 | #include 7 | 8 | #ifndef M_PI 9 | #define M_PI 3.141592653589793 10 | #endif 11 | 12 | typedef unsigned char uchar; 13 | 14 | typedef struct { uchar r, g, b; } rgb; 15 | 16 | inline bool operator==(const rgb &a, const rgb &b) { 17 | return ((a.r == b.r) && (a.g == b.g) && (a.b == b.b)); 18 | } 19 | 20 | template 21 | inline T abs(const T &x) { return (x > 0 ? x : -x); }; 22 | 23 | template 24 | inline int sign(const T &x) { return (x >= 0 ? 1 : -1); }; 25 | 26 | template 27 | inline T square(const T &x) { return x*x; }; 28 | 29 | template 30 | inline T bound(const T &x, const T &min, const T &max) { 31 | return (x < min ? min : (x > max ? max : x)); 32 | } 33 | 34 | template 35 | inline bool check_bound(const T &x, const T&min, const T &max) { 36 | return ((x < min) || (x > max)); 37 | } 38 | 39 | inline int vlib_round(float x) { return (int)(x + 0.5F); } 40 | 41 | inline int vlib_round(double x) { return (int)(x + 0.5); } 42 | 43 | inline double gaussian(double val, double sigma) { 44 | return exp(-square(val/sigma)/2)/(sqrt(2*M_PI)*sigma); 45 | } 46 | 47 | #endif 48 | -------------------------------------------------------------------------------- /libs/egs/pnmfile.h: -------------------------------------------------------------------------------- 1 | /* basic image I/O */ 2 | 3 | #ifndef PNM_FILE_H 4 | #define PNM_FILE_H 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "image.h" 11 | #include "misc.h" 12 | //#include "cv.h" 13 | //#include "highgui.h" 14 | #include "ofMain.h" 15 | 16 | #define BUF_SIZE 256 17 | 18 | class pnm_error { }; 19 | 20 | static void read_packed(unsigned char *data, int size, std::ifstream &f) { 21 | unsigned char c = 0; 22 | 23 | int bitshift = -1; 24 | for (int pos = 0; pos < size; pos++) { 25 | if (bitshift == -1) { 26 | c = f.get(); 27 | bitshift = 7; 28 | } 29 | data[pos] = (c >> bitshift) & 1; 30 | bitshift--; 31 | } 32 | } 33 | 34 | static void write_packed(unsigned char *data, int size, std::ofstream &f) { 35 | unsigned char c = 0; 36 | 37 | int bitshift = 7; 38 | for (int pos = 0; pos < size; pos++) { 39 | c = c | (data[pos] << bitshift); 40 | bitshift--; 41 | if ((bitshift == -1) || (pos == size-1)) { 42 | f.put(c); 43 | bitshift = 7; 44 | c = 0; 45 | } 46 | } 47 | } 48 | 49 | /* read PNM field, skipping comments */ 50 | static void pnm_read(std::ifstream &file, char *buf) { 51 | char doc[BUF_SIZE]; 52 | char c; 53 | 54 | file >> c; 55 | while (c == '#') { 56 | file.getline(doc, BUF_SIZE); 57 | file >> c; 58 | } 59 | file.putback(c); 60 | 61 | file.width(BUF_SIZE); 62 | file >> buf; 63 | file.ignore(); 64 | } 65 | 66 | static image11 *loadPBM(const char *name) { 67 | char buf[BUF_SIZE]; 68 | 69 | /* read header */ 70 | std::ifstream file(name, std::ios::in | std::ios::binary); 71 | pnm_read(file, buf); 72 | if (strncmp(buf, "P4", 2)) 73 | throw pnm_error(); 74 | 75 | pnm_read(file, buf); 76 | int width = atoi(buf); 77 | pnm_read(file, buf); 78 | int height = atoi(buf); 79 | 80 | /* read data */ 81 | image11 *im = new image11(width, height); 82 | for (int i = 0; i < height; i++) 83 | read_packed(imPtr(im, 0, i), width, file); 84 | 85 | return im; 86 | } 87 | 88 | static void savePBM(image11 *im, const char *name) { 89 | int width = im->width(); 90 | int height = im->height(); 91 | std::ofstream file(name, std::ios::out | std::ios::binary); 92 | 93 | file << "P4\n" << width << " " << height << "\n"; 94 | for (int i = 0; i < height; i++) 95 | write_packed(imPtr(im, 0, i), width, file); 96 | } 97 | 98 | static image11 *loadPGM(const char *name) { 99 | char buf[BUF_SIZE]; 100 | 101 | /* read header */ 102 | std::ifstream file(name, std::ios::in | std::ios::binary); 103 | pnm_read(file, buf); 104 | if (strncmp(buf, "P5", 2)) 105 | throw pnm_error(); 106 | 107 | pnm_read(file, buf); 108 | int width = atoi(buf); 109 | pnm_read(file, buf); 110 | int height = atoi(buf); 111 | 112 | pnm_read(file, buf); 113 | if (atoi(buf) > UCHAR_MAX) 114 | throw pnm_error(); 115 | 116 | /* read data */ 117 | image11 *im = new image11(width, height); 118 | file.read((char *)imPtr(im, 0, 0), width * height * sizeof(uchar)); 119 | 120 | return im; 121 | } 122 | 123 | static void savePGM(image11 *im, const char *name) { 124 | int width = im->width(); 125 | int height = im->height(); 126 | std::ofstream file(name, std::ios::out | std::ios::binary); 127 | 128 | file << "P5\n" << width << " " << height << "\n" << UCHAR_MAX << "\n"; 129 | file.write((char *)imPtr(im, 0, 0), width * height * sizeof(uchar)); 130 | } 131 | 132 | static image11 *loadPPM(const char *name) { 133 | char buf[BUF_SIZE], doc[BUF_SIZE]; 134 | 135 | /* read header */ 136 | std::ifstream file(name, std::ios::in | std::ios::binary); 137 | pnm_read(file, buf); 138 | if (strncmp(buf, "P6", 2)) 139 | throw pnm_error(); 140 | 141 | pnm_read(file, buf); 142 | int width = atoi(buf); 143 | pnm_read(file, buf); 144 | int height = atoi(buf); 145 | 146 | pnm_read(file, buf); 147 | if (atoi(buf) > UCHAR_MAX) 148 | throw pnm_error(); 149 | 150 | /* read data */ 151 | image11 *im = new image11(width, height); 152 | file.read((char *)imPtr(im, 0, 0), width * height * sizeof(rgb)); 153 | 154 | return im; 155 | } 156 | 157 | static void savePPM(image11 *im, const char *name) { 158 | int width = im->width(); 159 | int height = im->height(); 160 | std::ofstream file(name, std::ios::out | std::ios::binary); 161 | 162 | file << "P6\n" << width << " " << height << "\n" << UCHAR_MAX << "\n"; 163 | file.write((char *)imPtr(im, 0, 0), width * height * sizeof(rgb)); 164 | } 165 | 166 | template 167 | void load_image(image11 **im, const char *name) { 168 | char buf[BUF_SIZE]; 169 | 170 | /* read header */ 171 | std::ifstream file(name, std::ios::in | std::ios::binary); 172 | pnm_read(file, buf); 173 | if (strncmp(buf, "VLIB", 9)) 174 | throw pnm_error(); 175 | 176 | pnm_read(file, buf); 177 | int width = atoi(buf); 178 | pnm_read(file, buf); 179 | int height = atoi(buf); 180 | 181 | /* read data */ 182 | *im = new image11(width, height); 183 | file.read((char *)imPtr((*im), 0, 0), width * height * sizeof(T)); 184 | } 185 | 186 | template 187 | void save_image(image11 *im, const char *name) { 188 | int width = im->width(); 189 | int height = im->height(); 190 | std::ofstream file(name, std::ios::out | std::ios::binary); 191 | 192 | file << "VLIB\n" << width << " " << height << "\n"; 193 | file.write((char *)imPtr(im, 0, 0), width * height * sizeof(T)); 194 | } 195 | 196 | 197 | 198 | static image11 *loadPixels(ofPixels& pixels) { 199 | image11 *im = new image11(pixels.getWidth(), pixels.getHeight()); 200 | memcpy(im->data,pixels.getPixels(),pixels.getWidth()*pixels.getHeight()*pixels.getBytesPerPixel()); 201 | return im; 202 | } 203 | 204 | 205 | #endif 206 | -------------------------------------------------------------------------------- /libs/egs/segment-graph.h: -------------------------------------------------------------------------------- 1 | #ifndef SEGMENT_GRAPH 2 | #define SEGMENT_GRAPH 3 | 4 | #include 5 | #include 6 | #include "disjoint-set.h" 7 | 8 | // threshold function 9 | #define THRESHOLD(size, c) (c/size) 10 | 11 | typedef struct { 12 | float w; 13 | int a, b; 14 | } edge; 15 | 16 | bool operator<(const edge &a, const edge &b) { 17 | return a.w < b.w; 18 | } 19 | 20 | /* 21 | * Segment a graph 22 | * 23 | * Returns a disjoint-set forest representing the segmentation. 24 | * 25 | * num_vertices: number of vertices in graph. 26 | * num_edges: number of edges in graph 27 | * edges: array of edges. 28 | * c: constant for treshold function. 29 | */ 30 | universe *segment_graph(int num_vertices, int num_edges, edge *edges, 31 | float c) { 32 | // sort edges by weight 33 | std::sort(edges, edges + num_edges); 34 | 35 | // make a disjoint-set forest 36 | universe *u = new universe(num_vertices); 37 | 38 | // init thresholds 39 | float *threshold = new float[num_vertices]; 40 | int i; 41 | for ( i = 0; i < num_vertices; i++) 42 | threshold[i] = THRESHOLD(1,c); 43 | 44 | // for each edge, in non-decreasing weight order... 45 | for (i = 0; i < num_edges; i++) { 46 | edge *pedge = &edges[i]; 47 | 48 | // components conected by this edge 49 | int a = u->find(pedge->a); 50 | int b = u->find(pedge->b); 51 | if (a != b) { 52 | if ((pedge->w <= threshold[a]) && 53 | (pedge->w <= threshold[b])) { 54 | u->join(a, b); 55 | a = u->find(a); 56 | threshold[a] = pedge->w + THRESHOLD(u->size(a), c); 57 | } 58 | } 59 | } 60 | 61 | // free up 62 | delete threshold; 63 | return u; 64 | } 65 | 66 | #endif 67 | -------------------------------------------------------------------------------- /libs/egs/segment-image.h: -------------------------------------------------------------------------------- 1 | #ifndef SEGMENT_IMAGE 2 | #define SEGMENT_IMAGE 3 | 4 | #include 5 | #include "image.h" 6 | #include "misc.h" 7 | #include "filter.h" 8 | #include "segment-graph.h" 9 | 10 | // random color 11 | rgb random_rgb(){ 12 | rgb c; 13 | double r; 14 | 15 | c.r = (uchar)rand(); 16 | c.g = (uchar)rand(); 17 | c.b = (uchar)rand(); 18 | 19 | return c; 20 | } 21 | 22 | // dissimilarity measure between pixels 23 | static inline float diff(image11 *r, image11 *g, image11 *b, 24 | int x1, int y1, int x2, int y2) { 25 | return sqrt(square(imRef(r, x1, y1)-imRef(r, x2, y2)) + 26 | square(imRef(g, x1, y1)-imRef(g, x2, y2)) + 27 | square(imRef(b, x1, y1)-imRef(b, x2, y2))); 28 | } 29 | 30 | bool maskbit_sort(vector< pair > a, vector< pair > b){ 31 | return a.size() > b.size(); 32 | } 33 | /* 34 | * Segment an image 35 | * 36 | * Returns a color image representing the segmentation. 37 | * 38 | * im: image to segment. 39 | * sigma: to smooth the image. 40 | * c: constant for treshold function. 41 | * min_size: minimum component size (enforced by post-processing stage). 42 | * num_ccs: number of connected components in the segmentation. 43 | */ 44 | int segment_image(image11 *im, float sigma, float c, int min_size, 45 | image11*& segmentation, image11**& masks) { 46 | int width = im->width(); 47 | int height = im->height(); 48 | 49 | image11 *r = new image11(width, height); 50 | image11 *g = new image11(width, height); 51 | image11 *b = new image11(width, height); 52 | 53 | // smooth each color channel 54 | int x,y,i,j; 55 | for ( y = 0; y < height; y++) { 56 | for ( x = 0; x < width; x++) { 57 | imRef(r, x, y) = imRef(im, x, y).r; 58 | imRef(g, x, y) = imRef(im, x, y).g; 59 | imRef(b, x, y) = imRef(im, x, y).b; 60 | } 61 | } 62 | image11 *smooth_r = smooth(r, sigma); 63 | image11 *smooth_g = smooth(g, sigma); 64 | image11 *smooth_b = smooth(b, sigma); 65 | delete r; 66 | delete g; 67 | delete b; 68 | 69 | // build graph 70 | edge *edges = new edge[width*height*4]; 71 | int num = 0; 72 | for (y = 0; y < height; y++) { 73 | for ( x = 0; x < width; x++) { 74 | if (x < width-1) { 75 | edges[num].a = y * width + x; 76 | edges[num].b = y * width + (x+1); 77 | edges[num].w = diff(smooth_r, smooth_g, smooth_b, x, y, x+1, y); 78 | num++; 79 | } 80 | 81 | if (y < height-1) { 82 | edges[num].a = y * width + x; 83 | edges[num].b = (y+1) * width + x; 84 | edges[num].w = diff(smooth_r, smooth_g, smooth_b, x, y, x, y+1); 85 | num++; 86 | } 87 | 88 | if ((x < width-1) && (y < height-1)) { 89 | edges[num].a = y * width + x; 90 | edges[num].b = (y+1) * width + (x+1); 91 | edges[num].w = diff(smooth_r, smooth_g, smooth_b, x, y, x+1, y+1); 92 | num++; 93 | } 94 | 95 | if ((x < width-1) && (y > 0)) { 96 | edges[num].a = y * width + x; 97 | edges[num].b = (y-1) * width + (x+1); 98 | edges[num].w = diff(smooth_r, smooth_g, smooth_b, x, y, x+1, y-1); 99 | num++; 100 | } 101 | } 102 | } 103 | delete smooth_r; 104 | delete smooth_g; 105 | delete smooth_b; 106 | 107 | // segment 108 | universe *u = segment_graph(width*height, num, edges, c); 109 | 110 | // post process small components 111 | for ( i = 0; i < num; i++) { 112 | int a = u->find(edges[i].a); 113 | int b = u->find(edges[i].b); 114 | if ((a != b) && ((u->size(a) < min_size) || (u->size(b) < min_size))) 115 | u->join(a, b); 116 | } 117 | delete [] edges; 118 | 119 | //pull the graph apart into a data structure 120 | vector< vector< pair > > maskbits_list; 121 | map maskbits_index; 122 | for (y = 0; y < height; y++) { 123 | for ( x = 0; x < width; x++) { 124 | int comp = u->find(y * width + x); 125 | if(maskbits_index.find(comp) == maskbits_index.end()){ 126 | maskbits_index[comp] = maskbits_list.size(); 127 | maskbits_list.push_back( vector>() ); 128 | } 129 | maskbits_list[ maskbits_index[comp] ].push_back(make_pair(x,y)); 130 | } 131 | } 132 | 133 | sort(maskbits_list.begin(), maskbits_list.end(), maskbit_sort); 134 | 135 | //pick random colors for each component 136 | rgb *colors = new rgb[maskbits_list.size()]; 137 | for (i = 0; i < maskbits_list.size(); i++){ 138 | colors[i] = random_rgb(); 139 | } 140 | 141 | //allocate segmentation images 142 | segmentation = new image11(width, height); 143 | masks = new image11*[maskbits_list.size()]; 144 | for(int i = 0; i < maskbits_list.size(); i++){ 145 | masks[i] = new image11(width,height); 146 | } 147 | 148 | //fill pixels in images 149 | for(int i = 0; i < maskbits_list.size(); i++){ 150 | for(int j = 0; j < maskbits_list[i].size(); j++){ 151 | imRef(segmentation, maskbits_list[i][j].first, maskbits_list[i][j].second) = colors[i]; 152 | imRef(masks[i], maskbits_list[i][j].first, maskbits_list[i][j].second) = 255; 153 | } 154 | } 155 | 156 | int num_css = u->num_sets(); 157 | cout << "num comps " << maskbits_list.size() << " num sets " << num_css << endl; 158 | 159 | delete [] colors; 160 | delete u; 161 | 162 | return num_css; 163 | } 164 | 165 | #endif 166 | -------------------------------------------------------------------------------- /src/ofxImageSegmentation.cpp: -------------------------------------------------------------------------------- 1 | #include "ofxImageSegmentation.h" 2 | 3 | #include "image.h" 4 | #include "misc.h" 5 | #include "pnmfile.h" 6 | #include "segment-image.h" 7 | 8 | //-------------------------------------------------------------- 9 | ofxImageSegmentation::ofxImageSegmentation(){ 10 | sigma = 0.5; 11 | k = 1000; 12 | min = 100.; 13 | createMasks = false; 14 | } 15 | 16 | //-------------------------------------------------------------- 17 | ofPixels& ofxImageSegmentation::segment(ofPixels& image){ 18 | 19 | if(!image.isAllocated()){ 20 | ofLogError("ofxImageSegmentation::segment") << "input image must be allocated"; 21 | return segmentedPixels; 22 | } 23 | 24 | if(!segmentedPixels.isAllocated() || 25 | segmentedPixels.getWidth() != image.getWidth() || 26 | segmentedPixels.getHeight() != image.getHeight() || 27 | segmentedPixels.getImageType() != image.getImageType() ) 28 | { 29 | segmentedPixels.allocate(image.getWidth(), image.getHeight(), OF_IMAGE_COLOR); 30 | segmentedMasks.clear(); 31 | } 32 | 33 | image11 *input = loadPixels(image); 34 | image11 *seg; 35 | image11 **masks; 36 | numSegments = segment_image(input, sigma, k, min, seg, masks); 37 | memcpy(segmentedPixels.getPixels(),seg->data,segmentedPixels.getWidth()*segmentedPixels.getHeight()*segmentedPixels.getBytesPerPixel()); 38 | 39 | //calculate segment masks 40 | if(numSegments > 0){ 41 | while(segmentedMasks.size() < numSegments){ 42 | segmentedMasks.push_back(ofPixels()); 43 | segmentedMasks.back().allocate(image.getWidth(), image.getHeight(), OF_IMAGE_GRAYSCALE); 44 | } 45 | int bytesPerMask = segmentedMasks[0].getWidth()*segmentedMasks[0].getHeight()*segmentedMasks[0].getBytesPerPixel(); 46 | for(int i = 0; i < numSegments; i++){ 47 | memcpy(segmentedMasks[i].getPixels(),masks[i]->data,bytesPerMask); 48 | } 49 | } 50 | 51 | //This is really slow to do, find a way to preserve memory 52 | delete input; 53 | delete seg; 54 | for(int i = 0; i < numSegments; i++){ 55 | delete masks[i]; 56 | } 57 | delete [] masks; 58 | 59 | return segmentedPixels; 60 | } 61 | 62 | //-------------------------------------------------------------- 63 | ofPixels& ofxImageSegmentation::getSegmentMask(int segment){ 64 | if(segment >= numSegments){ 65 | ofLogError("ofxImageSegmentation::getSegmentMask") << "segment out of range, max " << numSegments; 66 | } 67 | return segmentedMasks[segment]; 68 | } 69 | 70 | //-------------------------------------------------------------- 71 | ofPixels& ofxImageSegmentation::getSegmentedPixels(){ 72 | return segmentedPixels; 73 | } 74 | 75 | -------------------------------------------------------------------------------- /src/ofxImageSegmentation.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ofMain.h" 4 | 5 | class ofxImageSegmentation { 6 | public: 7 | ofxImageSegmentation(); 8 | 9 | float sigma; 10 | float k; 11 | int min; 12 | bool createMasks; 13 | 14 | ofPixels& segment(ofPixels& image); 15 | ofPixels& getSegmentedPixels(); 16 | ofPixels& getSegmentMask(int segment); 17 | int numSegments; 18 | 19 | protected: 20 | ofPixels segmentedPixels; 21 | vector segmentedMasks; 22 | }; 23 | --------------------------------------------------------------------------------