Масштабирование и вращение изображений на C / C++
каков наилучший способ масштабирования массива 2D-изображений? Например, предположим, что у меня есть изображение 1024 x 2048 байт, причем каждый байт является пикселем. Каждый пиксель имеет градации серого от 0 до 255. Я хотел бы иметь возможность масштабировать это изображение произвольным фактором и получить новое изображение. Итак, если я масштабирую изображение в 0,68 раза, я должен получить новое изображение размером 0,68*1024 x 0,68*2048. некоторые пиксели будут свернуты друг на друга. И, если я масштаб с коэффициентом 3.15 сказать, я бы вам больше изображение с дублируемыми пикселями. Итак, каков наилучший способ добиться этого?
далее, я хотел бы иметь возможность поворачивать изображение на произвольный угол в диапазоне от 0 до 360 градусов (0 - 2Pi). Обрезка изображения после поворота не является проблемой. Как лучше всего это сделать?
10 ответов
там нет "простой" способ acommplish, что. Как масштабирование, так и вращение не "тривиальной" procesis.
Google для библиотеки 2d-изображений. магия++ может быть идея как divideandconquer.se указывает, но есть и другие.
существует множество способов масштабирования и поворота изображений. Самый простой способ масштабирования:
dest[dx,dy] = src[dx*src_width/dest_width,dy*src_height/dest_height]
но это производит блочные влияния увеличивая размер и потерю детали уменьшая размер. Существуют способы получения более привлекательных результатов, например,билинейная фильтрация.
для вращения местоположение пикселя src можно рассчитать с помощью матрица поворота:
sx,sy = M(dx,dy)
где M-матрица, отображающая назначение пиксели к исходному изображению. Опять же, вам нужно будет сделать интерполяцию, чтобы получить неблочные результаты.
но есть много библиотек, доступных, если вы не хотите попасть в математике обработки изображений.
то, что вы делаете, это сопоставление набора входных точек с набором выходных точек. Первая часть задачи-определить отображение для изменения размера или вращения; вторая часть-обрабатывать точки, которые не лежат точно на границе пикселя.
отображение для изменения размера очень просто:
x' = x * (width' / width)
y' = y * (height' / height)
отображение для вращения только немного сложнее.
x' = x * cos(a) + y * sin(a)
y' = y * cos(a) - x * sin(a)
метод для определения значения пикселей, которые лежат вне сетки называется интерполяция. Существует множество таких алгоритмов, широко варьирующихся по скорости и качеству конечного изображения. Некоторые из них в порядке возрастания качества/времени ближайшего соседа, билинейный, бикубический, и sinc фильтр.
вы хотите сделать грязную работу самостоятельно или можете ImageMagick сделать это для вас?
дублирование или удаление пикселей-не лучший метод или изменение размера изображения, так как результаты покажут пикселизацию и неровность. Для достижения наилучших результатов, вы должны resample изображение, которое придаст полученному изображению более плавный вид. Есть много методов интерполяции, как билинейная, бикубический, Ланцоша и т. д.
посмотри BicubicResample функция от wxWidgets. Он работает будет все виды изображений, не только оттенки серого, но и вы должны быть в состоянии адаптировать его к вашим потребностям. То есть пересчет кода от VirtualDub. Google Codesearch может выявить более связанный код.
EDIT: ссылки выглядят нормально в предварительном просмотре, но нарушаются при публикации. Это странно. Перейдите в Google codesearch и запросите "wxWidgets resamplebicubic" и "virtualdub resample" соответственно, чтобы получить те же результаты.
CxImage - это бесплатная библиотека для обработки изображений, который может делать то, что вы хотите. Я лично не использовал его, за исключением тривиальных вещей, но я видел, что его рекомендовали неоднократно.
это еще не упоминалось, поэтому я укажу, что 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, используя интерполяции lanczos.
Я не так хорошо знаком с ротациями, но вот часть примера из одного из учебников на веб-сайте 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());
методы изменения размера CxImage дают странный результат. Я использовал функции Resample и Resample2 со всеми доступными вариантами методов интерполяции с тем же результатом. Например, попробуйте изменить размер изображения 1024 x 768, заполненного белым цветом, до размера 802 x 582. Вы обнаружите, что на изображении есть пиксели, которые имеют цвет разные в Белом! Вы можете проверить это: откройте измененное изображение в Windows Paint и попробуйте заполнить его черным цветом. Результат наверняка вас позабавит.
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;
}
Проверьте Примитивы Производительности Intel. Я использовал его раньше, и он производит почти оптимальную производительность на x86. Также есть тестовая программа, которая позволяет играть с различными алгоритмами.