Rotate polygons without cutting edges - python

I am writing an augmentation code to rotate annotated polygons inside images. I wrote a code but it's not working right. Just Copy paste the code and you can get the results. Thank you for helping me out.
Image:
Need to rotate the image as well as a polygon for respective angle. Currently, I am not able to rotate the polygon
The image is rotated but the polygon is still in its place.
I tried This code. It rotates the polygon but not at the right position
import math
from PIL import Image, ImageDraw
from PIL import ImagePath
from PIL import Image
import matplotlib.pyplot as plt
from math import sin, cos, radians
import requests
from io import BytesIO
def rotatePolygon(polygon, degrees, height, width):
"""
Description:
Rotate polygon the given angle about its center.
Input:
polygon (list of tuples) : list of tuples with (x,y) cordinates
e.g [(1,2), (2,3), (4,5)]
degrees int : Rotation Degrees
Output:
polygon (list of tuples) : Polygon rotated on angle(degrees)
e.g [(1,2), (2,3), (4,5)]
"""
# Convert angle to radians
theta = radians(degrees)
# Getting sin and cos with respect to theta
cosang, sinang = cos(theta), sin(theta)
# find center point of Polygon to use as pivot
y, x = [i for i in zip(*polygon)]
# find center point of Polygon to use as pivot
cx = width / 2
cy = height / 2
# Rotating every point
new_points = []
for x, y in zip(x, y):
tx, ty = x-cx, y-cy
new_x = (tx*cosang + ty*sinang) + cx
new_y = (-tx*sinang + ty*cosang) + cy
new_points.append((new_y, new_x))
return new_points
# Polygon
xy = [(85, 384), (943, 374), (969, 474), (967, 527), (12, 540), (7, 490)]
degrees = 270
# Getting Image from URL
try:
img = Image.open("polygon_image.png")
except:
url = "https://github.com/SohaibAnwaar/Mask---RCNN-Polygons-/blob/main/2_image_augmentation/extras/problamatic_image.jpg?raw=true"
response = requests.get(url)
img = Image.open(BytesIO(response.content))
img.save("polygon_image.png")
# Rotating Image
rotated_image = img.rotate(degrees,expand = True)
h, w = img.size
print("NotRotated", xy)
rotated_xy = rotatePolygon(xy, 360 - (degrees), h, w)
# Ploting Rotated Image
img1 = ImageDraw.Draw(rotated_image)
img1.polygon(rotated_xy, fill ="#FFF000", outline ="blue")
# Ploting Straight Image
img1 = ImageDraw.Draw(img)
img1.polygon(xy, fill ="#FFF000", outline ="blue")
plt.imshow(rotated_image)
plt.show()
plt.imshow(img)
plt.show()

