您的位置:首页 > 移动开发 > IOS开发

PKU-文件操作作业-BMP图像文件旋转

2012-03-24 13:36 429 查看
解题思路:

1.待处理的对象:

待处理的对象是BMP位图,实际上只是一种类型的文件流,和字符流什么的没有区别,这次练习的核心是为了锻炼出惊人的操作读写流的能力,所以,就将其当作01流就可以了。因此关于BMP最重要的就是要理解流的什么地方是什么,实际上文档上已经说得比较详细了。在此不再赘述BMP的基本理论,只重点讨论24位和32位位图。这里的位,实际上就是指一个像素格所占的位数(bit),需要知道的是1字节(Byte)=8bit,因此我们不得不回顾c++基本数据类型的占位。

2.c++基本数据类型的占位

char :1byte = 8bit

short:2byte = 16bit

int :4byte = 32bit

long :4byte = 32bit

所以下面的WORD和DWORD类型其实占位是一样的,可是为了保持文档中的叙述方式,再加上基本数据占位在不同编译器中有所差异,所以就这样吧

...

3.待处理对象的进一步讨论

从作业的文档中提取出来的有用的信息是

(1)BMP有一个文件头,见下面Head类的定义。这一串流占位14个字节(自己数)

(2)BMP有一个信息头,见下面Indo类的定义。这一串流占位40个字节(到这里是不是明白为什么文件头中图像数据的偏移一般是54了?)

(3)图像的像素数据,其中每一个像素格要么是RGB24的要么是RGB32的。RGB24就是一个像素占24位,这也就是普遍流行的24位图,24位是3个字节,分别就是为红、绿、蓝分配的字节。而RGB32多了一个字节,这是为alpha通道分配的,alpha通道是设置透明度的,故RGB32又叫做RGBa。我们只要分别定义这两种像素的基本结构就可以了。其余的颜色表什么的不用理会。在此有一个非常重要的理论,引用文档:“文件存储图像的每一行像素值时,如果存储该行像素值所占的字节数为4的倍数,则正常存储,否则,需要在后端补0,凑足4的倍数。”,拿24位位图来说,一个像素是3byte,如果一张图片的尺寸是37,那么就是111byte每行,按照这个规则,就要补1个0补成112位,如果正好被4整除了,那么就不补。由此我们至少知道两件事情:1、补0很麻烦,会影像流中有用数据的读取以及对转换后的图像数据也需要补0; 2、每行补0数量都是一样的。

4.转置的算法

实际上就是矩阵转置了,只是位图储存规则比较奇葩,倒着存什么的,自己想象加测试应该是没有问题的

5.泛型编程

我的旋转函数用了模板类型,因为处理24位和32位只有像素格的大小不同,其他都是一样的。在此有一个值得注意的地方就是32位位图不会出现补零的情况,套用24位的算法实际上是影像效率的

6.对齐边界

这是一个提示,之前我们了解到Head类型和Info类型的占位分别是14和40,前者不是4的倍数,为了防止读取流的时候Head被读取为16个字节,将对齐边界设置为16.其他值得注意的参考文档中有一处错误“002E-0031:此图像所用的颜色数,如值为0,表示所有颜色一样重要。”,002E-0031应该是002E-0035,从参考资料2可以看出来,不然从000E-0031只有36个字节了,少了4个

7.其他

32位的图像处理我还没有测试过!因为找不到图。。据说可以ps生成。。如果32位的我有错误的地方请一定评论下来

#include<iostream>
#include<fstream>
#include<cstring>
#include<string>

#pragma pack(1)

using namespace std;

typedef unsigned char BYTE;
typedef unsigned int DWORD;
typedef unsigned short WORD;
typedef long LONG;

class Head
{
public:
WORD type;          // 位图文件的类型,必为16进制的4D42,或10进制的19778
DWORD size;         // 位图文件整个文件的大小,有用
WORD r1;            // 保留字1,必为0,无用
WORD r2;            // 保留字2,必为0,无用
DWORD offBits;      // 图像数据偏移字节,一般来说是10进制下的54
};

