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

Существует множество алгоритмов для изменения размера изображения - lancorz, bicubic, bilinear, например. Но большинство из них довольно сложны и поэтому потребляют слишком много 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);
    }
}

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

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

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

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

Ответы [ 3 ]

3 голосов
/ 01 ноября 2011

Почему вы делаете это на процессоре?Использование 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
2 голосов
/ 08 ноября 2011

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

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

1 голос
/ 01 ноября 2011

Опять же, зачем это делать на процессоре?Почему бы не использовать 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 - хорошее начало

...