Rotation equations are:
xnew = x * cos(theta) - y * sin(theta)
ynew = x * sin(theta) + y * cos(theta)
only mistake you are doing is this:
new_x = (tx*cosang - ty*sinang) + cy
new_y = (tx*sinang + ty*cosang) + cx
After rotating image, cx and cy should be changed
Your complete code is as below:
import math
import numpy as np
from PIL import Image, ImageDraw
from PIL import ImagePath
from PIL import Image
import matplotlib.pyplot as plt
from math import sin, cos, radians
import requests
from io import BytesIO
def rotatePolygon(polygon, degrees, height, width):
"""
Description:
Rotate polygon the given angle about its center.
Input:
polygon (list of tuples) : list of tuples with (x,y) cordinates
e.g [(1,2), (2,3), (4,5)]
degrees int : Rotation Degrees
Output:
polygon (list of tuples) : Polygon rotated on angle(degrees)
e.g [(1,2), (2,3), (4,5)]
"""
# Convert angle to radians
theta = radians(degrees)
# Getting sin and cos with respect to theta
cosang, sinang = cos(theta), sin(theta)
# find center point of Polygon to use as pivot
y, x = [i for i in zip(*polygon)]
# find center point of Polygon to use as pivot
cx1 = width[0] / 2
cy1 = height[0] / 2
cx2 = width[1] / 2
cy2 = height[1] / 2
# Rotating every point
new_points = []
for x, y in zip(x, y):
tx, ty = x-cx1, y-cy1
new_x = (tx*cosang - ty*sinang) + cx2
new_y = (tx*sinang + ty*cosang) + cy2
new_points.append((new_y, new_x))
return new_points
# Polygon
xy = [(85, 384), (943, 374), (969, 474), (967, 527), (12, 540), (7, 490)]
degrees = 270
# Getting Image from URL
try:
img = Image.open("polygon_image.png")
except:
url = "https://github.com/SohaibAnwaar/Mask---RCNN-Polygons-/blob/main/2_image_augmentation/extras/problamatic_image.jpg?raw=true"
response = requests.get(url)
img = Image.open(BytesIO(response.content))
img.save("polygon_image.png")
# Rotating Image
rotated_image = img.rotate(degrees,expand = True)
h1, w1 = img.size
h2, w2 = rotated_image.size
print("NotRotated", xy)
rotated_xy = rotatePolygon(xy, degrees, [h1,h2], [w1,w2])
# Ploting Rotated Image
img1 = ImageDraw.Draw(rotated_image)
img1.polygon(rotated_xy, fill ="#FFF000", outline ="blue")
# Ploting Straight Image
img1 = ImageDraw.Draw(img)
img1.polygon(xy, fill ="#FFF000", outline ="blue")
plt.imshow(rotated_image)
plt.show()
plt.imshow(img)
plt.show()

For a rotation to the right by a right angle, use the following equations (assuming X/U left-to-right and Y/V top-down):
U = H - Y
V = X
where the image size is W x H.

Related

How to rotate a rectangle/bounding box together with an image

