断电续传:IDL教程–六

来源:百度文库 编辑:偶看新闻 时间:2024/06/11 11:55:16
IDL教程–六(2008-10-13 10:57:47) 标签:学习 杂谈  分类:IDL编程学习 读写非格式化数据
迟早,数据会越来越多。若这样,就要开始思考更好的数据储存方法。非格式化数据(有时叫二进制数据)比格式化数据紧凑得多,经常用于大数据文件。有两种命令读写非格式化数据,它们与早期用来读取格式数据文件的ReadF和 Print 命令等效。它们是ReadU和WriteU命令。
非格式化数据文件基本是以一长串的二进制字节存在文件中。这些字节的含义(也就是说,这些字节如何翻译成特定数据类型和结构的)很艰难的描述的,除非刚开始就知道文件写入的是什么内容。在文件里面,各字节都很相似。将字节读入正确类型和结构的变量中就可理解了。原则上这很容易做到,因为多数数据类型有给定的字节长度。例如,每个浮点值有4个字节。IDL整数有两个字节,等等。
要读取非格式数据文件,简单定义变量,打开文件读取,并用ReadU命令将字节一个接一个地读入变量中。如果给定了变量的数据类型和组织结构,每个变量按其要求从文件中读出相应的字节数。例如,一个5个元素的浮点矢量将从文件中读取五(元素数)乘四(一个浮点值的字节数)共二十个字节。
读取非格式化图像数据文件
例如,假设想读取储存在coyote子目录下的几个非被格式化图像数据文件中的一个。这些文件碰巧包含的是字节型数据,但它们也能容易地包含整数与浮点数。下面的一些命令被用来打开其中之一,galaxy.dat文件。星系图像的字节已被组织成一个256*256字节的数组。(这个代码已假定 coyote子目录存在于IDL主目录中。)
IDL>filename=Filepath(Root_Dir=!Dir, $
Subdirectory=’coyote’,’galaxy.dat’)
IDL>OpenR, lun, filename, /Get_Lun
此幅图像的结构为一个256*256的数组。如果事先不知道这些,为正确地读取这个数据,将会花费很大的精力。因为在数据文件里没有信息提示这些字节是如何被组织起来的。
在研究非格式化数据文件之前,如果不知道它有多大,那么只有一件事情要做。这也就是它包含多少字节。例如,FStat(文件的状态)命令能告知文件的字节数。
IDL>fileInfo=Fstat(lun)
IDL>Print,fileInfo.size
65536
在这个文件里有65536个字节,但这没有告知这个数据的结构是怎样组织的。例如,这些字节能被组成128*512的二维数组,也可以被组成 64*64*16的三维数组。没有办法知道这些。绝望的程序员有时尝试用各种数组组合来显示数据。如果这个看起来不对,就试着用另外一个,等等。
在此例子中,这些数据被组成256*256字节型数组。因此图像变量能够这样设定:
IDL>image=BytArr(256,256)
现在,从文件里读取数据,然后关掉文件,如下:
IDL>ReadU, lun, image
IDL>Free_Lun, lun
显示数据,键入:
IDL>Window,Xsize=256,Ysize=256
IDL>Tvscl, image
写非格式化图像数据文件
假设在图像上进行了一些处理,想将结果保存在另一个数据文件里。例如,在图像上进行了一个Sobel边界增强操作,如下:
IDL>edge=Sobel(image)
Sobel操作不仅可以帮助图像增强它的边缘,而且被返回的图像不再是字节类型。事实上,它是整数数据,键入:
IDL>Help, image, edge
在IDL中整数占两个字节,因此如果这个数据被写入一个数据文件,这个文件的大小将会是原来的字节型数据文件的两倍。这些对于那些试图读取处理后的图像数据文件的人来说可能有些混淆。所以可以考虑在文件里给出一些信息,告诉用户关于这种数据的相关类型以及怎样组织的。这个文件信息可以按如下定义:
IDL>fileInfo =’Sobel Edge Enhanced, 256 by 256 INTEGERS’
这些字符串可以在文件里写在图像数据的前面。
但是,字符串fileInfo也是一串字节。在此例中,它是一个有31个字节的字符串。如果不知道关于此非格式化文件的这一点,以后从文件里读取数据将会非常困难。例如,设或许认为信息字符串是30个字节长。这样,在读完文件信息字符串之后,继续读出的每一个整数(两个字节)将会完全是错误的。
为避免这么多的限制,大多数非格式化数据文件的文件头有着固定的大小(通常是256的倍数)。例如,假设决定所有的图像文件都有一个512个字节的头文件。可用IDL 里的Replicate和String命令创造一个有512个字节长的空格字符串。
IDL>header = String (Replicate (32B, 512))
字节值32是一个空格字符的ASCII值。把文件信息字符串插入这个长一点的文件头字符串中,从而建立具有一个正确尺寸的头文件。可以用IDL里的StrPut (把一个字符串放进另外一个字符串)命令。如下:
IDL>strPut, header, fileInfo, 0
最后,将文件头和数据写入一个新的非格式化数据文件中。如下:
IDL>OpenW, lun, ‘process.dat’
IDL>WriteU, lun, header, edge
IDL>Free_Lun, lun
当字符串被写进非格式化的文件之时,也就相当于,字符串含多少个字符,就有多少个字节被写进文件。
读取带有文件头的非格式化数据文件
假设想读取上面刚建立的文件,里面有512个字节的头文件信息,紧接着是256*256*2个字节的图像数据。可以将头信息看成是一个字符串,因为它含有关于文件里保存的数据类型的文本信息。
对于格式化的数据,文件头的变量可以被创建成空一个字符串或一个空字符串数组,以便数据文件的全部行能一次读入到文件头变量。对于非格式化的文件这是不可能的。事实上,非格式化字符串数据的规则是当从文件里读取字符串时,仅仅只是读取确定个数的字节去填满该字符串目前的长度。这样,一个文件头被定义为一个空字符串,将不会从非格式化的文件里读出什么。
这意味着在从文件中读取字符串之前,必须知道正在读的字符串长度。在刚创建的process.dat文件里,文件头有512个字节长。可以用String 和Replicate命令建立一个合适长度的空白字符串去读入,象前面那样。但是更容易的方法是把文件头读进一个字节型数组变量,然后把它转变成一个字符串。键入:
IDL>OpenR, lun, ‘process.dat’, /Get_Lun
IDL>header =BytArr(512)
IDL>ReadU, lun, header
IDL>Print, String(header)
Sobel Edge Enhanced,256 by 256 INTEGERS
从这条信息里面,就能够建立正确的数据数组,并从文件里读出图像数据并显示它们。如下:
IDL>edgeImage =IntArr(256,256)
IDL>ReadU, lun, edgeImage
IDL>Free_Lun, lun
IDL>Window, XSize=256,YSize=256
IDL> TV, edgeImage

