Python 2.7.3 + OpenCV 2.4 после поворота не подходит Изображение
Я пытаюсь повернуть изображение на несколько градусов, а затем показать его в окне.
моя идея состоит в том, чтобы повернуть и затем показать его в новом окне с новой шириной и высотой окна, рассчитанной из старой ширины и высоты:
new_width = x * cos angle + y * sin angle
new_height = y * cos angle + x * sin angle
Я ожидал, что результат будет выглядеть ниже:
![enter image description here]()
но получается, что результат выглядит так:
![enter image description here]()
и мой код здесь:
#!/usr/bin/env python -tt
#coding:utf-8
import sys
import math
import cv2
import numpy as np
def rotateImage(image, angle):#parameter angle in degrees
if len(image.shape) > 2:#check colorspace
shape = image.shape[:2]
else:
shape = image.shape
image_center = tuple(np.array(shape)/2)#rotation center
radians = math.radians(angle)
x, y = im.shape
print 'x =',x
print 'y =',y
new_x = math.ceil(math.cos(radians)*x + math.sin(radians)*y)
new_y = math.ceil(math.sin(radians)*x + math.cos(radians)*y)
new_x = int(new_x)
new_y = int(new_y)
rot_mat = cv2.getRotationMatrix2D(image_center,angle,1.0)
print 'rot_mat =', rot_mat
result = cv2.warpAffine(image, rot_mat, shape, flags=cv2.INTER_LINEAR)
return result, new_x, new_y
def show_rotate(im, width, height):
# width = width/2
# height = height/2
# win = cv2.cv.NamedWindow('ro_win',cv2.cv.CV_WINDOW_NORMAL)
# cv2.cv.ResizeWindow('ro_win', width, height)
win = cv2.namedWindow('ro_win')
cv2.imshow('ro_win', im)
if cv2.waitKey() == '\x1b':
cv2.destroyWindow('ro_win')
if __name__ == '__main__':
try:
im = cv2.imread(sys.argv[1],0)
except:
print '\n', "Can't open image, OpenCV or file missing."
sys.exit()
rot, width, height = rotateImage(im, 30.0)
print width, height
show_rotate(rot, width, height)
Должны быть некоторые глупые ошибки в моем коде, приводящие к этой проблеме, но я не могу понять это...
и я знаю, что мой код недостаточно питоничен :(.. извините за это..
Кто-нибудь может мне помочь?
Лучшее,
bearzk
Ответы
Ответ 1
Как сказал BloodyD ответ, cv2.warpAffine
не автоцентрирует преобразованное изображение. Вместо этого он просто преобразует каждый пиксель с помощью матрицы преобразования. (Это может перемещать пиксели в любом месте в декартовом пространстве, в том числе из исходной области изображения.) Затем, когда вы указываете размер изображения назначения, он захватывает область этого размера, начиная с (0,0), то есть в верхнем левом углу оригинальная рамка. Любые части вашего преобразованного изображения, которые не лежат в этом регионе, будут обрезаны.
Здесь код Python для поворота и масштабирования изображения, с результатом:
def rotateAndScale(img, scaleFactor = 0.5, degreesCCW = 30):
(oldY,oldX) = img.shape #note: numpy uses (y,x) convention but most OpenCV functions use (x,y)
M = cv2.getRotationMatrix2D(center=(oldX/2,oldY/2), angle=degreesCCW, scale=scaleFactor) #rotate about center of image.
#choose a new image size.
newX,newY = oldX*scaleFactor,oldY*scaleFactor
#include this if you want to prevent corners being cut off
r = np.deg2rad(degreesCCW)
newX,newY = (abs(np.sin(r)*newY) + abs(np.cos(r)*newX),abs(np.sin(r)*newX) + abs(np.cos(r)*newY))
#the warpAffine function call, below, basically works like this:
# 1. apply the M transformation on each pixel of the original image
# 2. save everything that falls within the upper-left "dsize" portion of the resulting image.
#So I will find the translation that moves the result to the center of that region.
(tx,ty) = ((newX-oldX)/2,(newY-oldY)/2)
M[0,2] += tx #third column of matrix holds translation, which takes effect after rotation.
M[1,2] += ty
rotatedImg = cv2.warpAffine(img, M, dsize=(int(newX),int(newY)))
return rotatedImg
![введите описание изображения здесь]()
Ответ 2
Когда вы получите матрицу вращения следующим образом:
rot_mat = cv2.getRotationMatrix2D(image_center,angel,1.0)
Ваш параметр "scale" установлен в 1.0, поэтому, если вы используете его для преобразования вашей матрицы изображений в матрицу результатов того же размера, она обязательно будет обрезана.
Вместо этого вы можете получить матрицу вращения следующим образом:
rot_mat = cv2.getRotationMatrix2D(image_center,angel,0.5)
который будет вращаться и сжиматься, оставляя место вокруг краев (вы можете масштабировать его сначала, чтобы у вас все равно получилось большое изображение).
Кроме того, похоже, что вы смешиваете соглашения numpy и OpenCV для размеров изображений. OpenCV использует (x, y) для размеров изображения и координат точек, а numpy использует (y, x). Вероятно, поэтому вы переходите от соотношения портрета к ландшафту.
Я обычно говорю об этом так:
imageHeight = image.shape[0]
imageWidth = image.shape[1]
pointcenter = (imageHeight/2, imageWidth/2)
и т.д...
В конечном счете, это отлично работает для меня:
def rotateImage(image, angel):#parameter angel in degrees
height = image.shape[0]
width = image.shape[1]
height_big = height * 2
width_big = width * 2
image_big = cv2.resize(image, (width_big, height_big))
image_center = (width_big/2, height_big/2)#rotation center
rot_mat = cv2.getRotationMatrix2D(image_center,angel, 0.5)
result = cv2.warpAffine(image_big, rot_mat, (width_big, height_big), flags=cv2.INTER_LINEAR)
return result
Update:
Вот полный script, который я выполнил. Просто cv2.imshow( "winname", изображение) и cv2.waitkey() без аргументов, чтобы держать его открытым:
import cv2
def rotateImage(image, angel):#parameter angel in degrees
height = image.shape[0]
width = image.shape[1]
height_big = height * 2
width_big = width * 2
image_big = cv2.resize(image, (width_big, height_big))
image_center = (width_big/2, height_big/2)#rotation center
rot_mat = cv2.getRotationMatrix2D(image_center,angel, 0.5)
result = cv2.warpAffine(image_big, rot_mat, (width_big, height_big), flags=cv2.INTER_LINEAR)
return result
imageOriginal = cv2.imread("/Path/To/Image.jpg")
# this was an iPhone image that I wanted to resize to something manageable to view
# so I knew beforehand that this is an appropriate size
imageOriginal = cv2.resize(imageOriginal, (600,800))
imageRotated= rotateImage(imageOriginal, 45)
cv2.imshow("Rotated", imageRotated)
cv2.waitKey()
На самом деле не так много... И вы определенно были правы в использовании if __name__ == '__main__':
, если это настоящий модуль, над которым вы работаете.
Ответ 3
Ну, этот вопрос кажется несовременным, но у меня была такая же проблема и потребовалось некоторое время, чтобы решить проблему без масштабирования исходного изображения вверх и вниз. Я просто опубликую свое решение (к сожалению, код на С++, но при необходимости он может быть легко перенесен на python):
#include <math.h>
#define PI 3.14159265
#define SIN(angle) sin(angle * PI / 180)
#define COS(angle) cos(angle * PI / 180)
void rotate(const Mat src, Mat &dest, double angle, int borderMode, const Scalar &borderValue){
int w = src.size().width, h = src.size().height;
// resize the destination image
Size2d new_size = Size2d(abs(w * COS((int)angle % 180)) + abs(h * SIN((int)angle % 180)), abs(w * SIN((int)angle % 180)) + abs(h * COS((int)angle % 180)));
dest = Mat(new_size, src.type());
// this is our rotation point
Size2d old_size = src.size();
Point2d rot_point = Point2d(old_size.width / 2.0, old_size.height / 2.0);
// and this is the rotation matrix
// same as in the opencv docs, but in 3x3 form
double a = COS(angle), b = SIN(angle);
Mat rot_mat = (Mat_<double>(3,3) << a, b, (1 - a) * rot_point.x - b * rot_point.y, -1 * b, a, b * rot_point.x + (1 - a) * rot_point.y, 0, 0, 1);
// next the translation matrix
double offsetx = (new_size.width - old_size.width) / 2,
offsety = (new_size.height - old_size.height) / 2;
Mat trans_mat = (Mat_<double>(3,3) << 1, 0, offsetx , 0, 1, offsety, 0, 0, 1);
// multiply them: we rotate first, then translate, so the order is important!
// inverse order, so that the transformations done right
Mat affine_mat = Mat(trans_mat * rot_mat).rowRange(0, 2);
// now just apply the affine transformation matrix
warpAffine(src, dest, affine_mat, new_size, INTER_LINEAR, borderMode, borderValue);
}
Общее решение - вращать и переводить повернутое изображение в нужное положение. Итак, мы создаем две матрицы преобразования (сначала для вращения, второе для перевода) и умножаем их на окончательное аффинное преобразование. Поскольку матрица, возвращаемая opencv, getRotationMatrix2D имеет только 2x3, мне пришлось создавать матрицы вручную в формате 3x3, чтобы они могли умножаться. Затем просто возьмите первые два ряда и примените аффинную трансформацию.
РЕДАКТИРОВАТЬ. Я создал Gist, потому что мне эта функция слишком часто нужна в разных проектах. Существует также версия Python: https://gist.github.com/BloodyD/97917b79beb332a65758