I'm working on a data augmentation and im trying to generate synthetic version of every image in my dataset. So i need to rotate images and together with bounding boxes as well in the images.
im only going to rotate images by 90, 180, 270 degrees.
I'm using pascal-voc annotation format as shown here. As a result i have following info.
x_min, y_min, x_max, y_max. Origin of image(i can get it from image size)
i've searched a lot on it. But i couldnt find any solution for rotating bounding boxes( or rectangles)
i've tried something like this;
i've got this solution from here and tried to adapt it but didnt work.
def rotateRect(bndbox, img_size, angle):
angle = angle * math.pi/180 # conversion from degree to radian
y_min, y_max, x_min, x_max = bndbox
ox, oy = img_size[0]/2, img_size[1]/2 # coordinate of origin of image
rect = [[x_min, y_min], [x_min, y_max],[x_max, y_min],[x_max, y_max]] # coordinates of points of corners of bounding box rectangle.
nrp = [[0, 0], [0,0 ],[0,0],[0, 0]] #new rectangle position
for i, pt in enumerate(rect):
newPx = int(ox + math.cos(angle) * (pt[0] - ox) - math.sin(angle) * (pt[1] - oy)) # new coordinate of point x
newPy = int(oy + math.sin(angle) * (pt[0] - ox) + math.cos(angle) * (pt[1] - oy)) # new coordinate of point y
nrp[i] = newPx,newPy
nx_min, ny_min, nx_max, ny_max = nrp[0][0], nrp[0][1], nrp[2][0], nrp[2][1] # new bounding boxes values.
return [ny_min, ny_max, nx_min, nx_max]
thanks.
EDIT:
I need to get this rotation together with image and bounding box.
First picture is original one, second one is rotated as 90 degree(counter-clockwise) and 3rd picture is rotated as -90 degree (counter-wise).
i tried to rotate manually on paint to be precise. So i got these results.
original of img size:(640x480)
rotation orj, 90, -90
--------------
x_min = 98, 345, 17
y_min = 345, 218, 98
x_max = 420, 462, 420
y_max = 462, 540, 134
i've found simpler way.
Base on this aproach. We can do this calculation without using trigonometric calculations like this:
def rotate90Deg(bndbox, img_width): # just passing width of image is enough for 90 degree rotation.
x_min,y_min,x_max,y_max = bndbox
new_xmin = y_min
new_ymin = img_width-x_max
new_xmax = y_max
new_ymax = img_width-x_min
return [new_xmin, new_ymin,new_xmax,new_ymax]
rotate90Deg([98,345,420,462],640)
this can be used over and over again. And returns new bounding boxes values in Pascal-voc format.
OK, maybe this can help. Assuming your rectangle is stored as a set of 4 points marking the corners, this will do arbitrary rotation around another point. If you store the points in circular order, then plot will even look like rectangles. I'm not forcing the aspect ratio on the plot, so the rotated rectangle looks like it is skewed, but it's not.
import math
import matplotlib.pyplot as plt
def rotatebox( rect, center, degrees ):
rads = math.radians(degrees)
newpts = []
for pts in rect:
diag_x = center[0] - pts[0]
diag_y = center[1] - pts[1]
# Rotate the diagonal from center to top left
newdx = diag_x * math.cos(rads) - diag_y * math.sin(rads)
newdy = diag_x * math.sin(rads) + diag_y * math.cos(rads)
newpts.append( (center[0] + newdx, center[1] + newdy) )
return newpts
# Return a set of X and Y for plotting.
def corners(rect):
return [k[0] for k in rect]+[rect[0][0]],[k[1] for k in rect]+[rect[0][1]]
rect = [[50,50],[50,120],[150,120],[150,50]]
plt.plot( *corners(rect) )
rect = rotatebox( rect, (100,100), 135 )
plt.plot( *corners(rect) )
plt.show()
The code can be made simpler for the 90/180/270 cases, because no trigonometry is needed. It's just addition, subtraction, and swapping points. Here, the rectangle is just stored [minx,miny,maxx,maxy].
import matplotlib.pyplot as plt
def rotaterectcw( rect, center ):
x0 = rect[0] - center[0]
y0 = rect[1] - center[1]
x1 = rect[2] - center[0]
y1 = rect[3] - center[1]
return center[0]+y0, center[1]-x0, center[0]+y1, center[1]-x1
def corners(rect):
x0, y0, x1, y1 = rect
return [x0,x0,x1,x1,x0],[y0,y1,y1,y0,y0]
rect = (50,50,150,120)
plt.plot( *corners(rect) )
rect = rotaterectcw( rect, (60,100) )
plt.plot( *corners(rect) )
rect = rotaterectcw( rect, (60,100) )
plt.plot( *corners(rect) )
rect = rotaterectcw( rect, (60,100) )
plt.plot( *corners(rect) )
plt.show()
I tried the implementations mentioned in the other answers but none of them worked for me. I had to rotate the image and the bounding box clockwise by 90 degrees so I made this method,
def rotate90Deg( bndbox , image_width ):
"""
image_width: Width of the image after clockwise rotation of 90 degrees
"""
x_min,y_min,x_max,y_max = bndbox
new_xmin = image_width - y_max # Reflection about center X-line
new_ymin = x_min
new_xmax = image_width - y_min # Reflection about center X-line
new_ymax = x_max
return [new_xmin, new_ymin,new_xmax,new_ymax]
Usage
image = Image.open( "..." )
image = image.rotate( -90 )
new_bbox = rotate90Deg( bbox , image.width )

Python: Show cartesian image in polar plot

