//////////非极大值抑制
/////////权重的选取,离得近权重大
///////////////////////
IplImage*N;//非极大值抑制结果
N = cvCreateImage(cvGetSize(ColorImage), ColorImage->depth, 1);
IplImage*OpencvCannyimg;//非极大值抑制的结果
OpencvCannyimg = cvCreateImage(cvGetSize(ColorImage), ColorImage->depth, 1);
int g1 = 0, g2 = 0, g3 = 0, g4 = 0;//用于进行插值,得到亚像素点坐标值
double dTmp1 = 0.0, dTmp2 = 0.0;//保存两个亚像素点插值得到的灰度数据
double dWeight = 0.0;//插值的权重
for (int i = 1; i < nWidth; i++)
{
for (int j = 1; j < nHeight - 1; j++)
{
//如果当前点梯度为0,该点就不是边缘点
if (M[i + j*nWidth] == 0)
{
N->imageData[i + j*nwidthstep] = 0;
}
else
{
///////////首先判断属于那种情况,然后根据情况插值
///////////第一种情况
/////////g1 g2
//////// c
/////// g3 g4/////
if ((Theta[i + j*nWidth] >= 90 & Theta[i + j*nWidth] < 135) || (Theta[i + j*nWidth] >= 270 & Theta[i + j*nWidth] < 315))
{
//根据斜率和四个中间值进行插值求解
g1 = M[i - 1 + (j - 1)*nWidth];
g2 = M[i + (j - 1)*nWidth];
g3 = M[i + (j - 1)*nWidth];
g4 = M[i + 1 + (j + 1)*nWidth];
dWeight = fabs(p[i + j*nWidth]) / fabs(Q[i + j*nWidth]);//反正切
dTmp1 = g1*dWeight + (1 - dWeight)*g2;
dTmp2 = g4*dWeight + (1 - dWeight)*g3;
}
///////////////////////////第二种情况
///////////////g1 ///////////
////////////g2 c g3/////////////
///////// g4/////////////////
else if ((Theta[i + j*nWidth] >= 135 && Theta[i + j*nWidth] < 180) || (Theta[i + j*nWidth] >= 315 && Theta[i + j*nWidth] < 360))
{
g1 = M[i - 1 + (j - 1)*nWidth];
g2 = M[i - 1 + (j)*nWidth];
g3 = M[i + 1 + (j)*nWidth];
g4 = M[i + 1 + (j + 1)*nWidth];
dWeight = fabs(Q[i + j*nWidth]) / fabs(p[i + j*nWidth]);//正切
dTmp1 = g1*dWeight + (1 - dWeight)*g2;
dTmp2 = g4*dWeight + (1 - dWeight)*g3;
}
//////////////////////第三种情况
///////////// g1 g2 /////////////
///////////// c
//////////// g4 g3 //////////////
///////////////////////////
else if ((Theta[i + j*nWidth] >= 45 && Theta[i + j*nWidth] < 90) || (Theta[i + j*nWidth] >= 225 && Theta[i + j*nWidth] < 270))
{
g1 = M[i + (j - 1)*nWidth];
g2 = M[(i +1)+ (j - 1)*nWidth];
g3 = M[i + (j + 1)*nWidth];
g4 = M[(i - 1) + (j + 1)*nWidth];
dWeight = fabs(p[i + j*nWidth]) / fabs(Q[i + 1 * nWidth]);//反正切
dTmp1 = g1*dWeight + (1 - dWeight)*g2;
dTmp2 = g4*dWeight + (1 - dWeight)*g3;
}
////////////第四种情况
/////////// g1
////////// g4 c g2
///////// g3
else if ((Theta[i + j*nWidth] >= 0 && Theta[i + j*nWidth] < 45) || (Theta[i + j*nWidth] >= 180 && Theta[i + j*nWidth] < 255))
{
g1 = M[(i + 1) + (j - 1)*nWidth];
g2 = M[(i + 1) + (j)*nWidth];
g3 = M[(i - 1 )+ (j)*nWidth];
g4 = M[(i - 1) + (j + 1)*nWidth];
dWeight = fabs(Q[i + j*nWidth]) / fabs(p[i + j*nWidth]);//正切
dTmp1 = g1*dWeight + (1 - dWeight)*g2;
dTmp2 = g4*dWeight + (1 - dWeight)*g3;
}
}
if ((M[i + j*nWidth] > dTmp1) && (M[i + j*nWidth] >= dTmp2))
{
N->imageData[i + j*nwidthstep] = 200;
}
else
N->imageData[i + j*nwidthstep] = 0;
}
}
//cvNamedWindow("Limteimg", 0);
//cvShowImage("Limteimg", N);
//cvWaitKey(0);
//cvDestroyWindow("Limteimg");
下一步,双阈值分割
构造灰度图的统计直方图,最大的梯度幅值为:
因此设置nHist为1024足够。
//双阈值分割
//是梯度幅值的阈值
////双阈值的选取是按照直方图来选择的,首先把梯度幅值的直方图求出来,按照灰度值从低到高排序,
//选取占直方图多少(代码是70%)所对应的梯度幅值为高阈值,高阈值的一般为低阈值
int nHist[1024];//直方图
int nEdgeNum;//所有边缘点的数目
int nMaxMag = 0;//最大梯度幅值
for (int k = 0; k < 1024; k++)
{
nHist[k] = 0;
}
for (int wx = 0; wx < nWidth; wx++)
{
for (int hy = 0; hy < nHeight; hy++)
{
if ((uchar)N->imageData[wx + hy*N->widthStep] == 200)
{
int Mindex = M[wx + hy*nWidth];
nHist[M[wx + hy*nWidth]]++;//获取了梯度直方图
}
}
}
//获取最大梯度幅值及潜在边缘点个数
nEdgeNum = nHist[0];
nMaxMag = 0; //获取最大的梯度值
for (int index = 1; index < 1024; index++)//统计经过“非最大值抑制”后有多少像素
{
if (nHist[index] != 0)//梯度为0的点是不可能为边界点的
{
nMaxMag = index;
}
nEdgeNum += nHist[index];//经过non-maxium suppression后有多少边缘点像素
}
//计算两个阈值,注意是梯度阈值
int nThrHigh;
int nThrlow;
double dReateHigh = 0.7;
double dReateLow = 0.5;
int nHightcount = (int)(dReateHigh* nEdgeNum + 0.5);
int count = 1;
nEdgeNum = nHist[1];
while ((nEdgeNum <= nHightcount) && (count < nMaxMag - 1))
{
count++;
nEdgeNum += nHist[count];
}
nThrHigh = count;//高阈值
nThrlow = (int)(nThrHigh*dReateLow + 0.5);//低阈值
printf("\n直方图的长度 %d \n", nMaxMag);
printf("\n梯度的阈值幅值 大阈值%d 小阈值%d \n", nThrHigh, nThrlow);
边缘检测
/////边缘检测
//TraceEdge是一个嵌套函数,用于在每个像素点的领域内寻找满足条件的点
void TraceEdge(int w, int h, int nThrlow, IplImage*pResult, int *pMag)
{
//对8邻域像素进行查询
int xNum[8] = { 1,1,0,-1,-1,-1,0,1 };
int yNum[8] = { 0,1,1,1,0,-1,-1,-1 };
int xx = 0;
int yy = 0;
int k = 0;
//bool change = true;
//while (change)
//{
//change = false;
for (int k = 0; k < 8; k++)
{
xx = w + xNum[k];
yy = h + yNum[k];
//如果该像素为可能的边界点,又没有处理过,并且梯度大于阈值
int curgrayvalue = (uchar)pResult->imageData[xx + yy*pResult->widthStep];
int curgrdvalue = pMag[xx + yy*pResult->width];
if (curgrayvalue == 200 && curgrdvalue > nThrlow)
{
change = true;
//把该点设置为边界点
pResult->imageData[xx + yy*pResult->widthStep] = 255;
//w = xx;
//h = yy;
//TraceEdge(xx, yy, nThrlow, IplImage*pResult, int *pMag);
}
//}
}
//在非极大值抑制产生的二值灰度矩阵的潜在点中按照高阈值寻找边缘
//并以所找到的点为中心寻找邻域内满足低阈值的点,从而形成一个闭合的轮廓
for (int is = 1; is < nWidth - 1; is++)
{
for (int jt = 0; jt < nHeight; jt++)
{
//cvScalar s=cvGet2D(n,jt,is);
//int currentvalue=s.val[0];
int currentvalue = (uchar)(N->imageData[is + jt*N->widthStep]);
if ((currentvalue == 200) && (M[is + jt*nWidth] >= nThrHigh))
//是非最大抑制后的点且梯度幅值要大于高阈值
{
N->imageData[is + jt*nwidthstep] = 255;
//邻域点判断
TraceEdge(is,jt,nThrlow,N,M);
}
}
}
//对于不满足的点,用下列代码进行删除
for (int si =1 ; si <nWidth; si++)
{
for (int tj = 1; tj < nHeight - 1; tj++)
{
if ((uchar)N->imageData[si + tj*nwidthstep] != 255)
{
N->imageData[si + tj*nwidthstep] = 0;//设为非边界点
}
}
}
}