介绍
本文提供了一种简单、快速、实用的 2:1 图像缩小算法的实现。
在非常特殊的情况下使用 2:1 图像缩小,但效果很好。
背景
图像处理始终是一项昂贵的工作。当需要显示缩放的图片时,最好使用 GPU 进行渲染(OpenGL、DirectX)。但使用 GPU 并不总是可行的,最好有替代方案。
高质量的图像大小调整(放大/缩小)对 CPU 的要求极高,尤其是在大图像上。此外,有时需要进行多次处理。为了缓解这种情况,可以应用不同的技巧。如果必须缩小图片(按比例缩小),可以应用类似于 mip-maps 的技巧。这种情况是如果我们想要将图片缩小两倍以上(目标大小小于原始大小的一半),我们可以通过在实际缩放之前应用半缩小来加快整个过程。由于使用较少计算的最简单算法,2:1 比例缩小非常快。
例如,如果我们有图像 1920x1024 并且需要将其缩小到 500x281,我们必须执行如下代码:
<span style="color:#000000"><span style="background-color:#fbedbb">shrink( imageDst, imageSrc )</span></span>
但是我们可以通过像这样预先收缩 2:1 来优化它:
<span style="color:#000000"><span style="background-color:#fbedbb">shrinkHalf( imageHalf, imageSrc );
shrink( imaheDst, imageHalf );</span></span>
在我的一个项目中,当我必须缩放 3 个监视器的图像以生成预览时,结果是:
<span style="color:#000000"><span style="background-color:#fbedbb">Scale: 46 ms
ShrinkHalf + Scale: 25 ms
ShrinkHalf + ShrinkHalf + Scale: 16 ms</span></span>
这意味着,应用一次的技巧会导致 %36 加速。
并且该技巧应用了两次,导致 %66 加速。
演示应用程序有两个按钮:
- 缩小 2:1 - 执行整个源图像的图像缩小
- Shrink 2:1 rnd - 从源图像执行随机矩形的图像收缩
左边是原图。右边是灰度的原始图像和彩色的缩小图像(或缩小的部分)。
当原件被更新并且也需要更新缩小的副本时,只缩小图像的一部分是合适的。这可以节省大量 CPU 和处理时间。
全图2:1
2:1 随机矩形
使用代码
项目中有一对文件:ShrinkHalf.h和ShrinkHalf.cpp
要使用代码,您只需将这些文件放入您的项目中并包含ShrinkHalf.h文件。
这些文件包含所有算法的实现。
所有函数都作为参数接收:
BYTE* pixels
- 24 位 BGR 格式的像素数组(b,g,r, b,g,r, ..., b,g,r)int width
- 图像的像素宽度int height
- 图像的像素高度
因此,为了使用这些功能,您应该执行以下操作:
请注意,目的地不提供尺寸,因为它们是根据源尺寸计算的。
以下是可用的功能:
第一个函数执行整个图像的 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;
}
*/
}
因此,如果有人想要一个干净简单的函数,他们可以删除调用的第一行shrinkHalfPart
并取消注释代码。