18

我正在编写一些代码来在 C/C++ 中缩放 32 位 RGBA 图像。我写了一些尝试,有些成功,但速度很慢,最重要的是,大小图像的质量是不可接受的。

我比较了由 OpenGL(即我的视频卡)和我的例程缩放的相同图像,它的质量相差甚远。我已经用 Google 代码搜索过所有我认为会有所启发的东西(SDL、Allegro、wxWidgets、CxImage、GD、ImageMagick 等)的源代码树,但通常它们的代码要么错综复杂,要么分散在各处,要么充满汇编程序和很少或没有评论。我还阅读了维基百科和其他地方的多篇文章,但我只是没有找到关于我需要什么的明确解释。我了解插值和采样的基本概念,但我正在努力使算法正确。我不想在一个例程中依赖外部库,并且必须转换为它们的图像格式并返回。此外,无论如何我想知道如何自己做。:)

我以前看过一个关于堆栈溢出的类似问题,但并没有真正以这种方式回答,但我希望有人可以帮助我朝着正确的方向前进。也许指向我一些文章或伪代码......任何可以帮助我学习和做的事情。

这就是我要找的东西:

  1. 没有汇编程序(我正在为多种处理器类型编写非常便携的代码)。
  2. 不依赖外部库。
  3. 我主要关心的是缩小,但以后还需要编写一个放大例程。
  4. 结果的质量和算法的清晰度是最重要的(我可以稍后对其进行优化)。

我的例程基本上采用以下形式:

DrawScaled(uint32 *src, uint32 *dst, 
      src_x, src_y, src_w, src_h, 
      dst_x, dst_y, dst_w, dst_h );

谢谢!

更新:为了澄清,我需要比框重采样更先进的东西来缩小图像,这会使图像过于模糊。我怀疑我想要的是某种双三次(或其他)滤波器,它有点与双三次放大算法相反(即每个目标像素都是从所有贡献的源像素中计算出来的,并结合一个保持清晰的加权算法。

例子

这是我从 wxWidgets BoxResample 算法得到的结果与我想要的 256x256 位图缩放到 55x55 的示例。

  • www.free_image_hosting.net/uploads/1a25434e0b.png

最后:

  • www.free_image_hosting.net/uploads/eec3065e2f.png

原始 256x256 图像

4

10 回答 10

2

我发现 wxWidgets 实现相当简单,可以根据需要进行修改。都是 C++,所以那里的可移植性没有问题。唯一的区别是它们的实现适用于 unsigned char 数组(无论如何我发现这是处理图像的最简单方法),RGB 的字节顺序和单独的数组中的 alpha 分量。

如果您参考 wxWidgets 源代码树中的“src/common/image.cpp”文件,则有一个使用框采样方法“wxImage::ResampleBox”的下采样器函数和一个称为“wxImage::重新采样双三次”。

于 2008-12-09T15:18:05.497 回答
2

对图像进行重新采样的一种相当简单且体面的算法是双三次插值,仅维基百科就拥有实现此功能所需的所有信息。

于 2008-12-09T15:21:09.453 回答
2

OpenGL是否有可能在矢量域中进行缩放?如果是这样,任何基于像素的缩放都无法在质量上接近它。这是基于矢量的图像的一大优势。

双三次算法可以针对清晰度与伪影进行调整 - 我正在尝试找到一个链接,我会在我这样做时对其进行编辑。

编辑:这是我正在考虑的 Mitchell-Netravali 作品,在此链接的底部引用:

http://www.cg.tuwien.ac.at/~theussl/DA/node11.html

您也可以考虑将Lanczos 重采样作为双三次的替代方法。

于 2008-12-09T18:00:02.300 回答
2

现在我看到了您的原始图像,我认为 OpenGL 正在使用最近邻算法。它不仅是调整大小的最简单方法,而且也是最快的。唯一的缺点是,如果您的原始图像中有任何细节,它看起来会非常粗糙。

这个想法是从原始图像中获取均匀间隔的样本;在您的情况下,256 个中有 55 个,或每 4.6545 个中有 1 个。只需将数字四舍五入即可选择像素。

于 2008-12-10T03:20:53.200 回答
1

如果您想要一些准备好的东西而不仅仅是算法,请尝试使用Adob​​e 通用图像库( http://opensource.adobe.com/wiki/display/gil/Downloads )。


摘自: http: //www.catenary.com/howto/enlarge.html#c

放大或缩小 - C 源代码需要用于 32 位 Windows v 5.3 或更高版本的 Victor 图像处理库。


int enlarge_or_reduce(imgdes *image1)
{
   imgdes timage;
   int dx, dy, rcode, pct = 83; // 83% percent of original size

   // Allocate space for the new image
   dx = (int)(((long)(image1->endx - image1->stx + 1)) * pct / 100);
   dy = (int)(((long)(image1->endy - image1->sty + 1)) * pct / 100);
   if((rcode = allocimage(&timage, dx, dy,
      image1->bmh->biBitCount)) == NO_ERROR) {
      // Resize Image into timage
      if((rcode = resizeex(image1, &timage, 1)) == NO_ERROR) {
         // Success, free source image
         freeimage(image1);
         // Assign timage to image1
         copyimgdes(&timage, image1);
         }
      else // Error in resizing image, release timage memory
         freeimage(&timage);
      }
   return(rcode);
}

此示例调整图像区域的大小并用新图像替换原始图像。

于 2008-12-09T15:44:42.497 回答
1

我们心爱的主持人的一篇通用文章:更好的图像调整,讨论各种算法的相对质量(并链接到另一篇 CodeProject 文章)。

于 2008-12-09T16:12:16.183 回答
1

英特尔拥有 IPP 库,可提供针对英特尔系列处理器优化的高速插值算法。它非常好,但它不是免费的。看看下面的链接:

英特尔 IPP

于 2008-12-09T16:33:58.357 回答
1

听起来您真正难以理解的是正确重新采样图像所涉及的离散->连续->离散流。Alvy Ray Smith 的A Pixel Is Not A Little Square是一份很好的技术报告,可以帮助您深入了解您需要的内容。

于 2008-12-09T19:41:03.033 回答
0

看看ImageMagick,它执行各种重新缩放过滤器。

于 2008-12-09T17:57:35.957 回答
0

作为后续,Jeremy Rudd 在上面发布了这篇文章。它实现了过滤后的两遍调整大小。源代码是 C#,但它看起来很清楚,我可以移植它来试一试。昨天我发现非常相似的 C 代码更难理解(非常糟糕的变量名)。我把它搞定了,但它很慢,没有产生好的结果,这让我相信我的适应有错误。以此作为参考,我可能会更幸运地从头开始编写它,我会尝试的。

但是考虑到两遍算法的工作原理,我想知道是否没有更快的方法来做到这一点,甚至可能一次通过?

于 2008-12-09T20:07:28.817 回答