aboutsummaryrefslogtreecommitdiff
path: root/src/core/NEON/kernels/NEMagnitudePhaseKernel.cpp
diff options
context:
space:
mode:
authorAnthony Barbier <anthony.barbier@arm.com>2017-09-04 18:44:23 +0100
committerAnthony Barbier <anthony.barbier@arm.com>2018-09-17 13:03:09 +0100
commit6ff3b19ee6120edf015fad8caab2991faa3070af (patch)
treea7a6dcd16dfd56d79fa1b56a313caeebcc939b68 /src/core/NEON/kernels/NEMagnitudePhaseKernel.cpp
downloadComputeLibrary-6ff3b19ee6120edf015fad8caab2991faa3070af.tar.gz
COMPMID-344 Updated doxygen
Change-Id: I32f7b84daa560e460b77216add529c8fa8b327ae
Diffstat (limited to 'src/core/NEON/kernels/NEMagnitudePhaseKernel.cpp')
-rw-r--r--src/core/NEON/kernels/NEMagnitudePhaseKernel.cpp869
1 files changed, 869 insertions, 0 deletions
diff --git a/src/core/NEON/kernels/NEMagnitudePhaseKernel.cpp b/src/core/NEON/kernels/NEMagnitudePhaseKernel.cpp
new file mode 100644
index 0000000000..a874d219d7
--- /dev/null
+++ b/src/core/NEON/kernels/NEMagnitudePhaseKernel.cpp
@@ -0,0 +1,869 @@
+/*
+ * Copyright (c) 2016, 2017 ARM Limited.
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include "arm_compute/core/NEON/kernels/NEMagnitudePhaseKernel.h"
+
+#include "arm_compute/core/Error.h"
+#include "arm_compute/core/Helpers.h"
+#include "arm_compute/core/IAccessWindow.h"
+#include "arm_compute/core/ITensor.h"
+#include "arm_compute/core/Validate.h"
+
+#include <arm_neon.h>
+#include <cstdint>
+
+using namespace arm_compute;
+
+namespace arm_compute
+{
+class Coordinates;
+} // namespace arm_compute
+
+namespace
+{
+// Defines for computing atan2
+constexpr float SCALE_FACTOR = 0.7111111111111111f;
+constexpr float PI = 3.141592653589793f;
+constexpr float SCALE_180 = 180.0f / PI;
+constexpr float SCALE_360 = SCALE_180 * SCALE_FACTOR;
+constexpr float PI_4 = 0.7853981633974483f;
+constexpr float COEFF1 = 0.0663f;
+constexpr float COEFF2 = 0.2447f;
+} // namespace
+
+#ifdef ARM_COMPUTE_ENABLE_FP16
+namespace fp16
+{
+inline float16x8_t inv(float16x8_t x)
+{
+ const float16x8_t estimate = vrecpeq_f16(x);
+ return vmulq_f16(estimate, vrecpsq_f16(x, estimate));
+}
+
+inline float16x8_t atan2_fast(float16x8_t gx, float16x8_t gy, float16x8_t scale)
+{
+ static const float16x8_t one = vdupq_n_f16(1.0f);
+ static const float16x8_t ninety = vdupq_n_f16(90.f * SCALE_FACTOR);
+ static const float16x8_t epsilon = vdupq_n_f16(1e-9f);
+ static const float16x8_t piover4 = vdupq_n_f16(PI_4);
+ static const float16x8_t coeff1 = vdupq_n_f16(COEFF1);
+ static const float16x8_t coeff2 = vdupq_n_f16(COEFF2);
+
+ const float16x8_t abs_gx = vabsq_f16(gx);
+ const float16x8_t abs_gy = vabsq_f16(gy);
+ const float16x8_t tmin = vminq_f16(abs_gx, abs_gy);
+ const float16x8_t tmax = vmaxq_f16(abs_gx, abs_gy);
+
+ // z = min(x, y) / max(x, y)
+ const float16x8_t z = vmulq_f16(tmin, inv(vaddq_f16(tmax, epsilon)));
+ const float16x8_t absz = vabsq_f16(z);
+
+ // = x * [pi/4 + (1 - |x|) * (0.2447 + 0.0663 * |x|)]
+ float16x8_t arctan = vmulq_f16(z, vfmaq_f16(piover4,
+ vsubq_f16(one, absz),
+ vfmaq_f16(coeff2, coeff1, absz)));
+
+ // Radians to degrees conversion with applied a scale factor in order to have the result [0, 255]
+ arctan = vmulq_f16(arctan, scale);
+
+ /* If z > 1, result = 90 - result */
+ return vbslq_f16(vcgeq_f16(abs_gx, abs_gy), arctan, vsubq_f16(ninety, arctan));
+}
+
+inline float16x8_t atan2_0_360(float16x8_t gx, float16x8_t gy)
+{
+ static const float16x8_t scale = vdupq_n_f16(SCALE_360);
+ static const float16x8_t threesixty = vdupq_n_f16(360.0f * SCALE_FACTOR);
+ static const float16x8_t zero = vdupq_n_f16(0.0f);
+ static const float16x8_t oneeighty = vdupq_n_f16(180.0f * SCALE_FACTOR);
+
+ float16x8_t arctan = atan2_fast(gx, gy, scale);
+
+ // Choose correct quadrant
+ arctan = vbslq_f16(vcltq_f16(gx, zero), vsubq_f16(oneeighty, arctan), arctan);
+ arctan = vbslq_f16(vcltq_f16(gy, zero), vsubq_f16(threesixty, arctan), arctan);
+
+ return arctan;
+}
+
+inline float16x8_t atan2_0_180(float16x8_t gx, float16x8_t gy)
+{
+ static const float16x8_t scale = vdupq_n_f16(SCALE_180);
+ static const float16x8_t threesixty = vdupq_n_f16(360.0f * SCALE_FACTOR);
+ static const float16x8_t oneeighty = vdupq_n_f16(180.0f * SCALE_FACTOR);
+ static const float16x8_t zero = vdupq_n_f16(0.0f);
+
+ float16x8_t arctan = atan2_fast(gx, gy, scale);
+
+ // Choose correct quadrant
+ arctan = vbslq_f16(vcltq_f16(gx, zero), vsubq_f16(oneeighty, arctan), arctan);
+ arctan = vbslq_f16(vcltq_f16(gy, zero), vsubq_f16(threesixty, arctan), arctan);
+ arctan = vbslq_f16(vcgtq_f16(arctan, oneeighty), vsubq_f16(arctan, oneeighty), arctan);
+
+ return arctan;
+}
+
+inline float32x4_t invsqrtv(float32x4_t x)
+{
+ float32x4_t sqrt_reciprocal = vrsqrteq_f32(x);
+
+ sqrt_reciprocal = vmulq_f32(vrsqrtsq_f32(vmulq_f32(x, sqrt_reciprocal), sqrt_reciprocal),
+ sqrt_reciprocal);
+ sqrt_reciprocal = vmulq_f32(vrsqrtsq_f32(vmulq_f32(x, sqrt_reciprocal), sqrt_reciprocal),
+ sqrt_reciprocal);
+
+ return sqrt_reciprocal;
+}
+
+inline float32x4_t sqrtv(float32x4_t x)
+{
+ float32x4_t res = vdupq_n_f32(0.5f);
+ return vmlaq_f32(res, x, invsqrtv(x));
+}
+
+inline int16x8_t magnitude_l1(int16x8_t input1, int16x8_t input2)
+{
+ return vqaddq_s16(vabsq_s16(input1), vabsq_s16(input2));
+}
+
+inline int16x8_t magnitude_l2(int16x8_t input1, int16x8_t input2)
+{
+ const int32x4x2_t square_x =
+ {
+ vmull_s16(vget_low_s16(input1), vget_low_s16(input1)),
+ vmull_s16(vget_high_s16(input1), vget_high_s16(input1))
+ };
+
+ const int32x4x2_t square_y =
+ {
+ vmull_s16(vget_low_s16(input2), vget_low_s16(input2)),
+ vmull_s16(vget_high_s16(input2), vget_high_s16(input2))
+ };
+
+ const uint32x4x2_t sum =
+ {
+ vaddq_u32(vreinterpretq_u32_s32(square_x.val[0]),
+ vreinterpretq_u32_s32(square_y.val[0])),
+ vaddq_u32(vreinterpretq_u32_s32(square_x.val[1]),
+ vreinterpretq_u32_s32(square_y.val[1]))
+ };
+
+ const float32x4x2_t res =
+ {
+ sqrtv(vcvtq_f32_u32(sum.val[0])),
+ sqrtv(vcvtq_f32_u32(sum.val[1]))
+ };
+
+ return vcombine_s16(vqmovn_s32(vcvtq_s32_f32(res.val[0])),
+ vqmovn_s32(vcvtq_s32_f32(res.val[1])));
+}
+
+inline uint8x8_t phase_signed(int16x8_t input1, int16x8_t input2)
+{
+ static const float16x8_t zeropointfive = vdupq_n_f16(0.5f);
+
+ const float16x8_t inputx_f16 = vcvtq_f16_s16(input1);
+ const float16x8_t inputy_f16 = vcvtq_f16_s16(input2);
+
+ // Compute fast atan2
+ const float16x8_t angle = atan2_0_360(inputx_f16, inputy_f16);
+
+ return vqmovun_s16(vcvtq_s16_f16(vaddq_f16(angle, zeropointfive)));
+}
+
+inline uint8x8_t phase_unsigned(int16x8_t input1, int16x8_t input2)
+{
+ static const float16x8_t zeropointfive = vdupq_n_f16(0.5f);
+
+ const float16x8_t inputx_f16 = vcvtq_f16_s16(input1);
+ const float16x8_t inputy_f16 = vcvtq_f16_s16(input2);
+
+ // Compute fast atan2
+ const float16x8_t angle = atan2_0_180(inputx_f16, inputy_f16);
+
+ return vqmovun_s16(vcvtq_s16_f16(vaddq_f16(angle, zeropointfive)));
+}
+
+template <MagnitudeType mag_type>
+inline int16x8x2_t compute_magnitude(const int16x8x2_t &in0, const int16x8x2_t &gx);
+
+template <>
+inline int16x8x2_t compute_magnitude<MagnitudeType::L2NORM>(const int16x8x2_t &in0, const int16x8x2_t &gx)
+{
+ const int16x8x2_t mag =
+ {
+ magnitude_l2(in0.val[0], gx.val[0]),
+ magnitude_l2(in0.val[1], gx.val[1])
+ };
+
+ return mag;
+}
+
+template <>
+inline int16x8x2_t compute_magnitude<MagnitudeType::L1NORM>(const int16x8x2_t &in0, const int16x8x2_t &gx)
+{
+ const int16x8x2_t mag =
+ {
+ magnitude_l1(in0.val[0], gx.val[0]),
+ magnitude_l1(in0.val[1], gx.val[1])
+ };
+
+ return mag;
+}
+
+template <PhaseType phase_type>
+inline uint8x16_t compute_phase(const int16x8x2_t &in0, const int16x8x2_t &gx);
+
+template <>
+inline uint8x16_t compute_phase<PhaseType::SIGNED>(const int16x8x2_t &in0, const int16x8x2_t &gx)
+{
+ return vcombine_u8(phase_signed(in0.val[0], gx.val[0]),
+ phase_signed(in0.val[1], gx.val[1]));
+}
+
+template <>
+inline uint8x16_t compute_phase<PhaseType::UNSIGNED>(const int16x8x2_t &in0, const int16x8x2_t &gx)
+{
+ return vcombine_u8(phase_unsigned(in0.val[0], gx.val[0]),
+ phase_unsigned(in0.val[1], gx.val[1]));
+}
+} // namespace fp16
+
+template <MagnitudeType mag_type, PhaseType phase_type>
+NEMagnitudePhaseFP16Kernel<mag_type, phase_type>::NEMagnitudePhaseFP16Kernel()
+ : _func(nullptr), _gx(nullptr), _gy(nullptr), _magnitude(nullptr), _phase(nullptr)
+{
+}
+
+template <MagnitudeType mag_type, PhaseType phase_type>
+void NEMagnitudePhaseFP16Kernel<mag_type, phase_type>::configure(const ITensor *gx, const ITensor *gy, ITensor *magnitude, ITensor *phase)
+{
+ ARM_COMPUTE_ERROR_ON_FORMAT_NOT_IN(gx, Format::S16);
+ ARM_COMPUTE_ERROR_ON_FORMAT_NOT_IN(gy, Format::S16);
+ ARM_COMPUTE_ERROR_ON((nullptr == magnitude) && (nullptr == phase));
+
+ const bool run_mag = magnitude != nullptr;
+ const bool run_phase = phase != nullptr;
+
+ if(run_mag)
+ {
+ ARM_COMPUTE_ERROR_ON_FORMAT_NOT_IN(magnitude, Format::S16);
+ }
+
+ if(run_phase)
+ {
+ ARM_COMPUTE_ERROR_ON_FORMAT_NOT_IN(phase, Format::U8);
+ }
+
+ _gx = gx;
+ _gy = gy;
+ _magnitude = magnitude;
+ _phase = phase;
+
+ if(run_mag && run_phase)
+ {
+ /* Run magnitude and phase */
+ _func = &NEMagnitudePhaseFP16Kernel<mag_type, phase_type>::magnitude_phase;
+ }
+ else if(run_mag)
+ {
+ /* Run magnitude */
+ _func = &NEMagnitudePhaseFP16Kernel<mag_type, phase_type>::magnitude;
+ }
+ else if(run_phase)
+ {
+ /* Run phase */
+ _func = &NEMagnitudePhaseFP16Kernel<mag_type, phase_type>::phase;
+ }
+ else
+ {
+ ARM_COMPUTE_ERROR("At least one output must be NOT NULL");
+ }
+
+ const unsigned int num_elems_processed_per_iteration = 16;
+
+ // Configure kernel window
+ Window win = calculate_max_window(*gx->info(), Steps(num_elems_processed_per_iteration));
+ AccessWindowHorizontal magnitude_access(magnitude == nullptr ? nullptr : magnitude->info(), 0, num_elems_processed_per_iteration);
+ AccessWindowHorizontal phase_access(phase == nullptr ? nullptr : phase->info(), 0, num_elems_processed_per_iteration);
+
+ update_window_and_padding(win,
+ AccessWindowHorizontal(gx->info(), 0, num_elems_processed_per_iteration),
+ AccessWindowHorizontal(gy->info(), 0, num_elems_processed_per_iteration),
+ magnitude_access,
+ phase_access);
+
+ ValidRegion valid_region = intersect_valid_regions(gx->info()->valid_region(),
+ gy->info()->valid_region());
+
+ magnitude_access.set_valid_region(win, valid_region);
+ phase_access.set_valid_region(win, valid_region);
+
+ INEKernel::configure(win);
+}
+
+template <MagnitudeType mag_type, PhaseType phase_type>
+void NEMagnitudePhaseFP16Kernel<mag_type, phase_type>::magnitude(const Window &window)
+{
+ Iterator gx(_gx, window);
+ Iterator gy(_gy, window);
+ Iterator magnitude(_magnitude, window);
+
+ execute_window_loop(window, [&](const Coordinates & id)
+ {
+ const int16x8x2_t input1 =
+ {
+ vld1q_s16(reinterpret_cast<int16_t *>(gx.ptr())),
+ vld1q_s16(reinterpret_cast<int16_t *>(gx.ptr()) + 8)
+ };
+
+ const int16x8x2_t input2 =
+ {
+ vld1q_s16(reinterpret_cast<int16_t *>(gy.ptr())),
+ vld1q_s16(reinterpret_cast<int16_t *>(gy.ptr()) + 8)
+ };
+
+ // Compute and store magnitude
+ const int16x8x2_t mag = fp16::compute_magnitude<mag_type>(input1, input2);
+
+ /* Store magnitude */
+ vst1q_s16(reinterpret_cast<int16_t *>(magnitude.ptr()), mag.val[0]);
+ vst1q_s16(reinterpret_cast<int16_t *>(magnitude.ptr()) + 8, mag.val[1]);
+ },
+ gx, gy, magnitude);
+}
+
+template <MagnitudeType mag_type, PhaseType phase_type>
+void NEMagnitudePhaseFP16Kernel<mag_type, phase_type>::phase(const Window &window)
+{
+ Iterator gx(_gx, window);
+ Iterator gy(_gy, window);
+ Iterator phase(_phase, window);
+
+ execute_window_loop(window, [&](const Coordinates & id)
+ {
+ const int16x8x2_t input1 =
+ {
+ vld1q_s16(reinterpret_cast<int16_t *>(gx.ptr())),
+ vld1q_s16(reinterpret_cast<int16_t *>(gx.ptr()) + 8)
+ };
+
+ const int16x8x2_t input2 =
+ {
+ vld1q_s16(reinterpret_cast<int16_t *>(gy.ptr())),
+ vld1q_s16(reinterpret_cast<int16_t *>(gy.ptr()) + 8)
+ };
+
+ // Compute and store phase
+ vst1q_u8(phase.ptr(), fp16::compute_phase<phase_type>(input1, input2));
+ },
+ gx, gy, phase);
+}
+
+template <MagnitudeType mag_type, PhaseType phase_type>
+void NEMagnitudePhaseFP16Kernel<mag_type, phase_type>::magnitude_phase(const Window &window)
+{
+ Iterator gx(_gx, window);
+ Iterator gy(_gy, window);
+ Iterator magnitude(_magnitude, window);
+ Iterator phase(_phase, window);
+
+ execute_window_loop(window, [&](const Coordinates & id)
+ {
+ const int16x8x2_t input1 =
+ {
+ vld1q_s16(reinterpret_cast<int16_t *>(gx.ptr())),
+ vld1q_s16(reinterpret_cast<int16_t *>(gx.ptr()) + 8)
+ };
+
+ const int16x8x2_t input2 =
+ {
+ vld1q_s16(reinterpret_cast<int16_t *>(gy.ptr())),
+ vld1q_s16(reinterpret_cast<int16_t *>(gy.ptr()) + 8)
+ };
+
+ // Compute and store magnitude
+ const int16x8x2_t mag = fp16::compute_magnitude<mag_type>(input1, input2);
+
+ vst1q_s16(reinterpret_cast<int16_t *>(magnitude.ptr()), mag.val[0]);
+ vst1q_s16(reinterpret_cast<int16_t *>(magnitude.ptr()) + 8, mag.val[1]);
+
+ // Compute and store phase
+ vst1q_u8(phase.ptr(), fp16::compute_phase<phase_type>(input1, input2));
+ },
+ gx, gy, magnitude, phase);
+}
+
+template <MagnitudeType mag_type, PhaseType phase_type>
+void NEMagnitudePhaseFP16Kernel<mag_type, phase_type>::run(const Window &window)
+{
+ ARM_COMPUTE_ERROR_ON_UNCONFIGURED_KERNEL(this);
+ ARM_COMPUTE_ERROR_ON_INVALID_SUBWINDOW(INEKernel::window(), window);
+ ARM_COMPUTE_ERROR_ON(_func == nullptr);
+
+ (this->*_func)(window);
+}
+
+template class arm_compute::NEMagnitudePhaseFP16Kernel<MagnitudeType::L1NORM, PhaseType::SIGNED>;
+template class arm_compute::NEMagnitudePhaseFP16Kernel<MagnitudeType::L2NORM, PhaseType::SIGNED>;
+template class arm_compute::NEMagnitudePhaseFP16Kernel<MagnitudeType::L1NORM, PhaseType::UNSIGNED>;
+template class arm_compute::NEMagnitudePhaseFP16Kernel<MagnitudeType::L2NORM, PhaseType::UNSIGNED>;
+#endif
+
+namespace
+{
+inline float32x4_t inv(float32x4_t x)
+{
+ float32x4_t result = vrecpeq_f32(x);
+ result = vmulq_f32(vrecpsq_f32(x, result), result);
+ return result;
+}
+
+inline float32x4_t atan2_0_360(float32x4_t gx, float32x4_t gy)
+{
+ const float32x4_t zero = vdupq_n_f32(0.0f);
+ const float32x4_t epsilon = vdupq_n_f32(1e-9f);
+ const float32x4_t piover4 = vdupq_n_f32(PI_4);
+ const float32x4_t coeff1 = vdupq_n_f32(COEFF1);
+ const float32x4_t coeff2 = vdupq_n_f32(COEFF2);
+ const float32x4_t ninety = vdupq_n_f32(90.0f * SCALE_FACTOR);
+ const float32x4_t oneeighty = vdupq_n_f32(180.0f * SCALE_FACTOR);
+ const float32x4_t threesixty = vdupq_n_f32(360.0f * SCALE_FACTOR);
+ const float32x4_t scale = vdupq_n_f32(SCALE_360);
+
+ float32x4_t abs_gx = vabsq_f32(gx);
+ float32x4_t abs_gy = vabsq_f32(gy);
+ float32x4_t tmin = vminq_f32(abs_gx, abs_gy);
+ float32x4_t tmax = vmaxq_f32(abs_gx, abs_gy);
+ float32x4_t z = vmulq_f32(tmin, inv(vaddq_f32(tmax, epsilon)));
+ float32x4_t absz = vabsq_f32(z);
+ float32x4_t term = vmulq_f32(z, vsubq_f32(vdupq_n_f32(1.0f), absz));
+
+ /* Compute y = pi/4 * x - x*(abs(x)-1)*(0.2447+0.0663 * abs(x) */
+ float32x4_t result = vaddq_f32(coeff2, vmulq_f32(absz, coeff1));
+ result = vmulq_f32(result, term);
+ result = vmlaq_f32(result, piover4, z);
+
+ /* Radians to degrees conversion with applied a scale factor in order to have the result [0, 255] */
+ result = vmulq_f32(result, scale);
+
+ /* If z > 1, result = 90 - result */
+ result = vbslq_f32(vcgeq_f32(abs_gx, abs_gy), result, vsubq_f32(ninety, result));
+
+ /* Choose correct quadrant */
+ result = vbslq_f32(vcltq_f32(gx, zero), vsubq_f32(oneeighty, result), result);
+ result = vbslq_f32(vcltq_f32(gy, zero), vsubq_f32(threesixty, result), result);
+
+ return result;
+}
+
+inline float32x4_t atan2_0_180(float32x4_t gx, float32x4_t gy)
+{
+ const float32x4_t zero = vdupq_n_f32(0.0f);
+ const float32x4_t epsilon = vdupq_n_f32(1e-9f); // epsilon used to avoiding division by 0
+ const float32x4_t piover4 = vdupq_n_f32(PI_4);
+ const float32x4_t coeff1 = vdupq_n_f32(COEFF1);
+ const float32x4_t coeff2 = vdupq_n_f32(COEFF2);
+ const float32x4_t ninety = vdupq_n_f32(90.0f);
+ const float32x4_t oneeighty = vdupq_n_f32(180.0f);
+ const float32x4_t threesixty = vdupq_n_f32(360.0f);
+ const float32x4_t scale = vdupq_n_f32(SCALE_180);
+
+ float32x4_t abs_gx = vabsq_f32(gx);
+ float32x4_t abs_gy = vabsq_f32(gy);
+ float32x4_t tmin = vminq_f32(abs_gx, abs_gy);
+ float32x4_t tmax = vmaxq_f32(abs_gx, abs_gy);
+ float32x4_t z = vmulq_f32(tmin, inv(vaddq_f32(tmax, epsilon)));
+ float32x4_t absz = vabsq_f32(z);
+
+ /* Compute y = pi/4 * z - z*(abs(z)-1)*(0.2447+0.0663 * abs(z) */
+ float32x4_t term = vmulq_f32(z, vsubq_f32(vdupq_n_f32(1.0f), absz));
+ float32x4_t result = vaddq_f32(coeff2, vmulq_f32(absz, coeff1));
+ result = vmulq_f32(result, term);
+ result = vmlaq_f32(result, piover4, z);
+
+ /* Radians to degrees conversion */
+ result = vmulq_f32(result, scale);
+
+ /* If z > 1, result = 90 - result */
+ result = vbslq_f32(vcgeq_f32(abs_gx, abs_gy), result, vsubq_f32(ninety, result));
+
+ /* Choose correct quadrant */
+ result = vbslq_f32(vcltq_f32(gx, zero), vsubq_f32(oneeighty, result), result);
+ result = vbslq_f32(vcltq_f32(gy, zero), vsubq_f32(threesixty, result), result);
+ result = vbslq_f32(vcgtq_f32(result, oneeighty), vsubq_f32(result, oneeighty), result);
+
+ return result;
+}
+
+inline float32x4_t invsqrtv(float32x4_t x)
+{
+ float32x4_t sqrt_reciprocal = vrsqrteq_f32(x);
+
+ sqrt_reciprocal = vmulq_f32(vrsqrtsq_f32(vmulq_f32(x, sqrt_reciprocal), sqrt_reciprocal),
+ sqrt_reciprocal);
+ sqrt_reciprocal = vmulq_f32(vrsqrtsq_f32(vmulq_f32(x, sqrt_reciprocal), sqrt_reciprocal),
+ sqrt_reciprocal);
+
+ return sqrt_reciprocal;
+}
+
+inline float32x4_t sqrtv(float32x4_t x)
+{
+ float32x4_t res = vdupq_n_f32(0.5f);
+ return vmlaq_f32(res, x, invsqrtv(x));
+}
+
+inline int16x8_t magnitude_l2(int16x8_t input1, int16x8_t input2)
+{
+ const int32x4x2_t square_x =
+ {
+ {
+ vmull_s16(vget_low_s16(input1), vget_low_s16(input1)),
+ vmull_s16(vget_high_s16(input1), vget_high_s16(input1))
+ }
+ };
+
+ const int32x4x2_t square_y =
+ {
+ {
+ vmull_s16(vget_low_s16(input2), vget_low_s16(input2)),
+ vmull_s16(vget_high_s16(input2), vget_high_s16(input2))
+ }
+ };
+
+ const uint32x4x2_t sum =
+ {
+ {
+ vaddq_u32(vreinterpretq_u32_s32(square_x.val[0]), vreinterpretq_u32_s32(square_y.val[0])),
+ vaddq_u32(vreinterpretq_u32_s32(square_x.val[1]), vreinterpretq_u32_s32(square_y.val[1]))
+ }
+ };
+
+ const float32x4x2_t res =
+ {
+ {
+ sqrtv(vcvtq_f32_u32(sum.val[0])),
+ sqrtv(vcvtq_f32_u32(sum.val[1]))
+ }
+ };
+
+ return vcombine_s16(vqmovn_s32(vcvtq_s32_f32(res.val[0])),
+ vqmovn_s32(vcvtq_s32_f32(res.val[1])));
+}
+
+inline int16x8_t magnitude_l1(int16x8_t input1, int16x8_t input2)
+{
+ int16x8_t gx_abs = vabsq_s16(input1);
+ int16x8_t gy_abs = vabsq_s16(input2);
+
+ /* Saturating add */
+ return vqaddq_s16(gx_abs, gy_abs);
+}
+
+inline uint8x8_t phase_signed(int16x8_t input1, int16x8_t input2)
+{
+ const float32x4_t zeropointfive = vdupq_n_f32(0.5f);
+
+ float32x4_t inputx_f32_high = vcvtq_f32_s32(vmovl_s16(vget_high_s16(input1)));
+ float32x4_t inputx_f32_low = vcvtq_f32_s32(vmovl_s16(vget_low_s16(input1)));
+ float32x4_t inputy_f32_high = vcvtq_f32_s32(vmovl_s16(vget_high_s16(input2)));
+ float32x4_t inputy_f32_low = vcvtq_f32_s32(vmovl_s16(vget_low_s16(input2)));
+
+ /* Compute fast atan2 */
+ float32x4_t angle_high = atan2_0_360(inputx_f32_high, inputy_f32_high);
+ float32x4_t angle_low = atan2_0_360(inputx_f32_low, inputy_f32_low);
+
+ angle_high = vaddq_f32(angle_high, zeropointfive);
+ angle_low = vaddq_f32(angle_low, zeropointfive);
+
+ return vmovn_u16(vcombine_u16(vqmovun_s32(vcvtq_s32_f32(angle_low)),
+ vqmovun_s32(vcvtq_s32_f32(angle_high))));
+}
+
+inline uint8x8_t phase_unsigned(int16x8_t input1, int16x8_t input2)
+{
+ const float32x4_t zeropointfive = vdupq_n_f32(0.5f);
+
+ float32x4_t inputx_f32_high = vcvtq_f32_s32(vmovl_s16(vget_high_s16(input1)));
+ float32x4_t inputx_f32_low = vcvtq_f32_s32(vmovl_s16(vget_low_s16(input1)));
+ float32x4_t inputy_f32_high = vcvtq_f32_s32(vmovl_s16(vget_high_s16(input2)));
+ float32x4_t inputy_f32_low = vcvtq_f32_s32(vmovl_s16(vget_low_s16(input2)));
+
+ /* Compute fast atan2 */
+ float32x4_t angle_high = atan2_0_180(inputx_f32_high, inputy_f32_high);
+ float32x4_t angle_low = atan2_0_180(inputx_f32_low, inputy_f32_low);
+
+ angle_high = vaddq_f32(angle_high, zeropointfive);
+ angle_low = vaddq_f32(angle_low, zeropointfive);
+
+ return vmovn_u16(vcombine_u16(vqmovun_s32(vcvtq_s32_f32(angle_low)),
+ vqmovun_s32(vcvtq_s32_f32(angle_high))));
+}
+} // namespace
+
+template <MagnitudeType mag_type, PhaseType phase_type>
+NEMagnitudePhaseKernel<mag_type, phase_type>::NEMagnitudePhaseKernel()
+ : _func(nullptr), _gx(nullptr), _gy(nullptr), _magnitude(nullptr), _phase(nullptr)
+{
+}
+
+template <MagnitudeType mag_type, PhaseType phase_type>
+void NEMagnitudePhaseKernel<mag_type, phase_type>::configure(const ITensor *gx, const ITensor *gy, ITensor *magnitude, ITensor *phase)
+{
+ ARM_COMPUTE_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(gx, 1, DataType::S16);
+ ARM_COMPUTE_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(gy, 1, DataType::S16);
+ ARM_COMPUTE_ERROR_ON((nullptr == magnitude) && (nullptr == phase));
+
+ const bool run_mag = magnitude != nullptr;
+ const bool run_phase = phase != nullptr;
+
+ if(run_mag)
+ {
+ ARM_COMPUTE_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(magnitude, 1, DataType::S16);
+ }
+
+ if(run_phase)
+ {
+ ARM_COMPUTE_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(phase, 1, DataType::U8);
+ }
+
+ _gx = gx;
+ _gy = gy;
+ _magnitude = magnitude;
+ _phase = phase;
+
+ if(run_mag && run_phase)
+ {
+ /* Run magnitude and phase */
+ _func = &NEMagnitudePhaseKernel<mag_type, phase_type>::magnitude_phase;
+ }
+ else
+ {
+ if(run_mag)
+ {
+ /* Run magnitude */
+ _func = &NEMagnitudePhaseKernel<mag_type, phase_type>::magnitude;
+ }
+ else if(run_phase)
+ {
+ /* Run phase */
+ _func = &NEMagnitudePhaseKernel<mag_type, phase_type>::phase;
+ }
+ else
+ {
+ ARM_COMPUTE_ERROR("At least one output must be NOT NULL");
+ }
+ }
+
+ constexpr unsigned int num_elems_processed_per_iteration = 16;
+
+ // Configure kernel window
+ Window win = calculate_max_window(*gx->info(), Steps(num_elems_processed_per_iteration));
+ AccessWindowHorizontal magnitude_access(magnitude == nullptr ? nullptr : magnitude->info(), 0, num_elems_processed_per_iteration);
+ AccessWindowHorizontal phase_access(phase == nullptr ? nullptr : phase->info(), 0, num_elems_processed_per_iteration);
+
+ update_window_and_padding(win,
+ AccessWindowHorizontal(gx->info(), 0, num_elems_processed_per_iteration),
+ AccessWindowHorizontal(gy->info(), 0, num_elems_processed_per_iteration),
+ magnitude_access,
+ phase_access);
+
+ ValidRegion valid_region = intersect_valid_regions(gx->info()->valid_region(),
+ gy->info()->valid_region());
+
+ magnitude_access.set_valid_region(win, valid_region);
+ phase_access.set_valid_region(win, valid_region);
+
+ INEKernel::configure(win);
+}
+
+template <MagnitudeType mag_type, PhaseType phase_type>
+void NEMagnitudePhaseKernel<mag_type, phase_type>::magnitude(const Window &window)
+{
+ Iterator gx(_gx, window);
+ Iterator gy(_gy, window);
+ Iterator magnitude(_magnitude, window);
+
+ execute_window_loop(window, [&](const Coordinates & id)
+ {
+ const int16x8x2_t input1 =
+ {
+ {
+ vld1q_s16(reinterpret_cast<int16_t *>(gx.ptr())),
+ vld1q_s16(reinterpret_cast<int16_t *>(gx.ptr()) + 8)
+ }
+ };
+
+ const int16x8x2_t input2 =
+ {
+ {
+ vld1q_s16(reinterpret_cast<int16_t *>(gy.ptr())),
+ vld1q_s16(reinterpret_cast<int16_t *>(gy.ptr()) + 8)
+ }
+ };
+
+ /* Compute magnitude */
+ int16x8x2_t mag{ {} };
+
+ if(MagnitudeType::L2NORM == mag_type)
+ {
+ mag.val[0] = magnitude_l2(input1.val[0], input2.val[0]);
+ mag.val[1] = magnitude_l2(input1.val[1], input2.val[1]);
+ }
+ else
+ {
+ mag.val[0] = magnitude_l1(input1.val[0], input2.val[0]);
+ mag.val[1] = magnitude_l1(input1.val[1], input2.val[1]);
+ }
+
+ /* Store magnitude */
+ vst1q_s16(reinterpret_cast<int16_t *>(magnitude.ptr()), mag.val[0]);
+ vst1q_s16(reinterpret_cast<int16_t *>(magnitude.ptr()) + 8, mag.val[1]);
+ },
+ gx, gy, magnitude);
+}
+
+template <MagnitudeType mag_type, PhaseType phase_type>
+void NEMagnitudePhaseKernel<mag_type, phase_type>::phase(const Window &window)
+{
+ Iterator gx(_gx, window);
+ Iterator gy(_gy, window);
+ Iterator phase(_phase, window);
+
+ execute_window_loop(window, [&](const Coordinates & id)
+ {
+ const int16x8x2_t input1 =
+ {
+ {
+ vld1q_s16(reinterpret_cast<int16_t *>(gx.ptr())),
+ vld1q_s16(reinterpret_cast<int16_t *>(gx.ptr()) + 8)
+ }
+ };
+
+ const int16x8x2_t input2 =
+ {
+ {
+ vld1q_s16(reinterpret_cast<int16_t *>(gy.ptr())),
+ vld1q_s16(reinterpret_cast<int16_t *>(gy.ptr()) + 8)
+ }
+ };
+
+ /* Compute phase */
+ uint8x8x2_t vphase{ {} };
+
+ if(PhaseType::SIGNED == phase_type)
+ {
+ vphase.val[0] = phase_signed(input1.val[0], input2.val[0]);
+ vphase.val[1] = phase_signed(input1.val[1], input2.val[1]);
+ }
+ else
+ {
+ vphase.val[0] = phase_unsigned(input1.val[0], input2.val[0]);
+ vphase.val[1] = phase_unsigned(input1.val[1], input2.val[1]);
+ }
+
+ /* Store phase */
+ vst1q_u8(phase.ptr(), vcombine_u8(vphase.val[0], vphase.val[1]));
+ },
+ gx, gy, phase);
+}
+
+template <MagnitudeType mag_type, PhaseType phase_type>
+void NEMagnitudePhaseKernel<mag_type, phase_type>::magnitude_phase(const Window &window)
+{
+ Iterator gx(_gx, window);
+ Iterator gy(_gy, window);
+ Iterator magnitude(_magnitude, window);
+ Iterator phase(_phase, window);
+
+ execute_window_loop(window, [&](const Coordinates & id)
+ {
+ const int16x8x2_t input1 =
+ {
+ {
+ vld1q_s16(reinterpret_cast<int16_t *>(gx.ptr())),
+ vld1q_s16(reinterpret_cast<int16_t *>(gx.ptr()) + 8)
+ }
+ };
+
+ const int16x8x2_t input2 =
+ {
+ {
+ vld1q_s16(reinterpret_cast<int16_t *>(gy.ptr())),
+ vld1q_s16(reinterpret_cast<int16_t *>(gy.ptr()) + 8)
+ }
+ };
+
+ /* Compute magnitude */
+ int16x8x2_t mag{ {} };
+
+ if(MagnitudeType::L2NORM == mag_type)
+ {
+ mag.val[0] = magnitude_l2(input1.val[0], input2.val[0]);
+ mag.val[1] = magnitude_l2(input1.val[1], input2.val[1]);
+ }
+ else
+ {
+ mag.val[0] = magnitude_l1(input1.val[0], input2.val[0]);
+ mag.val[1] = magnitude_l1(input1.val[1], input2.val[1]);
+ }
+
+ /* Store magnitude */
+ vst1q_s16(reinterpret_cast<int16_t *>(magnitude.ptr()), mag.val[0]);
+ vst1q_s16(reinterpret_cast<int16_t *>(magnitude.ptr()) + 8, mag.val[1]);
+
+ /* Compute phase */
+ uint8x8x2_t vphase{ {} };
+
+ if(PhaseType::SIGNED == phase_type)
+ {
+ vphase.val[0] = phase_signed(input1.val[0], input2.val[0]);
+ vphase.val[1] = phase_signed(input1.val[1], input2.val[1]);
+ }
+ else
+ {
+ vphase.val[0] = phase_unsigned(input1.val[0], input2.val[0]);
+ vphase.val[1] = phase_unsigned(input1.val[1], input2.val[1]);
+ }
+
+ /* Store phase */
+ vst1q_u8(phase.ptr(), vcombine_u8(vphase.val[0], vphase.val[1]));
+ },
+ gx, gy, magnitude, phase);
+}
+
+template <MagnitudeType mag_type, PhaseType phase_type>
+void NEMagnitudePhaseKernel<mag_type, phase_type>::run(const Window &window)
+{
+ ARM_COMPUTE_ERROR_ON_UNCONFIGURED_KERNEL(this);
+ ARM_COMPUTE_ERROR_ON_INVALID_SUBWINDOW(INEKernel::window(), window);
+ ARM_COMPUTE_ERROR_ON(_func == nullptr);
+
+ (this->*_func)(window);
+}
+
+template class arm_compute::NEMagnitudePhaseKernel<MagnitudeType::L1NORM, PhaseType::SIGNED>;
+template class arm_compute::NEMagnitudePhaseKernel<MagnitudeType::L2NORM, PhaseType::SIGNED>;
+template class arm_compute::NEMagnitudePhaseKernel<MagnitudeType::L1NORM, PhaseType::UNSIGNED>;
+template class arm_compute::NEMagnitudePhaseKernel<MagnitudeType::L2NORM, PhaseType::UNSIGNED>;