getting equation of a line between 2 points in OpenCV - python

I have two points in the image, let's say (30, 220) and (1385, 1050). I want to find the equation that is passing through these two points. I can draw the line with OpenCV's library in python.
cv.line(frame, (30, 220), (1385, 1050), (0, 255, 0), thickness=3, lineType=8)
PS. My ultimate goal is to calculate the lowest distance between a point and that line.

Here how I find it:
from numpy import ones,vstack
from numpy.linalg import lstsq
import math
points = [(30, 220),(1385, 1050)]
x_coords, y_coords = zip(*points)
A = vstack([x_coords,ones(len(x_coords))]).T
m, c = lstsq(A, y_coords)[0]
print("Line Solution is y = {m}x + {c}".format(m=m,c=c))

https://en.wikipedia.org/wiki/Linear_equation
Find the slope m = (y_1 - y_2)/(x_1 - x_2), then solve the equation y = mx + b for b using y_1 for y, and x_1 for x (or you can use your second coordinate).

Related

Python scipy - Line fit with a constraint of all points are above the line

I have the image below and, I would like to fit a line to the white points on the image. However, I have a constraint of all points should be above the line like in the last image. I have prepared the script below when no constraints are given. Can anybody help me with how do I refactor this code with the constraint of all points should be above the fitted line?
import numpy as np
import matplotlib.pyplot as plt
import cv2
from scipy import optimize
def fit_line(img):
def func_linear(x, x0, y0, k):
# y=k(x−xo)+yo
f = lambda x:k*(x-x0)+y0
return f(x)
points = np.where(img>0)
points = np.array([points[1], points[0]]).T
x = points[:,0]
y = points[:,1]
p0 = [1, 1, 1]
p , e = optimize.curve_fit(func_linear, x, y, p0)
pt1 = (np.min(x).astype(int), func_linear(np.min(x), *p).astype(int))
pt2 = (np.max(x).astype(int), func_linear(np.max(x), *p).astype(int))
cv2.line(img, pt1, pt2, (255,0,0), 3)
img = cv2.imread("toy_2.png")
fit_line(img)
plt.imshow(img)
You can shift the line along the y axis to ensure that all the points are above the line:
p[1] += np.max(y - func_linear(x, *p))
Result:
Full example:
import numpy as np
import matplotlib.pyplot as plt
import cv2
from scipy import optimize
def fit_line(img):
def func_linear(x, x0, y0, k):
# y=k(x−xo)+yo
f = lambda x:k*(x-x0)+y0
return f(x)
points = np.where(img > 0)
points = np.array([points[1], points[0]]).T
x = points[:, 0]
y = points[:, 1]
p0 = [1, 1, 1]
p, _ = optimize.curve_fit(func_linear, x, y, p0)
# shift you line along the y axis
p[1] += np.max(y - func_linear(x, *p)) + 1e-6 # use eps to ensure strictly greater
pt1 = (np.min(x).astype(int), func_linear(np.min(x), *p).astype(int))
pt2 = (np.max(x).astype(int), func_linear(np.max(x), *p).astype(int))
cv2.line(img, pt1, pt2, (255, 0, 0), 3)
img = cv2.imread("toy_2.png")
fit_line(img)
plt.imshow(img)
You can also consider using adding a penalization term but it is more difficult to ensure that all points will be strictly above the line. Please see similar question:
How do I put a constraint on SciPy curve fit?

Transpose 3D coordinates on a plane to a new 2D coordinate system

