Поворот изображения без обрезки в OpenCV в C ++
Я хотел бы повернуть изображение, но я не могу получить повернутое изображение без обрезки
Мое исходное изображение:
- Как я могу получить позицию и рисовать прямоугольник с помощью opencv?
- Как заострить изображение в OpenCV?
- OpenCV лучше обнаруживает красный цвет?
- Обнаружение полукруга в opencv
- Сериализация OpenCV Mat_
Теперь я использую этот код:
#include #include #include // Compile with g++ code.cpp -lopencv_core -lopencv_highgui -lopencv_imgproc int main() { cv::Mat src = cv::imread("im.png", CV_LOAD_IMAGE_UNCHANGED); cv::Mat dst; cv::Point2f pc(src.cols/2., src.rows/2.); cv::Mat r = cv::getRotationMatrix2D(pc, -45, 1.0); cv::warpAffine(src, dst, r, src.size()); // what size I should use? cv::imwrite("rotated_im.png", dst); return 0; }
И получите следующее изображение:
Но я хотел бы получить следующее:
Большое спасибо за Вашу помощь!
- Эквивалентен cvSetImageROI в интерфейсе OpenCV C ++
- использование статических библиотек вместо динамических библиотек в opencv
- Отслеживание OpenCV с использованием оптического streamа
- Python-OpenCV cv2 Ошибка OpenCV: утверждение не выполнено (scn == 3 || scn == 4) в неизвестной функции, файл .. \ .. \ .. \ modules \ imgproc \ src \ color.cpp
- Как обрезать CvMat в OpenCV?
- Передача изображения OpenCV на C ++ через сокет
- PyQt показывает видеоstream из opencv
- Потоковое и декодирование MJPEG
Мой ответ вдохновлен следующими сообщениями / блогами:
- Поворот cv :: Mat с использованием cv :: warpAffine смещает изображение цели
- http://john.freml.in/opencv-rotation
Основные идеи:
- Настройка матрицы вращения путем добавления перевода в новый центр изображения
- Использование
cv::RotatedRect
дляcv::RotatedRect
возможного использования существующих функций opencv
Код, проверенный с помощью opencv 3.4.1:
#include "opencv2/opencv.hpp" int main() { cv::Mat src = cv::imread("im.png", CV_LOAD_IMAGE_UNCHANGED); double angle = -45; // get rotation matrix for rotating the image around its center in pixel coordinates cv::Point2f center((src.cols-1)/2.0, (src.rows-1)/2.0); cv::Mat rot = cv::getRotationMatrix2D(center, angle, 1.0); // determine bounding rectangle, center not relevant cv::Rect2f bbox = cv::RotatedRect(cv::Point2f(), src.size(), angle).boundingRect2f(); // adjust transformation matrix rot.at(0,2) += bbox.width/2.0 - src.cols/2.0; rot.at (1,2) += bbox.height/2.0 - src.rows/2.0; cv::Mat dst; cv::warpAffine(src, dst, rot, bbox.size()); cv::imwrite("rotated_im.png", dst); return 0; }
Просто попробуйте код ниже, идея проста:
-
Вам нужно создать пустую картинку с максимальным размером, который вы ожидаете, при повороте под любым углом. Здесь вы должны использовать Pythagoras, как указано в приведенных выше комментариях.
-
Теперь скопируйте исходное изображение на вновь созданное изображение и передайте его в
warpAffine
. Здесь вы должны использовать центр вновь созданного изображения для вращения. -
После
warpAffine
если вам нужно обрезать точное изображение для этого, переведите четыре угла исходного изображения в увеличенное изображение с использованием матрицы вращения, как описано здесь -
Найдите минимальный x и минимум y для верхнего угла и максимальный x и максимум y для нижнего угла из приведенного выше результата, чтобы обрезать изображение.
Это код:
int theta = 0; Mat src,frame, frameRotated; src = imread("rotate.png",1); cout<
в(3,4) << x1, x2, x3, x4, y1, y2, y3, y4, 1, 1, 1, 1 ); Mat RotCo_Ordinate = rot_mat * co_Ordinate; for(int i=0;i<4;i++){ if(RotCo_Ordinate.at (0,i) (0,i); //access smallest if(RotCo_Ordinate.at (1,i) (1,i); //access smallest y } for(int i=0;i<4;i++){ if(RotCo_Ordinate.at (0,i)>bound_Rect.width) bound_Rect.width=(int)RotCo_Ordinate.at (0,i); //access largest x if(RotCo_Ordinate.at (1,i)>bound_Rect.height) bound_Rect.height=RotCo_Ordinate.at (1,i); //access largest y } bound_Rect.width=bound_Rect.width-bound_Rect.x; bound_Rect.height=bound_Rect.height-bound_Rect.y; Mat cropedResult; Mat ROI = frameRotated(bound_Rect); ROI.copyTo(cropedResult); imshow("Result", cropedResult); imshow("frame", frame); imshow("rotated frame", frameRotated); char k=waitKey(); if(k=='+') theta+=10; if(k=='-') theta-=10; if(k=='s') imwrite("rotated.jpg",cropedResult); if(k==27) break; } int theta = 0; Mat src,frame, frameRotated; src = imread("rotate.png",1); cout<
(3,4) << x1, x2, x3, x4, y1, y2, y3, y4, 1, 1, 1, 1 ); Mat RotCo_Ordinate = rot_mat * co_Ordinate; for(int i=0;i<4;i++){ if(RotCo_Ordinate.at (0,i) (0,i); //access smallest if(RotCo_Ordinate.at (1,i) (1,i); //access smallest y } for(int i=0;i<4;i++){ if(RotCo_Ordinate.at (0,i)>bound_Rect.width) bound_Rect.width=(int)RotCo_Ordinate.at (0,i); //access largest x if(RotCo_Ordinate.at (1,i)>bound_Rect.height) bound_Rect.height=RotCo_Ordinate.at (1,i); //access largest y } bound_Rect.width=bound_Rect.width-bound_Rect.x; bound_Rect.height=bound_Rect.height-bound_Rect.y; Mat cropedResult; Mat ROI = frameRotated(bound_Rect); ROI.copyTo(cropedResult); imshow("Result", cropedResult); imshow("frame", frame); imshow("rotated frame", frameRotated); char k=waitKey(); if(k=='+') theta+=10; if(k=='-') theta-=10; if(k=='s') imwrite("rotated.jpg",cropedResult); if(k==27) break; }
Обрезанное изображение
Спасибо, Робула! На самом деле вам не нужно дважды вычислять синус и косинус.
import cv2 def rotate_image(mat, angle): # angle in degrees height, width = mat.shape[:2] image_center = (width/2, height/2) rotation_mat = cv2.getRotationMatrix2D(image_center, angle, 1.) abs_cos = abs(rotation_mat[0,0]) abs_sin = abs(rotation_mat[0,1]) bound_w = int(height * abs_sin + width * abs_cos) bound_h = int(height * abs_cos + width * abs_sin) rotation_mat[0, 2] += bound_w/2 - image_center[0] rotation_mat[1, 2] += bound_h/2 - image_center[1] rotated_mat = cv2.warpAffine(mat, rotation_mat, (bound_w, bound_h)) return rotated_mat
Спасибо @Haris! Вот версия Python:
def rotate_image(image, angle): '''Rotate image "angle" degrees. How it works: - Creates a blank image that fits any rotation of the image. To achieve this, set the height and width to be the image's diagonal. - Copy the original image to the center of this blank image - Rotate using warpAffine, using the newly created image's center (the enlarged blank image center) - Translate the four corners of the source image in the enlarged image using homogenous multiplication of the rotation matrix. - Crop the image according to these transformed corners ''' diagonal = int(math.sqrt(pow(image.shape[0], 2) + pow(image.shape[1], 2))) offset_x = (diagonal - image.shape[0])/2 offset_y = (diagonal - image.shape[1])/2 dst_image = np.zeros((diagonal, diagonal, 3), dtype='uint8') image_center = (diagonal/2, diagonal/2) R = cv2.getRotationMatrix2D(image_center, angle, 1.0) dst_image[offset_x:(offset_x + image.shape[0]), \ offset_y:(offset_y + image.shape[1]), \ :] = image dst_image = cv2.warpAffine(dst_image, R, (diagonal, diagonal), flags=cv2.INTER_LINEAR) # Calculate the rotated bounding rect x0 = offset_x x1 = offset_x + image.shape[0] x2 = offset_x x3 = offset_x + image.shape[0] y0 = offset_y y1 = offset_y y2 = offset_y + image.shape[1] y3 = offset_y + image.shape[1] corners = np.zeros((3,4)) corners[0,0] = x0 corners[0,1] = x1 corners[0,2] = x2 corners[0,3] = x3 corners[1,0] = y0 corners[1,1] = y1 corners[1,2] = y2 corners[1,3] = y3 corners[2:] = 1 c = np.dot(R, corners) x = int(c[0,0]) y = int(c[1,0]) left = x right = x up = y down = y for i in range(4): x = int(c[0,i]) y = int(c[1,i]) if (x < left): left = x if (x > right): right = x if (y < up): up = y if (y > down): down = y h = down - up w = right - left cropped = np.zeros((w, h, 3), dtype='uint8') cropped[:, :, :] = dst_image[left:(left+w), up:(up+h), :] return cropped
После поиска для чистого и легкого для понимания решения и чтения ответов выше, пытаясь понять их, я в итоге придумал решение, использующее тригонометрию.
Надеюсь, это поможет кому-то 🙂
import cv2 import math def rotate_image(mat, angle): height, width = mat.shape[:2] image_center = (width / 2, height / 2) rotation_mat = cv2.getRotationMatrix2D(image_center, angle, 1) radians = math.radians(angle) sin = math.sin(radians) cos = math.cos(radians) bound_w = int((height * abs(sin)) + (width * abs(cos))) bound_h = int((height * abs(cos)) + (width * abs(sin))) rotation_mat[0, 2] += ((bound_w / 2) - image_center[0]) rotation_mat[1, 2] += ((bound_h / 2) - image_center[1]) rotated_mat = cv2.warpAffine(mat, rotation_mat, (bound_w, bound_h)) return rotated_mat
EDIT: см. Ответ @Remi Cuingnet ниже.
Увеличьте canvas изображения (в равной степени от центра без изменения размера изображения), чтобы он мог поместиться в изображение после вращения, а затем примените warpAffine
:
Mat img = imread ("/path/to/image", 1); double offsetX, offsetY; double angle = -45; double width = img.size().width; double height = img.size().height; Point2d center = Point2d (width / 2, height / 2); Rect bounds = RotatedRect (center, img.size(), angle).boundingRect(); Mat resized = Mat::zeros (bounds.size(), img.type()); offsetX = (bounds.width - width) / 2; offsetY = (bounds.height - height) / 2; Rect roi = Rect (offsetX, offsetY, width, height); img.copyTo (resized (roi)); center += Point2d (offsetX, offsetY); Mat M = getRotationMatrix2D (center, angle, 1.0); warpAffine (resized, resized, M, resized.size());
Спасибо всем за этот пост, это было очень полезно. Тем не менее, я нашел несколько черных линий слева и вверх (используя версию python Роуза) при вращении на 90º. Проблема заключалась в том, что некоторые () округления. В дополнение к этому, я изменил знак угла, чтобы заставить его расти по часовой стрелке.
def rotate_image(image, angle): '''Rotate image "angle" degrees. How it works: - Creates a blank image that fits any rotation of the image. To achieve this, set the height and width to be the image's diagonal. - Copy the original image to the center of this blank image - Rotate using warpAffine, using the newly created image's center (the enlarged blank image center) - Translate the four corners of the source image in the enlarged image using homogenous multiplication of the rotation matrix. - Crop the image according to these transformed corners ''' diagonal = int(math.ceil(math.sqrt(pow(image.shape[0], 2) + pow(image.shape[1], 2)))) offset_x = (diagonal - image.shape[0])/2 offset_y = (diagonal - image.shape[1])/2 dst_image = np.zeros((diagonal, diagonal, 3), dtype='uint8') image_center = (float(diagonal-1)/2, float(diagonal-1)/2) R = cv2.getRotationMatrix2D(image_center, -angle, 1.0) dst_image[offset_x:(offset_x + image.shape[0]), offset_y:(offset_y + image.shape[1]), :] = image dst_image = cv2.warpAffine(dst_image, R, (diagonal, diagonal), flags=cv2.INTER_LINEAR) # Calculate the rotated bounding rect x0 = offset_x x1 = offset_x + image.shape[0] x2 = offset_x + image.shape[0] x3 = offset_x y0 = offset_y y1 = offset_y y2 = offset_y + image.shape[1] y3 = offset_y + image.shape[1] corners = np.zeros((3,4)) corners[0,0] = x0 corners[0,1] = x1 corners[0,2] = x2 corners[0,3] = x3 corners[1,0] = y0 corners[1,1] = y1 corners[1,2] = y2 corners[1,3] = y3 corners[2:] = 1 c = np.dot(R, corners) x = int(round(c[0,0])) y = int(round(c[1,0])) left = x right = x up = y down = y for i in range(4): x = c[0,i] y = c[1,i] if (x < left): left = x if (x > right): right = x if (y < up): up = y if (y > down): down = y h = int(round(down - up)) w = int(round(right - left)) left = int(round(left)) up = int(round(up)) cropped = np.zeros((w, h, 3), dtype='uint8') cropped[:, :, :] = dst_image[left:(left+w), up:(up+h), :] return cropped
Если это просто поворот на 90 gradleусов, возможно, этот код может быть полезен.
Mat img = imread("images.jpg"); Mat rt(img.rows, img.rows, CV_8U); Point2f pc(img.cols / 2.0, img.rows / 2.0); Mat r = getRotationMatrix2D(pc, 90, 1); warpAffine(img, rt, r, rt.size()); imshow("rotated", rt);
Надеюсь, это полезно.
Кстати, только для поворотов на 90º здесь более эффективная + точная функция:
def rotate_image_90(image, angle): angle = -angle rotated_image = image if angle == 0: pass elif angle == 90: rotated_image = np.rot90(rotated_image) elif angle == 180 or angle == -180: rotated_image = np.rot90(rotated_image) rotated_image = np.rot90(rotated_image) elif angle == -90: rotated_image = np.rot90(rotated_image) rotated_image = np.rot90(rotated_image) rotated_image = np.rot90(rotated_image) return rotated_image