Rotate the image and crop the black borders

My application: I'm trying to rotate an image (using OpenCV and Python)

Rotating images

At the moment, I have developed the code below that rotates the input image, filling it with black borders, giving me A. What I want is B - the window with the largest possible cropping area inside the rotated image. I call it an axis-aligned frame.

This is essentially the same as Rotate and crop , however I cannot get an answer to this question for work. In addition, this answer seems to be valid only for square images. My images are rectangular.

Code for getting A:

import cv2 import numpy as np def getTranslationMatrix2d(dx, dy): """ Returns a numpy affine transformation matrix for a 2D translation of (dx, dy) """ return np.matrix([[1, 0, dx], [0, 1, dy], [0, 0, 1]]) def rotateImage(image, angle): """ Rotates the given image about it centre """ image_size = (image.shape[1], image.shape[0]) image_center = tuple(np.array(image_size) / 2) rot_mat = np.vstack([cv2.getRotationMatrix2D(image_center, angle, 1.0), [0, 0, 1]]) trans_mat = np.identity(3) w2 = image_size[0] * 0.5 h2 = image_size[1] * 0.5 rot_mat_notranslate = np.matrix(rot_mat[0:2, 0:2]) tl = (np.array([-w2, h2]) * rot_mat_notranslate).A[0] tr = (np.array([w2, h2]) * rot_mat_notranslate).A[0] bl = (np.array([-w2, -h2]) * rot_mat_notranslate).A[0] br = (np.array([w2, -h2]) * rot_mat_notranslate).A[0] x_coords = [pt[0] for pt in [tl, tr, bl, br]] x_pos = [x for x in x_coords if x > 0] x_neg = [x for x in x_coords if x < 0] y_coords = [pt[1] for pt in [tl, tr, bl, br]] y_pos = [y for y in y_coords if y > 0] y_neg = [y for y in y_coords if y < 0] right_bound = max(x_pos) left_bound = min(x_neg) top_bound = max(y_pos) bot_bound = min(y_neg) new_w = int(abs(right_bound - left_bound)) new_h = int(abs(top_bound - bot_bound)) new_image_size = (new_w, new_h) new_midx = new_w * 0.5 new_midy = new_h * 0.5 dx = int(new_midx - w2) dy = int(new_midy - h2) trans_mat = getTranslationMatrix2d(dx, dy) affine_mat = (np.matrix(trans_mat) * np.matrix(rot_mat))[0:2, :] result = cv2.warpAffine(image, affine_mat, new_image_size, flags=cv2.INTER_LINEAR) return result 
+52
python algorithm opencv aabb
May 22 '13 at 23:03
source share
10 answers

So, after exploring many of the claimed solutions, I finally found a way that works; Answer by Andri and Magnus Hoff to Calculate the largest rectangle in a rotated rectangle .

