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_H_
17 #define TENSORFLOW_EXAMPLES_ANDROID_JNI_OBJECT_TRACKING_IMAGE_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/utils.h"
23 
24 // TODO(andrewharp): Make this a cast to uint32_t if/when we go unsigned for
25 // operations.
26 #define ZERO 0
27 
28 #ifdef SANITY_CHECKS
29   #define CHECK_PIXEL(IMAGE, X, Y) {\
30     SCHECK((IMAGE)->ValidPixel((X), (Y)), \
31           "CHECK_PIXEL(%d,%d) in %dx%d image.", \
32           static_cast<int>(X), static_cast<int>(Y), \
33           (IMAGE)->GetWidth(), (IMAGE)->GetHeight());\
34   }
35 
36   #define CHECK_PIXEL_INTERP(IMAGE, X, Y) {\
37     SCHECK((IMAGE)->validInterpPixel((X), (Y)), \
38           "CHECK_PIXEL_INTERP(%.2f, %.2f) in %dx%d image.", \
39           static_cast<float>(X), static_cast<float>(Y), \
40           (IMAGE)->GetWidth(), (IMAGE)->GetHeight());\
41   }
42 #else
43   #define CHECK_PIXEL(image, x, y) {}
44   #define CHECK_PIXEL_INTERP(IMAGE, X, Y) {}
45 #endif
46 
47 namespace tf_tracking {
48 
49 #ifdef SANITY_CHECKS
50 // Class which exists solely to provide bounds checking for array-style image
51 // data access.
52 template <typename T>
53 class RowData {
54  public:
RowData(T * const row_data,const int max_col)55   RowData(T* const row_data, const int max_col)
56       : row_data_(row_data), max_col_(max_col) {}
57 
58   inline T& operator[](const int col) const {
59     SCHECK(InRange(col, 0, max_col_),
60           "Column out of range: %d (%d max)", col, max_col_);
61     return row_data_[col];
62   }
63 
64   inline operator T*() const {
65     return row_data_;
66   }
67 
68  private:
69   T* const row_data_;
70   const int max_col_;
71 };
72 #endif
73 
74 // Naive templated sorting function.
75 template <typename T>
Comp(const void * a,const void * b)76 int Comp(const void* a, const void* b) {
77   const T val1 = *reinterpret_cast<const T*>(a);
78   const T val2 = *reinterpret_cast<const T*>(b);
79 
80   if (val1 == val2) {
81     return 0;
82   } else if (val1 < val2) {
83     return -1;
84   } else {
85     return 1;
86   }
87 }
88 
89 // TODO(andrewharp): Make explicit which operations support negative numbers or
90 // struct/class types in image data (possibly create fast multi-dim array class
91 // for data where pixel arithmetic does not make sense).
92 
93 // Image class optimized for working on numeric arrays as grayscale image data.
94 // Supports other data types as a 2D array class, so long as no pixel math
95 // operations are called (convolution, downsampling, etc).
96 template <typename T>
97 class Image {
98  public:
99   Image(const int width, const int height);
100   explicit Image(const Size& size);
101 
102   // Constructor that creates an image from preallocated data.
103   // Note: The image takes ownership of the data lifecycle, unless own_data is
104   // set to false.
105   Image(const int width, const int height, T* const image_data,
106         const bool own_data = true);
107 
108   ~Image();
109 
110   // Extract a pixel patch from this image, starting at a subpixel location.
111   // Uses 16:16 fixed point format for representing real values and doing the
112   // bilinear interpolation.
113   //
114   // Arguments fp_x and fp_y tell the subpixel position in fixed point format,
115   // patchwidth/patchheight give the size of the patch in pixels and
116   // to_data must be a valid pointer to a *contiguous* destination data array.
117   template<class DstType>
118   bool ExtractPatchAtSubpixelFixed1616(const int fp_x,
119                                        const int fp_y,
120                                        const int patchwidth,
121                                        const int patchheight,
122                                        DstType* to_data) const;
123 
124   Image<T>* Crop(
125       const int left, const int top, const int right, const int bottom) const;
126 
GetWidth()127   inline int GetWidth() const { return width_; }
GetHeight()128   inline int GetHeight() const { return height_; }
129 
130   // Bilinearly sample a value between pixels.  Values must be within the image.
131   inline float GetPixelInterp(const float x, const float y) const;
132 
133   // Bilinearly sample a pixels at a subpixel position using fixed point
134   // arithmetic.
135   // Avoids float<->int conversions.
136   // Values must be within the image.
137   // Arguments fp_x and fp_y tell the subpixel position in
138   // 16:16 fixed point format.
139   //
140   // Important: This function only makes sense for integer-valued images, such
141   // as Image<uint8_t> or Image<int> etc.
142   inline T GetPixelInterpFixed1616(const int fp_x_whole,
143                                    const int fp_y_whole) const;
144 
145   // Returns true iff the pixel is in the image's boundaries.
146   inline bool ValidPixel(const int x, const int y) const;
147 
148   inline BoundingBox GetContainingBox() const;
149 
150   inline bool Contains(const BoundingBox& bounding_box) const;
151 
GetMedianValue()152   inline T GetMedianValue() {
153     qsort(image_data_, data_size_, sizeof(image_data_[0]), Comp<T>);
154     return image_data_[data_size_ >> 1];
155   }
156 
157   // Returns true iff the pixel is in the image's boundaries for interpolation
158   // purposes.
159   // TODO(andrewharp): check in interpolation follow-up change.
160   inline bool ValidInterpPixel(const float x, const float y) const;
161 
162   // Safe lookup with boundary enforcement.
GetPixelClipped(const int x,const int y)163   inline T GetPixelClipped(const int x, const int y) const {
164     return (*this)[Clip(y, ZERO, height_less_one_)]
165                   [Clip(x, ZERO, width_less_one_)];
166   }
167 
168 #ifdef SANITY_CHECKS
169   inline RowData<T> operator[](const int row) {
170     SCHECK(InRange(row, 0, height_less_one_),
171           "Row out of range: %d (%d max)", row, height_less_one_);
172     return RowData<T>(image_data_ + row * stride_, width_less_one_);
173   }
174 
175   inline const RowData<T> operator[](const int row) const {
176     SCHECK(InRange(row, 0, height_less_one_),
177           "Row out of range: %d (%d max)", row, height_less_one_);
178     return RowData<T>(image_data_ + row * stride_, width_less_one_);
179   }
180 #else
181   inline T* operator[](const int row) {
182     return image_data_ + row * stride_;
183   }
184 
185   inline const T* operator[](const int row) const {
186     return image_data_ + row * stride_;
187   }
188 #endif
189 
data()190   const T* data() const { return image_data_; }
191 
stride()192   inline int stride() const { return stride_; }
193 
194   // Clears image to a single value.
Clear(const T & val)195   inline void Clear(const T& val) {
196     memset(image_data_, val, sizeof(*image_data_) * data_size_);
197   }
198 
199 #ifdef __ARM_NEON
200   void Downsample2x32ColumnsNeon(const uint8_t* const original,
201                                  const int stride, const int orig_x);
202 
203   void Downsample4x32ColumnsNeon(const uint8_t* const original,
204                                  const int stride, const int orig_x);
205 
206   void DownsampleAveragedNeon(const uint8_t* const original, const int stride,
207                               const int factor);
208 #endif
209 
210   // Naive downsampler that reduces image size by factor by averaging pixels in
211   // blocks of size factor x factor.
212   void DownsampleAveraged(const T* const original, const int stride,
213                           const int factor);
214 
215   // Naive downsampler that reduces image size by factor by averaging pixels in
216   // blocks of size factor x factor.
DownsampleAveraged(const Image<T> & original,const int factor)217   inline void DownsampleAveraged(const Image<T>& original, const int factor) {
218     DownsampleAveraged(original.data(), original.GetWidth(), factor);
219   }
220 
221   // Native downsampler that reduces image size using nearest interpolation
222   void DownsampleInterpolateNearest(const Image<T>& original);
223 
224   // Native downsampler that reduces image size using fixed-point bilinear
225   // interpolation
226   void DownsampleInterpolateLinear(const Image<T>& original);
227 
228   // Relatively efficient downsampling of an image by a factor of two with a
229   // low-pass 3x3 smoothing operation thrown in.
230   void DownsampleSmoothed3x3(const Image<T>& original);
231 
232   // Relatively efficient downsampling of an image by a factor of two with a
233   // low-pass 5x5 smoothing operation thrown in.
234   void DownsampleSmoothed5x5(const Image<T>& original);
235 
236   // Optimized Scharr filter on a single pixel in the X direction.
237   // Scharr filters are like central-difference operators, but have more
238   // rotational symmetry in their response because they also consider the
239   // diagonal neighbors.
240   template <typename U>
241   inline T ScharrPixelX(const Image<U>& original,
242                         const int center_x, const int center_y) const;
243 
244   // Optimized Scharr filter on a single pixel in the X direction.
245   // Scharr filters are like central-difference operators, but have more
246   // rotational symmetry in their response because they also consider the
247   // diagonal neighbors.
248   template <typename U>
249   inline T ScharrPixelY(const Image<U>& original,
250                         const int center_x, const int center_y) const;
251 
252   // Convolve the image with a Scharr filter in the X direction.
253   // Much faster than an equivalent generic convolution.
254   template <typename U>
255   inline void ScharrX(const Image<U>& original);
256 
257   // Convolve the image with a Scharr filter in the Y direction.
258   // Much faster than an equivalent generic convolution.
259   template <typename U>
260   inline void ScharrY(const Image<U>& original);
261 
HalfDiff(int32_t first,int32_t second)262   static inline T HalfDiff(int32_t first, int32_t second) {
263     return (second - first) / 2;
264   }
265 
266   template <typename U>
267   void DerivativeX(const Image<U>& original);
268 
269   template <typename U>
270   void DerivativeY(const Image<U>& original);
271 
272   // Generic function for convolving pixel with 3x3 filter.
273   // Filter pixels should be in row major order.
274   template <typename U>
275   inline T ConvolvePixel3x3(const Image<U>& original,
276                             const int* const filter,
277                             const int center_x, const int center_y,
278                             const int total) const;
279 
280   // Generic function for convolving an image with a 3x3 filter.
281   // TODO(andrewharp): Generalize this for any size filter.
282   template <typename U>
283   inline void Convolve3x3(const Image<U>& original,
284                           const int32_t* const filter);
285 
286   // Load this image's data from a data array. The data at pixels is assumed to
287   // have dimensions equivalent to this image's dimensions * factor.
288   inline void FromArray(const T* const pixels, const int stride,
289                         const int factor = 1);
290 
291   // Copy the image back out to an appropriately sized data array.
ToArray(T * const pixels)292   inline void ToArray(T* const pixels) const {
293     // If not subsampling, memcpy should be faster.
294     memcpy(pixels, this->image_data_, data_size_ * sizeof(T));
295   }
296 
297   // Precompute these for efficiency's sake as they're used by a lot of
298   // clipping code and loop code.
299   // TODO(andrewharp): make these only accessible by other Images.
300   const int width_less_one_;
301   const int height_less_one_;
302 
303   // The raw size of the allocated data.
304   const int data_size_;
305 
306  private:
Allocate()307   inline void Allocate() {
308     image_data_ = new T[data_size_];
309     if (image_data_ == NULL) {
310       LOGE("Couldn't allocate image data!");
311     }
312   }
313 
314   T* image_data_;
315 
316   bool own_data_;
317 
318   const int width_;
319   const int height_;
320 
321   // The image stride (offset to next row).
322   // TODO(andrewharp): Make sure that stride is honored in all code.
323   const int stride_;
324 
325   TF_DISALLOW_COPY_AND_ASSIGN(Image);
326 };
327 
328 template <typename t>
329 inline std::ostream& operator<<(std::ostream& stream, const Image<t>& image) {
330   for (int y = 0; y < image.GetHeight(); ++y) {
331     for (int x = 0; x < image.GetWidth(); ++x) {
332       stream << image[y][x] << " ";
333     }
334     stream << std::endl;
335   }
336   return stream;
337 }
338 
339 }  // namespace tf_tracking
340 
341 #endif  // TENSORFLOW_EXAMPLES_ANDROID_JNI_OBJECT_TRACKING_IMAGE_H_
342