Faire pivoter une image sans recadrer dans OpenCV en C ++

Je voudrais faire pivoter une image, mais je ne peux pas obtenir l’image pivotée sans recadrage

Mon image originale:

entrer la description de l'image ici

J’utilise maintenant ce code:

#include  #include  #include  // Comstack 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::getRotationMasortingx2D(pc, -45, 1.0); cv::warpAffine(src, dst, r, src.size()); // what size I should use? cv::imwrite("rotated_im.png", dst); return 0; } 

Et obtenez l’image suivante:

entrer la description de l'image ici

Mais j’aimerais obtenir ceci:

entrer la description de l'image ici

Merci beaucoup pour votre aide!

Ma réponse s’inspire des articles / entrées de blog suivants:

Les idées principales:

  • Ajuster la masortingce de rotation en ajoutant une traduction au nouveau centre de l’image
  • Utiliser cv::RotatedRect pour s’appuyer autant que possible sur les fonctionnalités existantes

Code testé avec 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 masortingx 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::getRotationMasortingx2D(center, angle, 1.0); // determine bounding rectangle, center not relevant cv::Rect2f bbox = cv::RotatedRect(cv::Point2f(), src.size(), angle).boundingRect2f(); // adjust transformation masortingx 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; } 

Essayez simplement le code ci-dessous, l’idée est simple:

  1. Vous devez créer une image vierge avec la taille maximale que vous attendez tout en tournant à n’importe quel angle. Ici, vous devez utiliser Pythagoras comme mentionné dans les commentaires ci-dessus.

  2. Copiez maintenant l’image source sur l’image nouvellement créée et transmettez-la à warpAffine . Ici, vous devez utiliser le centre de l’image nouvellement créée pour la rotation.

  3. Après warpAffine si vous avez besoin de rogner l’image exacte pour cela, traduisez les quatre coins de l’image source en image agrandie en utilisant la masortingce de rotation comme décrit ici

  4. Trouvez le minimum x et le minimum y pour le coin supérieur et le maximum x et le maximum y pour le coin inférieur du résultat ci-dessus pour recadrer l’image.

Ceci est le 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; } 

entrer la description de l'image ici

Image recadrée

entrer la description de l'image icientrer la description de l'image ici

Merci Robula! En fait, vous n’avez pas besoin de calculer le sinus et le cosinus deux fois.

 import cv2 def rotate_image(mat, angle): # angle in degrees height, width = mat.shape[:2] image_center = (width/2, height/2) rotation_mat = cv2.getRotationMasortingx2D(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 

Merci @Haris! Voici la version de 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 masortingx. - 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.getRotationMasortingx2D(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 

Après avoir cherché une solution propre et facile à comprendre et lu les réponses ci-dessus en essayant de les comprendre, j’ai finalement trouvé une solution utilisant la sortinggonomésortinge.

J’espère que cela aide quelqu’un 🙂

 import cv2 import math def rotate_image(mat, angle): height, width = mat.shape[:2] image_center = (width / 2, height / 2) rotation_mat = cv2.getRotationMasortingx2D(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: Veuillez vous reporter à la réponse de @Remi Cuingnet ci-dessous.

Augmentez le canevas d’image (également à partir du centre sans changer la taille de l’image) pour qu’il puisse s’adapter à l’image après la rotation, puis appliquez 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 = getRotationMasortingx2D (center, angle, 1.0); warpAffine (resized, resized, M, resized.size()); 

entrer la description de l'image ici

Merci à tous pour ce post, ça a été super utile. Cependant, j’ai trouvé des lignes noires à gauche et à droite (en utilisant la version python de Rose) en tournant de 90º. Le problème semblait être des arrondissements int (). En plus de cela, j’ai changé le signe de l’angle pour le faire croître dans le sens des aiguilles d’une montre.

 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 masortingx. - 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.getRotationMasortingx2D(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 

Si c’est juste pour faire pivoter 90 degrés, peut-être que ce code pourrait être utile.

  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 = getRotationMasortingx2D(pc, 90, 1); warpAffine(img, rt, r, rt.size()); imshow("rotated", rt); 

J’espère que c’est utile.

En passant, pour les rotations à 90º seulement, voici une fonction plus efficace + précise:

 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