I have been working on a personal project to produce an image of the integer solutions to the equation x^2 + y^2 + z^2 = S where 'S' is any integer.
In other words, I am looking for all the 3D points [x,y,z] where x, y, and z are all perfect square integers and x + y + z = S
For example, S = 2809 will have solutions:
[144, 1296, 1369],
[144, 729, 1936],
[0, 0, 2809]
... plus all permutations of the above (i.e. 144+729+1936 = 1936+729+144)
Before I get to my question, here is a small tangent for some context:
All of the solutions to the general equation x + y + z = S will lie on a 2D plane defined by:
A = [S, 0, 0]
B = [0, S, 0]
C = [0, 0, S]
Here is a graph of all solutions (not just square points) to x + y + z = 50 to illustrate that all of the solutions to this equation will lie on the same plane bounded by ABC defined above. Notice that the tips of the triangle below are: [50, 0, 0], [0, 50, 0], and [0, 0, 50]
Back to my question:
After finding the square solution points, I want to transpose the 3D solution points into 2D coordinates based on the ABC plane with A as (0,0), B is the max 'x' value, and C is the max 'y' value. I then hope to output these solutions to an image file.
My linear algebra knowledge is sparse, and I have been unable to find a method to transpose 3D coordinates into 2D plane coordinates based on 3 non-colinear points.
My code is currently in python, but an algorithmic/mathematical answer is just as good!
Any help is much appreciated!
As I see it, you can already find your (x, y, z) points, and your question is about a way to project them onto a plane.
Please refer to projection matrix to learn how to project the 3d world onto an image plane of your choosing.
Specifically, you will have to express your (x, y, z) coordinates as homogeneous coordinates by refering to them as (x, y, z, 1), and to multiply them by a relevant camera matrix which is orthogonal to the plane on which you need to cast them.
This will yield 2d homogeneous coordinates of the form (x', y', f) from which you will be able to obtain the projected coordinates by (x_projected, y_projected) = (x'/f, y'/f).
OpenCV is your friend.
Recap:
Input: n (x, y, z) points
Obtain projection (camera) matrix M of size (4, 3) using opencv or calculate yourself using whatever tools.
Add last dimension 1 to all points to get them as 3d homogeneous coordinates: n points (x, y, z, 1)
Multiply all points by the matrix to obtain projected points as 2d homogeneous coordinates: M * (x, y, z, 1)^T = (x', y', f)
Get n actual 2d projected coordinates (relative to camera center as defined by the M matrix) by (x, y) = (x'/f, y'/f)
Bonus: you can stack all your (x, y, z, 1) points as columns into a (4, n) matrix, P, and the entire multiplication process will be R = M * P, a result matrix R of shape (3, n) whose columns are the resulting homogeneous coordinates.
I think Gulzar's answer is correct, but more centered around rendering (i.e. camera and homogenous coordinates). I did however figure out how to do what I wanted.
import ast
import math
import matplotlib.pyplot as plt
def dot_3d(a, b):
return (a[0]*b[0])+ (a[1]*b[1]) + (a[2]*b[2])
def minus_3d(a, b):
return [a[0] - b[0], a[1] - b[1], a[2] - b[2]]
def midpoint_3d(a, b):
return [(a[0] + b[0])/2, (a[1] + b[1])/2, (a[2] + b[2])/2]
def normalize_3d(vec):
magnitude = math.sqrt(vec[0]**2 + vec[1]**2 + vec[2]**2)
return [vec[0]/magnitude, vec[1]/magnitude, vec[2]/magnitude]
X = 2809
A = [X, 0, 0]
B = [0, X, 0]
C = [0, 0, X]
S = set([])
for a in range(X+1):
if int(math.sqrt(a))**2 == a:
for b in range(X+1):
if int(math.sqrt(b))**2 == b:
for c in range(X+1):
if int(math.sqrt(c))**2 == c and a + b + c == X:
S.add(str([a, b, c]))
S = list(S)
origin = A
normal = normalize_3d([X/3, X/3, X/3])
ax1 = normalize_3d(minus_3d(B, A))
ax2 = normalize_3d(minus_3d(C, midpoint_3d(A, B)))
answers = []
for point_str in S:
point = ast.literal_eval(point_str)
x = dot_3d(ax1, minus_3d(point, origin))
y = dot_3d(ax2, minus_3d(point, origin))
answers.append([x, y])
plt.scatter([p[0] for p in answers], [p[1] for p in answers])
plt.xlabel('x')
plt.ylabel('y')
plt.show()
Starting 3D coordinates graphed:
"projected" coordinates on the ABC plane:

How to draw the best ellipse given number of points?

Given the vectors p,q and number of points n I want to approximate shape of the ellipse with these n points as best as possible. To do this I use parametric equation of ellipse and change radius and angle in a double for loop:
n = 10000
points = []
p = [300, 0]
q = [0, 200]
root = int(math.sqrt(n))
for a in range(root):
for b in range(root):
x = 400 + (a/root)*(p[0] - q[0])*math.cos(2*math.pi*b/root)
y = 300 - (a/root)*(p[1] - q[1])*math.sin(2*math.pi*b/root)
points.append([x, y])
for w in points:
pygame.draw.circle(screen, (200, 50, 75), (int(w[0]), int(w[1])), 1)
Here I'm using pygame to draw but it doesn't really matter. Given these parameters my ellipse looks like this:
with n = 100000. It looks like this:
Given the nature of the ellipse there are more points in the middle using basic parametrization of the radious and angle. Because of that I need very large n to get good picture. How can I change parameterization to make points better spread on the whole area?
Perhaps create the points uniformly and then filter out the ones outside the ellipse?
import numpy as np
from math import sqrt
import matplotlib.pyplot as plt
def filter_points(h, k, a, b, x, y):
mask = ((x-h)**2)/(a**2) + ((y-k)**2)/(b**2) <= 1
return x[mask], y[mask]
h = 0
k = 0
a = 4
b = 5
N = 10000
n = int(sqrt(N))
X, Y = np.meshgrid(
np.linspace(-a,a,n),
np.linspace(-b,b,n)
)
X, Y = filter_points(h, k, a, b, X[:], Y[:])
points = np.asarray([X, Y]).T
plt.figure
plt.plot(X,Y,'.')

Python - Return y coordinates of polygon path given x

