Canny边缘检测算法C++实现
Canny边缘检测流程
- 滤波
- 计算图像梯度值和方向
- 非极大值抑制----减小边缘宽度
- 迟滞阈值法筛选
Canny类实现
#ifndef _CANNY_
#define _CANNY_
#include<string>
#include<opencv2/opencv.hpp>
class canny
{
public:
canny(std::string src);
~canny();
void Show();
void Gaussian_filter(float sigma);
void Sobel();
void NMS();
void Delay_thread();
private:
cv::Mat m_src;
cv::Mat m_filter;
cv::Mat m_sobel;
cv::Mat m_nms;
cv::Mat m_thread;
float m_gaussian_kernel[5][5];
void Get_Gaussian_Kernel(float sigma);
float *seta;
};
#endif
#include"canny.h"
#define pi 3.14159264
canny::canny(std::string src)
{
this->m_src=cv::imread(src,0);
this->m_filter=cv::Mat::zeros(this->m_src.size(),0);
this->m_sobel=cv::Mat::zeros(this->m_src.size(),0);
this->seta=new float[this->m_src.rows*this->m_src.cols]();
this->m_nms=cv::Mat::zeros(this->m_src.size(),0);
this->m_thread=cv::Mat::zeros(this->m_src.size(),0);
}
canny::~canny()
{
if(this->seta!=NULL)
{
delete [] this->seta;
this->seta=NULL;
}
}
void canny::Show()
{
cv::namedWindow("src", 1);
cv::imshow("src", this->m_src);
cv::namedWindow("gaussian_filter", 1);
cv::imshow("gaussian_filter", this->m_filter);
cv::namedWindow("sobel", 1);
cv::imshow("sobel", this->m_sobel);
cv::namedWindow("NMS", 1);
cv::imshow("NMS", this->m_nms);
cv::namedWindow("Delay_thread", 1);
cv::imshow("Delay_thread", this->m_thread);
cv::waitKey(30000);
}
void canny::Get_Gaussian_Kernel(float sigma)
{
float sum=0.0;
const float sigma_2=sigma*sigma;
const float coe=1.0/(2*pi*sigma_2);
int kernel_len=sizeof(this->m_gaussian_kernel)/sizeof(this->m_gaussian_kernel[0]);
int center=kernel_len/2;
for(int i=0;i<kernel_len;++i)
{
float y=i-center;
for(int j=0;j<kernel_len;++j)
{
float x=j-center;
this->m_gaussian_kernel[i][j]=coe*exp(-(x*x+y*y)/(2.0*sigma_2));
sum+=this->m_gaussian_kernel[i][j];
}
}
sum=1.0/sum;
for(int i=0;i<kernel_len;++i)
{
for(int j=0;j<kernel_len;++j)
{
this->m_gaussian_kernel[i][j]*=sum;
}
}
}
void canny::Gaussian_filter(float sigma)
{
this->Get_Gaussian_Kernel(sigma);
int kernel_len=sizeof(this->m_gaussian_kernel)/sizeof(this->m_gaussian_kernel[0]);
int center=kernel_len/2;
cv::Mat src_board;
cv::copyMakeBorder(this->m_src,src_board,center,center,center,center,cv::BORDER_REFLECT);
int rows=src_board.rows-center;
int cols=src_board.cols-center;
for(int i=center;i<rows;++i)
{
uchar* dst_p=this->m_filter.ptr<uchar>(i-center);
for(int j=center;j<cols;++j)
{
float sum=0.0;
for(int y=0;y<kernel_len;++y)
{
uchar* p=src_board.ptr<uchar>(i-center+y);
for(int x=0;x<kernel_len;++x)
{
sum+=p[j-center+x]*this->m_gaussian_kernel[y][x];
}
}
dst_p[j-center]=(uchar)sum;
}
}
}
void canny::Sobel()
{
cv::Mat s_b;
cv::copyMakeBorder(this->m_filter,s_b,1,1,1,1,cv::BORDER_REFLECT);
int rows=s_b.rows;
int cols=s_b.cols;
for(int i=1;i<rows-1;++i)
{
for(int j=1;j<cols-1;++j)
{
float Gx=s_b.at<uchar>(i-1,j+1)+2*s_b.at<uchar>(i,j+1)+s_b.at<uchar>(i+1,j+1)
-(s_b.at<uchar>(i-1,j-1)+2*s_b.at<uchar>(i,j-1)+s_b.at<uchar>(i+1,j-1));
float Gy=s_b.at<uchar>(i+1,j-1)+s_b.at<uchar>(i+1,j)+s_b.at<uchar>(i+1,j+1)
-(s_b.at<uchar>(i-1,j-1)+s_b.at<uchar>(i-1,j)+s_b.at<uchar>(i-1,j+1));
float G=sqrt(Gx*Gx+Gy*Gy);
if(G>255) this->m_sobel.at<uchar>(i-1,j-1)=255;
else this->m_sobel.at<uchar>(i-1,j-1)=(uchar)G;
this->seta[(i-1)*this->m_src.cols+j-1]=atan2(Gy,Gx);
}
}
}
void canny::NMS()
{
int rows=this->m_sobel.rows;
int cols=this->m_sobel.cols;
for(int i=1;i<rows-1;++i)
{
for(int j=1;j<cols-1;++j)
{
uchar q=255,r=255;
float angle=this->seta[i*cols+j]*180.0/pi;
if(angle<0) angle+=180.0;
if(angle<22.5||(angle>=157.5&&angle<=180.0))
{
q=this->m_sobel.at<uchar>(i,j+1);
r=this->m_sobel.at<uchar>(i,j-1);
}else if(angle>=22.5&&angle<67.5)
{
q=this->m_sobel.at<uchar>(i+1,j+1);
r=this->m_sobel.at<uchar>(i-1,j-1);
}else if(angle>=67.5&&angle<112.5)
{
q=this->m_sobel.at<uchar>(i+1,j);
r=this->m_sobel.at<uchar>(i-1,j);
}else if(angle>=112.5&&angle<157.5)
{
q=this->m_sobel.at<uchar>(i+1,j-1);
r=this->m_sobel.at<uchar>(i-1,j+1);
}
if(this->m_sobel.at<uchar>(i,j)>=q && this->m_sobel.at<uchar>(i,j)>=r)
{
this->m_nms.at<uchar>(i,j)=this->m_sobel.at<uchar>(i,j);
}
}
}
}
void canny::Delay_thread()
{
int rows=this->m_nms.rows;
int cols=this->m_nms.cols;
uchar low_t=20;
uchar high_t=90;
for(int i=1;i<rows-1;++i)
{
for(int j=1;j<cols-1;++j)
{
if(this->m_nms.at<uchar>(i,j)>=high_t)
this->m_thread.at<uchar>(i,j)=255;
else if(this->m_nms.at<uchar>(i,j)>=low_t)
{
if(this->m_nms.at<uchar>(i-1,j-1)>=high_t||this->m_nms.at<uchar>(i-1,j)>=high_t||this->m_nms.at<uchar>(i-1,j+1)>=high_t||
this->m_nms.at<uchar>(i+1,j-1)>=high_t||this->m_nms.at<uchar>(i+1,j)>=high_t||this->m_nms.at<uchar>(i+1,j+1)>=high_t||
this->m_nms.at<uchar>(i,j-1)>=high_t||this->m_nms.at<uchar>(i,j+1)>=high_t)
{
this->m_thread.at<uchar>(i,j)=this->m_nms.at<uchar>(i,j);
}
}
}
}
}
测试
#include<iostream>
#include"canny.h"
int main()
{
canny src("/home/wjf2023/WorkStation/code/canny/mouse_cat.jpeg");
src.Gaussian_filter(0.6);//高斯滤波
src.Sobel();//Sobel算梯度和梯度方向
src.NMS();//非极大值抑制
src.Delay_thread();//迟滞阈值法筛选
src.Show();
return 0;
}