【问题标题】:Inserting lines of pixels into an image quickly快速将像素线插入图像
【发布时间】:2009-12-18 16:07:18
【问题描述】:

我目前的困境如下:

我有一个 2550x3300 的 tiff。在我的 tiff 中的某些(可变)点,我需要从 tiff 的其他地方插入一行像素。例如我需要在第 500 行和第 501 行之间的第 100 行的一行中插入 12 个副本。

我这几天一直在寻找不同的图像处理技术,但找不到其他人在做这种事情,这让我相信我可能走错了路。

或者,如果我正在做的事情非常缓慢并且没有更好的方法来做到这一点,那么最快的方法是什么?使用 GDI+ 大约需要 12 秒来添加 1330 行,如果我使用“不安全”(我现在正在 C# 中执行所有这些操作)则需要 7.7 秒,如果我使用 FreeImage dll,我可以将其缩短到大约 2.5 秒。

提前致谢。

【问题讨论】:

  • 在不进行任何图像修改的情况下,仅读取图像并将其写回需要多长时间?这可能是瓶颈。
  • 我很确定这不是瓶颈,但在我写完这篇文章后,我会仔细检查。我在 GDI+ 上将插入过程计时为 10-11 秒。我还没有考虑对 FreeImage 中的各个部分进行计时。我现在就试试。
  • 是否需要将每次插入都写入磁盘,或者可以延迟磁盘写入以便在写入之前进行多次插入?如果是这样,可能不会为数据保留连续内存,但某种链表方法可能会起作用。写入磁盘时(可能)会降低速度,但如果您不必每次执行插入时都敲击磁盘,这可能会使其整体速度更快。
  • 这可能是压缩问题吗? Tiff 是压缩的,所以你可以在每次插入一行后解压缩然后重新压缩。也许尝试转换为原始字节,然后插入新行并转换回 tiff。另请记住,您正在进行大约 13 MB 的复制,每行 2500 像素 * 每像素 4 字节 * 1100 行。
  • 我写入磁盘的唯一时间是所有处理完成后,所以我相当肯定这不是最大的问题。另外,我不认为压缩是一个问题,尽管我承认我没有仔细检查过。在我对它做任何事情之前,我正在将文件从内存流转换为 Bitmap 对象,所以我想它不会像这样在内存中保持压缩。不过我会调查一下。

标签: .net performance data-structures image-processing


【解决方案1】:

也许你最好看看数据结构。如果您只处理一个维度,那么 B-tree 将是一种非常有效的方法,可以按照您喜欢的次数插入对数成本。如果您需要插入列和行,您将需要更复杂的东西,但如果您插入和删除行,您的生活会相对简单。

如果您可以将图像描述为具有所有您想要执行的操作的抽象数据类型以及您想要的成本的一些想法,这将有所帮助。

如果您想要一个现成的解决方案,您可以尝试将图像的行表示为数据库表中的条目。或者另一种选择是将您的像素行视为字符串,然后您可以尝试article by Boehm, Atkinson, and Plass 中描述的“C 线”库。它描述了一种用于高效处理超大字符串的数据结构。

【讨论】:

  • 我已经把整个过程缩短到一秒钟多一点(处理在哪里剪切图像、读取数据等),所以我不确定还有多少 B-tree将加快添加行的过程。此外,不幸的是,我没有时间实现 B-tree 文件类,因为我的大学不知何故设法跳过了大多数树类型(我们得到了 Binary,仅此而已)。不过,我会四处寻找它们的一些 3rd 方实现。谢谢。
  • @AgroMan:对于大多数应用来说,一秒钟听起来就足够了。我编辑了我的答案,包括对 Boehm、Atkinson 和 Plass 的一些作品的引用。我不知道代码是否仍然可用,但您可能会发现它很有用。
【解决方案2】:

除非这是使用的性能问题,否则我会坚持使用 GDI+,因为它已融入框架并降低了依赖性。如果您确实需要性能,请使用 FreeImage 库。

【讨论】:

  • 很遗憾这是一个性能问题。那么你知道没有更好的方法来做到这一点?
【解决方案3】:

在我看来,您可能已经尽可能地利用了 FreeImage。

FreeImage 源 (http://freeimage.sourceforge.net/download.html, BitmapAccess.cpp::FreeImage_AllocateT) 似乎将图像存储分配为一维数组:

unsigned dib_size = FreeImage_GetImageSize(width, height, bpp); 

bitmap->data = (BYTE *)FreeImage_Aligned_Malloc(dib_size * sizeof(BYTE), FIBITMAP_ALIGNMENT);

这里,dib_size 是“设备无关位图大小”,bpp 是“每像素位数”。

我假设您使用 PixelAccess.cpp::FreeImage_GetScanLine() 来获取要复制的行:

BYTE * DLL_CALLCONV
FreeImage_GetScanLine(FIBITMAP *dib, int scanline) {
 return (dib) ? CalculateScanLine(FreeImage_GetBits(dib), FreeImage_GetPitch(dib), scanline) : NULL;
}

调用

inline unsigned char *
CalculateScanLine(unsigned char *bits, unsigned pitch, int scanline) {
 return (bits + (pitch * scanline));
}

对于基于数组的查找来说,这似乎是 O(1)。

由于 FreeImage 内部使用静态一维数组存储,因此在增长图像时(例如,在插入行的副本时)如何获得比 O(n) 更好的性能似乎并不明显。在我看来,FreeImage 能做的最好的事情是在内部 malloc 新存储足以增长图像,然后将图像数据从源复制到新图像。这似乎是 O(n)。

使用新的数据结构(例如 B-tree)会花费一些精力,但会为您提供更好的插入时间特性 (O(log(n))。但是,加速的代价是增加存储空间 - - 你会将每个像素存储在更大的空间中。

由于 GDI+ 似乎是封闭源代码,我不确定它是如何实现位图的,但考虑到性能特征,它似乎比 FreeImage 差。

【讨论】:

    猜你喜欢
    • 2017-08-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-18
    • 2011-04-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多