Given a unit circle and two polygons inscribed, how can I go about calculating the Intersection over Union(IoU) of the two polygons?
Assume I have a Numpy array of the polygon coordinates with respect to the unit circle as:
Polygon 1: [
(-0.708, 0.707),
(0.309, -0.951),
(0.587, -0.809),
]
Polygon 2: [
(1, 0),
(0, 1),
(-1, 0),
(0, -1),
(0.708, -0.708),
]
The expected IoU output should be: 0.124
Three Approaches
Shapely
Redo RaffleBuffle approach in Python rather than Java (check his/her post for description)
Monte Carlo Simulation
Shapely
from shapely.geometry import Polygon
p1 = [ (-0.708, 0.707),
(0.309, -0.951),
(0.587, -0.809)]
p2 = [ (1, 0),
(0, 1),
(-1, 0),
(0, -1),
(0.708, -0.708)]
# Create polygons from coordinates
poly1 = Polygon(p1)
poly2 = Polygon(p2)
# ratio of intersection area to union area
print(poly1.intersection(poly2).area/poly1.union(poly2).area)
# Output: 0.12403616470027264
RaffleBuffle Approach in Python
import math
import numpy as np
class Point:
def __init__(self, x, y, id = None):
self.x = x
self.y = y
self.id = id
self.prev = None
self.next = None
def __repr__(self):
result = f"{self.x} {self.y} {self.id}"
result += f" Prev: {self.prev.x} {self.prev.y} {self.prev.id}" if self.prev else ""
result += f" Next: {self.next.x} {self.next.y} {self.next.id}" if self.next else ""
return result
class Poly:
def __init__(self, pts):
self.pts = [p for p in pts]
' Sort polynomial coordinates based upon angle and radius in clockwize direction'
# Origin is the centroid of points
origin = Point(*[sum(pt.x for pt in self.pts)/len(self.pts), sum(pt.y for pt in self.pts)/len(self.pts)])
# Sort points by incresing angle around centroid based upon angle and distance to centroid
self.pts.sort(key=lambda p: clockwiseangle_and_distance(p, origin))
def __add__(self, other):
' Overload for adding two polynomials '
return Poly(self.pts + other.pts)
def assign_chain(self):
' Assign prev and next '
n = len(self.pts)
for i in range(n):
self.pts[i].next = self.pts[ (i + 1) % n]
self.pts[i].prev = self.pts[(i -1) % n]
return self
def area(self):
'''
area enclosed by polynomial coordinates '
Source: https://stackoverflow.com/questions/24467972/calculate-area-of-polygon-given-x-y-coordinates
'''
x = np.array([p.x for p in self.pts])
y = np.array([p.y for p in self.pts])
return 0.5*np.abs(np.dot(x,np.roll(y,1))-np.dot(y,np.roll(x,1)))
def intersection(self):
' Intersection of coordinates with different ids '
pts = self.pts
n = len(pts)
intersections = []
for i in range(n):
j = (i -1) % n
if pts[j].id != pts[i].id:
get_j = [pts[j], pts[j].next]
get_i = [pts[i].prev, pts[i]]
pt_intersect = line_intersect(get_j, get_i)
if pt_intersect:
intersections.append(pt_intersect)
return intersections
def __str__(self):
return '\n'.join(str(pt) for pt in self.pts)
def __repr__(self):
return '\n'.join(str(pt) for pt in self.pts)
def clockwiseangle_and_distance(point, origin):
# Source: https://stackoverflow.com/questions/41855695/sorting-list-of-two-dimensional-coordinates-by-clockwise-angle-using-python
# Vector between point and the origin: v = p - o
vector = [point.x-origin.x, point.y-origin.y]
refvec = [0, 1]
# Length of vector: ||v||
lenvector = math.hypot(vector[0], vector[1])
# If length is zero there is no angle
if lenvector == 0:
return -math.pi, 0
# Normalize vector: v/||v||
normalized = [vector[0]/lenvector, vector[1]/lenvector]
dotprod = normalized[0]*refvec[0] + normalized[1]*refvec[1] # x1*x2 + y1*y2
diffprod = refvec[1]*normalized[0] - refvec[0]*normalized[1] # x1*y2 - y1*x2
angle = math.atan2(diffprod, dotprod)
# Negative angles represent counter-clockwise angles so we need to subtract them
# from 2*pi (360 degrees)
if angle < 0:
return 2*math.pi+angle, lenvector
# I return first the angle because that's the primary sorting criterium
# but if two vectors have the same angle then the shorter distance should come first.
return angle, lenvector
def line_intersect(segment1, segment2):
""" returns a (x, y) tuple or None if there is no intersection
segment1 and segment2 are two line segements
specified by their starting/ending points
Source: https://rosettacode.org/wiki/Find_the_intersection_of_two_lines#Python
"""
Ax1, Ay1 = segment1[0].x, segment1[0].y # starting point in line segment 1
Ax2, Ay2 = segment1[1].x, segment1[1].y # ending point in line segment 1
Bx1, By1 = segment2[0].x, segment2[0].y # starting point in line segment 2
Bx2, By2 = segment2[1].x, segment2[1].y # ending point in line segment 2
d = (By2 - By1) * (Ax2 - Ax1) - (Bx2 - Bx1) * (Ay2 - Ay1)
if d:
uA = ((Bx2 - Bx1) * (Ay1 - By1) - (By2 - By1) * (Ax1 - Bx1)) / d
uB = ((Ax2 - Ax1) * (Ay1 - By1) - (Ay2 - Ay1) * (Ax1 - Bx1)) / d
else:
return
if not(0 <= uA <= 1 and 0 <= uB <= 1):
return
x = Ax1 + uA * (Ax2 - Ax1)
y = Ay1 + uA * (Ay2 - Ay1)
return Point(x, y, None)
def polygon_iou(coords1, coords2):
'''
Calculates IoU of two 2D polygons based upon coordinates
'''
# Make polynomials ordered clockwise and assign ID (0 and 1)
poly1 = Poly(Point(*p, 0) for p in coords1) # counter clockwise with ID 0
poly2 = Poly(Point(*p, 1) for p in coords2) # counter clockwise with ID 1
# Assign previous and next coordinates in polynomial chain
poly1.assign_chain()
poly2.assign_chain()
# Polygon areas
area1 = poly1.area()
area2 = poly2.area()
# Combine both polygons into one counter clockwise
poly = poly1 + poly2
# Get interesections
intersections = poly.intersection()
# IoU based upon intersection and sum of areas
if intersections:
area_intersection = Poly(intersections).area()
result = area_intersection/(area1 + area2 - area_intersection)
else:
result = 0.0
return result
print(polygon_iou(p1, p2))
Test
p1 = [ (-0.708, 0.707),
(0.309, -0.951),
(0.587, -0.809)]
p2 = [ (1, 0),
(0, 1),
(-1, 0),
(0, -1),
(0.708, -0.708)]
print(polygon_iou(p1, p2))
# Output: 0.12403616470027277
Monte Carlo Simulation
Generate random points in the min/max x and y range of the points
Count the number of points in either polygon (i.e. union)
Count the number of points in both polygon (i.e. intersection)
Ratio of the number of points in the intersection to the number in the union is the answer
Code
import math
from random import uniform
def ray_tracing_method(x, y, poly):
'''
Determines if point x, y is inside polygon poly
Source: "What's the fastest way of checking if a point is inside a polygon in python"
at URL: https://stackoverflow.com/questions/36399381/whats-the-fastest-way-of-checking-if-a-point-is-inside-a-polygon-in-python
'''
n = len(poly)
inside = False
p1x,p1y = poly[0]
for i in range(n+1):
p2x,p2y = poly[i % n]
if y > min(p1y,p2y):
if y <= max(p1y,p2y):
if x <= max(p1x,p2x):
if p1y != p2y:
xints = (y-p1y)*(p2x-p1x)/(p2y-p1y)+p1x
if p1x == p2x or x <= xints:
inside = not inside
p1x,p1y = p2x,p2y
return inside
def intersection_union(p1, p2, num_throws = 1000000):
'''
Computes the intersection untion for polygons p1, p2
(assuming that p1, and p2 are inside at polygon of radius 1)
'''
# Range of values
p = p1 + p2
xmin = min(x[0] for x in p)
xmax = max(x[0] for x in p)
ymin = min(x[1] for x in p)
ymax = max(x[1] for x in p)
# Init counts
in_union = 0
in_intersect = 0
throws = 0
while (throws < num_throws):
# Choose random x, y position in rectangle
x_pos = uniform (xmin, xmax)
y_pos = uniform (ymin, ymax)
# Only test points inside unit circle
# Check if points are inside p1 & p2
in_p1 = ray_tracing_method(x_pos, y_pos, p1)
in_p2 = ray_tracing_method(x_pos, y_pos, p2)
if in_p1 or in_p2:
in_union += 1 # in union
if in_p1 and in_p2:
in_intersect += 1 # in intersetion
throws += 1
return in_intersect/in_union
print(intersection_union(p1, p2))
Test
p1 = [ (-0.708, 0.707),
(0.309, -0.951),
(0.587, -0.809)]
p2 = [ (1, 0),
(0, 1),
(-1, 0),
(0, -1),
(0.708, -0.708)]
intersection_union(p1, p2)
# Out: 0.12418051627698147
Assuming polygons are non self-intersecting, i.e. the ordering of the points around the circle is monotonic, then I believe there is a relatively simple way to determine the IoU value without requiring a general shape package.
Assume the points of each polygon are ordered clockwise around the circle. We can ensure this by sorting by increasing angle w.r.t the x-axis or reversing the points if we find the signed area is negative.
Combine points from both polygons into a single list, L, keeping track of which polygon each point belongs to. We also need to be able to determine the previous and next point in the original polygon for each point.
Sort L by increasing angle w.r.t the x-axis.
If the input polygons intersect the number of transitions from one polygon to the other while iterating through L will be greater than two.
Iterate through L. If successive points are encountered belonging to different polygons then the intersection of the lines between the 1st point and its next point and the 2nd point and its previous point will belong to the intersection between the two polygons.
Add each point identified in step 4 to a new polygon, I. Points of I will be encountered in order.
The sum of the areas of each polygon will be equal to their union plus the area of the intersection, since this will be counted twice.
The value of IoU will therefore be given by the area of I divided by the sum of the areas of the two polygons minus the area of I.
The only geometry required is to calculate the area of a simple polygon using the Shoelace formula, and to determine the point of intersection between two line segments, required by step 5.
Here's some Java code (Ideone) to illustrate - you can probably make it a lot more compact in Python.
double[][] coords = {{-0.708, 0.707, 0.309, -0.951, 0.587, -0.809},
{1, 0, 0, 1, -1, 0, 0, -1, 0.708, -0.708}};
double areaSum = 0;
List<CPoint> pts = new ArrayList<>();
for(int p=0; p<coords.length; p++)
{
List<CPoint> poly = new ArrayList<>();
for(int j=0; j<coords[p].length; j+=2)
{
poly.add(new CPoint(p, coords[p][j], coords[p][j+1]));
}
double area = area(poly);
if(area < 0)
{
area = -area;
Collections.reverse(poly);
}
areaSum += area;
pts.addAll(poly);
int n = poly.size();
for(int i=0, j=n-1; i<n; j=i++)
{
poly.get(i).prev = poly.get(j);
poly.get(j).next = poly.get(i);
}
}
pts.sort((a, b) -> Double.compare(a.theta, b.theta));
List<Point2D> intersections = new ArrayList<>();
int n = pts.size();
for(int i=0, j=n-1; i<n; j=i++)
{
if(pts.get(j).id != pts.get(i).id)
{
intersections.add(intersect(pts.get(j), pts.get(j).next, pts.get(i).prev, pts.get(i)));
}
}
double areaInt = area(intersections);
double iou = areaInt/(areaSum - areaInt);
System.out.println(iou);
Output:
0.12403616470027268
And supporting code:
static class CPoint extends Point2D.Double
{
int id;
double theta;
CPoint prev, next;
public CPoint(int id, double x, double y)
{
super(x, y);
this.id = id;
theta = Math.atan2(y, x);
if(theta < 0) theta = 2*Math.PI + theta;
}
}
static double area(List<? extends Point2D> poly)
{
double area = 0;
for(int i=0, j=poly.size()-1; i<poly.size(); j=i++)
area += (poly.get(j).getX() * poly.get(i).getY()) - (poly.get(i).getX() * poly.get(j).getY());
return Math.abs(area)/2;
}
// https://rosettacode.org/wiki/Find_the_intersection_of_two_lines#Java
static Point2D intersect(Point2D p1, Point2D p2, Point2D p3, Point2D p4)
{
double a1 = p2.getY() - p1.getY();
double b1 = p1.getX() - p2.getX();
double c1 = a1 * p1.getX() + b1 * p1.getY();
double a2 = p4.getY() - p3.getY();
double b2 = p3.getX() - p4.getX();
double c2 = a2 * p3.getX() + b2 * p3.getY();
double delta = a1 * b2 - a2 * b1;
return new Point2D.Double((b2 * c1 - b1 * c2) / delta, (a1 * c2 - a2 * c1) / delta);
}
Related
I am try to find the angle of line
i know the coordinate points
Start Point : 404, 119
Mid Point : 279, 214
End Point : 154, 310
import numpy as np
def findangle(x1,y1,x2,y2,x3,y3):
ria = np.arctan2(y2 - y1, x2 - x1) - np.arctan2(y3 - y1, x3 - x1)
webangle = int(np.abs(ria * 180 / np.pi))
return webangle
result
Its return 270. But actual angle is 85-90.
Now, I want formula to calculate the angle (Either i Will rotate the image clockwise or Anticlockwise that time also return actual angle) in python code
I think you need to adjust the returned angle based on the range you wish the result to be in.
The following assumes you desire results between -180 (inclusive) and 180 (exclusive).
def adjust(a, degrees=True):
v = 180 if degrees else np.pi
return (a + v) % (2 * v) - v
def angle(p, degrees=True):
assert p.shape == (3, 2), 'p should have 3 points, in 2D'
yx = p[:, ::-1]
a0 = np.arctan2(*(yx[1] - yx[0]))
a1 = np.arctan2(*(yx[2] - yx[1]))
a = np.rad2deg(a1 - a0) if degrees else a1 - a0
return adjust(a, degrees)
And here is some code to test validity:
def rot(a, degrees=True):
a = np.deg2rad(a) if degrees else a
sa, ca = np.sin(a), np.cos(a)
return np.array([[ca, sa], [-sa, ca]])
def genp(angle, initial=0, lengths=(1, 1), degrees=True):
p0 = np.array((0, 0))
p1 = np.array((lengths[0], 0))
p2 = p1 + np.array((lengths[1], 0)) # rot(angle, degrees=degrees)
p = np.vstack((p0, p1, p2)) # rot(initial, degrees=degrees)
return p
def plot_problem(p, ax=None):
ax = plt.gca() if ax is None else ax
ax.plot(*p.T, '-o')
for s, xy in zip(list('abc'), p):
ax.annotate(s, xy, xytext=(0, 5), textcoords='offset points')
ax.set_aspect('equal')
Example:
p = genp(60, initial=30, lengths=(2, 1))
>>> angle(p)
60.0
plot_problem(p)
Now, a stress test:
n = 1000
np.random.seed(0)
for _ in range(n):
a = np.random.uniform(-180, 180)
initial = np.random.uniform(-180, 180)
lengths = np.random.uniform(1, 10, size=2)
p = genp(a, initial, lengths)
ahat = angle(p)
assert np.allclose(a, ahat)
Which passes.
It's a simple math. The numpy.arctan2() returns a value in the range [-π, π]. So a difference of two values is in the range [-2π, 2π], and the absolute value of that is in the range [0, 2π]. You want an angle x in the range [0, π], and x is equivalent to (2π - x) in your context. So you can take x if x < π, and take (2π - x) otherwise.
So, just do like this using min() for example.
def findangle(x1,y1,x2,y2,x3,y3):
...
return min(webangle, 360 - webangle)
As a side note, you don't need to use Numpy functions for a scalar input. Just use the math.atan2() and math.fabs().
I have a simple 2D ray-casting routine that gets terribly slow as soon as the number of obstacles increases.
This routine is made up of:
2 for loops (outer loop iterates over each ray/direction, then inner loop iterates over each line obstacle)
multiple if statements (check if a value is > or < than another value or if an array is empty)
Question: How can I condense all these operations into 1 single block of vectorized instructions using Numpy ?
More specifically, I am facing 2 issues:
I have managed to vectorize the inner loop (intersection between a ray and each obstacle) but I am unable to run this operation for all rays at once.
The only workaround I found to deal with the if statements is to use masked arrays. Something tells me it is not the proper way to handle these statements in this case (it seems clumsy, cumbersome and unpythonic)
Original code:
from math import radians, cos, sin
import matplotlib.pyplot as plt
import numpy as np
N = 10 # dimensions of canvas (NxN)
sides = np.array([[0, N, 0, 0], [0, N, N, N], [0, 0, 0, N], [N, N, 0, N]])
edges = np.random.rand(5, 4) * N # coordinates of 5 random segments (x1, x2, y1, y2)
edges = np.concatenate((edges, sides))
center = np.array([N/2, N/2]) # coordinates of center point
directions = np.array([(cos(radians(a)), sin(radians(a))) for a in range(0, 360, 10)]) # vectors pointing in all directions
intersections = []
# for each direction
for d in directions:
min_dist = float('inf')
# for each edge
for e in edges:
p1x, p1y = e[0], e[2]
p2x, p2y = e[1], e[3]
p3x, p3y = center
p4x, p4y = center + d
# find intersection point
den = (p1x - p2x) * (p3y - p4y) - (p1y - p2y) * (p3x - p4x)
if den:
t = ((p1x - p3x) * (p3y - p4y) - (p1y - p3y) * (p3x - p4x)) / den
u = -((p1x - p2x) * (p1y - p3y) - (p1y - p2y) * (p1x - p3x)) / den
# if any:
if t > 0 and t < 1 and u > 0:
sx = p1x + t * (p2x - p1x)
sy = p1y + t * (p2y - p1y)
isec = np.array([sx, sy])
dist = np.linalg.norm(isec-center)
# make sure to select the nearest one (from center)
if dist < min_dist:
min_dist = dist
nearest = isec
# store nearest interesection point for each ray
intersections.append(nearest)
# Render
plt.axis('off')
for x, y in zip(edges[:,:2], edges[:,2:]):
plt.plot(x, y)
for isec in np.array(intersections):
plt.plot((center[0], isec[0]), (center[1], isec[1]), '--', color="#aaaaaa", linewidth=.8)
Vectorized version (attempt):
from math import radians, cos, sin
import matplotlib.pyplot as plt
from scipy import spatial
import numpy as np
N = 10 # dimensions of canvas (NxN)
sides = np.array([[0, N, 0, 0], [0, N, N, N], [0, 0, 0, N], [N, N, 0, N]])
edges = np.random.rand(5, 4) * N # coordinates of 5 random segments (x1, x2, y1, y2)
edges = np.concatenate((edges, sides))
center = np.array([N/2, N/2]) # coordinates of center point
directions = np.array([(cos(radians(a)), sin(radians(a))) for a in range(0, 360, 10)]) # vectors pointing in all directions
intersections = []
# Render edges
plt.axis('off')
for x, y in zip(edges[:,:2], edges[:,2:]):
plt.plot(x, y)
# for each direction
for d in directions:
p1x, p1y = edges[:,0], edges[:,2]
p2x, p2y = edges[:,1], edges[:,3]
p3x, p3y = center
p4x, p4y = center + d
# denominator
den = (p1x - p2x) * (p3y - p4y) - (p1y - p2y) * (p3x - p4x)
# first 'if' statement -> if den > 0
mask = den > 0
den = den[mask]
p1x = p1x[mask]
p1y = p1y[mask]
p2x = p2x[mask]
p2y = p2y[mask]
t = ((p1x - p3x) * (p3y - p4y) - (p1y - p3y) * (p3x - p4x)) / den
u = -((p1x - p2x) * (p1y - p3y) - (p1y - p2y) * (p1x - p3x)) / den
# second 'if' statement -> if (t>0) & (t<1) & (u>0)
mask2 = (t > 0) & (t < 1) & (u > 0)
t = t[mask2]
p1x = p1x[mask2]
p1y = p1y[mask2]
p2x = p2x[mask2]
p2y = p2y[mask2]
# x, y coordinates of all intersection points in the current direction
sx = p1x + t * (p2x - p1x)
sy = p1y + t * (p2y - p1y)
pts = np.c_[sx, sy]
# if any:
if pts.size > 0:
# find nearest intersection point
tree = spatial.KDTree(pts)
nearest = pts[tree.query(center)[1]]
# Render
plt.plot((center[0], nearest[0]), (center[1], nearest[1]), '--', color="#aaaaaa", linewidth=.8)
Reformulation of the problem – Finding the intersection between a line segment and a line ray
Let q and q2 be the endpoints of a segment (obstacle). For convenience let's define a class to represent points and vectors in the plane. In addition to the usual operations, a vector multiplication is defined by u × v = u.x * v.y - u.y * v.x.
Caution: here Coord(2, 1) * 3 returns Coord(6, 3) while Coord(2, 1) * Coord(-1, 4) outputs 9. To avoid this confusion it might have been possible to restrict * to the scalar multiplication and use ^ via __xor__ for the vector multiplication.
class Coord:
def __init__(self, x, y):
self.x = x
self.y = y
#property
def radius(self):
return np.sqrt(self.x ** 2 + self.y ** 2)
def _cross_product(self, other):
assert isinstance(other, Coord)
return self.x * other.y - self.y * other.x
def __mul__(self, other):
if isinstance(other, Coord):
# 2D "cross"-product
return self._cross_product(other)
elif isinstance(other, int) or isinstance(other, float):
# scalar multiplication
return Coord(self.x * other, self.y * other)
def __rmul__(self, other):
return self * other
def __sub__(self, other):
return Coord(self.x - other.x, self.y - other.y)
def __add__(self, other):
return Coord(self.x + other.x, self.y + other.y)
def __repr__(self):
return f"Coord({self.x}, {self.y})"
Now, I find it easier to handle a ray in polar coordinates: For a given angle theta (direction) the goal is to determine if it intersects the segment, and if so determine the corresponding radius. Here is a function to find that. See here for an explanation of why and how. I tried to use the same variable names as in the previous link.
def find_intersect_btw_ray_and_sgmt(q, q2, theta):
"""
Args:
q (Coord): first endpoint of the segment
q2 (Coord): second endpoint of the segment
theta (float): angle of the ray
Returns:
(float): np.inf if the ray does not intersect the segment,
the distance from the origin of the intersection otherwise
"""
assert isinstance(q, Coord) and isinstance(q2, Coord)
s = q2 - q
r = Coord(np.cos(theta), np.sin(theta))
cross = r * s # 2d cross-product
t_num = q * s
u_num = q * r
## the intersection point is roughly at a distance t_num / cross
## from the origin. But some cases must be checked beforehand.
## (1) the segment [PQ2] is aligned with the ray
if np.isclose(cross, 0) and np.isclose(u_num, 0):
return min(q.radius, q2.radius)
## (2) the segment [PQ2] is parallel with the ray
elif np.isclose(cross, 0):
return np.inf
t, u = t_num / cross, u_num / cross
## There is actually an intersection point
if t >= 0 and 0 <= u <= 1:
return t
## (3) No intersection point
return np.inf
For instance find_intersect_btw_ray_and_sgmt(Coord(1, 2), Coord(-1, 2), np.pi / 2) should returns 2.
Note that here for simplicity, I only considered the case where the origin of the rays is at Coord(0, 0). This can be easily extended to the general case by setting t_num = (q - origin) * s and u_num = (q - origin) * r.
Let's vectorize it!
What is very interesting here is that the operations defined in the Coord class also apply to cases where x and y are numpy arrays! Hence applying any defined operation on Coord(np.array([1, 2, 0]), np.array([2, -1, 3])) amounts applying it elementwise to the points (1, 2), (2, -1) and (0, 3). The operations of Coord are therefore already vectorized. The constructor can be modified into:
def __init__(self, x, y):
x, y = np.array(x), np.array(y)
assert x.shape == y.shape
self.x, self.y = x, y
self.shape = x.shape
Now, we would like the function find_intersect_btw_ray_and_sgmt to be able to handle the case where the parameters q and q2contains sequences of endpoints. Before the sanity checks, all the operations are working properly since, as we have mentioned, they are already vectorized. As you mentionned the conditional statements can be "vectorized" using masks. Here is what I propose:
def find_intersect_btw_ray_and_sgmts(q, q2, theta):
assert isinstance(q, Coord) and isinstance(q2, Coord)
assert q.shape == q2.shape
EPS = 1e-14
s = q2 - q
r = Coord(np.cos(theta), np.sin(theta))
cross = r * s
cross_sign = np.sign(cross)
cross = cross * cross_sign
t_num = (q * s) * cross_sign
u_num = (q * r) * cross_sign
radii = np.zeros_like(t_num)
mask = ~np.isclose(cross, 0) & (t_num >= -EPS) & (-EPS <= u_num) & (u_num <= cross + EPS)
radii[~mask] = np.inf # no intersection
radii[mask] = t_num[mask] / cross[mask] # intersection
return radii
Note that cross, t_num and u_num are multiplied by the sign of cross to ensure that the division by cross keeps the sign of the dividends. Hence conditions of the form ((t_num >= 0) & (cross >= 0)) | ((t_num <= 0) & (cross <= 0)) can be replaced by (t_num >= 0).
For simplicity, we omitted the case (1) where the radius and the segment were aligned ((cross == 0) & (u_num == 0)). This could be incorporated by carefully adding a second mask.
For a given value of theta, we are able to determine if the corresponing ray intersects with several segments at once.
## Some useful functions
def polar_to_cartesian(r, theta):
return Coord(r * np.cos(theta), r * np.sin(theta))
def plot_segments(p, q, *args, **kwargs):
plt.plot([p.x, q.x], [p.y, q.y], *args, **kwargs)
def plot_rays(radii, thetas, *args, **kwargs):
endpoints = polar_to_cartesian(radii, thetas)
n = endpoints.shape
origin = Coord(np.zeros(n), np.zeros(n))
plot_segments(origin, endpoints, *args, **kwargs)
## Data generation
M = 5 # size of the canvas
N = 10 # number of segments
K = 16 # number of rays
q = Coord(*np.random.uniform(-M/2, M/2, size=(2, N)))
p = q + Coord(*np.random.uniform(-M/2, M/2, size=(2, N)))
thetas = np.linspace(0, 2 * np.pi, K, endpoint=False)
## For each ray, find the minimal distance of intersection
## with all segments
plt.figure(figsize=(5, 5))
plot_segments(p, q, "royalblue", marker=".")
for theta in thetas:
radii = find_intersect_btw_ray_and_sgmts(p, q, theta)
radius = np.min(radii)
if not np.isinf(radius):
plot_rays(radius, theta, color="orange")
else:
plot_rays(2*M, theta, ':', c='orange')
plt.plot(0, 0, 'kx')
plt.xlim(-M, M)
plt.ylim(-M, M)
And that's not all! Thanks to the broadcasting of python, it is possible to avoid iteration on theta values. For example, recall that np.array([1, 2, 3]) * np.array([[1], [2], [3], [4]]) produces a matrix of size 4 × 3 of the pairwise products. In the same way Coord([[5],[7]], [[5],[1]]) * Coord([2, 4, 6], [-2, 4, 0]) outputs a 2 × 3 matrix containing all the pairwise cross product between vectors (5, 5), (7, 1) and (2, -2), (4, 4), (6, 0).
Finally, the intersections can be determined in the following way:
radii_all = find_intersect_btw_ray_and_sgmts(p, q, np.vstack(thetas))
# p and q have a shape of (N,) and np.vstack(thetas) of (K, 1)
# this radii_all have a shape of (K, N)
# radii_all[k, n] contains the distance from the origin of the intersection
# between k-th ray and n-th segment (or np.inf if there is no intersection point)
radii = np.min(radii_all, axis=1)
# radii[k] contains the distance from the origin of the closest intersection
# between k-th ray and all segments
do_intersect = ~np.isinf(radii)
plot_rays(radii[do_intersect], thetas[do_intersect], color="orange")
plot_rays(2*M, thetas[~do_intersect], ":", color="orange")
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)
You are confronted with an enemy within a rectangular shaped room and you've got only a laser beam weapon, the room has no obstructions in it and the walls can completely reflect the laser beam. However the laser can only travels a certain distance before it become useless and if it hit a corner it would reflect back in the same direction it came from.
That's how the puzzle goes and you are given the coordinates of your location and the target's location, the room dimensions and the maximum distance the beam can travel. for example If the room is 3 by 2 and your location is (1, 1) and the target is (2, 1) then the possible solutions are:
I tried the following approach, start from the source (1, 1) and create a vector at angle 0 radians, trace the vector path and reflections until either it hits the target or the total length of the vectors exceeds the max allowed length, repeat with 0.001 radians interval until it completes a full cycle. This the code I have so far:
from math import *
UPRIGHT = 0
DOWNRIGHT = 1
DOWNLEFT = 2
UPLEFT = 3
UP = 4
RIGHT = 5
LEFT = 6
DOWN = 7
def roundDistance (a):
b = round (a * 100000)
return b / 100000.0
# only used for presenting and doesn't affect percision
def double (a):
b = round (a * 100)
if b / 100.0 == b: return int (b)
return b / 100.0
def roundAngle (a):
b = round (a * 1000)
return b / 1000.0
def isValid (point):
x,y = point
if x < 0 or x > width or y < 0 or y > height: return False
return True
def isCorner (point):
if point in corners: return True
return False
# Find the angle direction in relation to the origin (observer) point
def getDirection (a):
angle = roundAngle (a)
if angle == 0: return RIGHT
if angle > 0 and angle < pi / 2: return UPRIGHT
if angle == pi / 2: return UP
if angle > pi / 2 and angle < pi: return UPLEFT
if angle == pi: return LEFT
if angle > pi and angle < 3 * pi / 2: return DOWNLEFT
if angle == 3 * pi / 2: return DOWN
return DOWNRIGHT
# Measure reflected vector angle
def getReflectionAngle (tail, head):
v1 = (head[0] - tail[0], head[1] - tail[1])
vx,vy = v1
n = (0, 0)
# Determin the normal vector from the tail's position on the borders
if head[0] == 0: n = (1, 0)
if head[0] == width: n = (-1, 0)
if head[1] == 0: n = (0, 1)
if head[1] == height: n = (0, -1)
nx,ny = n
# Calculate the reflection vector using the formula:
# r = v - 2(v.n)n
r = (vx * (1 - 2 * nx * nx), vy * (1 - 2 * ny * ny))
# calculating the angle of the reflection vector using it's a and b values
# if b (adjacent) is zero that means the angle is either pi/2 or -pi/2
if r[0] == 0:
return pi / 2 if r[1] >= 0 else 3 * pi / 2
return (atan2 (r[1], r[0]) + (2 * pi)) % (2 * pi)
# Find the intersection point between the vector and borders
def getIntersection (tail, angle):
if angle < 0:
print "Negative angle: %f" % angle
direction = getDirection (angle)
if direction in [UP, RIGHT, LEFT, DOWN]: return None
borderX, borderY = corners[direction]
x0,y0 = tail
opp = borderY - tail[1]
adj = borderX - tail[0]
p1 = (x0 + opp / tan (angle), borderY)
p2 = (borderX, y0 + adj * tan (angle))
if isValid (p1) and isValid (p2):
print "Both intersections are valid: ", p1, p2
if isValid (p1) and p1 != tail: return p1
if isValid (p2) and p2 != tail: return p2
return None
# Check if the vector pass through the target point
def isHit (tail, head):
d = calcDistance (tail, head)
d1 = calcDistance (target, head)
d2 = calcDistance (target, tail)
return roundDistance (d) == roundDistance (d1 + d2)
# Measure distance between two points
def calcDistance (p1, p2):
x1,y1 = p1
x2,y2 = p2
return ((y2 - y1)**2 + (x2 - x1)**2)**0.5
# Trace the vector path and reflections and check if it can hit the target
def rayTrace (point, angle):
path = []
length = 0
tail = point
path.append ([tail, round (degrees (angle))])
while length < maxLength:
head = getIntersection (tail, angle)
if head is None:
#print "Direct reflection at angle (%d)" % angle
return None
length += calcDistance (tail, head)
if isHit (tail, head) and length <= maxLength:
path.append ([target])
return [path, double (length)]
if isCorner (head):
#print "Corner reflection at (%d, %d)" % (head[0], head[1])
return None
p = (double (head[0]), double (head[1]))
path.append ([p, double (degrees (angle))])
angle = getReflectionAngle (tail, head)
tail = head
return None
def solve (w, h, po, pt, m):
# Initialize global variables
global width, height, origin, target, maxLength, corners, borders
width = w
height = h
origin = po
target = pt
maxLength = m
corners = [(w, h), (w, 0), (0, 0), (0, h)]
angle = 0
solutions = []
# Loop in anti-clockwise direction for one cycle
while angle < 2 * pi:
angle += 0.001
path = rayTrace (origin, angle)
if path is not None:
# extract only the points coordinates
route = [x[0] for x in path[0]]
if route not in solutions:
solutions.append (route)
print path
# Anser is 7
solve (3, 2, (1, 1), (2, 1), 4)
# Answer is 9
#solve (300, 275, (150, 150), (185, 100), 500)
The code works somehow but it doesn't find all the possible solutions, I have a big precision problem in it, I dont' know how many decimals should I consider when comparing distances or angles. I'm not sure it's the right way to do it but that's the best I was able to do.
How can I fix my code to extract all solutions? I need it to be efficient because the room can get quite large (500 x 500). Is there a better way or maybe some sort of algorithm to do this?
what if you started by mirroring the target at all the walls; then mirror the mirror images at all the walls and so on until the distance gets too big for the laser to reach the target? any laser shot in any direction of a target mirrored that way will hit said target. (this is my comment from above; repeated here to make answer more self-contained...)
this is the mirroring part of the answer: get_mirrored will return the four mirror images of point with the mirror-box limited by BOTTOM_LEFT and TOP_RIGHT.
BOTTOM_LEFT = (0, 0)
TOP_RIGHT = (3, 2)
SOURCE = (1, 1)
TARGET = (2, 1)
def get_mirrored(point):
ret = []
# mirror at top wall
ret.append((point[0], point[1] - 2*(point[1] - TOP_RIGHT[1])))
# mirror at bottom wall
ret.append((point[0], point[1] - 2*(point[1] - BOTTOM_LEFT[1])))
# mirror at left wall
ret.append((point[0] - 2*(point[0] - BOTTOM_LEFT[0]), point[1]))
# mirror at right wall
ret.append((point[0] - 2*(point[0] - TOP_RIGHT[0]), point[1]))
return ret
print(get_mirrored(TARGET))
this will return the 4 mirror images of the given point:
[(2, 3), (2, -1), (-2, 1), (4, 1)]
which is the target mirrored one time.
then you could iterate that until all the mirrored targets are out of range. all the mirror images within range will give you a direction in which to point your laser.
this is a way how you could iteratively get to the mirrored targets within a given DISTANCE
def get_targets(start_point, distance):
all_targets = set((start_point, )) # will also be the return value
last_targets = all_targets # need to memorize the new points
while True:
new_level_targets = set() # if this is empty: break the loop
for tgt in last_targets: # loop over what the last iteration found
new_targets = get_mirrored(tgt)
# only keep the ones within range
new_targets = set(
t for t in new_targets
if math.hypot(SOURCE[0]-t[0], SOURCE[1]-t[1]) <= DISTANCE)
# subtract the ones we already have
new_targets -= all_targets
new_level_targets |= new_targets
if not new_level_targets:
break
# add the new targets
all_targets |= new_level_targets
last_targets = new_level_targets # need these for the next iteration
return all_targets
DISTANCE = 5
all_targets = get_targets(start_point=TARGET, distance=DISTANCE)
print(all_targets)
all_targets is now the set that contains all the reachable points.
(none of that has been thouroughly tested...)
small update for your counter example:
def ray_length(point_list):
d = sum((math.hypot(start[0]-end[0], start[1]-end[1])
for start, end in zip(point_list, point_list[1:])))
return d
d = ray_length(point_list=((1,1),(2.5,2),(3,1.67),(2,1)))
print(d) # -> 3.605560890844135
d = ray_length(point_list=((1,1),(4,3)))
print(d) # -> 3.605551275463989
I have this programme to discuss and I think its a challenging one.. Here I have a yml file which contains the data for an image. The image has x,y,z values and intensity data which is stored in this yml file. I have used opencv to load the data and its working fine with masking.. but I am having problems in dynamically appending the masks created.. Here is the code I made for solving the problem :
import cv
from math import floor, sqrt, ceil
from numpy import array, dot, subtract, add, linalg as lin
mask_size = 9
mask_size2 = mask_size / 2
f = open("Classified_Image1.txt", "w")
def distance(centre, point):
''' To find out the distance between centre and the point '''
dist = sqrt(
((centre[0]-point[0])**2) +
((centre[1]-point[1])**2) +
((centre[2]-point[2])**2)
)
return dist
def CalcCentre(points): # Calculates centre for a given set of points
centre = array([0,0,0])
count = 0
for p in points:
centre = add(centre, array(p[:3]))
count += 1
centre = dot(1./count, centre)
print centre
return centre
def addrow(data, points, x, y, ix , iy ):# adds row to the mask
iy = y + 1
for dx in xrange(-mask_size2 , mask_size2 + 2):
ix = x + dx
rowpoints = addpoints(data, points, iy, ix)
return rowpoints
def addcolumn(data, points, x, y, ix , iy ):# adds column to the mask
ix = x + 1
for dy in xrange(-mask_size2-1 , mask_size2 + 1):
iy = y + dy
columnpoints = addpoints(data, points, iy, ix)
return columnpoints
def addpoints (data, points, iy, ix): # adds a list of relevant points
if 0 < ix < data.width and 0 < iy < data.height:
pnt = data[iy, ix]
if pnt != (0.0, 0.0, 0.0):
print ix, iy
print pnt
points.append(pnt)
return points
def CreateMask(data, y, x):
radius = 0.3
points = []
for dy in xrange(-mask_size2, mask_size2 + 1): ''' Masking the data '''
for dx in xrange(-mask_size2, mask_size2 + 1):
ix, iy = x + dx, y + dy
points = addpoints(data, points, iy , ix )
if len(points) > 3:
centre = CalcCentre(points)
distances = []
for point in points :
dist = distance(centre, point)
distances.append(dist)
distancemax = max(distances)
print distancemax
if distancemax < radius: ''' Dynamic Part of the Programme'''
#while dist < radius: # Going into infinite loop .. why ?
p = addrow(data, points, x, y, ix , iy )
q = addcolumn(data, points, x, y, ix , iy )
dist = distance(centre, point) # While should not go in infinite
#loop as dist is changing here
print dist
print len(p), p
print len(q), q
points = p + q
points = list(set(points)) # To remove duplicate points in the list
print len(points), points
def ComputeClasses(data):
for y in range(0, data.height):
for x in range(0, data.width):
CreateMask(data, y, x)
if __name__ == "__main__":
data = cv.Load("Z:/data/xyz_00000_300.yml")
print "data loaded"
ComputeClasses(data)
Feel free to suggest alternative methods/ideas to solve this problem.
Thanks in advance.