一、引言
在物理、化学、自然科学等领域中,为了测量确定某一个粒子的运动轨迹,常常会使用云室,粒子在云室中运动后会留下一条运动轨迹,利用相机等获取一幅实验数据图,图中有时明显有时模糊地存在一条曲线,这条曲线就大致表示粒子的轨迹,解析运动轨迹后就能获得想要的数据了。
二、图像格式
利用摄像头获取的图像在经过数模转换后,它总是按一定的图像文件格式来提供信息,比较常用的有BMP格式、JPEG格式、GIF格式等等,其中BMP格式是各种图像格式的基础,其它的图像格式都能够通过转化变成BMP格式,因此本文只简单介绍BMP图像格式。
1. BMP文件组成
BMP文件由文件头、位图信息头、颜色信息和图形数据四部分组成。文件头主要包含文件的大小、文件类型、图像数据偏离文件头的长度等信息;位图信息头包含图像的尺寸信息、图像用几个比特数值来表示一个像素、图像是否压缩、图像所用的颜色数等信息。颜色信息包含图像所用到的颜色表,显示图像时需用到这个颜色表来生成调色板,但如果图像为真彩色,即图像的每个像素用24个比特来表示,文件中就没有这一块信息,也就不需要操作调色板。文件中的数据块表示图像的相应的像素值,需要注意的是:图像的像素值在文件中的存放顺序为从左到右,从下到上,也就是说,在BMP文件中首先存放的是图像的最后一行像素,最后才存储图像的第一行像素,但对与同一行的像素,则是按照先左边后右边的的顺序存储的;另外,文件存储图像的每一行像素值时,如果存储该行像素值所占的字节数为4的倍数,则正常存储;否则,需要在后端补0,凑足4的倍数。
2. BMP文件头
BMP文件头数据结构含有BMP文件的类型、文件大小和位图起始位置等信息。其结构定义如下:
typedef struct tagBITMAPFILEHEADER { WORD bfType; // 位图文件的类型,必须为“BM” DWORD bfSize; // 位图文件的大小,以字节为单位 WORD bfReserved1; // 位图文件保留字,必须为0 WORD bfReserved2; // 位图文件保留字,必须为0 DWORD bfOffBits; // 位图数据的起始位置,以相对于位图文件头的偏移量表示,以字节为单位 } BITMAPFILEHEADER;该结构占据14个字节。
3. 位图信息头
BMP位图信息头数据用于说明位图的尺寸等信息。其结构如下:
typedef struct tagBITMAPINFOHEADER{ DWORD biSize; // 本结构所占用字节数 LONG biWidth; // 位图的宽度,以像素为单位 LONG biHeight; // 位图的高度,以像素为单位 WORD biPlanes; // 目标设备的平面数不清,必须为1 WORD biBitCount// 每个像素所需的位数,必须是1(双色), 4(16色),8(256色)或24(真彩色)之一 DWORD biCompression; // 位图压缩类型,必须是 0(不压缩),1(BI_RLE8压缩类型)或2(BI_RLE4压缩类型)之一 DWORD biSizeImage; // 位图的大小,以字节为单位 LONG biXPelsPerMeter; // 位图水平分辨率,每米像素数 LONG biYPelsPerMeter; // 位图垂直分辨率,每米像素数 DWORD biClrUsed;// 位图实际使用的颜色表中的颜色数 DWORD biClrImportant;// 位图显示过程中重要的颜色数 } BITMAPINFOHEADER;该结构占据40个字节。
注意:对于BMP文件格式,在处理单色图像和真彩色图像的时候,无论图像数据多么庞大,都不对图像数据进行任何压缩处理。一般情况下,如果位图采用压缩格式,那么16色图像采用RLE4压缩算法,256色图像采用RLE8压缩算法。
4. 颜色表
颜色表用于说明位图中的颜色,它有若干个表项,每一个表项是一个RGBQUAD类型的结构,定义一种颜色。RGBQUAD结构的定义如下:
typedef struct tagRGBQUAD { BYTErgbBlue;// 蓝色的亮度(值范围为0-255) BYTErgbGreen; // 绿色的亮度(值范围为0-255) BYTErgbRed; // 红色的亮度(值范围为0-255) BYTErgbReserved;// 保留,必须为0 } RGBQUAD;
颜色表中RGBQUAD结构数据的个数由BITMAPINFOHEADER 中的biBitCount项来确定,当biBitCount=1,4,8时,分别有2,16,256个颜色表项,当biBitCount=24时,图像为真彩色,图像中每个像素的颜色用三个字节表示,分别对应R、G、B值,图像文件没有颜色表项。位图信息头和颜色表组成位图信息,BITMAPINFO结构定义如下:
typedef struct tagBITMAPINFO { BITMAPINFOHEADER bmiHeader; // 位图信息头 RGBQUAD bmiColors[1]; // 颜色表 } BITMAPINFO;
注意:RGBQUAD数据结构中,增加了一个保留字段rgbReserved,它不代表任何颜色,必须取固定的值为“0”,同时,RGBQUAD结构中定义的颜色值中,红色、绿色和蓝色的排列顺序与一般真彩色图像文件的颜色数据排列顺序恰好相反,即:若某个位图中的一个像素点的颜色的描述为“00,00,ff,00”,则表示该点为红色,而不是蓝色。
5. 位图数据
位图数据记录了位图的每一个像素值或该对应像素的颜色表的索引值,图像记录顺序是在扫描行内是从左到右,扫描行之间是从下到上。这种格式又称为Bottom_Up位图,当然与之相对的还有Up_Down形式的位图,它的记录顺序是从上到下,对于这种形式的位图,也不存在压缩形式。位图的一个像素值所占的字节数:当biBitCount=1时,8个像素占1个字节;当biBitCount=4时,2个像素占1个字节;当biBitCount=8时,1个像素占1个字节;当biBitCount=24时,1个像素占3个字节,此时图像为真彩色图像。当图像不是为真彩色时,图像文件中包含颜色表,位图的数据表示对应像素点在颜色表中相应的索引值,当为真彩色时,每一个像素用三个字节表示图像相应像素点彩色值,每个字节分别对应R、G、B分量的值,这时候图像文件中没有颜色表。上面已经讲过,Windows规定图像文件中一个扫描行所占的字节数必须是4的倍数(即以字为单位),不足的以0填充,图像文件中一个扫描行所占的字节数计算方法:
DataSizePerLine= (biWidth* biBitCount+31)/8;// 一个扫描行所占的字节数
位图数据的大小按下式计算(不压缩情况下):
DataSize= DataSizePerLine* biHeight。
上述是BMP文件格式的说明,搞清楚了以上的结构,就可以正确地操作图像文件,对它进行读或写操作了。
三、图像曲线像素提取
由于在云室中拍摄的图像,它的显示不是很清楚,如图1所示(图中图像已经做了强化处理,所以其边缘已显示的较清晰):
图1:原始实验图像
从图像中可以看出,曲线的轨迹呈现稍有规律:大约是一条某范围斜率的曲线,但是拍摄出来后,每一行相似点却不止一个,如何确定每一行曲线点的位置就是此问题的关键。在本方法中采用利用亮度的方法来提取点,在图1中由于已经作了强化处理,可以明显地看到每一行中曲线点只是在大约不到十个像素的范围内,但是也不能盲目地认为亮度最大的点就是所求点,但是所求点一定是在亮度最大的一些点附近。因此先求出某一行的亮度最大点,然后在此点周围一定范围内,只要是亮度值相差不超过某一域值的点都认为是可能点,最后求取这些可能值的平均值就认为是所求点。
本方法的实现过程如图2所示,分为图像预处理、确定起始点、确定最亮点、区分临界点、计算所求点、保存(显示)曲线轨迹。
图2:方法实现过程
1.图像预处理
由于云室所拍摄的图像比较模糊,曲线边界不清楚,这就有可能在后面的实现过程中陷入歧义,以致影响最后的数据效果。因此,对原始图像进行手动处理,得到曲线的大致走向,所以就在图像中曲线附近进行了强化颜色,使得图中的曲线与云室的其它部分进行分离。
2.确定起始点
程序运行需要一个开始点,在图像中的下部读出一个起始点作为开始值输入给函数。
3.确定最亮点
亮度的定义就是RGB值中的最大值,确定某一行最亮点的函数如下:
int CSlopeView::ConfirmPoint1(int x, int y)
{//返回值为曲线在当前行的像素值的X值
int i,j,r,g,b;
int Lum = 0;
j = -1;
for(i=-3;i<=3;i++)
{
image.GetColor(x+i,y,r,g,b);
if(Max(r,Max(g,b))>Lum)
{
Lum = Max(r,Max(g,b));
j = i;
}
else if(Max(r,Max(g,b))==Lum)
{
int m,k;
if(fabs(j)>fabs(i))
j = i;
else if(fabs(j)==fabs(i))
{
k = 0;
do
{
k++;
m = ConfirmPoint1(x,y+k);
if(fabs(m-j)<fabs(m-i))
j = j;
else if(fabs(m-j)>fabs(m-i))
j = i;
}
while(fabs(m-j)!=fabs(m-i));
}
}
}
return(x+j);
}
4.区分临界点
for(i = -3;i<=0;i++)
{
image.GetColor(x+i,y,r,g,b);
diff = Lum - Max(r,Max(g,b));
if(diff<DIFF&&!flag)
{
left = i;
flag = 1;
}
}
for(i = 0;i<=3;i++)
{
image.GetColor(x+i,y,r,g,b);
diff = Lum - Max(r,Max(g,b));
if(diff<DIFF)
{
right = i;
}
}
5.计算所求点
计算所求点就是将上一步所确定的临界点之间的所有点取平均值,这里就不再强调了。
四、实验结果分析
本方法用VC++6.0实现,操作系统Windows XP SP2,图3 是一组实验结果。从图3 中看,实验结果较好地拟合了原始图,由实验后曲线轨迹计算得到的曲线最大似然斜率也和理论值相差不大。
图3 :左图是原始实验图,右图是实验结果图
五、结语
实验数据图的分析在工程实验中是经常遇到,如果靠人为地确定每一个像素点,未免有些太麻烦,也有一些大型的图形软件可以对实验曲线进行提取,但是由于其功能较多,往往价格不菲。本文所采用的方法针对性强,实现简单,实验效果较好,操作简单,比较适合非计算机专业的实验人员处理实验数据图。
|