我需要一个非常快的
atan2
来从索贝尔值中获取梯度(我正在实现精明的边缘算法)。有谁知道一个非常快速的实现,最好是内在函数(SIMD)或非常快速的近似。 (我认为近似值就足够了,因为这些值四舍五入为 0°、45°、90°、135°)
添加:我知道 SVML 中的英特尔 IPP atan2,不幸的是我无法使用它。
您似乎想四舍五入到一个八分圆数,大概是从
-22.5°
到 337.5°
,以 45°
为增量。
八分圆由通过原点的四条线与方程分开
Y = X tan(Θ),
或
a.Y - b.X = 0.
具有合适的缩放因子。
通过计算四个所需角度的这些表达式的符号,您将找到八分圆。通过巧妙的组合,您可以将符号评估限制为三个,因为存在
8=2³
的可能性。
这很可能可以使用 SIMD 指令通过计算判别表达式、它们的符号以及它们的符号的适当组合来评估,但这并不是微不足道的。
可能不需要转换为 45° 的倍数,甚至不需要顺序编号。这完全取决于您如何处理八分圆信息。
额外的 SIMD 建议:
通过预加载系数,您可以使用 16 位整数算术(可能使用乘法和加法)一次计算 (X, Y) 对的所有四个直线方程。然后获取符号并用
_mm_movemask_epi8
将它们打包为四位。使用四位值作为小型查找表的输入。
如here所述,
atan2()
已经是单个FPU指令:x87 FPU操作码FPATAN
。只需查看编译器在调用 std::atan2()
时生成的反汇编代码即可。如果不是单个 FPU 指令,那么您可以在 GCC 内联汇编中尝试this:
inline double my_atan2 (double y, double x) {
double result;
asm (
"fpatan\n\t"
: "=t" (result) // outputs; t = top of fpu stack
: "0" (x), // inputs; 0 = same as result
"u" (y) // u = 2nd floating point register
);
return result;
}