how to rotate Rectangle shape cv2 python - python

I have simple rectangle I just want to rotate this rectangle in any input angle
my code is:
import cv2
import numpy as np
imgc = np.zeros((500, 500, 3), np.uint8)
p0 = (100, 100)
p1 = (100 , 150)
p2 = (150, 150)
p3 = (150, 100)
pp = np.array([p0, p1, p2, p3])
cv2.drawContours(imgc, [pp], 0, (155, 155, 155), -1, cv2.LINE_AA)
cv2.imshow("image",imgc)
cv2.waitKey()

What you need is Rotation Matrices. But you need to remember this rotating a point with a given angle (in radians) around the ORIGIN.
You need to move your points to the origin rotate them and move them back same amount.
Here what is would look line when you break down all dot production steps into one equation:
def rotate(points, angle):
ANGLE = np.deg2rad(angle)
c_x, c_y = np.mean(points, axis=0)
return np.array(
[
[
c_x + np.cos(ANGLE) * (px - c_x) - np.sin(ANGLE) * (py - c_x),
c_y + np.sin(ANGLE) * (px - c_y) + np.cos(ANGLE) * (py - c_y)
]
for px, py in points
]
).astype(int)
Please notice: drawContours expects points as integers not floats so you need to convert your arrays to int.
Here the blue rectangle was the original one and the magenta rectangle is the 45 degrees rotated one:

Related

How to find hotspot points when an image is rotated by different angle?

I have 36 images of the same car, each one rotated by ~10 degrees. Now, I need to show the new position of any hotspots marked on the image when they are rotated by 10 degrees. I tried using the below formula but it's not giving the right coordinates.
function rotate(x, y, xm, ym, a) {
var cos = Math.cos,
sin = Math.sin,
a = a * Math.PI / 180,
xr = (x - xm) * cos(a) - (y - ym) * sin(a) + xm,
yr = (x - xm) * sin(a) + (y - ym) * cos(a) + ym;
return [xr, yr];
}
rotate(526, 327, 640, 379, 10);
The original image size is: 1280x758.
I visualized the images using opencv, below is the result:
img = cv2.imread('img.jpg')
x,y = (526,327)
image = cv2.circle(img, (x,y), radius=5, color=(0, 0, 255), thickness=-1)
cv2_imshow(image)
Image 1 Hotspot(Red circle) at: 526,327
Image 2 Hotspot after rotating by 10 deg at: 536,307
Original Images are here:
https://i.imgur.com/gO4SLZU.jpg
https://i.imgur.com/dJbCmcW.jpg
Can anyone tell me what I am doing wrong here ?

Rotate (alignment) an image by line along the y-axis

I am trying to rotate (alignment) an image, which contain line (with two points P1 and P2) along the y-axis
Original image:
Note: the green area represents the original image
Result should be:
Note: the red area represents the original image after rotation
So I need to calculate the angle between line defined by P1(x1,y1) and P2(x2,y2) and by y-axis,
Note: the green line represents the y-axis
My code is:
import cv2
import numpy as np
from math import *
import math
import imutils
height = 500
width = 500
original_image = np.zeros((height,width,3), np.uint8)
original_image[:] = (0,255,0)
x1 = 400
y1 = 50
P1 = (x1, y1)
x2 = 100
y2 = 300
P2 = (x2, y2)
cv2.line(original_image, P1, P2, (0, 0, 0), 3)
deltaY = y1 - y2
deltaX = x1 - x2
angleInDegrees = atan2(deltaY, deltaX) * 180 / math.pi
print(angleInDegrees)
rotated_image = imutils.rotate_bound(original_image, angleInDegrees)
cv2.imshow("Original", original_image)
cv2.imshow("Rotated", rotated_image)
cv2.waitKey(0)
But my rotated_image is not properly aligned
Result looks like:
How should I fix it?
First off you are calculating the wrong angle. The angle you are calcuating is between a vector originating at the origin and ending at P1 & a vector orignating at the origin and ending at P2.
The angle you need is between the vector starting at P1 and ending at P2 [P2-P1] & a vector that represents the direction of the y-axis which would be [0, 1].
Secondly you have to take into account that your origin is in the top left corner so you need to reflect the angle once calculated.
import cv2
import numpy as np
from math import *
import math
import imutils
height = 500
width = 500
original_image = np.zeros((height,width,3), np.uint8)
original_image[:] = (0,255,0)
x1 = 400
y1 = 50
P1 = np.array([x1, y1])
x2 = 100
y2 = 300
P2 = np.array([x2, y2])
# checks orientation of p vector & selects appropriate y_axis_vector
if (P2[1] - P1[1]) < 0:
y_axis_vector = np.array([0, -1])
else:
y_axis_vector = np.array([0, 1])
if (P2[0] - P1[0]) < 0 and (P2[1] - P1[1]) :
y_axis_vector = np.array([0, 1])
p_unit_vector = (P2 - P1) / np.linalg.norm(P2-P1)
angle_p_y = np.arccos(np.dot(p_unit_vector, y_axis_vector)) * 180 /math.pi
cv2.line(original_image, tuple(P1), tuple(P2), (0, 0, 0), 3)
print(angle_p_y)
print (P2-P1)
rotated_image = imutils.rotate_bound(original_image, -angle_p_y)
cv2.imshow("Original", original_image)
cv2.imshow("Rotated", rotated_image)
cv2.waitKey(0)

