Масштабирование и поворот изображений в C / C ++ - PullRequest
13 голосов
/ 18 ноября 2008

Каков наилучший способ масштабирования массива 2D-изображений? Например, предположим, что у меня есть изображение размером 1024 x 2048 байт, причем каждый байт является пикселем. Каждый пиксель имеет уровень шкалы яркости от 0 до 255. Я хотел бы иметь возможность масштабировать это изображение по произвольному коэффициенту и получить новое изображение. Итак, если я масштабирую изображение с коэффициентом 0,68, я должен получить новое изображение размером 0,68 * 1024 x 0,68 * 2048. некоторые пиксели будут свернуты друг на друга. И если я масштабируюсь, скажем, в 3,15 раза, я получу увеличенное изображение с дублирующимися пикселями. Итак, как лучше всего это сделать?

Далее я хотел бы иметь возможность поворачивать изображение на произвольный угол в диапазоне от 0 до 360 градусов (0 - 2Pi). Обрезка изображения после поворота не является проблемой. Каков наилучший способ сделать это?

Ответы [ 10 ]

13 голосов
/ 18 ноября 2008

Существует множество способов масштабирования и поворота изображений. Самый простой способ масштабирования:

dest[dx,dy] = src[dx*src_width/dest_width,dy*src_height/dest_height]

но это приводит к блочным эффектам при увеличении размера и потере деталей при уменьшении размера. Существуют способы получения более привлекательных результатов, например, билинейная фильтрация .

Для поворота местоположение пикселя src можно рассчитать, используя матрицу вращения :

sx,sy = M(dx,dy)

где M - матрица, которая отображает пиксели назначения в исходное изображение. Опять же, вам нужно выполнить интерполяцию, чтобы получить неблокированные результаты.

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

10 голосов
/ 18 ноября 2008

То, что вы делаете, это сопоставление набора входных точек с набором выходных точек. Первая часть проблемы состоит в том, чтобы определить отображение для вашего изменения размера или поворота; вторая часть предназначена для обработки точек, которые не лежат точно на границе пикселей.

Отображение для изменения размера легко:

x' = x * (width' / width)
y' = y * (height' / height)

Картографирование вращения немного сложнее.

x' = x * cos(a) + y * sin(a)
y' = y * cos(a) - x * sin(a)

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

10 голосов
/ 18 ноября 2008

Не существует "простого" способа сделать это. И масштабирование, и вращение не являются «тривиальными» процессами.

Google это для библиотеки 2d изображений. Магия ++ может быть идеей в качестве точек для разделения и захвата, но есть и другие.

8 голосов
/ 18 ноября 2008

Хотите сделать грязную работу самостоятельно или можете ImageMagick сделать это за вас?

3 голосов
/ 18 ноября 2008

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

Посмотрите на функцию BicubicResample из wxWidgets. Он будет работать со всеми видами изображений, не только с оттенками серого, но вы сможете адаптировать его к вашим потребностям. Тогда есть также код ресемплинга из VirtualDub . Google Codesearch может раскрыть больше связанных кодов.

РЕДАКТИРОВАТЬ: ссылки выглядят хорошо в предварительном просмотре, но прерываются при публикации. Это странно. Перейдите в Google Codesearch и запросите «wxwidgets resamplebicubic» и «virtualdub resample» соответственно, чтобы получить одинаковые результаты.

2 голосов
/ 30 августа 2016

Это еще не было упомянуто, поэтому я укажу, что OpenCV имеет функции для масштабирования и вращения изображений, а также огромное количество других утилит. Он может содержать множество функций, которые не относятся к данному вопросу, но его очень легко настроить и использовать для библиотеки такого рода.

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

Используя OpenCV, масштабирование можно сделать так:

float scaleFactor = 0.68f;
cv::Mat original = cv::imread(path);
cv::Mat scaled;
cv::resize(original, scaled, cv::Size(0, 0), scaleFactor, scaleFactor, cv::INTER_LANCZOS4);
cv::imwrite("new_image.jpg", scaled);

Это уменьшает масштаб изображения в 0,68 раза, используя интерполяцию Ланцоша.

Я не так хорошо знаком с ротациями, но вот часть примера из одного из руководств на сайте OpenCV, который я отредактировал до соответствующих частей. (Оригинал имел перекос и перевод в нем также ...)

/// Compute a rotation matrix with respect to the center of the image
Point center = Point(original.size().width / 2, original.size().height / 2);
double angle = -50.0;
double scale = 0.6;

/// Get the rotation matrix with the specifications above
Mat rot_mat( 2, 3, CV_32FC1 );
rot_mat = getRotationMatrix2D(center, angle, scale);

/// Rotate the image
Mat rotated_image;
warpAffine(src, rotated_image, rot_mat, src.size());

Сайт OpenCV

У них тоже есть очень хорошая документация.

2 голосов
/ 18 ноября 2008

CxImage - бесплатная библиотека для обработки изображений, которая может делать то, что вы хотите. Я лично не использовал его, за исключением тривиальных вещей, но я видел, что это неоднократно рекомендовано.

0 голосов
/ 15 февраля 2012

Ознакомьтесь с Intel Performance Primitives . Я использовал его раньше, и он дает почти оптимальную производительность на x86. Существует также тестовая программа, которая позволяет играть с различными алгоритмами.

0 голосов
/ 19 августа 2010

Методы изменения размера CxImage дают странный результат. Я использовал функции Resample и Resample2 со всеми доступными вариациями методов интерполяции с одинаковым результатом. Например, попробуйте изменить размер изображения 1024 x 768, заполненного белым цветом, до размера 802 x 582. Вы увидите, что на изображении есть пиксели, цвет которых отличается от до белого! Вы можете проверить это: откройте измененное изображение в Windows Paint и попробуйте залить его черным цветом. Результат наверняка вас позабавит.

0 голосов
/ 04 августа 2009
point scaling(point p,float sx,float sy) {
    point s;

    int c[1][3];
    int a[1][3]={p.x,p.y,1};
    int b[3][3]={sx,0,0,0,sy,0,0,0,1};

    multmat(a,b,c);

    s.x=c[0][0];
    s.y=c[0][1];

    return s;
}
...