Polygon Class: Finding area and length of Rectangle and Triangle - python

I have been given the following code.
class Polygon:
'''Class to represent polygon objects.'''
def __init__(self, points):
'''Initialize a Polygon object with a list of points.'''
self.points = points
def length(self):
'''Return the length of the perimeter of the polygon.'''
P = self.points
return sum(sqrt((x1 - x0) ** 2 + (y1 - y0) ** 2)
for (x0, y0), (x1, y1) in zip(P, P[1:] + P[:1]))
def area(self):
'''Return the area of the polygon.'''
P = self.points
A = 0
for (x0, y0), (x1, y1) in zip(P, P[1:] + P[:1]):
A += x0 * y1 - y0 * x1
return abs(A / 2)
I have to implement __init__ methods (and no other method) of two subclasses; Rectangle and Triangle such that a rectangle can be created by:
rectangle = Rectangle(width, height)
and a triangle by:
triangle = Triangle(a, b, c)
I have coded the Rectangle one with the following:
class Rectangle(Polygon):
def __init__(self, width, height):
self.width = width
self.height = height
self.points = [(0,0), (0, height), (width, height), (width, 0)]
And the above code passes all the tests when input is only for Rectangle.
However, I have trouble doing the same for Triangle. The input should be a, b and c where those are the side lengths of the triangle. I cannot figure out which points to use to generate the length and area of the Triangle:
class Triangle(Polygon):
def __init__(self, a, b, c):
self.a = a
self.b = b
self.c = c
self.points = ??
I have tried all combinations of points using the side lengths, however, none are passing the test.

Have a look at:
https://www.omnicalculator.com/math/triangle-height#how-to-find-the-height-of-a-triangle-formulas
h = 0.5 * ((a + b + c) * (-a + b + c) * (a - b + c) * (a + b - c))**0.5 / b
ac = (c**2 - h**2)**0.5
self.points = [
(0, 0),
(a, 0),
(ac, h),
]
By getting h and then applying Pythagoras' Theorem you are obtain the co-ordinates of the "third" point. The first two are trivial: the origin, and another point along one of the axes.
A minor point: instead of setting points directly it might be cleaner to call super().__init__(points).

Related

Find the intersection and union of two rectangles

