OpenCV 2d line intersection helper function - opencv

I was looking for a helper function to calculate the intersection of two lines in OpenCV. I have searched the API Documentation, but couldn't find a useful resource.
Are there basic geometric helper functions for intersection/distance calculations on lines/line segments in OpenCV?

There are no function in OpenCV API to calculate lines intersection, but distance is:
cv::Point2f start, end;
double length = cv::norm(end - start);
If you need a piece of code to calculate line intersections then here it is:
// Finds the intersection of two lines, or returns false.
// The lines are defined by (o1, p1) and (o2, p2).
bool intersection(Point2f o1, Point2f p1, Point2f o2, Point2f p2,
Point2f &r)
{
Point2f x = o2 - o1;
Point2f d1 = p1 - o1;
Point2f d2 = p2 - o2;
float cross = d1.x*d2.y - d1.y*d2.x;
if (abs(cross) < /*EPS*/1e-8)
return false;
double t1 = (x.x * d2.y - x.y * d2.x)/cross;
r = o1 + d1 * t1;
return true;
}

There's one cool trick in 2D geometry which I find to be very useful to calculate lines intersection. In order to use this trick we represent each 2D point and each 2D line in homogeneous 3D coordinates.
At first let's talk about 2D points:
Each 2D point (x, y) corresponds to a 3D line that passes through points (0, 0, 0) and (x, y, 1).
So (x, y, 1) and (α•x, α•y, α) and (β•x, β•y, β) correspond to the same point (x, y) in 2D space.
Here's formula to convert 2D point into homogeneous coordinates: (x, y) -> (x, y, 1)
Here's formula to convert homogeneous coordinates into 2D point: (x, y, ω) -> (x / ω, y / ω). If ω is zero that means "point at infinity". It doesn't correspond to any point in 2D space.
In OpenCV you may use convertPointsToHomogeneous() and convertPointsFromHomogeneous()
Now let's talk about 2D lines:
Each 2D line can be represented with three coordinates (a, b, c) which corresponds to 2D line equation: a•x + b•y + c = 0
So (a, b, c) and (ω•a, ω•b, ω•c) correspond to the same 2D line.
Also, (a, b, c) corresponds to (nx, ny, d) where (nx, ny) is unit length normal vector and d is distance from the line to (0, 0)
Also, (nx, ny, d) is (cos φ, sin φ, ρ) where (φ, ρ) are polar coordinates of the line.
There're two interesting formulas that link together points and lines:
Cross product of two distinct points in homogeneous coordinates gives homogeneous line coordinates: (α•x₁, α•y₁, α) ✕ (β•x₂, β•y₂, β) = (a, b, c)
Cross product of two distinct lines in homogeneous coordinates gives homogeneous coordinate of their intersection point: (a₁, b₁, c₁) ✕ (a₂, b₂, c₂) = (x, y, ω). If ω is zero that means lines are parallel (have no single intersection point in Euclidean geometry).
In OpenCV you may use either Mat::cross() or numpy.cross() to get cross product
If you're still here, you've got all you need to find lines given two points and intersection point given two lines.

An algorithm for finding line intersection is described very well in the post How do you detect where two line segments intersect?
The following is my openCV c++ implementation. It uses the same notation as in above post
bool getIntersectionPoint(Point a1, Point a2, Point b1, Point b2, Point & intPnt){
Point p = a1;
Point q = b1;
Point r(a2-a1);
Point s(b2-b1);
if(cross(r,s) == 0) {return false;}
double t = cross(q-p,s)/cross(r,s);
intPnt = p + t*r;
return true;
}
double cross(Point v1,Point v2){
return v1.x*v2.y - v1.y*v2.x;
}