Creating an arc between two points pygame

I am trying to create a smooth curve between two straight lines with known gradients, I have found an equation to find theses two point but when i try to find the angle for each of the points the arc doesn't line up.I have done the math and it (should) be right.
def angle(A,B):
o = abs(B[1]-A[1])
a = abs(B[0] - A[0])
angle = math.atan(o/a)
return angle
a1 = angle((250, 175),(75, 300))
a2 = angle((250, 175),(400, 315))
pygame.draw.arc(gameDisplay, WHITE, (0 ,0 ,500,350), (math.pi) + a2,(2*math.pi) - a1)
pygame.draw.line(gameDisplay, BLUE, [400,0], [400, 500], 1)
pygame.draw.line(gameDisplay, BLUE, [0,315], [1000, 315], 1)
pygame.draw.line(gameDisplay, RED, [75,0], [75, 500], 1)
pygame.draw.line(gameDisplay, RED, [0,300], [1000, 300], 1)
This is the basic code for finding the start and stop angle, with (250,175) being the center of the ellipse and (75, 300) and (400, 315) being the two points. The lines are where the arc should start and stop.
You do not draw a perfect circular arc, actually you are drawing an elliptical arc. You've to take into account the rectangular area, which is passed to pygame.draw.arc() ((0, 0, 500, 350)), when the start and end angle for the arc is calculated.
As mention in the comments you've to use math.atan2 rather than math.atan to get angles in the range [-pi, pi]. In pygame, the y axis points downwards, so the y component of the direction vector has to be inverted
def angle(A, B, aspectRatio):
x = B[0] - A[0]
y = B[1] - A[1]
angle = math.atan2(-y, x / aspectRatio)
return angle
a1 = angle((250, 175), (75, 300), 500/350)
a2 = angle((250, 175),(400, 315), 500/350)
pygame.draw.arc(gameDisplay, WHITE, (0, 0, 500, 350), a1, a2)

How to blur non-rectangular or circular area of image with Python PIL?