I need to find the area of ​​intersection and union of two rectangles, as well as the ratio of the intersection area to the union area. What is the best way to do this?
Picture
def intersection_over_union(a, b):
ax1 = a['left']
ay1 = a['top']
aw = a['width']
ah = a['height']
ax2 = ax1 + aw
ay2 = ay1 + ah
bx1 = b['left']
by1 = b['top']
bw = b['width']
bh = b['height']
bx2 = bx1 + bw
by2 = by1 + bh
delta_x = max(ax1, bx1) - min(ax2, bx2)
delta_y = max(ay1, by1) - min(ay2, by2)
overlap = delta_x * delta_y
union = aw * ah + bw * bh - overlap
print(overlap, union)
return overlap / union
Assuming you are dealing with AABB (axis aligned bounding box) this little class defines everything you need:
class Rect:
def __init__(self, x, y, w, h):
self.x = x
self.y = y
self.w = w
self.h = h
def bottom(self):
return self.y + self.h
def right(self):
return self.x + self.w
def area(self):
return self.w * self.h
def union(self, b):
posX = min(self.x, b.x)
posY = min(self.y, b.y)
return Rect(posX, posY, max(self.right(), b.right()) - posX, max(self.bottom(), b.bottom()) - posY)
def intersection(self, b):
posX = max(self.x, b.x)
posY = max(self.y, b.y)
candidate = Rect(posX, posY, min(self.right(), b.right()) - posX, min(self.bottom(), b.bottom()) - posY)
if candidate.w > 0 and candidate.h > 0:
return candidate
return Rect(0, 0, 0, 0)
def ratio(self, b):
return self.intersection(b).area() / self.union(b).area()
if __name__ == "__main__":
assert Rect(1, 1, 1, 1).ratio(Rect(1, 1, 1, 1)) == 1
assert round(Rect(1, 1, 2, 2).ratio(Rect(2, 2, 2, 2)), 4) == 0.1111
One way I believe you could do this (although I am not sure it is the best solution as you've asked for), is to develop a function that creates a rectangle in a plane, and stores all of its corners' coordinates in a list.
Every time you create a rectangle (let's say you create it from the bottom left, increasing its x and y coordinates as you go), check for each coordinate if the x coordinate is within the other rectangle's x-range i.e. x_min < x < x_max. If so, check the y-coordinate. If the coordinate is within the x and y ranges of another rectangle, it's part of the intersection.
Repeat for all coordinates as you create them and save the intersection coordinates, and find the intersection's corners. With that, you can calculate the area of intersection and union between the two triangles, and their ratio to one another.

Tkinter: Label lines of triangle (create_polygon)

I have this python function:
def draw(a, b, c):
A = (0, 0)
B = (c, 0)
hc = (2 * (a**2*b**2 + b**2*c**2 + c**2*a**2) - (a**4 + b**4 + c**4))**0.5 / (2.*c)
dx = (b**2 - hc**2)**0.5
if abs((c - dx)**2 + hc**2 - a**2) > 0.01: dx = -dx
C = (dx, hc)
coords = [float((x + 1) * 75) for x in A+B+C]
canvas.delete("all")
canvas.create_polygon(*coords, outline='black', fill='white')
I can enter a, b and c and a triangle is drawed by Tkinter.
So it looks like this:
The problem
Now I want to label the lines a, b and c. Like this:
And the label should always be in the middle of the lines.
How can I do this?
I looked at a lot of posts, but none is for dynamically generated triangles.
This is a more a math problem than a Tkinter question. You have the vertices coordinates, so you can compute the coordinates of middle of each edge. However, this would put the text on the edge instead of a bit aside. To offset a bit the text, you can do a barycenter between the middle of the edge and the opposite vertex, giving the x coordinates of the 'c' label:
xc = (xA + xB)/2 * (1 - w) + xC
If you take a slightly negative weight w, then the text will be slightly outside the edge, however, the offset from the edge is relative to the distance between the middle of the edge and the vertex. To avoid this, we need to divide the wanted absolute offset by this distance:
def text_coords(x1, y1, x2, y2, offset):
d = sqrt((x1-x2)**2 + (y1-y2)**2)
w = - offset / d
xt = (1 - w) * x1 + w * x2
yt = (1 - w) * y1 + w * y2
return xt, yt
(x1, y1) are the coordinates of the middle of the edge and (x2, y2) the coordinates of the opposite vertex. Then we use this function in draw() to add the edge labels:
def draw(a, b, c):
A = (0, 0)
B = (c, 0)
hc = (2 * (a**2*b**2 + b**2*c**2 + c**2*a**2) - (a**4 + b**4 + c**4))**0.5 / (2.*c)
dx = (b**2 - hc**2)**0.5
if abs((c - dx)**2 + hc**2 - a**2) > 0.01: dx = -dx
C = (dx, hc)
coords = [float((x + 1) * 75) for x in A+B+C]
canvas.delete("all")
canvas.create_polygon(*coords, outline='black', fill='white')
xA, yA = coords[:2]
xB, yB = coords[2:4]
xC, yC = coords[4:]
xAB, yAB = (xA + xB)/2, (yA + yB)/2
xAC, yAC = (xA + xC)/2, (yA + yC)/2
xCB, yCB = (xC + xB)/2, (yC + yB)/2
dc = distance(xAB, yAB, xC, yC)
db = distance(xAC, yAC, xB, yB)
da = distance(xCB, yCB, xA, yA)
xc, yc = text_coords(xAB, yAB, xC, yC, 10)
xb, yb = text_coords(xAC, yAC, xB, yB, 10)
xa, ya = text_coords(xCB, yCB, xA, yA, 10)
canvas.create_text(xc, yc, text='c')
canvas.create_text(xa, ya, text='a')
canvas.create_text(xb, yb, text='b')
For instance, draw(3, 4, 5) gives:

A point class and a rectangle class without using inheritance

I am trying to create a point class that has the data attribute of floating point values 'x' and 'y' to therefore define a location of a point in 2D space. Additionally I want to have methods of init such as it being an initialization of default values x = 0 and y = 0. And then a move function which accepts the 'x' and 'y' as a new location of the point. And finally a function which tells the distance to a point. I want this to return the Euclidian distance from this point to another point at x, y. How would this be done?
Here is the code I have so far for the above description:
import math
class Point:
def __init__(self):
self.x = 0 # initialize to 0
self.y = 0 # initialize to 0
def move(self, x, y):
self.x = x
self.y = y
Could use help on this and the Eucliean distance from this point to another point at x,y. Not sure if I have the right idea so far. New to python so don't know how to test the functionality of this code. Would appreciate the help!
After that part I'd to be able to define two points at two opposite corners of a rectangle and use the Points defined above without using inheritance. Any ideas on how to create this class?
You could do something like this (comments in the code):
example:
class Point:
def __init__(self, x: float=0.0, y: float=0.0)-> None: # assign default values
self.x = x
self.y = y
def move_by(self, dx: float, dy: float)-> None: # move Point by dx, dy
self.x += dx
self.y += dy
def move_to(self, new_x: float, new_y: float)-> None: # relocate Point to new x, y position
self.x = new_x
self.y = new_y
def distance(self, other: 'Point')-> float: # calculates and returns the Euclidian distance between self and other
if isinstance(other, Point):
x0, y0 = self.x, self.y
x1, y1 = other.x, other.y
return ((x1 - x0)**2 + (y1 - y0)**2) ** 0.5
return NotImplemented
def __str__(self)-> str: # add a nice string representation
return f'Point({self.x}, {self.y})'
tests:
p = Point(1, 2)
q = Point()
print(p, q, p.distance(q) == 5**0.5)
p.move_by(.1, -.1)
print(p)
output:
Point(1, 2) Point(0.0, 0.0) True
Point(1.1, 1.9)
And the Rectangle class could be like this: [edited to add default Point values]
In Rectangle.__init__, the min and max values of the x and y values of the Points provided as parameters are sorted in order to determine the top left and bottom right points defining the rectangle
class Rectangle:
def __init__(self, p0: Point=Point(), p1: Point=Point(1.0, 1.0))-> None: # <--- [edit]: now with default values
x0, y0 = p0.x, p0.y
x1, y1 = p1.x, p1.y
self.topleft = Point(min(x0, x1), max(y0, y1)) # calculate the topleft and bottomright
self.bottomright = Point(max(x0, x1), min(y0, y1)) # of the bounding box
def __str__(self)-> str:
return f'Rectangle defined by bbox at: {self.topleft}, {self.bottomright})'
tests:
p = Point(1, 2)
q = Point()
print(p, q, p.distance(q) == 5**0.5)
p.move_by(.1, -.1)
print(p)
r = Rectangle(p, q)
print(r)
output:
Point(1, 2) Point(0.0, 0.0) True
Point(1.1, 1.9)
Rectangle defined by bbox at: Point(0.0, 1.9), Point(1.1, 0.0))
[Edit:] with default values for Points in Rectangle:
s = Rectangle()
print(s)
output:
Rectangle defined by bbox at: Point(0.0, 1.0), Point(1.0, 0.0))

