miki725
miki725

Reputation: 27861

Cropping rotated image with same aspect ratio

How can I find out the width and height of the final image when the input image is rotated by given amount of degrees and then cropped to avoid any non-image areas while maintaining the original image aspect ratio.

example: example

Upvotes: 5

Views: 3978

Answers (3)

Roman Dubinin
Roman Dubinin

Reputation: 1

This code returns coords of this block

returned box - is the coords of this block

from PIL import Image
def cropp_rotated(image, degrees):
    x, y = image.size
    cosA = abs(math.cos(math.radians(degrees)))
    sinA = abs(math.sin(math.radians(degrees)))

    a = x * cosA 
    b = x * sinA

    relation = a / (a + b)
    right_indent1 = a - x * relation * cosA

    relation = b / (a + b)
    bottom_ident1 = b - x * relation *sinA


    c = y * cosA 
    d = y * sinA

    relation = c / (c + d)
    right_indent2 = c - y * relation * cosA

    relation = d / (c + d)
    bottom_ident2 = d - y * relation *sinA

    right_indent = max(right_indent1, right_indent2)
    top_indent = max(bottom_ident1, bottom_ident2)

    #size of rotated image:
    w_rotated = x * cosA + y * sinA
    h_rotated = y * cosA + x * sinA


    box = (
    int(right_indent), 
    int(top_indent), 
    int(w_rotated - right_indent), 
    int(h_rotated - top_indent))

    return box

I import PIL only for get image real size. You can not import PIL, instead of this you can get image size any other way.

Upvotes: 0

mhlester
mhlester

Reputation: 23231

enter image description here

Definitions:

  • W0 and H0 are the original rectangle's width and height

  • D0 is the diagonal (hypotenuse) of the original rectangle

  • W1 and H1 are the padded width and height of the rotated rectangle

  • W2 and H2 are the width and height of the target rectangle

  • D2 is the diagonal (hypotenuse) of the target rectangle

  • E is complicated and transient

  • You probably want to precompute sin(A), cos(A), and tan(A) as they are used repeatedly

Computing W1 and H1:

These are made up of two components, the two triangle sides that make up each length

  • W1 = W0*cos(A) + H0*sin(A)
  • H1 = H0*cos(A) + W0*sin(A)

Computing W2 and H2:

  • E = W0 / (1 + tan(A) / W0 * H0)
  • W2 = E / tan(A)
  • H2 = W2 / W0 * H0

As Python:

W0 = # original rectangle width
H0 = # original rectangle height
cosA = cos(A)
sinA = sin(A)
tanA = tan(A)
W1 = W0 * cosA + H0 * sinA
H1 = H0 * cosA + W0 * sinA
E = W0 / (1 + tanA / W0 * H0)
W2 = E / tanA       # requested width
H2 = W2 / W0 * H0   # requested height

Disclaimer: this is all untested


Edit: by request, my best guess of what I meant by E:

r0 = w2/h2
h2 = (w0-e1) / sin(A)
h2 = e1/ cos(A) / r0
(w0-e1) / sin(A) = e1/ cos(A) / r0
x = cos(A) / sin(A)
e1 = w0 * x / (1/r0 + x)

E was the horizontal component of w2, extended through the line a+b. Then it was about computing what was a vs b, using the aspect ratio and sin/cos to figure that out. Terms cancelled, voices were raised, and that's what I ended up with.

Upvotes: 1

miki725
miki725

Reputation: 27861

enter image description here

  • Red rectangle is original image with original aspect ratio.
  • W/t rectangle (enclosed by green and yellow triangles) is rotated image aspect ratio with extend=True.

Here is how to get the w and h.

image = Image.open(...)
rotated = image.rotate(degrees, Image.BICUBIC, True)

aspect_ratio = float(image.size[0]) / image.size[1]
rotated_aspect_ratio = float(rotated.size[0]) / rotated.size[1]
angle = math.fabs(degrees) * math.pi / 180

if aspect_ratio < 1:
    total_height = float(image.size[0]) / rotated_aspect_ratio
else:
    total_height = float(image.size[1])

h = total_height / (aspect_ratio * math.sin(angle) + math.cos(angle))
w = h * aspect_ratio

Now the rotated image can be cropped in the center with wxh dimensions to get the final image.

Upvotes: 8

Related Questions