c语言之彩色图像几何变换

理论知识之图像存储格式和结构类型

常见的图像格式主要有.bmp、.png、.jpg、.jpeg。jpeg兼容jpg格式。bmp文件格式,又称为Bitmap(位图)或是DIB(Device-Independent Device,设备无关位图),没有经过压缩的图片格式。所有存储在存储设备上的文件都是二进制文件,一连串的10101数据,通过对这些二进制文件进行不同的格式解析会得到不同的内容。通过代码读取的数据也是这些二进制格式,因此,我们需要参照规定的格式要求,bmp文件的数据按照从文件头开始的先后顺序分为四个部分:
1、bmp文件头(bmp file header):提供文件的格式、大小等信息(14字节)
BMP文件头数据结构含有BMP文件的类型、文件大小和位图起始位置等信息。
  其结构定义如下:
  typedef struct tagBITMAPFILEHEADER
  {
  WORD bfType; // 位图文件的类型,必须为字符BM(1-2字节)
  DWORD bfSize; // 位图文件的大小,以字节为单位(3-6字节)
  WORD bfReserved1; // 位图文件保留字,必须为0(7-8字节)
  WORD bfReserved2; // 位图文件保留字,必须为0(9-10字节)
  DWORD bfOffBits; // 位图数据的起始位置,以相对于位图(11-14字节)
// 文件头的偏移量表示,以字节为单位
  } BITMAPFILEHEADER;
2、位图信息头(bitmap information):提供图像数据的尺寸、位平面数、压缩方式、颜色索引等信息(40字节)
BMP位图信息头数据用于说明位图的尺寸等信息。
  typedef struct tagBITMAPINFOHEADER{
  DWORD biSize; // 本结构所占用字节数(15-18字节)
  LONG biWidth; // 位图的宽度,以像素为单位(19-22字节)
  LONG biHeight; // 位图的高度,以像素为单位(23-26字节)
  WORD biPlanes; // 目标设备的级别,必须为1(27-28字节)
  WORD biBitCount;// 每个像素所需的位数,必须是1(双色),
  (29-30字节), 4(16色),8(256色)或24(真彩色)之一
  DWORD biCompression; // 位图压缩类型,必须是 0(不压缩),
  (31-34字节)1(BI_RLE8压缩类型)或2(BI_RLE4压缩类型)之一
  DWORD biSizeImage; // 位图的大小,以字节为单位(35-38字节)
  LONG biXPelsPerMeter; // 位图水平分辨率,每米像素数(39-42字节)
  LONG biYPelsPerMeter; // 位图垂直分辨率,每米像素数(43-46字节)
  DWORD biClrUsed;// 位图实际使用的颜色表中的颜色数(47-50字节)
  DWORD biClrImportant;// 位图显示过程中重要的颜色数(51-54字节)
  } BITMAPINFOHEADER;
3、调色板(color palette):可选,如使用索引来表示图像,调色板就是索引与其对应的颜色的映射表(由颜色索引值决定)
4、位图数据(bitmap data):图像数据(真正的图像数据)。
(图片大小由文件头的第3到第6个字节数表示)

以二进制格式查看文件可以用winhex软件,软件打开文件都是以二进制格式展现,可以试着去打开一个bmp格式图像,按照上面的格式对比一些,看看理论和实际是否一样(这里我就不都说了,大家自己可以去尝试一下)。
然后需要重点注意的是:文件格式(第1 、2字节)、文件大小(第3-6字节)、图像数据与文件头的偏移量(第11-14字节)、图像分辨率(宽:19-22字节,高:23-26字节)、像素位数(29-30字节)图像压缩类型(31-34字节),图像数据大小(35-38字节)。这些数据在最后处理完图像之后还需要修改,然后写入新的图像中。

理论知识之图像插值算法