非格式化数据文件的一些问题
不幸的是,虽然处理非格式化的数据较方便,但同时在使用这种数据是也有一些相关的问题。首先,非格式化的数据与机器类型有很大关系。在SUN计算机上写的数据,如果不做任何处理的话,在SGI或者HP计算机上经常读不出来,而在PC或者Macintosh计算机上是肯定读不出来的。(ByteOrder命令能够用来解决许多这类问题。IDL5.1版将新的Swap_If_Big_Endian和Swap_If_Little_Endian关键字引入到 Open命令中,可用于在各种各样的机器结构上编写代码来读取二进制数据。)
为了能在不同的机器结构上传递非格式化数据,IDL支持XDR(eXternal Data Representation,外部数据表示)文件格式。XDR格式是Sun Microsystems创建的公用的数据格式。在几乎所有的现代化计算机上都可用。它在二进制文件里存储了少量的元数据(数据本身的一些附加信息)。但是XDR文件仍然很简洁。
如果文件是用XDR非格式化的形式写的,数据文件在计算机之间很容易传递。换句话说,XDR非格式化文件成为跨机器结构的文件格式。
要读写XDR格式的文件,必须用XDR关键字打开。例如:把上面的process.dat文件写成XDR文件,可以键入:
IDL>OpenW, lun, ‘process.dat’, /Get_Lun, /XDR
常规的WriteU命令用来把数据写进文件:
IDL>WriteU, lun, header, edge
IDL> Free_Lun, lun
在XDR文件里字符串的长度被存储起来,并随着字符串本身一起被恢复。这意味着不必要象一般的非格式化文件那样,每次都初始化一个正确长度的字符串变量。例如,打开读取XDR文件里的信息,可以键入:
IDL>OpenR, lun, ‘process.dat’,/XDR
IDL> thisHeader = ‘’
IDL> thisData =IntArr(256,256)
IDL> ReadU, lun, thisHeader, thisData
IDL> Free_Lun, lun
用关联变量存取非格式化数据文件
大型的非格式化数据文件通常都有一系列的重复单元组成。例如,一个卫星每隔半小时就拍摄一幅512*600像素的浮点图像,并将这些图像一个接一个地存储在一个数据文件里,这个文件每隔一定的时间被下载一次。在数据文件里包含50-100M的数据是很寻常的。一个IDL关联变量通常是处理这种数据形式的最好方式(有时候是唯一的方式)。
IDL关联变量是把一个IDL数组或结构变量的组织结构映射到数据文件的内容上。文件被看作是这些重复单元的一个数组。第一个单元的索引号是0,第二个单元的索引号1等等。关联变量不象常规变量那样将整个数据组都存储在内存里。而是当一关联变量被引用时,IDL仅对需要的部分数据执行相关的输入或输出请求,这部分数据就是要读入内存的。
关联变量的一些优点
关联变量有以下几个优点:
1. 当该变量被用于表达式时,才产生文件的输入和输出动作。不需要单独的读或写命令。
2.数据集的大小不受内存容量的限制,因为有时它可处理大型的数据集。对于物理存储器来说是太大的数据,通过把此数据分成块就能很容易地处理。
3. 不必提前声明用于映射该数据的数组或结构的数量。
4. 关联变量是效率最高的I/O形式。
定义关联变量
定义和使用关联变量,可按通常的方式打开数据文件,然后用Assoc命令创建关联变量。例如,打开位于IDL主目录下的coyote 子目录中的abnorm.dat文件。
IDL> filename = Filepath(Subdir=’coyote’, ‘abnorm.dat’)
IDL> OpenR, lun, filename, /Get_Lun
这个文件里含有16幅图像或16帧画面,每幅都是64*64个字节型数组。为这些数组创建关联变量:
IDL> image = Assoc(lun, BytArr(64,64))
Assoc命令的第一个参数是与image变量相关联的文件的逻辑设备号。第二个参数是文件中被重复的单元的描述。
这些文件在重复文件单元前通常有文件头信息,尽管此文件没有。如果这样的话,Assoc命令的第三个定位参数是给定文件头的大小或文件头在文件中的偏移量。例如,假设abnorm.dat文件的前4096个字节是文件头信息,并且希望跳过文件头,那么Assoc命令可以被写成这样:
IDL> image2 = Assoc(lun, BytArr(64,64), 4096)
注意,现在有两个变量,image和image2,与同一个数据文件关联。这在IDL中完全合法,事实上,这对于存取重复单元不一致的非格式化数据文件是一种好办法。通过改变在文件中的偏移量,可以用关联变量来实现随机读写数据。
显示上面image变量里的第五幅图像或画面,键入:
IDL> TvScl, image (4)
从数据文件里把数据读进一个临时变量,在其被显示后,被IDL删除。没有显式的ReadU命令,也不需要常规情况下IDL处理这幅图像所需要的永久内存。如果想从关联变量中创建一个变量,可按通常的方式建立这个变量。例如,可以键入:
IDL> image5 = image (4)
IDL> TV, Rebin (image5, 256,256)
数据文件里重复单元的形式没有必要只是一个简单的二维图像数组。它可以是一个复杂的结构。例如,每一个重复单元可以包含128个字节的文件头,两个100个元素的浮点型矢量和一个100*100的整型数组。如果是这样,可以建立一个文件关联变量。键入:
OpenR, 10, ‘example.dat’
Info = BytArr (128)
xvector = FltArr (100)
yvector = FlatArr(100)
data = IntArr (100,100)
struct = {header: info, x: xvector, y: yvector, image: data}
repeatingUnit = Assoc (10, struct)
因为映射到此数据文件相的变量是一个结构变量,因此在它的引用被删除之前,必须对此结构变量进行临时拷贝。例如,显示文件第三个重复单元的图像部分,可以键入:
TempVariable = repearingUnit (2)
TvScl, temPvariable.image
按照通常的方式,可以用Free_Lun或Close命令将关联变量和文件之间的联系关闭。如下:
Free_Lun, lun
Close, 10

