图像编程学习笔记2——bmp位图平移
以下文字内容copy于<<数字图像处理编程入门>>,code为自己实现,是win32控制台程序。
2.1 平移
平移(translation)变换大概是几何变换中最简单的一种了。
如图2.1所示,初始坐标为(x0,y0)的点经过平移(tx,ty)(以向右,向下为正方向)后,坐标变为(x1,y1)。这两点之间的关系是x1=x0+tx,y1=y0+ty。
图2.1 平移的示意图
以矩阵的形式表示为
我们更关心的是它的逆变换:
这是因为:我们想知道的是平移后的图象中每个象素的颜色。例如我们想知道,新图中左上角点的RGB值是多少?很显然,该点是原图的某点经过平移后得到的,这两点的颜色肯定是一样的,所以只要知道了原图那点的RGB值即可。那么到底新图中的左上角点对应原图中的哪一点呢?将左上角点的坐标(0,0)入公式(2.2),得到x0=-tx,y0=-ty;所以新图中的(0,0)点的颜色和原图中(-tx, -ty)的一样。
这样就存在一个问题:如果新图中有一点(x1,y1),按照公式(2.2)得到的(x0,y0)不在原图中该怎么办?通常的做法是,把该点的RGB值统一设成(0,0,0)或者(255,255,255)。
另一个问题是:平移后的图象是否要放大?一种做法是不放大,移出的部分被截断。例如,图2.2为原图,图2.3为移动后的图。这种处理,文件大小不会改变。
图2.2 移动前的图
图2.3 移动后的图
还有一种做法是:将图象放大,使得能够显示下所有部分,如图2.4所示。
图2.4 移动后图象被放大
这种处理,文件大小要改变。设原图的宽和高分别是w1,h1则新图的宽和高变为w1+|tx|和h1+|ty|,加绝对值符号是因为tx,ty有可能为负(即向左,向上移动)。
下面的函数Translation采用的是第一种做法,即移出的部分被截断。在给出源代码之前,先说明一个问题。
如果你用过Photoshop,CorelPhotoPaint等图象处理软件,可能听说过“灰度图”(grayscale)这个词。灰度图是指只含亮度信息,不含色彩信息的图象,就象我们平时看到的黑白照片:亮度由暗到明,变化是连续的。因此,要表示灰度图,就需要把亮度值进行量化。通常划分成0到255共256个级别,其中0最暗(全黑),255最亮(全白)。.bmp格式的文件中,并没有灰度图这个概念,但是,我们可以很容易在.bmp文件中表示灰度图。方法是用256色的调色板,只不过这个调色板有点特殊,每一项的RGB值都是相同的。也就是说RGB值从(0,0,0),(1,1,1)一直到(255,255,255)。(0,0,0)是全黑色,(255,255,255)是全白色,中间的是灰色。这样,灰度图就可以用256色图来表示了。为什么会这样呢?难道是一种巧合?其实并不是。
在表示颜色的方法中,除了RGB外,还有一种叫YUV的表示方法,应用也很多。电视信号中用的就是一种类似于YUV的颜色表示方法。
在这种表示方法中,Y分量的物理含义就是亮度,U和V分量代表了色差信号(你不必了解什么是色差,只要知道有这么一个概念就可以了)。使用这种表示方法有很多好处,最主要的有两点:
(1) 因为Y代表了亮度,所以Y分量包含了灰度图的所有信息,只用Y分量就能完全能够表示出一幅灰度图来。当同时考虑U,V分量时,就能够表示出彩色信息来。这样,用同一种表示方法可以很方便的在灰度和彩色图之间切换,而RGB表示方法就做不到这一点了。
(2) 人眼对于亮度信号非常敏感,而对色差信号的敏感程度相对较弱。也就是说,图象的主要信息包含在Y分量中。这就提示我们:如果在对YUV信号进行量化时,可以“偏心”一点,让Y的量化级别多一些(谁让它重要呢?)而让UV的量化级别少一些,就可以实现图象信息的压缩。这一点将在第9章介绍图象压缩时仔细研究,这里就不深入讨论了。而RGB的表示方法就做不到这一点,因为RGB三个分量同等重要,缺了谁也不行。YUV和RGB之间有着如下的对应关系
(2.3)
(2.4)
当RGB三个分量的大小一样时,假设都是a,代入公式(2.3),得到Y=a,U=0,V=0 。你现在该明白我前面所说不是巧合的原因了吧。
使用灰度图有一个好处,那就是方便。首先RGB的值都一样;其次,图象数据即调色板索引值,也就是实际的RGB值,也就是亮度值;另外,因为是256色调色板,所以图象数据中一个字节代表一个象素,很整齐。如果是2色图或16色图,还要拼凑字节,很麻烦。如果是彩色的256色图,由于图象处理后有可能会产生不属于这256种颜色的新颜色,就更麻烦了;这一点,今后你就会有深刻体会的。所以,做图象处理时,一般采用灰度图。为了将重点放在算法本身上,今后给出的程序如不做特殊说明,都是针对256级灰度图的。其它颜色的情况,你可以自己想一想,把算法补全。
如果想得到一幅灰度图,可以使用Sea或者PhotoShop等软件提供的颜色转换功能将彩色图转换成灰度图。
好了,言归正传,下面给出Translation的源代码。算法的思想是先将所有区域填成白色,然后找平移后显示区域的左上角点(x0,y0)和右下角点(x1,y1) ,分几种情况进行处理。
先看x方向(width指图象的宽度)
(1) tx≤-width:很显然,图象完全移出了屏幕,不用做任何处理;
(2) -width<tx≤0:如图2.5所示。容易看出,图象区域的x范围从0到width-|tx|,对应原图的范围从|tx|到width;
图2.5 tx≤0,ty≤0的情况
(3) 0< tx<width:如图2.6所示。容易看出,图象区域的x范围从tx到width,对应原图的范围从0到width - tx;
图2.6 0< tx<width,0<ty<height的情况
(4) tx≥width:很显然,图象完全移出了屏幕,不用做任何处理。
y方向是对应的(height表示图象的高度):
(1) ty≤-height,图象完全移出了屏幕,不用做任何处理;
(2) -height<ty≤0,图象区域的y范围从0到height-|ty|,对应原图的范围从|ty|到height;
(3) 0<ty<height,图象区域的y范围从ty到height,对应原图的范围从0到height-ty;
(4) ty≥height,图象完全移出了屏幕,不用做任何处理。
这种做法利用了位图存储的连续性,即同一行的象素在内存中是相邻的。利用memcpy函数,从(x0,y0)点开始,一次可以拷贝一整行(宽度为x1-x0),然后将内存指针移到(x0,y0+1)处,拷贝下一行。这样拷贝(y1-y0)行就完成了全部操作,避免了一个一个象素的计算,提高了效率。
CODE:(注:该程序需要一副bmp格式的灰度图像,并放到工程目录下,文件名为nv1.bmp)
- /**
- * 程序名: Translation.cpp
- * 功 能: 实现bmp格式灰度图片的平移,移出部分用白色填充
- */
- #include <iostream>
- #include <cstdio>
- #include <fstream>
- #include <cstring>
- #include <windows.h>
- using namespace std;
- BITMAPFILEHEADER bmpFileHeader; //位图文件头
- BITMAPINFOHEADER bmpInfoHeader; //位图信息头
- RGBQUAD *pColorTable = new RGBQUAD[256]; //颜色表指针
- unsigned char *pBmpData; //图像数据指针
- unsigned char *pBmpData1; //平移后图像数据指针
- unsigned char *pTemp,*pTemp1; //临时指针
- int width,height,imgSize; //图像宽,高,实际大小,imgSize必须为4的倍数,bmp格式文件结构规定
- int srcX[2],srcY[2],dstX[2],dstY[2]; //平移前后位置
- /**
- * 函数名: readBmp
- * 参 数: bmpFileName--指向读入bmp文件的文件名指针
- * 功 能: 读入一个bmp文件,获得相应数据
- */
- bool readBmp(char *bmpFileName)
- {
- FILE *fp = fopen(bmpFileName,"rb"); //以二进制读方式打开指定的图像文件
- if(NULL == fp)
- {
- printf("%s is not exist!",bmpFileName);
- return FALSE;
- }
- fread(&bmpFileHeader,sizeof(BITMAPFILEHEADER),1,fp); //读取位图头信息放入bmpFileHeader,注:指针也相应移动
- fread(&bmpInfoHeader,sizeof(BITMAPINFOHEADER),1,fp); //读取位图信息头放入bmpInfoHeader
- width = bmpInfoHeader.biWidth; //宽
- height = bmpInfoHeader.biHeight; //高
- fread(pColorTable,sizeof(RGBQUAD),256,fp); //读取颜色表放入pColorTable
- // int bytePerLine = (bmpInfoHeader.biWidth * bmpInfoHeader.biBitCount + 31) / 32 * 4;
- pBmpData = new unsigned char [imgSize = bmpInfoHeader.biSizeImage];
- pBmpData1 = new unsigned char [imgSize];
- memset(pBmpData1,(BYTE)255,sizeof(char)*imgSize); //把新的图像信息用255(白色)填充,平移后没有图像的区域就是白色了
- fread(pBmpData,sizeof(char),bmpInfoHeader.biSizeImage,fp); //读取图像信息放入pBmpData
- fclose(fp); //记住要关闭文件
- return TRUE;
- }
- /**
- * 函数名: translation
- * 参 数: tx--平移的x距离,ty--平移的y距离
- * 功 能: 实现平移,并把平移后图像信息写入pBmpData1
- */
- void translation(int tx,int ty)
- {
- bool xVisible = TRUE,yVisible = TRUE;
- //xVisible为FALSE时,表示x方向已经移出了可显示的范围
- if(tx <= -width)
- {
- xVisible = FALSE;
- }
- else if(tx <= 0)
- {
- dstX[0] = 0; //表示移动后,有图区域的左上角点的x坐标
- dstX[1] = width + tx; //表示移动后,有图区域的右下角点的x坐标
- }
- else if(tx < width)
- {
- dstX[0] = tx;
- dstX[1] = width;
- }
- else
- xVisible = FALSE;
- srcX[0] = dstX[0] - tx; //对应DstX0在原图中的x坐标
- srcX[1] = dstX[1] - tx; //对应DstX1在原图中的x坐标
- int rectWidth = srcX[1] - srcX[0]; //有图区域的宽度
- //y的和x类似,就不加注释了
- if(ty <= -height)
- yVisible = FALSE;
- else if(ty <= 0)
- {
- dstY[0] = 0;
- dstY[1] = height + ty;
- }
- else if(ty < height)
- {
- dstY[0] = ty;
- dstY[1] = height;
- }
- else
- yVisible = FALSE;
- srcY[0] = dstY[0] - ty;
- srcY[1] = dstY[1] - ty;
- int rectHeight = srcY[1] - srcY[0];
- int lineBytes = (width * bmpInfoHeader.biBitCount + 31) / 32 * 4; //每行所占的字节数,必须为4的倍数
- if(xVisible && yVisible)
- {
- for(int i = 0; i < rectHeight; i++ )
- {
- //pTemp指向要拷贝的那一行的最左边的象素对应在原图中的位
- //置。特别要注意的是,由于.bmp是上下颠倒的,
- pTemp = pBmpData + (height - 1 - (srcY[0] + i)) * lineBytes + srcX[0];
- //pTemp1指向要拷贝的那一行的最左边的象素对应在新图中的位置。同样要注意上面的问题。
- pTemp1 = pBmpData1 + (height - 1 - (dstY[0] + i)) * lineBytes + dstX[0];
- memcpy(pTemp1,pTemp,rectWidth); //从pTemp中复制大小为rectWidth的数据到pTemp1,这里就是copy图像的一行数据
- }
- }
- }
- /**
- * 函数名: writeBmp
- * 功 能: 新建一个bmp文件,把平移后的图像信息写入,生成一个新的bmp
- */
- void writeBmp()
- {
- char writeBmpName[] = "new.bmp";
- FILE *fp = fopen(writeBmpName,"wb"); //以二进制写方式打开指定的图像文件
- if(NULL == fp)
- {
- cout<<"file not exist!";
- return ;
- }
- //写入BMP文件数据
- fwrite(&bmpFileHeader,sizeof(BITMAPFILEHEADER),1,fp);
- fwrite(&bmpInfoHeader,sizeof(BITMAPINFOHEADER),1,fp);
- fwrite(pColorTable,sizeof(RGBQUAD),256,fp);
- fwrite(pBmpData1,sizeof(char),imgSize,fp);
- fclose(fp);
- //释放内存
- delete []pColorTable;
- delete []pBmpData1;
- delete []pBmpData;
- }
- /**
- * 函数名: work
- * 功 能: 处理
- */
- void work()
- {
- int x,y;
- char readBmpName[] = "nv1.bmp";
- if ( !readBmp(readBmpName) )
- printf("Bmp file reads faliure");
- printf("the distance of translation,cx,cy:"); //读入平移的x和y
- scanf("%d %d",&x,&y);
- translation(x,y);
- writeBmp();
- }
- int main()
- {
- work();
- return 0;
- }
- from:http://blog.csdn.net/sun1956/article/details/8646800
图像编程学习笔记2——bmp位图平移的更多相关文章
- 图像编程学习笔记1——bmp文件结构处理与显示
文本内容转载自<数字图像处理编程入门>,代码为自己实现 1.1图和调色板的概念 如今Windows(3.x以及95,98,NT)系列已经成为绝大多数用户使用的操作系统,它比DOS成功的一个 ...
- 【Visual C++】游戏编程学习笔记之四:透明动画实现
本系列文章由@二货梦想家张程 所写,转载请注明出处. 本文章链接:http://blog.csdn.net/terence1212/article/details/44224963 作者:ZeeCod ...
- 【Visual C++】游戏编程学习笔记之八:鼠标输入消息(小demo)
本系列文章由@二货梦想家张程 所写,转载请注明出处. 作者:ZeeCoder 微博链接:http://weibo.com/zc463717263 我的邮箱:michealfloyd@126.c ...
- 【Visual C++】游戏编程学习笔记之七:键盘输入消息
本系列文章由@二货梦想家张程 所写,转载请注明出处. 作者:ZeeCoder 微博链接:http://weibo.com/zc463717263 我的邮箱:michealfloyd@126.c ...
- 【Visual C++】游戏编程学习笔记之六:多背景循环动画
本系列文章由@二货梦想家张程 所写,转载请注明出处. 本文章链接:http://blog.csdn.net/terence1212/article/details/44264153 作者:ZeeCod ...
- DirectX 11游戏编程学习笔记之6: 第5章The Rendering Pipeline(渲染管线)
本文由哈利_蜘蛛侠原创,转载请注明出处.有问题欢迎联系2024958085@qq.com 注:我给的电子版是700多页,而实体书是800多页,所以我在提到相关概念的时候 ...
- JAVA GUI编程学习笔记目录
2014年暑假JAVA GUI编程学习笔记目录 1.JAVA之GUI编程概述 2.JAVA之GUI编程布局 3.JAVA之GUI编程Frame窗口 4.JAVA之GUI编程事件监听机制 5.JAVA之 ...
- Linux Shell编程学习笔记——目录(附笔记资源下载)
LinuxShell编程学习笔记目录附笔记资源下载 目录(?)[-] 写在前面 第一部分 Shell基础编程 第二部分 Linux Shell高级编程技巧 资源下载 写在前面 最近花了些时间学习She ...
- DirectX 11游戏编程学习笔记之8: 第6章Drawing in Direct3D(在Direct3D中绘制)(习题解答)
本文由哈利_蜘蛛侠原创,转载请注明出处.有问题欢迎联系2024958085@qq.com 注:我给的电子版是700多页,而实体书是800多页,所以我在提到相关概念的时候 ...
随机推荐
- SQLServer分页查询存储过程
项目中用到的SQLServer分页查询存储过程. [存储过程] create PROCEDURE prcPageResult -- 获得某一页的数据 -- @currPage int = 1, ...
- CCIE路由实验(10) -- IS-IS
1.配置基本的集成IS-IS路由协议2.多区域的集成IS-IS配置3.IS-IS认证4.IS-IS路由泄露5.IS-IS汇总6.在IS-IS中手动产生一条默认路由7.IS-IS协议重分布8.用IS-I ...
- 对于一颗完全二叉树,要求给所有节点加上一个pNext指针,指向同一层的相邻节点-----层序遍历的应用题
题目:对于一颗完全二叉树,要求给所有节点加上一个pNext指针,指向同一层的相邻节点:如果当前节点已经是该层的最后一个节点,则将pNext指针指向NULL:给出程序实现,并分析时间复杂度和空间复杂度. ...
- appium 真机测试问题 出现 instruments crashed on startup
1.appium 真机测试的时候 instruments crashed on startup,必须在真机上打开UI Automation 在设置里: Developer->Enable UI ...
- JSTL解析——005——core标签库04
直接入主题,标签讲解 1.<c:import>标签 JSP里面有<% file include="XX"%> 与<jsp:include>,JS ...
- 2014 Multi-University Training Contest 1 — D. Task
题目链接:pid=4864">http://acm.hdu.edu.cn/showproblem.php?pid=4864 题目大意: 有N个机器.M个任务. 当中每一个机器有xi,y ...
- Maven项目下 java.lang.ClassNotFoundException 常规解决的方法
网上非常多要改动.class .project的比較麻烦有时候还不一定管用.以下的方法适合于项目用已经引用了jar.可是执行时却ClassNotFound,请用例如以下方法试试: 严重: Error ...
- C语言字符串操作函数
1.函数名: stpcpy 功 能: 拷贝一个字符串到另一个 用 法: char *stpcpy(char *destin, char *source); 程序例: #include < ...
- qt事件传递过程和处理
处理监控系统的时候遇到问题,在MainWidget中创建多个子Widget的时候,原意是想鼠标点击先让MainWidget截获处理后再分派给子Widget去处理,但调试后发现如果子Widget重新实现 ...
- isHiden和isVisible的区别(isVisible更可靠)
之前一直对isHiden和isVisible的区别比较模糊,都是乱用的.今天因需要仔细看了一下. 1.isHiden只是返回部件的隐藏属性,并不能表示部件当前的真实状态.比如A部件有个子部件B,而A处 ...