Using PIL in Python, I am superimposing a PNG image on top of another, larger image. The smaller image is semi-transparent.
I would like for the area behind the smaller image to be blurred on the larger image. The following code blurs a rectangular area:
box = (3270, 1150, 4030, 2250) # (x1, y1, x2, y2)
ic = outputImage.crop(box)
ic = ic.filter(ImageFilter.BoxBlur(20))
outputImage.paste(ic, box)
However, I need to blur a rectangular area that has rounded corners.
This is what the superimposed image looks like:
So, is it possible to define a custom shape for a cropped area in PIL?
If not, is it possible to at least crop circle-shaped areas? (For full coverage and without any overhang, my area would have to broken down into 6 sub-areas: 4 circles and 2 rectangles. Doing all this will slow down my code, but I will take whatever solution I can get.)
I understand that this can be done with Numpy, but I would prefer to use PIL because everything else in this script is already coded with PIL.
Take a look at this example (rounded_rectangle function from here):
from PIL import Image
from PIL import ImageDraw
from PIL import ImageFilter
def rounded_rectangle(draw, xy, rad, fill=None):
x0, y0, x1, y1 = xy
draw.rectangle([ (x0, y0 + rad), (x1, y1 - rad) ], fill=fill)
draw.rectangle([ (x0 + rad, y0), (x1 - rad, y1) ], fill=fill)
draw.pieslice([ (x0, y0), (x0 + rad * 2, y0 + rad * 2) ], 180, 270, fill=fill)
draw.pieslice([ (x1 - rad * 2, y1 - rad * 2), (x1, y1) ], 0, 90, fill=fill)
draw.pieslice([ (x0, y1 - rad * 2), (x0 + rad * 2, y1) ], 90, 180, fill=fill)
draw.pieslice([ (x1 - rad * 2, y0), (x1, y0 + rad * 2) ], 270, 360, fill=fill)
# Open an image
im = Image.open(INPUT_IMAGE_FILENAME)
# Create rounded rectangle mask
mask = Image.new('L', im.size, 0)
draw = ImageDraw.Draw(mask)
rounded_rectangle(draw, (im.size[0]//4, im.size[1]//4, im.size[0]//4*3, im.size[1]//4*3), rad=40, fill=255)
mask.save('mask.png')
# Blur image
blurred = im.filter(ImageFilter.GaussianBlur(20))
# Paste blurred region and save result
im.paste(blurred, mask=mask)
im.save(OUTPUT_IMAGE_FILENAME)
Input image:
Mask:
Output image:
Tested with Python 2.7.12 and Pillow 3.1.2 (it doesn't have BoxBlur).

Is it possible in OpenCV to plot local curvature as a heat-map representing an object's "pointiness"?

Given a thresholded image of blobs that you can detect and draw contours around, is it possible when drawing the contour to represent the local curvature as a heat-map?
i.e. is it (1) possible to determine local curvature on a open cv contour (2) map this curvature to a heat-map color space (3) draw the contour as a heatmap.
My goal is to measure the "pointiness" of an object so that I can draw a vector from the pointy side to the opposite non-pointy side. For my objects, I happen to know that the pointy side is the top.
If other techniques would be more effective at representing "pointiness" than curvature feel free to suggest.
EDIT: Fixed a bug in the previous version.
I used angle between the gradient vectors at the ith and (i + n)th point on the contour as the score to determine the pointiness of a point. Code and results below.
import numpy as np
import cv2
import pylab as pl
def compute_pointness(I, n=5):
# Compute gradients
# GX = cv2.Sobel(I, cv2.CV_32F, 1, 0, ksize=5, scale=1)
# GY = cv2.Sobel(I, cv2.CV_32F, 0, 1, ksize=5, scale=1)
GX = cv2.Scharr(I, cv2.CV_32F, 1, 0, scale=1)
GY = cv2.Scharr(I, cv2.CV_32F, 0, 1, scale=1)
GX = GX + 0.0001 # Avoid div by zero
# Threshold and invert image for finding contours
_, I = cv2.threshold(I, 100, 255, cv2.THRESH_BINARY_INV)
# Pass in copy of image because findContours apparently modifies input.
C, H = cv2.findContours(I.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
heatmap = np.zeros_like(I, dtype=np.float)
pointed_points = []
for contour in C:
contour = contour.squeeze()
measure = []
N = len(contour)
for i in xrange(N):
x1, y1 = contour[i]
x2, y2 = contour[(i + n) % N]
# Angle between gradient vectors (gx1, gy1) and (gx2, gy2)
gx1 = GX[y1, x1]
gy1 = GY[y1, x1]
gx2 = GX[y2, x2]
gy2 = GY[y2, x2]
cos_angle = gx1 * gx2 + gy1 * gy2
cos_angle /= (np.linalg.norm((gx1, gy1)) * np.linalg.norm((gx2, gy2)))
angle = np.arccos(cos_angle)
if cos_angle < 0:
angle = np.pi - angle
x1, y1 = contour[((2*i + n) // 2) % N] # Get the middle point between i and (i + n)
heatmap[y1, x1] = angle # Use angle between gradient vectors as score
measure.append((angle, x1, y1, gx1, gy1))
_, x1, y1, gx1, gy1 = max(measure) # Most pointed point for each contour
# Possible to filter for those blobs with measure > val in heatmap instead.
pointed_points.append((x1, y1, gx1, gy1))
heatmap = cv2.GaussianBlur(heatmap, (3, 3), heatmap.max())
return heatmap, pointed_points
def plot_points(image, pointed_points, radius=5, color=(255, 0, 0)):
for (x1, y1, _, _) in pointed_points:
cv2.circle(image, (x1, y1), radius, color, -1)
def main():
I = cv2.imread("glLqt.jpg", 0)
heatmap, pointed_points = compute_pointness(I, n=5)
pl.figure()
pl.imshow(heatmap, cmap=pl.cm.jet)
pl.colorbar()
I_color = cv2.cvtColor(I, cv2.COLOR_GRAY2RGB)
plot_points(I_color, pointed_points)
pl.figure()
pl.imshow(I_color)
if __name__ == '__main__':
main()
Notice that sharper points are brighter in the heatmap.
The point is that " if you approximate the contour to continues lines you can see that the pointiness is the point where maximum angle deviation for consecutive line occurs", based on this you can develop your algorithm.
You need to do
Find contour.
Find approxPolyDP() for the contour.
Calculate angle for each consecutive line and store the point where the maximum deviation occur.
You can calculate the angle of a line using the equation
double Angle = atan2(P2.y - P1.y, P2.x - P1.x) * 180.0 / CV_PI;

Categories