用于调整 DIB 图像大小并保持良好图像质量的快速代码

发布于 2024-12-13 06:16:18 字数 1693 浏览 9 评论 0原文

有很多算法可以调整图像大小 - lancorz、双三次、双线性等,但大多数算法都非常复杂,因此消耗太多的 CPU

我需要的是快速相对简单的C++代码来以可接受的质量调整图像大小。

这是我当前正在做的一个示例:

for (int y = 0; y < height; y ++)
{
    int         srcY1Coord = int((double)(y * srcHeight) / height);
    int         srcY2Coord = min(srcHeight - 1, max(srcY1Coord, int((double)((y + 1) * srcHeight) / height) - 1));

    for (int x = 0; x < width; x ++)
    {
        int     srcX1Coord = int((double)(x * srcWidth) / width);
        int     srcX2Coord = min(srcWidth - 1, max(srcX1Coord, int((double)((x + 1) * srcWidth) / width) - 1));
        int     srcPixelsCount = (srcX2Coord - srcX1Coord + 1) * (srcY2Coord - srcY1Coord + 1);
        RGB32       color32;
        UINT32      r(0), g(0), b(0), a(0);

        for (int xSrc = srcX1Coord; xSrc <= srcX2Coord; xSrc ++)
            for (int ySrc = srcY1Coord; ySrc <= srcY2Coord; ySrc ++)
            {
                RGB32   curSrcColor32 = pSrcDIB->GetDIBPixel(xSrc, ySrc);
                r += curSrcColor32.r; g += curSrcColor32.g; b += curSrcColor32.b; a += curSrcColor32.alpha;
            }

            color32.r = BYTE(r / srcPixelsCount); color32.g = BYTE(g / srcPixelsCount); color32.b = BYTE(b / srcPixelsCount); color32.alpha = BYTE(a / srcPixelsCount);

            SetDIBPixel(x, y, color32);
    }
}

上面的代码足够快,但是在放大图片时质量不佳。

因此,可能有人已经拥有用于扩展 DIB 的快速且良好的 C++ 代码示例

注意:我之前使用过 StretchDIBits - 当需要将 10000x10000 图片缩小到 100x100 大小时,速度非常慢,我的代码要快得多,我只是想要更高的

质量我正在使用自己的 SetPixel/GetPixel 函数,直接快速地处理数据数组,这不是设备上下文!

There is many algorithms to do image resizing - lancorz, bicubic, bilinear, e.g. But most of them are pretty complex and therefore consume too much CPU.

What I need is fast relatively simple C++ code to resize images with acceptable quality.

Here is an example of what I'm currently doing:

for (int y = 0; y < height; y ++)
{
    int         srcY1Coord = int((double)(y * srcHeight) / height);
    int         srcY2Coord = min(srcHeight - 1, max(srcY1Coord, int((double)((y + 1) * srcHeight) / height) - 1));

    for (int x = 0; x < width; x ++)
    {
        int     srcX1Coord = int((double)(x * srcWidth) / width);
        int     srcX2Coord = min(srcWidth - 1, max(srcX1Coord, int((double)((x + 1) * srcWidth) / width) - 1));
        int     srcPixelsCount = (srcX2Coord - srcX1Coord + 1) * (srcY2Coord - srcY1Coord + 1);
        RGB32       color32;
        UINT32      r(0), g(0), b(0), a(0);

        for (int xSrc = srcX1Coord; xSrc <= srcX2Coord; xSrc ++)
            for (int ySrc = srcY1Coord; ySrc <= srcY2Coord; ySrc ++)
            {
                RGB32   curSrcColor32 = pSrcDIB->GetDIBPixel(xSrc, ySrc);
                r += curSrcColor32.r; g += curSrcColor32.g; b += curSrcColor32.b; a += curSrcColor32.alpha;
            }

            color32.r = BYTE(r / srcPixelsCount); color32.g = BYTE(g / srcPixelsCount); color32.b = BYTE(b / srcPixelsCount); color32.alpha = BYTE(a / srcPixelsCount);

            SetDIBPixel(x, y, color32);
    }
}