文件格式 读此类文件的IDL程序 写此类文件的IDL程序
BMP Read_BMP Write_BMP
CDF 参考CDF库 参考CDF库
DICOM IDLffDICOM对象 IDLffDICOM对象
DXF IDLffDXF对象 IDLffDXF对象
GIF Read_GIF Write_GIF
HDF 参考HDF库 参考HDF库
HDF-EOS 参考HDF库 参考HDF库
Interfile Read_Interfile 无
JPEG Read_JPEG Write_JPEG
netCDF 参考netCDF库 参考netCDF库
PICT Read_PICT Write_PICT
PBM/PPM Read_PPM Write_PPM
PNG Read_PNG Write_PNG
PostScript 无 PS或打印设备
Sun Rasterfiles Read_SRF Write_SRF
SYLK Read_SYLK Write_SYLK
TIFF/GeoTIFF Read_TIFF Write_TIFF
WAVE Read_WAVE Write_WAVE
X11-bitmap Read_X11_Bitmap 无
XWD Read_XWD 无
表9:IDL能够读写许多常用的数据文件格式。一般情况下通过用IDL语言写的库程序或动态连接模块(DLM)来完成的,DLM在运行时可以添加到IDL中。CDF、netCDF和HDF文件格式是著名的科学数据格式,有它们自己的IDL接口和库程序。
读写常用文件格式的文件

