枕上书的书 图像处理——(源)边缘检测canny算子(canny)函数编程实现

https://blog.csdn.net/weixin_40647819/article/details/91411424

  1 #include <iostream>
  2 #include <opencv2/core.hpp>
  3 #include <opencv2/highgui.hpp>
  4 #include <opencv2/imgproc.hpp>
  5 ////////////////////sobel算子/////////////////////////
  6 //阶乘
  7 int factorial(int n){
  8     int fac = 1;
  9     //0的阶乘
 10     if (n == 0)
 11         return fac;
 12     for (int i = 1; i <= n; ++i){
 13         fac *= i;
 14     }
 15     return fac;
 16 }
 17  
 18 //获得Sobel平滑算子
 19 cv::Mat getSobelSmoooth(int wsize){
 20     int n = wsize - 1;
 21     cv::Mat SobelSmooothoper = cv::Mat::zeros(cv::Size(wsize, 1), CV_32FC1);
 22     for (int k = 0; k <= n; k++){
 23         float *pt = SobelSmooothoper.ptr<float>(0);
 24         pt[k] = factorial(n) / (factorial(k)*factorial(n - k));
 25     }
 26     return SobelSmooothoper;
 27 }
 28  
 29 //获得Sobel差分算子
 30 cv::Mat getSobeldiff(int wsize){
 31     cv::Mat Sobeldiffoper = cv::Mat::zeros(cv::Size(wsize, 1), CV_32FC1);
 32     cv::Mat SobelSmoooth = getSobelSmoooth(wsize - 1);
 33     for (int k = 0; k < wsize; k++){
 34         if (k == 0)
 35             Sobeldiffoper.at<float>(0, k) = 1;
 36         else if (k == wsize - 1)
 37             Sobeldiffoper.at<float>(0, k) = -1;
 38         else
 39             Sobeldiffoper.at<float>(0, k) = SobelSmoooth.at<float>(0, k) - SobelSmoooth.at<float>(0, k - 1);
 40     }
 41     return Sobeldiffoper;
 42 }
 43  
 44 //卷积实现
 45 void conv2D(cv::Mat& src, cv::Mat& dst, cv::Mat kernel, int ddepth, cv::Point anchor = cv::Point(-1, -1), int delta = 0, int borderType = cv::BORDER_DEFAULT){
 46     cv::Mat  kernelFlip;
 47     cv::flip(kernel, kernelFlip, -1);
 48     cv::filter2D(src, dst, ddepth, kernelFlip, anchor, delta, borderType);
 49 }
 50  
 51  
 52 //可分离卷积———先垂直方向卷积,后水平方向卷积
 53 void sepConv2D_Y_X(cv::Mat& src, cv::Mat& dst, cv::Mat kernel_Y, cv::Mat kernel_X, int ddepth, cv::Point anchor = cv::Point(-1, -1), int delta = 0, int borderType = cv::BORDER_DEFAULT){
 54     cv::Mat dst_kernel_Y;
 55     conv2D(src, dst_kernel_Y, kernel_Y, ddepth, anchor, delta, borderType); //垂直方向卷积
 56     conv2D(dst_kernel_Y, dst, kernel_X, ddepth, anchor, delta, borderType); //水平方向卷积
 57 }
 58  
 59 //可分离卷积———先水平方向卷积,后垂直方向卷积
 60 void sepConv2D_X_Y(cv::Mat& src, cv::Mat& dst, cv::Mat kernel_X, cv::Mat kernel_Y, int ddepth, cv::Point anchor = cv::Point(-1, -1), int delta = 0, int borderType = cv::BORDER_DEFAULT){
 61     cv::Mat dst_kernel_X;
 62     conv2D(src, dst_kernel_X, kernel_X, ddepth, anchor, delta, borderType); //水平方向卷积
 63     conv2D(dst_kernel_X, dst, kernel_Y, ddepth, anchor, delta, borderType); //垂直方向卷积
 64 }
 65  
 66  
 67 //Sobel算子边缘检测
 68 //dst_X 垂直方向
 69 //dst_Y 水平方向
 70 void Sobel(cv::Mat& src, cv::Mat& dst_X, cv::Mat& dst_Y, cv::Mat& dst, int wsize, int ddepth, cv::Point anchor = cv::Point(-1, -1), int delta = 0, int borderType = cv::BORDER_DEFAULT){
 71  
 72     cv::Mat SobelSmooothoper = getSobelSmoooth(wsize); //平滑系数
 73     cv::Mat Sobeldiffoper = getSobeldiff(wsize); //差分系数
 74  
 75     //可分离卷积———先垂直方向平滑,后水平方向差分——得到垂直边缘
 76     sepConv2D_Y_X(src, dst_X, SobelSmooothoper.t(), Sobeldiffoper, ddepth);
 77  
 78     //可分离卷积———先水平方向平滑,后垂直方向差分——得到水平边缘
 79     sepConv2D_X_Y(src, dst_Y, SobelSmooothoper, Sobeldiffoper.t(), ddepth);
 80  
 81     //边缘强度(近似)
 82     dst = abs(dst_X) + abs(dst_Y);
 83     cv::convertScaleAbs(dst, dst); //求绝对值并转为无符号8位图
 84 }
 85  
 86  
 87 //确定一个点的坐标是否在图像内
 88 bool checkInRang(int r,int c, int rows, int cols){
 89     if (r >= 0 && r < rows && c >= 0 && c < cols)
 90         return true;
 91     else
 92         return false;
 93 }
 94  
 95 //从确定边缘点出发,延长边缘
 96 void trace(cv::Mat &edgeMag_noMaxsup, cv::Mat &edge, float TL,int r,int c,int rows,int cols){
 97     if (edge.at<uchar>(r, c) == 0){
 98         edge.at<uchar>(r, c) = 255;
 99         for (int i = -1; i <= 1; ++i){
100             for (int j = -1; j <= 1; ++j){
101                 float mag = edgeMag_noMaxsup.at<float>(r + i, c + j);
102                 if (checkInRang(r + i, c + j, rows, cols) && mag >= TL)
103                     trace(edgeMag_noMaxsup, edge, TL, r + i, c + j, rows, cols);
104             }
105         }
106     }
107 }
108  
109 //Canny边缘检测
110 void Edge_Canny(cv::Mat &src, cv::Mat &edge, float TL, float TH, int wsize=3, bool L2graydient = false){
111     int rows = src.rows;
112     int cols = src.cols;
113  
114     //高斯滤波
115     cv::GaussianBlur(src,src,cv::Size(5,5),0.8);
116     //sobel算子
117     cv::Mat dx, dy, sobel_dst;
118     Sobel(src, dx, dy, sobel_dst, wsize, CV_32FC1);
119  
120     //计算梯度幅值
121     cv::Mat edgeMag;
122     if (L2graydient = false)  edgeMag = abs(dx) + abs(dy); //绝对值之和近似
123     else if (L2graydient = true)  cv::magnitude(dx, dy, edgeMag); //开平方
124  
125     //计算梯度方向 以及 非极大值抑制
126     cv::Mat edgeMag_noMaxsup = cv::Mat::zeros(rows, cols, CV_32FC1);
127     for (int r = 1; r < rows - 1; ++r){
128         for (int c = 1; c < cols - 1; ++c){
129             float x = dx.at<float>(r, c);
130             float y = dy.at<float>(r, c);
131             float angle = std::atan2f(y, x) / CV_PI * 180; //当前位置梯度方向
132             float mag = edgeMag.at<float>(r, c);  //当前位置梯度幅值
133  
134             //非极大值抑制
135             //垂直边缘--梯度方向为水平方向-3*3邻域内左右方向比较
136             if (abs(angle)<22.5 || abs(angle)>157.5){
137                 float left = edgeMag.at<float>(r, c - 1);
138                 float right = edgeMag.at<float>(r, c + 1);
139                 if (mag >= left && mag >= right)
140                     edgeMag_noMaxsup.at<float>(r, c) = mag;
141             }
142         
143             //水平边缘--梯度方向为垂直方向-3*3邻域内上下方向比较
144             if ((angle>=67.5 && angle<=112.5 ) || (angle>=-112.5 && angle<=-67.5)){
145                 float top = edgeMag.at<float>(r-1, c);
146                 float down = edgeMag.at<float>(r+1, c);
147                 if (mag >= top && mag >= down)
148                     edgeMag_noMaxsup.at<float>(r, c) = mag;
149             }
150  
151             //+45°边缘--梯度方向为其正交方向-3*3邻域内右上左下方向比较
152             if ((angle>112.5 && angle<=157.5) || (angle>-67.5 && angle<=-22.5)){
153                 float right_top = edgeMag.at<float>(r - 1, c+1);
154                 float left_down = edgeMag.at<float>(r + 1, c-1);
155                 if (mag >= right_top && mag >= left_down)
156                     edgeMag_noMaxsup.at<float>(r, c) = mag;
157             }
158  
159  
160             //+135°边缘--梯度方向为其正交方向-3*3邻域内右下左上方向比较
161             if ((angle >=22.5 && angle < 67.5) || (angle >= -157.5 && angle < -112.5)){
162                 float left_top = edgeMag.at<float>(r - 1, c - 1);
163                 float right_down = edgeMag.at<float>(r + 1, c + 1);
164                 if (mag >= left_top && mag >= right_down)
165                     edgeMag_noMaxsup.at<float>(r, c) = mag;
166             }
167         }
168     }
169  
170     //双阈值处理及边缘连接
171     edge = cv::Mat::zeros(rows, cols, CV_8UC1);
172     for (int r = 1; r < rows - 1; ++r){
173         for (int c = 1; c < cols - 1; ++c){
174             float mag = edgeMag_noMaxsup.at<float>(r, c);
175             //大于高阈值,为确定边缘点
176             if (mag >= TH)
177                 trace(edgeMag_noMaxsup, edge, TL, r, c, rows, cols);
178             else if (mag < TL)
179                 edge.at<uchar>(r, c) = 0;
180         }
181     }
182 }
183  
184 int main(){
185     cv::Mat src = cv::imread("E://lena.jpg");
186  
187     if (src.empty()){
188         return -1;
189     }
190     if (src.channels() > 1) cv::cvtColor(src, src, CV_RGB2GRAY);
191     cv::Mat edge,dst;
192  
193     //Canny
194     Edge_Canny(src, edge, 20,60);
195  
196     //opencv自带Canny
197     cv::Canny(src, dst, 20, 80);
198  
199     cv::namedWindow("src", CV_WINDOW_NORMAL);
200     imshow("src", src);
201     cv::namedWindow("My_canny", CV_WINDOW_NORMAL);
202     imshow("My_canny", edge);
203     cv::namedWindow("Opencv_canny", CV_WINDOW_NORMAL);
204     imshow("Opencv_canny", dst);
205     cv::waitKey(0);
206     return 0;
207 }

猜你喜欢

转载自www.cnblogs.com/fpzs/p/11223211.html