Here is my implementation for EmguCV (C#).
static PointF GetIntersection(LineSegment2D line1, LineSegment2D line2)
{
double a1 = (line1.P1.Y - line1.P2.Y) / (double)(line1.P1.X - line1.P2.X);
double b1 = line1.P1.Y - a1 * line1.P1.X;
double a2 = (line2.P1.Y - line2.P2.Y) / (double)(line2.P1.X - line2.P2.X);
double b2 = line2.P1.Y - a2 * line2.P1.X;
if (Math.Abs(a1 - a2) < double.Epsilon)
throw new InvalidOperationException();
double x = (b2 - b1) / (a1 - a2);
double y = a1 * x + b1;
return new PointF((float)x, (float)y);
}

Using homogeneous coordinates makes your life easier:
cv::Mat intersectionPoint(const cv::Mat& line1, const cv::Mat& line2)
{
// Assume we receive lines as l=(a,b,c)^T
assert(line1.rows == 3 && line1.cols = 1
&& line2.rows == 3 && line2.cols == 1);
// Point is p=(x,y,w)^T
cv::Mat point = line1.cross(line2);
// Normalize so it is p'=(x',y',1)^T
if( point.at<double>(2,0) != 0)
point = point * (1.0/point.at<double>(2,0));
}
Note that if the third coordinate is 0 the lines are parallel and there is not solution in R² but in P^2, and then the point means a direction in 2D.

my implementation in Python (using numpy array)
with line1 = [[x1, y1],[x2, y2]] & line2 = [[x1, y1],[x2, y2]]
def getIntersection(line1, line2):
s1 = numpy.array(line1[0])
e1 = numpy.array(line1[1])
s2 = numpy.array(line2[0])
e2 = numpy.array(line2[1])
a1 = (s1[1] - e1[1]) / (s1[0] - e1[0])
b1 = s1[1] - (a1 * s1[0])
a2 = (s2[1] - e2[1]) / (s2[0] - e2[0])
b2 = s2[1] - (a2 * s2[0])
if abs(a1 - a2) < sys.float_info.epsilon:
return False
x = (b2 - b1) / (a1 - a2)
y = a1 * x + b1
return (x, y)

Related

F# arrays to 2D histogram

I’m using OxyPlot with F#. I have code to create a single parameter histogram and plot it. My code for dual parameter histograms in the form of a contour is too time consuming. I’d like an efficient way to map two vectors or arrays into a 2D histogram. I’m including my code for regular histogram.
let myHistogram c =
flatten dataArray.[c..c,*]
|> Seq.toArray
|> Array.map (fun x -> round(float(x)/16.0))
|> Seq.countBy (fun x -> x)
|> Seq.sort
|> Seq.map snd
So, I’m looking to take dataArray.[a…a,], dataArray[b…b,] and place them into bins of a specific resolution to create histogram[x,y]. OxyPlot needs the histogram in order to create a contour.
Imagine two arrays of data with one being called Alexa647-H and the other BV786-H. Each array contains 100,000 integers ranging between 0 and 10,000. You could plot these arrays as a dot plot in OxyPlot. That is straight forward, simply plot one array for the X-Axis and one array for the Y-Axis. I've included a plot below.
My question involves creating a contour plot out of the same data. For that, I need to first determine a resolution, say for convenience 100x100. Therefore I want to end up with a 2D array call hist2(100,100). The array is basically 10,000 bins of 1000x1000 in size. Each bin contains the count of elements which fall into a particular range -- a 2D histogram.
Dot and Contour
The coding example in OxyPlot generates a peak array mathematically. I want to generate that contour input peak array as outline above, instead.
var model = new PlotModel { Title = "ContourSeries" };
double x0 = -3.1;
double x1 = 3.1;
double y0 = -3;
double y1 = 3;
//generate values
Func<double, double, double> peaks = (x, y) => 3 * (1 - x) * (1 - x) * Math.Exp(-(x * x) - (y + 1) * (y + 1)) - 10 * (x / 5 - x * x * x - y * y * y * y * y) * Math.Exp(-x * x - y * y) - 1.0 / 3 * Math.Exp(-(x + 1) * (x + 1) - y * y);
var xx = ArrayBuilder.CreateVector(x0, x1, 100);
var yy = ArrayBuilder.CreateVector(y0, y1, 100);
var peaksData = ArrayBuilder.Evaluate(peaks, xx, yy);
var cs = new ContourSeries
{
Color = OxyColors.Black,
LabelBackground = OxyColors.White,
ColumnCoordinates = yy,
RowCoordinates = xx,
Data = peaksData
};
model.Series.Add(cs);
Plot generated by OxyPlot code
I hope this clears things up.
Don

finding the distance between one camera and one object, knowing the dimensions of the object using opencv

I am working on robotic arm, and trying to find the distance between one camera and one object, knowing the dimensions of the object using open CV.
I not sure how to do it.
I tried using visual servoing method but not succeeded.
Any help will be nice
Here is a simple solution for starting:
1.) First, you have to calibrate your camera to receive the C matrix. For example:
http://docs.opencv.org/doc/tutorials/calib3d/camera_calibration/camera_calibration.html
2.) Then you have to identify on the image two points of the object which distance is known. Let's be these points P1(X1, Y1, Z1) and P2(X2, Y2, Z2) in the 3D space and p1(x1, y1) and p2(x2, y2) in 2D image plane. Certainly P1 and P2 are not known, only their pairs on the image (p1 and p2) and their distance D.
3.) Next, if you know the camera calibration matrix C, you have two equations:
p1 = C * P1 and p2 = C * P2
Then: C^-1 * p1 = P1' and C^-1 * p2 = P2' where P1' = (X1 / Z1, Y1 / Z1, 1) and P2' = (X2 / Z2, Y2 / Z2, 1). This means that C^-1 * p1 returns the 3D coordinates of the point belong the Z1 = 1 distance.
But the exact distance is not 1 but it is Z1 and Z2. Now, let's suppose that Z1 == Z2 == Z and we are looking for the distance Z.
This follows that Z * C^-1 * p1 = P1 and Z * C^-1 * p2 = P2.
Next (Z * C^-1 * p1) - (Z * C^-1 * p2) = P1 - P2, and
Z * ((C^-1 * p1) - C^-1 * p2)) = P1 - P2, and
Z^2 * ((C^-1 * p1) - C^-1 * p2))^2 = (P1 - P2)^2 = D^2
Now, C, p1, p2 and D are known, only Z is the unknown variable in the last equation.
Certainly, this is only a basic solution and uses the assumpion that the distances of the object's points are very similar, but it can work.

