Как сделать качественное масштабирование изображения?

Я пишу код для масштабирования 32-битного изображения RGBA на C / C ++. Я написал несколько попыток, которые были в некоторой степени успешными, но они медленные и, что наиболее важно, качество изображения не приемлемого размера.

Я сравнил одно и то же изображение, отмасштабированное OpenGL (то есть моей видеокартой), и мою рутину, и оно сильно различается по качеству. Я искал в Google Code, просматривал исходные деревья всего, что, как мне казалось, могло пролить свет (SDL, Allegro, wxWidgets, CxImage, GD, ImageMagick и т. Д.), Но обычно их код либо запутан и разбросан повсюду, либо пронизан ассемблер и почти нет комментариев. Я также прочитал несколько статей в Википедии и других местах, и я просто не нахожу четкого объяснения того, что мне нужно. Я понимаю основные концепции интерполяции и выборки, но изо всех сил пытаюсь понять алгоритм. Я НЕ хочу полагаться на внешнюю библиотеку для одной процедуры и должен конвертировать в их формат изображения и обратно. Кроме того, мне все равно хотелось бы знать, как это сделать самому. :)

Я видел подобный вопрос о переполнении стека и раньше, но на самом деле на него не было дано такого ответа, но я надеюсь, что есть кто-то, кто может помочь подтолкнуть меня в правильном направлении. Может быть, укажите мне какие-нибудь статьи или псевдокод ... что-нибудь, что поможет мне научиться и делать.

Вот что я ищу:

  1. Нет ассемблера (я пишу очень переносимый код для нескольких типов процессоров).
  2. Нет зависимости от внешних библиотек.
  3. Меня в первую очередь интересует масштабирование ВНИЗ, но позже мне нужно будет написать процедуру масштабирования.
  4. Главное - качество результата и ясность алгоритма (потом смогу оптимизировать).

По сути, мой распорядок выглядит следующим образом:

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


person Patrick Hogan    schedule 09.12.2008    source источник
comment
Я взял ваше исходное изображение и повторно применил уменьшение масштаба с помощью wxWidgets, чтобы получить следующее freeimagehosting .net / image.php? 128f096527.png Как видите, он не такой резкий, как OpenGL, но он правильно сглажен и значительно резче, чем ваш пример wx.   -  person Dan    schedule 10.12.2008


Ответы (10)


Я обнаружил, что реализацию wxWidgets довольно просто изменить по мере необходимости. Это все C ++, поэтому проблем с переносимостью нет. Единственное отличие состоит в том, что их реализация работает с массивами беззнаковых символов (которые я считаю самым простым способом работы с изображениями) с порядком байтов RGB и альфа-компонентом в отдельном массиве.

Если вы обратитесь к файлу «src / common / image.cpp» в дереве исходных кодов wxWidgets, то есть функция понижающей дискретизации, которая использует метод блочной выборки «wxImage :: ResampleBox» и функцию масштабирования под названием «wxImage :: ResampleBicubic ".

person Dan    schedule 09.12.2008
comment
Спасибо. Я пробовал это, но не проходит тест качества (размыто). Смотрите обновление в моем сообщении. - person Patrick Hogan; 09.12.2008

Достаточно простой и достойный алгоритм для пересчета изображений - это бикубическая интерполяция, вся информация, которую вы можете получить, есть только в Википедии. нужно, чтобы это было реализовано.

person Pieter    schedule 09.12.2008
comment
Бикубическая интерполяция делает звездную работу. - person Chris O; 25.06.2012

Возможно ли, что OpenGL выполняет масштабирование в векторной области? Если это так, то никакое пиксельное масштабирование не может быть близко к нему по качеству. Это большое преимущество векторных изображений.

Бикубический алгоритм можно настроить для резкости и артефактов - я пытаюсь найти ссылку, я отредактирую ее, когда найду.

Изменить: это была работа Митчелла-Нетравали, о которой я думал, на которую есть ссылка внизу этой ссылки:

http://www.cg.tuwien.ac.at/~theussl/DA/node11.html

Вы также можете изучить передискретизацию Ланцоша в качестве альтернативы бикубической.

person Mark Ransom    schedule 09.12.2008
comment
Нет, на самом деле это растровое изображение 256x256, которое уменьшается в масштабе. Он не прорисовывается с помощью векторных операций (хотя он необычайно четкий, даже по сравнению с бикубическим ресэмплингом в Photoshop, который сам по себе довольно хорош). - person Patrick Hogan; 09.12.2008
comment
Если вы можете предоставить исходное растровое изображение 256x256, это тоже будет полезно. - person Mark Ransom; 09.12.2008
comment
Передискретизация Ланцоша отлично справляется. - person Chris O; 25.06.2012
comment
Ланцош требует больших вычислительных ресурсов, но лучше сохраняет четкие линии. - person VoteCoffee; 09.10.2014
comment
@VoteCoffee не обязательно лучше, чем бикубический. На самом деле бикубическая версия Catmull-Rom удивительно похожа на Lanczos-2 и быстрее вычисляется. Польза от высших форм Ланцоша становится гораздо более тонкой по мере того, как вы поднимаетесь. Самая большая проблема с бикубикой заключается в том, что люди реализуют ее как интерполятор, а не как фильтр. - person Mark Ransom; 10.10.2014
comment
Что вы подразумеваете под его реализацией как интерполятор, а не как фильтр? Мой опыт работы с Лаанцосом - это Lanczos 3 для оптического распознавания символов и визуальной идентификации. Для этих целей он оказался гораздо более эффективным, чем бикубический. Я еще не играл с Catrom. Какие-нибудь предпочтительные примеры, на которые вы можете указать мне? - person VoteCoffee; 10.10.2014
comment
@VoteCoffee, интерполятор пытается оценить точку на основе ее соседей, в данном случае сетки 4x4. Фильтр принимает разное количество пикселей в зависимости от величины изменения размера. Разница заметна только при уменьшении размеров. Самые последние версии Imagemagick хорошо справляются со своей задачей независимо от того, какой алгоритм вы выберете. - person Mark Ransom; 10.10.2014

