Как сделать высококачественное масштабирование изображения?
Я пишу код для масштабирования 32-битного изображения RGBA в C/С++. Я написал несколько попыток, которые были несколько успешными, но они медленны, а главное качество изображения не приемлемо.
Я сравнивал одно и то же изображение, масштабируемое OpenGL (то есть моя видеокарта), и моя рутина, и она была немного отличается качеством. Я искал Google Code, просматривал исходные деревья всего, что, как я думал, прольет свет (SDL, Allegro, wxWidgets, CxImage, GD, ImageMagick и т.д.), Но обычно их код либо запутан и разбросан по всему месту, либо пронизан сборщиком и небольшими комментариями или без комментариев. Я также читал несколько статей в Википедии и в других местах, и я просто не нашел ясного объяснения того, что мне нужно. Я понимаю основные понятия интерполяции и выборки, но я изо всех сил пытаюсь получить алгоритм правильно. Я НЕ хочу полагаться на внешнюю библиотеку для одной подпрограммы и должен конвертировать в свой формат изображения и обратно. Кроме того, я хотел бы знать, как это сделать в любом случае.:)
Я уже видел подобный вопрос, заданный перед переполнением стека, но на него не ответили так, но я надеюсь, что там кто-то есть, кто может помочь подтолкнуть меня в правильном направлении. Возможно, укажите мне некоторые статьи или псевдокод... что-нибудь, что поможет мне учиться и делать.
Вот что я ищу:
- Нет ассемблера (я пишу очень портативный код для нескольких типов процессоров).
- Никаких зависимостей от внешних библиотек.
- В первую очередь я занимаюсь масштабированием DOWN, но также должен будет написать процедуру масштабирования позже.
- Качество результата и ясность алгоритма наиболее важны (я могу его оптимизировать позже).
Моя процедура по существу принимает следующий вид:
DrawScaled(uint32 *src, uint32 *dst,
src_x, src_y, src_w, src_h,
dst_x, dst_y, dst_w, dst_h );
Спасибо!
ОБНОВЛЕНИЕ: Чтобы уточнить, мне нужно что-то более продвинутое, чем перемасштабирование коробки для уменьшения масштаба, что слишком сильно размывает изображение. Я подозреваю, что я хочу, это какой-то бикубический (или другой) фильтр, который в некотором роде обращен к бикубическому алгоритму масштабирования (т.е. Каждый пиксель назначения вычисляется из всех исходных пикселей источника в сочетании с алгоритмом взвешивания, который сохраняет четкость.
Пример
Вот пример того, что я получаю от алгоритма wxWidgets BoxResample по сравнению с тем, что я хочу, на растровом изображении 256x256, масштабированном до 55x55.
- www.free_image_hosting.net/uploads/1a25434e0b.png
И наконец:
- www.free_image_hosting.net/uploads/eec3065e2f.png
исходное изображение 256x256
Ответы
Ответ 1
Я нашел, что реализация wxWidgets довольно проста для изменения, если требуется. Это все С++, поэтому проблем с переносимостью там нет. Единственное отличие заключается в том, что их реализация работает с unsigned char массивами (которые, как мне кажется, проще всего обрабатывать изображения) с байтовым порядком RGB и альфа-компонентом в отдельном массиве.
Если вы ссылаетесь на файл "src/common/image.cpp" в дереве исходных данных wxWidgets, есть функция сэмплирования вниз, которая использует метод выборки ящиков "wxImage:: ResampleBox" и функцию масштабирования, называемую "wxImage:: ResampleBicubic".
Ответ 2
Достаточно простой и достойный алгоритм для повторной выборки изображений Бикубическая интерполяция, только для википедии есть вся информация, необходимая для ее реализации.
Ответ 3
Возможно ли, что OpenGL делает масштабирование в векторном домене? Если это так, нет никакого способа, чтобы какое-либо масштабирование на основе пикселей было рядом с ним по качеству. Это большое преимущество векторных изображений.
Бикубический алгоритм может быть настроен на резкость против артефактов - я пытаюсь найти ссылку, я отредактирую ее, когда буду делать.
Изменить: Это была работа Митчелла-Нетравали, о которой я думал, на которую ссылается внизу этой ссылки:
http://www.cg.tuwien.ac.at/~theussl/DA/node11.html
Вы также можете изучить повторную выборку Lanczos в качестве альтернативы бикубическому.
Ответ 4
Теперь, когда я вижу исходное изображение, я думаю, что OpenGL использует алгоритм ближайшего соседа. Это не только самый простой способ изменения размера, но и самый быстрый. Единственным недостатком является то, что он выглядит очень грубым, если в исходном изображении есть какие-либо детали.
Идея состоит в том, чтобы принимать равномерно отложенные образцы из исходного изображения; в вашем случае, 55 из 256 или один из каждых 4.6545. Просто округлите число, чтобы выбрать пиксель.
Ответ 5
Статьи CodeProject, обсуждающие и использующие исходный код для масштабирования изображений:
Ответ 6
Попробуйте использовать Adobe Generic Image Library (http://opensource.adobe.com/wiki/display/gil/Downloads), если вы хотите что-то готовое и не только алгоритм.
Выдержка из: http://www.catenary.com/howto/enlarge.html#c
Увеличить или уменьшить - исходный код C
Требуется библиотека обработки изображений Виктора для 32-разрядной версии Windows версии 5.3 или выше.
int enlarge_or_reduce(imgdes *image1)
{
imgdes timage;
int dx, dy, rcode, pct = 83; // 83% percent of original size
// Allocate space for the new image
dx = (int)(((long)(image1->endx - image1->stx + 1)) * pct / 100);
dy = (int)(((long)(image1->endy - image1->sty + 1)) * pct / 100);
if((rcode = allocimage(&timage, dx, dy,
image1->bmh->biBitCount)) == NO_ERROR) {
// Resize Image into timage
if((rcode = resizeex(image1, &timage, 1)) == NO_ERROR) {
// Success, free source image
freeimage(image1);
// Assign timage to image1
copyimgdes(&timage, image1);
}
else // Error in resizing image, release timage memory
freeimage(&timage);
}
return(rcode);
}
Этот пример изменяет размер области изображения и заменяет исходное изображение новым изображением.
Ответ 7
Общая статья нашего любимого хозяина: Улучшение изображения в обратном направлении, обсуждение относительных качеств различных алгоритмов (и ссылка на другую статью CodeProject).
Ответ 8
Intel имеет библиотеки IPP, которые обеспечивают высокоскоростные алгоритмы интерполяции, оптимизированные для процессоров семейства Intel. Это очень хорошо, но это не бесплатно. Взгляните на следующую ссылку:
Intel IPP
Ответ 9
Похоже на то, что вы действительно испытываете трудности с пониманием, - это дискретный → непрерывный → дискретный поток, задействованный в правильной передискретизации изображения. Хороший технический отчет, который может помочь вам понять, что вам нужно, это Alvy Ray Smith Пиксель - это не маленький квадрат.
Ответ 10
Посмотрите ImageMagick, который выполняет все виды фильтров масштабирования.
Ответ 11
В качестве продолжения, Джереми Радд разместил эту статью выше. Он реализует фильтрованное изменение размера двух проходов. Источниками являются С#, но это выглядит достаточно ясно, что я могу его портировать, чтобы попробовать. Вчера я нашел очень похожий код C, который было намного сложнее понять (очень плохие имена переменных). Я получил его на работу, но он был очень медленным и не давал хороших результатов, что заставило меня поверить, что в моей адаптации была ошибка. Возможно, мне удастся написать это с нуля с этой ссылкой, которую я попробую.
Но учитывая, как работает алгоритм с двумя проходами, я задаюсь вопросом, не существует ли более быстрый способ сделать это, возможно, даже за один проход?