## OpenCV Canny算法自实现

一.简介
  最近一段时间开始学习opencv,先从造轮子开始的,有一些内容我也想通过博客分享出来。比如说这次的Canny边缘滤波。很多大牛的博客都将其中的算法原理讲解的十分清晰,在此我将不再重复。有兴趣的可以直接访问我下面贴出的链接,都是总结的比较到位的。
     fengye2two https://blog.csdn.net/fengye2two/article/details/79190759
    爱鱼 https://www.cnblogs.com/mightycode/p/6394810.html
     mitutao https://www.cnblogs.com/love6tao/p/5152020.html
  我自实现的Canny算法能够应用,但是有很多改进的空间。比如说其中应用的GaussianBlur以及filter2D等也可以通过自实现的方式来完成;再者就是我实现的算法的最后一步,并没有通过堆栈跟队列的方式来实现。在以后的博客中我会将这些坑填上。希望看到本博客的各位能够指出我的错误,在此我表示由心的感谢。
废话不多说,第二部分就是我贴出的代码。
二.代码部分


#include<iostream>
#include<opencv2/highgui/highgui.hp>
#include<opencv2/core/core.hpp>
#include<opencv2/imgproc/imgproc.hpp>
#include <math.h>
using namespace cv;
using namespace std;void TraceEdge(int w, int h,double low_gradvalue, Mat& image_restrain, Mat& image_sobel_xy) {int xNum[8] = { 1,1,0,-1,-1,-1,0,1 };int yNum[8] = { 0,1,1,1,0,-1,-1,-1 };int xx = 0;int yy = 0;bool change = true;while (change){change = false;for (int k = 0; k < 8; k++){xx = w + xNum[k];yy = h + yNum[k];//   如果该象素为可能的边界点,又没有处理过  //   并且梯度大于阈值  double curgrayvalue = image_restrain.at<double>(xx, yy);double curgrdvalue = image_sobel_xy.at<double>(xx, yy);if (curgrayvalue == 200 && curgrdvalue > low_gradvalue){change = true;//   把该点设置成为边界点  image_restrain.at<double>(xx, yy) = 255;h = yy;w = xx;break;}}}}
int main() {Rect rect;
Mat image = imread("pic.jpg");
image.convertTo(image, CV_64FC3);//如果装换成64FC3则无法使用imread读取图像
Mat image_gray = Mat::zeros(image.rows, image.cols, CV_64FC1);
Mat image_gaussian = Mat::zeros(image.rows, image.cols, CV_64FC1);
Mat image_sobel_x = Mat::zeros(image.rows, image.cols, CV_64FC1);
Mat image_sobel_y = Mat::zeros(image.rows, image.cols, CV_64FC1);
Mat image_sobel_xy = Mat::zeros(image.rows, image.cols, CV_64FC1);//梯度结果;需要对此结果进行非极大值抑制
Mat image_restrain = Mat::zeros(image.rows, image.cols, CV_64FC1);//用来存储非极大值抑制之后的结果
Mat image_sobel_angle = Mat::zeros(image.rows, image.cols, CV_64FC1);
Mat image_canny = Mat::zeros(image.rows, image.cols, CV_64FC1);/*+++++++++++++++++++++++++++第一步:灰度处理++++++++++++++++++++++++++*/
for (int i = 0; i < image.rows; i++) {for (int j = 0; j < image.cols; j++) {image_gray.at<double>(i, j) = (0.114 * image.at<Vec3d>(i, j)[0] + 0.299 * image.at<Vec3d>(i, j)[1] + 0.587 * image.at<Vec3d>(i, j)[2]);}
}
image.convertTo(image, CV_8UC3);
imshow("原图", image);              //he function may scale the image, depending on its depth:If the image is 8 - bit unsigned, it is displayed as is./*If the image is 16 - bit unsigned or 32 - bit integer, the pixels are divided by 256. That is, the value range[0, 255 * 256] is mapped to[0, 255].If the image is 32 - bit floating - point, the pixel values are multiplied by 255. That is, the value range[0, 1] is mapped to[0, 255].*/
//image_gray.convertTo(image_gray,CV_8UC1);
//imshow("灰度图", image_gray);
/*+++++++++++++++++++++++++++++++++第一步:结束++++++++++++++++++++++++++++++++++++++++*//*+++++++++++++++++++++++++++++++++第二步:高斯滤波(7*7)模板++++++++++++++++++++++++++++++++++++++++*/
GaussianBlur(image_gray, image_gaussian, Size(7, 7), 50, 50);//直接调用的opencv的高斯滤波模板,其中sigma = 50;注意调用高斯滤波会自动将dst的//图片类型转换为src的图片类型/*+++++++++++++++++++++++++++++++++第二步:结束++++++++++++++++++++++++++++++++++++++++*/
/*+++++++++++++++++++++++++++++++++第三步:利用系统自带的filter2D+sobel掩码模板进行卷积++++++++++++++++++++++++++++++++++++++++*/
//x方向的sobel算子
Mat Sx = (Mat_<double>(3,3)<<-1, 0, 1,-2, 0, 2,-1, 0,1);
//y方向的sobel算子
Mat Sy = (Mat_<double>(3, 3) << 1, 2, 1, 0, 0, 0, -1, -2, -1);
filter2D(image_gaussian,image_sobel_x,-1,Sx);//filter2d具有转变图像类型的功能
filter2D(image_gaussian,image_sobel_y,-1,Sy);
for (int i = 0; i < image_sobel_x.rows; i++)for (int j = 0; j < image_sobel_x.cols; j++) {image_sobel_xy.at<double>(i, j) = sqrt((pow(image_sobel_x.at<double>(i, j), 2)) + pow(image_sobel_y.at<double>(i, j), 2));image_sobel_angle.at<double>(i, j) = atan2((image_sobel_y.at<double>(i, j)), (image_sobel_x.at<double>(i, j))) * 180 / 3.1415;//double必须用CV_64C1if (image_sobel_angle.at<double>(i, j) < 0)image_sobel_angle.at<double>(i, j) += 360;}
/*+++++++++++++++++++++++++++++++++第三步:结束++++++++++++++++++++++++++++++++++++++++*//*+++++++++++++++++++++++++++++++++第四步:非极大值抑制++++++++++++++++++++++++++++++++++++++++*/
double G1;
double G2;//用来存储线性插值之后的结果
int g1 = 0;
int g2 = 0;
int g3 = 0;
int g4 = 0;//存储四个方向上的梯度值
double tan_angle = 0;//计算插值比例系数for(int i = 1; i < image_gray.rows - 1 ;i++)//注意此处要排除边界for (int j = 1; j < image_gray.cols - 1; j++) {//cout << tan_angle;//如果出错可能出在这里??????????????if (image_sobel_xy.at<double>(i, j) == 0)image_restrain.at<double>(i, j) = 0;//如果不为零将其考虑进去会怎样?else {if ((image_sobel_angle.at<double>(i, j) >= 90 && image_sobel_angle.at<double>(i, j) < 135) || (image_sobel_angle.at<double>(i, j) >= 270 && image_sobel_angle.at<double>(i, j) < 315)) {g1 = image_sobel_xy.at<double>(i - 1, j - 1);g2 = image_sobel_xy.at<double>(i - 1, j);g3 = image_sobel_xy.at<double>(i + 1, j);g4 = image_sobel_xy.at<double>(i + 1, j + 1);tan_angle = fabs(image_sobel_x.at<double>(i, j) / fabs(image_sobel_y.at<double>(i, j)));G1 = g1 * tan_angle + (1 - tan_angle) * g2;G2 = g4 * tan_angle + (1 - tan_angle) * g3;}else if ((image_sobel_angle.at<double>(i, j) >= 135 && image_sobel_angle.at<double>(i, j) < 180) | (image_sobel_angle.at<double>(i, j) >= 315 & image_sobel_angle.at<double>(i, j) < 360)) {g1 = image_sobel_xy.at<double>(i - 1, j - 1);g2 = image_sobel_xy.at<double>(i, j - 1);g3 = image_sobel_xy.at<double>(i, j + 1);g4 = image_sobel_xy.at<double>(i + 1, j + 1);tan_angle = fabs(image_sobel_y.at<double>(i, j) /fabs(image_sobel_x.at<double>(i, j)));G1 = g1 * tan_angle + (1 - tan_angle) * g2;G2 = g4 * tan_angle + (1 - tan_angle) * g3;}else if ((image_sobel_angle.at<double>(i, j) >= 45 & image_sobel_angle.at<double>(i, j) < 90) | (image_sobel_angle.at<double>(i, j) >= 225 & image_sobel_angle.at<double>(i, j) < 270)) {g1 = image_sobel_xy.at<double>(i - 1, j);g2 = image_sobel_xy.at<double>(i - 1, j + 1);g3 = image_sobel_xy.at<double>(i + 1, j);g4 = image_sobel_xy.at<double>(i + 1, j - 1);tan_angle = fabs(image_sobel_x.at<double>(i, j) / fabs(image_sobel_y.at<double>(i, j)));G1 = g1 * tan_angle + (1 - tan_angle) * g2;G2 = g4 * tan_angle + (1 - tan_angle) * g3;}else if ((image_sobel_angle.at<double>(i, j) >= 0 & image_sobel_angle.at<double>(i, j) < 45) | (image_sobel_angle.at<double>(i, j) >= 180 & image_sobel_angle.at<double>(i, j) < 225)) {g1 = image_sobel_xy.at<double>(i - 1, j + 1);g2 = image_sobel_xy.at<double>(i, j + 1);g3 = image_sobel_xy.at<double>(i + 1, j - 1);g4 = image_sobel_xy.at<double>(i, j - 1);tan_angle = fabs(image_sobel_y.at<double>(i, j) / fabs(image_sobel_x.at<double>(i, j)));G1 = g1 * tan_angle + (1 - tan_angle) * g2;G2 = g4 * tan_angle + (1 - tan_angle) * g3;}}if (image_sobel_xy.at<double>(i, j) >= G1 && image_sobel_xy.at<double>(i, j) >= G2)image_restrain.at<double>(i, j) = 200;//非极大值抑制之后的结果elseimage_restrain.at<double>(i, j) = 0;}/*+++++++++++++++++++++++++++++++++第四步:结束++++++++++++++++++++++++++++++++++++++++*//*+++++++++++++++++++++++++++++++++第五步:双阈值确定++++++++++++++++++++++++++++++++++++++++*/
/*++++++++++++++++++++双阈值确定的是梯度(sobel_xy)的阈值,分为三步++++++++++++++++++++*/
/*+++++++++++++++++++++++第一步:查看共有多少像素N++++++++++++++++++++++++++++++*/
double high_factor = 0.9;
int low_factor = 0.5;
int pixel_table[1024];//直方图
int high_gradvalue = 0;//梯度高阈值
int max_grad = 0;
for (int i = 0; i < 1024; i++)pixel_table[i] = 0;
for (int i = 0; i < image_sobel_xy.rows; i++)for (int j = 0; j < image_sobel_xy.cols; j++) {//此处的统计有两种方法;方法一:认为所有的sobel获得的梯度都应该被统计进去://方法二:仅认为梯度是极大值点的像素才能被统计进去。我认为第二种方法正确//因为通过了第四步的非极大值抑制,有很多点已经排除在外了,如果使用第一种方法if (image_restrain.at<double>(i, j) == 200) { //,相当于又将被排除的点重新考虑了一次。int t = image_sobel_xy.at<double>(i, j);pixel_table[t]++;}//直方图统计完成int pixel_sum = 0;for (int i = 0; i < 1024; i++) {if (pixel_table[i] != 0)max_grad = i;pixel_sum += pixel_table[i];//统计被非极大抑制之后边界像素的个数}int high_index = high_factor * pixel_sum;//获得高梯度阈值像素的索引值/*++++++++++++第二步:选择第0.7N梯度值作为梯度高阈值++++++++++++++++++++++*/int count = 0;for (int i = 0; i < 1024; i++) {count += pixel_table[i];if (count >= high_index) {high_gradvalue = i;break;}}}
/*++++++++++++++++++++++++++第三步:选择低梯度阈值:低梯度阈值是高梯度阈值的一半++++++++++++++++++++++++++++++++*/
int low_gradvalue = high_gradvalue * low_factor;//低梯度阈值是高梯度阈值的一半
/*+++++++++++++++++++++++++++++++++第五步:结束++++++++++++++++++++++++++++++++++++++++*//*+++++++++++++++++++++++++++++++++第六步:邻域追踪++++++++++++++++++++++++++++++++++++++++*/
//通过比较梯度(sobel_xy)是否满足阈值,去决定image_restrain中被二值化(200,0)的点的去留,三值化sobel_xy中的值为(0,low_gradvalue,high_gradcalue)
/*++++++++++++++++++++非堆栈法有一定的改进空间++++++++++++++++++++++++++++++++++*/
通过邻域追踪抑制孤立点
for (int i = 1; i < image.rows - 1; i++)for (int j = 1; j < image.cols - 1; j++)if (image_sobel_xy.at<double>(i, j) == low_gradvalue) {rect.x = j - 1;rect.y = i - 1;rect.width = 3;rect.height = 3;for(int i1 = 0;i1 <3;i1++)for(int j1 = 0;j1 <3;j++)if (image_sobel_xy(rect).at<double>(i1, j1) == high_gradvalue) {image_canny.at<double>(i, j) = 255;break;}}
for (int i = 1; i < image_sobel_xy.rows - 1; i++)//双阈值过滤sobelfor (int j = 1; j < image_sobel_xy.cols - 1; j++) {if (image_sobel_xy.at<double>(i, j) >= high_gradvalue && image_restrain.at<double>(i, j) == 200) {image_restrain.at<double>(i, j) = 255;TraceEdge(i, j, low_gradvalue, image_restrain, image_sobel_xy);}}
for (int i1 = 1; i1 < image_sobel_xy.rows - 1; i1++)//双阈值过滤sobelfor (int j1 = 1; j1 < image_sobel_xy.cols - 1; j1++){if (image_restrain.at<double>(i1, j1) != 255)image_restrain.at<double>(i1, j1) = 0;}/*++++++++++++++++++++非堆栈法结束++++++++++++++++++++++++++++++++++*/
/*++++++++++++++++++++改进后的堆栈法++++++++++++++++++++++++++++++++++*//*++++++++++++++++++++堆栈法结束++++++++++++++++++++++++++++++++++*/
/*+++++++++++++++++++++++++++++++++第六步:结束++++++++++++++++++++++++++++++++++++++++*/Mat a = imread("pic.jpg", 0);
Mat b = Mat::zeros(a.rows, a.cols, CV_8UC1);
Canny(a, b, 200,100);//调用opencv自带的canny函数
imshow("canny", b);
image_gray.convertTo(image_gray,CV_8UC1);
imshow("灰度图", image_gray);
image_gaussian.convertTo(image_gaussian, CV_8UC1);
imshow("高斯滤波", image_gaussian);
image_sobel_x.convertTo(image_sobel_x, CV_8UC1);
image_sobel_y.convertTo(image_sobel_y, CV_8UC1);
image_sobel_xy.convertTo(image_sobel_xy, CV_8UC1);
imshow("sobel_x", image_sobel_x);
imshow("sobel_y", image_sobel_y);
imshow("sobel_xy", image_sobel_xy);
image_restrain.convertTo(image_restrain, CV_8UC1);
imshow("image_restrain", image_restrain);
image_canny.convertTo(image_canny, CV_8UC1);
imshow("image_canny", image_canny);waitKey();while(1);
return 0;
}

OpenCV学习之Canny算法自实现相关推荐

  1. 【opencv学习】Fast算法进行角点检测

    今天学习角点检测的一个Fast算法,顾名思义,很快! FAST 算法 1: 在图像中选择一个像素点p,得到其灰度值I_p 2: 以半径r为半径画圆,覆盖p点周围M个像素点,通常设置r=3,则M=16, ...

  2. 【opencv学习】RANSAC算法在图像拼接中的应用实战

    一:单应性变换 我们得到两张图像的图像后,可以通过BFMatcher得到匹配的点,其实就是一个暴力搜索来比较最相近的特征点(128维度的向量,求向量的近似度). 通过匹配的多个关键点的配对信息,我们能 ...

  3. OpenCV学习(21) Grabcut算法详解

    grab cut算法是graph cut算法的改进.在理解grab cut算之前,应该学习一下graph cut算法的概念及实现方式. 我搜集了一些graph cut资料:http://yunpan. ...

  4. 【OpenCV学习】Bresenham算法opencv实现

    作者:gnuhpc 出处:http://www.cnblogs.com/gnuhpc/ /** ==================================================== ...

  5. OpenCV学习(19) 细化算法(7)

    最后再来看一种通过形态学腐蚀和开操作得到骨架的方法.http://felix.abecassis.me/2011/09/opencv-morphological-skeleton/ 代码非常简单: v ...

  6. 【opencv学习】光流算法以及物体追踪算法(Lucas-Kanade算法)

    正所谓的光流估计,就是在视频的前后帧的分析中,能分析出图中的一些object的移动方向和速度,可以做目标追踪使用. Lucas-Kanade算法改进 经典的光流估计是Lucas-Kanade 算法,这 ...

  7. 【opencv学习】SIFT算法的基本使用以及特征匹配

    下面我们学习特征点匹配的使用 import cv2def cv_show_image(name, img):cv2.imshow(name, img)cv2.waitKey(0) # 等待时间,单位是 ...

  8. OpenCV学习(13) 细化算法(1)(转)

    1.转载链接:http://www.cnblogs.com/mikewolf2002/p/3321732.html 程序编码参考经典的细化或者骨架算法文章: T. Y. Zhang and C. Y. ...

  9. OpenCV学习(14) 细化算法(2)

    前面一篇教程中,我们实现了Zhang的快速并行细化算法,从算法原理上,我们可以知道,算法是基于像素8邻域的形状来决定是否删除当前像素.还有很多与此算法相似的细化算法,只是判断的条件不一样.在综述文章, ...

  10. OpenCV学习(17) 细化算法(5)

    本章我们看下Pavlidis细化算法,参考资料http://www.imageprocessingplace.com/downloads_V3/root_downloads/tutorials/con ...

最新文章

  1. VS2010 编译 QT4.8.7 x64
  2. DiffServ实现技术
  3. python培训深圳-深圳哪里有Python培训?
  4. 一个vue管理系统的初步搭建总结
  5. 若S作主串,P作模式串,试分别写出利用BF算法和KMP算法的匹配过程。
  6. 我是大富豪php源码,最全大富豪3.4源码【自用可运营】含23款子游戏+可控制输赢工具...
  7. 编写你自己的单点登录(SSO)服务(转)
  8. 京东2020算法工程师0824笔试题整理
  9. 思想一碰就冒火: 开源贡献须耐心, 选择框架要谨慎
  10. Android中kt转java_将我现有的andorid工作室项目转换为kotlin?
  11. bzoj 1641: [Usaco2007 Nov]Cow Hurdles 奶牛跨栏(floyd)
  12. 我爱淘冲刺阶段站立会议2每天任务4
  13. char类型和字符串
  14. 后门准则(Backdoor Criterion)与前门准则(Frontdoor Criterion)
  15. 博雅数智|第四次直播|PageRank算法
  16. 项目初验一般要怎么走
  17. 超市收银软件测试自学,pos前台收银员培训 免费超市收银软件 视频教程
  18. Python爬虫入门——爬取贴吧图片
  19. 一群小青蛙呱蹦呱蹦呱
  20. Apache ranger 简介、原理、安装部署

热门文章

  1. UE4 从文件夹读取音乐文件并播放
  2. 蓝桥杯--鲁卡斯队列
  3. Python判断字符串中连续最长的递增英文字母串
  4. 小成开发日记----物联网项目LoveTv实现web网页传输数据到单片机-表白女朋友(技术栈涉及web前端,php后端,c/c++ socket,嵌入式前后端)
  5. 群晖docker搭建halo个人博客
  6. uni-app 中使用背景图片
  7. java pdf转jpg清晰度_java 库 pdfbox 将 pdf 文件转换成高清图片方法
  8. wlan 网速测试软件,wifi网速测试-让你一键查看当前网络状态的wifi网络测速工具...
  9. python微信机器人之小i
  10. 一本书读懂财报 | 现金流量表剖析