Drehen Sie ein Bild ohne Beschneiden in OpenCV in C ++

Ich möchte ein Bild drehen, aber das gedrehte Bild kann ich nicht ohne Zuschneiden erhalten

Mein ursprüngliches Bild:

Bildbeschreibung hier eingeben

Jetzt benutze ich diesen Code:

#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; } 

Und erhalte das folgende Bild:

Bildbeschreibung hier eingeben

Aber ich möchte das erhalten:

Bildbeschreibung hier eingeben

Vielen Dank für Ihre Hilfe!

   

Meine Antwort ist inspiriert von den folgenden Posts / Blogeinträgen:

Hauptideen:

  • Anpassen der Rotationsmatrix durch Hinzufügen einer Translation zum neuen Bildzentrum
  • Verwenden Sie cv::RotatedRect um so weit wie möglich auf vorhandene opencv-functionen zu vertrauen

Code getestet mit 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; } 

Probieren Sie einfach den folgenden Code aus, die Idee ist einfach:

  1. Sie müssen ein leeres Bild mit der maximalen Größe erstellen, die Sie erwarten, wenn Sie in einem beliebigen angular drehen. Hier sollten Sie Pythagoras wie in den obigen Kommentaren erwähnt verwenden.

  2. Kopieren Sie nun das warpAffine in das neu erstellte Bild und übergeben Sie es an warpAffine . Hier sollten Sie den Mittelpunkt des neu erstellten Bildes für die Rotation verwenden.

  3. Nach warpAffine wenn Sie ein exaktes Bild warpAffine müssen, werden vier Ecken des warpAffine im vergrößerten Bild mit Hilfe der Rotationsmatrix, wie hier beschrieben , übertragen

  4. Bestimmen Sie das Minimum x und das Minimum y für die obere Ecke und das Maximum x und das Maximum y für die untere Ecke aus dem obigen Ergebnis, um das Bild zu beschneiden.

Dies ist der Code:

 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; } 

Bildbeschreibung hier eingeben

Bild abgeschnitten

Bildbeschreibung hier eingebenBildbeschreibung hier eingeben

Danke Robula! Eigentlich müssen Sie Sinus und Cosinus nicht zweimal berechnen.

 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 

Danke @Haris! Hier ist die Python-Version:

 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 

Nachdem ich mich nach einer sauberen und leicht verständlichen Lösung umgesehen habe und die obigen Antworten durchgelesen habe, um sie zu verstehen, fand ich schließlich eine Lösung, die Trigonometrie verwendet.

Ich hoffe, das hilft jemandem 🙂

 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: Bitte beziehen Sie sich auf @Remi Cuingnet Antwort unten.

Vergrößern Sie den Bildbereich (gleichmäßig von der Mitte aus, ohne die Bildgröße zu ändern), sodass er nach der Drehung in das Bild passt, und wenden warpAffine dann 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()); 

Bildbeschreibung hier eingeben

Danke an alle für diesen Beitrag, es war super nützlich. Ich habe jedoch einige schwarze Linien links und oben gefunden (mit Rose’s Python-Version), wenn ich um 90º rotiere. Das Problem schien einige int () Rundungen zu sein. Außerdem habe ich das Vorzeichen des angulars geändert, um es im Uhrzeigersinn wachsen zu lassen.

 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 

Wenn es nur um 90 Grad gedreht werden soll, könnte dieser Code nützlich sein.

  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); 

Ich hoffe, es ist nützlich.

Übrigens, hier ist nur für 90º Drehungen eine effizientere + genaue function:

 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