algorithm to create a circle in a 2D array EDIT: diamond would be okay aswel

I have a 2D in python that represents a tile map, each element in the array is either a 1 or 0, 0 representing land and 1 representing water. I need an algorithm that takes 2 random coordinates to be the center of the circle, a variable for the radius (max 5) and replace the necessary elements in the array to form a full circle.
x = random.randint(0,MAPWIDTH)
y = random.randint(0,MAPHEIGHT)
rad = random.randint(0,5)
tileMap[x][y] = 1 #this creates the center of the circle
how would I do this?
As previously said, you can use the definition of a circle, like so:
import math
def dist(x1, y1, x2, y2):
return math.sqrt((x1 - x2) ** 2 + (y1 - y2) ** 2)
def make_circle(tiles, cx, cy, r):
for x in range(cx - r, cx + r):
for y in range(cy - r, cy + r):
if dist(cx, cy, x, y) <= r:
tiles[x][y] = 1
width = 50
height = 50
cx = width // 2
cy = height // 2
r = 23
tiles = [[0 for _ in range(height)] for _ in range(width)]
make_circle(tiles, cx, cy, r)
print("\n".join("".join(map(str, i)) for i in tiles))
This outputs
00000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000
00000000000000000000000001000000000000000000000000
00000000000000000001111111111111000000000000000000
00000000000000001111111111111111111000000000000000
00000000000000111111111111111111111110000000000000
00000000000001111111111111111111111111000000000000
00000000000111111111111111111111111111110000000000
00000000001111111111111111111111111111111000000000
00000000011111111111111111111111111111111100000000
00000000111111111111111111111111111111111110000000
00000001111111111111111111111111111111111111000000
00000001111111111111111111111111111111111111000000
00000011111111111111111111111111111111111111100000
00000111111111111111111111111111111111111111110000
00000111111111111111111111111111111111111111110000
00001111111111111111111111111111111111111111111000
00001111111111111111111111111111111111111111111000
00001111111111111111111111111111111111111111111000
00011111111111111111111111111111111111111111111100
00011111111111111111111111111111111111111111111100
00011111111111111111111111111111111111111111111100
00011111111111111111111111111111111111111111111100
00011111111111111111111111111111111111111111111100
00011111111111111111111111111111111111111111111100
00111111111111111111111111111111111111111111111100
00011111111111111111111111111111111111111111111100
00011111111111111111111111111111111111111111111100
00011111111111111111111111111111111111111111111100
00011111111111111111111111111111111111111111111100
00011111111111111111111111111111111111111111111100
00011111111111111111111111111111111111111111111100
00001111111111111111111111111111111111111111111000
00001111111111111111111111111111111111111111111000
00001111111111111111111111111111111111111111111000
00000111111111111111111111111111111111111111110000
00000111111111111111111111111111111111111111110000
00000011111111111111111111111111111111111111100000
00000001111111111111111111111111111111111111000000
00000001111111111111111111111111111111111111000000
00000000111111111111111111111111111111111110000000
00000000011111111111111111111111111111111100000000
00000000001111111111111111111111111111111000000000
00000000000111111111111111111111111111110000000000
00000000000001111111111111111111111111000000000000
00000000000000111111111111111111111110000000000000
00000000000000001111111111111111111000000000000000
00000000000000000001111111111111000000000000000000
00000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000
Note that I deliberately used a rather large array and radius - this results in being able to actually see the circle a bit better. For some radius around 5, it would probably be pixelated beyond belief.
You would have to set a coordinate to one if
((x – h)(x - h)) + ((y – k)(y - k)) = r * r is true.
h is the centre x coordinate and k is the centre y coordinate.
Inspired by Izaak van Dongen, just re-worked a bit:
from pylab import imshow, show, get_cmap
from numpy import random
import math
def dist(x1, y1, x2, y2):
return math.sqrt((x1 - x2) ** 2 + (y1 - y2) ** 2)
def make_circle(tiles, cx, cy, r):
for x in range(cx - r, cx + r):
for y in range(cy - r, cy + r):
if dist(cx, cy, x, y) < r:
tiles[x][y] = 1
return tiles
def generate_image_mask(iw,ih,cx,cy,cr):
mask = [[0 for _ in range(ih)] for _ in range(iw)]
mask = make_circle(mask, cx, cy, cr)
#print("\n".join("".join(map(str, i)) for i in mask))
imshow(mask, cmap=get_cmap("Spectral"), interpolation='nearest')
show()
if __name__ == '__main__':
image_w = 60
image_h = 60
circle_x = image_w/2
circle_y = image_h/2
circle_r = 15
generate_image_mask(image_w,image_h,circle_x,circle_y,circle_r)