Description:
I have this data represented in a cartesian coordinate system with 256 columns and 640 rows. Each column represents an angle, theta, from -65 deg to 65 deg. Each row represents a range, r, from 0 to 20 m.
An example is given below:
With the following code I try to make a grid and transform each pixel location to the location it would have on a polar grid:
def polar_image(image, bearings):
(h,w) = image.shape
x_max = (np.ceil(np.sin(np.deg2rad(np.max(bearings)))*h)*2+1).astype(int)
y_max = (np.ceil(np.cos(np.deg2rad(np.min(np.abs(bearings))))*h)+1).astype(int)
blank = np.zeros((y_max,x_max,1), np.uint8)
for i in range(w):
for j in range(h):
X = (np.sin(np.deg2rad( bearings[i]))*j)
Y = (-np.cos(np.deg2rad(bearings[i]))*j)
blank[(Y+h).astype(int),(X+562).astype(int)] = image[h-1-j,w-1-i]
return blank
This returns an image as below:
Questions:
This is sort of what I actually want to achieve except from two things:
1) there seem to be some artifacts in the new image and also the mapping seems a bit coarse.
Does someone have a suggestion on how to interpolate to get rid of this?
2) The image remains in a Cartesian representation, meaning that I don't have any polar gridlines, nor can I visualize intervals of range/angle.
Anybody know how to visualize the polar grids with axis ticks in theta and range?
You can use pyplot.pcolormesh() to plot the converted mesh:
import numpy as np
import pylab as pl
img = pl.imread("c:/tmp/Wnov4.png")
angle_max = np.deg2rad(65)
h, w = img.shape
angle, r = np.mgrid[-angle_max:angle_max:h*1j, 0:20:w*1j]
x = r * np.sin(angle)
y = r * np.cos(angle)
fig, ax = pl.subplots()
ax.set_aspect("equal")
pl.pcolormesh(x, y, img, cmap="gray");
or you can use the remap() in OpenCV to convert it to a new image:
import cv2
import numpy as np
from PIL import Image
img = cv2.imread(r"c:/tmp/Wnov4.png", cv2.IMREAD_GRAYSCALE)
angle_max = np.deg2rad(65)
r_max = 20
x = np.linspace(-20, 20, 800)
y = np.linspace(20, 0, 400)
y, x = np.ix_(y, x)
r = np.hypot(x, y)
a = np.arctan2(x, y)
map_x = r / r_max * img.shape[1]
map_y = a / (2 * angle_max) * img.shape[0] + img.shape[0] * 0.5
img2 = cv2.remap(img, map_x.astype(np.float32), map_y.astype(np.float32), cv2.INTER_CUBIC)
Image.fromarray(img2)

Image processing - eliminate arc-like smears

