BMP文件操作方法(一个月研究出来的)
BMP文件操作方法(一个月研究出来的)
2011年07月20日
大家可能会问,我这一个月去干嘛啦?(看一下上一篇日志的时间)刚好一个月。我在进行计算机特训,研究BMP文件(在老师不允许的情况下)。下面是针对pascal的bmp的操作:
大家都知道,Pascal最初开发可不是专用的,它可以用来编制各式的程序,甚至带图像的。
以前有很多人问我说,怎样读入一个bmp位图文件,并在pascal画图模式下显示出来?我也给过不少答复,但是都无一例外地使用了TurboPascal与指针,以及一堆一般只能理解60%的语句。(因为我当时还在TP时代)但是不可否认的是TP的graph单元最多只有800*600分辨率,而现有图片一般都在1000*1000以上,而且TP内存也伤不起,指针操作复杂易错易崩溃,代码过程艰险难懂难移植。鉴于现在已经全民进入FP时代,我研究了几天,就从最基础的赋值(:=)语句和FP的Windows Graph Application讲起吧,相信每个学了竞赛的人都能懂,以便推广社会,造福大众。
FP最大能支持4G的内存,但是对于数组却支持有限。大家知道,一个24位位图文件的内容主要是像素的红,绿,蓝值,范围从0-255表示相应颜色的亮度。因为每种颜色占8位二进制数,三个共占24位,所以叫24位位图。经多方实验,我们可定义以下数组来装载像素信息:
var map:array[0..1800,0..1800,1..3]of byte;
其中,两个0..1800分别表示横纵像素,1..3表示三种颜色。当然,如果你只读取一幅一般的图片,不进行旋转等操作,你也可以定义成2000*1500的。这里我们用数字来储存而不用字符,因为在进行图像处理时一般是对数字的运算。
我们还需要两个数来储存当前图像的长宽,因此……
var x,y:word;
大家还知道,位图文件不是一开始就是像素信息,它有一个文件头,24bit位图文件头长度标准是54字符,因此还要有一个数组装文件头(文件头是干嘛用的稍后讲到):
var head:array[1..54]of char;
文件头一般不用处理,所以就用字符储存。
当然,文件本身还需定义:
var f:file of char;{字符文件类型}
s:string; {文件名}
用file of char可以轻松读入一个个字符。
当然,肯定还要定义一堆i,j,k一类的,这里就不再说了。
//===========================================================================
好了,假设我们有一个位图文件叫做jenemy.bmp。我们要读入它(把它放到那个map数组里),首先:
begin{总开始}
readln(s);{读入文件名,例如这里读入jenemy.bmp}
assign(f,s);reset(f);{做好文件读入准备}
for i:=1 to 54 do read(f,head);{读入文件头}
close(f);
{在开始先读入文件头,以获取关于图片的信息}
接下来,先要判断该文件是否为位图文件。文件头前两个字符是“BM”,是位图文件的标志。因此:
if (head[1]'B')or(head[2]'M') then halt;{不是“BM”开头,就退出}
接下来,我们操作的是24位位图,要先判断是否为24位真彩色位图。文件头第29个字符包含了这一信息。如果是n位位图,则ord(head[29])就等于n。那么:
if ord(head[29])24 then halt;{不是24位位图,就退出}
然后,就可以肯定该文件是24位bmp位图了。首先我们要得到图片的长宽,而图片长宽信息分别保存在head[19..22]和head[23..26],使用256进制保存,所以需要一点小计算:
x:=ord(head[19])+ord(head[20])*256+ord(head[21])*65536+ord(head[22])*16777216;
y:=ord(head[23])+ord(head[24])*256+ord(head[25])*65536+ord(head[26])*16777216;
其中,65536=256^2,16777216=256^3。计算出图片的长宽x,y。
//============================================================================
好了,文件头我们只用管这么多就够了。接下来就是读入像素信息的时候了。像素信息由相邻的三个字符组成,储存方式是左下到右上。(这点要特别注意,要不然读入的方向是反的)而且一行像素读完后,如果该行的字符数不是4的倍数,要用chr(0)补成最接近的4的倍数。我开始就是没注意到这一点,结果读入不是4的倍数宽度的图片时总是歪七八扭的,一定要小心。
我们定义一个临时字符来储存补齐用的空字符:
var temp:char;
再定义三个用来中途运算的字符:
var c1,c2,c3:char;
好了,开始读入:
assign(f,s);reset(f);
for i:=1 to 54 do read(f,temp);{跳过文件头,用seek过程也可}
for j:=0 to y-1 do {注意是从0到y-1,共y个}
begin
for i:=0 to x-1 do {同上}
begin
read(f,c1,c2,c3); {读入三个相邻字符}
map[i,y-j-1,3]:=ord(m1);{转换蓝色}
map[i,y-j-1,2]:=ord(m2);{转换绿色}
map[i,y-j-1,1]:=ord(m3);{转换红色}{注意颜色顺序是B,G,R}
end;
for i:=1 to (4-(x*3)mod 4)mod 4 do read(f,temp);{补齐成为4的倍数}
end;
close(f);
注意补齐那一句,其效果是如果余1就读3个,如果余2就读2个,如果余3就读1个,如果余0就不读。
//==========================================================================
我们已经成功地读入了一个文件了(map里,不信自己看)。接下来是不是该把它画出来了呢?
要画图,首先还是要用到graph单元。只是FP的graph单元已经窗口化了,像素值随分辨率不同而不同。但是,我们还是得先开启graph单元。
在程序最最最开始打:
uses graph;
var gd,gm:smallint;{用于开启图像模式的变量}
然后开启图像模式:
gd:=detect;{储存颜色数量,一般是256}
initgraph(gd,gm,'');{以gd颜色数量,无窗口模式,无附加驱动开启图像模式}
注意gm是必须的。
然后就该绘制图形了。但是我们是24位真彩色,而FP的传统单元只支持256色,怎么办呢?没关系,只要图像是按24bit的算法处理的,显示出来是256色也没关系。这里就涉及到一点:怎么把24bit颜色转换成8bit(256)颜色呢?
实际上,这256种颜色并不是都可用的,能用的只有192种混合色和16种黑白色共208种(黑心的graph……)。但是只有这些,显示一个图片大体上不成问题。那么对于每一个像素的颜色,我们都要找到这208种颜色中最接近的颜色来匹配。说到“最近”,(至少是我)很容易想到初中数学课的一个内容:方差!没错,通过计算不同颜色的R,G,B方差,我们就可以找到相应的最接近颜色。而对于RGB差的绝对值均在20以内的,基本都被人眼看为灰色,可以直接按黑白色处理。
其中,192种混合色颜色号范围是32-247。因此,我们把它保存在一个文件(在这里用rgb.dat)。它包含这192种颜色的RGB信息,内容见附表……格式是R(空格)G(空格)B。
//========================================================================
我们再定义一个数组保存192个RGB信息:
var color:array[32..247,1..3]of byte;
然后在程序开始时就读入:
assign(f,'rgb.dat');reset(f);
for i:=32 to 247 do read(f,color[i,1],color[i,2],color[i.3]);
close(f);
然后我们编写一个函数colortable来实现24bit向8bit的转化(利用方差):
function colortable(r,g,b:byte):byte;
var i:byte;
diff:longint;{用来保存最小方差}
begin
if (abs(r-g)bmp文件。假设是abc.bmp吧。
还是先操作一下文件:
readln(s);{读入要保存的文件名,这里读入abc.bmp}
assign(f,s);rewrite(f);{建立新文件}
我们接下来就该向文件中写文件头了。要写哪些呢?前两位肯定先写“BM”没错:
write(f,'BM');
接下来4个字符便代表了整个文件的大小(字节数,256进制)。于是,我们定义以下一些东西:
var size:longint;{整个文件大小}
s1,s2,s3,s4:longint;{四个256进制的位数}{其实这两行可写成一行,都是longint}
然后计算文件大小,并保存:
size:=x*y*3+54;{像素数乘以3加上文件头长度54}
s1:=size mod 256;size:=size div 256;
s2:=size mod 256;size:=size div 256;
s3:=size mod 256;size:=size div 256;
s4:=size mod 256;{将size转化为256进制}
write(f,chr(s1),chr(s2),chr(s3),chr(s4));{写入文件长度}
接下来的东西你不管为什么,照着写就是了。{是跟文件头有关的一些信息}
write(f,chr(0),chr(0),chr(0),chr(0));
write(f,chr(54),chr(0),chr(0),chr(0));
write(f,chr(40),chr(0),chr(0),chr(0));
然后就到了我们的“19-22位,23-26位”,用来保存长宽的:
size:=x;{将长化为256进制}
s1:=size mod 256;size:=size div 256;
s2:=size mod 256;size:=size div 256;
s3:=size mod 256;size:=size div 256;
s4:=size mod 256;
write(f,chr(s1),chr(s2),chr(s3),chr(s4));{写入长}
size:=y;{将宽化为256进制}
s1:=size mod 256;size:=size div 256;
s2:=size mod 256;size:=size div 256;
s3:=size mod 256;size:=size div 256;
s4:=size mod 256;
write(f,chr(s1),chr(s2),chr(s3),chr(s4));{写入宽}
注:所有的 size div 256 均可用位运算的 size shr 8 代替,速度会快一些。
接下来的东西你还是可以不管为什么,照着写就是了。{是跟颜色有关的信息}
write(f,chr(1),chr(0),chr(24),chr(0));
for i:=1 to 8 do write(f,chr(0));
for i:=1 to 2 do write(f,chr(196),chr(14),chr(0),chr(0));
for i:=1 to 8 do write(f,chr(0));
计算一下,是不是刚好54位啦?
文件头写完,该写正文了。还是按照“左下到右上”的规律保存。别忘了补齐一些位数,以免保存的文件打不开:
for j:=y downto 1 do {下到上}
begin
for i:=1 to x do {左到右}
write(f,chr(map[i,j,3]),chr(map[i,j,2]),chr(map[i,j,1]));{蓝绿红,不要忘}
for i:=1 to (4-(x*3)mod 4)mod 4 do write(f,chr(0));{补齐位}
end;
最后别忘了:
close(f);
end.{总结束}
好啦,我的研究也讲完啦!实际测试打开一个1600*1200的文件,保存一个1600*1200的文件1s都不到,绘制一个1600*1200的24bit彩色文件只需约5s。
希望大家能灵活运用bmp文件操作方法,制作更精美的程序!
附表:rgb.dat 内容:
0 0 252
64 0 252
124 0 252
188 0 252
252 0 252
252 0 188
252 0 124
252 0 64
252 0 0
252 64 0
252 124 0
252 188 0
252 252 0
188 252 0
124 252 0
64 252 0
0 252 0
0 252 64
0 252 124
0 252 188
0 252 252
0 188 252
0 124 252
0 64 252
124 124 252
156 124 252
188 124 252
220 124 252
252 124 252
252 124 220
252 124 188
252 124 156
252 124 124
252 156 124
252 188 124
252 220 124
252 252 124
220 252 124
188 252 124
156 252 124
124 252 124
124 252 156
124 252 188
124 252 220
124 252 252
124 220 252
124 188 252
124 156 252
180 180 252
196 180 252
216 180 252
232 180 252
252 180 252
252 180 232
252 180 216
252 180 196
252 180 180
252 196 180
252 216 180
252 232 180
252 252 180
232 252 180
216 252 180
196 252 180
180 252 180
180 252 196
180 252 216
180 252 232
180 252 252
180 232 252
180 216 252
180 196 252
0 0 112
28 0 112
56 0 112
84 0 112
112 0 112
112 0 84
112 0 56
112 0 28
112 0 0
112 28 0
112 56 0
112 84 0
112 112 0
84 112 0
56 112 0
28 112 0
0 112 0
0 112 28
0 112 56
0 112 84
0 112 112
0 84 112
0 56 112
0 28 112
56 56 112
68 56 112
84 56 112
96 56 112
112 56 112
112 56 96
112 56 84
112 56 68
112 56 56
112 68 56
112 84 56
112 96 56
112 112 56
96 112 56
84 112 56
68 112 56
56 112 56
56 112 68
56 112 84
56 112 96
56 112 112
56 96 112
56 84 112
56 68 112
80 80 112
88 80 112
96 80 112
104 80 112
112 80 112
112 80 104
112 80 96
112 80 88
112 80 80
112 88 80
112 96 80
112 104 80
112 112 80
104 112 80
96 112 80
88 112 80
80 112 80
80 112 88
80 112 96
80 112 104
80 112 112
80 104 112
80 96 112
80 88 112
0 0 64
16 0 64
32 0 64
48 0 64
64 0 64
64 0 48
64 0 32
64 0 16
64 0 0
64 16 0
64 32 0
64 48 0
64 64 0
48 64 0
32 64 0
16 64 0
0 64 0
0 64 16
0 64 32
0 64 48
0 64 64
0 48 64
0 32 64
0 16 64
32 32 64
40 32 64
48 32 64
56 32 64
64 32 64
64 32 56
64 32 48
64 32 40
64 32 32
64 40 32
64 48 32
64 56 32
64 64 32
56 64 32
48 64 32
40 64 32
32 64 32
32 64 40
32 64 48
32 64 56
32 64 64
32 56 64
32 48 64
32 40 64
44 44 64
48 44 64
52 44 64
60 44 64
64 44 64
64 44 60
64 44 52
64 44 48
64 44 44
64 48 44
64 52 44
64 60 44
64 64 44
60 64 44
52 64 44
48 64 44
44 64 44
44 64 48
44 64 52
44 64 60
44 64 64
44 60 64
44 52 64
44 48 64
猜你喜欢
转载自kuh796xv.iteye.com/blog/1357714
今日推荐
周排行