LIMP:大型图像处理项目
库设计是一门不精确的艺术。在设计中包含任何足够大小或复杂度的库的所有可能用途是不切实际的。因此,许多库(和程序)不可避免地会走到进化的尽头,新的预期用途或算法无法再很好地融入现有架构。
最快的短期解决方案通常是加入新的接口,但随着时间的推移,大量 hack 代码的积累往往会降低代码的稳定性、可理解性和可维护性。软件行业经常遇到这个问题,但幸运的是找到了一个简单而优雅的解决方案——重新开始。通常,重写中低级接口可以消除 hack 代码的积累。但是,如果所讨论的设计标准贯穿整个代码,有时只有完全重写才能真正有所帮助。
这不仅仅是软件设计的特性。软件工程是在有限空间(您的计算机)中对数学的表达。通过这种传承,它与物理学等其他不精确的科学有着共同的特征,在物理学中,重写或修改理论并不罕见。
在过去的五年里,我编写了许多图像处理算法,从用于机器视觉的专用例程到用于商业视频和航空图像处理软件的完整库。最后一个商业库已经使用了三年,经历了许多接口更改和增强。但是,正如每个库在某些方面都比以前的尝试有所改进一样,我看到了提高性能和能力的方法。追随《六百万美元的人》的制作者的脚步——我想让它比以前更快、更智能、更好,并且成本显著降低。
我的商业库附带了大量代码,因此简单地修改现有代码是不可行的。由于巨大的设计不兼容性,我似乎永远无法将新库纳入我的商业工作中。与其让这个新库注定在我的硬盘上积满电子灰尘,我决定完全从头开始,以开源的方式进行。1998 年 11 月下旬,大型图像处理程序 (LIMP) 诞生了。
即使是开源的,这个库也可能非常不起眼,不会引起任何关注。然而,在花费了几个月的时间在业余时间开发 LIMP 之后,开源遥感 (OSRS, http://remotesensing.org/) 诞生了。我很高兴能有一个对某些人真正有用的开源库,因此 LIMP 被移至 OSRS 进行公开开发。
LIMP 的目的是允许使用最少的内存处理大型图像。许多可用的库可用于图像处理,其中任何一个都可用于给出相同的结果。这些库之间的差异通常可以通过回答几个问题来概括
数据可以按需处理,还是所有处理后的数据都必须在内存中或磁盘上?
为库编写新算法有多容易?
它的效率如何?
最简单的图像处理库首先允许将图像加载到内存中,然后提供对数据的像素级访问(读取和写入),最后允许将数据存储回磁盘。这种方案的优点包括一组简单的接口以及(在有足够的内存或足够小的图像的情况下)接近最佳的计算效率。缺点包括高内存使用率,因此随着图像大小或图像数量的增加,可扩展性差。
如果内存使用不是问题,这将是处理图像的最佳方式。不幸的是,对于许多图像处理需求来说,内存还不能被认为是无限的。例如,我的最后一个商业库的第一个测试是加载 1200 张图像,包含 300 GB 的数据,并一次性显示它们。实际处理是以图像块的形式完成的,以避免一次失败就抹去几周的处理时间。为了避免重蹈历史覆辙,我永远不会说没有人会拥有数百 GB 的内存。我设想当那个时候到来时,人们将处理更大的数据集。
在处理大型图像时,许多专有库通过牺牲易用性和效率来减少内存使用。可以采取很多措施来减少损失,但所有此类库在这些方面都略有劣势,LIMP 也不例外。
通过从过去的经验中学习,LIMP 中的许多接口都旨在促进提高速度的优化,并将复杂的代码分组到少数内部位置,以便更易于维护。由于复杂的代码被分组到可重用的模板中,因此可以编写许多类型的函数和转换,而无需处理通常在大型图像处理中遇到的任何复杂性。
LIMP 几乎是纯 C++ 编写的,就像我过去几年编写的大部分代码一样。我的感觉是编译器应该尽可能多地完成工作,让程序员专注于更高层次的概念。C++ 当然不是编程的圣杯,但我觉得使用面向对象的语言比在非面向对象的语言中手动近似面向对象的接口要简单得多。毕竟,每个人都知道所有计算机语言都可以简化为汇编语言;这只是您必须做多少工作与编译器为您做多少工作的问题。
Qt 库 (www.troll.no) 被用作窗口小部件集和模板库。由于所有核心库都独立于显示子系统,因此可以使用 STL 等库,但 Qt 的文档记录更好,并且平台之间没有不一致之处。我还打算使用核心库编写图形界面,因此 Qt 是自然的选择。
插件类型用于许多接口。这使得在不更改现有代码的情况下轻松添加新实现。图像加载、保存和序列化是使用插件接口完成的。简单的插值滤波器也以这种方式实现。插件管理器非常通用,可以处理任何类型的插件。这也使得可以添加外部插件的运行时加载,尽管此功能尚未实现。
LIMP 中的图像只不过是一个用于缓存和移动数据的类。图像本身不生成或以任何方式修改数据。所有生产和处理步骤都在“层”中执行。一个图像可以包含任意数量的层,但有用工作的最小值是一个——源层。此层通常对应于某种类型的文件加载器(例如,tiff),但也可以是更简单的类型,例如内存缓冲区、常量值图像或任何可以从头开始生成图像的类型。为了有效地处理大型图像,所有数据都应按需生成或加载。
其他层可以执行诸如数据格式转换、辐射或几何变换以及通过组合多个图像进行镶嵌等功能。预定义了许多数据类型转换(例如,从 RGB 到 YCbCr 或 RGB 到灰度),并且可以轻松定义许多其他转换。当处理层添加到图像时,图像的所有方面都会受到影响。2D 属性(宽度/高度)可以更改,或者深度(每像素样本数、像素数据类型等)可能会被修改。例如,一个修改 2D 大小的类是缩放层,它生成一个新的虚拟图像,该图像是现有图像的放大倍数。这不仅可以用于放大视觉图像,还可以用于对几乎任何受支持的类型进行上采样。
通过设计,尽可能多的处理被转移到流水线方法中。加载和处理数据的基本单元是瓦片。对数据的所有请求都发送到 Image 类,在该类中,它被分解为瓦片进行处理。通过预先确定需要哪些瓦片,可以执行额外的优化(例如,重新排序瓦片请求以优化数据缓存命中率)。每个瓦片都会被处理,并且不在缓存中的瓦片会被创建。将各种类型的层链接在一起的所有复杂性都由 Image 类处理,这简化了层构建。当一个层处理给定瓦片的时候,它会获得已经填充的输入数据空间和已经分配的输出数据空间;因此,它只需要处理像素。
Linux 是主要的开发平台;但是,正在努力使该库可移植到其他平台。GNU 自动配置工具用于测试所需的系统特性。据了解,LIMP 可以在 Red Hat/ix86 5.2、Irix/Mips 6.3 和 Red Hat/Alpha 5.2 上构建——每个都使用 egcs 1.1.2。
支持多种格式的 tiff 图像;支持扫描线 tiff 以及平铺 tiff。已知颜色和灰度功能可以工作,但 tiff 层还支持许多其他格式,包括短整型、浮点型和双精度型,以及多通道类型。其他人最近的工作,特别是 Frank Warmerdam 的工作,通过桥接到他的开源 GDAL 库,实现了对其他图像格式的支持。
为 LIMP 设计的许多优化尚未完全实现。这并不是说 LIMP 在当前状态下很慢,但在设计规范范围内仍有改进空间。已经添加了一些优化,这些优化可能难以在其他架构中添加。例如,LIMP 实现了数据请求优化,可以重新排序请求以充分利用图像缓存。大多数优化都在内部完成,因此调用对象可以从优化中受益,而无需增加任何额外的复杂性。
今天,LIMP 主要面向开发人员。唯一可供非程序员使用的东西是它的图像查看程序 (imgview)。 imgview 是一个简单的显示工具,能够快速查看非常大的图像,而无需太多内存。它从其他图像处理工具中获取用户界面理念,以允许诸如在后台更新显示的同时拖动画布之类的选项。它还支持许多缩放过滤器,用于平滑上采样(即放大)的图像数据。
LIMP 现在所做的只是冰山一角。基本基础已经奠定,但许多算法仍有待编写。随着工作的进展,LIMP 的每个领域都可能会得到扩展。当然,为 LIMP 和 OSRS 做出贡献的人们将推动开发。我将概述一些计划中的工作,以提供一些我认为有用的想法。这不是一个包罗万象的列表,因为可能还有许多有趣的功能尚未出现在我的脑海中。
需要在一定程度上添加对地理信息的支持,以使应用程序能够了解图像与真实世界以及彼此之间的关系。这是高级 GIS 程序的基本要求。可能没有必要将此信息直接放入 LIMP 中,但图像中的元数据信息提供了一种潜在的便捷方式来存储和检索此类信息。
LIMP 图像查看器中的类将得到扩展,以处理多个重叠图像以及矢量数据。这也与 LIMP 的核心有些脱节,因为它将创建一个新的显示管道。图像显示类已经包含一个复杂的绘图类,该类能够对显示瓦片进行排序和计算,而对 GUI 的影响最小。一旦 Qt 支持线程事件处理,这种影响就可以减少到几乎为零。
各种各样的辐射图像调整将非常有用。这些将从用于查看的简单直方图拉伸开始,并发展到更复杂的颜色和强度修改。这种类型的修改应该为 LIMP 增加非常少的额外开销,因为它专门设计用于最大限度地减少此类对象的过程和计算开销。
有关 LIMP 预期修改的更完整列表,请参阅 LIMP 发行版中的 TODO 文件。同样,如果您对进度感兴趣,请参阅 NEWS 和 ChangeLog 文件。
与大多数迎合极端处理的库一样,LIMP 注定不会面向大众市场受众。已经存在用于处理类似图像处理需求(例如编辑)的良好解决方案。通用编辑程序的设计者面临的问题是,每个像素都可能被交互式更改,但这并不是我们选择的驱动因素。相反,LIMP 旨在很好地处理科学图像处理需求。在这个类别中,我最熟悉航空和卫星图像处理,但我认为其他领域也有类似的需求。
GIS 市场的图像通常覆盖较大的区域,图像比例相对较低。另一个领域中一个明显的潜在并行是图像比例较高的小区域图像——例如在显微镜工作中所发现的。正如每个玩过分形生成器的人都知道的那样,如果您将对象放大到足够远的程度,则在该比例下看到的原始对象将覆盖极其广阔的区域。
航空和卫星图像只是在最近的历史中才开始在计算机上进行处理和存储。随着计算机变得更加强大并能够访问更大量的数据,无疑将进行新的尝试,以处理和理解指数级更大的数据集,并具有更精细的分辨率。LIMP 并不期望成为这些问题的最终答案,而只是一次在处理这些问题的同时保持性能、易用性和程序员理智的实验。
