跳转至

在 IDL 8.3 中使用剪贴板、静态方法和 ZLIB 压缩

原文链接: https://www.nv5geospatialsoftware.com/Learn/Blogs/Blog-Details/using-the-clipboard-static-methods-and-zlib-compression-in-idl-83

4838 Rate this article:

No rating

在 IDL 8.3 中使用剪贴板、静态方法和 ZLIB 压缩

Jim Pendleton 2014年4月3日,星期四

当 Exelis 的 专业服务组 受委托为客户编写"大型"应用程序时,我们通常有幸构建完整的安装程序,用于将各种资源文件和目录与应用程序或工具集的主要功能一起放置到磁盘上。

在此环境中,ROUTINE_FILEPATH 函数对于引导应用程序以使其能够相对于其安装目录找到其资源文件方面提供了巨大帮助。

然而,对于较小的项目,将所有资源(例如位图按钮图像)捆绑到单个文件中,特别是已编译的 IDL SAVE 文件中,通常更为方便。这降低了安装和测试的复杂性。

存在各种将位图图像等转换为 IDL 源代码的例程。我将在下面展示一个版本,它使用了新的 IDL 8.3 函数对图像数据进行无损的内存内 ZLIB 压缩。它还使用了新的 Clipboard 静态类,因此您可以通过一次单击将生成的代码粘贴到文本编辑器中。

最后,我们将研究一个用于自动生成整个静态方法类的实用程序,这些方法存储一个图像目录,极大地简化了它们的访问。

下面列出的例程接收一个 BYTARR 格式的图像以及一个将用作新 IDL 函数名称的字符串作为输入。它生成 PRO 代码并将其作为 ASCII 文本放入系统的剪贴板中。然后,您可以将生成的代码直接粘贴到编辑器窗口中,进行编译和执行。

PRO ImageToCode, image, functionName
d = SIZE(image, /DIMENSIONS)
z = ZLIB_COMPRESS(image)
b64 = IDL_BASE64(z)
l = LIST()
l.Add, 'FUNCTION ' + functionName
dims = STRJOIN(STRTRIM(D, 2), ',')
l.Add, 'lines = LIST()'
FOR i = 0L, STRLEN(b64) - 1, 66 DO $
l.Add, "lines.ADD, '" + STRMID(b64, I, 66) + "'"
l.Add, 's = IDL_BASE64(STRJOIN(lines.ToArray(/NO_COPY), ""))'
l.Add, 'd = ZLIB_UNCOMPRESS(s)'
l.Add, 'RETURN, REFORM(d, ' + dims + ')'
l.Add, 'END'
Clipboard.Set, l.ToArray(/NO_COPY)
END

为了生成图像数据的输出代码,我们首先调用 ZLIB_COMPRESS 函数,使用无损压缩创建图像位图的内存内版本。为了简化代码,我们首先假设输入图像是 BYTARR 中的 24 位(RGB)图像。

因为我们想从此生成 IDL 源代码,所以我们使用 IDL_BASE64 函数将 ZLIB_COMPRESS 产生的二进制数据转换为 ASCII 格式。这是一个有用的技巧,用于编码需要以 ASCII 格式交换的任何类型的二进制数据,例如通过标准 JSON 对象在 ENVI Services Engine 实例之间传递。

为了使输出的 PRO 代码更具可读性,IDL_BASE64 的输出字符串被分成多行,每行最大长度为 80 个字符。

我们创建的新函数代码执行相反的操作,通过 IDL_BASE64 将字符串转换回二进制,然后通过 ZLIB_UNCOMPRESS 函数重新生成 BYTARR 二进制数据。注意,需要 REFORM 函数调用,因为 ZLIB 压缩不编码原始输入数据的维度。

试试这个:

IDL> f = filepath('colorbar24.png', subdir=['resource','bitmaps'])
IDL> imagetocode, read_image(f), 'cbimage'

只需打开一个编辑器窗口,并将系统剪贴板的内容粘贴到其中。imagetocode 例程已用该例程的文本填充了剪贴板。

输出应如下所示:

FUNCTION cbimage
lines = LIST()
lines.ADD, 'eJz6//8/w/9RPGLwuct3/udUTf2PLDZ32Y7/IIwsBlIDUossZuNX9H8gzQfpAamhFK'
lines.ADD, 'O7D4Yb+lf9r+lZ9b+ic9X/kraV/wtaVv7PaVz5P6Nuxf/UmhX/EytX/I8tW/4/snj5'
lines.ADD, '/9DC5f8D85b/981e9t8zc9l/17Rl/x2Tl/63TVz63zJu6aj5o+aPmj9qPknmj2L6Yw'
lines.ADD, 'AAAAD//wMA32owCw=='
s = IDL_BASE64(STRJOIN(lines.ToArray(/NO_COPY), ""))
d = ZLIB_UNCOMPRESS(s)
RETURN, REFORM(d, 4,24,24)
END

