数字图像处理图像缩放的VC++实现.docx
《数字图像处理图像缩放的VC++实现.docx》由会员分享,可在线阅读,更多相关《数字图像处理图像缩放的VC++实现.docx(16页珍藏版)》请在冰豆网上搜索。
数字图像处理图像缩放的VC++实现
图像缩放的VC++实现
实验要求
1.试用最邻近插值和双线性插值对所给图像进行缩小和放大操作。
先缩小12倍,再放大12倍。
2.描述图像的变化,分析变化的原因,并给出对两种插值方法的评价。
实验原理
1.最邻近插值(近邻取样法)
最邻近插值是一种简单的插值算法,也称为零阶插值。
它输出的像素灰度值就等于距离它映射到的位置最近的输入像素的灰度值。
对于通过反向变换得到的的一个浮点坐标,对其进行简单的取整,得到一个整数型坐标,这个整数型坐标对应的像素值就是目的像素的像素值,也就是说,取浮点坐标最邻近的左上角点(对于DIB是右上角,因为它的扫描行是逆序存储的)对应的像素值。
可见,最邻近插值简单且直观,但得到的图像质量不高;当图像中包含像素之间灰度级有变化的细微结构时,最邻近查值法会在图像中产生人为的加工痕迹。
2.双线性插值
对于一个目的像素,设置坐标通过反向变换得到的浮点坐标为(i+u,j+v),其中i、j均为非负整数,u、v为[0,1)区间的浮点数,则这个像素得值f(i+u,j+v)可由原图像中坐标为(i,j)、(i+1,j)、(i,j+1)、(i+1,j+1)所对应的周围四个像素的值决定,即:
f(i+u,j+v)=(1-u)(1-v)f(i,j)+(1-u)vf(i,j+1)+u(1-v)f(i+1,j)+uvf(i+1,j+1)
其中f(i,j)表示源图像(i,j)处的的像素值,以此类推。
这就是双线性内插值法。
双线性内插值法计算量大,但缩放后图像质量高,不会出现像素值不连续的的情况。
由于双线性插值具有低通滤波器的性质,使高频分量受损,所以可能会使图像轮廓在一定程度上变得模糊。
概要设计
1.本实验的系统软件整体流程图如下:
本实验使用的平台是VC6.0,使用VC++实现的图形用户界面。
2.功能模块的划分和描述
由上面的程序整体流程图可以看出,本程序主要分为下面几个模块:
1.读取灰度图像模块:
该模块主要实现读入一幅灰度图像。
2.设置放大缩小倍数模块:
可以自由设置图片要放大和缩小的比例,且设置了最大只能放大15倍,最小缩小0.001倍,不过可以多次放大和缩小。
3.最近邻插值模块:
实现最近邻插值放大和缩小。
4.双线性插值模块:
实现双线性插值放大和缩小。
5.保存处理后的图片模块:
实现图片的保存。
详细设计
由于读取灰度图像模块、保存处理后的图片模块可以由系统自带的类CFileDialog类实现,因此本实验的重点是放在设置放大缩小倍数模块、最近邻插值模块、双线性插值模块这三个模块,以下是关于这三个模块的实现:
1.设置放大和缩小比例系数
本程序中新建了一个继承自对话框类CDialog的类SetClass用来接收用户输入的放大缩小比例的值,用户通过菜单的设置项来设置该系数,该对话框的界面如下:
其中的X和Y分别是用来设置图片的横向和纵向放大系数。
用户设置的变量的值存放在文档类的四个浮点型变量m_BXZoomRatio(横向放大倍数,初值为1.25),m_BYZoomRatio(纵向放大倍数,初值为1.25),m_SXZoomRatio(横向缩小系数,初值为0.8),m_SYZoomRatio(纵向缩小系数,初值是0.8)中。
其程序流程图如下:
其代码实现如下:
//放大系数设置菜单项的响应函数
voidCMyDIPDoc:
:
OnZoombig()
{
//TODO:
Addyourcommandhandlercodehere
SetClasssetclass;//设置对话框
if(setclass.DoModal()==IDOK)
{
//将用户设置值存到文档类中的相应变量中
m_BXZoomRatio=setclass.m_getx;
m_BYZoomRatio=setclass.m_gety;
}
}
//缩小系数设置菜单项的响应函数
voidCMyDIPDoc:
:
OnZoomsmall()
{
//TODO:
Addyourcommandhandlercodehere
SetClasssetclass;
if(setclass.DoModal()==IDOK)
{
//将用户设置值存到文档类中的相应变量中
m_SXZoomRatio=setclass.m_getx;
m_SYZoomRatio=setclass.m_gety;
}
}
2.最近邻插值模块和双线性插值模块
本程序实现这两个模块功能代码是在一个全局函数中实现的,其原型为:
HGLOBALWINAPIZoomDIB(LPSTRlpDIB,floatfXZoomRatio,floatfYZoomRatio,BOOLtag);
其中lpDIB是指向需处理的设备无关位图的指针,fXZoomRatio和fYZoomRatio分别为横向和纵向缩放系数,tag为一标记变量(tag=1时执行最近邻插值操作,tag=0时执行双线性插值操作)。
程序流程图如下:
其实现代码如下:
HGLOBALWINAPIZoomDIB(LPSTRlpDIB,floatfXZoomRatio,floatfYZoomRatio,BOOLtag)
{
//源图像的宽度和高度
LONGlWidth;
LONGlHeight;
//缩放后图像的宽度和高度
LONGlNewWidth;
LONGlNewHeight;
//缩放后图像的宽度(lNewWidth',必须是4的倍数)
LONGlNewLineBytes;
//指向源图像的指针
LPSTRlpDIBBits;
//指向源象素的指针
LPSTRlpSrc;
//缩放后新DIB句柄
HDIBhDIB;
//指向缩放图像对应象素的指针
LPSTRlpDst;
//指向缩放图像的指针
LPSTRlpNewDIB;
LPSTRlpNewDIBBits;
//指向BITMAPINFO结构的指针(Win3.0)
LPBITMAPINFOHEADERlpbmi;
//指向BITMAPCOREINFO结构的指针
LPBITMAPCOREHEADERlpbmc;
//循环变量(象素在新DIB中的坐标)
LONGi;
LONGj;
//象素在源DIB中的坐标
LONGi0;
LONGj0;
doublex,y;
//图像每行的字节数
LONGlLineBytes;
//找到源DIB图像象素起始位置
lpDIBBits=:
:
FindDIBBits(lpDIB);
//获取图像的宽度
lWidth=:
:
DIBWidth(lpDIB);
//计算图像每行的字节数
lLineBytes=WIDTHBYTES(lWidth*8);
//获取图像的高度
lHeight=:
:
DIBHeight(lpDIB);
//计算缩放后的图像实际宽度
//此处直接加0.5是由于强制类型转换时不四舍五入,而是直接截去小数部分
lNewWidth=(LONG)(:
:
DIBWidth(lpDIB)*fXZoomRatio+0.5);
//计算新图像每行的字节数
lNewLineBytes=WIDTHBYTES(lNewWidth*8);
//计算缩放后的图像高度
lNewHeight=(LONG)(lHeight*fYZoomRatio+0.5);
//分配内存,以保存新DIB
hDIB=(HDIB):
:
GlobalAlloc(GHND,lNewLineBytes*lNewHeight+*(LPDWORD)lpDIB+:
:
PaletteSize(lpDIB));
//判断是否内存分配失败
if(hDIB==NULL)
{
//分配内存失败
returnNULL;
}
//锁定内存
lpNewDIB=(char*):
:
GlobalLock((HGLOBAL)hDIB);
//复制DIB信息头和调色板
memcpy(lpNewDIB,lpDIB,*(LPDWORD)lpDIB+:
:
PaletteSize(lpDIB));
//找到新DIB象素起始位置
lpNewDIBBits=:
:
FindDIBBits(lpNewDIB);
//获取指针
lpbmi=(LPBITMAPINFOHEADER)lpNewDIB;
lpbmc=(LPBITMAPCOREHEADER)lpNewDIB;
//更新DIB中图像的高度和宽度
if(IS_WIN30_DIB(lpNewDIB))
{
//对于Windows3.0DIB
lpbmi->biWidth=lNewWidth;
lpbmi->biHeight=lNewHeight;
}
else
{
//对于其它格式的DIB
lpbmc->bcWidth=(unsignedshort)lNewWidth;
lpbmc->bcHeight=(unsignedshort)lNewHeight;
}
//针对图像每行进行操作
for(i=0;i{
//针对图像每列进行操作
for(j=0;j{
//指向新DIB第i行,第j个象素的指针
//注意此处宽度和高度是新DIB的宽度和高度
lpDst=(char*)lpNewDIBBits+lNewLineBytes*(lNewHeight-1-i)+j;
x=j/fXZoomRatio;
y=i/fYZoomRatio;
//计算该象素在源DIB中的坐标
if(tag)
{//tag=1,则执行以下的最近邻插值代码
i0=(LONG)(y+0.5);
j0=(LONG)(x+0.5);
//判断是否在源图范围内
if((j0>=0)&&(j0=0)&&(i0{
//指向源DIB第i0行,第j0个象素的指针
lpSrc=(char*)lpDIBBits+lLineBytes*(lHeight-1-i0)+j0;
//复制象素
*lpDst=*lpSrc;
}
else
{
//对于源图中没有的象素,直接赋值为255
*((unsignedchar*)lpDst)=255;
}
}
else
{//否则,执行下面的双线性插值代码
doublem=x-LONG(x);//X方向的小数部分
doublen=y-LONG(y);//Y方向的小数部分
unsignedcharrd,ld,lu,ru;//r:
右;l:
左;d:
下;u:
上
unsignedcharpix;
ld=*((char*)lpDIBBits+lLineBytes*(lHeight-1-LONG(y))+LONG(x));//左下角点的像素值
rd=*((char*)lpDIBBits+lLineBytes*(lHeight-1-LONG(y))+LONG(x)+1);//右下角点的像素值
lu=*((char*)lpDIBBits+lLineBytes*(lHeight-1-LONG(y)-1)+LONG(x));//左上角点的像素值
ru=*((char*)lpDIBBits+lLineBytes*(lHeight-1-LONG(y)-1)+LONG(x)+1);//右上角点的像素值
pix=(1-m)*(1-n)*ld+(1-m)*n*lu+m*n*ru+m*(1-n)*rd;//双线性插值,得到变换后的像素
*((unsignedchar*)lpDst)=pix;//将像素值赋给目的图片
}
}
}
//返回
returnhDIB;
}
该函数分别在视图类CMyDIPView中下面的函数(也就是相应菜单项的响应函数)中调用:
voidOnMenuitem32778();//最近邻插值缩小图片
voidOnMenuitem32779();//最近邻插值放大图片
voidOnMenuitem32780();//双线性插值放大图片
voidOnMenuitem32781();//双线性插值缩小图片
实验结果
运行程序,各模块实验结果如下:
读入灰度图片和设置放大缩小系数的结果
读入图片:
设置放大缩小系数:
最近邻插值放大缩小图片结果
图片使用最近邻插值缩小12倍:
再使用最近邻插值放大12倍:
双线性插值放大缩小图片结果
缩小12倍:
再放大12倍:
结果分析
上面实验结果分别用最邻近插值和双线性插值对钟表图片进行缩小和放大操作。
先缩小12倍,再放大12倍。
我们可以看到用最近邻插值方法处理后的图片产生了严重的疵点,边界出现了毛疵,图像质量很差。
而用双线性插值处理后的图片边界较为平滑,图像质量较好,但是图中的一些细节也被模糊了。
产生上面结果的原因:
最近邻插值是取距离源图像像素点最近的点的像素值赋给新图像相应的像素点,因此在新图像中各像素点之间的方差比较大,因此处理后的图像就产生了很多疵点;而双线性插值取的是源图像中对应的四个点像素的平均值作为新图像像素点的值,因此在新图像中各像素点之间的方差比较小,图像就比较平滑,但是这种方法也会产生一些源图像中所不具有的像素值,会导致图像的一些细节的丢失,并且是不可逆的。
参考文献
[1]冈萨雷斯.数字图像处理(第二版).阮秋琦等译.电子工业出版社.2009.6(5)
[2]谢凤英赵丹培.VisualC++数字图像处理.电子工业出版社.2008.9
(1)