到目前为止,本章已经介绍了IDL读写数据文件的一般方法。这种底层的能力已经可以用IDL读写许多数据文件了。但是可能还想知道许多其它文件格式如何读写。这些文件格式中就有GIF和JPEG文件格式,它们常常在不同办公室之间或全球范围内被用来共享数据。当想在硬拷贝中出版图形输出时,可能想知道也必须知道如何建立PostScript文件。
IDL可以读写许多常用文件格式,这些文件格式已在表9中列出。
创建彩色GIF文件
GIF文件常被用来在万维网上发布图形信息。如果想和同事共享图形结果,迟早要读写GIF文件。
要看其是如何完成的,可装入一些数据,然后在图形窗口中显示这些数据。键入:
IDL> Window, XSize=300, YSize=300
IDL> data= LoadData (1)
IDL> TVLCT, [100,255,0], [100,255,255], [100,0,0], 0
IDL> Plot, data, /NoData, Color=2, Background=0
IDL> OPlot, data, Color=1
写GIF文件
下面的命令生成一个大小为300*300像素的GIF文件。首先,将图形窗口的内容复制到一个2D字节型图像变量中。如果在使用8位显示器,TVRD命令可用来实现这个目的。
IDL> image = TVRD ()
如果正在16位或24位彩色显示器运行IDL,那么需要用TVRD命令获取一幅24位的图像,然后用Color_Quan命令将它压缩成一幅带有正确色彩表矢量的2D图像,命令如下。只有是在16位或24位显示器上运行IDL,才用下面的命令代替上面的命令:
IDL> image = TVRD (True=1)
IDL> image = Color_Quan (image24, l, r, g, b)
假如已经有一个2D字节型数组,就没有必要再拷贝图形窗口。
GIF文件格式要求将色彩表随图像数据一起存储到GIF文件内。如果使用8位显示器,那么用TVCL命令和Get关键字就可以得到由红、绿、蓝三种颜色矢量组成的当前色彩表:
IDL> TVCL, r, g, b, /Get
假如正在16位或24位的显示器上运行作,没有必要键入上面的命令。可以在上面Color_Quan命令里得到相关图像的色彩表矢量。
色彩矢量必须是256个元素。如果在8位显示器上运行IDL,这些矢量可能就没有这么长,但是不必担心。如果在写GIF文件时这些色彩矢量不够长的话, IDL将会加长色彩矢量。如果想充分利用256种颜色,可考虑在Z图形缓冲区装载色彩表,然后得到颜色矢量,缺省情况下可获得256种颜色。参考125页的“Z图形缓冲区中的图形显示技巧”。
最后,用Write_GIF命令将图像和颜色矢量写进名为test.gif 的GIF文件:
IDL> Write_GIF, ‘test.gif’, image, r, g, b
以上就是所有要做的。没有必要获取逻辑设备号或其它东西。所有这些细节都是IDL库程序Write_GIF命令来处理的。如果对具体如何实现感到好奇的话,可以检查源代码。
如果读者有某个应用程序能打开和读取GIF文件,试着读一下刚建立的文件。许多万维网的浏览器都支持读取GIF文件。看一下,如果浏览器有一个Open File按钮,用它试试看能否读取这个GIF文件。
读GIF文件
要读刚建立的GIF文件,可按下面简单地用Read_GIF命令读GIF文件里的图像和颜色矢量。
IDL> Read_GIF, ‘test.gif’,thisImage, rr, gg, bb
清除图形窗口,装载一个灰色级调色板,可以看到将发生什么。键入:
IDL> Erase
IDL> LoadCT, 0
现在,显示刚从文件里读取的图像,如下:
IDL> TV,thisImage
有时候在图形窗口里什么都看不到,这是因为GIF图像使用的颜色还没有装入。必须装载和GIF图像相关的色彩表,以便这个图像能够正确地显示。键入:
IDL> TVCT, rr, gg, bb
将在显示窗口里看到原始图像。
假设在16位或24位上的显示器上,必须关掉颜色分解。为了看到正确的颜色,必须在装入颜色表矢量之后重新显示这个图像。
IDL> Device, Decomposed=0
IDL> TV, thisImage
创建彩色JPEG文件
另外一个常被用在万维网上共享图形结果的文件格式是JPEG格式。这个JPEG格式被称为有损压缩格式。也就是说,当图像数据被压缩报存到文件时,数据的一些信息内容会被丢失,并且不能被恢复。压缩比例,丢失的信息量以及输出图像的质量通常可用质量索引值来设定,质量索引值的范围从0(丢失许多信息内容的,质量差)到100(很少或根本就没有信息丢失,质量好)。
通常,质量索引值被设置为75,即保证一个适当的压缩比,没有丢失很多信息且图像质量损失不大。
一幅彩色JPEG图像一般是24位的图像。也就是说,这个图像是3D字节型数组。在这个数组中,维数之一将为3。这个维数的位置将决定图像是隔像素扫描(3, m, n),隔行扫描(m, 3, n),还是隔波段扫描(m, n, 3)。在很多情况下,所拥有的图像是8位的图像,而不是24位的图像,希望将其转变成一幅JPEG文件。例如,图像可能是图形窗口的屏幕转储,象上面的 GIF例子一样。下面是如何从8位图像创建隔像素扫描的24位图像的实例。
首先,打开一个图形窗口,在色棒旁显示一幅图像。代码中的TVImage和Colorbar命令在已下载的本书配套程序中。键入:
IDL> Window, Size=400,Ysize=300
IDL> LoadCT, 3
IDL> image = LoadData(7)
IDL> TVImage, image, Position=[0.1, 0.1, 0.75, 0.9]
IDL> Colorbar, Position=[0.8, 0.1, 0.86, 0.9], /Right, $
/Vertical, Division=5, Format=’(F5.1)’
写JPEG文件
接下来,对图形窗口进行拍照,获得图形输出的一幅二维图像,并用这幅图像创建隔像素扫描的24位图像。24位图像是通过用颜色表矢量建立的,实质上是将显示图像的颜色进行分离。IDL代码如下:
IDL> image = TVRD()
IDL> image = BytArr(3, 400, 300)
IDL> TVLCT, r, g, b, /Get
IDL> image3D[0, *,*]=r[image]
IDL> image3D1, *,*] =g [image]
IDL> image3D[2, *, *] = b[image]
注意,如果是在16位或24位显示器上,用一个简单的命令就可以得到24位的图像。不需要键入上述的命令,只需键入:
IDL> image3D=TVRD(true=1)
最后,用Write_JPEG命令,将此幅24位的图像用较好的图像质量和适当的压缩比输出到JPEG文件。键入:
IDL> Write_JPEG, ‘test.jpg’, image3D, true=1, quality=75
如果读者有某个应用程序能打开和读取JPEG文件的话,试着读一下刚建立的文件。许多万维网的浏览器都支持读取JPEG文件。看一下,如果浏览器有一个Open File按钮,用它试试看能否读取这个JPEG文件。
读取JPEG文件
用Read_JPEG命令就可以读取并显示一个JPEG文件。例如,如果打算在8位显示器上显示24位图像,可以用下面这个命令:
IDL> Read_JPEG, ‘test,jpg’, thisImage, colortable,$
Colors=!D.Table_Size, Dither=1, /Two_Pass_Quantize
关键字Colors指明24位图像应该量化到多少种颜色,它的值应该是从8到256。关键字Dither 选择Floyd-Steinbeig抖动法,它把颜色量化时的错误分散到旁边的周围的像素中去,从而获得高质量的图像。关键字 Two_Pass_Quantize将颜色量化分为两步进行处理,同样也可以获得更好的颜色量化效果和更高的图像质量。
显示数据,键入:
IDL> Erase
IDL> Tv, thisImage
所看到的可能有些奇怪。这是因为这幅图像的颜色量化方法。为了看到输出结果到底是什么,必须装载与图像相关的颜色表。这个颜色表返回在变量colortable中。装载颜色表,键入:
IDL> TVLCT,colortable
假设正在8位显示器上显示24位的图像,应该使用TV命令里的关键字True:
IDL> TV, thisimage, true=1
查询图像文件信息
常用图像文件格式的查询程序已在IDL5.2版中提供。这些程序允许在没有真正读取其数据的情况下,就可以查询图像文件。这些程序可以存取随着图像数据文件一起存储在文件里的元数据(关于数据的一些信息)。
下面是新的图像查寻程序列表:
Query_BMP
Query_DICOM
Query_GIF
Query_JPEG
Query_PICT
Query_PNG
Query_PPM
Query_SRF
Query_TIFF
所有这些查询命令都是以同样的方式工作。它们都是返回0或1的函数,通过返回值确定是否成功地(返回值为1)读取了图像文件里的元数据。如果它们成功地读取了文件,将保存文件信息的IDL结构变量作为输出命令返回给用户。用户通过存取这个结构里面的字段从而获取文件的有关信息。
例如,查询刚创建的JPEG文件,将文件的返回信息返回到变量fileinfo,可以键入:
IDL> ok=Query_JPEG(‘test, jpg’, fileinfo)
看看返回的是什么信息,键入:
IDL> Help, fileinfo, /Structure
可以看到打印输出的信息:
**Structure, 7 tags, length=36, refs=1:
CHANNELS LONG 3
DIMENSIONS LONG Array[2]
HAS_PALETTE INT 0
IMAGE_INDEX LONG 0
NUM_IMAGES LONG 1
PIXEL_TYPE INT 1
TYPE STRING ‘JPEG’
能够看到此文件(Num_Image=1)里有一幅图像,它是字节型数据(Pixel_Type=1),是一个24位的图像(Channels=3)。这个图像的大小能够通过打印维数字段可以看到,如下:
IDL> Print, fileinfo.dimensions
400 300
其它的图像查询程序在返回结构里含有类似的字段