zoukankan      html  css  js  c++  java
  • 图像处理——(源)边缘检测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 }

    萍水相逢逢萍水,浮萍之水水浮萍!
  • 相关阅读:
    在Chrome浏览器中保存的密码有多安全?
    进程上下文切换 – 残酷的性能杀手(上)
    进程上下文切换 – 残酷的性能杀手(下)
    javascript推荐书籍
    使用Visual Studio 利用WinGDB编译和远程调试嵌入式Linux的程序
    Source Insight 3.X 标签插件v1.0发布
    QQ空间自动发广告解决方法
    Java---实力弹弹球,弹弹弹
    HDOJ 2027 统计元音
    Java---计算机贷款支付额计算(用对话框实现)
  • 原文地址:https://www.cnblogs.com/AIBigTruth/p/11223211.html
Copyright © 2011-2022 走看看