图像处理实验报告

更新时间:2023-10-27 09:55:01 阅读量: 综合文库 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

数字图像处理实验报告

专 业: 计算机科学与技术 学 号: 20121004357 学生姓名: 李世荣 班 级: 191123 指导教师: 徐凯

图像的旋转变换

一、 实验目的:

主要是图像的旋转变换的编程实现,具体包括图像的读取、改写,图像的镜像,图像的转置,旋转变换等.

具体要求如下:

编程实现以任意角度对图像进行旋转变换; 在MFC的操作环境下达到实验目的。

二、 实验原理及算法:

1、图像旋转的定义

图像旋转是指图像以某一点为中心旋转一定的角度,形成一幅新的图像的过程。当然这

个点通常就是图像的中心。既然是按照中心旋转,自然会有这样一个属性:旋转前和旋转后的点离中心的位置不变. 根据这个属性,我们可以得到旋转后的点的坐标与原坐标的对应关系。旋转后,图像的大小一般会改变。和图像平移一样,既可以把转出显示区域的图像截去,也可以扩大图像范围以显示所有的图像。与图像平移一样,图像旋转也是图像的位置变换,对于旋转后超出源图像范围的区域要处理为不显示。

旋转后的图像不会变形,但是其垂直对称轴和水平对称轴都发生了改变,旋转后像素的坐标需要经过较复杂的数学运算得出。而且图像在经过旋转变换后,其宽度和高度都要发生变化,所以原始图像的中心点和输出图像的中心点的坐标是不同的。图像的旋转不再是由一个矩阵变换就能获得坐标的映射关系,它涉及多次矩阵变换。 2、图像旋转的算法

设图像绕任意点 C(x0,y0 )旋转θ角度,如图所示:

我们可通过以下几个步骤来实现该旋转变换:

(1)将指定的任意旋转中心C(x0,y0)平移到坐标原点O,变换矩阵为Ts1 (2)使图像绕坐标原点逆时针旋转θ角,变换矩阵为Tr

(3)使旋转中心从坐标原点平移回原来的位置C(x0,y0)。变换矩阵为Ts2 所以图像绕任意点阵: C(x0,y0)的旋转过程为将每一象素点的齐次坐标的行向量右乘变换矩

则,

[x',y',1] = [x,y,1] · T = [(x-x0)cosθ+(y-y0)sinθ+x0 ,-(x-x0)sinθ+(y-y0)cosθ+y0 , 1] 即:

x' = (x - x0)cosθ + (y - y0)sinθ + x0 y' = - (x - x0)sinθ + (y - y0)cosθ + y0 利用上式,就可以实现图像的旋转。

三、 实验流程图

程序开始 单击“文件->打开”打开一幅图像 单击“几何变换->图像旋转” 获取旋转角度 计算旋转角度的正弦和余弦 计算原图和新图四个角的坐标 计算旋转后图像实际宽度和高度 更新DIB中图像的宽度和高度 针对图像每行每列进行操作 计算指向新DIB第i行,第j个像素的指针 计算该像素在原DIB中的坐标 否 判断坐标是否在原图范围内 是 计算指向原DIB在该坐标下像素的指针 复制像素 将该像素赋值为255 否 判断旋转是否成功 是 替换DIB,更新DIB大小和调色板,设置脏标记 重新设置滚动视图大小,并更新视图 提示用户 程序结束 四、 实验结果 原图:

旋转90°后:

四、总结与体会:

这次专选课学到最大的东西,是自己总算有MFC编程的概念了,很多东西不通过自己的编程是绝对不能真正理解.比如说封装性,这次编程,很好地利用了类的封装性.另外,比如MFC是基于消息响应机制的,这就决定了,要利用鼠标或者菜单响应函数去实现功能,而用c语言编写程序的时候,完全是按主函数的线程来的.

刚开始的时候做的是位图的读取和显示,实在是不知从哪里做起,所以就照着从图书馆里借来的教材上敲了前面的部分,但是慢慢地也看懂了代码的意思.不过后来的基本上都是从网上找的了,但是算法还是基本上和书上差不多.不过自己想的时候还是有很多细节的部分没有注意到,比如说,强制数据类型转换,有些是由于函数调用引起的,有些是由于不

