*Canny算子与Sobel算子求图像边缘笔记*
1、Canny求边缘算法原理简述
Canny检测边缘主要分为以下 四个算法步骤:
A:噪声去除
canny算子是通过对每个像素点求一阶导数来找到梯度明显的边缘,对图像中的噪声很敏感。所以用Canny算子对图像求导前,先用高斯滤波核函数对图像灰度矩阵的每一点进行加权求平均,以平滑图像去除噪声。这里采用
的高斯核对图像进行卷积实现高斯滤波。
B:求图像梯度
利用如下图的Canny算子对图像求X,Y方向梯度(数学意义上的X,Y方向的偏导),然后综合X,Y方向梯度计算该点的梯度,及梯度与X轴的夹角。
上图是算子,下图是梯度计算公式:
C:极大值抑制(代码中未实现)
其实就是找出局部的极大值,将非极大值设为0。
D: 双阈值提取与边缘连接
采用一大一小阈值对梯度图像进行过滤,摄取出两幅边缘图像, 一个边缘噪声少,但有可能损失部分边缘,一个边缘噪声多,但边缘比前者充分。再通过类似求联通区域的方法连接两图像,得到边缘。
C代码实现如下
(这只是一个很粗陋的代码实现,上班之余的小练习,如错误欢迎指正):
{
/*先高斯滤波*/
/*求对应像素点四个方向的梯度幅值与方向*/
/*非极大值抑制*/
/*双阀值边缘连接*/
S16 asGaussFilter[E_3x3_KERNEL][E_3x3_KERNEL] = {{1, 2, 1},
{2, 4, 2},
{1, 2, 1}};
U8 *pucGaussBuf = pucTmpBuf;
PU16 pusCnyGrdBuf = (PU16)(pucGaussBuf + usImgStep*usImgHgt*sizeof(U8)); /*梯度大小*/
PU16 pusCnyCnrBuf = (PU16)(pusCnyGrdBuf + usImgStep*usImgHgt*sizeof(U8)); /*梯度角度*/
U8 *pucCnyGrdBuf3 = (PU8)(pusCnyCnrBuf) + usImgStep*usImgHgt*sizeof(U16);
U8 *pucCnyGrdBuf4 = pucCnyGrdBuf3 + usImgStep*usImgHgt*sizeof(U8);
U16 *pusCnyStkPosY = (U16 *)(pucCnyGrdBuf4 + usImgStep * usImgHgt * sizeof(U8));
U16 *pusCnyStkPosX = pusCnyStkPosY + usImgStep * usImgHgt;
U16 usKnlWth = E_3x3_KERNEL;
U16 usKnlHgt = E_3x3_KERNEL;
S16 sThreshCny1 = 20;
S16 sThreshCny2 = 30;
U8 ucSrlNum = 1;
U32 uiStackCnt = 0;
/*高斯滤波*/
for ( usYIdx = 0 ; usYIdx < usImgHgt ; usYIdx++ )
{
for ( usXIdx = 0 ; usXIdx < usImgWth ; usXIdx++ )
{
uiXYIdx = (U32)(usYIdx * usImgStep + usXIdx);
if( 0 == usXIdx ||
0 == usYIdx ||
usImgWth - 1 == usXIdx ||
usImgHgt - 1 == usYIdx )
{
pucGaussBuf[uiXYIdx] = pucYImgIn[uiXYIdx];
}
else
{
U16 usGaussTmpVal = 0;
U8 *pucBuf = pucYImgIn + uiXYIdx - usImgStep - 1;
for ( usKYIdx = 0 ; usKYIdx < usKnlHgt ; usKYIdx++ )
{
for ( usKXIdx = 0 ; usKXIdx < usKnlWth ; usKXIdx++ )
{
usGaussTmpVal += (U16)(pucBuf[usKYIdx * usImgStep + usKXIdx]) * (U16)(asGaussFilter[usKYIdx][usKXIdx]);
}
}
pucGaussBuf[uiXYIdx] = ((usGaussTmpVal >> 4) > 255) ? (255) : (usGaussTmpVal >> 4);
}
}
}
memset(pucCnyGrdBuf3, 0, usImgStep*usImgHgt*sizeof(U8));
memset(pucCnyGrdBuf4, 0, usImgStep*usImgHgt*sizeof(U8));
/*Canny求梯度*/
for(usYIdx = 0; usYIdx < usImgHgt; usYIdx++)
{
for(usXIdx = 0; usXIdx < usImgWth; usXIdx++)
{
uiXYIdx = (U32)(usYIdx*usImgStep+usXIdx);
if(usImgWth - 1 == usXIdx || usImgHgt - 1 == usYIdx)
{
pusCnyGrdBuf[uiXYIdx] = 0;
pusCnyCnrBuf[uiXYIdx] = 0;
}
else
{
PU8 pucTisBuf = pucGaussBuf + uiXYIdx;
PU8 pucDwBuf = pucGaussBuf + uiXYIdx + usImgStep;
PU8 pucRgtBuf = pucGaussBuf + uiXYIdx + 1;
PU8 pucRgtDwBuf = pucGaussBuf + uiXYIdx + usImgStep + 1;
S32 iHorGrad = abs((int)(pucRgtBuf[0] +pucRgtDwBuf[0])-(int)(pucTisBuf[0]+pucDwBuf[0]))/2;
S32 iVerGrad = abs((int)(pucRgtDwBuf[0]+pucDwBuf[0]) -(int)(pucTisBuf[0]+pucRgtBuf[0]))/2;
FL fGrdCnr = atan2(iVerGrad, iHorGrad)*57.3;
pusCnyGrdBuf[uiXYIdx] = (U16)((S32)(sqrt((FL)(iHorGrad*iHorGrad)+(FL)(iVerGrad*iVerGrad))));
pusCnyCnrBuf[uiXYIdx] = (fGrdCnr < 0) ? (fGrdCnr+360.0) : (fGrdCnr);
if(pusCnyGrdBuf[uiXYIdx] > (U16)(sThreshCny2))
{
pucCnyGrdBuf3[uiXYIdx] = 255;
}
else if(pusCnyGrdBuf[uiXYIdx] > (U16)(sThreshCny1))
{
pucCnyGrdBuf4[uiXYIdx] = 255;
}
}
}
}
memset(pucYImgOut, 0, sizeof(U8)*usImgStep*usImgHgt);
/*边缘连接*/
for(usYIdx = 0; usYIdx < usImgHgt; usYIdx++ )
{
for(usXIdx = 0; usXIdx < usImgWth; usXIdx++)
{
uiXYIdx = (U32)(usYIdx * usImgStep + usXIdx);
if(0 == usXIdx ||
0 == usYIdx ||
usImgWth - 1 == usXIdx ||
usImgHgt - 1 == usYIdx )
{
/*不处理*/
}
else
{
if(255 == pucCnyGrdBuf3[uiXYIdx])
{
uiStackCnt = 0;
pusCnyStkPosY[uiStackCnt] = usYIdx;
pusCnyStkPosX[uiStackCnt] = usXIdx;
uiStackCnt += 1;
pucCnyGrdBuf3[uiXYIdx] = ucSrlNum;
while(0 != uiStackCnt)
{
U16 usYTmpIdx = 0;
U16 usXTmpIdx = 0;
U32 uiTmpXYIdx = 0;
U32 uiTmpIdx = 0;
uiStackCnt--;
usYTmpIdx = pusCnyStkPosY[uiStackCnt];
usXTmpIdx = pusCnyStkPosX[uiStackCnt];
uiTmpIdx = (U32)(usYTmpIdx * usImgStep + usXTmpIdx);
/*左上*/
uiTmpXYIdx = (U32)(uiTmpIdx - usImgStep - 1);
if(255 == pucCnyGrdBuf3[uiTmpXYIdx])
{
pusCnyStkPosY[uiStackCnt] = usYTmpIdx - 1;
pusCnyStkPosX[uiStackCnt] = usXTmpIdx - 1;
pucYImgOut[uiTmpXYIdx] = 255;
pucCnyGrdBuf3[uiTmpXYIdx] = ucSrlNum;
uiStackCnt += 1;
}
else if(255 == pucCnyGrdBuf4[uiTmpXYIdx])
{
pusCnyStkPosY[uiStackCnt] = usYTmpIdx - 1;
pusCnyStkPosX[uiStackCnt] = usXTmpIdx - 1;
pucCnyGrdBuf4[uiTmpXYIdx] = ucSrlNum;
pucYImgOut[uiTmpXYIdx] = 255;
uiStackCnt += 1;
}
/*左*/
uiTmpXYIdx = (U32)(uiTmpIdx - 1);
if(255 == pucCnyGrdBuf3[uiTmpXYIdx])
{
pusCnyStkPosY[uiStackCnt] = usYTmpIdx;
pusCnyStkPosX[uiStackCnt] = usXTmpIdx - 1;
pucYImgOut[uiTmpXYIdx] = 255;
pucCnyGrdBuf3[uiTmpXYIdx] = ucSrlNum;
uiStackCnt += 1;
}
else if( 255 == pucCnyGrdBuf4[uiTmpXYIdx] )
{
pusCnyStkPosY[uiStackCnt] = usYTmpIdx;
pusCnyStkPosX[uiStackCnt] = usXTmpIdx - 1;
pucYImgOut[uiTmpXYIdx] = 255;
pucCnyGrdBuf4[uiTmpXYIdx] = ucSrlNum;
uiStackCnt += 1;
}
/*左下*/
uiTmpXYIdx = (U32)(uiTmpIdx + usImgStep - 1);
if(255 == pucCnyGrdBuf3[uiTmpXYIdx])
{
pusCnyStkPosY[uiStackCnt] = usYTmpIdx + 1;
pusCnyStkPosX[uiStackCnt] = usXTmpIdx - 1;
pucYImgOut[uiTmpXYIdx] = 255;
pucCnyGrdBuf3[uiTmpXYIdx] = ucSrlNum;
uiStackCnt += 1;
}
else if(255 == pucCnyGrdBuf4[uiTmpXYIdx])
{
pusCnyStkPosY[uiStackCnt] = usYTmpIdx + 1;
pusCnyStkPosX[uiStackCnt] = usXTmpIdx - 1;
pucYImgOut[uiTmpXYIdx] = 255;
pucCnyGrdBuf4[uiTmpXYIdx] = ucSrlNum;
uiStackCnt += 1;
}
/*下*/
uiTmpXYIdx = (U32)(uiTmpIdx + usImgStep);
if(255 == pucCnyGrdBuf3[uiTmpXYIdx])
{
pusCnyStkPosY[uiStackCnt] = usYTmpIdx + 1;
pusCnyStkPosX[uiStackCnt] = usXTmpIdx;
pucYImgOut[uiTmpXYIdx] = 255;
pucCnyGrdBuf3[uiTmpXYIdx] = ucSrlNum;
uiStackCnt += 1;
}
else if(255 == pucCnyGrdBuf4[uiTmpXYIdx])
{
pusCnyStkPosY[uiStackCnt] = usYTmpIdx + 1;
pusCnyStkPosX[uiStackCnt] = usXTmpIdx;
pucYImgOut[uiTmpXYIdx] = 255;
pucCnyGrdBuf4[uiTmpXYIdx] = ucSrlNum;
uiStackCnt += 1;
}
/*右下*/
uiTmpXYIdx = (U32)(uiTmpIdx + usImgStep + 1);
if(255 == pucCnyGrdBuf3[uiTmpXYIdx])
{
pusCnyStkPosY[uiStackCnt] = usYTmpIdx + 1;
pusCnyStkPosX[uiStackCnt] = usXTmpIdx + 1;
pucYImgOut[uiTmpXYIdx] = 255;
pucCnyGrdBuf3[uiTmpXYIdx] = ucSrlNum;
uiStackCnt += 1;
}
else if(255 == pucCnyGrdBuf4[uiTmpXYIdx])
{
pusCnyStkPosY[uiStackCnt] = usYTmpIdx + 1;
pusCnyStkPosX[uiStackCnt] = usXTmpIdx + 1;
pucYImgOut[uiTmpXYIdx] = 255;
pucCnyGrdBuf4[uiTmpXYIdx] = ucSrlNum;
uiStackCnt += 1;
}
/*右*/
uiTmpXYIdx = (U32)(uiTmpIdx + 1);
if(255 == pucCnyGrdBuf3[uiTmpXYIdx])
{
pusCnyStkPosY[uiStackCnt] = usYTmpIdx;
pusCnyStkPosX[uiStackCnt] = usXTmpIdx + 1;
pucYImgOut[uiTmpXYIdx] = 255;
pucCnyGrdBuf3[uiTmpXYIdx] = ucSrlNum;
uiStackCnt += 1;
}
else if(255 == pucCnyGrdBuf3[uiTmpXYIdx])
{
pusCnyStkPosY[uiStackCnt] = usYTmpIdx;
pusCnyStkPosX[uiStackCnt] = usXTmpIdx + 1;
pucYImgOut[uiTmpXYIdx] = 255;
pucCnyGrdBuf4[uiTmpXYIdx] = ucSrlNum;
uiStackCnt += 1;
}
/*右上*/
uiTmpXYIdx = (U32)(uiTmpIdx - usImgStep + 1);
if(255 == pucCnyGrdBuf3[uiTmpXYIdx])
{
pusCnyStkPosY[uiStackCnt] = usYTmpIdx - 1;
pusCnyStkPosX[uiStackCnt] = usXTmpIdx + 1;
pucYImgOut[uiTmpXYIdx] = 255;
pucCnyGrdBuf3[uiTmpXYIdx] = ucSrlNum;
uiStackCnt += 1;
}
else if(255 == pucCnyGrdBuf4[uiTmpXYIdx])
{
pusCnyStkPosY[uiStackCnt] = usYTmpIdx - 1;
pusCnyStkPosX[uiStackCnt] = usXTmpIdx + 1;
pucYImgOut[uiTmpXYIdx] = 255;
pucCnyGrdBuf4[uiTmpXYIdx] = ucSrlNum;
uiStackCnt += 1;
}
}
}
}
}
}
}
实现效果如下:
2、Soble边缘检测算法:
算法的大致流程跟Canny类似,只是算子改成:
分别对图像做卷积,求出每个点Y,X方向梯度,再求出综合的梯度及梯度方向。
感谢:
http://www.cnblogs.com/lancidie/archive/2011/07/17/2108885.html
http://blog.csdn.net/likezhaobin/article/details/6892176