参考知识:https://www.runoob.com/cplusplus/cpp-files-streams.html(文件操作)
https://www.cnblogs.com/wainiwann/p/7086844.html(BMP文件详解)
数据集上网找bmp真彩色的有很多。
编译环境:VS2017
最近觉得学信号与系统,数字信号处理学的太枯燥了,想写一些数字图像处理的算法。作为一名底层的码农,那要做的入门第一步,我觉得就是得先把图像文件加载进内存。
为什么采用C++,其实我也用了MATLAB,但MATLAB只有double型,而且效率也不高,实际运用肯定是C++的多。为什么不用opencv,在下才疏学浅,目前还没掌握。未来可能会学。
为什么选择BMP格式文件,我们常见的图像文件格式其实有JPG呀,TIFF那些。选择BMP是因为首先它是没有压缩过的,更接近于原始的图像,第二它的格式非常的简单,你看了参考知识就发现真的非常简单。其他格式你可能得写解码,对初学很不友好第三是图像处理的经典教材数字图像处理冈萨雷斯版的原始图像BMP格式可以在网上找的到。对了再啰嗦一句,别找有着色版的BMP文件,那个好像只有256个颜色,也有可能更多,主要它用了映射表,后面每个像素都是查表得到值,离原始图像有点远了。废话说多了,来看下操作吧!
#include "pch.h"
#include <iostream>
#include <fstream>
#include<string>
using namespace std;
typedef char byte;
class bmp
{
byte image_type[2];
byte int_temp[4];
byte short_temp[2];
int image_width;
int image_heigth;
short image_depth;
int image_size;
int horizontal_resolution;
int vertical_resolution;
long pixel_count;
int offset;
int byte_to_int(byte *p);
short byte_to_short(byte *p);
public:
bmp(string filen_ame);
void show();
};
int bmp::byte_to_int(byte *p)
{
int value=0;
for(int i=3;i>=0;--i)
{
value = value << 8;
value+=unsigned char(p[i]);
}
return value;
}
short bmp::byte_to_short(byte *p)
{
short value=0;
for(int i=1;i>=0;--i)
{
value = value << 8;
value+=unsigned char(p[i]);
}
return value;
}
void bmp::show()
{
cout << "该图像文件大小为:" <<image_size<<"字节"<< endl;
cout<<"该图像高度为:"<<image_heigth<<"个像素"<<endl;
cout<<"该图像宽度为:"<<image_width<<"个像素"<<endl;
cout << "该图像像素数:" << pixel_count << endl;
cout<<"该图像单个像素位宽为:"<<image_depth<<endl;
cout << "该图像数据距离文件头" << offset << endl;
cout<<"该图像垂直分辨率:"<<vertical_resolution<<endl;
cout<<"该图像水平分辨率:"<<horizontal_resolution<<endl;
}
bmp::bmp(string filen_ame)
{
fstream fp;
fp.open(filen_ame, ios::in|ios::binary);
if (!fp.is_open())
{
cout << "文件打开失败"<<endl;
return;
}
fp.get(image_type[0]);
fp.get(image_type[1]);
if(image_type[0]!='B'||image_type[1]!='M')
{
cout<<"此文件图像不是BMP格式";
fp.close();
return;
}
for(int i=0;i<=3;++i)
fp.get(int_temp[i]);
image_size=byte_to_int(int_temp);
fp.seekg(0xA,ios::beg);
for(int i=0;i<=3;++i)
fp.get(int_temp[i]);
offset=byte_to_int(int_temp);
fp.seekg(0x12,ios::beg);
for(int i=0;i<=3;++i)
fp.get(int_temp[i]);
image_width=byte_to_int(int_temp);
for(int i=0;i<=3;++i)
fp.get(int_temp[i]);
image_heigth=byte_to_int(int_temp);
pixel_count = image_width * image_heigth;
fp.seekg(0x1C,ios::beg);
for(int i=0;i<=1;++i)
fp.get(int_temp[i]);
image_depth=byte_to_short(int_temp);
fp.seekg(0x26,ios::beg);
for(int i=0;i<=3;++i)
fp.get(int_temp[i]);
horizontal_resolution=byte_to_int(int_temp);
for(int i=0;i<=3;++i)
fp.get(int_temp[i]);
vertical_resolution=byte_to_int(int_temp);
fp.close();
}
int main()
{
string file_name = "C:\\Users\\hj\\Desktop\\DIP\\DIP3E_Original_Images_CH11\\Fig1128(a)(superconductor-smooth-texture)-DO NOT SEND.bmp";
bmp a(file_name);
a.show();
}
原谅我不写注释,代码非常的短,但已经可以拿到所有的信息,最核心的思想就是一个一个字节的读取文件的内容。这里注意char和unsigned char其实你从文件读出来并没有区别不大,我看好多人用unsigned char,但C++内置文件读取操作都是针对char,其实char也可以赋值255,个人愚见unsigned char和char的主要区别其实是解释。就你cout的时候,如果你是char型,cout是输出字符的,unsigned char就直接输出编码的值,但都是相同的编码。
还有就是我的那个byte to int,我其实很想干的一件事情就是直接读编码读到int型的数据里去,但好像c++没有内置的函数,所以我用了个byte int temp就是先用字符数组读取了文件的4个字节的编码,然后就通过移位操作的手段把它弄到int型里去。其实byte to int 不太妥当,我更觉得这个应该叫4个离散的字节数据组装成一个合起来的4字节数据。
还有就是如何以一个二进制的形式打开文件,方法为我用的VS,你点文件,然后注意打开文件右下角打开哪里你可以选择打开方式选二进制编辑器就行了
打开后是这样子的:
展示:
这是我C++从内存拿到的数据
和我查看属性是一样的。其实你只要会从底层16进制操纵文件,你可以拿到文件的任何信息。对了一个建议文件流少要用<<和>>,我只能说我碰到了奇怪的BUG,感谢你的阅读!