数字图像处理编程之一:熟悉BMP图像格式

来源:互联网 发布:adobe audition mac 编辑:程序博客网 时间:2024/05/17 03:28

我又在搞最令人头痛的编程了。这回是老刘的课题,老刘平时虽然没什么压力加给我,但是他给的任务,会让你自己感到压力,这就是我为什么头痛加累累累了。


APC项目(Automatic Passenger Counter Project),就是实现自动对客车上的乘客人数进行统计,(本例中的BMP图像是24位DIB图)利用视频采集、图像处理与模式识别技术来实现。初步有两个想法:一是灰度化,进行加阈值和二值化处理特征提取等手法进行;二是光流法。


我走其中一条路:光流法。由于乘客的运动,其实是三维的运动,用光流法未免有点冒险,但是,考虑到乘客在上下车时的运动二维运动占主要,以及实践性方面的因素。我还是接纳这种方法,其实,我务必这么做。老刘说的,因为。


我开始是用“VC++ 数字图像处理”上的一个与设备无关的BMP图像类进行编程,即是CDib类,没想到后面麻烦那么多。这个类其实是面向读取图像来处理,实现读取、操作和显示到设备上;但是对于创建Dib则没有处理。我只好另开途径了。当然,这需要我对BMP图像作一番了解。


经过我一段时间的了解加上我的个人编程体会,有下面这几点在图像处理上相当重要:


1.不论是用别人的CDib类还是自己创建CDib类,都要对BMP图像格式作一番了解;在了解BMP架构的同时,最好了解它的读取与存储机制。这些在实际的图像操作时,有很大帮助:如图像的读取与存储都是将BMP图像的数据通过VC机制上的文件功能(file)来实现的,那么BMP(具体到我这个课题是DIB图像)的结构就要作很大了解了。


2.构建自己的CDib类会更好些。一般地,这样更易于帮助实现特定的功能。其实,CDib类应该是一些通用功能,这样会有更强的移植性。在我这个实例中,用到图像的缩放,我是想集成到这个类中,而通过输入缩放参数来得到结果,并能实现存储。


3.转换成灵活的架构。在DIB图像中,是这样存放象素数据的:一个显示界面以二维方式表示的话是X*Y的矩形窗,在DIB数据中,一个象素可能就对应三个字节(24Bits),这样就要是说并非X*Y就是DIB文件存储区中实际像素的存储区。而是要作特殊处理,根据按行显示的方式来计算,这个存储区的“长”应该为RealWidth = (31 + X*24)/32*4。当然这里是针对24位图像来说的,如果是8位图像则将当中的24转成8即可。从而际的存储区大小为:RealWidth×Y(Bytes)。在操作时也要按这个矩阵来轮循处理。为了转换成灵活的架构,可以考虑将BMP中的一维指针的格式转成二维的格式,得出的结果是(针对24位图)Array[RealWidth][Y],而每个Array中的元素对应是包含有三个Byte型的R、G、B的结构体,一个Byte则表示一个R或G或B的分量,这样应用起来比较方便。


4.整理方便的处理方式。我是这样进行的:将DIB图像分成二个块--一是Lpbi表示位图的BITMAPINFOHEADER,另个就是LpDIBBits表示位图的象素数据。这是两个指针,通常定义成LPSTR,即是BYTE*。在实际运用时有时候要将Lpbi转成LPBITMAPINFOHEADER。因为两个DIB中内容的区别就体现在这两个块上,特别是在显示时,只要得到这两块数据就可以显示OK了,比如Lpbi中的DIB的长和宽,还有就是LpDIBBits中的象素数据了。而至于另外一块,BITMAPFILEHEADER只要在保存的时候,将正确数据写入File中就OK了。当然还有一个块不可忽略就是调色板,如果不是24位或32位图,那么在位图头信息中还要加处一个

Palattee。在我这个实例中不作考虑,没有调色板。

OK,下面有BMP图像格式的详细分析,来自一个网页:http://glaciersl.blog.hexun.com/1310303_d.html。

在编程过程中遇到几个有趣的问题:
第一是,在使用CString时,对中文字符串的处理上,使用.Left/.Right/.TrimLeft/.TrimRight并不好用,有时甚至不能截去相关字符。而使用.Delete则可以实现对中文字符串的删除。这估计是一个漏洞,其对英文和数字字符是可以有效实现的
第二是,LPSTR tlpDIBBits = viewdib.PicToDib(pic,mbi,lpDIBBits);这里其中的mbi是一个BITMAPINFOHEADER变量。而定义中有一个变量是引用型的:LPSTR WINAPI CDib::PicToDib(picture pic,BITMAPINFOHEADER &inbi,LPSTR lpDIBBits)。这样的调用可以有效的实现不用返回就在子程序中改变mbi的值。相当于一个子程序可以返回两个参数。
第三点,在图像的读取与显示时,我采用了头信息与像素信息、句柄参数两种方式进行读取或显示。

补充一点:在后来的实践中,我发学利用两个指针来处理DIB图并不是很方便,在编程时,传递的参数少会效率有很大的帮助。后来转换成一个指针,LpDIB用来指向DIB的信息头(注意不是文件头,文件头一般地都在保存和读取的时候用到,而在显示和算法处理时不用到所以Drop!)。这样在得到LpDIB时,可以同时得到BITMAPINFOHEADER的信息,得像素信息。同时,图像的长宽参数和位宽等都包含在这个指针的区间里了。还有一点就是如果算法中遇到有不断的要开辟内存空间的,要注意在适当的地方Delete掉。否则应用功能不强。
 

原创粉丝点击