Быстрый код для изменения размера изображения DIB и поддержания хорошего качества изображений

Существует множество алгоритмов для изменения размера изображения - lancorz, bicubic, bilinear, например. Но большинство из них довольно сложны и поэтому потребляют слишком много ресурсов процессора.

Что мне нужно, так это быстрый относительно простой код 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);
    }
}

Приведенный выше код достаточно быстрый, но качество не подходит для масштабирования изображений.

Поэтому, возможно, у кого-то уже есть быстрый и хороший пример кода C++ для масштабирования DIB?

Примечание: я использовал StretchDIBits раньше - он был очень медленным, когда требовалось уменьшить размер изображения 10000x10000 до размера 100x100, мой код стал намного, намного быстрее, я просто хочу иметь немного более высокое качество

PS Я использую свои собственные функции SetPixel/GetPixel, чтобы работать напрямую с массивом данных и быстро, это не контекст устройства!

3 ответа

Решение

Хорошо, вот ответ, я должен был сделать это сам... Он отлично работает для масштабирования изображений (для масштабирования мой исходный код тоже работает отлично). Надеюсь, кто-то найдет для него подходящее применение, он достаточно быстрый и дает очень хорошее качество изображения.

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 доступен или приемлем.

Почему вы делаете это на процессоре? Использование GDI дает хорошие шансы на некоторое аппаратное ускорение. Используйте StretchBlt и SetStretchBltMode.

В псевдокоде:

 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

Опять же зачем это делать на процессоре? Почему бы не использовать OpenGL / DirectX и фрагментные шейдеры? В псевдокоде:

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

где shader program метод фильтрации, который вы используете Графический процессор намного лучше обрабатывает пиксели, чем CPU/GetPixel/SetPixel.

Вероятно, вы могли бы найти фрагменты шейдеров для множества различных методов фильтрации в Интернете - GPU Gems - хорошее место для начала.

Другие вопросы по тегам