缩放和加载非常大的 TIFF 文件

Héc*_*orn 5 java image loading large-files rescale

我有一个非常大的高分辨率地图,我想在应用程序中使用它(图像大小约为 80 mb)。

我想知道以下内容:

  • 如何以最佳方式加载此图像?我知道加载图像需要几秒钟(没关系)但我想通知用户进度。我想使用确定的模式并以某种方式JProgressBar向用户显示。这应该反映已加载的字节数或类似的内容。是否有任何图像加载方法可以提供此功能(如ImageIO.read())?
  • 由于地图具有非常高的分辨率,我想为用户提供滚动放大和缩小功能。我怎样才能以最好的方式做到这一点?我知道一个事实,BufferedImage对于这么大的文件,重新缩放标准方式需要很长时间。有没有什么有效的方法可以做到这一点?

谢谢您的意见!

亲切的问候,
埃克托·范登布恩

ps 图像将绘制在 JPanel 的画布上。


嗨,安德鲁,非常感谢您的帮助;一切都很完美,加载速度很快。如果没有您的专业知识和解释,我会一直致力于此,因此您赢得了公平公正的赏金。

我所做的是以下内容;使用 imagemagick 我创建了多个不同分辨率的图像,并且在执行开始时我只加载最小的 res。图片。其余的加载在单独的线程中,因此执行不会停止。使用您提供给我的信息,我然后在放大或缩小时使用适当的图像。我对使用瓷砖有点怀疑,因为我需要在地图上绘制自己的图像,而我在您告诉我使用的外部罐子中找不到油漆功能,所以我最终使用了一些简单的东西;当缩放或平移时,重新缩放模式设置为快速,当您不缩放或平移时,重新缩放设置为平滑以获得像素完美的图像(就像您建议的那样),但事实证明这足够快,我不”

所以再次感谢,一切正常:)

And*_*ock 3

您应该(同时)采取两种方法:

  1. 将图像缩小为各种尺寸。您应该以一系列较低的分辨率(1/2、1/4、1/8 等)缩小图像,直到图像达到最大可能的屏幕分辨率。当用户第一次打开图像时,您会显示较低分辨率的图像。这将快速加载并允许用户平移。当用户放大时,您将使用更高分辨率的图像。您可以使用 ImageMagick 来实现此目的:http ://www.imagemagick.org/Usage/resize/
  2. 平铺较大的图像。这会将单个大图像分解为网格图案中的大量小图像。当用户放大某个区域时,您会计算用户正在查看哪些图块,然后仅渲染它们,而不渲染图像的其他区域。您可以使用ImageMagick 将图像分割成图块,例如ImageMagick。将图像切成子图块的正确方法是什么?该文档是http://www.imagemagick.org/Usage/crop/#crop_tile

(提供适当大小和平铺图像的缓存使得 GoogleEarth 和无数其他地图应用程序能够如此快速地渲染,同时以令人难以置信的高分辨率放大地图)

获得图块后,您可以使用 Java 中的几个引擎之一:

可能还有其他人。

您可以在此框架内实现任意缩放(适用于捏合缩放或类似操作)。在您允许的缩放限制内,您的算法将类似于:

  1. 对于用户选择的缩放级别,选择最接近的更高分辨率缓存。例如,如果您有 100%、50%、25% 和 12.5% 的图块,并且用户选择 33% 缩放,请选择 50% 的图块
  2. 设置图块的布局,以便图块方块具有适合所选缩放的正确大小(这可能是最低缩放级别的单个图块)。例如,在 33% 缩放下使用 50% 的图块,图块为 100 像素正方形,网格将为 67 像素正方形
  3. 单独加载和缩放图块图像以适合屏幕(这可以是多线程,在现代 CPU 架构上运行良好)

有几点需要注意:

  1. 当您达到图块的最大分辨率时,缩放算法会发生变化 。
    1. 使用双线性或双三次缩放对图像进行高达 100% 的缩放。这为照片提供了出色的外观,几乎没有锯齿
    2. 高于 100%,您可能想要显示像素,因此最近邻可能是一个不错的选择
  2. 为了获得更高的保真度,请使用更高比例的图块并缩小比例 > 50%。例如,假设您准备了 100%、50%、25% 和 12.5% 的图块。要显示 40% 缩放,请不要缩小 50% 的图块;相反,使用 100% 的图块并将其缩小到 40%。这很有用:
    1. 如果您的图像是文本或图表(即包含许多直线的光栅图像)。如果不过度采样,缩放这些类型的图像通常会产生令人讨厌的伪影
    2. 如果您需要非常高保真度的摄影风格图像
  3. 如果您需要渲染缩放预览(例如,当用户仍在捏合和缩放时),请在手势开始时抓取屏幕截图并对其进行缩放。动画的流畅度比缩放预览的像素完美度更重要。
  4. 选择合适尺寸的瓷砖很重要。非常大的图块(每个屏幕<1)渲染速度很慢。太小的图块会产生其他开销,并且经常会产生令人讨厌的渲染伪像,您会看到屏幕随机填满。性能和复杂性之间的一个很好的折衷是将图块设置为全屏尺寸的四分之一左右。

使用这些技术时,图像加载速度应该会快得多,因此进度条并不那么重要。如果是,那么您需要在ImageReader上注册一个 IIOReadProgressListener :

来自 JavaDoc:

ImageReader 实现使用的接口,用于通知调用者其图像和缩略图读取方法的进度。

该接口接收解码进度的一般指示(通过 imageProgress 和thumbnailProgress 方法),以及指示整个图像何时更新的事件(通过 imageStarted、imageComplete、thumbnailStarted 和thumbnailComplete 方法)。希望在发生像素更新时(例如,在渐进式解码期间)获知像素更新的应用程序应提供 IIOReadUpdateListener。