class Info
{
public:
DWORD size;          // 信息头的大小,40,无用
LONG w;              // 图像的宽,单位是像素,重要
LONG h;              // 图像的高,单位是像素,重要
WORD biPlanes;       // 目标设备的平面数不清,必须为1,无用
WORD bitc;           // 每个像素所需的位数,必须是1(双色), 4(16色),8(256色)或24(真彩色)之一,重要
DWORD biCompression; // 位图压缩类型,必须是 0(不压缩),1(BI_RLE8压缩类型)或2(BI_RLE4压缩类型)之一,无用
DWORD biSizeImage;   // 位图的大小,以字节为单位,有用(注意和Head中的size的区别)
LONG biXPelsPerMeter;// 位图水平分辨率,每米像素数,无用
LONG biYPelsPerMeter;// 位图垂直分辨率,每米像素数,无用
DWORD biClrUsed;     // 位图实际使用的颜色表中的颜色数,无用
DWORD biClrImportant;// 位图显示过程中重要的颜色数,无用
};

class RGB24
{
public:
RGB24(){ green = 0; red = 0; blue = 0; }
BYTE green;
BYTE red;
BYTE blue;
};

class RGB32: public RGB24
{
public:
RGB32():RGB24(), alpha(0){};
BYTE alpha;
};
/* getDiff: 获取每行需要补的0的个数 */
int getDiff(Info& info)
{
int t = (info.w * info.bitc + 31) / 8;
t -= t % 4;
return  t - info.w * info.bitc / 8;
}

/* Trans: 核心函数,函数模板,BMP转置 */
template<typename T>
bool Trans(ifstream& src, ofstream& dest, Head& head, Info& info)
{
Head new_head = head;
Info new_info = info;
new_info.h = info.w;
new_info.w = info.h;

int diff = getDiff(info);
T* pic = new T[info.h * info.w];

for(int i = 0; i < info.h; i++)
{
src.read((char*) (pic + i * info.w), info.w * sizeof(T));
src.seekg(diff, ios::cur);
}

diff = getDiff(new_info);
char* null = new char[diff + 1];
memset(null, 0, diff + 1);
new_info.biSizeImage = new_info.h * (new_info.w + diff);
new_head.size = new_info.biSizeImage + sizeof(new_head) + sizeof(new_info);
T* new_pic = new T[new_info.w * new_info.h];

for(int i = 0; i < new_info.h; i++)
{
for(int j = 0; j < new_info.w; j++)
{
*(new_pic + (new_info.h - 1 - i) * new_info.w + j) =
*(pic + j * info.w + i);
}
}
dest.write((char *) &new_head, sizeof(Head));
dest.write((char *) &new_info, sizeof(Info));

for(int i = 0; i < new_info.h; i++)
{
dest.write((char*) (new_pic + new_info.w * i), new_info.w * sizeof(T));
dest.write((char*) null, diff);
}
return true;
}

int main(int argc, char* argv[])
{
char* src_name;
char* dest_name;
if(argc == 1)
{
cout << "use \"src.bmp\" as default input bmp file name, and \"dest.bmp\" as default ouput name\n";
src_name = new char[10];
dest_name = new char[10];
strcpy(src_name, "src.bmp");
strcpy(dest_name, "dest.bmp");
}
else
{
src_name = argv[1];
dest_name = argv[2];
}
Head head;
Info info;
ifstream src(src_name, ios::in|ios::binary);
if(!src)
{
cout << "文件读取失败~" << endl;
return 0;
}
ofstream dest(dest_name, ios::out|ios::binary);

src.read((char *) &head, sizeof(Head));
src.read((char *) &info, sizeof(Info));
/* 判断BMP的位数,分流 */
if(info.bitc == 24)
Trans<RGB24>(src, dest, head, info);
else
Trans<RGB32>(src, dest, head, info);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息