canny算子我看到很多都是介绍原理,实现用OpenCV函数实现。下面我用c++实现了一下canny算子。
原理参考CSDN博客,我觉得解释的很好CSDN博客
用高斯滤波器平滑图像
void Glbq(BYTE * im, int h, int w, BYTE * om) //用的是灰度图像,0-255,所以用BYTE*
{
int mask[9] = {1,1,1, ///高斯核
1,8,1,
1,1,1};
BYTE temp[9];//用来存储某个像素点的3*3邻域
int i, j, n, m;
for (i = 0; i < 9;i++)//初始化
{
temp[i]= 0;
}
memcpy(om, im, sizeof(BYTE)*w*h);//把输入图像复制到输出图像
for (i = 1; i < h-1; i++)//在图像的最外一层不进行循环
{
for (j = 1; j < w-1; j++)
{
for (m = -1; m < 2;m++)//选中一块3*3邻域
{
for (n = -1; n < 2;n++)
{
temp[(m+ 1) * 3 + (n + 1)] = im[(i + m)*w + j + n];
}
}
om[i*w + j] =convolution(temp, mask)/16+0.5;//这里调用的是一个卷积函数
}
}
}
卷积
int convolution(BYTE * temp, int * mask)
{
int i, sum{0};
for (i = 0; i < 9;i++)
{
sum+= temp[i] * mask[i];//3*3的邻域和3*3的模板对应相乘,相加。
}
return sum;
}
实现,包括梯度幅值和方向、非最大值抑制、双阈值连接
void Slbq(BYTE * im, int h, int w, BYTE * om,BYTE* om1)
{
//用sobel算子计算梯度幅值和方向
//下面是sobel算子的实现
int mask[9] = {-1,0,1, //Sobel算子
-2,0,2,
-1,0,1 };
int mask1[9] = {1,2,1, //Sobel算子
0,0,0,
-1,-2,-1};
BYTE temp[9];
int i, j, n, m;
for (i = 0; i < 9;i++)
{
temp[i]= 0;
}
double* ymw1 = new double[w*h];
double* ymw2 = new double[w*h];
for (i = 0; i < h*w; i++)
{
ymw1[i]= 0;
ymw2[i]= 0;
}
for (i = 1; i < h - 1; i++)
{
for (j = 1; j < w - 1; j++)
{
for (m = -1; m < 2;m++)
{
for (n = -1; n < 2;n++)
{
temp[(m+ 1) * 3 + (n + 1)] = im[(i + m)*w + j + n];//卷积
}
}
ymw1[i*w + j] =convolution(temp, mask);
ymw2[i*w + j] =convolution(temp, mask1);
}
}
//下面做计算梯度幅度值和方向,对梯度幅值进行非极大值抑制
double G,G1,G2,G3,G4,P,P2;
double T1, T2;
for (i = 1; i < h-1; i++)
{
for (j = 1; j < w-1; j++)
{
P= atan2(ymw2[i*w + j], ymw1[i*w + j])*180/PI;//方向,度数表示
P2= atan2(ymw2[i*w + j], ymw1[i*w + j]);//方向,弧度表示。
if ((P >= 90&& P < 135) || (P >= -90 && P < -45))//原理见那个网站
{
G= sqrt(ymw1[i*w + j] * ymw1[i*w + j] + ymw2[i*w + j] * ymw2[i*w + j]);
G1=sqrt(ymw1[(i-1)*w + j] * ymw1[(i-1)*w + j] + ymw2[(i-1)*w + j] * ymw2[(i-1)*w + j]);
G2=sqrt(ymw1[(i+1)*w + j] * ymw1[(i+1)*w + j] + ymw2[(i+1)*w + j] * ymw2[(i+1)*w + j]);
G3=sqrt(ymw1[(i-1)*w + j-1] *ymw1[(i-1)*w + j-1] +ymw2[(i-1)*w + j-1] *ymw2[(i-1)*w + j-1]);
G4=sqrt(ymw1[(i+1)*w+j+1] *ymw1[(i+1)*w+j+1]+ymw2[(i+1)*w+j+1] *ymw2[(i+1)*w+ j+1]);
T1= abs(1.0 / tan(P2))*G3 + (1 - abs(1.0 / tan(P2)))*G1;
T2= abs(1.0 / tan(P2))*G4 + (1 - abs(1.0 / tan(P2)))*G2;
}
else if ((P >= 0&& P < 45) || (P >= -180 && P < -135))
{
G= sqrt(ymw1[i*w + j] * ymw1[i*w + j] + ymw2[i*w + j] * ymw2[i*w + j]);
G1= sqrt(ymw1[(i)*w + j+1] * ymw1[(i)*w + j+1] + ymw2[(i)*w + j+1] * ymw2[(i)*w + j+1]);
G2= sqrt(ymw1[(i)*w + j-1] * ymw1[(i)*w + j-1] + ymw2[(i)*w + j-1] * ymw2[(i)*w + j-1]);
G3= sqrt(ymw1[(i - 1)*w+j+1]*ymw1[(i- 1)*w+j+1]+ymw2[(i- 1)*w+j+1]*ymw2[(i-1)*w+j+1]);
G4= sqrt(ymw1[(i + 1)*w+j-1]*ymw1[(i+1)*w+j-1]+ymw2[(i+1)*w+j -1]*ymw2[(i+ 1)*w + j - 1]);
T1= abs(tan(P2))*G3 + (1 - abs(tan(P2)))*G1;
T2= abs(tan(P2))*G4 + (1 - abs(tan(P2)))*G2;
}
else if ((P >= 45&& P < 90) || (P >= -135 && P < -90))
{
G = sqrt(ymw1[i*w + j] * ymw1[i*w + j] + ymw2[i*w + j] * ymw2[i*w + j]);
G1= sqrt(ymw1[(i - 1)*w + j] * ymw1[(i -1)*w + j] + ymw2[(i -1)*w + j] * ymw2[(i -1)*w + j]);
G2= sqrt(ymw1[(i + 1)*w + j] * ymw1[(i +1)*w + j] + ymw2[(i +1)*w + j] * ymw2[(i +1)*w + j]);
G3= sqrt(ymw1[(i - 1)*w+j+1]*ymw1[(i-1)*w+j+1] + ymw2[(i- 1)*w+j+1]*ymw2[(i- 1)*w+j+1]);
G4= sqrt(ymw1[(i+1)*w+j-1]*ymw1[(i+1)*w+j-1]+ymw2[(i+1)*w+j-1]*ymw2[(i+1)*w+j-1]);
T1= abs(1.0 / tan(P2))*G3 + (1 - abs(1.0 / tan(P2)))*G1;
T2= abs(1.0 / tan(P2))*G4 + (1 - abs(1.0 / tan(P2)))*G2;
}
else if ((P >= 135&& P <= 180) || (P >= -45 && P <= 0))
{
G= sqrt(ymw1[i*w + j] * ymw1[i*w + j] + ymw2[i*w + j] * ymw2[i*w + j]);
G1= sqrt(ymw1[(i)*w + j + 1] *ymw1[(i)*w + j + 1] +ymw2[(i)*w + j + 1] *ymw2[(i)*w + j + 1]);
G2= sqrt(ymw1[(i)*w + j - 1] *ymw1[(i)*w + j - 1] +ymw2[(i)*w + j - 1] *ymw2[(i)*w + j - 1]);
G3= sqrt(ymw1[(i+1)*w+j + 1] * ymw1[(i+1)*w+j+1]+ymw2[(i+1)*w+j+1]*ymw2[(i+1)*w+j+1]);
G4= sqrt(ymw1[(i-1)*w+j-1]*ymw1[(i-1)*w+j-1]+ymw2[(i-1)*w+j-1]*ymw2[(i-1)*w+j-1]);
T1= abs(tan(P2))*G3 + (1 - abs(tan(P2)))*G1;
T2= abs(tan(P2))*G4 + (1 - abs(tan(P2)))*G2;
}
if (G >= T1&& G >= T2)
{
if (G > 255)
outImg1[i*w + j] = 255;
else
outImg1[i*w + j] = G+0.5;
}
else
outImg1[i*w + j] = 0;
}
}
// 下面是双阈值和连接边缘。
for (i = 1; i < h-1; i++)
{
for (j = 1; j < w-1; j++)
{
if (outImg1[i*w + j] < 50)//小于最低阈值则为0
{
outImg1[i*w + j] = 0;
}
if (outImg1[i*w + j] >= 100)//大于最高阈值为255
outImg1[i*w + j] = 255;
if (outImg1[i*w + j] >= 50&& outImg1[i*w + j] < 100)//介于其间
{
int flag{ 0 };
for (m = -1; m < 2;m++)//检测其周围有无大于最大阈值的点,有则置为255
{ //没有则为0
for (n = -1; n < 2;n++)
{
if (outImg1[(i + m)*w + j + n] >= 100)
flag++;
}
}
if (flag > 0)
outImg1[i*w + j] = 255;
else
outImg1[i*w + j] = 0;
}
}
}
delete ymw1;
delete ymw2;
}
左上为原图,右上为高斯滤波后的图片,左下为本文canny算子实现的结果。
程序是没问题的,是我从完整程序中截取出来的,如果前面的东西不对,可能无法直接运行。
另外,本人水平有限,写的不一定是最优的,希望大家批评指教。