根据我的了解情况,插值算法主要有最近邻插值,双线性邻插值和高阶邻插值(其实这些都是在课上裂解到的,应该还有很多插值算法,我就不去深入研究了,感兴趣的自己去查阅啊)。我重点介绍一下前面两个算法原理。在介绍原理前,现解释一下为什么需要插值?我们都知道,图像是以像素位单位组成的,那么物理器件上,一个像素点对应一个物理器件,我们以网格的交点来比喻像素点,如下图所示,本来在图像没有变换前,像素点a在交点处待的好好的,没什么问题,但是当进行几何变换后,例如平移 10.5个像素点,那么像素点a就来到网格中间,那么如何去对这个新的像素点进行处理就叫做插值。
什么是图像插值
最近邻插值
最简单的插值方法是所谓的零阶插值法或者称之为最近邻插值法,即令输出像素的灰度值等于离它最近所映射到的位置的输入像素灰度值。最近邻插值计算简单,速度也是最快的一种算法。在细节方面,最近邻插值的体现的效果就不是很好。
双线性邻插值
双线性邻插值也称为一阶插值,相比于零阶插值,一阶插值可以产生更加令人满意的效果。只是程序的复杂性大大的提升了,运行时间也长了。双线性插值需要运用到双线性函数。如下图所示,点f(x,y)的值由其周围四个像素点的值确定,每个像素点的权重不一样。令f(x,y)为两个变量的函数,其在单位正方形顶点的值已知。假设我们希望通过插值得到正方形内任意点的f(x,y)值,则我们需要用到双线性函数。令f(x,y)=ax+by+cxy+d;
先对上端的两个顶点进行拟合可以得到
f(x,0)=f(0,0)+x[f(1,0)-f(0,0)];
对于低端的两个顶点进行拟合可以得到
f(x,1)=f(0,1)+x[f(1,1)-f(0,1)];
将上述的两个方程式进行合并可以得到
f(x,y)=[f(1,0)-f(0,0)]x+[f(0,1)-f(0,0)]y+[f(1,1)+f(0,0)-f(0,1)-f(1,0)]xy+f(0,0);
每一个像素变化后都需要进行插值,运算量大大增加了
双线性插值

理论知识之彩色图像

彩色图像的组成是按照RGB(或者BGR)格式组合而成,也就是三原色。每个彩色图像的像素点都是RGB三种颜色组成,一个像素点按照数学表示就是一个整数,一般是占3个字节,RGB各占一个字节。例如一个24位的像素,其白色的像素点的值为0xFFFFFF,红色值为FF,绿色值为FF,蓝色值为FF。组合起来就是0xFFFFFF。一般现在的图像都是24位真彩色,也就是上面这个格式。several年前有16位彩色(464)格式等等。我不再一一赘述。

实现步骤

无论做什么事,胸有成竹才能悠然自得,要实现一个功能,心中必须要先构思出整个项目的大概,列出实现的步骤。
软件功能:输入要处理的图像名字,输入几何变换的参数,将变换后的图像存储为新的图像。
第一步:打开图片,获得图片的头信息和图像数据信息,将图像数据信息存储为一维数据格式,保存头信息。
第二步:将获得的一维图像数据格式转换成二维数据格式,申请新的内存存储变换后的图像数据。
第三步:矩阵相乘,获得新的像素位置分布信息,分别使用一阶和零阶插值图像进行插值变换。
第四步:更改变换后图像的头信息,将变换后图像的头信息和图像数据写入输出。

源码

源码比较长,没怎么整理,大部分的代码我都已经添加了注释了,授人以鱼不如授人以渔。方法理论get 到了的话,应该很容易理解。源码我网盘格式分享。需要的自己取,不要客气。

效果

原始图像
原始图像
平移
横向平移-23纵向平移-21
旋转
旋转-36度
最近邻插值缩放
横向放大(1/0.8)纵向放大(1/0.9
双线性邻插值缩放
横向放大(1/0.8)纵向放大(1/0.9)

总结和预告

毕竟这只是一个课程设计,我没有花太多的时间,也不想花太多时间。程序还有很多可以改进和拓展的。例如添加libpng和libjpeg库,就可以实现png和jpeg格式的图像了,修改一下框架可以更加好用人性化。如果需要做成一个app,有个漂亮好用的UI,那么可以用QT去封装一下。我没有时间,我就不弄了。也许这个软件看起来没什么用,但是其实能做出来,也是很考验能力的,看来起好像这是个没什么用的项目,其实对我还是有很大帮助的,这个算法刚好帮我解决了另一个项目的大难题。如果是大一大二的同学,不妨试试看看自己能不能自己独立写出来,相信如果能写出来,对自身能力会是一个很大的提高。之前做了一个嵌入式Linux图像播放器,等空闲的时候写成博客分享出来。那么下一期预告就是:“嵌入式Linux图像播放器”,很酷,很难的哦,很有挑战哦。ps:(没想到写一个准备好的博客也是需要2个钟咧)

源码包

考虑到有些人是在IDE下运行的,我准备了两个包,一个是IDE运行版本,一个是命令台运行版本。源码里有说明。
链接:https://pan.baidu.com/s/1hR-3ehQbz-Bqr9oJoK8R4Q 密码:gv42

肯定会有很多错误和不足的地方,欢迎指正,我是石小星!

猜你喜欢

转载自blog.csdn.net/hotstaryar/article/details/82585884