The Python code below contains the method of interest - largest_rotated_rect - and a short demo.

 import math import cv2 import numpy as np def rotate_image(image, angle): """ Rotates an OpenCV 2 / NumPy image about it centre by the given angle (in degrees). The returned image will be large enough to hold the entire new image, with a black background """ # Get the image size # No that not an error - NumPy stores image matricies backwards image_size = (image.shape[1], image.shape[0]) image_center = tuple(np.array(image_size) / 2) # Convert the OpenCV 3x2 rotation matrix to 3x3 rot_mat = np.vstack( [cv2.getRotationMatrix2D(image_center, angle, 1.0), [0, 0, 1]] ) rot_mat_notranslate = np.matrix(rot_mat[0:2, 0:2]) # Shorthand for below calcs image_w2 = image_size[0] * 0.5 image_h2 = image_size[1] * 0.5 # Obtain the rotated coordinates of the image corners rotated_coords = [ (np.array([-image_w2, image_h2]) * rot_mat_notranslate).A[0], (np.array([ image_w2, image_h2]) * rot_mat_notranslate).A[0], (np.array([-image_w2, -image_h2]) * rot_mat_notranslate).A[0], (np.array([ image_w2, -image_h2]) * rot_mat_notranslate).A[0] ] # Find the size of the new image x_coords = [pt[0] for pt in rotated_coords] x_pos = [x for x in x_coords if x > 0] x_neg = [x for x in x_coords if x < 0] y_coords = [pt[1] for pt in rotated_coords] y_pos = [y for y in y_coords if y > 0] y_neg = [y for y in y_coords if y < 0] right_bound = max(x_pos) left_bound = min(x_neg) top_bound = max(y_pos) bot_bound = min(y_neg) new_w = int(abs(right_bound - left_bound)) new_h = int(abs(top_bound - bot_bound)) # We require a translation matrix to keep the image centred trans_mat = np.matrix([ [1, 0, int(new_w * 0.5 - image_w2)], [0, 1, int(new_h * 0.5 - image_h2)], [0, 0, 1] ]) # Compute the tranform for the combined rotation and translation affine_mat = (np.matrix(trans_mat) * np.matrix(rot_mat))[0:2, :] # Apply the transform result = cv2.warpAffine( image, affine_mat, (new_w, new_h), flags=cv2.INTER_LINEAR ) return result def largest_rotated_rect(w, h, angle): """ Given a rectangle of size wxh that has been rotated by 'angle' (in radians), computes the width and height of the largest possible axis-aligned rectangle within the rotated rectangle. Original JS code by 'Andri' and Magnus Hoff from Stack Overflow Converted to Python by Aaron Snoswell """ quadrant = int(math.floor(angle / (math.pi / 2))) & 3 sign_alpha = angle if ((quadrant & 1) == 0) else math.pi - angle alpha = (sign_alpha % math.pi + math.pi) % math.pi bb_w = w * math.cos(alpha) + h * math.sin(alpha) bb_h = w * math.sin(alpha) + h * math.cos(alpha) gamma = math.atan2(bb_w, bb_w) if (w < h) else math.atan2(bb_w, bb_w) delta = math.pi - alpha - gamma length = h if (w < h) else w d = length * math.cos(alpha) a = d * math.sin(alpha) / math.sin(delta) y = a * math.cos(gamma) x = y * math.tan(gamma) return ( bb_w - 2 * x, bb_h - 2 * y ) def crop_around_center(image, width, height): """ Given a NumPy / OpenCV 2 image, crops it to the given width and height, around it centre point """ image_size = (image.shape[1], image.shape[0]) image_center = (int(image_size[0] * 0.5), int(image_size[1] * 0.5)) if(width > image_size[0]): width = image_size[0] if(height > image_size[1]): height = image_size[1] x1 = int(image_center[0] - width * 0.5) x2 = int(image_center[0] + width * 0.5) y1 = int(image_center[1] - height * 0.5) y2 = int(image_center[1] + height * 0.5) return image[y1:y2, x1:x2] def demo(): """ Demos the largest_rotated_rect function """ image = cv2.imread("lenna_rectangle.png") image_height, image_width = image.shape[0:2] cv2.imshow("Original Image", image) print "Press [enter] to begin the demo" print "Press [q] or Escape to quit" key = cv2.waitKey(0) if key == ord("q") or key == 27: exit() for i in np.arange(0, 360, 0.5): image_orig = np.copy(image) image_rotated = rotate_image(image, i) image_rotated_cropped = crop_around_center( image_rotated, *largest_rotated_rect( image_width, image_height, math.radians(i) ) ) key = cv2.waitKey(2) if(key == ord("q") or key == 27): exit() cv2.imshow("Original Image", image_orig) cv2.imshow("Rotated Image", image_rotated) cv2.imshow("Cropped Image", image_rotated_cropped) print "Done" if __name__ == "__main__": demo() 

Image Rotation Demo

Just put this image (cropped to demonstrate that it works with non-square images) in the same directory as the above file, and then run it.

+24
May 27 '13 at 9:39
source share

The math behind this solution / implementation is equivalent to this solution to a similar question , but the formulas are simplified and avoid features. This is python code with the same interface as largest_rotated_rect from another solution, but at the same time it gives a large area in almost all cases (the best option is always proven):

 def rotatedRectWithMaxArea(w, h, angle): """ Given a rectangle of size wxh that has been rotated by 'angle' (in radians), computes the width and height of the largest possible axis-aligned rectangle (maximal area) within the rotated rectangle. """ if w <= 0 or h <= 0: return 0,0 width_is_longer = w >= h side_long, side_short = (w,h) if width_is_longer else (h,w) # since the solutions for angle, -angle and 180-angle are all the same, # if suffices to look at the first quadrant and the absolute values of sin,cos: sin_a, cos_a = abs(math.sin(angle)), abs(math.cos(angle)) if side_short <= 2.*sin_a*cos_a*side_long or abs(sin_a-cos_a) < 1e-10: # half constrained case: two crop corners touch the longer side, # the other two corners are on the mid-line parallel to the longer line x = 0.5*side_short wr,hr = (x/sin_a,x/cos_a) if width_is_longer else (x/cos_a,x/sin_a) else: # fully constrained case: crop touches all 4 sides cos_2a = cos_a*cos_a - sin_a*sin_a wr,hr = (w*cos_a - h*sin_a)/cos_2a, (h*cos_a - w*sin_a)/cos_2a return wr,hr 

