A、BMPファイル解析
1. BMP(ビットマップ)とは何ですか?
一般的な画像ファイル形式:BMP、JPG(JPE、JPEG )、GIF など。
BMP画像ファイル(ビットマップファイル)形式はWindowsがWindows環境下で実行されている全ての画像処理ソフトウェアを格納するために使用する画像のファイル形式です。この形式をサポートしています。Windowsの3.0 BMPファイルは、後でデバイスに依存しないビットマップ(DIB、デバイスに依存しないビットマップ)を参照してください 。BMPビットマップファイルのデフォルトのファイル拡張子は.BMPあり、時にはそれが拡張のために.DIBまたは.RLEがかかります。このように使用される任意の圧縮アルゴリズムを使用することなく、BMP形式の画像は、はるかに少ないと、今、より過去にあるが、画像処理アルゴリズムを学ぶために、その最初のファイル形式の一種で始まります。
ファイル構造2.BMP
一緒にイメージファイルを形成する4つのパーツは、4つの部分から構成BMPファイル、不可欠
ビットマップファイルヘッダ(ビットマップファイルヘッダ)
部分14バイトの合計。私たちは、形式、ファイルサイズやその他の情報を提供することができます
typedef struct tagBITMAPFILEHEADER
{
UINT16 bfType; // 说明位图类型 2字节
DWORD bfSize; // 说明位图大小 4字节
UINT16 bfReserved1; // 保留字,必须为0 2字节
UINT16 bfReserved2; // 保留字,必须为0 2字节
DWORD bfOffBits; // 从文件头到实际的图像数据的偏移量是多少 4字节
} BITMAPFILEHEADER; //一共16个字节
ビットマップ情報ヘッダ(ビットマップinformationheader)
typedef struct tagBITMAPINFOHEADER
{
DWORD biSize; // 说明该结构一共需要的字节数 2字节
LONG biWidth; // 说明图片的宽度,以像素为单位 4字节
LONG biHeight; // 说明图片的高度,以像素为单位 4字节
WORD biPlanes; //颜色板,总是设为1 2个字节
WORD biBitCount; //说明每个比特占多少bit位,可以通过这个字段知道图片类型 2个字节
DWORD biCompression; // 说明使用的压缩算法 2个字节 (BMP无压缩算法)
DWORD biSizeImage; //说明图像大小 2个字节
LONG biXPelsPerMeter; //水平分辨率 4字节 单位:像素/米
LONG biYPelsPerMeter; //垂直分辨率4字节
DWORD biClrUsed; //说明位图使用的颜色索引数 4字节
DWORD biClrImportant; //4字节
} BITMAPINFOHEADER; // 一共40个字节
真の色のビットマップとして、私たちの主な関心事は、これら2つの値biWidthとのbiHeightで、二つの値は私たちに、画像のサイズを教えてください。biSize、複葉機、biBitCountこれらの値は固定されています。怠惰になりたい、そして他のすべての値がゼロで充填することができます。
カラーテーブル(色テーブル)
直ちにヘッダ情報の背後にビットマップデータがカラーテーブルであるカラーテーブルが二次元アレイ状に形成され、行は4バイト、4つのバイトはR、G、B、アルファ(透明度を表していますチャンネルの成分)。我々は、これらのピクセルのビットマップデータによって着色することができます。ビットマップは、24ビットトゥルーカラー画像である場合には、何色のテーブルが存在しないことに留意されたいです。
ドットのビットマップデータ(ビットデータ)
ピクセルのカラー・テーブル・データの後ろに擬似カラー画像は、各画素その後、わずか8ビット、すなわち1バイトBMPである場合、です。BMPは、24ビットトゥルーカラーイメージの場合、各ピクセルは、24で表される、つまり3バイト、3は、B、G、Rの色成分それぞれバイト。
さらに、我々は、そのピクセルの配置を左から右へ、下からである、BMPビットマップを注意してください。つまり、私たちは、ビットマップデータの最初のバイトは、左下隅の画素値で読み。
私は、Windowsシステムが「ゼロパディング」癖を持っている、ことに注意する必要はありませんでした!バイトの数は、ビットマップが4で割り切れなければならない必要があり、画素の各列によって占有されます。それが「00」のビットマップ「フィル」1-3バイトの各行の16進コードの終わりに、4倍で割り切れない場合 次に例を示します。
この写真の下に256色の疑似カラー画像、501 * 502のサイズです。
今度は、理論上のサイズを計算してみましょう
像素数量=501 * 502=251502
像素所占字节=像素数量=251502个字节
文件信息头=14个字节
位图信息头=40个字节
颜色表=256种颜色*4=1024个字节
理论大小=251502+14+40+1024=252580个字节
しかし実際には、本当のサイズは= 254086のバイト
この中間の差、及び差分254086-252580 = 1506バイト=
彼らはそれどこから来た1506年のこの違いはバイト?私が前に言ったので(4つのバイトをスキャンするために使用さの窓があるため)、各ラインの画素幅が4で割り切れない場合は、その後、いくつかの追加、4の倍数でなければなりません、Windowsシステムは、「ゼロパディング」癖があります0ピクセルは、であるとラインアップ当たり4つの画素で割り切れる知っています。
画像対502の幅は、ジョブが3つの画素我々が望むライン当たり(すなわち、3バイト)、最終的な差分値= 3×502 = 1506バイトが追加され、4で割り切れません
BMPファイルを読むためのpythonで第二に、
バイト変換
Pythonでバイナリファイルを読み込むときに読む前に、私たちはどのようにバイト変換(のreadIntでJavaに似た、のreadFloatなど)、理解する必要があります。私たちは、Pythonで使用することができますstruct模块
。このモジュールでは、2つの重要な機能がありますpack()
、unpack()
機能包装:pack(fmt, v1, v2, v3, ...) 这个函数可以把各种类型的数据打包成字节
開梱機能:unpack(fmt, buffer) 这个函数可以把二进制数据转换成各种数据类型
上記の二つの機能を通じ、それは似たのreadInt、のreadFloat効果を得ることができます
コードの実装
首先,要用到三个包,numpy
是用来装像素数据和颜色表的,struct
是用来进行字节转换的,matplotlib
我们则用来显示最终的图片的
下面部分的代码,逐字节的进行读取,暂时未进行转换,是纯字节数据。我们读取了一副图像里最关键的几个信息,忽略了几个无用的信息
下面的这一部分代码就是将上面读取的字节数据转化为指定类型,通过fmt符
来转换成我们想要的数据类型。 在这里值得一提的是,windows里的数据是小端模式,也就是低位字节在前,高位字节在后。比如说 存储在计算机里的"3c 05" 这两个字节,实际上是"053c"
读取完信息头之后,我们就可以来读取颜色表了。需要注意的是,bmp位图读取颜色表的时候,读取的颜色分量的顺序不是R、G、B,而是B、G、R!这个一定要注意。因为我们是用matplotlib来显示图像的,所以我们还增加了一个255的分量(实际上没什么用)
然后就是来读取位图数据了,读取位图数据的时候,我们一定要注意,数据的排列方式是从左到右,从下到上!还有一个while循环,是用来判断行像素是否为4的倍数,如果不是我们还要将填充的用字节给读取扔掉
最后再把图像显示出来,即可
最终效果图
import numpy as np
import struct
import matplotlib.pyplot as plt
def main():
'先将位图打开'
f=open('1_256.bmp','rb') #打开对应的文件
'下面部分用来读取BMP位图的基础信息'
f_type=str(f.read(2)) #这个就可以用来读取 文件类型 需要读取2个字节
file_size_byte=f.read(4)# 这个可以用来读取文件的大小 需要读取4个字节
f.seek(f.tell()+4) # 跳过中间无用的四个字节
file_ofset_byte=f.read(4) # 读取位图数据的偏移量
f.seek(f.tell()+4) # 跳过无用的两个字节
file_wide_byte=f.read(4) #读取宽度字节
file_height_byte=f.read(4) #读取高度字节
f.seek(f.tell()+2) ## 跳过中间无用的两个字节
file_bitcount_byte=f.read(4) #得到每个像素占位大小
#下面就是将读取的字节转换成指定的类型
f_size,=struct.unpack('l',file_size_byte)
f_ofset,=struct.unpack('l',file_ofset_byte)
f_wide,=struct.unpack('l',file_wide_byte)
f_height,=struct.unpack('l',file_height_byte)
f_bitcount,=struct.unpack('i',file_bitcount_byte)
print("类型:",f_type,"大小:",f_size,"位图数据偏移量:",f_ofset,"宽度:",f_wide,"高度:",f_height,"位图:",f_bitcount)
'然后来读取颜色表'
color_table=np.empty(shape=[256,4],dtype=int)
f.seek(54) #跳过文件信息头和位图信息头
for i in range(0,256):
b=struct.unpack('B',f.read(1))[0];
g = struct.unpack('B', f.read(1))[0];
r = struct.unpack('B', f.read(1))[0];
alpha = struct.unpack('B', f.read(1))[0];
color_table[i][0]=r
color_table[i][1]=g
color_table[i][2]=b
color_table[i][3]=255
'下面部分用来读取BMP位图数据区域,将数据存入numpy数组'
#首先对文件指针进行偏移
f.seek(f_ofset)
#因为图像是8位伪彩色图像,所以一个像素点占一个字节,即8位
img=np.empty(shape=[f_height,f_wide,4],dtype=int)
cout = 0
for y in range(0, f_height):
for x in range(0,f_wide):
cout=cout+1
index=struct.unpack('B',f.read(1))[0]
img[f_height-y-1,x]=color_table[index]
while cout %4 !=0:
f.read(1)
cout=cout+1
plt.imshow(img)
plt.show()
f.close()
if __name__ == '__main__':
main()
后续会继续写一些图像处理的算法,并且将其集成一个小小的框架