图像处理——(源)边缘检测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 }
萍水相逢逢萍水,浮萍之水水浮萍!