I am using following thread to perform angle detection for a rectangle image.
Detect centre and angle of rectangles in an image using Opencv
I am stuck at following piece of code.
cv::Point2f edge1 = cv::Vec2f(rect_points[1].x, rect_points[1].y) - cv::Vec2f(rect_points[0].x, rect_points[0].y);
cv::Point2f edge2 = cv::Vec2f(rect_points[2].x, rect_points[2].y) - cv::Vec2f(rect_points[1].x, rect_points[1].y);
cv::Point2f usedEdge = edge1;
if(cv::norm(edge2) > cv::norm(edge1)) usedEdge = edge2;
cv::Point2f reference = cv::Vec2f(1,0); // horizontal edge
angle = 180.0f/CV_PI * acos((reference.x*usedEdge.x + reference.y*usedEdge.y) / (cv::norm(reference) *cv::norm(usedEdge)));
I am not able to figure out following few lines which i required to convert in emgu csharp.
cv::Point2f edge1 = cv::Vec2f(rect_points[1].x, rect_points[1].y) - cv::Vec2f(rect_points[0].x, rect_points[0].y);
cv::Point2f edge2 = cv::Vec2f(rect_points[2].x, rect_points[2].y) - cv::Vec2f(rect_points[1].x, rect_points[1].y);
angle = 180.0f/CV_PI * acos((reference.x*usedEdge.x + reference.y*usedEdge.y) / (cv::norm(reference) *cv::norm(usedEdge)));
if(cv::norm(edge2) > cv::norm(edge1)) usedEdge = edge2;
cv::Point2f reference = cv::Vec2f(1,0);
Can anyone help me how to resolve the same? Any help or suggestion will be highly appreciated?
The Point2f here are simply points, having float precision properties of X and Y, being used to store 2D vectors of I and J. Their method if declaration is setting the edges to be the vector between two points, i.e. the delta between those two points. In C#, I would write this as:
float deltaX = rect_points[1].X - rect_points[0].X;
float deltaY = rect_points[1].Y - rect_points[0].Y;
PointF edge1 = new PointF(deltaX, deltaY);
OR of course...
PointF edge1 = new PointF(rect_points[1].X - rect_points[0].X, rect_points[1].Y - rect_points[0].Y);
PointF edge2 = new PointF(rect_points[2].X - rect_points[1].X, rect_points[2].Y - rect_points[1].Y);
These PointF are now the two vectors, or edges, that join at rect_points[1]. Next, norm is performed in order to compare the magnitude of the two. This is simply Pythagoras if we perform the same manually:
edge1Magnitude = Math.Sqrt(Math.Pow(edge1.X, 2) + Math.Pow(edge1.Y, 2));
edge2Magnitude = Math.Sqrt(Math.Pow(edge2.X, 2) + Math.Pow(edge2.Y, 2));
The longer of the edges, that with the greatest magnitude, is considered the "primary", or longer edge the rectangle:
PointF primaryEdge = edge1Magnitude > edge2Magnitude ? edge1 : edge2;
double primaryMagnitude = edge1Magnitude > edge2Magnitude ? edge1Magnitude : edge2Magnitude;
Finally, to find the angle between the primaryEdge, and a horizontal vector, reference. This is the acos, of the "Dot Product", of the two, or:
PointF reference = new PointF(1,0);
double refMagnitude = 1;
double thetaRads = Math.Acos(((primaryEdge.X * reference.X) + (primaryEdge.Y * reference.Y)) / (primaryMagnitude * refMagnitude));
double thetaDeg = thetaRads * 180 / Math.PI;
Now, thetaDeg is the angle between edge1 and the horizontal, in degrees.
Related
I have referenced following link to develop following csharp code to detect angle of Image. https://stackoverflow.com/a/34285205/7805023
Image<Gray, byte> imgout = imgInput.Convert<Gray, byte>().Not().ThresholdBinary(new
Gray(50), new Gray(255));
VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint();
Mat hier = new Mat();
CvInvoke.FindContours(imgout, contours, hier, Emgu.CV.CvEnum.RetrType.External, Emgu.CV.CvEnum.ChainApproxMethod.ChainApproxSimple);
for (int i = 0; i <= 1; i++)
{
Rectangle rect = CvInvoke.BoundingRectangle(contours[i]);
RotatedRect box = CvInvoke.MinAreaRect(contours[i]);
PointF[] Vertices = box.GetVertices();
PointF point = box.Center;
PointF edge1 = new PointF(Vertices[1].X - Vertices[0].X,
Vertices[1].Y - Vertices[0].Y);
PointF edge2 = new PointF(Vertices[2].X - Vertices[1].X,Vertices[2].Y - Vertices[1].Y);
double edge1Magnitude = Math.Sqrt(Math.Pow(edge1.X, 2) + Math.Pow(edge1.Y, 2));
double edge2Magnitude = Math.Sqrt(Math.Pow(edge2.X, 2) + Math.Pow(edge2.Y, 2));
PointF primaryEdge = edge1Magnitude > edge2Magnitude ? edge1 : edge2;
PointF reference = new PointF(Vertices[1].X, Vertices[0].Y);
double thetaRads = Math.Acos((primaryEdge.X * reference.X) + (primaryEdge.Y * reference.Y))/(edge1Magnitude* edge2Magnitude);
double thetaDeg = thetaRads * 180 / Math.PI;
}
I am getting NaN as output for angle. Please go through the code let me know what wrong I have done?
I have used emgucv for image processing.
Any help will be highly appreciated.
NaN, Not a Number, is returned by Math.Acos() when the provided value is not between -1 and 1.
Your calculation of thetaRads is wrong, you need to calculate the Acos of the vectors' dot product divided by the product of the vectors' magnitudes, as per:
double thetaRads = Math.Acos(((primaryEdge.X * reference.X) + (primaryEdge.Y * reference.Y)) / (primaryMagnitude * refMagnitude));
For a while now I've been using the following function to rotate a series of Points around a pivot point in various programs of mine.
private Point RotatePoint(Point point, Point pivot, double radians)
{
var cosTheta = Math.Cos(radians);
var sinTheta = Math.Sin(radians);
var x = (cosTheta * (point.X - pivot.X) - sinTheta * (point.Y - pivot.Y) + pivot.X);
var y = (sinTheta * (point.X - pivot.X) + cosTheta * (point.Y - pivot.Y) + pivot.Y);
return new Point((int)x, (int)y);
}
This has always worked great, until I tried to rotate a shape repeatedly by small amounts. For example, this is what I get from calling it for 45° on a rectangular polygon made up of 4 points:
foreach (var point in points)
Rotate(point, center, Math.PI / 180f * 45);
But this is what I get by calling rotate 45 times for 1°:
for (var i = 0; i < 45; ++i)
foreach (var point in points)
Rotate(point, center, Math.PI / 180f * 1)
As long as I call it only once it's fine, and it also seems like it gets gradually worse the lower the rotation degree is. Is there some flaw in the function, or am I misunderstanding something fundamental about what this function does?
How could I rotate repeatedly by small amounts? I could save the base points and use them to update the current points whenever the rotation changes, but is that the only way?
Your Point position measure is off because of the integer rounding generated by the RotatePoint() method.
A simple correction in the method returned value, using float coordinates, will produce the correct measure:
To test it, create a Timer and register its Tick event as RotateTimerTick():
Added a rotation spin increment (see the rotationSpin Field) to emphasize the motion effect.
PointF pivotPoint = new PointF(100F, 100F);
PointF rotatingPoint = new PointF(50F, 100F);
double rotationSpin = 0D;
private PointF RotatePoint(PointF point, PointF pivot, double radians)
{
var cosTheta = Math.Cos(radians);
var sinTheta = Math.Sin(radians);
var x = (cosTheta * (point.X - pivot.X) - sinTheta * (point.Y - pivot.Y) + pivot.X);
var y = (sinTheta * (point.X - pivot.X) + cosTheta * (point.Y - pivot.Y) + pivot.Y);
return new PointF((float)x, (float)y);
}
private void RotateTimerTick(object sender, EventArgs e)
{
rotationSpin += .5;
if (rotationSpin > 90) rotationSpin = 0;
rotatingPoint = RotatePoint(rotatingPoint, pivotPoint, (Math.PI / 180f) * rotationSpin);
Panel1.Invalidate(new Rectangle(new Point(50,50), new Size(110, 110)));
}
private void Panel1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
e.Graphics.FillEllipse(Brushes.White, new RectangleF(100, 100, 8, 8));
e.Graphics.FillEllipse(Brushes.Yellow, new RectangleF(rotatingPoint, new SizeF(8, 8)));
}
This is the result using float values:
And this is what happens using integer values:
If you want you can use the Media3D to only deal with matrix and simplify the coding. Something as simple as this will work.
public Point3D Rotate(Point3D point, Point3D rotationCenter, Vector3D rotation, double degree)
{
// create empty matrix
var matrix = new Matrix3D();
// translate matrix to rotation point
matrix.Translate(rotationCenter - new Point3D());
// rotate it the way we need
matrix.Rotate(new Quaternion(rotation, degree));
// apply the matrix to our point
point = matrix.Transform(point);
return point;
}
Then you simply call the method and specify the rotation. Lets say you work with 2D (like in your example) and lets assume we work with XY plane so the rotation is in Z. You can do something like :
var rotationPoint = new Point3D(0, 0, 0);
var currentPoint = new Point3D(10, 0, 0);
// rotate the current point around the rotation point in Z by 45 degree
var newPoint = Rotate(currentPoint, rotation, new Vector3D(0, 0, 1), 45d);
I need to be able to check whether the angle between three points (A, B and C) which make up part of a shape is reflex (> PI radians), as in the diagram below (sorry for poor paint skills!):
My points should always be anti-clockwise, and I always want to measure the angle on the inside of the shape.
I am currently doing this using the following code:
//triangle[] is an array of the three points I am testing, corresponding
// to [A, B, C] on the diagram above
//Vectors from B to A and C
PointF toA = PointFVectorTools.difference(triangle[0], triangle[1]);
PointF toC = PointFVectorTools.difference(triangle[2], triangle[1]);
double angle = Math.Atan2(toB.Y, toB.X) - Math.Atan2(toA.Y, toA.X);
//Put angle in range 0 to 2 PI
if (angle < 0) angle += 2 * Math.PI;
return angle > Math.PI;
This has worked in all the cases I have tried up until now, but with these co-ords it does not work:
(Where B=(2,3) )
The angle I get back is ~-0.5, whereas I would expect ~+0.5. Any ideas why this is wrong?
UPDATE
I've attempted to implement Nico's solution, and while I understand it in theory I'm getting a real headache trying to implement it. Here is the code so far:
//Vector A -> B
float dx = triangle[1].X - triangle[0].X;
float dy = triangle[1].Y - triangle[0].Y;
//Left normal = (y, -x)
PointF leftDir = new PointF(dy, -dx);
//Vector B -> C
dx = triangle[2].X - triangle[1].X;
dy = triangle[2].Y - triangle[1].Y;
//Dot product of B->C and Left normal
float dot = dx * leftDir.X + dy * leftDir.Y;
return dot < 0;
In the following, I assume that the x-axis points to the right and the y-axis points upwards. If this is not the case in your scenario, you might need to switch some signs.
If you have the line segment (x1, y1) - (x2, y2) and points are sorted counter-clockwise, you know that the shape is left of the line segment. The orthogonal direction vector that points to the line segment's left is:
leftDir = (y1 - y2, x2 - x1)
Together with the line segment, this direction defines a half space. If the following angle is convex, the third point must lie in this half space. If that's not the case, the angle is concave (which you apparently call reflex):
You can determine if the point lies in the same half space with the dot product:
isConcave = dot(p3 - p2, leftDir) < 0
In code:
float dx = x3 - x2;
float dy = y3 - y2;
float dot = dx * leftDir.x + dy * leftDir.y
return dot < 0;
I'm not sure how toB in your code is defined, and also I'm not familar with PointF.
Anyway you should use the cosine rule c^2 = a^2 + b^2 - 2ab cos(C) (where a,b,c are the lengths of the sides of the triangle, and C is the angle subtending c):
public bool IsReflex(... triangle)
{
var a = GetVectorLength(triangle[0].x, triangle[0].y, triangle[1].x, triangle[1].y);
var b = GetVectorLength(triangle[1].x, triangle[1].y, triangle[2].x, triangle[2].y);
var c = GetVectorLength(triangle[2].x, triangle[2].y, triangle[0].x, triangle[0].y);
var cosC = (c*c - a*a - b*b) / (2*a*b);
var C = Math.Acos(cosC); // this returns a value between 0 and pi
return Math.Abs(C) > (Math.PI/2);
}
private double GetVectorLength(double x0, double y0, double x1, double y1)
{
// using Pythagoras
var sideX = x0 - x1;
var sideY = y0 - y1;
return Math.Sqrt(sideX*sideX + sideY*sideY);
}
I have 3 particles and one of them is the center particle. I want to rotate other two particle ( stored in particles list ) relative to the center particle with the formula q' = Θq + p where q' is the new position of the rotated particle, Θ is the orientation angle and p is the position of center particle. The initial position of other two particles is stored in initialParticlePosition list. THe problem is I think the angle I calculate is wrong because of the range. I thing I should take the range as [-pi, pi) or something like this. In some parts it calculates correct but sometimes it is wrong. Can someone help me with this code or give me another method of rotating.
{
angle = Math.Acos(Vector2.Dot(heading,new Vector2(0,-1) ));
for (int i = 0; i < 2; i++)
{
tempX = (double)initialParticlePositions[i].X * Math.Cos(angle) - (double)initialParticlePositions[i].Y * Math.Sin(angle) + centerParticle.position.x;
tempY = (double)initialParticlePositions[i].X * Math.Sin(angle) + (double)initialParticlePositions[i].Y * Math.Cos(angle) + centerParticle.position.y;
particles[i].position.x = tempX;
particles[i].position.y = tempY;
}
}
Some methods that might help (angles always in degrees, not rad):
public static double GetAngle(Vector v)
{
return Math.Atan2(v.X, -v.Y) * 180.0 / Math.PI;
}
public static Vector SetAngle(Vector v, double angle)
{
var angleInRads = angle * (Math.PI / 180.0);
var distance = v.Length;
v.X = (Math.Sin(angleInRads) * distance);
v.Y = -(Math.Cos(angleInRads) * distance);
return v;
}
static public Point RotatePointAroundCenter(Point point, Point center, double rotationChange)
{
Vector centerToPoint = point - center;
double angle = GetAngle(centerToPoint);
Vector centerToNewPoint = SetAngle(centerToPoint, angle + rotationChange);
return center + centerToNewPoint;
}
(You should start marking answers that help as answer, click the checkmark outline below the votes on the left, e.g. you could accept this answer)
Edit: Optimized the methods a bit.
The particle positions that are orbiting can be set with a single line of code each:
Assume p1, p2, & p3 are Vector2s and p2 & p3 are orbiting p1.
p2 = Vector2.Transform(p2 - p1, Matrix.CreateRotationZ(rotationChangeP2)) + p1;
p3 = Vector2.Transform(p3 - p1, Matrix.CreateRotationZ(rotationChangeP3)) + p1;
The Matrix.Create...() method will call the two trig functions for you.
edit. the Matrix & Vector2 structures & methods are XNA specific but included here because that's what the OP tagged his Q with.
angle = Math.Acos(Vector2.Dot(heading,new Vector2(0,-1)));
As you suspect, your combination of dot product and Acos will only give you angles in a 180
degree range.
Instead, use Atan2 on your unit vector to get a full range of angles from -pi to pi.
angle = (float)Math.Atan2((double)heading.Y, (double)heading.X);
You may need to negate the Y term if your Y axis is positive in the down direction.
I've been trying to do this for a while but haven't had much success. All I want to do is rotate the rectangle and then create a new rectangle which encompasses the rotated points.
Anyone have any ideas how it should be done properly?
The code I have doesn't work, but I'm not sure where it's going wrong exactly (the numbers make me think it actually works), for example if I have a rectangle with the following values:
{X:865 Y:76 Width:22 Height:164}
The result is:
{X:1863 Y:1740 Width:164 Height:22}
Where it is rotated -1.57094443
What I do is grab all four points of the original rectangle and rotate them with this function:
static public Vector2 RotateVectorAround(Vector2 anchor, Vector2 vector, float rotation)
{
Matrix mat = new Matrix();
mat.Translation = new Vector3(vector.X - anchor.X, vector.Y - anchor.Y, 0);
Matrix rot = new Matrix();
rot = Matrix.CreateRotationZ(rotation);
mat *= rot;
mat.Translation += new Vector3(anchor.X, anchor.Y, 0);
return new Vector2(mat.Translation.X, mat.Translation.Y);
}
Where 'anchor' is the pivot point (I'm not sure if this function is mathematically sound), then I determine the corners of the rotated rectangle with this:
Vector2 newTopLeft = new Vector2( Math.Min(Math.Min(topleft.X, bottomRight.X), Math.Min(bottomleft.X, topright.X)),
Math.Min(Math.Min(topleft.Y, bottomRight.Y), Math.Min(bottomleft.Y, topright.Y)));
Vector2 newBottomRight = new Vector2(
Math.Max(Math.Max(topleft.X, bottomRight.X), Math.Max(bottomleft.X, topright.X)),
Math.Max(Math.Max(topleft.Y, bottomRight.Y), Math.Max(bottomleft.Y, topright.Y) ));
You can multiply the points of the rectangle with a rotation matrix.
so given point P in a rotation will result in point R
where a is the rotation
a = degrees * (PI/180)
Rx = Px * cos(a) + Py * -sin(a)
Ry = Px * sin(a) + Py * cos(a)
to rotate about a point you can substract the pivot point before rotating it and add them after the rotation again (so the rotation is virtually around (0,0)
Px = Px - PivotX
Py = Py - PivotY
Rx = Px * cos(a) + Py * -sin(a)
Ry = Px * sin(a) + Py * cos(a)
Px = Rx + PivotX
Py = Ry + PivotY
I would not use the 3'th dimension here for a 2d rotation
in XNA that is something like (sorry no VStudio here):
point -= pivot
point = Vector2.Transform(point, Matrix.CreateRotationZ(angle));
point += pivot