1 // Copyright (c) 2007, 2008, 2009, 2011 libmv authors.
2 //
3 // Permission is hereby granted, free of charge, to any person obtaining a copy
4 // of this software and associated documentation files (the "Software"), to
5 // deal in the Software without restriction, including without limitation the
6 // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
7 // sell copies of the Software, and to permit persons to whom the Software is
8 // furnished to do so, subject to the following conditions:
9 //
10 // The above copyright notice and this permission notice shall be included in
11 // all copies or substantial portions of the Software.
12 //
13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
19 // IN THE SOFTWARE.
20 
21 #include "libmv/tracking/klt_region_tracker.h"
22 
23 #include "libmv/logging/logging.h"
24 #include "libmv/image/image.h"
25 #include "libmv/image/convolve.h"
26 #include "libmv/image/sample.h"
27 
28 namespace libmv {
29 
30 // Compute the gradient matrix noted by Z and the error vector e. See Good
31 // Features to Track.
32 //
33 // TODO(keir): The calls to SampleLinear() do boundary checking that should
34 // instead happen outside the loop. Since this is the innermost loop, the extra
35 // bounds checking hurts performance.
ComputeTrackingEquation(const Array3Df & image_and_gradient1,const Array3Df & image_and_gradient2,double x1,double y1,double x2,double y2,int half_width,float * gxx,float * gxy,float * gyy,float * ex,float * ey)36 static void ComputeTrackingEquation(const Array3Df &image_and_gradient1,
37                                     const Array3Df &image_and_gradient2,
38                                     double x1, double y1,
39                                     double x2, double y2,
40                                     int half_width,
41                                     float *gxx,
42                                     float *gxy,
43                                     float *gyy,
44                                     float *ex,
45                                     float *ey) {
46   *gxx = *gxy = *gyy = 0;
47   *ex = *ey = 0;
48   for (int r = -half_width; r <= half_width; ++r) {
49     for (int c = -half_width; c <= half_width; ++c) {
50       float xx1 = x1 + c;
51       float yy1 = y1 + r;
52       float xx2 = x2 + c;
53       float yy2 = y2 + r;
54       float I =  SampleLinear(image_and_gradient1, yy1, xx1, 0);
55       float J =  SampleLinear(image_and_gradient2, yy2, xx2, 0);
56       float gx = SampleLinear(image_and_gradient2, yy2, xx2, 1);
57       float gy = SampleLinear(image_and_gradient2, yy2, xx2, 2);
58       *gxx += gx * gx;
59       *gxy += gx * gy;
60       *gyy += gy * gy;
61       *ex += (I - J) * gx;
62       *ey += (I - J) * gy;
63     }
64   }
65 }
66 
RegionIsInBounds(const FloatImage & image1,double x,double y,int half_window_size)67 static bool RegionIsInBounds(const FloatImage &image1,
68                       double x, double y,
69                       int half_window_size) {
70   // Check the minimum coordinates.
71   int min_x = floor(x) - half_window_size - 1;
72   int min_y = floor(y) - half_window_size - 1;
73   if (min_x < 0.0 ||
74       min_y < 0.0) {
75     return false;
76   }
77 
78   // Check the maximum coordinates.
79   int max_x = ceil(x) + half_window_size + 1;
80   int max_y = ceil(y) + half_window_size + 1;
81   if (max_x > image1.cols() ||
82       max_y > image1.rows()) {
83     return false;
84   }
85 
86   // Ok, we're good.
87   return true;
88 }
89 
Track(const FloatImage & image1,const FloatImage & image2,double x1,double y1,double * x2,double * y2) const90 bool KltRegionTracker::Track(const FloatImage &image1,
91                              const FloatImage &image2,
92                              double  x1, double  y1,
93                              double *x2, double *y2) const {
94   if (!RegionIsInBounds(image1, x1, y1, half_window_size)) {
95     LG << "Fell out of image1's window with x1=" << x1 << ", y1=" << y1
96        << ", hw=" << half_window_size << ".";
97     return false;
98   }
99 
100   Array3Df image_and_gradient1;
101   Array3Df image_and_gradient2;
102   BlurredImageAndDerivativesChannels(image1, sigma, &image_and_gradient1);
103   BlurredImageAndDerivativesChannels(image2, sigma, &image_and_gradient2);
104 
105   int i;
106   float dx = 0, dy = 0;
107   for (i = 0; i < max_iterations; ++i) {
108     // Check that the entire image patch is within the bounds of the images.
109     if (!RegionIsInBounds(image2, *x2, *y2, half_window_size)) {
110       LG << "Fell out of image2's window with x2=" << *x2 << ", y2=" << *y2
111          << ", hw=" << half_window_size << ".";
112       return false;
113     }
114 
115     // Compute gradient matrix and error vector.
116     float gxx, gxy, gyy, ex, ey;
117     ComputeTrackingEquation(image_and_gradient1,
118                             image_and_gradient2,
119                             x1, y1,
120                             *x2, *y2,
121                             half_window_size,
122                             &gxx, &gxy, &gyy, &ex, &ey);
123 
124     // Solve the tracking equation
125     //
126     //   [gxx gxy] [dx] = [ex]
127     //   [gxy gyy] [dy] = [ey]
128     //
129     // for dx and dy.  Borrowed from Stan Birchfield's KLT implementation.
130     float determinant = gxx * gyy - gxy * gxy;
131     dx = (gyy * ex - gxy * ey) / determinant;
132     dy = (gxx * ey - gxy * ex) / determinant;
133 
134     // Update the position with the solved displacement.
135     *x2 += dx;
136     *y2 += dy;
137 
138     // Check for the quality of the solution, but not until having already
139     // updated the position with our best estimate. The reason to do the update
140     // anyway is that the user already knows the position is bad, so we may as
141     // well try our best.
142     if (determinant < min_determinant) {
143       // The determinant, which indicates the trackiness of the point, is too
144       // small, so fail out.
145       LG << "Determinant " << determinant << " is too small; failing tracking.";
146       return false;
147     }
148     LG << "x=" << *x2 << ", y=" << *y2 << ", dx=" << dx << ", dy=" << dy
149        << ", det=" << determinant;
150 
151     // If the update is small, then we probably found the target.
152     if (dx * dx + dy * dy < min_update_squared_distance) {
153       LG << "Successful track in " << i << " iterations.";
154       return true;
155     }
156   }
157   // Getting here means we hit max iterations, so tracking failed.
158   LG << "Too many iterations; max is set to " << max_iterations << ".";
159   return false;
160 }
161 
162 }  // namespace libmv
163