当然,这个转换例程本可以设计为直接将其输出写入文件,但那样还有什么乐趣呢?

保存并编译新文件 cbimage.pro。

通过在 IMAGE 函数的上下文中执行该函数来显示输出。

IDL> image(cbimage())

现在来点更有趣的,可以轻松调整算法以创建整个 静态方法 类。

例如,我可能希望扩展此功能,以创建一个包含 IDL 分发的 resource\bitmaps 目录中 所有 位图的单一静态类。使用此机制,我将不再需要在我的应用程序分发中包含该目录中的文件。我将简单地包含我的对象类,无论是源代码形式还是 SAVE 文件中的已编译代码形式。我可以简单地通过静态类方法名称引用每个位图。

PRO ImagesToStaticClass, directory, className
files = FILE_SEARCH(FILEPATH('*', ROOT = directory))
l = LIST()
FOREACH file, files DO BEGIN
CATCH, errorNumber
IF (errorNumber NE 0) THEN BEGIN
CATCH, /CANCEL
CONTINUE
ENDIF
image = READ_IMAGE(file, r, g, b)
CATCH, /CANCEL
IF (N_ELEMENTS(image) LT 2) THEN CONTINUE
s = SIZE(image, /STRUCTURE)
IF (s.N_DIMENSIONS eq 2) THEN BEGIN
newImage = BYTARR(3, s.Dimensions[0], s.Dimensions[1])
newImage[0, *, *] = r[image]
newImage[1, *, *] = g[image]
newimage[2, *, *] = b[image]
image = TEMPORARY(newimage)
ENDIF
image = TRANSPOSE(TEMPORARY(image), [1, 2, 0])
d = SIZE(image, /DIMENSIONS)
baseName = FILE_BASENAME(file)
methodName = IDL_VALIDNAME(STRMID(baseName, 0, $
STRPOS(baseName, '.', /REVERSE_SEARCH)))
IF (methodName eq '') THEN CONTINUE
z = IDL_BASE64(ZLIB_COMPRESS(image))
l.Add, 'FUNCTION ' + className + '::' + methodName
l.Add, 'COMPILE_OPT STATIC'
dims = STRJOIN(STRTRIM(D, 2), ',')
l.Add, 'lines = LIST()'
FOR i = 0L, STRLEN(z) - 1, 66 DO $
l.Add, "lines.ADD, '" + STRMID(Z, I, 66) + "'"
l.Add, 's = IDL_BASE64(STRJOIN(lines.ToArray(/NO_COPY), ""))'
l.Add, 'd = ZLIB_UNCOMPRESS(s)'
l.Add, 'RETURN, REFORM(d, ' + dims + ')'
l.Add, 'END'
ENDFOREACH
l.Add, 'PRO ' + className + '__DEFINE'
l.Add, '!null = {' + className + ', INHERITS IDL_Object}'
l.Add, 'END'
Clipboard.Set, l.ToArray(/NO_COPY)
END

复制、保存并编译上面显示的源代码。

执行该例程,为其提供包含图像文件的目录名称和要生成的对象类的名称。例如,

IDL> imagestostaticclass, FILEPATH('', SUBDIR=['resource','bitmaps']), 'IDLBitmaps'

将例程的输出粘贴到编辑器文件中,并将其另存为 idlbitmaps__define.pro,然后进行编译。

例如,要创建一个"打开文件夹"位图小部件按钮,我可以简单地指定一个调用,其方法名称对应于原始图像文件名,但要经过对 IDL_VALIDNAME 的调用进行修改。

IDL> tlb = WIDGET_BASE()
IDL> b = WIDGET_BUTTON(tlb, VALUE=IDLBitmaps.Open(), /BITMAP)
IDL> WIDGET_CONTROL, tlb, /REALIZE

IDL 资源目录中的许多位图文件是带有相关颜色查找表的调色板化单平面图像。为了简便起见,并且在以 ASCII 字符代码大小为代价的前提下保持代码简洁,该实用程序在压缩之前将它们从单平面图像转换为 RGB 格式。

请注意,该实用程序会跳过任何无法被 READ_IMAGE 函数成功读取的文件。除此之外,没有健全的错误检查,这留作读者的练习。

该例程返回维度为 [x, y, 3] 的图像,这是 WIDGET_BUTTON 所需的格式。

我们可以期待 WorldView-3 带来什么 用于图像处理的网络化 GIS