├── tryout ├── hough_circle │ ├── README.md │ ├── img │ │ └── United_States_money_coins.jpg │ ├── hough_circle.h │ ├── Makefile │ ├── hough.h │ ├── hough_circle.cpp │ ├── hough.cpp │ └── main.cpp └── README.TXT ├── img └── russell-crowe-robin-hood-arrow.jpg ├── README.md ├── Makefile ├── hough.h ├── hough.cpp └── main.cpp /tryout/hough_circle/README.md: -------------------------------------------------------------------------------- 1 | hough 2 | ===== 3 | 4 | Linear Hough Transformation in C++ 5 | -------------------------------------------------------------------------------- /img/russell-crowe-robin-hood-arrow.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brunokeymolen/hough/HEAD/img/russell-crowe-robin-hood-arrow.jpg -------------------------------------------------------------------------------- /tryout/hough_circle/img/United_States_money_coins.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brunokeymolen/hough/HEAD/tryout/hough_circle/img/United_States_money_coins.jpg -------------------------------------------------------------------------------- /tryout/README.TXT: -------------------------------------------------------------------------------- 1 | hough_circle is just some try-out code. 2 | Somebody asked me if it was possible to do circles and this hack is used to show how that may work. 3 | Use it as a kind of guideline, dont expect it to be even near perfect. 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | hough 2 | ===== 3 | 4 | Linear Hough Transformation in C++ 5 | 6 | 7 | 8 | 9 | Docker build environment: 10 | https://github.com/brunokeymolen/devops/tree/master/docker-images/hough-transform 11 | 12 | 13 | (C) Bruno Keymolen, bruno.keymolen@gmail.com 14 | -------------------------------------------------------------------------------- /tryout/hough_circle/hough_circle.h: -------------------------------------------------------------------------------- 1 | /* 2 | * hough_circle.h 3 | * 4 | * Created on: May 4, 2013 5 | * Author: bruno 6 | */ 7 | 8 | #ifndef HOUGH_CIRCLE_H_ 9 | #define HOUGH_CIRCLE_H_ 10 | 11 | #include 12 | 13 | namespace keymolen { 14 | 15 | class HoughCircle { 16 | public: 17 | HoughCircle(); 18 | virtual ~HoughCircle(); 19 | public: 20 | //img_data: 8 bit edge image ( >250 is edge) 21 | int Transform(unsigned char* img_data, int w, int h, int r); 22 | //threshold: number of pixels in a circle; result[(x, y), r] 23 | int GetCircles(int threshold, std::vector< std::pair< std::pair, int> >& result ); 24 | const unsigned int* GetAccu(int *w, int *h); 25 | private: 26 | unsigned int* _accu; 27 | int _accu_w; 28 | int _accu_h; 29 | int _img_w; 30 | int _img_h; 31 | int _r; 32 | 33 | }; 34 | 35 | } 36 | 37 | #endif /* HOUGH_CIRCLE_H_ */ 38 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # (C)2013, Bruno Keymolen 2 | # http://www.keymolen.com 3 | # http://www.keymolen.com/2013/05/hough-transformation-c-implementation.html 4 | CXX=g++ 5 | CC=gcc 6 | OPTFLAGS=-g3 -ggdb -O0 7 | CXXFLAGS=-std=c++11 -Wall -I. -I/usr/local/include $(OPTFLAGS) 8 | CFLAGS=-Wall $(OPTFLAGS) 9 | LDFLAGS= -L/usr/local/lib $(OPTFLAGS) 10 | 11 | #opencv 12 | CXXFLAGS+=`pkg-config opencv --cflags` 13 | LDFLAGS+=`pkg-config opencv --libs` 14 | 15 | SRC = main.o hough.o 16 | 17 | all: hough 18 | 19 | hough: $(SRC) $(MODULES) 20 | $(CXX) $(MODULES) $(SRC) $(LDFLAGS) -o hough 21 | 22 | %.o: %.c %.h 23 | $(CC) $(CFLAGS) -c -o $@ $< 24 | 25 | %.o: %.c 26 | $(CC) $(CFLAGS) -c -o $@ $< 27 | 28 | %.o: %.cpp %.h 29 | $(CXX) $(CXXFLAGS) -c -o $@ $< 30 | 31 | %.o: %.cpp 32 | $(CXX) $(CXXFLAGS) -c -o $@ $< 33 | 34 | clean: 35 | rm -f *.o hough 36 | 37 | PREFIX ?= /usr 38 | 39 | install: all 40 | install -d $(PREFIX)/bin 41 | install hough $(PREFIX)/bin 42 | 43 | dependencies: 44 | sudo apt install libopencv-dev 45 | 46 | 47 | .PHONY: clean all hough install 48 | -------------------------------------------------------------------------------- /tryout/hough_circle/Makefile: -------------------------------------------------------------------------------- 1 | # (C)2013, Bruno Keymolen 2 | # http://www.keymolen.com 3 | # http://www.keymolen.com/2013/05/hough-transformation-c-implementation.html 4 | CXX=g++ 5 | CC=gcc 6 | #OPTFLAGS=-g3 -ggdb -O0 7 | OPTFLAGS=-g0 -O3 8 | CXXFLAGS=-Wall -I. -I/usr/local/include $(OPTFLAGS) 9 | CFLAGS=-Wall $(OPTFLAGS) 10 | LDFLAGS= -L/usr/local/lib $(OPTFLAGS) 11 | 12 | #LDFLAGS+= -L/opt/keymolen/opencv/2.4.8/lib 13 | #CXXFLAGS+= -I/opt/keymolen/opencv/2.4.8/include/ 14 | #LDFLAGS+= -lopencv_highgui -lopencv_core -lopencv_calib3d -lopencv_features2d -lopencv_flann -lopencv_imgproc -lopencv_objdetect -lopencv_ts 15 | 16 | CXXFLAGS+=`pkg-config opencv --cflags` 17 | LDFLAGS+=`pkg-config opencv --libs` 18 | 19 | 20 | SRC = main.o hough.o hough_circle.o 21 | 22 | all: hough 23 | 24 | hough: $(SRC) $(MODULES) 25 | $(CXX) $(MODULES) $(SRC) $(LDFLAGS) -o hough 26 | 27 | %.o: %.c %.h 28 | $(CC) $(CFLAGS) -c -o $@ $< 29 | 30 | %.o: %.c 31 | $(CC) $(CFLAGS) -c -o $@ $< 32 | 33 | %.o: %.cpp %.h 34 | $(CXX) $(CXXFLAGS) -c -o $@ $< 35 | 36 | %.o: %.cpp 37 | $(CXX) $(CXXFLAGS) -c -o $@ $< 38 | 39 | clean: 40 | rm -f *.o hough 41 | 42 | PREFIX ?= /usr 43 | 44 | install: all 45 | install -d $(PREFIX)/bin 46 | install hough $(PREFIX)/bin 47 | 48 | .PHONY: clean all hough install 49 | -------------------------------------------------------------------------------- /hough.h: -------------------------------------------------------------------------------- 1 | // ********************************************************************************** 2 | // 3 | // BSD License. 4 | // This file is part of a Hough Transformation tutorial 5 | // see: http://www.keymolen.com/2013/05/hough-transformation-c-implementation.html 6 | // 7 | // Copyright (c) 2013, Bruno Keymolen, email: bruno.keymolen@gmail.com 8 | // All rights reserved. 9 | // 10 | // Redistribution and use in source and binary forms, with or without modification, 11 | // are permitted provided that the following conditions are met: 12 | // 13 | // Redistributions of source code must retain the above copyright notice, 14 | // this list of conditions and the following disclaimer. 15 | // Redistributions in binary form must reproduce the above copyright notice, this 16 | // list of conditions and the following disclaimer in the documentation and/or other 17 | // materials provided with the distribution. 18 | // Neither the name of "Bruno Keymolen" nor the names of its contributors may be 19 | // used to endorse or promote products derived from this software without specific 20 | // prior written permission. 21 | // 22 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 23 | // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 24 | // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 25 | // IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 26 | // INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 27 | // NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 28 | // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 29 | // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | // POSSIBILITY OF SUCH DAMAGE. 32 | // 33 | // ********************************************************************************** 34 | 35 | #ifndef HOUGH_H_ 36 | #define HOUGH_H_ 37 | 38 | #include 39 | 40 | namespace keymolen { 41 | 42 | class Hough { 43 | public: 44 | Hough(); 45 | virtual ~Hough(); 46 | public: 47 | int Transform(unsigned char* img_data, int w, int h); 48 | std::vector< std::pair< std::pair, std::pair > > GetLines(int threshold); 49 | const unsigned int* GetAccu(int *w, int *h); 50 | private: 51 | unsigned int* _accu; 52 | int _accu_w; 53 | int _accu_h; 54 | int _img_w; 55 | int _img_h; 56 | }; 57 | 58 | } 59 | 60 | #endif /* HOUGH_H_ */ 61 | -------------------------------------------------------------------------------- /tryout/hough_circle/hough.h: -------------------------------------------------------------------------------- 1 | // ********************************************************************************** 2 | // 3 | // BSD License. 4 | // This file is part of a Hough Transformation tutorial 5 | // see: http://www.keymolen.com/2013/05/hough-transformation-c-implementation.html 6 | // 7 | // Copyright (c) 2013, Bruno Keymolen, email: bruno.keymolen@gmail.com 8 | // All rights reserved. 9 | // 10 | // Redistribution and use in source and binary forms, with or without modification, 11 | // are permitted provided that the following conditions are met: 12 | // 13 | // Redistributions of source code must retain the above copyright notice, 14 | // this list of conditions and the following disclaimer. 15 | // Redistributions in binary form must reproduce the above copyright notice, this 16 | // list of conditions and the following disclaimer in the documentation and/or other 17 | // materials provided with the distribution. 18 | // Neither the name of "Bruno Keymolen" nor the names of its contributors may be 19 | // used to endorse or promote products derived from this software without specific 20 | // prior written permission. 21 | // 22 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 23 | // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 24 | // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 25 | // IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 26 | // INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 27 | // NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 28 | // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 29 | // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | // POSSIBILITY OF SUCH DAMAGE. 32 | // 33 | // ********************************************************************************** 34 | 35 | #ifndef HOUGH_H_ 36 | #define HOUGH_H_ 37 | 38 | #include 39 | 40 | namespace keymolen { 41 | 42 | class Hough { 43 | public: 44 | Hough(); 45 | virtual ~Hough(); 46 | public: 47 | int Transform(unsigned char* img_data, int w, int h); 48 | std::vector< std::pair< std::pair, std::pair > > GetLines(int threshold); 49 | const unsigned int* GetAccu(int *w, int *h); 50 | private: 51 | unsigned int* _accu; 52 | int _accu_w; 53 | int _accu_h; 54 | int _img_w; 55 | int _img_h; 56 | }; 57 | 58 | } 59 | 60 | #endif /* HOUGH_H_ */ 61 | -------------------------------------------------------------------------------- /tryout/hough_circle/hough_circle.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * hough_circle.cpp 3 | * 4 | * Created on: May 4, 2013 5 | * Author: bruno 6 | */ 7 | 8 | #include "hough_circle.h" 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #ifndef DEG2RAD 16 | #define DEG2RAD 0.017453293f 17 | #endif 18 | 19 | namespace keymolen { 20 | 21 | HoughCircle::HoughCircle():_accu(0), _accu_w(0), _accu_h(0), _img_w(0), _img_h(0) 22 | { 23 | 24 | } 25 | 26 | HoughCircle::~HoughCircle() { 27 | if(_accu) 28 | free(_accu); 29 | } 30 | 31 | int HoughCircle::Transform(unsigned char* img_data, int w, int h, int r) 32 | { 33 | _r = r; 34 | 35 | _img_w = w; 36 | _img_h = h; 37 | 38 | //Create the accu 39 | _accu_h = h; 40 | _accu_w = w; 41 | 42 | if(_accu) 43 | free(_accu); 44 | _accu = (unsigned int*)calloc(_accu_h * _accu_w, sizeof(unsigned int)); 45 | 46 | for(int y=0;y 250 ) 51 | { 52 | for(int t=1;t<=360;t++) 53 | { 54 | int a = ((double)x - ((double)_r * cos((double)t * DEG2RAD))); 55 | int b = ((double)y - ((double)_r * sin((double)t * DEG2RAD))); 56 | 57 | if( (b>=0 && b<_accu_h) && (a>=0 && a<_accu_w)) 58 | _accu[(b * _accu_w) + a]++; 59 | } 60 | } 61 | } 62 | } 63 | 64 | return 0; 65 | } 66 | 67 | const unsigned int* HoughCircle::GetAccu(int *w, int *h) 68 | { 69 | *w = _accu_w; 70 | *h = _accu_h; 71 | 72 | return _accu; 73 | } 74 | 75 | int HoughCircle::GetCircles(int threshold, std::vector< std::pair< std::pair, int> >& result ) 76 | { 77 | int found = 0; 78 | 79 | if(_accu == 0) 80 | return found; 81 | 82 | for(int b=0;b<_accu_h;b++) 83 | { 84 | for(int a=0;a<_accu_w;a++) 85 | { 86 | if((int)_accu[(b*_accu_w) + a] >= threshold) 87 | { 88 | //Is this point a local maxima (9x9) 89 | int max = _accu[(b*_accu_w) + a]; 90 | for(int ly=-4;ly<=4;ly++) 91 | { 92 | for(int lx=-4;lx<=4;lx++) 93 | { 94 | if( (ly+b>=0 && ly+b<_accu_h) && (lx+a>=0 && lx+a<_accu_w) ) 95 | { 96 | if( (int)_accu[( (b+ly)*_accu_w) + (a+lx)] > max ) 97 | { 98 | max = _accu[( (b+ly)*_accu_w) + (a+lx)]; 99 | ly = lx = 5; 100 | } 101 | } 102 | } 103 | } 104 | if(max > (int)_accu[(b*_accu_w) + a]) 105 | continue; 106 | 107 | result.push_back(std::pair< std::pair, int>(std::pair(a,b), _r)); 108 | found++; 109 | 110 | } 111 | } 112 | } 113 | 114 | std::cout << "result: " << found << " " << threshold << std::endl; 115 | 116 | return found; 117 | } 118 | 119 | } 120 | -------------------------------------------------------------------------------- /hough.cpp: -------------------------------------------------------------------------------- 1 | // ********************************************************************************** 2 | // 3 | // BSD License. 4 | // This file is part of a Hough Transformation tutorial, 5 | // see: http://www.keymolen.com/2013/05/hough-transformation-c-implementation.html 6 | // 7 | // Copyright (c) 2013, Bruno Keymolen, email: bruno.keymolen@gmail.com 8 | // All rights reserved. 9 | // 10 | // Redistribution and use in source and binary forms, with or without modification, 11 | // are permitted provided that the following conditions are met: 12 | // 13 | // Redistributions of source code must retain the above copyright notice, 14 | // this list of conditions and the following disclaimer. 15 | // Redistributions in binary form must reproduce the above copyright notice, this 16 | // list of conditions and the following disclaimer in the documentation and/or other 17 | // materials provided with the distribution. 18 | // Neither the name of "Bruno Keymolen" nor the names of its contributors may be 19 | // used to endorse or promote products derived from this software without specific 20 | // prior written permission. 21 | // 22 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 23 | // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 24 | // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 25 | // IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 26 | // INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 27 | // NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 28 | // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 29 | // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | // POSSIBILITY OF SUCH DAMAGE. 32 | // 33 | // ********************************************************************************** 34 | 35 | #include "hough.h" 36 | #include 37 | #include 38 | #include 39 | #include 40 | 41 | 42 | #define DEG2RAD 0.017453293f 43 | 44 | namespace keymolen { 45 | 46 | Hough::Hough():_accu(0), _accu_w(0), _accu_h(0), _img_w(0), _img_h(0) 47 | { 48 | 49 | } 50 | 51 | Hough::~Hough() { 52 | if(_accu) 53 | free(_accu); 54 | } 55 | 56 | 57 | int Hough::Transform(unsigned char* img_data, int w, int h) 58 | { 59 | _img_w = w; 60 | _img_h = h; 61 | 62 | //Create the accu 63 | double hough_h = ((sqrt(2.0) * (double)(h>w?h:w)) / 2.0); 64 | _accu_h = hough_h * 2.0; // -r -> +r 65 | _accu_w = 180; 66 | 67 | _accu = (unsigned int*)calloc(_accu_h * _accu_w, sizeof(unsigned int)); 68 | 69 | double center_x = w/2; 70 | double center_y = h/2; 71 | 72 | 73 | for(int y=0;y 250 ) 78 | { 79 | for(int t=0;t<180;t++) 80 | { 81 | double r = ( ((double)x - center_x) * cos((double)t * DEG2RAD)) + (((double)y - center_y) * sin((double)t * DEG2RAD)); 82 | _accu[ (int)((round(r + hough_h) * 180.0)) + t]++; 83 | } 84 | } 85 | } 86 | } 87 | 88 | return 0; 89 | } 90 | 91 | std::vector< std::pair< std::pair, std::pair > > Hough::GetLines(int threshold) 92 | { 93 | std::vector< std::pair< std::pair, std::pair > > lines; 94 | 95 | if(_accu == 0) 96 | return lines; 97 | 98 | for(int r=0;r<_accu_h;r++) 99 | { 100 | for(int t=0;t<_accu_w;t++) 101 | { 102 | if((int)_accu[(r*_accu_w) + t] >= threshold) 103 | { 104 | //Is this point a local maxima (9x9) 105 | int max = _accu[(r*_accu_w) + t]; 106 | for(int ly=-4;ly<=4;ly++) 107 | { 108 | for(int lx=-4;lx<=4;lx++) 109 | { 110 | if( (ly+r>=0 && ly+r<_accu_h) && (lx+t>=0 && lx+t<_accu_w) ) 111 | { 112 | if( (int)_accu[( (r+ly)*_accu_w) + (t+lx)] > max ) 113 | { 114 | max = _accu[( (r+ly)*_accu_w) + (t+lx)]; 115 | ly = lx = 5; 116 | } 117 | } 118 | } 119 | } 120 | if(max > (int)_accu[(r*_accu_w) + t]) 121 | continue; 122 | 123 | 124 | int x1, y1, x2, y2; 125 | x1 = y1 = x2 = y2 = 0; 126 | 127 | if(t >= 45 && t <= 135) 128 | { 129 | //y = (r - x cos(t)) / sin(t) 130 | x1 = 0; 131 | y1 = ((double)(r-(_accu_h/2)) - ((x1 - (_img_w/2) ) * cos(t * DEG2RAD))) / sin(t * DEG2RAD) + (_img_h / 2); 132 | x2 = _img_w - 0; 133 | y2 = ((double)(r-(_accu_h/2)) - ((x2 - (_img_w/2) ) * cos(t * DEG2RAD))) / sin(t * DEG2RAD) + (_img_h / 2); 134 | } 135 | else 136 | { 137 | //x = (r - y sin(t)) / cos(t); 138 | y1 = 0; 139 | x1 = ((double)(r-(_accu_h/2)) - ((y1 - (_img_h/2) ) * sin(t * DEG2RAD))) / cos(t * DEG2RAD) + (_img_w / 2); 140 | y2 = _img_h - 0; 141 | x2 = ((double)(r-(_accu_h/2)) - ((y2 - (_img_h/2) ) * sin(t * DEG2RAD))) / cos(t * DEG2RAD) + (_img_w / 2); 142 | } 143 | 144 | lines.push_back(std::pair< std::pair, std::pair >(std::pair(x1,y1), std::pair(x2,y2))); 145 | 146 | } 147 | } 148 | } 149 | 150 | std::cout << "lines: " << lines.size() << " " << threshold << std::endl; 151 | return lines; 152 | } 153 | 154 | const unsigned int* Hough::GetAccu(int *w, int *h) 155 | { 156 | *w = _accu_w; 157 | *h = _accu_h; 158 | 159 | return _accu; 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /tryout/hough_circle/hough.cpp: -------------------------------------------------------------------------------- 1 | // ********************************************************************************** 2 | // 3 | // BSD License. 4 | // This file is part of a Hough Transformation tutorial, 5 | // see: http://www.keymolen.com/2013/05/hough-transformation-c-implementation.html 6 | // 7 | // Copyright (c) 2013, Bruno Keymolen, email: bruno.keymolen@gmail.com 8 | // All rights reserved. 9 | // 10 | // Redistribution and use in source and binary forms, with or without modification, 11 | // are permitted provided that the following conditions are met: 12 | // 13 | // Redistributions of source code must retain the above copyright notice, 14 | // this list of conditions and the following disclaimer. 15 | // Redistributions in binary form must reproduce the above copyright notice, this 16 | // list of conditions and the following disclaimer in the documentation and/or other 17 | // materials provided with the distribution. 18 | // Neither the name of "Bruno Keymolen" nor the names of its contributors may be 19 | // used to endorse or promote products derived from this software without specific 20 | // prior written permission. 21 | // 22 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 23 | // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 24 | // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 25 | // IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 26 | // INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 27 | // NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 28 | // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 29 | // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | // POSSIBILITY OF SUCH DAMAGE. 32 | // 33 | // ********************************************************************************** 34 | 35 | #include "hough.h" 36 | #include 37 | #include 38 | #include 39 | #include 40 | 41 | 42 | #define DEG2RAD 0.017453293f 43 | 44 | namespace keymolen { 45 | 46 | Hough::Hough():_accu(0), _accu_w(0), _accu_h(0), _img_w(0), _img_h(0) 47 | { 48 | 49 | } 50 | 51 | Hough::~Hough() { 52 | if(_accu) 53 | free(_accu); 54 | } 55 | 56 | 57 | int Hough::Transform(unsigned char* img_data, int w, int h) 58 | { 59 | _img_w = w; 60 | _img_h = h; 61 | 62 | //Create the accu 63 | double hough_h = ((sqrt(2.0) * (double)(h>w?h:w)) / 2.0); 64 | _accu_h = hough_h * 2.0; // -r -> +r 65 | _accu_w = 180; 66 | 67 | _accu = (unsigned int*)calloc(_accu_h * _accu_w, sizeof(unsigned int)); 68 | 69 | double center_x = w/2; 70 | double center_y = h/2; 71 | 72 | 73 | for(int y=0;y 250 ) 78 | { 79 | for(int t=0;t<180;t++) 80 | { 81 | double r = ( ((double)x - center_x) * cos((double)t * DEG2RAD)) + (((double)y - center_y) * sin((double)t * DEG2RAD)); 82 | _accu[ (int)((round(r + hough_h) * 180.0)) + t]++; 83 | } 84 | } 85 | } 86 | } 87 | 88 | return 0; 89 | } 90 | 91 | std::vector< std::pair< std::pair, std::pair > > Hough::GetLines(int threshold) 92 | { 93 | std::vector< std::pair< std::pair, std::pair > > lines; 94 | 95 | if(_accu == 0) 96 | return lines; 97 | 98 | for(int r=0;r<_accu_h;r++) 99 | { 100 | for(int t=0;t<_accu_w;t++) 101 | { 102 | if((int)_accu[(r*_accu_w) + t] >= threshold) 103 | { 104 | //Is this point a local maxima (9x9) 105 | int max = _accu[(r*_accu_w) + t]; 106 | for(int ly=-4;ly<=4;ly++) 107 | { 108 | for(int lx=-4;lx<=4;lx++) 109 | { 110 | if( (ly+r>=0 && ly+r<_accu_h) && (lx+t>=0 && lx+t<_accu_w) ) 111 | { 112 | if( (int)_accu[( (r+ly)*_accu_w) + (t+lx)] > max ) 113 | { 114 | max = _accu[( (r+ly)*_accu_w) + (t+lx)]; 115 | ly = lx = 5; 116 | } 117 | } 118 | } 119 | } 120 | if(max > (int)_accu[(r*_accu_w) + t]) 121 | continue; 122 | 123 | 124 | int x1, y1, x2, y2; 125 | x1 = y1 = x2 = y2 = 0; 126 | 127 | if(t >= 45 && t <= 135) 128 | { 129 | //y = (r - x cos(t)) / sin(t) 130 | x1 = 0; 131 | y1 = ((double)(r-(_accu_h/2)) - ((x1 - (_img_w/2) ) * cos(t * DEG2RAD))) / sin(t * DEG2RAD) + (_img_h / 2); 132 | x2 = _img_w - 0; 133 | y2 = ((double)(r-(_accu_h/2)) - ((x2 - (_img_w/2) ) * cos(t * DEG2RAD))) / sin(t * DEG2RAD) + (_img_h / 2); 134 | } 135 | else 136 | { 137 | //x = (r - y sin(t)) / cos(t); 138 | y1 = 0; 139 | x1 = ((double)(r-(_accu_h/2)) - ((y1 - (_img_h/2) ) * sin(t * DEG2RAD))) / cos(t * DEG2RAD) + (_img_w / 2); 140 | y2 = _img_h - 0; 141 | x2 = ((double)(r-(_accu_h/2)) - ((y2 - (_img_h/2) ) * sin(t * DEG2RAD))) / cos(t * DEG2RAD) + (_img_w / 2); 142 | } 143 | 144 | lines.push_back(std::pair< std::pair, std::pair >(std::pair(x1,y1), std::pair(x2,y2))); 145 | 146 | } 147 | } 148 | } 149 | 150 | std::cout << "lines: " << lines.size() << " " << threshold << std::endl; 151 | return lines; 152 | } 153 | 154 | const unsigned int* Hough::GetAccu(int *w, int *h) 155 | { 156 | *w = _accu_w; 157 | *h = _accu_h; 158 | 159 | return _accu; 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | // ********************************************************************************** 2 | // 3 | // BSD License. 4 | // This file is part of a Hough Transformation tutorial, 5 | // see: http://www.keymolen.com/2013/05/hough-transformation-c-implementation.html 6 | // 7 | // Copyright (c) 2013, Bruno Keymolen, email: bruno.keymolen@gmail.com 8 | // All rights reserved. 9 | // 10 | // Redistribution and use in source and binary forms, with or without modification, 11 | // are permitted provided that the following conditions are met: 12 | // 13 | // Redistributions of source code must retain the above copyright notice, 14 | // this list of conditions and the following disclaimer. 15 | // Redistributions in binary form must reproduce the above copyright notice, this 16 | // list of conditions and the following disclaimer in the documentation and/or other 17 | // materials provided with the distribution. 18 | // Neither the name of "Bruno Keymolen" nor the names of its contributors may be 19 | // used to endorse or promote products derived from this software without specific 20 | // prior written permission. 21 | // 22 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 23 | // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 24 | // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 25 | // IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 26 | // INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 27 | // NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 28 | // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 29 | // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | // POSSIBILITY OF SUCH DAMAGE. 32 | // 33 | // ********************************************************************************** 34 | 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | 42 | #include 43 | #include "opencv2/highgui/highgui.hpp" 44 | #include "opencv2/imgproc/imgproc.hpp" 45 | #include "opencv2/core/core.hpp" 46 | 47 | #include "hough.h" 48 | 49 | extern FILE *stdin; 50 | extern FILE *stdout; 51 | extern FILE *stderr; 52 | 53 | std::string img_path; 54 | int threshold = 0; 55 | 56 | const char* CW_IMG_ORIGINAL = "Result"; 57 | const char* CW_IMG_EDGE = "Canny Edge Detection"; 58 | const char* CW_ACCUMULATOR = "Accumulator"; 59 | 60 | void doTransform(std::string, int threshold); 61 | 62 | 63 | void usage(char * s) 64 | { 65 | 66 | fprintf( stderr, "\n"); 67 | fprintf( stderr, "%s -s [-t ] - hough transform. build: %s-%s \n", s, __DATE__, __TIME__); 68 | fprintf( stderr, " s: path image file\n"); 69 | fprintf( stderr, " t: hough threshold\n"); 70 | fprintf( stderr, "\nexample: ./hough -s ./img/russell-crowe-robin-hood-arrow.jpg -t 195\n"); 71 | fprintf( stderr, "\n"); 72 | } 73 | 74 | int main(int argc, char** argv) { 75 | 76 | int c; 77 | while ( ((c = getopt( argc, argv, "s:t:?" ) ) ) != -1 ) 78 | { 79 | switch (c) 80 | { 81 | case 's': 82 | img_path = optarg; 83 | break; 84 | case 't': 85 | threshold = atoi(optarg); 86 | break; 87 | case '?': 88 | default: 89 | usage(argv[0]); 90 | return -1; 91 | } 92 | } 93 | 94 | if(img_path.empty()) 95 | { 96 | usage(argv[0]); 97 | return -1; 98 | } 99 | 100 | cv::namedWindow(CW_IMG_ORIGINAL, cv::WINDOW_AUTOSIZE); 101 | cv::namedWindow(CW_IMG_EDGE, cv::WINDOW_AUTOSIZE); 102 | cv::namedWindow(CW_ACCUMULATOR, cv::WINDOW_AUTOSIZE); 103 | 104 | cvMoveWindow(CW_IMG_ORIGINAL, 10, 10); 105 | cvMoveWindow(CW_IMG_EDGE, 680, 10); 106 | cvMoveWindow(CW_ACCUMULATOR, 1350, 10); 107 | 108 | doTransform(img_path, threshold); 109 | 110 | return 0; 111 | } 112 | 113 | 114 | 115 | void doTransform(std::string file_path, int threshold) 116 | { 117 | cv::Mat img_edge; 118 | cv::Mat img_blur; 119 | 120 | cv::Mat img_ori = cv::imread( file_path, 1 ); 121 | cv::blur( img_ori, img_blur, cv::Size(5,5) ); 122 | cv::Canny(img_blur, img_edge, 100, 150, 3); 123 | 124 | int w = img_edge.cols; 125 | int h = img_edge.rows; 126 | 127 | //Transform 128 | keymolen::Hough hough; 129 | hough.Transform(img_edge.data, w, h); 130 | 131 | 132 | 133 | if(threshold == 0) 134 | threshold = w>h?w/4:h/4; 135 | 136 | while(1) 137 | { 138 | cv::Mat img_res = img_ori.clone(); 139 | 140 | //Search the accumulator 141 | std::vector< std::pair< std::pair, std::pair > > lines = hough.GetLines(threshold); 142 | 143 | //Draw the results 144 | std::vector< std::pair< std::pair, std::pair > >::iterator it; 145 | for(it=lines.begin();it!=lines.end();it++) 146 | { 147 | cv::line(img_res, cv::Point(it->first.first, it->first.second), cv::Point(it->second.first, it->second.second), cv::Scalar( 0, 0, 255), 2, 8); 148 | } 149 | 150 | //Visualize all 151 | int aw, ah, maxa; 152 | aw = ah = maxa = 0; 153 | const unsigned int* accu = hough.GetAccu(&aw, &ah); 154 | 155 | for(int p=0;p<(ah*aw);p++) 156 | { 157 | if((int)accu[p] > maxa) 158 | maxa = accu[p]; 159 | } 160 | double contrast = 1.0; 161 | double coef = 255.0 / (double)maxa * contrast; 162 | 163 | cv::Mat img_accu(ah, aw, CV_8UC3); 164 | for(int p=0;p<(ah*aw);p++) 165 | { 166 | unsigned char c = (double)accu[p] * coef < 255.0 ? (double)accu[p] * coef : 255.0; 167 | img_accu.data[(p*3)+0] = 255; 168 | img_accu.data[(p*3)+1] = 255-c; 169 | img_accu.data[(p*3)+2] = 255-c; 170 | } 171 | 172 | 173 | cv::imshow(CW_IMG_ORIGINAL, img_res); 174 | cv::imshow(CW_IMG_EDGE, img_edge); 175 | cv::imshow(CW_ACCUMULATOR, img_accu); 176 | 177 | char c = cv::waitKey(360000); 178 | if(c == '+') 179 | threshold += 5; 180 | if(c == '-') 181 | threshold -= 5; 182 | if(c == 27) 183 | break; 184 | } 185 | } 186 | 187 | -------------------------------------------------------------------------------- /tryout/hough_circle/main.cpp: -------------------------------------------------------------------------------- 1 | // ********************************************************************************** 2 | // 3 | // BSD License. 4 | // This file is part of a Hough Transformation tutorial, 5 | // see: http://www.keymolen.com/2013/05/hough-transformation-c-implementation.html 6 | // 7 | // Copyright (c) 2013, Bruno Keymolen, email: bruno.keymolen@gmail.com 8 | // All rights reserved. 9 | // 10 | // Redistribution and use in source and binary forms, with or without modification, 11 | // are permitted provided that the following conditions are met: 12 | // 13 | // Redistributions of source code must retain the above copyright notice, 14 | // this list of conditions and the following disclaimer. 15 | // Redistributions in binary form must reproduce the above copyright notice, this 16 | // list of conditions and the following disclaimer in the documentation and/or other 17 | // materials provided with the distribution. 18 | // Neither the name of "Bruno Keymolen" nor the names of its contributors may be 19 | // used to endorse or promote products derived from this software without specific 20 | // prior written permission. 21 | // 22 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 23 | // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 24 | // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 25 | // IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 26 | // INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 27 | // NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 28 | // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 29 | // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | // POSSIBILITY OF SUCH DAMAGE. 32 | // 33 | // ********************************************************************************** 34 | 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | 42 | #include 43 | #include "opencv2/highgui/highgui.hpp" 44 | #include "opencv2/imgproc/imgproc.hpp" 45 | #include "opencv2/core/core.hpp" 46 | 47 | #include "hough.h" 48 | #include "hough_circle.h" 49 | 50 | extern FILE *stdin; 51 | extern FILE *stdout; 52 | extern FILE *stderr; 53 | 54 | std::string img_path; 55 | int threshold = 0; 56 | 57 | const char* CW_IMG_ORIGINAL = "Result"; 58 | const char* CW_IMG_EDGE = "Canny Edge Detection"; 59 | const char* CW_ACCUMULATOR = "Accumulator"; 60 | 61 | void doTransformCircle(std::string file_path, int threshold); 62 | 63 | struct SortCirclesDistance 64 | { 65 | bool operator()( const std::pair< std::pair, int>& a, const std::pair< std::pair, int>& b ) 66 | { 67 | //int dist_a = sqrt( pow(a.first.first, 2) + pow(a.first.second, 2)); 68 | //int dist_b = sqrt( pow(b.first.first, 2) + pow(b.first.second, 2)); 69 | 70 | int d = sqrt( pow(abs(a.first.first - b.first.first), 2) + pow(abs(a.first.second - b.first.second), 2) ); 71 | if(d <= a.second + b.second) 72 | { 73 | //overlap 74 | return a.second > b.second; 75 | } 76 | return false; 77 | 78 | // return dist_a < dist_b; 79 | } 80 | 81 | }; 82 | 83 | 84 | void usage(char * s) 85 | { 86 | 87 | fprintf( stderr, "\n"); 88 | fprintf( stderr, "%s -s [-t ] - hough transform. build: %s-%s \n", s, __DATE__, __TIME__); 89 | fprintf( stderr, " s: path image file\n"); 90 | fprintf( stderr, " t: hough threshold\n"); 91 | fprintf( stderr, "\nexample: ./hough -s ./img/russell-crowe-robin-hood-arrow.jpg -t 195\n"); 92 | fprintf( stderr, "\n"); 93 | } 94 | 95 | int main(int argc, char** argv) { 96 | 97 | int c; 98 | while ( ((c = getopt( argc, argv, "s:t:?" ) ) ) != -1 ) 99 | { 100 | switch (c) 101 | { 102 | case 's': 103 | img_path = optarg; 104 | break; 105 | case 't': 106 | threshold = atoi(optarg); 107 | break; 108 | case '?': 109 | default: 110 | usage(argv[0]); 111 | return -1; 112 | } 113 | } 114 | 115 | if(img_path.empty()) 116 | { 117 | usage(argv[0]); 118 | return -1; 119 | } 120 | 121 | cv::namedWindow(CW_IMG_ORIGINAL, cv::WINDOW_AUTOSIZE); 122 | cv::namedWindow(CW_IMG_EDGE, cv::WINDOW_AUTOSIZE); 123 | cv::namedWindow(CW_ACCUMULATOR, cv::WINDOW_AUTOSIZE); 124 | 125 | cvMoveWindow(CW_IMG_ORIGINAL, 10, 10); 126 | cvMoveWindow(CW_IMG_EDGE, 680, 10); 127 | cvMoveWindow(CW_ACCUMULATOR, 1350, 10); 128 | 129 | doTransformCircle(img_path, threshold); 130 | 131 | return 0; 132 | } 133 | 134 | 135 | void doTransformCircle(std::string file_path, int threshold) 136 | { 137 | cv::Mat img_edge; 138 | cv::Mat img_blur; 139 | 140 | cv::Mat img_ori = cv::imread( file_path, 1 ); 141 | cv::blur( img_ori, img_blur, cv::Size(5,5) ); 142 | cv::Canny(img_blur, img_edge, 175, 200, 3); 143 | 144 | int w = img_edge.cols; 145 | int h = img_edge.rows; 146 | 147 | 148 | //Transform 149 | keymolen::HoughCircle hough; 150 | 151 | std::vector< std::pair< std::pair, int> > circles; 152 | 153 | for(int r=20;r<100/*h/2*/;r=r+5) 154 | { 155 | hough.Transform(img_edge.data, w, h, r); 156 | 157 | std::cout<< r << " / " << h/2; 158 | 159 | if(threshold == 0) 160 | threshold = 0.95 * (2.0 * (double)r * M_PI); 161 | 162 | // threshold = 200; 163 | 164 | //while(1) 165 | { 166 | 167 | //Search the accumulator 168 | hough.GetCircles(threshold, circles); 169 | 170 | //Draw the results 171 | 172 | int aw, ah, maxa; 173 | aw = ah = maxa = 0; 174 | const unsigned int* accu = hough.GetAccu(&aw, &ah); 175 | 176 | for(int p=0;p<(ah*aw);p++) 177 | { 178 | if((int)accu[p] > maxa) 179 | maxa = accu[p]; 180 | } 181 | double contrast = 1.0; 182 | double coef = 255.0 / (double)maxa * contrast; 183 | 184 | cv::Mat img_accu(ah, aw, CV_8UC3); 185 | for(int p=0;p<(ah*aw);p++) 186 | { 187 | unsigned char c = (double)accu[p] * coef < 255.0 ? (double)accu[p] * coef : 255.0; 188 | img_accu.data[(p*3)+0] = 255; 189 | img_accu.data[(p*3)+1] = 255-c; 190 | img_accu.data[(p*3)+2] = 255-c; 191 | } 192 | 193 | 194 | cv::imshow(CW_IMG_ORIGINAL, img_ori); 195 | cv::imshow(CW_IMG_EDGE, img_edge); 196 | cv::imshow(CW_ACCUMULATOR, img_accu); 197 | 198 | cv::waitKey(1); 199 | 200 | } 201 | } //r 202 | 203 | 204 | 205 | //Filter the results 206 | { 207 | std::sort(circles.begin(), circles.end(), SortCirclesDistance()); 208 | int a,b,r; 209 | a=b=r=0; 210 | std::vector< std::pair< std::pair, int> > result; 211 | std::vector< std::pair< std::pair, int> >::iterator it; 212 | for(it=circles.begin();it!=circles.end();it++) 213 | { 214 | int d = sqrt( pow(abs(it->first.first - a), 2) + pow(abs(it->first.second - b), 2) ); 215 | if( d > it->second + r) 216 | { 217 | result.push_back(*it); 218 | //ok 219 | a = it->first.first; 220 | b = it->first.second; 221 | r = it->second; 222 | } 223 | } 224 | 225 | //Visualize all 226 | cv::Mat img_res = img_ori.clone(); 227 | for(it=result.begin();it!=result.end();it++) 228 | { 229 | std::cout << it->first.first << ", " << it->first.second << std::endl; 230 | cv::circle(img_res, cv::Point(it->first.first, it->first.second), it->second, cv::Scalar( 0, 0, 255), 2, 8); 231 | } 232 | cv::imshow(CW_IMG_ORIGINAL, img_res); 233 | cv::imshow(CW_IMG_EDGE, img_edge); 234 | //cv::imshow(CW_ACCUMULATOR, img_accu); 235 | cv::waitKey(360000); 236 | } 237 | 238 | } 239 | 240 | --------------------------------------------------------------------------------