边缘检测算子
Sobel算子
常用的Sobel边缘检测算子有两种,一种是常用的,另一种的改进的,就是对Sobel算子中的2取其平方根作为新的值,也就是如下:
通常的Sobel算子:
水平检测算子:
-1
-2 -1
0 0 0
1 2 1
垂直检测算子:
-1 0 1
-2 0 2
-1 0 1
改进后的各向同性的Sobel算子:
水平检测算子:
-1 -sqrt(2) -1
0 0 0
1 sqrt(2) 1
垂直检测算子:
-1 0 1
-sqrt(2) 0 sqrt(2)
-1 0 1
LOG算子
也就是高斯拉普拉斯算子,也可以叫做墨西哥草帽检测算子,有3*3和5*5两种,比较常用的是5*5的。算子如下:
double *Temp=new double[25];
Temp[0]=Temp[4]=-2;
Temp[1]=Temp[2]=Temp[3]=-4;
Temp[5]=-4;
Temp[6]=0;
Temp[7]=8;
Temp[8]=0;
Temp[9]=Temp[10]=-4;
Temp[11]=8;
Temp[12]=24;
Temp[13]=8;
Temp[14]=-4;
Temp[15]=-4;
Temp[16]=0;
Temp[17]=8;
Temp[18]=0;
Temp[19]=-4;
Temp[20]=-2;
Temp[21]=Temp[22]=Temp[23]=-4;
Temp[24]=-2;
Temp[0]=Temp[4]=-2;
Temp[1]=Temp[2]=Temp[3]=-4;
Temp[5]=-4;
Temp[6]=0;
Temp[7]=8;
Temp[8]=0;
Temp[9]=Temp[10]=-4;
Temp[11]=8;
Temp[12]=24;
Temp[13]=8;
Temp[14]=-4;
Temp[15]=-4;
Temp[16]=0;
Temp[17]=8;
Temp[18]=0;
Temp[19]=-4;
Temp[20]=-2;
Temp[21]=Temp[22]=Temp[23]=-4;
Temp[24]=-2;
对图像进行模板操作的函数
要对图像进行的很多操作,其实都可以归结到对图形进行模板操作上面,比如图像的锐化和滤波,当然还有经典的边缘检测算子了,就是Sobel、LOG算子等。所以,我们有必要写一个对图形进行模板操作的函数,这样后面就能避免不少的麻烦。另外,我这里的模板程序种只考虑了8位的位图,对于24位的位图需要先转化为24位的灰度图,然后对其进行边缘检测。不过我想如果分别对RGB各分量进行边缘检测的结果会如何呢?还真的不知道,有时间尝试下。目前我们所说的边缘检测,通常都是对灰度图像来说的。
#include "math.h"
/******************************************************************************
*函数功能:对一副图像进行模板操作
*函数声明:void TemplateTIM(
BYTE* srcImage, -指向源图像像素数据的指针
BYTE* dstImage, -指向目的图像像素数据的指针
LONG imageWidth, -源图像的宽度
LONG imageHeight, -源图像的高度
int nTempSize, -模板的大小
double* TempData, -指向模板数组的指针
double TempCoef, -模板的系数
BYTE BitCount, -图片的位数
)
******************************************************************************/
void TemplateTIM(BYTE* srcImage,BYTE *dstImage,LONG imageWidth,LONG imageHeight,
int nTempSize,double* TempData,double TempCoef,BYTE BitCount=8)
{
//检查模板的大小不能为偶数,且不为1
if(nTempSize%2==0 || nTempSize==1)
{
AfxMessageBox("模板的大小需为奇数且不为1");
return;
}
*函数功能:对一副图像进行模板操作
*函数声明:void TemplateTIM(
BYTE* srcImage, -指向源图像像素数据的指针
BYTE* dstImage, -指向目的图像像素数据的指针
LONG imageWidth, -源图像的宽度
LONG imageHeight, -源图像的高度
int nTempSize, -模板的大小
double* TempData, -指向模板数组的指针
double TempCoef, -模板的系数
BYTE BitCount, -图片的位数
)
******************************************************************************/
void TemplateTIM(BYTE* srcImage,BYTE *dstImage,LONG imageWidth,LONG imageHeight,
int nTempSize,double* TempData,double TempCoef,BYTE BitCount=8)
{
//检查模板的大小不能为偶数,且不为1
if(nTempSize%2==0 || nTempSize==1)
{
AfxMessageBox("模板的大小需为奇数且不为1");
return;
}
//计算应该除去的图像边缘的列数
int n=(nTempSize-1)/2;
int n=(nTempSize-1)/2;
//保存计算的像素值加权后的值
double res;
double res;
if(BitCount==8)
{
//从图像的左下角进行模板操作
for(int i=n;i<imageHeight-n;i++)
{
for(int j=n;j<imageWidth-n;j++)
{
//设置累计变量的值为0
res=0.0;
{
//从图像的左下角进行模板操作
for(int i=n;i<imageHeight-n;i++)
{
for(int j=n;j<imageWidth-n;j++)
{
//设置累计变量的值为0
res=0.0;
//计算模板内的各像素点的值
for(int k=0;k<nTempSize;k++)
{
for(int l=0;l<nTempSize;l++)
{
BYTE pixel=*(srcImage+imageWidth*(i+n-k)+(j-n+l));
res+=pixel*TempData[k*nTempSize+l];
}
}
for(int k=0;k<nTempSize;k++)
{
for(int l=0;l<nTempSize;l++)
{
BYTE pixel=*(srcImage+imageWidth*(i+n-k)+(j-n+l));
res+=pixel*TempData[k*nTempSize+l];
}
}
//根据模板系数计算新的像素值
res*=TempCoef;
res=(double)fabs(res);
if(res>255)
{
*(dstImage+imageWidth*i+j)=255;
}
else
{
*(dstImage+imageWidth*i+j)=(BYTE)(res+0.5);
}
}
}
}
else if(BitCount==24)
{
AfxMessageBox("暂时不支持24位图片");
return;
}
else
{
AfxMessageBox("暂时只能处理8或24位图片");
return;
}
}
res*=TempCoef;
res=(double)fabs(res);
if(res>255)
{
*(dstImage+imageWidth*i+j)=255;
}
else
{
*(dstImage+imageWidth*i+j)=(BYTE)(res+0.5);
}
}
}
}
else if(BitCount==24)
{
AfxMessageBox("暂时不支持24位图片");
return;
}
else
{
AfxMessageBox("暂时只能处理8或24位图片");
return;
}
}
Visual C++6.0中进行数字图像的边缘检测试验
新建一个基于对话框的MFC应用程序
如何创建在这里就不啰嗦了。当然,创建之后需要添加按钮,而且还需要一个选择用哪个Sobel算子的对话框,这里不会详细的说明,主要在于模板操作函数和如何调用该函数。至于其他的繁琐的操作都是基本功,不说了。
Sobel检测算子按钮下的程序
void CICETIMDlg::OnBtnSobeledge()
{
// TODO: Add your control notification handler code here
if(m_dib.GetHeight()==0)
{
AfxMessageBox("请先打开图片");
return;
}
int sel;
CSobelArgumentDlg *dlg=new CSobelArgumentDlg();
if(dlg->DoModal()==IDOK)
{
sel=dlg->m_sel;
}
else
{
return;
}
delete dlg;
{
// TODO: Add your control notification handler code here
if(m_dib.GetHeight()==0)
{
AfxMessageBox("请先打开图片");
return;
}
int sel;
CSobelArgumentDlg *dlg=new CSobelArgumentDlg();
if(dlg->DoModal()==IDOK)
{
sel=dlg->m_sel;
}
else
{
return;
}
delete dlg;
double *Temp=new double[9];
switch(sel)
{
case 1:
Temp[0]=-1;
Temp[1]=-2;
Temp[2]=-1;
Temp[3]=0;
Temp[4]=0;
Temp[5]=0;
Temp[6]=1;
Temp[7]=2;
Temp[8]=1;
break;
case 2:
Temp[0]=-1;
Temp[1]=0;
Temp[2]=1;
Temp[3]=-2;
Temp[4]=0;
Temp[5]=2;
Temp[6]=-1;
Temp[7]=0;
Temp[8]=1;
break;
case 3:
Temp[0]=-1;
Temp[1]=-sqrt(2);
Temp[2]=-1;
Temp[3]=0;
Temp[4]=0;
Temp[5]=0;
Temp[6]=1;
Temp[7]=sqrt(2);
Temp[8]=1;
break;
case 4:
Temp[0]=-1;
Temp[1]=0;
Temp[2]=1;
Temp[3]=-sqrt(2);
Temp[4]=0;
Temp[5]=sqrt(2);
Temp[6]=-1;
Temp[7]=0;
Temp[8]=1;
break;
default:
Temp[0]=-1;
Temp[1]=-2;
Temp[2]=-1;
Temp[3]=0;
Temp[4]=0;
Temp[5]=0;
Temp[6]=1;
Temp[7]=2;
Temp[8]=1;
break;
}
switch(sel)
{
case 1:
Temp[0]=-1;
Temp[1]=-2;
Temp[2]=-1;
Temp[3]=0;
Temp[4]=0;
Temp[5]=0;
Temp[6]=1;
Temp[7]=2;
Temp[8]=1;
break;
case 2:
Temp[0]=-1;
Temp[1]=0;
Temp[2]=1;
Temp[3]=-2;
Temp[4]=0;
Temp[5]=2;
Temp[6]=-1;
Temp[7]=0;
Temp[8]=1;
break;
case 3:
Temp[0]=-1;
Temp[1]=-sqrt(2);
Temp[2]=-1;
Temp[3]=0;
Temp[4]=0;
Temp[5]=0;
Temp[6]=1;
Temp[7]=sqrt(2);
Temp[8]=1;
break;
case 4:
Temp[0]=-1;
Temp[1]=0;
Temp[2]=1;
Temp[3]=-sqrt(2);
Temp[4]=0;
Temp[5]=sqrt(2);
Temp[6]=-1;
Temp[7]=0;
Temp[8]=1;
break;
default:
Temp[0]=-1;
Temp[1]=-2;
Temp[2]=-1;
Temp[3]=0;
Temp[4]=0;
Temp[5]=0;
Temp[6]=1;
Temp[7]=2;
Temp[8]=1;
break;
}
if(m_dib.GetBitCount()==8)
{
BYTE* newImage=new BYTE[m_dib.GetHeight()*m_dib.GetLineBytes()];
TemplateTIM(m_dib.GetDibData(),newImage,m_dib.GetWidth(),m_dib.GetHeight(),3,Temp,(double)1);
m_dib.SetDibData(newImage);
ShowImage(m_dib,"SOBEL边缘检测");
delete[] Temp;
}
else
{
AfxMessageBox("暂时只处理8位位图");
return;
}
}
{
BYTE* newImage=new BYTE[m_dib.GetHeight()*m_dib.GetLineBytes()];
TemplateTIM(m_dib.GetDibData(),newImage,m_dib.GetWidth(),m_dib.GetHeight(),3,Temp,(double)1);
m_dib.SetDibData(newImage);
ShowImage(m_dib,"SOBEL边缘检测");
delete[] Temp;
}
else
{
AfxMessageBox("暂时只处理8位位图");
return;
}
}
LOG检测算子按钮下的程序
void CICETIMDlg::OnBtnLogedge()
{
// TODO: Add your control notification handler code here
if(m_dib.GetHeight()==0)
{
AfxMessageBox("请先打开图片");
return;
}
{
// TODO: Add your control notification handler code here
if(m_dib.GetHeight()==0)
{
AfxMessageBox("请先打开图片");
return;
}
double *Temp=new double[25];
Temp[0]=Temp[4]=-2;
Temp[1]=Temp[2]=Temp[3]=-4;
Temp[5]=-4;
Temp[6]=0;
Temp[7]=8;
Temp[8]=0;
Temp[9]=Temp[10]=-4;
Temp[11]=8;
Temp[12]=24;
Temp[13]=8;
Temp[14]=-4;
Temp[15]=-4;
Temp[16]=0;
Temp[17]=8;
Temp[18]=0;
Temp[19]=-4;
Temp[20]=-2;
Temp[21]=Temp[22]=Temp[23]=-4;
Temp[24]=-2;
Temp[0]=Temp[4]=-2;
Temp[1]=Temp[2]=Temp[3]=-4;
Temp[5]=-4;
Temp[6]=0;
Temp[7]=8;
Temp[8]=0;
Temp[9]=Temp[10]=-4;
Temp[11]=8;
Temp[12]=24;
Temp[13]=8;
Temp[14]=-4;
Temp[15]=-4;
Temp[16]=0;
Temp[17]=8;
Temp[18]=0;
Temp[19]=-4;
Temp[20]=-2;
Temp[21]=Temp[22]=Temp[23]=-4;
Temp[24]=-2;
if(m_dib.GetBitCount()==8)
{
BYTE* newImage=new BYTE[m_dib.GetHeight()*m_dib.GetLineBytes()];
TemplateTIM(m_dib.GetDibData(),newImage,m_dib.GetWidth(),m_dib.GetHeight(),5,Temp,(double)1);
m_dib.SetDibData(newImage);
ShowImage(m_dib,"LOG边缘检测");
delete[] Temp;
}
else
{
AfxMessageBox("暂时只处理8位位图");
return;
}
}
{
BYTE* newImage=new BYTE[m_dib.GetHeight()*m_dib.GetLineBytes()];
TemplateTIM(m_dib.GetDibData(),newImage,m_dib.GetWidth(),m_dib.GetHeight(),5,Temp,(double)1);
m_dib.SetDibData(newImage);
ShowImage(m_dib,"LOG边缘检测");
delete[] Temp;
}
else
{
AfxMessageBox("暂时只处理8位位图");
return;
}
}
选择Sobel检测算子对话框
在OnInitialDialog函数中需要设置默认选项:
BOOL CSobelArgumentDlg::OnInitDialog()
{
CDialog::OnInitDialog();
// TODO: Add extra initialization here
((CButton*)GetDlgItem(IDC_RADIO2))->SetCheck(true);
return TRUE; // return TRUE unless you set the focus to a control
// EXCEPTION: OCX Property Pages should return FALSE
}
{
CDialog::OnInitDialog();
// TODO: Add extra initialization here
((CButton*)GetDlgItem(IDC_RADIO2))->SetCheck(true);
return TRUE; // return TRUE unless you set the focus to a control
// EXCEPTION: OCX Property Pages should return FALSE
}
需要新建一个整形的成员变量m_sel,然后在每个单选按钮下面写下如下的程序:
void CSobelArgumentDlg::OnRadio2()
{
// TODO: Add your control notification handler code here
m_sel=1;
}
{
// TODO: Add your control notification handler code here
m_sel=1;
}
void CSobelArgumentDlg::OnRadio3()
{
// TODO: Add your control notification handler code here
m_sel=2;
}
{
// TODO: Add your control notification handler code here
m_sel=2;
}
void CSobelArgumentDlg::OnRadio4()
{
// TODO: Add your control notification handler code here
m_sel=3;
}
{
// TODO: Add your control notification handler code here
m_sel=3;
}
void CSobelArgumentDlg::OnRadio5()
{
// TODO: Add your control notification handler code here
m_sel=4;
}
{
// TODO: Add your control notification handler code here
m_sel=4;
}
程序运行时截图
Sobel检测算子
原始图片如下,是8位的位图。