The code above is fast enough, but the quality is not ok on scaling pictures up.

Therefore, possibly someone already has fast and good C++ code sample for scaling DIBs?

Note: I was using StretchDIBits before - it was super-slow when was needed to downsize 10000x10000 picture down to 100x100 size, my code is much, much faster, I just want to have a bit higher quality

P.S. I'm using my own SetPixel/GetPixel functions, to work directly with data array and fast, that's not device context!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

无戏配角 2024-12-20 06:16:18

为什么要在CPU上做呢?使用 GDI,很有可能实现一些硬件加速。使用 StretchBltSetStretchBltMode

在伪代码中:

 create source dc and destination dc using CreateCompatibleDC
 create source and destination bitmaps
 SelectObject source bitmap into source DC and dest bitmap into dest DC
 SetStretchBltMode
 StretchBlt
 release DCs

Why are you doing it on the CPU? Using GDI, there's a good chance of some hardware acceleration. Use StretchBlt and SetStretchBltMode.

In pseudocode:

 create source dc and destination dc using CreateCompatibleDC
 create source and destination bitmaps
 SelectObject source bitmap into source DC and dest bitmap into dest DC
 SetStretchBltMode
 StretchBlt
 release DCs
晚风撩人 2024-12-20 06:16:18

好吧,这就是答案,必须自己做...它对于放大图片非常有效(对于缩小我的初始代码也非常有效)。希望有人能找到它的好用处,它足够快并且产生非常良好的图像质量

for (int y = 0; y < height; y ++)
{
    double      srcY1Coord = (y * srcHeight) / (double)height;
    int         srcY1CoordInt = (int)(srcY1Coord);
    double      srcY2Coord = ((y + 1) * srcHeight) / (double)height - 0.00000000001;
    int         srcY2CoordInt = min(maxSrcYcoord, (int)(srcY2Coord));
    double      yMultiplierForFirstCoord = (0.5 * (1 - (srcY1Coord - srcY1CoordInt)));
    double      yMultiplierForLastCoord = (0.5 * (srcY2Coord - srcY2CoordInt));

    for (int x = 0; x < width; x ++)
    {
        double  srcX1Coord = (x * srcWidth) / (double)width;
        int     srcX1CoordInt = (int)(srcX1Coord);
        double  srcX2Coord = ((x + 1) * srcWidth) / (double)width - 0.00000000001;
        int     srcX2CoordInt = min(maxSrcXcoord, (int)(srcX2Coord));
        RGB32   color32;

        ASSERT(srcX1Coord < srcWidth && srcY1Coord < srcHeight);
        double  r(0), g(0), b(0), a(0), multiplier(0);

        for (int xSrc = srcX1CoordInt; xSrc <= srcX2CoordInt; xSrc ++)
            for (int ySrc = srcY1CoordInt; ySrc <= srcY2CoordInt; ySrc ++)
            {
                RGB32   curSrcColor32 = pSrcDIB->GetDIBPixel(xSrc, ySrc);
                double  xMultiplier = xSrc < srcX1Coord ? (0.5 * (1 - (srcX1Coord - srcX1CoordInt))) : (xSrc >= srcX2Coord ? (0.5 * (srcX2Coord - srcX2CoordInt)) : 0.5);
                double  yMultiplier = ySrc < srcY1Coord ? yMultiplierForFirstCoord : (ySrc >= srcY2Coord ? yMultiplierForLastCoord : 0.5);
                double  curPixelMultiplier = xMultiplier + yMultiplier;

                if (curPixelMultiplier > 0)
                {
                    r += (curSrcColor32.r * curPixelMultiplier); g += (curSrcColor32.g * curPixelMultiplier); b += (curSrcColor32.b * curPixelMultiplier); a += (curSrcColor32.alpha * curPixelMultiplier);
                    multiplier += curPixelMultiplier;
                }
            }
            color32.r = BYTE(r / multiplier); color32.g = BYTE(g / multiplier); color32.b = BYTE(b / multiplier); color32.alpha = BYTE(a / multiplier);

            SetDIBPixel(x, y, color32);
    }
}