Теперь, когда я вижу ваше исходное изображение, я думаю, что OpenGL использует алгоритм ближайшего соседа. Это не только самый простой способ изменить размер, но и самый быстрый. Единственным недостатком является то, что это выглядит очень грубо, если на исходном изображении есть какие-либо детали.

Идея состоит в том, чтобы взять равномерно распределенные образцы из исходного изображения; в вашем случае 55 из 256 или один из 4,6545. Просто округлите число, чтобы выбрать пиксель.

person Mark Ransom    schedule 10.12.2008
comment
На мгновение я подумал, что вы правы, поэтому я проверил свой параметр GL_TEXTURE_MIN_FILTER, и он был установлен на GL_LINEAR. Для удовольствия я сравнил его с GL_NEAREST и взорвал в фотошопе. Есть небольшая разница в плавности. - person Patrick Hogan; 10.12.2008
comment
На самом деле у меня теперь есть рабочий распорядок, основанный на двухпроходном масштабировании, который поддерживает несколько фильтров. Я попробую их и, возможно, в какой-то момент опубликую результаты по сравнению с OpenGL и Photoshop. Я надеюсь опубликовать общий код, который могут использовать другие, поскольку этот вопрос, похоже, породил некоторых наблюдателей. - person Patrick Hogan; 10.12.2008
comment
@pbhogan: если вам требуется, чтобы OpenGL пересчитывал изображение в операции рендеринга с 256 до 55 (т.е. в 4,6 раза меньше), даже при билинейной выборке вы получите очень близкое значение к ближайшему, потому что каждый результирующий пиксель состоит только из 2x2 текселей. Таким образом, резкость возникает из-за сильной недостаточной дискретизации исходного изображения, и результат выглядит хорошо только потому, что в исходном изображении отсутствуют мелкие детали. - person Suma; 13.04.2011

Попробуйте использовать Adobe Generic Image Library (http://opensource.adobe.com/wiki/display/gil/Downloads), если вам нужно что-то готовое, а не только алгоритм.


Выдержка из: http://www.catenary.com/howto/enlarge.html#c

Увеличить или уменьшить - исходный код на C требует наличия библиотеки обработки изображений Victor для 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);
}

В этом примере изменяется размер области изображения и исходное изображение заменяется новым.

person berlindev    schedule 09.12.2008

У Intel есть библиотеки IPP, которые обеспечивают алгоритмы высокоскоростной интерполяции, оптимизированные для процессоров семейства Intel. Это очень хорошо, но не бесплатно. Взгляните на следующую ссылку:

Intel IPP

person Naveen    schedule 09.12.2008
comment
Библиотеки IPP - это необработанные операции низкого уровня. Библиотека openCV предоставляет функции обработки изображений более высокого уровня и может использовать IPP, если он доступен - opencv бесплатен, я думал, что IPP? - person Martin Beckett; 09.12.2008

Общая статья от нашего любимого хозяина: Лучшее изменение размера изображения, в котором обсуждаются относительные качества различные алгоритмы (и это ссылка на другую статью CodeProject).

person PhiLho    schedule 09.12.2008

Похоже, вам действительно трудно понять дискретный -> непрерывный -> дискретный поток, участвующий в правильной передискретизации изображения. Хороший технический отчет, который может помочь вам понять это, - это Элви Рэя Смита. Пиксель - это не маленький квадрат.

person Boojum    schedule 09.12.2008
comment
Это отличная статья, спасибо, но проблема не в этом. Я так понимаю, пиксели - это не квадраты. Проблема, с которой я столкнулся, заключается в том, чтобы найти четкую, понятную и достаточно быструю ссылку на алгоритм для правильной реализации масштабирования. - person Patrick Hogan; 09.12.2008

Взгляните на ImageMagick, который выполняет всевозможные фильтры масштабирования.

person derobert    schedule 09.12.2008

В качестве продолжения Джереми Радд разместил эту статью выше. Он реализует отфильтрованное двухпроходное изменение размера. Исходный код написан на C #, но он выглядит достаточно ясным, чтобы я мог перенести его и попробовать. Вчера я нашел очень похожий код C, который было намного сложнее понять (очень плохие имена переменных). Я заставил это работать, но это было очень медленно и не дало хороших результатов, что заставило меня поверить в ошибку в моей адаптации. Возможно, мне больше повезет, если я напишу это с нуля, взяв это в качестве ссылки, и я попробую.

Но, учитывая, как работает двухпроходный алгоритм, мне интересно, нет ли более быстрого способа сделать это, возможно, даже за один проход?

person Patrick Hogan    schedule 09.12.2008