Blender V4.3
libmv/autotrack/autotrack.cc
Go to the documentation of this file.
1// Copyright (c) 2014 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// Author: mierle@gmail.com (Keir Mierle)
22
30
31namespace mv {
32
33namespace {
34
35class DisableChannelsTransform : public FrameAccessor::Transform {
36 public:
37 DisableChannelsTransform(int disabled_channels)
38 : disabled_channels_(disabled_channels) {}
39
40 int64_t key() const { return disabled_channels_; }
41
42 void run(const FloatImage& input, FloatImage* output) const {
43 bool disable_red = (disabled_channels_ & Marker::CHANNEL_R) != 0,
44 disable_green = (disabled_channels_ & Marker::CHANNEL_G) != 0,
45 disable_blue = (disabled_channels_ & Marker::CHANNEL_B) != 0;
46
47 LG << "Disabling channels: " << (disable_red ? "R " : "")
48 << (disable_green ? "G " : "") << (disable_blue ? "B" : "");
49
50 // It's important to rescale the resultappropriately so that e.g. if only
51 // blue is selected, it's not zeroed out.
52 float scale = (disable_red ? 0.0f : 0.2126f) +
53 (disable_green ? 0.0f : 0.7152f) +
54 (disable_blue ? 0.0f : 0.0722f);
55
56 output->Resize(input.Height(), input.Width(), 1);
57 for (int y = 0; y < input.Height(); y++) {
58 for (int x = 0; x < input.Width(); x++) {
59 float r = disable_red ? 0.0f : input(y, x, 0);
60 float g = disable_green ? 0.0f : input(y, x, 1);
61 float b = disable_blue ? 0.0f : input(y, x, 2);
62 (*output)(y, x, 0) = (0.2126f * r + 0.7152f * g + 0.0722f * b) / scale;
63 }
64 }
65 }
66
67 private:
68 // Bitfield representing visible channels, bits are from Marker::Channel.
69 int disabled_channels_;
70};
71
72template <typename QuadT, typename ArrayT>
73void QuadToArrays(const QuadT& quad, ArrayT* x, ArrayT* y) {
74 for (int i = 0; i < 4; ++i) {
75 x[i] = quad.coordinates(i, 0);
76 y[i] = quad.coordinates(i, 1);
77 }
78}
79
80void MarkerToArrays(const Marker& marker, double* x, double* y) {
81 Quad2Df offset_quad = marker.patch;
82 Vec2f origin = marker.search_region.Rounded().min;
83 offset_quad.coordinates.rowwise() -= origin.transpose();
84 QuadToArrays(offset_quad, x, y);
85 x[4] = marker.center.x() - origin(0);
86 y[4] = marker.center.y() - origin(1);
87}
88
89FrameAccessor::Key GetImageForMarker(const Marker& marker,
90 FrameAccessor* frame_accessor,
91 FloatImage* image) {
92 // TODO(sergey): Currently we pass float region to the accessor,
93 // but we don't want the accessor to decide the rounding, so we
94 // do rounding here.
95 // Ideally we would need to pass IntRegion to the frame accessor.
96 Region region = marker.search_region.Rounded();
98 if (marker.disabled_channels != 0) {
99 transform.reset(new DisableChannelsTransform(marker.disabled_channels));
100 }
101 return frame_accessor->GetImage(marker.clip,
102 marker.frame,
104 0, // No downscale for now.
105 &region,
106 transform.get(),
107 image);
108}
109
110FrameAccessor::Key GetMaskForMarker(const Marker& marker,
111 FrameAccessor* frame_accessor,
112 FloatImage* mask) {
113 Region region = marker.search_region.Rounded();
114 return frame_accessor->GetMaskForTrack(
115 marker.clip, marker.frame, marker.track, &region, mask);
116}
117
118PredictDirection getPredictDirection(const TrackRegionOptions* track_options) {
119 switch (track_options->direction) {
122 }
123
124 LOG(FATAL) << "Unhandled tracking direction " << track_options->direction
125 << ", should never happen.";
126
128}
129
130} // namespace
131
132bool AutoTrack::TrackMarker(Marker* tracked_marker,
133 TrackRegionResult* result,
134 const TrackRegionOptions* track_options) {
135 // Try to predict the location of the second marker.
136 const PredictDirection predict_direction = getPredictDirection(track_options);
137 bool predicted_position = false;
138 if (PredictMarkerPosition(tracks_, predict_direction, tracked_marker)) {
139 LG << "Successfully predicted!";
140 predicted_position = true;
141 } else {
142 LG << "Prediction failed; trying to track anyway.";
143 }
144
145 Marker reference_marker;
146 tracks_.GetMarker(tracked_marker->reference_clip,
147 tracked_marker->reference_frame,
148 tracked_marker->track,
149 &reference_marker);
150
151 // Convert markers into the format expected by TrackRegion.
152 double x1[5], y1[5];
153 MarkerToArrays(reference_marker, x1, y1);
154
155 double x2[5], y2[5];
156 MarkerToArrays(*tracked_marker, x2, y2);
157
158 // TODO(keir): Technically this could take a smaller slice from the source
159 // image instead of taking one the size of the search window.
160 FloatImage reference_image;
161 FrameAccessor::Key reference_key =
162 GetImageForMarker(reference_marker, frame_accessor_, &reference_image);
163 if (!reference_key) {
164 LG << "Couldn't get frame for reference marker: " << reference_marker;
165 return false;
166 }
167
168 FloatImage reference_mask;
169 FrameAccessor::Key reference_mask_key =
170 GetMaskForMarker(reference_marker, frame_accessor_, &reference_mask);
171
172 FloatImage tracked_image;
173 FrameAccessor::Key tracked_key =
174 GetImageForMarker(*tracked_marker, frame_accessor_, &tracked_image);
175 if (!tracked_key) {
176 frame_accessor_->ReleaseImage(reference_key);
177 LG << "Couldn't get frame for tracked marker: " << tracked_marker;
178 return false;
179 }
180
181 // Store original position before tracking, so we can claculate offset later.
182 Vec2f original_center = tracked_marker->center;
183
184 // Do the tracking!
185 TrackRegionOptions local_track_region_options;
186 if (track_options) {
187 local_track_region_options = *track_options;
188 }
189 if (reference_mask_key != NULL) {
190 LG << "Using mask for reference marker: " << reference_marker;
191 local_track_region_options.image1_mask = &reference_mask;
192 }
193 local_track_region_options.num_extra_points = 1; // For center point.
194 local_track_region_options.attempt_refine_before_brute = predicted_position;
195 TrackRegion(reference_image,
196 tracked_image,
197 x1,
198 y1,
199 local_track_region_options,
200 x2,
201 y2,
202 result);
203
204 // Copy results over the tracked marker.
205 Vec2f tracked_origin = tracked_marker->search_region.Rounded().min;
206 for (int i = 0; i < 4; ++i) {
207 tracked_marker->patch.coordinates(i, 0) = x2[i] + tracked_origin[0];
208 tracked_marker->patch.coordinates(i, 1) = y2[i] + tracked_origin[1];
209 }
210 tracked_marker->center(0) = x2[4] + tracked_origin[0];
211 tracked_marker->center(1) = y2[4] + tracked_origin[1];
212 Vec2f delta = tracked_marker->center - original_center;
213 tracked_marker->search_region.Offset(delta);
214 tracked_marker->source = Marker::TRACKED;
215 tracked_marker->status = Marker::UNKNOWN;
216 tracked_marker->reference_clip = reference_marker.clip;
217 tracked_marker->reference_frame = reference_marker.frame;
218
219 // Release the images and masks from the accessor cache.
220 frame_accessor_->ReleaseImage(reference_key);
221 frame_accessor_->ReleaseImage(tracked_key);
222 frame_accessor_->ReleaseMask(reference_mask_key);
223
224 // TODO(keir): Possibly the return here should get removed since the results
225 // are part of TrackResult. However, eventually the autotrack stuff will have
226 // extra status (e.g. prediction fail, etc) that should get included.
227 return true;
228}
229
230void AutoTrack::AddMarker(const Marker& marker) {
231 tracks_.AddMarker(marker);
232}
233
234void AutoTrack::SetMarkers(vector<Marker>* markers) {
235 tracks_.SetMarkers(markers);
236}
237
239 int frame,
240 int track,
241 Marker* markers) const {
242 return tracks_.GetMarker(clip, frame, track, markers);
243}
244
246 int num_clips = frame_accessor_->NumClips();
247 for (int clip = 0; clip < num_clips; ++clip) {
248 int num_frames = frame_accessor_->NumFrames(clip);
249 vector<Marker> previous_frame_markers;
250 // Q: How to decide track #s when detecting?
251 // Q: How to match markers from previous frame? set of prev frame tracks?
252 // Q: How to decide what markers should get tracked and which ones should
253 // not?
254 for (int frame = 0; frame < num_frames; ++frame) {
255 if (Cancelled()) {
256 LG << "Got cancel message while detecting and tracking...";
257 return;
258 }
259 // First, get or detect markers for this frame.
260 vector<Marker> this_frame_markers;
261 tracks_.GetMarkersInFrame(clip, frame, &this_frame_markers);
262 LG << "Clip " << clip << ", frame " << frame << " have "
263 << this_frame_markers.size();
264 if (this_frame_markers.size() < options.min_num_features) {
265 DetectFeaturesInFrame(clip, frame);
266 this_frame_markers.clear();
267 tracks_.GetMarkersInFrame(clip, frame, &this_frame_markers);
268 LG << "... detected " << this_frame_markers.size() << " features.";
269 }
270 if (previous_frame_markers.empty()) {
271 LG << "First frame; skipping tracking stage.";
272 previous_frame_markers.swap(this_frame_markers);
273 continue;
274 }
275 // Second, find tracks that should get tracked forward into this frame.
276 // To avoid tracking markers that are already tracked to this frame, make
277 // a sorted set of the tracks that exist in the last frame.
278 vector<int> tracks_in_this_frame;
279 for (int i = 0; i < this_frame_markers.size(); ++i) {
280 tracks_in_this_frame.push_back(this_frame_markers[i].track);
281 }
282 std::sort(tracks_in_this_frame.begin(), tracks_in_this_frame.end());
283
284 // Find tracks in the previous frame that are not in this one.
285 vector<Marker*> previous_frame_markers_to_track;
286 for (int i = 0; i < previous_frame_markers.size(); ++i) {
287 if (std::binary_search(tracks_in_this_frame.begin(),
288 tracks_in_this_frame.end(),
289 previous_frame_markers[i].track)) {
290 continue;
291 }
292 previous_frame_markers_to_track.push_back(&previous_frame_markers[i]);
293 }
294
295 // Finally track the markers from the last frame into this one.
296 // TODO(keir): Use OMP.
297 for (int i = 0; i < previous_frame_markers_to_track.size(); ++i) {
298 Marker this_frame_marker = *previous_frame_markers_to_track[i];
299 this_frame_marker.frame = frame;
300 LG << "Tracking: " << this_frame_marker;
302 TrackMarker(&this_frame_marker, &result);
303 if (result.is_usable()) {
304 LG << "Success: " << this_frame_marker;
305 AddMarker(this_frame_marker);
306 this_frame_markers.push_back(this_frame_marker);
307 } else {
308 LG << "Failed to track: " << this_frame_marker;
309 }
310 }
311 // Put the markers from this frame
312 previous_frame_markers.swap(this_frame_markers);
313 }
314 }
315}
316
317} // namespace mv
void reset(T *new_resource)
Definition scoped_ptr.h:43
void DetectAndTrack(const DetectAndTrackOptions &options)
void SetMarkers(vector< Marker > *markers)
bool GetMarker(int clip, int frame, int track, Marker *marker) const
void AddMarker(const Marker &tracked_marker)
bool TrackMarker(Marker *tracked_marker, TrackRegionResult *result, const TrackRegionOptions *track_options=NULL)
void DetectFeaturesInFrame(int clip, int frame, const DetectFeaturesInFrameOptions *options=NULL)
void AddMarker(const Marker &marker)
void GetMarkersInFrame(int clip, int frame, vector< Marker > *markers) const
void SetMarkers(vector< Marker > *markers)
bool GetMarker(int clip, int frame, int track, Marker *marker) const
local_group_size(16, 16) .push_constant(Type b
CCL_NAMESPACE_BEGIN struct Options options
#define NULL
blender::gpu::Batch * quad
const vector< Marker > & markers
#define LG
#define LOG(severity)
Definition log.h:33
bool PredictMarkerPosition(const Tracks &tracks, const PredictDirection direction, Marker *marker)
PredictDirection
__int64 int64_t
Definition stdint.h:89
virtual Key GetImage(int clip, int frame, InputMode input_mode, int downscale, const Region *region, const Transform *transform, FloatImage *destination)=0
virtual int NumClips()=0
virtual Key GetMaskForTrack(int clip, int frame, int track, const Region *region, FloatImage *destination)=0
virtual int NumFrames(int clip)=0
virtual void ReleaseMask(Key key)=0
virtual void ReleaseImage(Key)=0
Region search_region
Definition marker.h:77
int disabled_channels
Definition marker.h:105
int frame
Definition marker.h:42
int clip
Definition marker.h:41
Vec2f center
Definition marker.h:47
@ TRACKED
Definition marker.h:62
Quad2Df patch
Definition marker.h:51
@ UNKNOWN
Definition marker.h:72
int reference_frame
Definition marker.h:81
Source source
Definition marker.h:68
Status status
Definition marker.h:73
@ CHANNEL_B
Definition marker.h:101
@ CHANNEL_G
Definition marker.h:100
@ CHANNEL_R
Definition marker.h:99
int track
Definition marker.h:43
int reference_clip
Definition marker.h:80
Eigen::Matrix< T, 4, D > coordinates
Definition quad.h:50
Region Rounded() const
void Offset(const T &offset)