Advertisement

山东大学数字图像处理实验(八) 计算机学院 图像边缘检测

阅读量:

本次课程实验由计算机科学与技术学院计算机专业大四学生限定选修课程,在2023-2024-1学年开展。相较于以往课程实验内容发生了明显的变化。

本实验使用vs2019,c++语言,需要提前安装opencv,具体方法请自行搜索。

• 实现如下图所示的边缘检测基本流程:

– 采用 cv::Sobel 函数计算边缘响应

对边缘响应执行非最大值去除操作以生成边缘图谱,并与 OpenCV 的 Canny 函数输出结果进行比较分析,探讨高低阈值的影响。

复制代码
 #include <opencv2/opencv.hpp>

    
 #include <iostream>
    
 #include <cmath>
    
  
    
 using namespace cv;
    
 using namespace std;
    
  
    
 Mat img; 
    
 Mat pic1, pic2;
    
  
    
 void customCanny(Mat& s, Mat& output, double ht, double lt)
    
 {
    
     Mat gray = Mat::zeros(s.size(), CV_8UC1);//gray用于存储灰度图像
    
     Mat new_gray = Mat::zeros(gray.size(), gray.type());
    
     output = Mat::zeros(gray.size(), gray.type());
    
  
    
     GaussianBlur(s, gray, cv::Size(5, 5), 0, 0);
    
  
    
     cvtColor(gray, gray, COLOR_RGB2GRAY);
    
  
    
     Mat gradientX, gradientY;//gradientX和gradientY用于存储x横向和纵向的梯度
    
     Sobel(gray, gradientX, CV_64F, 1, 0);
    
     Sobel(gray, gradientY, CV_64F, 0, 1);
    
  
    
     Mat gradientD = Mat::zeros(gradientX.size(), gradientX.type());//gradientD用于存储梯度的幅值
    
  
    
     Mat gradientTAN = Mat::zeros(gradientX.size(), gradientX.type());//gradientTAN用于存储梯度的方向
    
  
    
     for (int x = 0; x < gray.rows; x++)//遍历,计算梯度幅值和方向
    
     {
    
     for (int y = 0; y < gray.cols; y++)
    
     {
    
         gradientD.at<double>(x, y) = sqrt(pow(gradientX.at<double>(x, y), 2.0) + pow(gradientY.at<double>(x, y), 2.0));
    
         gradientTAN.at<double>(x, y) = gradientY.at<double>(x, y) / gradientX.at<double>(x, y);
    
     }
    
     }
    
     //非极大值抑制
    
     for (int x = 0; x < new_gray.rows; x++)
    
     {
    
     for (int y = 0; y < new_gray.cols; y++)
    
     {
    
         double temp1 = gradientX.at<double>(x, y);
    
         double temp2 = gradientY.at<double>(x, y);
    
         if (temp1 == 0 && temp2 == 0)//无视梯度为0的情况 
    
             continue;
    
         if (temp1 == 0 && temp2 != 0)
    
         {//垂直边缘
    
             if (x == 0)//如果是第一行,只对比他的下面的像素
    
                 if (gradientD.at<double>(x, y) >= gradientD.at<double>(x + 1, y))
    
                     new_gray.at<uchar>(x, y) = (int)gradientD.at<double>(x, y);
    
             if (x == new_gray.rows - 1)//如果是最后一行,只对比他上面的像素
    
                 if (gradientD.at<double>(x, y) >= gradientD.at<double>(x - 1, y))
    
                     new_gray.at<uchar>(x, y) = (int)gradientD.at<double>(x, y);
    
             if (x != 0 && x != new_gray.rows - 1)//中间的那么就对比上下
    
                 if ((gradientD.at<double>(x, y) >= gradientD.at<double>(x + 1, y)) && (gradientD.at<double>(x, y) >= gradientD.at<double>(x - 1, y)))
    
                     new_gray.at<uchar>(x, y) = (int)gradientD.at<double>(x, y);
    
         }
    
         if (temp1 != 0 && temp2 == 0) 
    
         {//水平边缘
    
             if (y == 0)
    
                 if (gradientD.at<double>(x, y) >= gradientD.at<double>(x, y + 1))
    
                     new_gray.at<uchar>(x, y) = (int)gradientD.at<double>(x, y);
    
             else if (y == new_gray.cols - 1)
    
                 if (gradientD.at<double>(x, y) >= gradientD.at<double>(x, y - 1))
    
                     new_gray.at<uchar>(x, y) = (int)gradientD.at<double>(x, y);
    
             else
    
                 if ((gradientD.at<double>(x, y) >= gradientD.at<double>(x, y + 1)) && (gradientD.at<double>(x, y) >= gradientD.at<double>(x, y - 1)))
    
                     new_gray.at<uchar>(x, y) = (int)gradientD.at<double>(x, y);
    
         }
    
         if (temp1 != 0 && temp2 != 0)
    
         {//斜边缘
    
             double p1 = 0.0, p2 = 0.0;
    
             if (gradientTAN.at<double>(x, y) > 0)
    
             {//正斜率
    
  
    
                 //做两次线性插值,在梯度方向上进行平滑,以获得更加准确的边缘响应
    
                 p1 = gradientTAN.at<double>(x, y) * gradientD.at<double>(x - 1, y + 1) + (1 - gradientTAN.at<double>(x, y) * gradientD.at<double>(x, y + 1));//线性插值,利用上一行和下一列的梯度值
    
                 p2 = gradientTAN.at<double>(x, y) * gradientD.at<double>(x + 1, y - 1) + (1 - gradientTAN.at<double>(x, y) * gradientD.at<double>(x, y - 1)); //线性插值,利用下一行和上一列的梯度值
    
             }
    
             if (gradientTAN.at<double>(x, y) < 0)
    
             {
    
                 p1 = gradientTAN.at<double>(x, y) * gradientD.at<double>(x - 1, y - 1) + (1 - gradientTAN.at<double>(x, y) * gradientD.at<double>(x, y - 1));
    
                 p2 = gradientTAN.at<double>(x, y) * gradientD.at<double>(x + 1, y + 1) + (1 - gradientTAN.at<double>(x, y) * gradientD.at<double>(x, y + 1));
    
             }
    
             if (gradientD.at<double>(x, y) >= p1 && gradientD.at<double>(x, y) >= p2)//如果当前像素点的梯度值大于两个插值,才会被认为是极大值
    
                 new_gray.at<uchar>(x, y) = (int)gradientD.at<double>(x, y);
    
         }
    
     }
    
     }
    
     // 使用高低阈值进行边缘检测
    
     for (int x = 0; x < new_gray.rows; x++)
    
     {
    
     for (int y = 0; y < new_gray.cols; y++)
    
     {
    
         if (new_gray.at<uchar>(x, y) >= ht)// 如果像素值大于等于高阈值,保留为边缘
    
             output.at<uchar>(x, y) = new_gray.at<uchar>(x, y);
    
         else if (new_gray.at<uchar>(x, y) >= lt) // 如果像素值大于等于低阈值但小于高阈值
    
         {//则该像素点周围有任一像素值大于高阈值,就保留为边缘
    
             if ((y != 0) && (new_gray.at<uchar>(x, y - 1) >= ht)) 
    
             {
    
                 output.at<uchar>(x, y) = new_gray.at<uchar>(x, y);
    
                 continue;
    
             }
    
             if ((y != 0) && ((x != 0)) && (new_gray.at<uchar>(x - 1, y - 1) >= ht)) 
    
             {
    
                 output.at<uchar>(x, y) = new_gray.at<uchar>(x, y);
    
                 continue;
    
             }
    
             if ((x != 0) && (new_gray.at<uchar>(x - 1, y) >= ht)) 
    
             {
    
                 output.at<uchar>(x, y) = new_gray.at<uchar>(x, y);
    
                 continue;
    
             }
    
             if ((y != new_gray.cols - 1) && ((x != 0)) && (new_gray.at<uchar>(x - 1, y + 1) >= ht)) 
    
             {
    
                 output.at<uchar>(x, y) = new_gray.at<uchar>(x, y);
    
                 continue;
    
             }
    
             if ((y != new_gray.cols - 1) && (new_gray.at<uchar>(x, y + 1) >= ht)) 
    
             {
    
                 output.at<uchar>(x, y) = new_gray.at<uchar>(x, y);
    
                 continue;
    
             }
    
             if ((y != new_gray.cols - 1) && ((x != new_gray.rows - 1)) && (new_gray.at<uchar>(x + 1, y + 1) >= ht))
    
             {
    
                 output.at<uchar>(x, y) = new_gray.at<uchar>(x, y);
    
                 continue;
    
             }
    
             if ((x != new_gray.rows - 1) && (new_gray.at<uchar>(x + 1, y) >= ht)) 
    
             {
    
                 output.at<uchar>(x, y) = new_gray.at<uchar>(x, y);
    
                 continue;
    
             }
    
             if ((y != 0) && ((x != new_gray.rows - 1)) && (new_gray.at<uchar>(x + 1, y - 1) >= ht)) 
    
             {
    
                 output.at<uchar>(x, y) = new_gray.at<uchar>(x, y);
    
                 continue;
    
             }
    
         }
    
     }
    
     }
    
 }
    
  
    
 int main() {
    
     // 读取图像
    
     img = imread("C:/Users/13441/Desktop/数字图像/bb.png");
    
     customCanny(img, pic1, 165, 30);
    
     Canny(img, pic2, 450, 280);
    
     cvtColor(img, img, COLOR_RGB2GRAY);
    
     imshow("源图像", img);
    
     imshow("examImage", pic1);
    
     imshow("cv::Canny", pic2);
    
     waitKey(0);
    
  
    
     return 0;
    
 }

源图像:

输出图像:

cv::canny图像:

全部评论 (0)

还没有任何评论哟~