Here is a comparison of the function with another solution:

 >>> wl,hl = largest_rotated_rect(1500,500,math.radians(20)) >>> print (wl,hl),', area=',wl*hl (828.2888697391496, 230.61639227890998) , area= 191016.990904 >>> wm,hm = rotatedRectWithMaxArea(1500,500,math.radians(20)) >>> print (wm,hm),', area=',wm*hm (730.9511000407718, 266.044443118978) , area= 194465.478358 

With an angle a in [0,pi/2[ bounding box of the rotated image (width w , height h ) has the following dimensions:

  • width w_bb = w*cos(a) + h*sin(a)
  • height h_bb = w*sin(a) + h*cos(a)

If w_r , h_r is the calculated optimal width and height of the cropped image, then inserts from the bounding box:

  • in the horizontal direction: (w_bb-w_r)/2
  • in the vertical direction: (h_bb-h_r)/2

Evidence:

Finding a rectangle with alignment along the axis between two parallel lines having the maximum area is an optimization task with one parameter, for example. x , as in this figure: animated parameter

Let s denote the distance between two parallel lines (it turns out to be the shorter side of the rotated rectangle). Then the sides a , b desired rectangle have a constant relationship with x , sx , namely x = a sin? and (sx) = b cos?:

enter image description here

Thus, maximizing the region a*b means maximizing x*(sx) . Due to the โ€œheight theoremโ€ for right triangles, we know x*(sx) = p*q = h*h . Therefore, the maximum area is achieved at x = sx = s/2 , i.e. Two angles E, G between parallel lines are on the midline:

enter image description here

This solution is only valid if this maximum rectangle fits into the rotated rectangle. Therefore, the diagonal EG should not be larger than the other side of the rotating rectangle l . As

EG = AF + DH = s / 2 * (cot? + Tan?) = S / (2 * sin? Cos?) = S / sin 2?

we have the condition s & le; lsin 2 ?, where s and l are the shorter and longer side of the rotated rectangle.

In the case s> lsin 2? the parameter x must be smaller (than s / 2) and st all the corners of the desired rectangle are located on the side of the rotary rectangle. This leads to the equation

x * cot? + (sx) * tan? = l

giving x = sin? (lcos? - ssin?) / cos 2 ?. From a = x / sin? and b = (sx) / cos? we get the above formulas.

+82
May 27 '13 at 18:40
source share

Congratulations on your excellent work! I wanted to use your code in OpenCV with the C ++ library, so I did the following conversion. Perhaps this approach may be useful to other people.

 #include <iostream> #include <opencv.hpp> #define PI 3.14159265359 using namespace std; double degree_to_radian(double angle) { return angle * PI / 180; } cv::Mat rotate_image (cv::Mat image, double angle) { // Rotates an OpenCV 2 image about its centre by the given angle // (in radians). The returned image will be large enough to hold the entire // new image, with a black background cv::Size image_size = cv::Size(image.rows, image.cols); cv::Point image_center = cv::Point(image_size.height/2, image_size.width/2); // Convert the OpenCV 3x2 matrix to 3x3 cv::Mat rot_mat = cv::getRotationMatrix2D(image_center, angle, 1.0); double row[3] = {0.0, 0.0, 1.0}; cv::Mat new_row = cv::Mat(1, 3, rot_mat.type(), row); rot_mat.push_back(new_row); double slice_mat[2][2] = { {rot_mat.col(0).at<double>(0), rot_mat.col(1).at<double>(0)}, {rot_mat.col(0).at<double>(1), rot_mat.col(1).at<double>(1)} }; cv::Mat rot_mat_nontranslate = cv::Mat(2, 2, rot_mat.type(), slice_mat); double image_w2 = image_size.width * 0.5; double image_h2 = image_size.height * 0.5; // Obtain the rotated coordinates of the image corners std::vector<cv::Mat> rotated_coords; double image_dim_d_1[2] = { -image_h2, image_w2 }; cv::Mat image_dim = cv::Mat(1, 2, rot_mat.type(), image_dim_d_1); rotated_coords.push_back(cv::Mat(image_dim * rot_mat_nontranslate)); double image_dim_d_2[2] = { image_h2, image_w2 }; image_dim = cv::Mat(1, 2, rot_mat.type(), image_dim_d_2); rotated_coords.push_back(cv::Mat(image_dim * rot_mat_nontranslate)); double image_dim_d_3[2] = { -image_h2, -image_w2 }; image_dim = cv::Mat(1, 2, rot_mat.type(), image_dim_d_3); rotated_coords.push_back(cv::Mat(image_dim * rot_mat_nontranslate)); double image_dim_d_4[2] = { image_h2, -image_w2 }; image_dim = cv::Mat(1, 2, rot_mat.type(), image_dim_d_4); rotated_coords.push_back(cv::Mat(image_dim * rot_mat_nontranslate)); // Find the size of the new image vector<double> x_coords, x_pos, x_neg; for (int i = 0; i < rotated_coords.size(); i++) { double pt = rotated_coords[i].col(0).at<double>(0); x_coords.push_back(pt); if (pt > 0) x_pos.push_back(pt); else x_neg.push_back(pt); } vector<double> y_coords, y_pos, y_neg; for (int i = 0; i < rotated_coords.size(); i++) { double pt = rotated_coords[i].col(1).at<double>(0); y_coords.push_back(pt); if (pt > 0) y_pos.push_back(pt); else y_neg.push_back(pt); } double right_bound = *max_element(x_pos.begin(), x_pos.end()); double left_bound = *min_element(x_neg.begin(), x_neg.end()); double top_bound = *max_element(y_pos.begin(), y_pos.end()); double bottom_bound = *min_element(y_neg.begin(), y_neg.end()); int new_w = int(abs(right_bound - left_bound)); int new_h = int(abs(top_bound - bottom_bound)); // We require a translation matrix to keep the image centred double trans_mat[3][3] = { {1, 0, int(new_w * 0.5 - image_w2)}, {0, 1, int(new_h * 0.5 - image_h2)}, {0, 0, 1}, }; // Compute the transform for the combined rotation and translation cv::Mat aux_affine_mat = (cv::Mat(3, 3, rot_mat.type(), trans_mat) * rot_mat); cv::Mat affine_mat = cv::Mat(2, 3, rot_mat.type(), NULL); affine_mat.push_back(aux_affine_mat.row(0)); affine_mat.push_back(aux_affine_mat.row(1)); // Apply the transform cv::Mat output; cv::warpAffine(image, output, affine_mat, cv::Size(new_h, new_w), cv::INTER_LINEAR); return output; } cv::Size largest_rotated_rect(int h, int w, double angle) { // Given a rectangle of size wxh that has been rotated by 'angle' (in // radians), computes the width and height of the largest possible // axis-aligned rectangle within the rotated rectangle. // Original JS code by 'Andri' and Magnus Hoff from Stack Overflow // Converted to Python by Aaron Snoswell (https://stackoverflow.com/questions/16702966/rotate-image-and-crop-out-black-borders) // Converted to C++ by Eliezer Bernart int quadrant = int(floor(angle/(PI/2))) & 3; double sign_alpha = ((quadrant & 1) == 0) ? angle : PI - angle; double alpha = fmod((fmod(sign_alpha, PI) + PI), PI); double bb_w = w * cos(alpha) + h * sin(alpha); double bb_h = w * sin(alpha) + h * cos(alpha); double gamma = w < h ? atan2(bb_w, bb_w) : atan2(bb_h, bb_h); double delta = PI - alpha - gamma; int length = w < h ? h : w; double d = length * cos(alpha); double a = d * sin(alpha) / sin(delta); double y = a * cos(gamma); double x = y * tan(gamma); return cv::Size(bb_w - 2 * x, bb_h - 2 * y); } // for those interested in the actual optimum - contributed by coproc #include <algorithm> cv::Size really_largest_rotated_rect(int h, int w, double angle) { // Given a rectangle of size wxh that has been rotated by 'angle' (in // radians), computes the width and height of the largest possible // axis-aligned rectangle within the rotated rectangle. if (w <= 0 || h <= 0) return cv::Size(0,0); bool width_is_longer = w >= h; int side_long = w, side_short = h; if (!width_is_longer) std::swap(side_long, side_short); // since the solutions for angle, -angle and pi-angle are all the same, // it suffices to look at the first quadrant and the absolute values of sin,cos: double sin_a = fabs(math.sin(angle)), cos_a = fabs(math.cos(angle)); double wr,hr; if (side_short <= 2.*sin_a*cos_a*side_long) { // half constrained case: two crop corners touch the longer side, // the other two corners are on the mid-line parallel to the longer line x = 0.5*side_short; wr = x/sin_a; hr = x/cos_a; if (!width_is_longer) std::swap(wr,hr); } else { // fully constrained case: crop touches all 4 sides double cos_2a = cos_a*cos_a - sin_a*sin_a; wr = (w*cos_a - h*sin_a)/cos_2a; hr = (h*cos_a - w*sin_a)/cos_2a; } return cv::Size(wr,hr); } cv::Mat crop_around_center(cv::Mat image, int height, int width) { // Given a OpenCV 2 image, crops it to the given width and height, // around it centre point cv::Size image_size = cv::Size(image.rows, image.cols); cv::Point image_center = cv::Point(int(image_size.height * 0.5), int(image_size.width * 0.5)); if (width > image_size.width) width = image_size.width; if (height > image_size.height) height = image_size.height; int x1 = int(image_center.x - width * 0.5); int x2 = int(image_center.x + width * 0.5); int y1 = int(image_center.y - height * 0.5); int y2 = int(image_center.y + height * 0.5); return image(cv::Rect(cv::Point(y1, x1), cv::Point(y2,x2))); } void demo(cv::Mat image) { // Demos the largest_rotated_rect function int image_height = image.rows; int image_width = image.cols; for (float i = 0.0; i < 360.0; i+=0.5) { cv::Mat image_orig = image.clone(); cv::Mat image_rotated = rotate_image(image, i); cv::Size largest_rect = largest_rotated_rect(image_height, image_width, degree_to_radian(i)); // for those who trust math (added by coproc): cv::Size largest_rect2 = really_largest_rotated_rect(image_height, image_width, degree_to_radian(i)); cout << "area1 = " << largest_rect.height * largest_rect.width << endl; cout << "area2 = " << largest_rect2.height * largest_rect2.width << endl; cv::Mat image_rotated_cropped = crop_around_center( image_rotated, largest_rect.height, largest_rect.width ); cv::imshow("Original Image", image_orig); cv::imshow("Rotated Image", image_rotated); cv::imshow("Cropped image", image_rotated_cropped); if (char(cv::waitKey(15)) == 'q') break; } } int main (int argc, char* argv[]) { cv::Mat image = cv::imread(argv[1]); if (image.empty()) { cout << "> The input image was not found." << endl; exit(EXIT_FAILURE); } cout << "Press [s] to begin or restart the demo" << endl; cout << "Press [q] to quit" << endl; while (true) { cv::imshow("Original Image", image); char opt = char(cv::waitKey(0)); switch (opt) { case 's': demo(image); break; case 'q': return EXIT_SUCCESS; default: break; } } return EXIT_SUCCESS; } 
+14
Nov 25 '14 at 21:29
source share

Rotate and crop in TensorFlow

I personally needed this function in TensorFlow, and thanks for Aaron Snoswell, I was able to implement this function.

 def _rotate_and_crop(image, output_height, output_width, rotation_degree, do_crop): """Rotate the given image with the given rotation degree and crop for the black edges if necessary Args: image: A 'Tensor' representing an image of arbitrary size. output_height: The height of the image after preprocessing. output_width: The width of the image after preprocessing. rotation_degree: The degree of rotation on the image. do_crop: Do cropping if it is True. Returns: A rotated image. """ # Rotate the given image with the given rotation degree if rotation_degree != 0: image = tf.contrib.image.rotate(image, math.radians(rotation_degree), interpolation='BILINEAR') # Center crop to ommit black noise on the edges if do_crop == True: lrr_width, lrr_height = _largest_rotated_rect(output_height, output_width, math.radians(rotation_degree)) resized_image = tf.image.central_crop(image, float(lrr_height)/output_height) image = tf.image.resize_images(resized_image, [output_height, output_width], method=tf.image.ResizeMethod.BILINEAR, align_corners=False) return image def _largest_rotated_rect(w, h, angle): """ Given a rectangle of size wxh that has been rotated by 'angle' (in radians), computes the width and height of the largest possible axis-aligned rectangle within the rotated rectangle. Original JS code by 'Andri' and Magnus Hoff from Stack Overflow Converted to Python by Aaron Snoswell Source: http://stackoverflow.com/questions/16702966/rotate-image-and-crop-out-black-borders """ quadrant = int(math.floor(angle / (math.pi / 2))) & 3 sign_alpha = angle if ((quadrant & 1) == 0) else math.pi - angle alpha = (sign_alpha % math.pi + math.pi) % math.pi bb_w = w * math.cos(alpha) + h * math.sin(alpha) bb_h = w * math.sin(alpha) + h * math.cos(alpha) gamma = math.atan2(bb_w, bb_w) if (w < h) else math.atan2(bb_w, bb_w) delta = math.pi - alpha - gamma length = h if (w < h) else w d = length * math.cos(alpha) a = d * math.sin(alpha) / math.sin(delta) y = a * math.cos(gamma) x = y * math.tan(gamma) return ( bb_w - 2 * x, bb_h - 2 * y ) 

If you need further implementation of the example and visualization in TensorFlow, you can use this repository . I hope this can be useful for other people.

+4
Jun 18 '18 at 9:41
source share

A small update for brevity that uses the excellent imutils library.

 def rotated_rect(w, h, angle): """ Given a rectangle of size wxh that has been rotated by 'angle' (in radians), computes the width and height of the largest possible axis-aligned rectangle within the rotated rectangle. Original JS code by 'Andri' and Magnus Hoff from Stack Overflow Converted to Python by Aaron Snoswell """ angle = math.radians(angle) quadrant = int(math.floor(angle / (math.pi / 2))) & 3 sign_alpha = angle if ((quadrant & 1) == 0) else math.pi - angle alpha = (sign_alpha % math.pi + math.pi) % math.pi bb_w = w * math.cos(alpha) + h * math.sin(alpha) bb_h = w * math.sin(alpha) + h * math.cos(alpha) gamma = math.atan2(bb_w, bb_w) if (w < h) else math.atan2(bb_w, bb_w) delta = math.pi - alpha - gamma length = h if (w < h) else w d = length * math.cos(alpha) a = d * math.sin(alpha) / math.sin(delta) y = a * math.cos(gamma) x = y * math.tan(gamma) return (bb_w - 2 * x, bb_h - 2 * y) def crop(img, w, h): x, y = int(img.shape[1] * .5), int(img.shape[0] * .5) return img[ int(np.ceil(y - h * .5)) : int(np.floor(y + h * .5)), int(np.ceil(x - w * .5)) : int(np.floor(x + h * .5)) ] def rotate(img, angle): # rotate, crop and return original size (h, w) = img.shape[:2] img = imutils.rotate_bound(img, angle) img = crop(img, *rotated_rect(w, h, angle)) img = cv2.resize(img,(w,h),interpolation=cv2.INTER_AREA) return img 
+3
Sep 25 '18 at 20:06
source share

Correction to the most favored solution above given by Coprox on May 27, 2013: when cosa = cosb infinity leads to the last two lines. Decide by adding "or cosa equal to cosb" in the previous selector.

Addition: if you do not know the original non-rotating nx and ny, but only have a rotated frame (or image), then find the square that just contains it (I do this by deleting empty = monochrome borders) and first run the program reversibly in size so that find nx and ny. If the image was rotated into a frame too small to be cut to the sides (in octagonal shape), I first find the x and y extensions for the full containment frame. However, this also does not work for angles of about 45 degrees, where the result gets a square instead of maintaining a non-rotating aspect ratio. For me, this procedure only works up to 30 degrees.

Still a great routine! This solved my grumbling problem in astronomical image alignment.

+1
Dec 24 '17 at 12:39 on
source share

There is an easy way to take care of this problem, which uses another module called PIL (useful only if you are not using opencv)

The code below does the same and moves any image so that you don't get black pixels

 from PIL import Image def array_to_img(x, scale=True): x = x.transpose(1, 2, 0) if scale: x += max(-np.min(x), 0) x /= np.max(x) x *= 255 if x.shape[2] == 3: return Image.fromarray(x.astype("uint8"), "RGB") else: return Image.fromarray(x[:,:,0].astype("uint8"), "L") def img_to_array(img): x = np.asarray(img, dtype='float32') if len(x.shape)==3: # RGB: height, width, channel -> channel, height, width x = x.transpose(2, 0, 1) else: # grayscale: height, width -> channel, height, width x = x.reshape((1, x.shape[0], x.shape[1])) return x if __name__ == "__main__": # Calls a function to convert image to array image_array = img_to_array(image_name) # Calls the function to rotate the image by given angle rotated_image = array_to_img(random_rotation(image_array, rotation_angle)) # give the location where you want to store the image rotated_image_name=<location_of_the_image_>+'roarted_image.png' # Saves the image in the mentioned location rotated_image.save(rotated_image_name) 
0
Jun 14 '16 at 6:51
source share

Inspired by the amazing work of Coprox, I wrote a function that, together with the Coprox code, forms a complete solution (so that it can be used by copying and pasting without problems). The rotate_max_area function below simply returns a rotated image without a black border.

 def rotate_bound(image, angle): # CREDIT: https://www.pyimagesearch.com/2017/01/02/rotate-images-correctly-with-opencv-and-python/ (h, w) = image.shape[:2] (cX, cY) = (w // 2, h // 2) M = cv2.getRotationMatrix2D((cX, cY), -angle, 1.0) cos = np.abs(M[0, 0]) sin = np.abs(M[0, 1]) nW = int((h * sin) + (w * cos)) nH = int((h * cos) + (w * sin)) M[0, 2] += (nW / 2) - cX M[1, 2] += (nH / 2) - cY return cv2.warpAffine(image, M, (nW, nH)) def rotate_max_area(image, angle): """ image: cv2 image matrix object angle: in degree """ wr, hr = rotatedRectWithMaxArea(image.shape[1], image.shape[0], math.radians(angle)) rotated = rotate_bound(image, angle) h, w, _ = rotated.shape y1 = h//2 - int(hr/2) y2 = y1 + int(hr) x1 = w//2 - int(wr/2) x2 = x1 + int(wr) return rotated[y1:y2, x1:x2] 
0
Jan 04 '18 at 18:37
source share

Fast decision

Thanks to Coproc for his excellent solution. Here is the code in Swift

 // Given a rectangle of size.width x size.height that has been rotated by 'angle' (in // radians), computes the width and height of the largest possible // axis-aligned rectangle (maximal area) within the rotated rectangle. func rotatedRectWithMaxArea(size: CGSize, angle: CGFloat) -> CGSize { let w = size.width let h = size.height if(w <= 0 || h <= 0) { return CGSize.zero } let widthIsLonger = w >= h let (sideLong, sideShort) = widthIsLonger ? (w, h) : (w, h) // since the solutions for angle, -angle and 180-angle are all the same, // if suffices to look at the first quadrant and the absolute values of sin,cos: let (sinA, cosA) = (sin(angle), cos(angle)) if(sideShort <= 2*sinA*cosA*sideLong || abs(sinA-cosA) < 1e-10) { // half constrained case: two crop corners touch the longer side, // the other two corners are on the mid-line parallel to the longer line let x = 0.5*sideShort let (wr, hr) = widthIsLonger ? (x/sinA, x/cosA) : (x/cosA, x/sinA) return CGSize(width: wr, height: hr) } else { // fully constrained case: crop touches all 4 sides let cos2A = cosA*cosA - sinA*sinA let (wr, hr) = ((w*cosA - h*sinA)/cos2A, (h*cosA - w*sinA)/cos2A) return CGSize(width: wr, height: hr) } } 
0
Jan 29 '18 at 23:31
source share

Perhaps an even simpler solution would be:

 def crop_image(image, angle): h, w = image.shape tan_a = abs(np.tan(angle * np.pi / 180)) b = int(tan_a / (1 - tan_a ** 2) * (h - w * tan_a)) d = int(tan_a / (1 - tan_a ** 2) * (w - h * tan_a)) return image[d:h - d, b:w - b] 

Instead of calculating the height and width of the rotated rectangle, as many have done, just find the height of the black triangles that form when the image is rotated.

0
Jul 08 '19 at 11:06 on
source share



All Articles