等号两边数据的匹配问题,还有的是由于指针的移动,直到这个时候,才真正明白实习书上程序为什么那么多强制类型转换,虽然书上很多东西不是尽善尽美,但是对于我这种刚开始学会编程的人还是有很多可以学习的地方的.

如老师所说,算法的效率是很重要的.要提高算法的效率,一个是要简化计算(不得不说,这需要数学基础),另外一个就是要避免许多重复的计算.在参考书上的程序里,很多时候,为了避免这种重复的计算(在循环中表现尤其明显),会把某些数当常数算出来,只要后来加上这个常数就可以,这样,效率高很多.

另外,对许多出错的情况,我的程序里也没有做好.这也是我应该向别人程序学习的地方.

部分源代码附录:

* 函数名称: RotateDIB() * 参数:

* LPSTR lpDIB

- 指向源DIB的指针

* int iRotateAngle - 旋转的角度(0-360度)

* 返回值: HGLOBAL - 旋转成功返回新DIB句柄,否则返回NULL。 * 说明:

* 该函数用来以图像中心为中心旋转DIB图像,返回新生成DIB的句柄。 * 调用该函数会自动扩大图像以显示所有的象素。函数中采用最邻近插 * 值算法进行插值。

************************************************************************/ HGLOBAL CDibImage::RotateDIB(LPSTR lpDIB, int iRotateAngle) {

lWidth; lHeight;

// 源图像的宽度

LONG LONG LONG LONG LONG LONG LPSTR LPSTR HDIB LPSTR LPSTR LPSTR

// 源图像的高度

lNewWidth; lNewHeight; lLineBytes;

// 旋转后图像的宽度 // 旋转后图像的高度

// 图像每行的字节数

// 旋转后图像的宽度(lNewWidth',必须是4的倍数) // 指向源图像的指针

// 指向源象素的指针 // 旋转后新DIB句柄

// 指向旋转图像对应象素的指针

lNewLineBytes; lpDIBBits; lpSrc; hDIB; lpDst;

lpNewDIB; // 指向旋转图像的指针

lpNewDIBBits;

LPBITMAPINFOHEADER lpbmi; LPBITMAPCOREHEADER lpbmc;

i; j; i0; j0;

// 指向BITMAPINFO结构的指针(Win3.0) // 指向BITMAPCOREINFO结构的指针

LONG LONG LONG LONG

// 循环变量(象素在新DIB中的坐标)

// 象素在源DIB中的坐标

float fRotateAngle; float fSina, fCosa;

// 旋转角度(弧度) // 旋转角度的正弦和余弦

// 源图四个角的坐标(以图像中心为坐标系原点)

float fSrcX1,fSrcY1,fSrcX2,fSrcY2,fSrcX3,fSrcY3,fSrcX4,fSrcY4; // 旋转后四个角的坐标(以图像中心为坐标系原点)

float fDstX1,fDstY1,fDstX2,fDstY2,fDstX3,fDstY3,fDstX4,fDstY4; float f1,f2;

lpDIBBits = FindDIBBits(lpDIB); lWidth = DIBWidth(lpDIB);

// 找到源DIB图像象素起始位置 // 获取图像的\宽度\(4的倍数)

lLineBytes = WIDTHBYTES(lWidth * 8);// 计算图像每行的字节数 lHeight = DIBHeight(lpDIB); // 将旋转角度从度转换到弧度

fRotateAngle = (float) RADIAN(iRotateAngle); fSina = (float) sin((double)fRotateAngle); fCosa = (float) cos((double)fRotateAngle);

// 获取图像的高度

// 计算旋转角度的正弦 // 计算旋转角度的余弦

// 计算原图的四个角的坐标(以图像中心为坐标系原点) fSrcX1 = (float) (- (lWidth - 1) / 2); fSrcY1 = (float) ( (lHeight - 1) / 2); fSrcX2 = (float) ( (lWidth - 1) / 2); fSrcY2 = (float) ( (lHeight - 1) / 2); fSrcX3 = (float) (- (lWidth - 1) / 2); fSrcY3 = (float) (- (lHeight - 1) / 2); fSrcX4 = (float) ( (lWidth - 1) / 2); fSrcY4 = (float) (- (lHeight - 1) / 2);

// 计算新图四个角的坐标(以图像中心为坐标系原点) fDstX1 = fCosa * fSrcX1 + fSina * fSrcY1; fDstY1 = -fSina * fSrcX1 + fCosa * fSrcY1; fDstX2 = fCosa * fSrcX2 + fSina * fSrcY2; fDstY2 = -fSina * fSrcX2 + fCosa * fSrcY2; fDstX3 = fCosa * fSrcX3 + fSina * fSrcY3; fDstY3 = -fSina * fSrcX3 + fCosa * fSrcY3; fDstX4 = fCosa * fSrcX4 + fSina * fSrcY4; fDstY4 = -fSina * fSrcX4 + fCosa * fSrcY4; // 计算旋转后的图像实际宽度

lNewWidth = (LONG)(max(fabs(fDstX4 - fDstX1), fabs(fDstX3 - fDstX2)) + 0.5); // 计算新图像每行的字节数

lNewLineBytes = WIDTHBYTES(lNewWidth * 8); // 计算旋转后的图像高度

lNewHeight = (LONG)(max(fabs(fDstY4 - fDstY1), fabs(fDstY3 - fDstY2)) + 0.5); // 两个常数,这样不用以后每次都计算了

f1 = (float) (-0.5 * (lNewWidth - 1) * fCosa - 0.5 * (lNewHeight - 1) * fSina

+ 0.5 * (lWidth - 1));

f2 = (float) ( 0.5 * (lNewWidth - 1) * fSina - 0.5 * (lNewHeight - 1) * fCosa

+ 0.5 * (lHeight - 1));

// 分配内存,以保存新DIB

hDIB = (HDIB) ::GlobalAlloc(GHND, lNewLineBytes * lNewHeight +

*(LPDWORD)lpDIB + PaletteSize(lpDIB));

if (hDIB == NULL) { }

lpNewDIB = (char * )::GlobalLock((HGLOBAL) hDIB); // 复制DIB信息头和调色板

memcpy(lpNewDIB, lpDIB, *(LPDWORD)lpDIB + PaletteSize(lpDIB)); // 找到新DIB象素起始位置

lpNewDIBBits = FindDIBBits(lpNewDIB);

return NULL;

lpbmi = (LPBITMAPINFOHEADER)lpNewDIB; lpbmc = (LPBITMAPCOREHEADER)lpNewDIB;

// 更新DIB中图像的高度和宽度 if (IS_WIN30_DIB(lpNewDIB)) { } else { }

// 针对图像每行进行操作

// 对于其它格式的DIB

lpbmc->bcWidth = (unsigned short) lNewWidth; lpbmc->bcHeight = (unsigned short) lNewHeight; // 对于Windows 3.0 DIB lpbmi->biWidth = lNewWidth; lpbmi->biHeight = lNewHeight;

for(i = 0; i < lNewHeight; i++) {

for(j = 0; j < lNewWidth; j++) {

// 针对图像每列进行操作

// 指向新DIB第i行,第j个象素的指针 // 注意此处宽度和高度是新DIB的宽度和高度

lpDst = (char *)lpNewDIBBits + lNewLineBytes * (lNewHeight - 1 - i) + j; // 计算该象素在源DIB中的坐标

i0 = (LONG) (-((float) j) * fSina + ((float) i) * fCosa + f2 + 0.5); j0 = (LONG) ( ((float) j) * fCosa + ((float) i) * fSina + f1 + 0.5); // 判断是否在源图范围内

if( (j0 >= 0) && (j0 < lWidth) && (i0 >= 0) && (i0 < lHeight)) {

// 指向源DIB第i0行,第j0个象素的指针

lpSrc = (char *)lpDIBBits + lLineBytes * (lHeight - 1 - i0) + j0; // 复制象素

}

}

} else { }

*lpDst = *lpSrc;

// 对于源图中没有的象素,直接赋值为255 * ((unsigned char*)lpDst) = 255;

return hDIB; }

本文来源:https://www.bwwdw.com/article/6zl2.html

Top