Как сделать качественное масштабирование изображения? - PullRequest
18 голосов
/ 09 декабря 2008

Я пишу некоторый код для масштабирования 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 / добавление / 1a25434e0b.png

И наконец:

  • www.free_image_hosting.net / добавление / eec3065e2f.png

оригинальное изображение 256x256

Ответы [ 11 ]

2 голосов
/ 09 декабря 2008

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

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

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

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

Вы также можете посмотреть Lanczos reampling как альтернативу бикубической.

2 голосов
/ 10 декабря 2008

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

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

2 голосов
/ 09 декабря 2008

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

2 голосов
/ 09 декабря 2008

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

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

1 голос
/ 09 декабря 2008

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

от Элви Рэй Смит
1 голос
/ 09 декабря 2008

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

Intel IPP

1 голос
/ 09 декабря 2008

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

1 голос
/ 09 декабря 2008

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


Выписка из: http://www.catenary.com/howto/enlarge.html#c

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

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

0 голосов
/ 09 декабря 2008

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

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

...