opencv边缘检测 canny源码剖析
2014-07-15 22:48
519 查看
前言
在做图像处理的过程中,经常需要提取边缘,这里主要针对canny源码进行一些简单解释。相关原理可以去其他地方看:
1)英文wiki
2)中文博客
实际上,canny源码内没有使用高斯滤波。
Canny源码
下面只贴出算法实现的关键代码,其他略掉。void cv::Canny( InputArray _src, OutputArray _dst, double low_thresh, double high_thresh, int aperture_size, bool L2gradient ) { ... //一. 计算横向、纵向sobel梯度 Sobel(src, dx, CV_16S, 1, 0, aperture_size, 1, 0, cv::BORDER_REPLICATE); Sobel(src, dy, CV_16S, 0, 1, aperture_size, 1, 0, cv::BORDER_REPLICATE); ... #define CANNY_PUSH(d) *(d) = uchar(2), *stack_top++ = (d) #define CANNY_POP(d) (d) = *--stack_top //二. 非极大值抑制,双梯度阈值处理是同时进行的 // calculate magnitude and angle of gradient, perform non-maxima supression. // fill the map with one of the following values: // 0 - the pixel might belong to an edge // 1 - the pixel can not belong to an edge // 2 - the pixel does belong to an edge for (int i = 0; i <= src.rows; i++) { int* _norm = mag_buf[(i > 0) + 1] + 1; if (i < src.rows) { short* _dx = dx.ptr<short>(i); short* _dy = dy.ptr<short>(i); //计算sobel横、纵梯度的norm. 即边缘响应的强度 if (!L2gradient) { //默认采用最简单的方式:norm = sobel横梯度绝对值 + sobel纵梯度绝对值 for (int j = 0; j < src.cols*cn; j++) _norm[j] = std::abs(int(_dx[j])) + std::abs(int(_dy[j])); } else { for (int j = 0; j < src.cols*cn; j++) _norm[j] = int(_dx[j])*_dx[j] + int(_dy[j])*_dy[j]; } ... } ... int prev_flag = 0; //非极大值抑制(这名字起得霸气!其实就是判断该坐标是否为最大值而已,当然要记得考虑方向) for (int j = 0; j < src.cols; j++) { #define CANNY_SHIFT 15 //tan22.5,在后面的非极大值抑制中判断梯度方向 const int TG22 = (int)(0.4142135623730950488016887242097*(1<<CANNY_SHIFT) + 0.5); int m = _mag[j]; //如果梯度响应(即上面计算出来的norm)大于低阈值,则判断该坐标位置是否为最大梯度响应 //否则,直接标记为1,不是边缘. if (m > low) { int xs = _x[j]; int ys = _y[j]; int x = std::abs(xs); int y = std::abs(ys) << CANNY_SHIFT; int tg22x = x * TG22; //四个方向极大值判断,如果是极大值则进入__ocv_canny_push,进一步判断该点是边缘(标记为2)或者可能是边缘(标记为0) if (y < tg22x) { //左右邻域 if (m > _mag[j-1] && m >= _mag[j+1]) goto __ocv_canny_push; } else { int tg67x = tg22x + (x << (CANNY_SHIFT+1)); if (y > tg67x) { //上下邻域 if (m > _mag[j+magstep2] && m >= _mag[j+magstep1]) goto __ocv_canny_push; } else { //45° -45°邻域 int s = (xs ^ ys) < 0 ? -1 : 1; if (m > _mag[j+magstep2-s] && m > _mag[j+magstep1+s]) goto __ocv_canny_push; } } } prev_flag = 0; _map[j] = uchar(1); continue; //梯度响应大于大阈值,且左侧和上侧都不是已确定的边缘(标记为2)时,才确定为边缘 //否则标记为可能边缘-0 __ocv_canny_push: if (!prev_flag && m > high && _map[j-mapstep] != 2) { CANNY_PUSH(_map + j); prev_flag = 1; } else _map[j] = 0; } ... } //三.边缘跟踪 //可简单理解为:在已确定为边缘点(标记为2)的8个邻域上,将可能是边缘的点(标记为0),判断为边缘(0->2). // now track the edges (hysteresis thresholding) while (stack_top > stack_bottom) { ... //取出一个边缘点 CANNY_POP(m); //如果该边缘点8个邻域上的点是可能边缘(标记为0),则判断其为边缘,重新标记为2. if (!m[-1]) CANNY_PUSH(m - 1); if (!m[1]) CANNY_PUSH(m + 1); if (!m[-mapstep-1]) CANNY_PUSH(m - mapstep - 1); if (!m[-mapstep]) CANNY_PUSH(m - mapstep); if (!m[-mapstep+1]) CANNY_PUSH(m - mapstep + 1); if (!m[mapstep-1]) CANNY_PUSH(m + mapstep - 1); if (!m[mapstep]) CANNY_PUSH(m + mapstep); if (!m[mapstep+1]) CANNY_PUSH(m + mapstep + 1); } //四.根据标记值0,1,2生成边缘。 // the final pass, form the final image const uchar* pmap = map + mapstep + 1; uchar* pdst = dst.ptr(); for (int i = 0; i < src.rows; i++, pmap += mapstep, pdst += dst.step) { //标记0: (uchar)-(0>>1)等于0 //标记1:(uchar)-(1>>1)也等于0 //标记2:(uchar)-(2>>1)=uchar(-1)=255 //边缘像素值全部是255,非边缘全部是0 for (int j = 0; j < src.cols; j++) pdst[j] = (uchar)-(pmap[j] >> 1); } }
相关文章推荐
- Canny边缘检测源码与图像结果(OpenCV2.0)
- Canny边缘检测源码与图像结果(OpenCV2.0)
- 【OpenCV入门指南】第三篇Canny边缘检测
- opencv c++函数 imgproc模块 10 Canny 边缘检测
- OpenCV中文网站例程——Canny边缘检测
- opencv边缘检测Sobel和Canny
- opencv实践程序4——canny实现摄像头的边缘检测,高斯背景建模
- opencv-canny边缘检测
- OpenCV笔记:图像边缘检测Sobel,Laplace,Canny
- 【OpenCV入门指南】第三篇Canny边缘检测
- canny 算子实现图像边缘检测(详细过程附源码)
- 修改opencv中的canny边缘检测算法
- opencv 学习第二天 学习opencv(中文版)对一幅图片进行canny边缘检测
- OpenCV__Canny边缘检测和缩放(译)
- OpenCV读入视频,灰度化,并Canny边缘检测
- [学习opencv]图像sobel、laplacian、canny边缘检测
- 【MATLAB图像处理3】 canny边缘检测 (附源码)
- OpenCV: Canny边缘检测算法原理及其VC实现详解
- OpenCV参考手册之Canny边缘检测
- OpenCV中文网站例程——Canny边缘检测(带滚动条)