• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Copyright 2016 The TensorFlow Authors. All Rights Reserved.
2 
3 Licensed under the Apache License, Version 2.0 (the "License");
4 you may not use this file except in compliance with the License.
5 You may obtain a copy of the License at
6 
7     http://www.apache.org/licenses/LICENSE-2.0
8 
9 Unless required by applicable law or agreed to in writing, software
10 distributed under the License is distributed on an "AS IS" BASIS,
11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 See the License for the specific language governing permissions and
13 limitations under the License.
14 ==============================================================================*/
15 
16 // NEON implementations of Image methods for compatible devices.  Control
17 // should never enter this compilation unit on incompatible devices.
18 
19 #ifdef __ARM_NEON
20 
21 #include <arm_neon.h>
22 
23 #include "tensorflow/tools/android/test/jni/object_tracking/geom.h"
24 #include "tensorflow/tools/android/test/jni/object_tracking/image-inl.h"
25 #include "tensorflow/tools/android/test/jni/object_tracking/image.h"
26 #include "tensorflow/tools/android/test/jni/object_tracking/utils.h"
27 
28 namespace tf_tracking {
29 
GetSum(const float32x4_t & values)30 inline static float GetSum(const float32x4_t& values) {
31   static float32_t summed_values[4];
32   vst1q_f32(summed_values, values);
33   return summed_values[0]
34        + summed_values[1]
35        + summed_values[2]
36        + summed_values[3];
37 }
38 
39 
ComputeMeanNeon(const float * const values,const int num_vals)40 float ComputeMeanNeon(const float* const values, const int num_vals) {
41   SCHECK(num_vals >= 8, "Not enough values to merit NEON: %d", num_vals);
42 
43   const float32_t* const arm_vals = (const float32_t* const) values;
44   float32x4_t accum = vdupq_n_f32(0.0f);
45 
46   int offset = 0;
47   for (; offset <= num_vals - 4; offset += 4) {
48     accum = vaddq_f32(accum, vld1q_f32(&arm_vals[offset]));
49   }
50 
51   // Pull the accumulated values into a single variable.
52   float sum = GetSum(accum);
53 
54   // Get the remaining 1 to 3 values.
55   for (; offset < num_vals; ++offset) {
56     sum += values[offset];
57   }
58 
59   const float mean_neon = sum / static_cast<float>(num_vals);
60 
61 #ifdef SANITY_CHECKS
62   const float mean_cpu = ComputeMeanCpu(values, num_vals);
63   SCHECK(NearlyEqual(mean_neon, mean_cpu, EPSILON * num_vals),
64         "Neon mismatch with CPU mean! %.10f vs %.10f",
65         mean_neon, mean_cpu);
66 #endif
67 
68   return mean_neon;
69 }
70 
71 
ComputeStdDevNeon(const float * const values,const int num_vals,const float mean)72 float ComputeStdDevNeon(const float* const values,
73                         const int num_vals, const float mean) {
74   SCHECK(num_vals >= 8, "Not enough values to merit NEON: %d", num_vals);
75 
76   const float32_t* const arm_vals = (const float32_t* const) values;
77   const float32x4_t mean_vec = vdupq_n_f32(-mean);
78 
79   float32x4_t accum = vdupq_n_f32(0.0f);
80 
81   int offset = 0;
82   for (; offset <= num_vals - 4; offset += 4) {
83     const float32x4_t deltas =
84         vaddq_f32(mean_vec, vld1q_f32(&arm_vals[offset]));
85 
86     accum = vmlaq_f32(accum, deltas, deltas);
87   }
88 
89   // Pull the accumulated values into a single variable.
90   float squared_sum = GetSum(accum);
91 
92   // Get the remaining 1 to 3 values.
93   for (; offset < num_vals; ++offset) {
94     squared_sum += Square(values[offset] - mean);
95   }
96 
97   const float std_dev_neon = sqrt(squared_sum / static_cast<float>(num_vals));
98 
99 #ifdef SANITY_CHECKS
100   const float std_dev_cpu = ComputeStdDevCpu(values, num_vals, mean);
101   SCHECK(NearlyEqual(std_dev_neon, std_dev_cpu, EPSILON * num_vals),
102         "Neon mismatch with CPU std dev! %.10f vs %.10f",
103         std_dev_neon, std_dev_cpu);
104 #endif
105 
106   return std_dev_neon;
107 }
108 
109 
ComputeCrossCorrelationNeon(const float * const values1,const float * const values2,const int num_vals)110 float ComputeCrossCorrelationNeon(const float* const values1,
111                                   const float* const values2,
112                                   const int num_vals) {
113   SCHECK(num_vals >= 8, "Not enough values to merit NEON: %d", num_vals);
114 
115   const float32_t* const arm_vals1 = (const float32_t* const) values1;
116   const float32_t* const arm_vals2 = (const float32_t* const) values2;
117 
118   float32x4_t accum = vdupq_n_f32(0.0f);
119 
120   int offset = 0;
121   for (; offset <= num_vals - 4; offset += 4) {
122     accum = vmlaq_f32(accum,
123                       vld1q_f32(&arm_vals1[offset]),
124                       vld1q_f32(&arm_vals2[offset]));
125   }
126 
127   // Pull the accumulated values into a single variable.
128   float sxy = GetSum(accum);
129 
130   // Get the remaining 1 to 3 values.
131   for (; offset < num_vals; ++offset) {
132     sxy += values1[offset] * values2[offset];
133   }
134 
135   const float cross_correlation_neon = sxy / num_vals;
136 
137 #ifdef SANITY_CHECKS
138   const float cross_correlation_cpu =
139       ComputeCrossCorrelationCpu(values1, values2, num_vals);
140   SCHECK(NearlyEqual(cross_correlation_neon, cross_correlation_cpu,
141                     EPSILON * num_vals),
142         "Neon mismatch with CPU cross correlation! %.10f vs %.10f",
143         cross_correlation_neon, cross_correlation_cpu);
144 #endif
145 
146   return cross_correlation_neon;
147 }
148 
149 }  // namespace tf_tracking
150 
151 #endif  // __ARM_NEON
152