I am dealing with this kind of image
(upper is post-processed)
(lower is raw)
So, first I converted the grayscale image into pure black and white binary image. I am interested in detecting the white blobs, and want to get rid of the arc-like smears in the corners. How can I do that?
I general, I know that my targets are almost circular in shape, not too big, but I want to encode something that automatically gets rid of everything else, like the lighter arcs in the upper left and right corners.
How would I do this in python, ideally skimage?
You can just detect circle of the right size with skimage's methods hough_circle and hough_circle_peaks and cut it out.
Here I adapted my previous answer to your other question to do this:
# skimage version 0.14.0
import math
import numpy as np
import matplotlib.pyplot as plt
from skimage import color
from skimage.io import imread
from skimage.transform import hough_circle, hough_circle_peaks
from skimage.feature import canny
from skimage.draw import circle
from skimage.util import img_as_ubyte
INPUT_IMAGE = 'dish1.png' # input image name
BEST_COUNT = 1 # how many circles to detect (one dish)
MIN_RADIUS = 100 # min radius of the Petri dish
MAX_RADIUS = 122 # max radius of the Petri dish (in pixels)
LARGER_THRESH = 1.2 # circle is considered significantly larger than another one if its radius is at least so much bigger
OVERLAP_THRESH = 0.1 # circles are considered overlapping if this part of the smaller circle is overlapping
def circle_overlap_percent(centers_distance, radius1, radius2):
'''
Calculating the percentage area overlap between circles
See Gist for comments:
https://gist.github.com/amakukha/5019bfd4694304d85c617df0ca123854
'''
R, r = max(radius1, radius2), min(radius1, radius2)
if centers_distance >= R + r:
return 0.0
elif R >= centers_distance + r:
return 1.0
R2, r2 = R**2, r**2
x1 = (centers_distance**2 - R2 + r2 )/(2*centers_distance)
x2 = abs(centers_distance - x1)
y = math.sqrt(R2 - x1**2)
a1 = R2 * math.atan2(y, x1) - x1*y
if x1 <= centers_distance:
a2 = r2 * math.atan2(y, x2) - x2*y
else:
a2 = math.pi * r2 - a2
overlap_area = a1 + a2
return overlap_area / (math.pi * r2)
def circle_overlap(c1, c2):
d = math.sqrt((c1[0]-c2[0])**2 + (c1[1]-c2[1])**2)
return circle_overlap_percent(d, c1[2], c2[2])
def inner_circle(cs, c, thresh):
'''Is circle `c` is "inside" one of the `cs` circles?'''
for dc in cs:
# if new circle is larger than existing -> it's not inside
if c[2] > dc[2]*LARGER_THRESH: continue
# if new circle is smaller than existing one...
if circle_overlap(dc, c)>thresh:
# ...and there is a significant overlap -> it's inner circle
return True
return False
# Load picture and detect edges
image = imread(INPUT_IMAGE, 1)
image = img_as_ubyte(image)
edges = canny(image, sigma=3, low_threshold=10, high_threshold=50)
# Detect circles of specific radii
hough_radii = np.arange(MIN_RADIUS, MAX_RADIUS, 2)
hough_res = hough_circle(edges, hough_radii)
# Select the most prominent circles (in order from best to worst)
accums, cx, cy, radii = hough_circle_peaks(hough_res, hough_radii)
# Determine BEST_COUNT circles to be drawn
drawn_circles = []
for crcl in zip(cy, cx, radii):
# Do not draw circles if they are mostly inside better fitting ones
if not inner_circle(drawn_circles, crcl, OVERLAP_THRESH):
# A good circle found: exclude smaller circles it covers
i = 0
while i<len(drawn_circles):
if circle_overlap(crcl, drawn_circles[i]) > OVERLAP_THRESH:
t = drawn_circles.pop(i)
else:
i += 1
# Remember the new circle
drawn_circles.append(crcl)
# Stop after have found more circles than needed
if len(drawn_circles)>BEST_COUNT:
break
drawn_circles = drawn_circles[:BEST_COUNT]
# Draw circle and cut it out
colors = [(250, 0, 0), (0, 250, 0), (0, 0, 250)]
fig, ax = plt.subplots(ncols=1, nrows=3, figsize=(10, 4))
color_image = color.gray2rgb(image)
black_image = np.zeros_like(image)
for center_y, center_x, radius in drawn_circles[:1]:
circy, circx = circle(center_y, center_x, radius, image.shape)
color = colors.pop(0)
color_image[circy, circx] = color
black_image[circy, circx] = image[circy, circx]
colors.append(color)
# Output
ax[0].imshow(image, cmap=plt.cm.gray) # original image
ax[1].imshow(color_image) # detected circle
ax[2].imshow(black_image, cmap=plt.cm.gray) # cutout
plt.show()
Output:
Again, as in my previous answer, most of the code here is doing "hierarchy" computation to find the biggest best fitting circle.

How to get x,y coordinates of a text that has been rotated by an angle in PIL python?

I'm adding a text on an image at a position (x,y) and then drawing a rectangle around it (x,y,x+text_width,y+text_height). Now I'm rotating the image by an angle of 30. How can I get the new coordinates ?
from PIL import Image
im = Image.open('img.jpg')
textlayer = Image.new("RGBA", im.size, (0,0,0,0))
textdraw = ImageDraw.Draw(textlayer)
textsize = textdraw.textsize('Hello World', font='myfont.ttf')
textdraw.text((75,267), 'Hello World', font='myfont.ttf', fill=(255,255,255))
textlayer = textlayer.rotate(30)
I tried this . But I'm not getting the point correctly. Can anyone point me what I'm doing wrong.
textpos = (75,267)
theta = 30
x0,y0 = 0,0
h = textsize[0] - textsize[1]
x,y = textpos[0], textpos[1]
xNew = (x-x0)*cos(theta) - (h-y-y0)*sin(theta) + x0
yNew = -(x-x0)*sin(theta) - (h-y-y0)*cos(theta) + (h-y0)
In PIL, the rotation happens about the center of the image. So considering your center of the image is given by:
cx = int(image_width / 2)
cy = int(image_height / 2)
a specified rotation angle:
theta = 30
and given coordinates (px, py), The new coordinates can be obtained using the following equation:
rad = radians(theta)
new_px = cx + int(float(px-cx) * cos(rad) + float(py-cy) * sin(rad))
new_py = cy + int(-(float(px-cx) * sin(rad)) + float(py-cy) * cos(rad))
Please note that the angle must be specified in radians, not degrees.
This answer is inspired from this following blog-post.