PS 请不要问我为什么不使用 StretchDIBits - 为那些了解系统 api 并不总是可用或可接受的人留下评论。

Allright, here is the answer, had to do it myself... It works perfectly well for scaling pictures up (for scaling down my initial code works perfectly well too). Hope someone will find a good use for it, it's fast enough and produced very good picture quality.

for (int y = 0; y < height; y ++)
{
    double      srcY1Coord = (y * srcHeight) / (double)height;
    int         srcY1CoordInt = (int)(srcY1Coord);
    double      srcY2Coord = ((y + 1) * srcHeight) / (double)height - 0.00000000001;
    int         srcY2CoordInt = min(maxSrcYcoord, (int)(srcY2Coord));
    double      yMultiplierForFirstCoord = (0.5 * (1 - (srcY1Coord - srcY1CoordInt)));
    double      yMultiplierForLastCoord = (0.5 * (srcY2Coord - srcY2CoordInt));

    for (int x = 0; x < width; x ++)
    {
        double  srcX1Coord = (x * srcWidth) / (double)width;
        int     srcX1CoordInt = (int)(srcX1Coord);
        double  srcX2Coord = ((x + 1) * srcWidth) / (double)width - 0.00000000001;
        int     srcX2CoordInt = min(maxSrcXcoord, (int)(srcX2Coord));
        RGB32   color32;

        ASSERT(srcX1Coord < srcWidth && srcY1Coord < srcHeight);
        double  r(0), g(0), b(0), a(0), multiplier(0);

        for (int xSrc = srcX1CoordInt; xSrc <= srcX2CoordInt; xSrc ++)
            for (int ySrc = srcY1CoordInt; ySrc <= srcY2CoordInt; ySrc ++)
            {
                RGB32   curSrcColor32 = pSrcDIB->GetDIBPixel(xSrc, ySrc);
                double  xMultiplier = xSrc < srcX1Coord ? (0.5 * (1 - (srcX1Coord - srcX1CoordInt))) : (xSrc >= srcX2Coord ? (0.5 * (srcX2Coord - srcX2CoordInt)) : 0.5);
                double  yMultiplier = ySrc < srcY1Coord ? yMultiplierForFirstCoord : (ySrc >= srcY2Coord ? yMultiplierForLastCoord : 0.5);
                double  curPixelMultiplier = xMultiplier + yMultiplier;

                if (curPixelMultiplier > 0)
                {
                    r += (curSrcColor32.r * curPixelMultiplier); g += (curSrcColor32.g * curPixelMultiplier); b += (curSrcColor32.b * curPixelMultiplier); a += (curSrcColor32.alpha * curPixelMultiplier);
                    multiplier += curPixelMultiplier;
                }
            }
            color32.r = BYTE(r / multiplier); color32.g = BYTE(g / multiplier); color32.b = BYTE(b / multiplier); color32.alpha = BYTE(a / multiplier);

            SetDIBPixel(x, y, color32);
    }
}

P.S. Please don't ask why I’m not using StretchDIBits - leave comments for these who understand that not always system api is available or acceptable.

何以心动 2024-12-20 06:16:18

再说一遍,为什么要在CPU上进行呢?为什么不使用 OpenGL / DirectX 和片段着色器?在伪代码中:

upload source texture (cache it if it's to be reused)
create destination texture
use shader program
render quad
download output texture

其中着色器程序是您正在使用的过滤方法。 GPU 在处理像素方面比 CPU/GetPixel/SetPixel 好得多。

您可能可以在网络上找到许多不同过滤方法的片段着色器 - GPU Gems 是一个很好的起点。

Again, why do it on the CPU? Why not use OpenGL / DirectX and fragment shaders? In pseudocode:

upload source texture (cache it if it's to be reused)
create destination texture
use shader program
render quad
download output texture

where shader program is the filtering method you're using. The GPU is much better at processing pixels than CPU/GetPixel/SetPixel.

You could probably find fragment shaders for lots of different filtering methods on the web - GPU Gems is a good place to start.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文