Intersection between two lines as defined by points without extending lines

I know there are various posts on stackoverflow about given two lines defined by two points each, being able to calculate the intersection, but typically those solutions extend the lines as opposed to treating their physical sizes.
Background search: The typical function used to calculate the intersection of two lines as defined by points is shown below, but credit goes to here.
// Finds the intersection of two lines, or returns false.
// The lines are defined by (o1, p1) and (o2, p2).
bool intersection(Point2f o1, Point2f p1, Point2f o2, Point2f p2,
Point2f &r)
{
Point2f x = o2 - o1;
Point2f d1 = p1 - o1;
Point2f d2 = p2 - o2;
float cross = d1.x*d2.y - d1.y*d2.x;
if (abs(cross) < /*EPS*/1e-8)
return false;
double t1 = (x.x * d2.y - x.y * d2.x)/cross;
r = o1 + d1 * t1;
return true;
}
Problem: While this code works phenomenally, I am wondering if anyone knows how to alter this function where if the lines as defined by the points do not physically intersect. Currently, the lines ultimately get extended.
ex:
o1 = 0,0
p1 = 10,0
o2 = 5,5
p2 = 5,-5
Naturally, this would intersect at 5,0.
ex:
o1 = 0,0
p1 = 10,0
o2 = 5,5
p2 = 5,2
While the function also says this intersection is at 5,0, lines defined by the points however do not physically intersect, so in this case I'd want the function to return false.
I've considered modifying this function to at the end, check if r is between the two points for each line and came across the suggested formula of here.. I'm wondering if there might be a better or easier solution?

How to get coordinates of end points in Cartesian coordinate system and the corresponding (rho,theta) in polar coordinate system of line segment

I have used Canny method to get edges of a image.Then I applied the approxPolyDP method to approximate edges,and got a set of polylines (not polygons) and line segments.Each polyline is formed from line segments.My purpose is to get coordinates of each line segment's end points in Cartesian coordinate system (2D plane) and the corresponding parameters (rho,theta) in polar coordinate.Any idea?Thanks!
BTW:I know that we can use the HoughLines method to find lines (not line segments) and get the parameters (rho,theta) in polar coordinate,or we can use the HoughLinesP method to find line segments and coordinates of end points.But the two method can not get the coordinates of end points of line segment and the corresponding parameters (rho,theta) at the same time.
Here's some sample C++ code for interpreting a line from OpenCV HoughLines. If you want the slope, just find two points and compute rise over run.
The important formula, which unfortunately is not as obvious as it should be in the documentation, is: rho = x*cos(theta) + y*sin(theta)
float yForHoughLine(float x, const Vec2f houghLine) {
float rho = houghLine[0];
float theta = houghLine[1];
if (theta == 0)
return NAN;
return (rho - (x * cos(theta))) / sin(theta);
}
float xForHoughLine(float y, const Vec2f houghLine) {
float rho = houghLine[0];
float theta = houghLine[1];
float cosTheta = cos(theta);
if (cosTheta == 0)
return NAN;
return (rho - (y * sin(theta))) / cosTheta;
}
Here's the equivalent Python:
def y_for_line(x, r, theta):
if theta == 0:
return np.nan
return (r - (x * np.cos(theta))) / np.sin(theta)
def x_for_line(y, r, theta):
cos_theta = np.cos(theta)
if cos_theta == 0:
return np.nan
return (r - (y * np.sin(theta))) / cos_theta

