2:1 图像压缩算法的实用实现

图像处理通常是一项计算密集型的任务。当需要展示缩放后的图片时,使用GPU进行渲染(如OpenGL或DirectX)通常是更好的选择。然而,并非总是可以使用GPU,因此拥有一个替代方案是有益的。高质量的图像缩放(放大/缩小)对CPU要求极高,特别是在处理大图像时。此外,有时需要多次进行这种处理。为了减轻这种负担,可以应用不同的技巧。如果需要缩小(缩小尺寸)图片,可以应用类似于mip-maps的技巧。例如,如果想要缩小图片超过两倍(目标尺寸小于原始尺寸的一半),可以通过在实际缩放之前先进行一半的缩小来加速整个过程。2:1的缩小非常快,因为它使用的算法计算量更少。

例如,如果有一张1920x1024的图像,并且需要将其缩小到500x281,可以执行如下代码:

shrink(imageDst, imageSrc);

但可以通过先进行2:1的预缩小来优化这个过程:

shrinkHalf(imageHalf, imageSrc); shrink(imageDst, imageHalf);

在一个项目中,当需要将3个显示器的图像缩放到生成预览时,结果如下:

  • 直接缩放:46毫秒
  • 先缩小一半再缩放:25毫秒
  • 先缩小一半再缩小一半再缩放:16毫秒

这意味着,应用一次技巧,可以提高36%的速度。应用两次技巧,可以提高66%的速度。

演示应用程序有两个按钮:

  • 2:1缩小 - 对整个源图像进行缩小
  • 2:1随机矩形缩小 - 对源图像的随机矩形区域进行缩小

左侧是原始图像。右侧是原始图像的灰度版本和缩小后的图像(或缩小的部分)的颜色版本。

当原始图像更新时,仅缩小图像的一部分是合适的,因为需要更新缩小后的副本。这可以节省大量的CPU和处理时间。

整个图像的2:1缩小

随机矩形的2:1缩小

使用代码

项目中包含一对文件:

  • ShrinkHalf.h
  • ShrinkHalf.cpp

要使用这些代码,只需要将这些文件放入项目中,并包含ShrinkHalf.h文件。

#include "ShrinkHalf.h"

这些文件包含了所有算法的实现。shrinkHalf函数有以下参数:

  • BYTE* dstPixels - 目标像素数组,格式为24位BGR(b,g,r, b,g,r, ..., b,g,r)
  • BYTE* srcPixels - 源像素数组,格式为24位BGR(b,g,r, b,g,r, ..., b,g,r)
  • int srcWidth - 图像的宽度(以像素为单位)
  • int srcHeight - 图像的高度(以像素为单位)

为了使用这些函数,应该执行以下操作:

shrinkHalf(dstPixels, srcPixels, srcWidth, srcHeight);

注意,目标不提供尺寸,因为它们是从源尺寸计算出来的。

可用的函数有:

void shrinkHalf(BYTE* target, const BYTE* source, int srcWidth, int srcHeight); void shrinkHalfPart(BYTE* target, const BYTE* source, int srcWidth, int srcHeight, int x1, int y1, int x2, int y2);

第一个函数对整个图像进行2:1缩小。第二个函数对源图像中指定的矩形区域进行2:1缩小,计算目标图像中相应的矩形区域。如果使用这个,应该注意到可能需要对源矩形点进行+1/-1的校正,因为整数四舍五入。

这在需要应用大量更新时非常有用(例如,视频渲染)。

感兴趣的点

在shrinkHalf函数中,有一段注释掉的代码是可行的,但更倾向于保留对shrinkHalfPart的内部调用。

//static void shrinkHalf(BYTE* target, const BYTE* source, int srcWidth, int srcHeight) { shrinkHalfPart(target, source, srcWidth, srcHeight, 0, 0, srcWidth-1, srcHeight-1); //~~~~~ /* int dstWidth = srcWidth / 2; int dstHeight = srcHeight / 2; int srcLineBytes = srcWidth * 3; int dstLineBytes = dstWidth * 3; int dstRows = dstHeight; const BYTE* sl = source; BYTE* tl = target; const BYTE* te = target + srcHeight/2 * dstLineBytes; while(tl < te) { BYTE* pt = tl; const BYTE* p1 = sl; const BYTE* p2 = p1 + srcLineBytes; const BYTE* pe = sl + srcLineBytes; while(p1 < pe) { *pt++ = (p1[0] + p1[3] + p2[0] + p2[3]) >> 2; // / 4; blue; *pt++ = (p1[1] + p1[4] + p2[1] + p2[4]) >> 2; // / 4; green; *pt++ = (p1[2] + p1[5] + p2[2] + p2[5]) >> 2; // / 4; red; p1 += 6;//2*3; p2 += 6;//2*3; } sl += srcLineBytes << 1; // Shift by 1 is equal to " * 2 " tl += dstLineBytes; } */ }
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485