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