• 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 #ifndef TENSORFLOW_EXAMPLES_ANDROID_JNI_OBJECT_TRACKING_IMAGE_UTILS_H_
17 #define TENSORFLOW_EXAMPLES_ANDROID_JNI_OBJECT_TRACKING_IMAGE_UTILS_H_
18 
19 #include <stdint.h>
20 
21 #include "tensorflow/tools/android/test/jni/object_tracking/geom.h"
22 #include "tensorflow/tools/android/test/jni/object_tracking/image-inl.h"
23 #include "tensorflow/tools/android/test/jni/object_tracking/image.h"
24 #include "tensorflow/tools/android/test/jni/object_tracking/utils.h"
25 
26 namespace tf_tracking {
27 
GetUV(const uint8_t * const input,Image<uint8_t> * const u,Image<uint8_t> * const v)28 inline void GetUV(const uint8_t* const input, Image<uint8_t>* const u,
29                   Image<uint8_t>* const v) {
30   const uint8_t* pUV = input;
31 
32   for (int row = 0; row < u->GetHeight(); ++row) {
33     uint8_t* u_curr = (*u)[row];
34     uint8_t* v_curr = (*v)[row];
35     for (int col = 0; col < u->GetWidth(); ++col) {
36 #ifdef __APPLE__
37       *u_curr++ = *pUV++;
38       *v_curr++ = *pUV++;
39 #else
40       *v_curr++ = *pUV++;
41       *u_curr++ = *pUV++;
42 #endif
43     }
44   }
45 }
46 
47 // Marks every point within a circle of a given radius on the given boolean
48 // image true.
49 template <typename U>
MarkImage(const int x,const int y,const int radius,Image<U> * const img)50 inline static void MarkImage(const int x, const int y, const int radius,
51                              Image<U>* const img) {
52   SCHECK(img->ValidPixel(x, y), "Marking invalid pixel in image! %d, %d", x, y);
53 
54   // Precomputed for efficiency.
55   const int squared_radius = Square(radius);
56 
57   // Mark every row in the circle.
58   for (int d_y = 0; d_y <= radius; ++d_y) {
59     const int squared_y_dist = Square(d_y);
60 
61     const int min_y = MAX(y - d_y, 0);
62     const int max_y = MIN(y + d_y, img->height_less_one_);
63 
64     // The max d_x of the circle must be strictly greater or equal to
65     // radius - d_y for any positive d_y. Thus, starting from radius - d_y will
66     // reduce the number of iterations required as compared to starting from
67     // either 0 and counting up or radius and counting down.
68     for (int d_x = radius - d_y; d_x <= radius; ++d_x) {
69       // The first time this criteria is met, we know the width of the circle at
70       // this row (without using sqrt).
71       if (squared_y_dist + Square(d_x) >= squared_radius) {
72         const int min_x = MAX(x - d_x, 0);
73         const int max_x = MIN(x + d_x, img->width_less_one_);
74 
75         // Mark both above and below the center row.
76         bool* const top_row_start = (*img)[min_y] + min_x;
77         bool* const bottom_row_start = (*img)[max_y] + min_x;
78 
79         const int x_width = max_x - min_x + 1;
80         memset(top_row_start, true, sizeof(*top_row_start) * x_width);
81         memset(bottom_row_start, true, sizeof(*bottom_row_start) * x_width);
82 
83         // This row is marked, time to move on to the next row.
84         break;
85       }
86     }
87   }
88 }
89 
90 #ifdef __ARM_NEON
91 void CalculateGNeon(
92     const float* const vals_x, const float* const vals_y,
93     const int num_vals, float* const G);
94 #endif
95 
96 // Puts the image gradient matrix about a pixel into the 2x2 float array G.
97 // vals_x should be an array of the window x gradient values, whose indices
98 // can be in any order but are parallel to the vals_y entries.
99 // See http://robots.stanford.edu/cs223b04/algo_tracking.pdf for more details.
CalculateG(const float * const vals_x,const float * const vals_y,const int num_vals,float * const G)100 inline void CalculateG(const float* const vals_x, const float* const vals_y,
101                        const int num_vals, float* const G) {
102 #ifdef __ARM_NEON
103   CalculateGNeon(vals_x, vals_y, num_vals, G);
104   return;
105 #endif
106 
107   // Non-accelerated version.
108   for (int i = 0; i < num_vals; ++i) {
109     G[0] += Square(vals_x[i]);
110     G[1] += vals_x[i] * vals_y[i];
111     G[3] += Square(vals_y[i]);
112   }
113 
114   // The matrix is symmetric, so this is a given.
115   G[2] = G[1];
116 }
117 
CalculateGInt16(const int16_t * const vals_x,const int16_t * const vals_y,const int num_vals,int * const G)118 inline void CalculateGInt16(const int16_t* const vals_x,
119                             const int16_t* const vals_y, const int num_vals,
120                             int* const G) {
121   // Non-accelerated version.
122   for (int i = 0; i < num_vals; ++i) {
123     G[0] += Square(vals_x[i]);
124     G[1] += vals_x[i] * vals_y[i];
125     G[3] += Square(vals_y[i]);
126   }
127 
128   // The matrix is symmetric, so this is a given.
129   G[2] = G[1];
130 }
131 
132 
133 // Puts the image gradient matrix about a pixel into the 2x2 float array G.
134 // Looks up interpolated pixels, then calls above method for implementation.
CalculateG(const int window_radius,const float center_x,const float center_y,const Image<int32_t> & I_x,const Image<int32_t> & I_y,float * const G)135 inline void CalculateG(const int window_radius, const float center_x,
136                        const float center_y, const Image<int32_t>& I_x,
137                        const Image<int32_t>& I_y, float* const G) {
138   SCHECK(I_x.ValidPixel(center_x, center_y), "Problem in calculateG!");
139 
140   // Hardcoded to allow for a max window radius of 5 (9 pixels x 9 pixels).
141   static const int kMaxWindowRadius = 5;
142   SCHECK(window_radius <= kMaxWindowRadius,
143         "Window %d > %d!", window_radius, kMaxWindowRadius);
144 
145   // Diameter of window is 2 * radius + 1 for center pixel.
146   static const int kWindowBufferSize =
147       (kMaxWindowRadius * 2 + 1) * (kMaxWindowRadius * 2 + 1);
148 
149   // Preallocate buffers statically for efficiency.
150   static int16_t vals_x[kWindowBufferSize];
151   static int16_t vals_y[kWindowBufferSize];
152 
153   const int src_left_fixed = RealToFixed1616(center_x - window_radius);
154   const int src_top_fixed = RealToFixed1616(center_y - window_radius);
155 
156   int16_t* vals_x_ptr = vals_x;
157   int16_t* vals_y_ptr = vals_y;
158 
159   const int window_size = 2 * window_radius + 1;
160   for (int y = 0; y < window_size; ++y) {
161     const int fp_y = src_top_fixed + (y << 16);
162 
163     for (int x = 0; x < window_size; ++x) {
164       const int fp_x = src_left_fixed + (x << 16);
165 
166       *vals_x_ptr++ = I_x.GetPixelInterpFixed1616(fp_x, fp_y);
167       *vals_y_ptr++ = I_y.GetPixelInterpFixed1616(fp_x, fp_y);
168     }
169   }
170 
171   int32_t g_temp[] = {0, 0, 0, 0};
172   CalculateGInt16(vals_x, vals_y, window_size * window_size, g_temp);
173 
174   for (int i = 0; i < 4; ++i) {
175     G[i] = g_temp[i];
176   }
177 }
178 
ImageCrossCorrelation(const Image<float> & image1,const Image<float> & image2,const int x_offset,const int y_offset)179 inline float ImageCrossCorrelation(const Image<float>& image1,
180                                    const Image<float>& image2,
181                                    const int x_offset, const int y_offset) {
182   SCHECK(image1.GetWidth() == image2.GetWidth() &&
183          image1.GetHeight() == image2.GetHeight(),
184         "Dimension mismatch! %dx%d vs %dx%d",
185         image1.GetWidth(), image1.GetHeight(),
186         image2.GetWidth(), image2.GetHeight());
187 
188   const int num_pixels = image1.GetWidth() * image1.GetHeight();
189   const float* data1 = image1.data();
190   const float* data2 = image2.data();
191   return ComputeCrossCorrelation(data1, data2, num_pixels);
192 }
193 
194 // Copies an arbitrary region of an image to another (floating point)
195 // image, scaling as it goes using bilinear interpolation.
CopyArea(const Image<uint8_t> & image,const BoundingBox & area_to_copy,Image<float> * const patch_image)196 inline void CopyArea(const Image<uint8_t>& image,
197                      const BoundingBox& area_to_copy,
198                      Image<float>* const patch_image) {
199   VLOG(2) << "Copying from: " << area_to_copy << std::endl;
200 
201   const int patch_width = patch_image->GetWidth();
202   const int patch_height = patch_image->GetHeight();
203 
204   const float x_dist_between_samples = patch_width > 0 ?
205       area_to_copy.GetWidth() / (patch_width - 1) : 0;
206 
207   const float y_dist_between_samples = patch_height > 0 ?
208       area_to_copy.GetHeight() / (patch_height - 1) : 0;
209 
210   for (int y_index = 0; y_index < patch_height; ++y_index) {
211     const float sample_y =
212         y_index * y_dist_between_samples + area_to_copy.top_;
213 
214     for (int x_index = 0; x_index < patch_width; ++x_index) {
215       const float sample_x =
216           x_index * x_dist_between_samples + area_to_copy.left_;
217 
218       if (image.ValidInterpPixel(sample_x, sample_y)) {
219         // TODO(andrewharp): Do area averaging when downsampling.
220         (*patch_image)[y_index][x_index] =
221             image.GetPixelInterp(sample_x, sample_y);
222       } else {
223         (*patch_image)[y_index][x_index] = -1.0f;
224       }
225     }
226   }
227 }
228 
229 
230 // Takes a floating point image and normalizes it in-place.
231 //
232 // First, negative values will be set to the mean of the non-negative pixels
233 // in the image.
234 //
235 // Then, the resulting will be normalized such that it has mean value of 0.0 and
236 // a standard deviation of 1.0.
NormalizeImage(Image<float> * const image)237 inline void NormalizeImage(Image<float>* const image) {
238   const float* const data_ptr = image->data();
239 
240   // Copy only the non-negative values to some temp memory.
241   float running_sum = 0.0f;
242   int num_data_gte_zero = 0;
243   {
244     float* const curr_data = (*image)[0];
245     for (int i = 0; i < image->data_size_; ++i) {
246       if (curr_data[i] >= 0.0f) {
247         running_sum += curr_data[i];
248         ++num_data_gte_zero;
249       } else {
250         curr_data[i] = -1.0f;
251       }
252     }
253   }
254 
255   // If none of the pixels are valid, just set the entire thing to 0.0f.
256   if (num_data_gte_zero == 0) {
257     image->Clear(0.0f);
258     return;
259   }
260 
261   const float corrected_mean = running_sum / num_data_gte_zero;
262 
263   float* curr_data = (*image)[0];
264   for (int i = 0; i < image->data_size_; ++i) {
265     const float curr_val = *curr_data;
266     *curr_data++ = curr_val < 0 ? 0 : curr_val - corrected_mean;
267   }
268 
269   const float std_dev = ComputeStdDev(data_ptr, image->data_size_, 0.0f);
270 
271   if (std_dev > 0.0f) {
272     curr_data = (*image)[0];
273     for (int i = 0; i < image->data_size_; ++i) {
274       *curr_data++ /= std_dev;
275     }
276 
277 #ifdef SANITY_CHECKS
278     LOGV("corrected_mean: %1.2f  std_dev: %1.2f", corrected_mean, std_dev);
279     const float correlation =
280         ComputeCrossCorrelation(image->data(),
281                                 image->data(),
282                                 image->data_size_);
283 
284     if (std::abs(correlation - 1.0f) > EPSILON) {
285       LOG(ERROR) << "Bad image!" << std::endl;
286       LOG(ERROR) << *image << std::endl;
287     }
288 
289     SCHECK(std::abs(correlation - 1.0f) < EPSILON,
290            "Correlation wasn't 1.0f:  %.10f", correlation);
291 #endif
292   }
293 }
294 
295 }  // namespace tf_tracking
296 
297 #endif  // TENSORFLOW_EXAMPLES_ANDROID_JNI_OBJECT_TRACKING_IMAGE_UTILS_H_
298