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位的我有错误的地方请一定评论下来
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; }
相关文章推荐
- DICOM医学图像处理:DICOM存储操作之“多幅BMP图像数据存入DCM文件”
- 【数字图像处理】C++读取、旋转和保存bmp图像文件编程实现
- C++读取、旋转和保存bmp图像文件编程实现
- C++读取、旋转和保存bmp图像文件编程实现
- 通过操作BMP文件的RGB值实现BMP文件的旋转,锐化,二值化,轮廓提取,边缘检测,合并等功能(二)
- bmp图片文件的操作 旋转、翻转、放大缩小
- 【数字图像处理】C++读取、旋转和保存bmp图像文件编程实现
- C++读取、旋转和保存bmp图像文件编程实现
- 【数字图像处理】C++读取、旋转和保存bmp图像文件编程实现
- C++读取、旋转和保存bmp图像文件编程实现
- DICOM医学图像处理:DICOM存储操作之“多幅BMP图像数据存入DCM文件”
- bmp图片文件的操作 旋转、翻转、放大缩小
- 【数字图像处理】C++读取、旋转和保存bmp图像文件编程实现
- 通过操作BMP文件的RGB值实现BMP文件的旋转,锐化,二值化,轮廓提取,边缘检测,合并等功能(一)
- 【数字图像处理】C++读取、旋转和保存bmp图像文件编程实现(转载)
- C++ bmp文件读写操作
- BMP文件结构读写操作
- 怎样显示Jpg,bmp,Gif图像文件
- DICOM医学图像处理:DICOM存储操作之 “多幅JPG图像数据存入DCM文件”
- BMP图像文件到CCS中DAT文件的转换