How to draw line not line segment OpenCV 2.4.2

Well the question says it all,
I know the function Line(), which draws line segment between two points.
I need to draw line NOT a line segment, also using the two points of the line segment.
[EN: Edit from what was previously posted as an answer for the question]
I used your solution and it performed good results in horizontal lines, but I still got problems in vertical lines.
For example, follows below an example using the points [306,411] and [304,8] (purple) and the draw line (red), on a image with 600x600 pixels. Do you have some tip?
I see this is pretty much old question. I had exactly the same problem and I used this simple code:
double Slope(int x0, int y0, int x1, int y1){
return (double)(y1-y0)/(x1-x0);
}
void fullLine(cv::Mat *img, cv::Point a, cv::Point b, cv::Scalar color){
double slope = Slope(a.x, a.y, b.x, b.y);
Point p(0,0), q(img->cols,img->rows);
p.y = -(a.x - p.x) * slope + a.y;
q.y = -(b.x - q.x) * slope + b.y;
line(*img,p,q,color,1,8,0);
}
First I calculate a slope of the line segment and then I "extend" the line segment into image's borders. I calculate new points of the line which lies in x = 0 and x = image.width. The point itself can be outside the Image, which is a kind of nasty trick, but the solution is very simple.
You will need to write a function to do that for yourself. I suggest you put your line in ax+by+c=0 form and then intersect it with the 4 edges of your image. Remember if you have a line in the form [a b c] finding its intersection with another line is simply the cross product of the two. The edges of your image would be
top_horizontal = [0 1 0];
left_vertical = [1 0 0];
bottom_horizontal = [0 1 -image.rows];
right_vertical = [1 0 -image.cols];
Also, if you know something about the distance between your points you could also just pick points very far along the line in each direction, I don't think the points handed to Line() need to be on the image.
I had the same problem and found out that there it is a known bug on 2.4.X OpenCV, fixed already for newer versions.
For the 2.4.X versions, the solution is to clip the line before plot it using cv::clipLine()
Here there is a function I did to myself that works fine on the 2.4.13 OpenCV
void Detector::drawFullImageLine(cv::Mat& img, const std::pair<cv::Point, cv::Point>& points, cv::Scalar color)
{
//points of line segment
cv::Point p1 = points.first;
cv::Point p2 = points.second;
//points of line segment which extend the segment P1-P2 to
//the image borders.
cv::Point p,q;
//test if line is vertical, otherwise computes line equation
//y = ax + b
if (p2.x == p1.x)
{
p = cv::Point(p1.x, 0);
q = cv::Point(p1.x, img.rows);
}
else
{
double a = (double)(p2.y - p1.y) / (double) (p2.x - p1.x);
double b = p1.y - a*p1.x;
p = cv::Point(0, b);
q = cv::Point(img.rows, a*img.rows + b);
//clipline to the image borders. It prevents a known bug on OpenCV
//versions 2.4.X when drawing
cv::clipLine(cv::Size(img.rows, img.cols), p, q);
}
cv::line(img, p, q, color, 2);
}
This answer is forked from pajus_cz answer but a little improved.
We have two points and we need to get the line equation y = mx + b to be able to draw the straight line.
There are two variables we need to get
1- Slope(m)
2- b which can be retrieved through the line equation using any given point from the two we have already after calculating the slope b = y - mx .
void drawStraightLine(cv::Mat *img, cv::Point2f p1, cv::Point2f p2, cv::Scalar color)
{
Point2f p, q;
// Check if the line is a vertical line because vertical lines don't have slope
if (p1.x != p2.x)
{
p.x = 0;
q.x = img->cols;
// Slope equation (y1 - y2) / (x1 - x2)
float m = (p1.y - p2.y) / (p1.x - p2.x);
// Line equation: y = mx + b
float b = p1.y - (m * p1.x);
p.y = m * p.x + b;
q.y = m * q.x + b;
}
else
{
p.x = q.x = p2.x;
p.y = 0;
q.y = img->rows;
}
cv::line(*img, p, q, color, 1);
}

Resources