1. canny.h
#ifndef _CANNY_ #define _CANNY_ #include "CImg.h" #include <vector> using namespace std; using namespace cimg_library; class canny { private: CImg<unsigned char> img; //Original Image CImg<unsigned char> grayscaled; // Grayscale CImg<unsigned char> gFiltered; // Gradient CImg<unsigned char> sFiltered; //Sobel Filtered CImg<unsigned char> angles; //Angle Map CImg<unsigned char> non; // Non-maxima supp. CImg<unsigned char> thres; //Double threshold and final public: canny(char const*); //Constructor CImg<unsigned char> toGrayScale(); vector<vector<double> > createFilter(int, int, double); //Creates a gaussian filter CImg<unsigned char> useFilter(CImg<unsigned char>, vector<vector<double> >); //Use some filter CImg<unsigned char> sobel(); //Sobel filtering CImg<unsigned char> nonMaxSupp(); //Non-maxima supp. CImg<unsigned char> threshold(CImg<unsigned char>, int, int); //Double threshold and finalize picture }; #endif
2. canny.cpp
Z #define _USE_MATH_DEFINES #include "canny.h" #include <vector> #include <iostream> using namespace std; canny::canny(char const* filename) { CImg<unsigned char> temp(filename); img = temp; if (0) // Check for invalid input { cout << "Could not open or find the image" << std::endl; } else { vector<vector<double> > filter = createFilter(3, 3, 1); //Print filter for (int i = 0; i<filter.size(); i++) { for (int j = 0; j<filter[i].size(); j++) { cout << filter[i][j] << " "; } } grayscaled = toGrayScale(); //Grayscale the image gFiltered = useFilter(grayscaled, filter); //Gaussian Filter sFiltered = sobel(); //Sobel Filter non = nonMaxSupp(); //Non-Maxima Suppression thres = threshold(non, 77, 95); //Double Threshold and Finalize 20 40 /***************way1 to display*******************/ CImgDisplay img_disp(img, "Original"), grayscaled_disp(grayscaled,"GrayScaled"), gFiltered_disp(gFiltered, "Gaussian Blur"), sFiltered_disp(sFiltered, "Sobel Filtered"), non_disp(non, "Non-Maxima Supp."), thres_disp(thres, "Final"); while (!img_disp.is_closed() || !grayscaled_disp.is_closed() || !gFiltered_disp.is_closed() || !sFiltered_disp.is_closed() || !non_disp.is_closed() || !thres_disp.is_closed()) { } } } CImg<unsigned char> canny::toGrayScale() { grayscaled = CImg<unsigned char>(img.rows, img.cols, 1); // one channel cimg_forXY(img, x, y) { int b = img(x, y, 0); int g = img(x, y, 1); int r = img(x, y, 2); double newValue = (r * 0.2126 + g * 0.7152 + b * 0.0722); grayscaled(x, y) = newValue; } return grayscaled; } vector<vector<double>> canny::createFilter(int row, int column, double sigmaIn) { vector<vector<double>> filter(row, vector<int>double(col, -1)); int row = img.row; int col = img.col; float coordSum = 0; float constant = 2.0 * sigmaIn * sigmaIn; // Sum is for normalization float sum = 0.0; for (int x = - row/2; x <= row/2; x++) { for (int y = -column/2; y <= column/2; y++) { coordSum = (x*x + y*y); filter[x + row/2][y + column/2] = (exp(-(coordSum) / constant)) / (M_PI * constant); sum += filter[x + row/2][y + column/2]; } } // Normalize the Filter for (int i = 0; i < row; i++) for (int j = 0; j < column; j++) filter[i][j] /= sum; return filter; } CImg<unsigned char> canny::useFilter(CImg<unsigned char> img_in, vector<vector<double>> filterIn) { int size = (int)filterIn.size()/2; CImg<unsigned char> filteredImg = CImg<unsigned char>(img_in.rows - 2*size, img_in.cols - 2*size, 1); for (int i = size; i < img_in.rows - size; i++) { for (int j = size; j < img_in.cols - size; j++) { double sum = 0; for (int x = 0; x < filterIn.size(); x++) for (int y = 0; y < filterIn.size(); y++) { sum += filterIn[x][y] * (double)(img_in(i + x - size, j + y - size)); } filteredImg(i-size, j-size) = sum; } } return filteredImg; } CImg<unsigned char> canny::sobel() { //Sobel X Filter double x1[] = {-1.0, 0, 1.0}; double x2[] = {-2.0, 0, 2.0}; double x3[] = {-1.0, 0, 1.0}; vector<vector<double>> xFilter(3); xFilter[0].assign(x1, x1+3); xFilter[1].assign(x2, x2+3); xFilter[2].assign(x3, x3+3); //Sobel Y Filter double y1[] = {1.0, 2.0, 1.0}; double y2[] = {0, 0, 0}; double y3[] = {-1.0, -2.0, -1.0}; vector<vector<double>> yFilter(3); yFilter[0].assign(y1, y1+3); yFilter[1].assign(y2, y2+3); yFilter[2].assign(y3, y3+3); //Limit Size int size = (int)xFilter.size()/2; CImg<unsigned char> filteredImg = CImg<unsigned char>(gFiltered.rows - 2*size, gFiltered.cols - 2*size); angles = CImg<unsigned char>(gFiltered.rows - 2*size, gFiltered.cols - 2*size, 1); //AngleMap for (int i = size; i < gFiltered.rows - size; i++) { for (int j = size; j < gFiltered.cols - size; j++) { double sumx = 0; double sumy = 0; for (int x = 0; x < xFilter.size(); x++) for (int y = 0; y < xFilter.size(); y++) { sumx += xFilter[x][y] * (double)(gFiltered(i + x - size, j + y - size)); //Sobel_X Filter Value sumy += yFilter[x][y] * (double)(gFiltered(i + x - size, j + y - size)); //Sobel_Y Filter Value } double sumxsq = sumx*sumx; double sumysq = sumy*sumy; double sq2 = sqrt(sumxsq + sumysq); if(sq2 > 255) //Unsigned Char Fix sq2 =255; filteredImg(i-size, j-size) = sq2; if(sumx==0) //Arctan Fix angles(i-size, j-size) = 90; else angles(i-size, j-size) = atan(sumy/sumx); } } return filteredImg; } CImg<unsigned char> canny::nonMaxSupp() { CImg<unsigned char> nonMaxSupped = CImg<unsigned char>(sFiltered.rows-2, sFiltered.cols-2, CV_8UC1); for (int i=1; i<sFiltered.rows - 1; i++) { for (int j=1; j<sFiltered.cols - 1; j++) { float Tangent = angles(i,j); nonMaxSupped(i-1, j-1) = sFiltered(i,j); //Horizontal Edge if (((-22.5 < Tangent) && (Tangent <= 22.5)) || ((157.5 < Tangent) && (Tangent <= -157.5))) { if ((sFiltered(i,j) < sFiltered(i,j+1)) || (sFiltered(i,j) < sFiltered<uchar>(i,j-1))) nonMaxSupped<uchar>(i-1, j-1) = 0; } //Vertical Edge if (((-112.5 < Tangent) && (Tangent <= -67.5)) || ((67.5 < Tangent) && (Tangent <= 112.5))) { if ((sFiltered.at(i,j) < sFiltered(i+1,j)) || (sFiltered(i,j) < sFiltered(i-1,j))) nonMaxSupped.at(i-1, j-1) = 0; } //-45 Degree Edge if (((-67.5 < Tangent) && (Tangent <= -22.5)) || ((112.5 < Tangent) && (Tangent <= 157.5))) { if ((sFiltered(i,j) < sFiltered(i-1,j+1)) || (sFiltered(i,j) < sFiltered(i+1,j-1))) nonMaxSupped.at<uchar>(i-1, j-1) = 0; } //45 Degree Edge if (((-157.5 < Tangent) && (Tangent <= -112.5)) || ((22.5 < Tangent) && (Tangent <= 67.5))) { if ((sFiltered(i,j) < sFiltered(i+1,j+1)) || (sFiltered(i,j) < sFiltered(i-1,j-1))) nonMaxSupped(i-1, j-1) = 0; } } } return nonMaxSupped; } CImg<unsigned char> canny::threshold(CImg<unsigned char> imgin,int low, int high) { if(low > 255) low = 255; if(high > 255) high = 255; CImg<unsigned char> EdgeMat = CImg<unsigned char>(imgin.rows, imgin.cols, imgin.type()); for (int i=0; i<imgin.rows; i++) { for (int j = 0; j<imgin.cols; j++) { EdgeMat(i,j) = imgin(i,j); if(EdgeMat(i,j) > high) EdgeMat(i,j) = 255; else if(EdgeMat(i,j) < low) EdgeMat(i,j) = 0; else { bool anyHigh = false; bool anyBetween = false; for (int x=i-1; x < i+2; x++) { for (int y = j-1; y<j+2; y++) { if(x <= 0 || y <= 0 || EdgeMat.rows || y > EdgeMat.cols) //Out of bounds continue; else { if(EdgeMat(x,y) > high) { EdgeMat(i,j) = 255; anyHigh = true; break; } else if(EdgeMat(x,y) <= high && EdgeMat(x,y) >= low) anyBetween = true; } } if(anyHigh) break; } if(!anyHigh && anyBetween) for (int x=i-2; x < i+3; x++) { for (int y = j-1; y<j+3; y++) { if(x < 0 || y < 0 || x > EdgeMat.rows || y > EdgeMat.cols) //Out of bounds continue; else { if(EdgeMat(x,y) > high) { EdgeMat(i,j) = 255; anyHigh = true; break; } } } if(anyHigh) break; } if(!anyHigh) EdgeMat(i,j) = 0; } } } return EdgeMat; }
3. 運行效果
3.1 lena
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
3.2 bigben
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
3.3 stpetro
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
3.4 twows
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
4. 參數說明
4.1 toGrayScale
主要在於double newValue = (r * 0.2126 + g * 0.7152 + b * 0.0722);
這句話,把每一個點轉爲灰色ios
4.2 高斯模糊
首先調用createFilter
生成卷積核,再用useFilter
對圖像進行卷積svg
4.3 sobel
用Gx和Gy兩個卷積覈對圖像進行卷積,獲得梯度變化大的邊界。再用非極大值抑制法剔除非邊緣的點。spa
4.4 雙閾值法
剔除那些梯度變化太小或過大的點,以消除噪聲.net
本文同步分享在 博客「不存在的裏皮」(JianShu)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。3d