I have an irregular polygon, the perimeter of which is defined by an array of Cartesian coordinates.
I'm looking for a method to find the minimum and maximum y coordinate given an x coordinate that would work for a continuous range from x-min to x-max.
I would imagine one way to do this would be to define equations of the straight lines between each point and then apply the two of these whose range is satisfied by a given x coordinate.
Is there a quicker method to calculate/implement? Or is there a module that would allow me to do this easily?
I have been using matplotlib.path to plot the polygon - perhaps this class can help?
Thanks
Per #tcaswell's suggestion, shapely makes this fairly easy. In particular, the approach is to compute the .intersection (i.e. overlap) between the boundary of your polygon, and a vertical line at your chosen x-location.
A key thing to note here is that shapely polygons are filled objects, such that computing the overlap of these polygons with a line will return the segment(s) of the line that are inside the polygon. Therefore, to get the points where the vertical line crosses the edge of the polygon, it's probably simplest to compute the intersection between the .boundary attribute of a polygon and the vertical line.
Implementation
Here is a function returns the intersection points of an input polygon with a vertical line at a specified x value:
import shapely.geometry as sg
def polygon_intersect_x(poly, x_val):
"""
Find the intersection points of a vertical line at
x=`x_val` with the Polygon `poly`.
"""
if x_val < poly.bounds[0] or x_val > poly.bounds[2]:
raise ValueError('`x_val` is outside the limits of the Polygon.')
if isinstance(poly, sg.Polygon):
poly = poly.boundary
vert_line = sg.LineString([[x_val, poly.bounds[1]],
[x_val, poly.bounds[3]]])
pts = [pt.xy[1][0] for pt in poly.intersection(vert_line)]
pts.sort()
return pts
Example Usage
First create an example shapely.geometry.Polygon:
p = sg.Polygon([(0, 0),
(10, 0),
(30, 10),
(70, -50),
(80, 30),
(40, 40),
(60, 80),
(50, 100),
(15, 20),
(0, 0)])
Note that you can also create shapely objects by:
a) wrapping NumPy arrays to shapely types,
b) Converting MPL paths to shapely polygons as p = sg.Polygon(my_path.vertices).
The function above can now be used to calculate min/max intersection points as:
x_val = 45
points = polygon_intersect_x(p, x_val)
minmax = [points[0], points[-1]] # The values are already sorted
print minmax
# [-12.5, 88.5714286]
Here's a simple plot that demonstrates the approach:
import matplotlib.pyplot as plt
ax = plt.gca()
ax.fill(*p.boundary.xy, color='y')
ax.axvline(x_val, color='b')
ax.plot([x_val, ] * len(points), points, 'b.')
ax.plot([x_val, ] * 2, minmax, 'ro', ms=10)
Here's a quick and dirty example of the interpolation:
def y_on_line(x, pos1, pos2):
if(x < min(pos1[0], pos2[0])) or (x > max(pos1[0], pos2[0])):
return None
else:
if(pos1[0] == pos2[0]):
return pos1[1]
else:
return pos1[1] + (pos2[1] - pos1[1]) * (x - pos1[0]) / (pos2[0] - pos1[0])
def min_max_y(x, array):
miny = None
maxy = None
for i in range(len(array)):
y = y_on_line(x, array[i], array[(i+1)%len(array)])
if(y is None):
continue
if(y < miny) or (miny is None):
miny = y
if(y > maxy) or (maxy is None):
maxy = y
return [miny, maxy]
print min_max_y(0.5, [[0,0],[0,2],[1,1]])
output: [0.5,1.5]
Note that if your polygon is concave then there's no reason that (x, miny) - (x,maxy) is all contained within it, in case that matters.

Python: Does a module exist which already find an angle and the distance between two points?

First of all, I apologize to post this easy question. Probably there is a module to compute angle and distance between two points.
A = (560023.44957588764,6362057.3904932579)
B = (560036.44957588764,6362071.8904932579)
Given
you could compute the angle, theta, and the distance between A and B with:
import math
def angle_wrt_x(A,B):
"""Return the angle between B-A and the positive x-axis.
Values go from 0 to pi in the upper half-plane, and from
0 to -pi in the lower half-plane.
"""
ax, ay = A
bx, by = B
return math.atan2(by-ay, bx-ax)
def dist(A,B):
ax, ay = A
bx, by = B
return math.hypot(bx-ax, by-ay)
A = (560023.44957588764, 6362057.3904932579)
B = (560036.44957588764, 6362071.8904932579)
theta = angle_wrt_x(A, B)
d = dist(A, B)
print(theta)
print(d)
which yields
0.839889619638 # radians
19.4743420942
(Edit: Since you are dealing with points in a plane, its easier to use atan2 than the dot-product formula).
Sure, math module has atan2. math.atan2(y, x) is an angle (0, 0) to (x, y).
Also math.hypot(x, y) is distance form (0, 0) to (x, y).

Categories

Resources