Быстрый код для изменения размера изображения 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 - хорошее место для начала.