atan2相关知识汇总

1.atan2的含义

C 语言里 double atan2(double y,double x) 返回的是原点至点(x,y)的方位角,即与 x 轴的夹角。返回值的单位为弧度,取值范围为(-π, π]。结果为正表示从 X 轴逆时针旋转的角度,结果为负表示从 X 轴顺时针旋转的角度。若要用度表示反正切值,请将结果再乘以 180/π。另外要注意的是,函数atan2(y,x)中参数的顺序是倒置的,atan2(y,x)计算的值相当于点(x,y)的角度值。


arctan2的图像表示(维基百科)


自己画的atan2的笛卡尔坐标系下的示意图,以及将atan2的范围转换到[0, 2π)的示意图。atan2转换到[0,2π)表示的是与x轴正方向的逆时针夹角,这样转换的好处是便于计算两条线之间的夹角。

参考博客:

https://baike.baidu.com/item/atan2/10931300?fr=aladdin

https://en.wikipedia.org/wiki/Atan2


2.atan2与atan的区别

atan接受的是一个正切值(直线的斜率)得到夹角,但是由于正切的规律性本可以有两个角度的但它却只返回一个,因为atan的值域是从-90~90 也就是它只处理一四象限,所以一般不用它。
atan2(double y,double x) 其中y代表已知点的Y坐标 同理x ,返回值是此点与远点连线与x轴正方向的夹角,这样它就可以处理四个象限的任意情况了,它的值域相应的也就是-180~180了。

例如:
例1:斜率是1的直线的夹角
cout<<atan(1.0)*180/PI;//45°
cout<<atan2(1.0,1.0)*180/PI;//45° 第一象限
cout<<atan2(-1.0,-1.0)*180/PI;//-135°第三象限
后两个斜率都是1 但是atan只能求出一个45°

例2:斜率是-1的直线的角度
cout<<atan(-1.0)*180/PI;//-45°
cout<<atan2(-1.0,1.0)*180/PI;//-45° y为负 在第四象限

cout<<atan2(1.0,-1.0)*180/PI;//135° x为负 在第二象限

常用的不是求过原点的直线的夹角 往往是求一个线段的夹角 这对于atan2就更是如鱼得水了
例如求A(1.0,1.0) B(3.0,3.0)这个线段AB与x轴正方向的夹角
用atan2表示为 atan2(y2-y1,x2-x1) 即 atan2(3.0-1.0,3.0-1.0)

它的原理就相当于把A点平移到原点B点相应变成B'(x2-x1,y2-y1)点

参考博客:https://www.cnblogs.com/dutlei/archive/2013/01/14/2860332.html


3.OpenCV中fastAtan2函数

fastAtan2函数在OpenCV中用户非常广,比如在SIFT描述子求取过程中需要计算特征点的方向,此时OpenCV的源码中就是使用的fastAtan2函数,fastAtan2函数原型如下:
float fastAtan2(float y,float x)
x—向量的x坐标
y—向量的y坐标
输入一个2维向量,计算这个向量的方向,以度为单位(范围是0度---360度),精度是0.3度。

static const float atan2_p1 = 0.9997878412794807f*(float)(180/CV_PI);
static const float atan2_p3 = -0.3258083974640975f*(float)(180/CV_PI);
static const float atan2_p5 = 0.1555786518463281f*(float)(180/CV_PI);
static const float atan2_p7 = -0.04432655554792128f*(float)(180/CV_PI);

float fastAtan2( float y, float x )
{
    float ax = std::abs(x), ay = std::abs(y);
    float a, c, c2;
    if( ax >= ay )
    {
        c = ay/(ax + (float)DBL_EPSILON);
        c2 = c*c;
        a = (((atan2_p7*c2 + atan2_p5)*c2 + atan2_p3)*c2 + atan2_p1)*c;
    }
    else
    {
        c = ax/(ay + (float)DBL_EPSILON);
        c2 = c*c;
        a = 90.f - (((atan2_p7*c2 + atan2_p5)*c2 + atan2_p3)*c2 + atan2_p1)*c;
    }
    if( x < 0 )
        a = 180.f - a;
    if( y < 0 )
        a = 360.f - a;
    return a;
}

fastAtan2函数得出的角度是以X轴正方向为0°方向,然后角度确定按照逆时针方向,以360°为终点,角度范围0°- 360°。此函数用到的应该是一种快速计算atan2的方法,没有找到出处。但是看到另一种快速计算法:

float ax = std::abs(dx), ay = std::abs(dy);
float a = std::min(ax, ay)/(std::max(ax, ay)+(float)DBL_EPSILON);
float s = a*a;
float r = ((-0.0464964749 * s + 0.15931422) * s - 0.327622764) * s * a + a;
if(ay > ax) r = 1.57079637 - r;
if(dx < 0) r = 3.14159274f - r;
if(dy<0) r = -r;
//如果这一行改为:if(dy < 0) r = 6.28318548f - r;
//结果的取值范围变为[0, 2*PI]

参考博客:

https://blog.csdn.net/mingzhentanwo/article/details/45155307

https://blog.csdn.net/ubunfans/article/details/46680915?utm_source=itdadao&utm_medium=referral

4.fastAtan2与atan2的关系

#include<opencv2\opencv.hpp>

using namespace std;
using namespace cv;

int main()
{
	cv::Point P1(20, 30);	//第一象限点
	cv::Point P2(-45, 23);	//第二象限点
	cv::Point P3(-73, -38);	//第三象限点
	cv::Point P4(29, -47);	//第四象限点

	cout << "第一象限:P1" << P1 << endl;
	cout << "atan2: " << atan2(P1.y, P1.x) * 180 / CV_PI << endl;
	cout << "fastAtan2: " << fastAtan2(P1.y, P1.x) << endl;

	cout << "第二象限:P2" << P2 << endl;
	cout << "atan2: " << atan2(P2.y, P2.x) * 180 / CV_PI << endl;
	cout << "fastAtan2: " << fastAtan2(P2.y, P2.x) << endl;

	cout << "第三象限:P3" << P3 << endl;
	cout << "atan2: " << atan2(P3.y, P3.x) * 180 / CV_PI << endl;
	cout << "fastAtan2: " << fastAtan2(P3.y, P3.x) << endl;

	cout << "第四象限:P4" << P4 << endl;
	cout << "atan2: " << atan2(P4.y, P4.x) * 180 / CV_PI << endl;
	cout << "fastAtan2: " << fastAtan2(P4.y, P4.x) << endl;

	system("pause");
	return 0;
}

结果:


通过结果可以发现第一象限和第二象限,atan2(*180 / π)和fastAtan2的值基本相等,第三象限和第四象限,fastAtan2的值基本等于360 + atan2(*180 / π)。可以理解为,fastAtan2实现了将atan2的范围转换到[0, 360).


猜你喜欢

转载自blog.csdn.net/weixin_42142612/article/details/80972768