OpenCV小项目:自实现 Canny 算子
Posted 流楚丶格念
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了OpenCV小项目:自实现 Canny 算子相关的知识,希望对你有一定的参考价值。
文章目录
实现步骤
- 用高斯滤波器平滑图像
- 计算滤波后图像梯度的幅值和方向
- 对梯度幅值应用非极大值抑制,其过程为找出图像梯度中的局部极大值点,把其它非局部极大值点置零以得到细化的边缘
- 用双阈值算法检测和连接边缘,使用两个阈值 T1 和 T2(T1>T2),T1 用来找到每条线段,T2 用来在这些线段的两个方向上延伸寻找边缘的断裂处,并连接这些边缘
高斯滤波
由高斯滤波器的二维可分性(X 轴与 Y 轴方向进行高斯滤波互不干扰),代码采用两次 1*5 一维高斯滤波器
[
1
,
4
,
6
,
4
,
1
]
[1, 4, 6, 4, 1]
[1,4,6,4,1]对 X、Y 方向分别进行卷积(对 Y 方向需要先转置再卷积,之后再转置回来)以实现 5*5 二维高斯滤波器。由于可将一维高斯滤波器封装为一个函数 SingleGaussFilter
,简化了代码量和程序复杂度
一阶偏导有限差分计算梯度幅值和方向
计算梯度
采用算子
以实现
P [ y , x ] ≈ 1 2 ( S [ y , x + 1 ] − S [ y , x ] + S [ y + 1 , x + 1 ] − S [ y + 1 , x ] ) P[y, x]\\approx \\frac12(S[y,x+1]-S[y,x]+S[y+1,x+1]-S[y+1,x]) P[y,x]≈21(S[y,x+1]−S[y,x]+S[y+1,x+1]−S[y+1,x])
Q [ y , x ] ≈ 1 2 ( S [ y + 1 , x ] − S [ y , x ] + S [ y + 1 , x + 1 ] − S [ y , x + 1 ] ) Q[y, x]\\approx \\frac12(S[y+1,x]-S[y,x]+S[y+1,x+1]-S[y,x+1]) Q[y,x]≈21(S[y+1,x]−S[y,x]+S[y+1,x+1]−S[y,x+1])
计算幅值和相角
幅值计算式
M [ y , x ] = P [ y , x ] 2 + Q [ y , x ] 2 M[y, x]=\\sqrtP[y,x]^2+Q[y,x]^2 M[y,x]=P[y,x]2+Q[y,x]2
相角计算式
θ [ y , x ] = a r c t a n Q [ y , x ] P [ y , x ] \\theta[y,x]=arctan\\fracQ[y,x]P[y,x] θ[y,x]=arctanP[y,x]Q[y,x]
非极大值抑制
由于得到梯度之后,仍存在双边缘、宽边缘和噪声点等影响,若直接进行阈值分割确定边缘,结果并不理想。为解决宽边缘问题,可以将整条边缘认为是一条山脉,而真边缘则为山脊,故尝试采用局部极大值抑制,只保留 3*3 邻域且特定方向内的极大值,以消除非山脊的山脉影响
具体措施
- 依据相角 [ − 9 0 ∘ , + 9 0 ∘ ] [-90^\\circ,+90^\\circ] [−90∘,+90∘]将梯度角的变化范围减小到圆周的四个扇区之一( [ − 22. 5 ∘ , + 22. 5 ∘ ] [-22.5^\\circ,+22.5^\\circ] [−22.5∘,+22.5∘]对应 0 区, [ − 67. 5 ∘ , − 22. 5 ∘ ] [-67.5^\\circ,-22.5^\\circ] [−67.5∘,−22.5∘]对应 1 区, [ + 22. 5 ∘ , + 67. 5 ∘ ] [+22.5^\\circ,+67.5^\\circ] [+22.5∘,+67.5∘]对应 3 区, [ − 9 0 ∘ , − 67. 5 ∘ ] [-90^\\circ,-67.5^\\circ] [−90∘,−67.5∘]和 [ + 67. 5 ∘ , + 9 0 ∘ ] [+67.5^\\circ,+90^\\circ] [+67.5∘,+90∘]对应 2 区),而每一个扇区对应着当前点 8 邻域的 4 个方向
- 将每一点与沿着梯度线方向的两个象素比较,若当前点梯度值 ≥ \\geq ≥沿梯度线的两个相邻象素梯度值,则令保留当前点不变;否则,置 0
双阈值算法检测和连接边缘
由于设置单一阈值,在调节阈值大小的同时,真实边缘的增多往往伴随着虚假边缘和噪声点的增多,而将阈值提高减少虚假边缘和噪声点的同时,会造成边缘轮廓丢失的问题。为解决这个矛盾,采用双阈值分割算法,通过低阈值将所有可能边缘检测出来,利用高阈值检测出所有真边缘(可能有部分轮廓丢失),则可以利用高阈值图像作为种子点,索引出所有在低阈值图像上的所有相邻点,以补全高阈值图像,来实现抑制噪声和虚假边缘,同时减少真边缘丢失的目的
实现代码
#include <iostream>
#include <cmath>
#include <opencv2/imgproc/types_c.h>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
using namespace cv;
using namespace std;
int MyCanny(const Mat gray_image, Mat &fin_image, int high_threshold, int low_threshold);
void GaussFilter(const Mat orig_image, Mat& gauss_image);
void SingleGaussFilter(Mat orig_image, Mat& single_gauss_image);
void Gradient(Mat image, Mat& X, Mat& Y);
void SingleGradient(Mat image, Mat& single_gradient);
void AmpliPhase(Mat X, Mat Y, Mat &li, Mat &phase);
void NonMaximaSuppression(Mat ampli, Mat phase, Mat &nms_image);
void DoubleThreshold(Mat image, Mat& high_threshold_image, Mat& low_threshold_image, int high_threshold, int low_threshold);
void EdgeTracking(Mat high_threshold_image, Mat low_threshold_image, Mat &edge_tracking_image);
void SinglePointTracking(Mat &high_threshold_image, Mat &subtract_image, Mat& edge_tracking_image, int row, int col);
int main()
Mat image, gray_image;
Mat fin_image= Mat::zeros(image.rows, image.cols, CV_8UC1);
image = imread("c++.jpeg");
if (image.empty())
cout << "Could not open or find the image" << endl;
return -1;
else
cvtColor(image, gray_image, CV_RGB2GRAY);
imshow("gray_image", gray_image);
imwrite("gray_image.jpg", gray_image);
int high_threshold=100; // 120
int low_threshold=20; // 50
MyCanny(gray_image, fin_image, high_threshold, low_threshold);
Mat cvCanny_image = Mat::zeros(gray_image.rows, gray_image.cols, CV_8UC1);
Canny(gray_image, cvCanny_image, high_threshold, low_threshold);
imshow("cvCanny_image", cvCanny_image);
imwrite("cvCanny_image.jpg", cvCanny_image);
waitKey(0);
return 0;
int MyCanny(const Mat gray_image, Mat& fin_image, int high_threshold, int low_threshold)
Mat gauss_image = Mat::zeros(gray_image.rows, gray_image.cols, CV_8UC1);
// 高斯滤波
GaussFilter(gray_image, gauss_image);
imshow("gauss_image", gauss_image);
imwrite("gauss_image.jpg", gauss_image);
// X、Y方向梯度计算
Mat X = Mat::zeros(gray_image.rows, gray_image.cols, CV_32FC1);
Mat Y = Mat::zeros(gray_image.rows, gray_image.cols, CV_32FC1);
Gradient(gray_image, X, Y);
Mat temp_X = X.clone();
temp_X.convertTo(temp_X, CV_8UC1);
imshow("X", temp_X);
imwrite("X.jpg", temp_X);
Mat temp_Y = Y.clone();
temp_Y.convertTo(temp_Y, CV_8UC1);
imshow("Y", temp_Y);
imwrite("Y.jpg", temp_Y);
// 计算幅值、相角
Mat ampli = Mat::zeros(gray_image.rows, gray_image.cols, CV_32FC1);
Mat phase = Mat::zeros(gray_image.rows, gray_image.cols, CV_32FC1);
AmpliPhase(X, Y, ampli, phase);
Mat temp_ampli = ampli.clone();
temp_ampli.convertTo(temp_ampli, CV_8UC1);
imshow("ampli", temp_ampli);
imwrite("ampli.jpg", temp_ampli);
Mat temp_phase = phase.clone();
temp_ampli.convertTo(temp_phase, CV_8UC1);
imshow("phase", temp_phase);
imwrite("phase.jpg", temp_phase);
// 非极大值抑制
Mat nms_image = Mat::zeros(gray_image.rows, gray_image.cols, CV_8UC1);
NonMaximaSuppression(ampli, phase, nms_image);
nms_image.convertTo(nms_image, CV_8UC1);
imshow("nms_image", nms_image);
imwrite("nms_image.jpg", nms_image);
// 双阈值分割
Mat high_threshold_image = Mat::zeros(gray_image.rows, gray_image.cols, CV_8UC1);
Mat low_threshold_image = Mat::zeros(gray_image.rows, gray_image.cols, CV_8UC1);
DoubleThreshold(nms_image, high_threshold_image, low_threshold_image, high_threshold, low_threshold);
imshow("high_threshold_image", high_threshold_image);
imwrite("high_threshold_image.jpg", high_threshold_image);
imshow("low_threshold_image", low_threshold_image);
imwrite("low_threshold_image.jpg", low_threshold_image);
// 边缘连接
Mat edge_tracking_image= Mat::zeros(gray_image.rows, gray_image.cols, CV_8UC1);
EdgeTracking( high_threshold_image,low_threshold_image, edge_tracking_image);
imshow("edge_tracking_image", edge_tracking_image);
imwrite("edge_tracking_image.jpg", edge_tracking_image);
return 0;
void GaussFilter(const Mat orig_image, Mat &gauss_image)
Mat temp_gauss_image = Mat::zeros(orig_image.rows, orig_image.cols, CV_8UC1);
SingleGaussFilter(orig_image, temp_gauss_image);
Mat t_temp_gauss_image = temp_gauss_image.t();
Mat temp_fin_gauss_image= Mat::zeros(t_temp_gauss_image.rows, t_temp_gauss_image.cols, CV_8UC1);
SingleGaussFilter(t_temp_gauss_image, temp_fin_gauss_image);
gauss_image = temp_fin_gauss_image.求教canny算子中非极大值抑制算法的理解
学习 opencv---(11)OpenC 边缘检测:Canny算子,Sobel算子,Laplace算子,Scharr滤波器