Calculate the area of intersection of two rotated rectangles in python

I have two 2D rotated rectangles, defined as an (center x,center y, height, width) and an angle of rotation (0-360°). How would I calculate the area of intersection of these two rotated rectangles.
Such tasks are solved using computational geometry packages, e.g. Shapely:
import shapely.geometry
import shapely.affinity
class RotatedRect:
def __init__(self, cx, cy, w, h, angle):
self.cx = cx
self.cy = cy
self.w = w
self.h = h
self.angle = angle
def get_contour(self):
w = self.w
h = self.h
c = shapely.geometry.box(-w/2.0, -h/2.0, w/2.0, h/2.0)
rc = shapely.affinity.rotate(c, self.angle)
return shapely.affinity.translate(rc, self.cx, self.cy)
def intersection(self, other):
return self.get_contour().intersection(other.get_contour())
r1 = RotatedRect(10, 15, 15, 10, 30)
r2 = RotatedRect(15, 15, 20, 10, 0)
from matplotlib import pyplot
from descartes import PolygonPatch
fig = pyplot.figure(1, figsize=(10, 4))
ax = fig.add_subplot(121)
ax.set_xlim(0, 30)
ax.set_ylim(0, 30)
ax.add_patch(PolygonPatch(r1.get_contour(), fc='#990000', alpha=0.7))
ax.add_patch(PolygonPatch(r2.get_contour(), fc='#000099', alpha=0.7))
ax.add_patch(PolygonPatch(r1.intersection(r2), fc='#009900', alpha=1))
pyplot.show()
Here is a solution that does not use any libraries outside of Python's standard library.
Determining the area of the intersection of two rectangles can be divided in two subproblems:
Finding the intersection polygon, if any;
Determine the area of the intersection polygon.
Both problems are relatively easy when you work with the
vertices (corners) of the rectangles. So first you have to determine
these vertices. Assuming the coordinate origin is in the center
of the rectangle, the vertices are,
starting from the lower left in a counter-clockwise direction:
(-w/2, -h/2), (w/2, -h/2), (w/2, h/2), and (-w/2, h/2).
Rotating this over the angle a, and translating them
to the proper position of the rectangle's center, these become:
(cx + (-w/2)cos(a) - (-h/2)sin(a), cy + (-w/2)sin(a) + (-h/2)cos(a)), and similar for the other corner points.
A simple way to determine the intersection polygon is the following:
you start with one rectangle as the candidate intersection polygon.
Then you apply the process of sequential cutting (as described here.
In short: you take each edges of the second rectangle in turn,
and remove all parts from the candidate intersection polygon that are on the "outer" half plane defined by the edge
(extended in both directions).
Doing this for all edges leaves the candidate intersection polygon
with only the parts that are inside the second rectangle or on its boundary.
The area of the resulting polygon (defined by a series of vertices) can be calculated
from the coordinates of the vertices.
You sum the cross products of the vertices
of each edge (again in counter-clockwise order),
and divide that by two. See e.g. www.mathopenref.com/coordpolygonarea.html
Enough theory and explanation. Here is the code:
from math import pi, cos, sin
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, v):
if not isinstance(v, Vector):
return NotImplemented
return Vector(self.x + v.x, self.y + v.y)
def __sub__(self, v):
if not isinstance(v, Vector):
return NotImplemented
return Vector(self.x - v.x, self.y - v.y)
def cross(self, v):
if not isinstance(v, Vector):
return NotImplemented
return self.x*v.y - self.y*v.x
class Line:
# ax + by + c = 0
def __init__(self, v1, v2):
self.a = v2.y - v1.y
self.b = v1.x - v2.x
self.c = v2.cross(v1)
def __call__(self, p):
return self.a*p.x + self.b*p.y + self.c
def intersection(self, other):
# See e.g. https://en.wikipedia.org/wiki/Line%E2%80%93line_intersection#Using_homogeneous_coordinates
if not isinstance(other, Line):
return NotImplemented
w = self.a*other.b - self.b*other.a
return Vector(
(self.b*other.c - self.c*other.b)/w,
(self.c*other.a - self.a*other.c)/w
)
def rectangle_vertices(cx, cy, w, h, r):
angle = pi*r/180
dx = w/2
dy = h/2
dxcos = dx*cos(angle)
dxsin = dx*sin(angle)
dycos = dy*cos(angle)
dysin = dy*sin(angle)
return (
Vector(cx, cy) + Vector(-dxcos - -dysin, -dxsin + -dycos),
Vector(cx, cy) + Vector( dxcos - -dysin, dxsin + -dycos),
Vector(cx, cy) + Vector( dxcos - dysin, dxsin + dycos),
Vector(cx, cy) + Vector(-dxcos - dysin, -dxsin + dycos)
)
def intersection_area(r1, r2):
# r1 and r2 are in (center, width, height, rotation) representation
# First convert these into a sequence of vertices
rect1 = rectangle_vertices(*r1)
rect2 = rectangle_vertices(*r2)
# Use the vertices of the first rectangle as
# starting vertices of the intersection polygon.
intersection = rect1
# Loop over the edges of the second rectangle
for p, q in zip(rect2, rect2[1:] + rect2[:1]):
if len(intersection) <= 2:
break # No intersection
line = Line(p, q)
# Any point p with line(p) <= 0 is on the "inside" (or on the boundary),
# any point p with line(p) > 0 is on the "outside".
# Loop over the edges of the intersection polygon,
# and determine which part is inside and which is outside.
new_intersection = []
line_values = [line(t) for t in intersection]
for s, t, s_value, t_value in zip(
intersection, intersection[1:] + intersection[:1],
line_values, line_values[1:] + line_values[:1]):
if s_value <= 0:
new_intersection.append(s)
if s_value * t_value < 0:
# Points are on opposite sides.
# Add the intersection of the lines to new_intersection.
intersection_point = line.intersection(Line(s, t))
new_intersection.append(intersection_point)
intersection = new_intersection
# Calculate area
if len(intersection) <= 2:
return 0
return 0.5 * sum(p.x*q.y - p.y*q.x for p, q in
zip(intersection, intersection[1:] + intersection[:1]))
if __name__ == '__main__':
r1 = (10, 15, 15, 10, 30)
r2 = (15, 15, 20, 10, 0)
print(intersection_area(r1, r2))
intersection, pnt = contourIntersection(rect1, rect2)
After looking at the possible duplicate page for this problem I couldn't find a completed answer for python so here is my solution using masking. This function will work with complex shapes on any angle, not just rectangles
You pass in the 2 contours of your rotated rectangles as parameters and it returns 'None' if no intersection occurs or an image of the intersected area and the left/top position of that image in relation to the original image the contours were taken from
Uses python, cv2 and numpy
import cv2
import math
import numpy as np
def contourIntersection(con1, con2, showContours=False):
# skip if no bounding rect intersection
leftmost1 = tuple(con1[con1[:, :, 0].argmin()][0])
topmost1 = tuple(con1[con1[:, :, 1].argmin()][0])
leftmost2 = tuple(con2[con2[:, :, 0].argmin()][0])
topmost2 = tuple(con2[con2[:, :, 1].argmin()][0])
rightmost1 = tuple(con1[con1[:, :, 0].argmax()][0])
bottommost1 = tuple(con1[con1[:, :, 1].argmax()][0])
rightmost2 = tuple(con2[con2[:, :, 0].argmax()][0])
bottommost2 = tuple(con2[con2[:, :, 1].argmax()][0])
if rightmost1[0] < leftmost2[0] or rightmost2[0] < leftmost1[0] or bottommost1[1] < topmost2[1] or bottommost2[1] < topmost1[1]:
return None, None
# reset top / left to 0
left = leftmost1[0] if leftmost1[0] < leftmost2[0] else leftmost2[0]
top = topmost1[1] if topmost1[1] < topmost2[1] else topmost2[1]
newCon1 = []
for pnt in con1:
newLeft = pnt[0][0] - left
newTop = pnt[0][1] - top
newCon1.append([newLeft, newTop])
# next
con1_new = np.array([newCon1], dtype=np.int32)
newCon2 = []
for pnt in con2:
newLeft = pnt[0][0] - left
newTop = pnt[0][1] - top
newCon2.append([newLeft, newTop])
# next
con2_new = np.array([newCon2], dtype=np.int32)
# width / height
right1 = rightmost1[0] - left
bottom1 = bottommost1[1] - top
right2 = rightmost2[0] - left
bottom2 = bottommost2[1] - top
width = right1 if right1 > right2 else right2
height = bottom1 if bottom1 > bottom2 else bottom2
# create images
img1 = np.zeros([height, width], np.uint8)
cv2.drawContours(img1, con1_new, -1, (255, 255, 255), -1)
img2 = np.zeros([height, width], np.uint8)
cv2.drawContours(img2, con2_new, -1, (255, 255, 255), -1)
# mask images together using AND
imgIntersection = cv2.bitwise_and(img1, img2)
if showContours:
img1[img1 > 254] = 128
img2[img2 > 254] = 100
imgAll = cv2.bitwise_or(img1, img2)
cv2.imshow('Merged Images', imgAll)
# end if
if not imgIntersection.sum():
return None, None
# trim
while not imgIntersection[0].sum():
imgIntersection = np.delete(imgIntersection, (0), axis=0)
top += 1
while not imgIntersection[-1].sum():
imgIntersection = np.delete(imgIntersection, (-1), axis=0)
while not imgIntersection[:, 0].sum():
imgIntersection = np.delete(imgIntersection, (0), axis=1)
left += 1
while not imgIntersection[:, -1].sum():
imgIntersection = np.delete(imgIntersection, (-1), axis=1)
return imgIntersection, (left, top)
# end function
To complete the answer so you can use the above function with the values of CenterX, CenterY, Width, Height and Angle of 2 rotated rectangles I have added the below functions. Simple change the Rect1 and Rect2 properties at the bottom of the code to your own
def pixelsBetweenPoints(xy1, xy2):
X = abs(xy1[0] - xy2[0])
Y = abs(xy1[1] - xy2[1])
return int(math.sqrt((X ** 2) + (Y ** 2)))
# end function
def rotatePoint(angle, centerPoint, dist):
xRatio = math.cos(math.radians(angle))
yRatio = math.sin(math.radians(angle))
xPotted = int(centerPoint[0] + (dist * xRatio))
yPlotted = int(centerPoint[1] + (dist * yRatio))
newPoint = [xPotted, yPlotted]
return newPoint
# end function
def angleBetweenPoints(pnt1, pnt2):
A_B = pixelsBetweenPoints(pnt1, pnt2)
pnt3 = (pnt1[0] + A_B, pnt1[1])
C = pixelsBetweenPoints(pnt2, pnt3)
angle = math.degrees(math.acos((A_B * A_B + A_B * A_B - C * C) / (2.0 * A_B * A_B)))
# reverse if above horizon
if pnt2[1] < pnt1[1]:
angle = angle * -1
# end if
return angle
# end function
def rotateRectContour(xCenter, yCenter, height, width, angle):
# calc positions
top = int(yCenter - (height / 2))
left = int(xCenter - (width / 2))
right = left + width
rightTop = (right, top)
centerPoint = (xCenter, yCenter)
# new right / top point
rectAngle = angleBetweenPoints(centerPoint, rightTop)
angleRightTop = angle + rectAngle
angleRightBottom = angle + 180 - rectAngle
angleLeftBottom = angle + 180 + rectAngle
angleLeftTop = angle - rectAngle
distance = pixelsBetweenPoints(centerPoint, rightTop)
rightTop_new = rotatePoint(angleRightTop, centerPoint, distance)
rightBottom_new = rotatePoint(angleRightBottom, centerPoint, distance)
leftBottom_new = rotatePoint(angleLeftBottom, centerPoint, distance)
leftTop_new = rotatePoint(angleLeftTop, centerPoint, distance)
contourList = [[leftTop_new], [rightTop_new], [rightBottom_new], [leftBottom_new]]
contour = np.array(contourList, dtype=np.int32)
return contour
# end function
# rect1
xCenter_1 = 40
yCenter_1 = 20
height_1 = 200
width_1 = 80
angle_1 = 45
rect1 = rotateRectContour(xCenter_1, yCenter_1, height_1, width_1, angle_1)
# rect2
xCenter_2 = 80
yCenter_2 = 25
height_2 = 180
width_2 = 50
angle_2 = 123
rect2 = rotateRectContour(xCenter_2, yCenter_2, height_2, width_2, angle_2)
intersection, pnt = contourIntersection(rect1, rect2, True)
if intersection is None:
print('No intersection')
else:
print('Area of intersection = ' + str(int(intersection.sum() / 255)))
cv2.imshow('Intersection', intersection)
# end if
cv2.waitKey(0)

Categories