OpenCV Python rotate image by X degrees around specific point

I'm having a hard time finding examples for rotating an image around a specific point by a specific (often very small) angle in Python using OpenCV.
This is what I have so far, but it produces a very strange resulting image, but it is rotated somewhat:
def rotateImage( image, angle ):
if image != None:
dst_image = cv.CloneImage( image )
rotate_around = (0,0)
transl = cv.CreateMat(2, 3, cv.CV_32FC1 )
matrix = cv.GetRotationMatrix2D( rotate_around, angle, 1.0, transl )
cv.GetQuadrangleSubPix( image, dst_image, transl )
cv.GetRectSubPix( dst_image, image, rotate_around )
return dst_image
import numpy as np
import cv2
def rotate_image(image, angle):
image_center = tuple(np.array(image.shape[1::-1]) / 2)
rot_mat = cv2.getRotationMatrix2D(image_center, angle, 1.0)
result = cv2.warpAffine(image, rot_mat, image.shape[1::-1], flags=cv2.INTER_LINEAR)
return result
Assuming you're using the cv2 version, that code finds the center of the image you want to rotate, calculates the transformation matrix and applies to the image.
Or much easier use
SciPy
from scipy import ndimage
#rotation angle in degree
rotated = ndimage.rotate(image_to_rotate, 45)
see
here
for more usage info.
def rotate(image, angle, center = None, scale = 1.0):
(h, w) = image.shape[:2]
if center is None:
center = (w / 2, h / 2)
# Perform the rotation
M = cv2.getRotationMatrix2D(center, angle, scale)
rotated = cv2.warpAffine(image, M, (w, h))
return rotated
I had issues with some of the above solutions, with getting the correct "bounding_box" or new size of the image. Therefore here is my version
def rotation(image, angleInDegrees):
h, w = image.shape[:2]
img_c = (w / 2, h / 2)
rot = cv2.getRotationMatrix2D(img_c, angleInDegrees, 1)
rad = math.radians(angleInDegrees)
sin = math.sin(rad)
cos = math.cos(rad)
b_w = int((h * abs(sin)) + (w * abs(cos)))
b_h = int((h * abs(cos)) + (w * abs(sin)))
rot[0, 2] += ((b_w / 2) - img_c[0])
rot[1, 2] += ((b_h / 2) - img_c[1])
outImg = cv2.warpAffine(image, rot, (b_w, b_h), flags=cv2.INTER_LINEAR)
return outImg
The cv2.warpAffine function takes the shape parameter in reverse order: (col,row) which the answers above do not mention. Here is what worked for me:
import numpy as np
def rotateImage(image, angle):
row,col = image.shape
center=tuple(np.array([row,col])/2)
rot_mat = cv2.getRotationMatrix2D(center,angle,1.0)
new_image = cv2.warpAffine(image, rot_mat, (col,row))
return new_image
import imutils
vs = VideoStream(src=0).start()
...
while (1):
frame = vs.read()
...
frame = imutils.rotate(frame, 45)
More: https://github.com/jrosebr1/imutils
You can simply use the imutils package to do the rotation. it has two methods
rotate: rotate the image at specified angle. however the drawback is image might get cropped if it is not a square image.
rotate_bound: it overcomes the problem happened with rotate. It adjusts the size of the image accordingly while rotating the image.
more info you can get on this blog:
https://www.pyimagesearch.com/2017/01/02/rotate-images-correctly-with-opencv-and-python/
Quick tweak to #alex-rodrigues answer... deals with shape including the number of channels.
import cv2
import numpy as np
def rotateImage(image, angle):
center=tuple(np.array(image.shape[0:2])/2)
rot_mat = cv2.getRotationMatrix2D(center,angle,1.0)
return cv2.warpAffine(image, rot_mat, image.shape[0:2],flags=cv2.INTER_LINEAR)
You need a homogenous matrix of size 2x3. First 2x2 is the rotation matrix and last column is a translation vector.
Here's how to build your transformation matrix:
# Exemple with img center point:
# angle = np.pi/6
# specific_point = np.array(img.shape[:2][::-1])/2
def rotate(img: np.ndarray, angle: float, specific_point: np.ndarray) -> np.ndarray:
warp_mat = np.zeros((2,3))
cos, sin = np.cos(angle), np.sin(angle)
warp_mat[:2,:2] = [[cos, -sin],[sin, cos]]
warp_mat[:2,2] = specific_point - np.matmul(warp_mat[:2,:2], specific_point)
return cv2.warpAffine(img, warp_mat, img.shape[:2][::-1])
You can easily rotate the images using opencv python-
def funcRotate(degree=0):
degree = cv2.getTrackbarPos('degree','Frame')
rotation_matrix = cv2.getRotationMatrix2D((width / 2, height / 2), degree, 1)
rotated_image = cv2.warpAffine(original, rotation_matrix, (width, height))
cv2.imshow('Rotate', rotated_image)
If you are thinking of creating a trackbar, then simply create a trackbar using cv2.createTrackbar() and the call the funcRotate()fucntion from your main script. Then you can easily rotate it to any degree you want. Full details about the implementation can be found here as well- Rotate images at any degree using Trackbars in opencv
Here's an example for rotating about an arbitrary point (x,y) using only openCV
def rotate_about_point(x, y, degree, image):
rot_mtx = cv.getRotationMatrix2D((x, y), angle, 1)
abs_cos = abs(rot_mtx[0, 0])
abs_sin = abs(rot_mtx[0, 1])
rot_wdt = int(frm_hgt * abs_sin + frm_wdt * abs_cos)
rot_hgt = int(frm_hgt * abs_cos + frm_wdt * abs_sin)
rot_mtx += np.asarray([[0, 0, -lftmost_x],
[0, 0, -topmost_y]])
rot_img = cv.warpAffine(image, rot_mtx, (rot_wdt, rot_hgt),
borderMode=cv.BORDER_CONSTANT)
return rot_img
you can use the following code:
import numpy as np
from PIL import Image
import math
def shear(angle,x,y):
tangent=math.tan(angle/2)
new_x=round(x-y*tangent)
new_y=y
#shear 2
new_y=round(new_x*math.sin(angle)+new_y)
#since there is no change in new_x according to the shear matrix
#shear 3
new_x=round(new_x-new_y*tangent)
#since there is no change in new_y according to the shear matrix
return new_y,new_x
image = np.array(Image.open("test.png"))
# Load the image
angle=-int(input("Enter the angle :- "))
# Ask the user to enter the angle of rotation
# Define the most occuring variables
angle=math.radians(angle)
#converting degrees to radians
cosine=math.cos(angle)
sine=math.sin(angle)
height=image.shape[0]
#define the height of the image
width=image.shape[1]
#define the width of the image
# Define the height and width of the new image that is to be formed
new_height = round(abs(image.shape[0]*cosine)+abs(image.shape[1]*sine))+1
new_width = round(abs(image.shape[1]*cosine)+abs(image.shape[0]*sine))+1
output=np.zeros((new_height,new_width,image.shape[2]))
image_copy=output.copy()
# Find the centre of the image about which we have to rotate the image
original_centre_height = round(((image.shape[0]+1)/2)-1)
#with respect to the original image
original_centre_width = round(((image.shape[1]+1)/2)-1)
#with respect to the original image
# Find the centre of the new image that will be obtained
new_centre_height= round(((new_height+1)/2)-1)
#with respect to the new image
new_centre_width= round(((new_width+1)/2)-1)
#with respect to the new image
for i in range(height):
for j in range(width):
#co-ordinates of pixel with respect to the centre of original image
y=image.shape[0]-1-i-original_centre_height
x=image.shape[1]-1-j-original_centre_width
#Applying shear Transformation
new_y,new_x=shear(angle,x,y)
new_y=new_centre_height-new_y
new_x=new_centre_width-new_x
output[new_y,new_x,:]=image[i,j,:]
pil_img=Image.fromarray((output).astype(np.uint8))
pil_img.save("rotated_image.png")

Categories

Resources