当前位置:   article > 正文

Canny边缘检测源码_边缘检测代码

边缘检测代码
  1. #include <iostream>
  2. #include <opencv2/core.hpp>
  3. #include <opencv2/highgui.hpp>
  4. #include <opencv2/imgproc.hpp>
  5. #include<vector>
  6. #include<Eigen/Core>
  7. #include <corecrt_math_defines.h>
  8. using namespace std;
  9. using namespace cv;
  10. class canny
  11. {
  12. public:
  13. Mat src;
  14. // 高斯滤波算子
  15. double** guassArray(int size, double sigma);
  16. // 高斯滤波
  17. Mat guassFilter(Mat& src,int size,double sigma);
  18. // 像素判定
  19. uchar pixelJudge(double pixel);
  20. // sobel梯度计算:梯度方向与边缘方向垂直
  21. Mat sobel(Mat& src);
  22. // 非极大值抑制(NMS)
  23. /*
  24. 非极大值抑制是一种将宽边缘带细化,只保留边缘峰值的处理。主要的手法是根据上一步获取
  25. 的梯度方向,将图像像素点沿梯度方向或逆方向的邻域像素点进行比较,如果为最大值则保留,
  26. 否则抑制,即设置像素点为0
  27. */
  28. Mat NMS(Mat& src);
  29. /*双阈值连接
  30. 设置两个阈值,一个高阈值和一个低阈值。图像像素点如果高于高阈值则表示是强边缘点,是
  31. 真实边缘点,高于低阈值但小于高阈值表示是弱边缘点,其它不是边缘,抑制。之后需要对弱
  32. 边缘点进行处理,若其邻域存在强边缘点则表示是真实边缘点,否则不是。这样设置两个阈值
  33. 可以滤除图像中噪声,改善图像质量。弱边缘的处理原则是因为真实边缘的弱边缘点都存在强边缘点。
  34. */
  35. Mat doubleThresh(Mat& src, uchar high, uchar low);
  36. //测试
  37. Mat cannyTest(Mat& src, int size, double sigma, uchar high, uchar low);
  38. };
  39. Mat canny::cannyTest(Mat& src, int size, double sigma, uchar high, uchar low)
  40. {
  41. Mat dst = guassFilter(src, size, sigma);
  42. Mat edge = sobel(dst);
  43. edge = NMS(edge);
  44. vector<Mat> grade;
  45. split(edge, grade);
  46. imshow("最大值抑制", grade[0]);
  47. edge = doubleThresh(edge, 30, 100);
  48. imshow("双阈值连接", edge);
  49. return edge;
  50. }
  51. uchar canny::pixelJudge(double pixel)
  52. {
  53. if (pixel < 0)
  54. {
  55. return 0;
  56. }
  57. else if (pixel > 255)
  58. {
  59. return 255;
  60. }
  61. else
  62. {
  63. return static_cast<uchar>(pixel);
  64. }
  65. }
  66. double** canny::guassArray(int size, double sigma)
  67. {
  68. // [1] 初始化权值数组
  69. double** array = new double* [size];
  70. for (int i = 0; i < size; i++) {
  71. array[i] = new double[size];
  72. }
  73. // [2] 高斯分布计算
  74. int center_i, center_j;
  75. center_i = center_j = size / 2;
  76. double pi = M_PI;
  77. double sum = 0;
  78. for (int i = 0; i < size; i++)
  79. {
  80. for (int j = 0; j < size; j++)
  81. {
  82. array[i][j] =
  83. //后面进行归一化,这部分可以不用
  84. //0.5f *pi*(sigma*sigma) *
  85. exp(-(1.0f) * (((i - center_i) * (i - center_i) + (j - center_j) * (j - center_j)) /
  86. (2.0f * sigma * sigma)));
  87. sum += array[i][j];
  88. //cout << array[i][j] << endl;
  89. }
  90. }
  91. // [2-2] 归一化求权值
  92. for (int i = 0; i < size; i++)
  93. {
  94. for (int j = 0; j < size; j++)
  95. {
  96. array[i][j] /= sum;
  97. //cout << array[i][j] << " ";
  98. }
  99. }
  100. return array;
  101. };
  102. Mat canny::guassFilter(Mat& src, int size, double sigma)
  103. {
  104. Mat dst = src.clone();
  105. double** array = guassArray(size, sigma);
  106. for (int i = 0; i < src.rows; i++)
  107. {
  108. for (int j = 0; j < src.cols; j++)
  109. {
  110. double sum = 0;
  111. for (int m = -size / 2; m <= size / 2; m++)
  112. {
  113. for (int n = -size / 2; n <= size / 2; n++)
  114. {
  115. // 边缘补零
  116. if (i + m < 0 || i + m >= src.rows || j + n < 0 || j + n >= src.cols)
  117. {
  118. sum += 0;
  119. }
  120. else
  121. {
  122. sum += src.at<uchar>(i + m, j + n)*array[m+size/2][n+size/2];
  123. }
  124. }
  125. }
  126. dst.at<uchar>(i, j) = pixelJudge(sum);
  127. }
  128. }
  129. return dst;
  130. }
  131. Mat canny::sobel(Mat& src)
  132. {
  133. Mat gradeValue = src.clone();
  134. Mat gradeDirect = src.clone();
  135. // x方向边缘的y模板
  136. double model1[9] = { -1, -2, -1, 0, 0, 0, 1, 2, 1 };
  137. // y方向边缘的x模板
  138. double model2[9] = { -1, 0, 1, -2, 0, 2, -1, 0, 1 };
  139. for (int i = 0; i < src.rows; i++)
  140. {
  141. for (int j = 0; j < src.cols; j++)
  142. {
  143. int p = 0;
  144. double sum_x = 0;
  145. double sum_y = 0;
  146. for (int m = -1; m <= 1; m++)
  147. {
  148. for (int n = -1; n <= 1; n++)
  149. {
  150. // 边缘补零
  151. if (i + m < 0 || i + m >= src.rows || j + n < 0 || j + n >= src.cols)
  152. {
  153. p++;
  154. }
  155. else
  156. {
  157. sum_y += src.at<uchar>(i + m, j + n) * model1[p];
  158. sum_x += src.at<uchar>(i + m, j + n) * model2[p];
  159. p++;
  160. }
  161. }
  162. }
  163. gradeValue.at<uchar>(i, j) = pixelJudge(pow(pow(sum_x, 2) + pow(sum_y, 2), 1.0 / 2));
  164. // 0,7垂直方向, 1,2斜对角方向\, 3,4水平方向, 5,6斜对角方向/
  165. // 梯度方向与边缘垂直,因此model1检测出横向边缘,梯度方向为竖向
  166. // 同理model2检测出竖向边缘,梯度方向为横向,因此梯度方向为y/x
  167. //arctanx的范围是-pi/2到pi/2
  168. gradeDirect.at<uchar>(i, j) = static_cast<uchar>(atan(sum_y / (sum_x + 0.001)) * 8 / CV_PI + 4);
  169. //cout << (int)gradeDirect.at<uchar>(i, j) << endl;
  170. }
  171. }
  172. Mat temp[] = { gradeValue, gradeDirect };
  173. Mat grad;
  174. // 将梯度幅值与梯度方向合并,通道数为2,类似RGB通道
  175. // 但是不能显示,因为只能显示通道数134的图像
  176. // Mat dst1 = Mat::zeros(src.rows, src.cols * 2, CV_8UC2);
  177. // imshow(dst1);
  178. merge(temp, 2, grad);
  179. //对多通道像素进行访问
  180. //int num = 0;
  181. //for (int i = 0; i <=0; i++)
  182. //{
  183. // for (int j = 0; j <= 1; j++)
  184. // {
  185. // cout << (int)grad.at<Vec2b>(i, j)[0] << endl;
  186. // cout << (int)grad.at<Vec2b>(i, j)[1] << endl;
  187. // }
  188. //}
  189. //显示梯度图
  190. //imshow("edge", gradeValue);
  191. return grad;
  192. }
  193. Mat canny::NMS(Mat& src)
  194. {
  195. // 梯度通道和方向通道
  196. vector<Mat> grad;
  197. split(src, grad);
  198. Mat gradValue = grad[0].clone();
  199. //imshow("edge", grade[0]);
  200. for (int i = 0; i < gradValue.rows; i++)
  201. for (int j = 0; j < gradValue.cols; j++)
  202. {
  203. //将宽边缘带细化:对于梯度方向是垂直方向,边缘是水平方向,想要细化边缘,就要比较上下gradValue
  204. //梯度方向是垂直方向,比较上下gradValue
  205. if (grad[1].at<uchar>(i, j) == 0 || grad[1].at<uchar>(i, j) == 7)
  206. {
  207. if (i < gradValue.rows - 1)
  208. if (gradValue.at<uchar>(i, j) <= gradValue.at<uchar>(i + 1, j))
  209. {
  210. grad[0].at<uchar>(i, j) = 0;
  211. continue;
  212. }
  213. if (i > 0)
  214. if (gradValue.at<uchar>(i, j) <= gradValue.at<uchar>(i - 1, j))
  215. {
  216. grad[0].at<uchar>(i, j) = 0;
  217. continue;
  218. }
  219. }
  220. //梯度方向是“\”方向,比较对角gradValue
  221. if (grad[1].at<uchar>(i, j) == 1 || grad[1].at<uchar>(i, j) == 2)
  222. {
  223. if (i < gradValue.rows - 1 && j < gradValue.cols - 1)
  224. if (gradValue.at<uchar>(i, j) <= gradValue.at<uchar>(i + 1, j + 1))
  225. {
  226. grad[0].at<uchar>(i, j) = 0;
  227. continue;
  228. }
  229. if (i > 0 && j > 0)
  230. if (gradValue.at<uchar>(i, j) <= gradValue.at<uchar>(i - 1, j - 1))
  231. {
  232. grad[0].at<uchar>(i, j) = 0;
  233. continue;
  234. }
  235. }
  236. //梯度方向是水平方向,比较左右gradValue
  237. if (grad[1].at<uchar>(i, j) == 3 || grad[1].at<uchar>(i, j) == 4)
  238. {
  239. if (j < gradValue.cols - 1)
  240. if (gradValue.at<uchar>(i, j) <= gradValue.at<uchar>(i, j + 1))
  241. {
  242. grad[0].at<uchar>(i, j) = 0;
  243. continue;
  244. }
  245. if (j > 0)
  246. if (gradValue.at<uchar>(i, j) <= gradValue.at<uchar>(i, j - 1))
  247. {
  248. grad[0].at<uchar>(i, j) = 0;
  249. continue;
  250. }
  251. }
  252. //梯度方向是“/”方向,比较对角gradValue
  253. if (grad[1].at<uchar>(i, j) == 5 || grad[1].at<uchar>(i, j) == 6)
  254. {
  255. if (i < gradValue.rows - 1 && j > 0)
  256. if (gradValue.at<uchar>(i, j) <= gradValue.at<uchar>(i + 1, j - 1))
  257. {
  258. grad[0].at<uchar>(i, j) = 0;
  259. continue;
  260. }
  261. if (i > 0 && j < gradValue.cols - 1)
  262. if (gradValue.at<uchar>(i, j) <= gradValue.at<uchar>(i - 1, j + 1))
  263. {
  264. grad[0].at<uchar>(i, j) = 0;
  265. continue;
  266. }
  267. }
  268. }
  269. //imshow("非极大值处理", grad[0]);
  270. merge(grad, src);
  271. return src;
  272. }
  273. Mat canny::doubleThresh(Mat& src, uchar high, uchar low)
  274. {
  275. vector<Mat> grad;
  276. // 梯度通道和方向通道
  277. split(src, grad);
  278. for (int i = 0; i < grad[0].rows; i++)
  279. for (int j = 0; j < grad[0].cols; j++)
  280. {
  281. if (grad[0].at<uchar>(i, j) > high)
  282. {
  283. grad[0].at<uchar>(i, j) = 255;
  284. // 标记强边缘点的位置
  285. grad[1].at<uchar>(i, j) = 2;
  286. }
  287. else if (grad[0].at<uchar>(i, j) > low)
  288. {
  289. grad[0].at<uchar>(i, j) = 0;
  290. // 标记弱边缘点的位置
  291. grad[1].at<uchar>(i, j) = 1;
  292. }
  293. else
  294. {
  295. grad[0].at<uchar>(i, j) = 0;
  296. grad[1].at<uchar>(i, j) = 0;
  297. }
  298. }
  299. // 真实的边缘会在弱边缘点的邻域内存在强边缘点
  300. for (int i = 0; i < grad[0].rows; i++)
  301. for (int j = 0; j < grad[0].cols; j++)
  302. {
  303. if (grad[1].at<uchar>(i, j) == 1)
  304. {
  305. for (int n = -1; n <= 1; n++)
  306. for (int m = -1; m <= 1; m++)
  307. {
  308. if (i + n >= 0 && j + m >= 0 && i + n < src.rows && j + m < src.cols && grad[1].at<uchar>(i + n, j + m) == 2)
  309. grad[0].at<uchar>(i, j) = 255;
  310. }
  311. }
  312. }
  313. return grad[0];
  314. }
  315. int main()
  316. {
  317. Mat imgLeft = imread("D:\\VC\\c++\\opencv源码\\opencv源码\\Stereo_Sample\\ImageL1.jpg", IMREAD_GRAYSCALE);
  318. canny c;
  319. c.src = imgLeft;
  320. c.cannyTest(c.src, 5, 1, 40, 100);
  321. cv::waitKey(0);
  322. return 0;
  323. }

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/码创造者/article/detail/846201
推荐阅读
相关标签
  

闽ICP备14008679号