├── .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 |
--------------------------------------------------------------------------------