Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'extern/libmv/libmv/autotrack')
-rw-r--r--extern/libmv/libmv/autotrack/autotrack.cc290
-rw-r--r--extern/libmv/libmv/autotrack/autotrack.h226
-rw-r--r--extern/libmv/libmv/autotrack/callbacks.h38
-rw-r--r--extern/libmv/libmv/autotrack/frame_accessor.h86
-rw-r--r--extern/libmv/libmv/autotrack/marker.h144
-rw-r--r--extern/libmv/libmv/autotrack/model.h44
-rw-r--r--extern/libmv/libmv/autotrack/predict_tracks.cc316
-rw-r--r--extern/libmv/libmv/autotrack/predict_tracks.h37
-rw-r--r--extern/libmv/libmv/autotrack/predict_tracks_test.cc201
-rw-r--r--extern/libmv/libmv/autotrack/quad.h57
-rw-r--r--extern/libmv/libmv/autotrack/reconstruction.h89
-rw-r--r--extern/libmv/libmv/autotrack/region.h67
-rw-r--r--extern/libmv/libmv/autotrack/tracks.cc193
-rw-r--r--extern/libmv/libmv/autotrack/tracks.h82
-rw-r--r--extern/libmv/libmv/autotrack/tracks_test.cc52
15 files changed, 1922 insertions, 0 deletions
diff --git a/extern/libmv/libmv/autotrack/autotrack.cc b/extern/libmv/libmv/autotrack/autotrack.cc
new file mode 100644
index 00000000000..96a0ef64a50
--- /dev/null
+++ b/extern/libmv/libmv/autotrack/autotrack.cc
@@ -0,0 +1,290 @@
+// Copyright (c) 2014 libmv authors.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//
+// Author: mierle@gmail.com (Keir Mierle)
+
+#include "libmv/autotrack/autotrack.h"
+#include "libmv/autotrack/quad.h"
+#include "libmv/autotrack/frame_accessor.h"
+#include "libmv/autotrack/predict_tracks.h"
+#include "libmv/base/scoped_ptr.h"
+#include "libmv/logging/logging.h"
+#include "libmv/numeric/numeric.h"
+
+namespace mv {
+
+namespace {
+
+class DisableChannelsTransform : public FrameAccessor::Transform {
+ public:
+ DisableChannelsTransform(int disabled_channels)
+ : disabled_channels_(disabled_channels) { }
+
+ int64_t key() const {
+ return disabled_channels_;
+ }
+
+ void run(const FloatImage& input, FloatImage* output) const {
+ bool disable_red = (disabled_channels_ & Marker::CHANNEL_R) != 0,
+ disable_green = (disabled_channels_ & Marker::CHANNEL_G) != 0,
+ disable_blue = (disabled_channels_ & Marker::CHANNEL_B) != 0;
+
+ LG << "Disabling channels: "
+ << (disable_red ? "R " : "")
+ << (disable_green ? "G " : "")
+ << (disable_blue ? "B" : "");
+
+ // It's important to rescale the resultappropriately so that e.g. if only
+ // blue is selected, it's not zeroed out.
+ float scale = (disable_red ? 0.0f : 0.2126f) +
+ (disable_green ? 0.0f : 0.7152f) +
+ (disable_blue ? 0.0f : 0.0722f);
+
+ output->Resize(input.Height(), input.Width(), 1);
+ for (int y = 0; y < input.Height(); y++) {
+ for (int x = 0; x < input.Width(); x++) {
+ float r = disable_red ? 0.0f : input(y, x, 0);
+ float g = disable_green ? 0.0f : input(y, x, 1);
+ float b = disable_blue ? 0.0f : input(y, x, 2);
+ (*output)(y, x, 0) = (0.2126f * r + 0.7152f * g + 0.0722f * b) / scale;
+ }
+ }
+ }
+
+ private:
+ // Bitfield representing visible channels, bits are from Marker::Channel.
+ int disabled_channels_;
+};
+
+template<typename QuadT, typename ArrayT>
+void QuadToArrays(const QuadT& quad, ArrayT* x, ArrayT* y) {
+ for (int i = 0; i < 4; ++i) {
+ x[i] = quad.coordinates(i, 0);
+ y[i] = quad.coordinates(i, 1);
+ }
+}
+
+void MarkerToArrays(const Marker& marker, double* x, double* y) {
+ Quad2Df offset_quad = marker.patch;
+ Vec2f origin = marker.search_region.Rounded().min;
+ offset_quad.coordinates.rowwise() -= origin.transpose();
+ QuadToArrays(offset_quad, x, y);
+ x[4] = marker.center.x() - origin(0);
+ y[4] = marker.center.y() - origin(1);
+}
+
+FrameAccessor::Key GetImageForMarker(const Marker& marker,
+ FrameAccessor* frame_accessor,
+ FloatImage* image) {
+ // TODO(sergey): Currently we pass float region to the accessor,
+ // but we don't want the accessor to decide the rounding, so we
+ // do rounding here.
+ // Ideally we would need to pass IntRegion to the frame accessor.
+ Region region = marker.search_region.Rounded();
+ libmv::scoped_ptr<FrameAccessor::Transform> transform = NULL;
+ if (marker.disabled_channels != 0) {
+ transform.reset(new DisableChannelsTransform(marker.disabled_channels));
+ }
+ return frame_accessor->GetImage(marker.clip,
+ marker.frame,
+ FrameAccessor::MONO,
+ 0, // No downscale for now.
+ &region,
+ transform.get(),
+ image);
+}
+
+} // namespace
+
+bool AutoTrack::TrackMarker(Marker* tracked_marker,
+ TrackRegionResult* result,
+ const TrackRegionOptions* track_options) {
+ // Try to predict the location of the second marker.
+ bool predicted_position = false;
+ if (PredictMarkerPosition(tracks_, tracked_marker)) {
+ LG << "Succesfully predicted!";
+ predicted_position = true;
+ } else {
+ LG << "Prediction failed; trying to track anyway.";
+ }
+
+ Marker reference_marker;
+ tracks_.GetMarker(tracked_marker->reference_clip,
+ tracked_marker->reference_frame,
+ tracked_marker->track,
+ &reference_marker);
+
+ // Convert markers into the format expected by TrackRegion.
+ double x1[5], y1[5];
+ MarkerToArrays(reference_marker, x1, y1);
+
+ double x2[5], y2[5];
+ MarkerToArrays(*tracked_marker, x2, y2);
+
+ // TODO(keir): Technically this could take a smaller slice from the source
+ // image instead of taking one the size of the search window.
+ FloatImage reference_image;
+ FrameAccessor::Key reference_key = GetImageForMarker(reference_marker,
+ frame_accessor_,
+ &reference_image);
+ if (!reference_key) {
+ LG << "Couldn't get frame for reference marker: " << reference_marker;
+ return false;
+ }
+
+ FloatImage tracked_image;
+ FrameAccessor::Key tracked_key = GetImageForMarker(*tracked_marker,
+ frame_accessor_,
+ &tracked_image);
+ if (!tracked_key) {
+ LG << "Couldn't get frame for tracked marker: " << tracked_marker;
+ return false;
+ }
+
+ // Store original position befoer tracking, so we can claculate offset later.
+ Vec2f original_center = tracked_marker->center;
+
+ // Do the tracking!
+ TrackRegionOptions local_track_region_options;
+ if (track_options) {
+ local_track_region_options = *track_options;
+ }
+ local_track_region_options.num_extra_points = 1; // For center point.
+ local_track_region_options.attempt_refine_before_brute = predicted_position;
+ TrackRegion(reference_image,
+ tracked_image,
+ x1, y1,
+ local_track_region_options,
+ x2, y2,
+ result);
+
+ // Copy results over the tracked marker.
+ Vec2f tracked_origin = tracked_marker->search_region.Rounded().min;
+ for (int i = 0; i < 4; ++i) {
+ tracked_marker->patch.coordinates(i, 0) = x2[i] + tracked_origin[0];
+ tracked_marker->patch.coordinates(i, 1) = y2[i] + tracked_origin[1];
+ }
+ tracked_marker->center(0) = x2[4] + tracked_origin[0];
+ tracked_marker->center(1) = y2[4] + tracked_origin[1];
+ Vec2f delta = tracked_marker->center - original_center;
+ tracked_marker->search_region.Offset(delta);
+ tracked_marker->source = Marker::TRACKED;
+ tracked_marker->status = Marker::UNKNOWN;
+ tracked_marker->reference_clip = reference_marker.clip;
+ tracked_marker->reference_frame = reference_marker.frame;
+
+ // Release the images from the accessor cache.
+ frame_accessor_->ReleaseImage(reference_key);
+ frame_accessor_->ReleaseImage(tracked_key);
+
+ // TODO(keir): Possibly the return here should get removed since the results
+ // are part of TrackResult. However, eventually the autotrack stuff will have
+ // extra status (e.g. prediction fail, etc) that should get included.
+ return true;
+}
+
+void AutoTrack::AddMarker(const Marker& marker) {
+ tracks_.AddMarker(marker);
+}
+
+void AutoTrack::SetMarkers(vector<Marker>* markers) {
+ tracks_.SetMarkers(markers);
+}
+
+bool AutoTrack::GetMarker(int clip, int frame, int track,
+ Marker* markers) const {
+ return tracks_.GetMarker(clip, frame, track, markers);
+}
+
+void AutoTrack::DetectAndTrack(const DetectAndTrackOptions& options) {
+ int num_clips = frame_accessor_->NumClips();
+ for (int clip = 0; clip < num_clips; ++clip) {
+ int num_frames = frame_accessor_->NumFrames(clip);
+ vector<Marker> previous_frame_markers;
+ // Q: How to decide track #s when detecting?
+ // Q: How to match markers from previous frame? set of prev frame tracks?
+ // Q: How to decide what markers should get tracked and which ones should not?
+ for (int frame = 0; frame < num_frames; ++frame) {
+ if (Cancelled()) {
+ LG << "Got cancel message while detecting and tracking...";
+ return;
+ }
+ // First, get or detect markers for this frame.
+ vector<Marker> this_frame_markers;
+ tracks_.GetMarkersInFrame(clip, frame, &this_frame_markers);
+ LG << "Clip " << clip << ", frame " << frame << " have "
+ << this_frame_markers.size();
+ if (this_frame_markers.size() < options.min_num_features) {
+ DetectFeaturesInFrame(clip, frame);
+ this_frame_markers.clear();
+ tracks_.GetMarkersInFrame(clip, frame, &this_frame_markers);
+ LG << "... detected " << this_frame_markers.size() << " features.";
+ }
+ if (previous_frame_markers.empty()) {
+ LG << "First frame; skipping tracking stage.";
+ previous_frame_markers.swap(this_frame_markers);
+ continue;
+ }
+ // Second, find tracks that should get tracked forward into this frame.
+ // To avoid tracking markers that are already tracked to this frame, make
+ // a sorted set of the tracks that exist in the last frame.
+ vector<int> tracks_in_this_frame;
+ for (int i = 0; i < this_frame_markers.size(); ++i) {
+ tracks_in_this_frame.push_back(this_frame_markers[i].track);
+ }
+ std::sort(tracks_in_this_frame.begin(),
+ tracks_in_this_frame.end());
+
+ // Find tracks in the previous frame that are not in this one.
+ vector<Marker*> previous_frame_markers_to_track;
+ int num_skipped = 0;
+ for (int i = 0; i < previous_frame_markers.size(); ++i) {
+ if (std::binary_search(tracks_in_this_frame.begin(),
+ tracks_in_this_frame.end(),
+ previous_frame_markers[i].track)) {
+ num_skipped++;
+ } else {
+ previous_frame_markers_to_track.push_back(&previous_frame_markers[i]);
+ }
+ }
+
+ // Finally track the markers from the last frame into this one.
+ // TODO(keir): Use OMP.
+ for (int i = 0; i < previous_frame_markers_to_track.size(); ++i) {
+ Marker this_frame_marker = *previous_frame_markers_to_track[i];
+ this_frame_marker.frame = frame;
+ LG << "Tracking: " << this_frame_marker;
+ TrackRegionResult result;
+ TrackMarker(&this_frame_marker, &result);
+ if (result.is_usable()) {
+ LG << "Success: " << this_frame_marker;
+ AddMarker(this_frame_marker);
+ this_frame_markers.push_back(this_frame_marker);
+ } else {
+ LG << "Failed to track: " << this_frame_marker;
+ }
+ }
+ // Put the markers from this frame
+ previous_frame_markers.swap(this_frame_markers);
+ }
+ }
+}
+
+} // namespace mv
diff --git a/extern/libmv/libmv/autotrack/autotrack.h b/extern/libmv/libmv/autotrack/autotrack.h
new file mode 100644
index 00000000000..1d7422f54e7
--- /dev/null
+++ b/extern/libmv/libmv/autotrack/autotrack.h
@@ -0,0 +1,226 @@
+// Copyright (c) 2014 libmv authors.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//
+// Author: mierle@gmail.com (Keir Mierle)
+
+#ifndef LIBMV_AUTOTRACK_AUTOTRACK_H_
+#define LIBMV_AUTOTRACK_AUTOTRACK_H_
+
+#include "libmv/autotrack/tracks.h"
+#include "libmv/autotrack/region.h"
+#include "libmv/tracking/track_region.h"
+
+namespace libmv {
+class CameraIntrinsics;
+};
+
+namespace mv {
+
+using libmv::CameraIntrinsics;
+using libmv::TrackRegionOptions;
+using libmv::TrackRegionResult;
+
+struct FrameAccessor;
+class OperationListener;
+
+// The coordinator of all tracking operations; keeps track of all state
+// relating to tracking and reconstruction; for example, 2D tracks and motion
+// models, reconstructed cameras, points, and planes; tracking settings; etc.
+//
+// Typical usage for full autotrack:
+//
+// AutoTrack auto_track(image_accessor);
+// auto_track.SetNumFramesInClip(0, 10);
+// auto_track.SetNumFramesInClip(1, 54);
+// auto_track.AutoTrack()
+//
+// It is also possible to specify options to control the reconstruction.
+// Furthermore, the individual methods of reconstruction are exposed to make it
+// possible to interact with the pipeline as it runs. For example, to track one
+// marker across frames,
+//
+// AutoTrack auto_track(image_accessor);
+// auto_track.SetNumFramesInClip(0, 10);
+// auto_track.SetNumFramesInClip(1, 54);
+// auto_track.AddMarker(...);
+// auto_track.TrackMarkerToFrame(int clip1, int frame1,
+// int clip2, int frame2,
+// options?)
+//
+class AutoTrack {
+ public:
+ struct Options {
+ // Default configuration for 2D tracking when calling TrackMarkerToFrame().
+ TrackRegionOptions track_region;
+
+ // Default search window for region tracking, in absolute frame pixels.
+ Region search_region;
+ };
+
+ AutoTrack(FrameAccessor* frame_accessor)
+ : frame_accessor_(frame_accessor) {}
+
+ // Marker manipulation.
+ // Clip manipulation.
+
+ // Set the number of clips. These clips will get accessed from the frame
+ // accessor, matches between frames found, and a reconstruction created.
+ //void SetNumFrames(int clip, int num_frames);
+
+ // Tracking & Matching
+
+ // Find the marker for the track in the frame indicated by the marker.
+ // Caller maintains ownership of *result and *tracked_marker.
+ bool TrackMarker(Marker* tracked_marker,
+ TrackRegionResult* result,
+ const TrackRegionOptions* track_options=NULL);
+
+ // Wrapper around Tracks API; however these may add additional processing.
+ void AddMarker(const Marker& tracked_marker);
+ void SetMarkers(vector<Marker>* markers);
+ bool GetMarker(int clip, int frame, int track, Marker* marker) const;
+
+ // TODO(keir): Implement frame matching! This could be very cool for loop
+ // closing and connecting across clips.
+ //void MatchFrames(int clip1, int frame1, int clip2, int frame2) {}
+
+ // Wrapper around the Reconstruction API.
+ // Returns the new ID.
+ int AddCameraIntrinsics(CameraIntrinsics* intrinsics) {
+ (void) intrinsics;
+ return 0;
+ } // XXX
+ int SetClipIntrinsics(int clip, int intrinsics) {
+ (void) clip;
+ (void) intrinsics;
+ return 0;
+ } // XXX
+
+ enum Motion {
+ GENERAL_CAMERA_MOTION,
+ TRIPOD_CAMERA_MOTION,
+ };
+ int SetClipMotion(int clip, Motion motion) {
+ (void) clip;
+ (void) motion;
+ return 0;
+ } // XXX
+
+ // Decide what to refine for the given intrinsics. bundle_options is from
+ // bundle.h (e.g. BUNDLE_FOCAL_LENGTH | BUNDLE_RADIAL_K1).
+ void SetIntrinsicsRefine(int intrinsics, int bundle_options) {
+ (void) intrinsics;
+ (void) bundle_options;
+ } // XXX
+
+ // Keyframe read/write.
+ struct ClipFrame {
+ int clip;
+ int frame;
+ };
+ const vector<ClipFrame>& keyframes() { return keyframes_; }
+ void ClearKeyframes() { keyframes_.clear(); }
+ void SetKeyframes(const vector<ClipFrame>& keyframes) {
+ keyframes_ = keyframes;
+ }
+
+ // What about reporting what happened? -- callbacks; maybe result struct.
+ void Reconstruct();
+
+ // Detect and track in 2D.
+ struct DetectAndTrackOptions {
+ int min_num_features;
+ };
+ void DetectAndTrack(const DetectAndTrackOptions& options);
+
+ struct DetectFeaturesInFrameOptions {
+ };
+ void DetectFeaturesInFrame(int clip, int frame,
+ const DetectFeaturesInFrameOptions* options=NULL) {
+ (void) clip;
+ (void) frame;
+ (void) options;
+ } // XXX
+
+ // Does not take ownership of the given listener, but keeps a reference to it.
+ void AddListener(OperationListener* listener) {(void) listener;} // XXX
+
+ // Create the initial reconstruction,
+ //void FindInitialReconstruction();
+
+ // State machine
+ //
+ // Question: Have explicit state? Or determine state from existing data?
+ // Conclusion: Determine state from existing data.
+ //
+ // Preliminary state thoughts
+ //
+ // No tracks or markers
+ // - Tracks empty.
+ //
+ // Initial tracks found
+ // - All images have at least 5 tracks
+ //
+ // Ran RANSAC on tracks to mark inliers / outliers.
+ // - All images have at least 8 "inlier" tracks
+ //
+ // Detector matching run to close loops and match across clips
+ // - At least 5 matching tracks between clips
+ //
+ // Initial reconstruction found (2 frames)?
+ // - There exists two cameras with intrinsics / extrinsics
+ //
+ // Preliminary reconstruction finished
+ // - Poses for all frames in all clips estimated.
+ //
+ // Final reconstruction finished
+ // - Final reconstruction bundle adjusted.
+
+ // For now, expose options directly. In the future this may change.
+ Options options;
+
+ private:
+ bool Log();
+ bool Progress();
+ bool Cancelled() { return false; }
+
+ Tracks tracks_; // May be normalized camera coordinates or raw pixels.
+ //Reconstruction reconstruction_;
+
+ // TODO(keir): Add the motion models here.
+ //vector<MotionModel> motion_models_;
+
+ // TODO(keir): Should num_clips and num_frames get moved to FrameAccessor?
+ // TODO(keir): What about masking for clips and frames to prevent various
+ // things like reconstruction or tracking from happening on certain frames?
+ FrameAccessor* frame_accessor_;
+ //int num_clips_;
+ //vector<int> num_frames_; // Indexed by clip.
+
+ // The intrinsics for each clip, assuming each clip has fixed intrinsics.
+ // TODO(keir): Decide what the semantics should be for varying focal length.
+ vector<int> clip_intrinsics_;
+
+ vector<ClipFrame> keyframes_;
+};
+
+} // namespace mv
+
+#endif // LIBMV_AUTOTRACK_AUTOTRACK_H_
diff --git a/extern/libmv/libmv/autotrack/callbacks.h b/extern/libmv/libmv/autotrack/callbacks.h
new file mode 100644
index 00000000000..e65841de3ce
--- /dev/null
+++ b/extern/libmv/libmv/autotrack/callbacks.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2014 libmv authors.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//
+// Author: mierle@gmail.com (Keir Mierle)
+
+#ifndef LIBMV_AUTOTRACK_LISTENER_H_
+#define LIBMV_AUTOTRACK_LISTENER_H_
+
+namespace mv {
+
+struct OperationListener {
+ // All hooks return true to continue or false to indicate the operation
+ // should abort. Hooks should be thread safe (reentrant).
+ virtual bool Log(const string& message) = 0;
+ virtual bool Progress(double fraction) = 0;
+ virtual bool Cancelled() = 0;
+};
+
+} // namespace mv
+
+#endif // LIBMV_AUTOTRACK_LISTENER_H_
diff --git a/extern/libmv/libmv/autotrack/frame_accessor.h b/extern/libmv/libmv/autotrack/frame_accessor.h
new file mode 100644
index 00000000000..8de5d865cd7
--- /dev/null
+++ b/extern/libmv/libmv/autotrack/frame_accessor.h
@@ -0,0 +1,86 @@
+// Copyright (c) 2014 libmv authors.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//
+// Author: mierle@gmail.com (Keir Mierle)
+
+#ifndef LIBMV_AUTOTRACK_FRAME_ACCESSOR_H_
+#define LIBMV_AUTOTRACK_FRAME_ACCESSOR_H_
+
+#include <stdint.h>
+
+#include "libmv/image/image.h"
+
+namespace mv {
+
+struct Region;
+
+using libmv::FloatImage;
+
+// This is the abstraction to different sources of images that will be part of
+// a reconstruction. These may come from disk or they may come from Blender. In
+// most cases it's expected that the implementation provides some caching
+// otherwise performance will be terrible. Sometimes the images need to get
+// filtered, and this interface provides for that as well (and permits
+// implementations to cache filtered image pieces).
+struct FrameAccessor {
+ struct Transform {
+ virtual ~Transform() { }
+ // The key should depend on the transform arguments. Must be non-zero.
+ virtual int64_t key() const = 0;
+
+ // Apply the expected transform. Output is sized correctly already.
+ // TODO(keir): What about blurs that need to access pixels outside the ROI?
+ virtual void run(const FloatImage& input, FloatImage* output) const = 0;
+ };
+
+ enum InputMode {
+ MONO,
+ RGBA
+ };
+
+ typedef void* Key;
+
+ // Get a possibly-filtered version of a frame of a video. Downscale will
+ // cause the input image to get downscaled by 2^downscale for pyramid access.
+ // Region is always in original-image coordinates, and describes the
+ // requested area. The transform describes an (optional) transform to apply
+ // to the image before it is returned.
+ //
+ // When done with an image, you must call ReleaseImage with the returned key.
+ virtual Key GetImage(int clip,
+ int frame,
+ InputMode input_mode,
+ int downscale, // Downscale by 2^downscale.
+ const Region* region, // Get full image if NULL.
+ const Transform* transform, // May be NULL.
+ FloatImage* destination) = 0;
+
+ // Releases an image from the frame accessor. Non-caching implementations may
+ // free the image immediately; others may hold onto the image.
+ virtual void ReleaseImage(Key) = 0;
+
+ virtual bool GetClipDimensions(int clip, int* width, int* height) = 0;
+ virtual int NumClips() = 0;
+ virtual int NumFrames(int clip) = 0;
+};
+
+} // namespace libmv
+
+#endif // LIBMV_AUTOTRACK_FRAME_ACCESSOR_H_
diff --git a/extern/libmv/libmv/autotrack/marker.h b/extern/libmv/libmv/autotrack/marker.h
new file mode 100644
index 00000000000..bb803313af8
--- /dev/null
+++ b/extern/libmv/libmv/autotrack/marker.h
@@ -0,0 +1,144 @@
+// Copyright (c) 2014 libmv authors.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//
+// Author: mierle@gmail.com (Keir Mierle)
+
+#ifndef LIBMV_AUTOTRACK_MARKER_H_
+#define LIBMV_AUTOTRACK_MARKER_H_
+
+#include <ostream>
+
+#include "libmv/autotrack/quad.h"
+#include "libmv/autotrack/region.h"
+#include "libmv/numeric/numeric.h"
+
+namespace mv {
+
+using libmv::Vec2f;
+
+// A marker is the 2D location of a tracked region (quad) in an image.
+// Note that some of this information could be normalized by having a
+// collection of inter-connected structs. Instead the "fat Marker" design below
+// trades memory for data structure simplicity.
+struct Marker {
+ int clip; // The clip this marker is from.
+ int frame; // The frame within the clip this marker is from.
+ int track; // The track this marker is from.
+
+ // The center of the marker in frame coordinates. This is typically, but not
+ // always, the same as the center of the patch.
+ Vec2f center;
+
+ // A frame-realtive quad defining the part of the image the marker covers.
+ // For reference markers, the pixels in the patch are the tracking pattern.
+ Quad2Df patch;
+
+ // Some markers are less certain than others; the weight determines the
+ // amount this marker contributes to the error. 1.0 indicates normal
+ // contribution; 0.0 indicates a zero-weight track (and will be omitted from
+ // bundle adjustment).
+ float weight;
+
+ enum Source {
+ MANUAL, // The user placed this marker manually.
+ DETECTED, // A keypoint detector found this point.
+ TRACKED, // The tracking algorithm placed this marker.
+ MATCHED, // A matching algorithm (e.g. SIFT or SURF or ORB) found this.
+ PREDICTED, // A motion model predicted this marker. This is needed for
+ // handling occlusions in some cases where an imaginary marker
+ // is placed to keep camera motion smooth.
+ };
+ Source source;
+
+ // Markers may be inliers or outliers if the tracking fails; this allows
+ // visualizing the markers in the image.
+ enum Status {
+ UNKNOWN,
+ INLIER,
+ OUTLIER
+ };
+ Status status;
+
+ // When doing correlation tracking, where to search in the current frame for
+ // the pattern from the reference frame, in absolute frame coordinates.
+ Region search_region;
+
+ // For tracked and matched markers, indicates what the reference was.
+ int reference_clip;
+ int reference_frame;
+
+ // Model related information for non-point tracks.
+ //
+ // Some tracks are on a larger object, such as a plane or a line or perhaps
+ // another primitive (a rectangular prisim). This captures the information
+ // needed to say that for example a collection of markers belongs to model #2
+ // (and model #2 is a plane).
+ enum ModelType {
+ POINT,
+ PLANE,
+ LINE,
+ CUBE
+ };
+ ModelType model_type;
+
+ // The model ID this track (e.g. the second model, which is a plane).
+ int model_id;
+
+ // TODO(keir): Add a "int model_argument" to capture that e.g. a marker is on
+ // the 3rd face of a cube.
+
+ enum Channel {
+ CHANNEL_R = (1 << 0),
+ CHANNEL_G = (1 << 1),
+ CHANNEL_B = (1 << 2),
+ };
+
+ // Channels from the original frame which this marker is unable to see.
+ int disabled_channels;
+
+ // Offset everything (center, patch, search) by the given delta.
+ template<typename T>
+ void Offset(const T& offset) {
+ center += offset.template cast<float>();
+ patch.coordinates.rowwise() += offset.template cast<int>();
+ search_region.Offset(offset);
+ }
+
+ // Shift the center to the given new position (and patch, search).
+ template<typename T>
+ void SetPosition(const T& new_center) {
+ Offset(new_center - center);
+ }
+};
+
+inline std::ostream& operator<<(std::ostream& out, const Marker& marker) {
+ out << "{"
+ << marker.clip << ", "
+ << marker.frame << ", "
+ << marker.track << ", ("
+ << marker.center.x() << ", "
+ << marker.center.y() << ")"
+ << "}";
+ return out;
+}
+
+} // namespace mv
+
+#endif // LIBMV_AUTOTRACK_MARKER_H_
diff --git a/extern/libmv/libmv/autotrack/model.h b/extern/libmv/libmv/autotrack/model.h
new file mode 100644
index 00000000000..1165281cdac
--- /dev/null
+++ b/extern/libmv/libmv/autotrack/model.h
@@ -0,0 +1,44 @@
+// Copyright (c) 2014 libmv authors.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//
+// Author: mierle@gmail.com (Keir Mierle)
+
+#ifndef LIBMV_AUTOTRACK_MODEL_H_
+#define LIBMV_AUTOTRACK_MODEL_H_
+
+#include "libmv/numeric/numeric.h"
+#include "libmv/autotrack/quad.h"
+
+namespace mv {
+
+struct Model {
+ enum ModelType {
+ POINT,
+ PLANE,
+ LINE,
+ CUBE
+ };
+
+ // ???
+};
+
+} // namespace mv
+
+#endif // LIBMV_AUTOTRACK_MODEL_H_
diff --git a/extern/libmv/libmv/autotrack/predict_tracks.cc b/extern/libmv/libmv/autotrack/predict_tracks.cc
new file mode 100644
index 00000000000..adc986a0033
--- /dev/null
+++ b/extern/libmv/libmv/autotrack/predict_tracks.cc
@@ -0,0 +1,316 @@
+// Copyright (c) 2014 libmv authors.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//
+// Author: mierle@gmail.com (Keir Mierle)
+
+#include "libmv/autotrack/marker.h"
+#include "libmv/autotrack/predict_tracks.h"
+#include "libmv/autotrack/tracks.h"
+#include "libmv/base/vector.h"
+#include "libmv/logging/logging.h"
+#include "libmv/tracking/kalman_filter.h"
+
+namespace mv {
+
+namespace {
+
+using libmv::vector;
+using libmv::Vec2;
+
+// Implied time delta between steps. Set empirically by tweaking and seeing
+// what numbers did best at prediction.
+const double dt = 3.8;
+
+// State transition matrix.
+
+// The states for predicting a track are as follows:
+//
+// 0 - X position
+// 1 - X velocity
+// 2 - X acceleration
+// 3 - Y position
+// 4 - Y velocity
+// 5 - Y acceleration
+//
+// Note that in the velocity-only state transition matrix, the acceleration
+// component is ignored; so technically the system could be modelled with only
+// 4 states instead of 6. For ease of implementation, this keeps order 6.
+
+// Choose one or the other model from below (velocity or acceleration).
+
+// For a typical system having constant velocity. This gives smooth-appearing
+// predictions, but they are not always as accurate.
+const double velocity_state_transition_data[] = {
+ 1, dt, 0, 0, 0, 0,
+ 0, 1, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 1, dt, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 1
+};
+
+// This 3rd-order system also models acceleration. This makes for "jerky"
+// predictions, but that tend to be more accurate.
+const double acceleration_state_transition_data[] = {
+ 1, dt, dt*dt/2, 0, 0, 0,
+ 0, 1, dt, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 1, dt, dt*dt/2,
+ 0, 0, 0, 0, 1, dt,
+ 0, 0, 0, 0, 0, 1
+};
+
+// This system (attempts) to add an angular velocity component. However, it's
+// total junk.
+const double angular_state_transition_data[] = {
+ 1, dt, -dt, 0, 0, 0, // Position x
+ 0, 1, 0, 0, 0, 0, // Velocity x
+ 0, 0, 1, 0, 0, 0, // Angular momentum
+ 0, 0, dt, 1, dt, 0, // Position y
+ 0, 0, 0, 0, 1, 0, // Velocity y
+ 0, 0, 0, 0, 0, 1 // Ignored
+};
+
+const double* state_transition_data = velocity_state_transition_data;
+
+// Observation matrix.
+const double observation_data[] = {
+ 1., 0., 0., 0., 0., 0.,
+ 0., 0., 0., 1., 0., 0.
+};
+
+// Process covariance.
+const double process_covariance_data[] = {
+ 35, 0, 0, 0, 0, 0,
+ 0, 5, 0, 0, 0, 0,
+ 0, 0, 5, 0, 0, 0,
+ 0, 0, 0, 35, 0, 0,
+ 0, 0, 0, 0, 5, 0,
+ 0, 0, 0, 0, 0, 5
+};
+
+// Process covariance.
+const double measurement_covariance_data[] = {
+ 0.01, 0.00,
+ 0.00, 0.01,
+};
+
+// Initial covariance.
+const double initial_covariance_data[] = {
+ 10, 0, 0, 0, 0, 0,
+ 0, 1, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 10, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 1
+};
+
+typedef mv::KalmanFilter<double, 6, 2> TrackerKalman;
+
+TrackerKalman filter(state_transition_data,
+ observation_data,
+ process_covariance_data,
+ measurement_covariance_data);
+
+bool OrderByFrameLessThan(const Marker* a, const Marker* b) {
+ if (a->frame == b->frame) {
+ if (a->clip == b->clip) {
+ return a->track < b->track;
+ }
+ return a->clip < b->clip;
+ }
+ return a->frame < b-> frame;
+}
+
+// Predicted must be after the previous markers (in the frame numbering sense).
+void RunPrediction(const vector<Marker*> previous_markers,
+ Marker* predicted_marker) {
+ TrackerKalman::State state;
+ state.mean << previous_markers[0]->center.x(), 0, 0,
+ previous_markers[0]->center.y(), 0, 0;
+ state.covariance = Eigen::Matrix<double, 6, 6, Eigen::RowMajor>(
+ initial_covariance_data);
+
+ int current_frame = previous_markers[0]->frame;
+ int target_frame = predicted_marker->frame;
+
+ bool predict_forward = current_frame < target_frame;
+ int frame_delta = predict_forward ? 1 : -1;
+
+ for (int i = 1; i < previous_markers.size(); ++i) {
+ // Step forward predicting the state until it is on the current marker.
+ int predictions = 0;
+ for (;
+ current_frame != previous_markers[i]->frame;
+ current_frame += frame_delta) {
+ filter.Step(&state);
+ predictions++;
+ LG << "Predicted point (frame " << current_frame << "): "
+ << state.mean(0) << ", " << state.mean(3);
+ }
+ // Log the error -- not actually used, but interesting.
+ Vec2 error = previous_markers[i]->center.cast<double>() -
+ Vec2(state.mean(0), state.mean(3));
+ LG << "Prediction error for " << predictions << " steps: ("
+ << error.x() << ", " << error.y() << "); norm: " << error.norm();
+ // Now that the state is predicted in the current frame, update the state
+ // based on the measurement from the current frame.
+ filter.Update(previous_markers[i]->center.cast<double>(),
+ Eigen::Matrix<double, 2, 2, Eigen::RowMajor>(
+ measurement_covariance_data),
+ &state);
+ LG << "Updated point: " << state.mean(0) << ", " << state.mean(3);
+ }
+ // At this point as all the prediction that's possible is done. Finally
+ // predict until the target frame.
+ for (; current_frame != target_frame; current_frame += frame_delta) {
+ filter.Step(&state);
+ LG << "Final predicted point (frame " << current_frame << "): "
+ << state.mean(0) << ", " << state.mean(3);
+ }
+
+ // The x and y positions are at 0 and 3; ignore acceleration and velocity.
+ predicted_marker->center.x() = state.mean(0);
+ predicted_marker->center.y() = state.mean(3);
+
+ // Take the patch from the last marker then shift it to match the prediction.
+ const Marker& last_marker = *previous_markers[previous_markers.size() - 1];
+ predicted_marker->patch = last_marker.patch;
+ Vec2f delta = predicted_marker->center - last_marker.center;
+ for (int i = 0; i < 4; ++i) {
+ predicted_marker->patch.coordinates.row(i) += delta;
+ }
+
+ // Alter the search area as well so it always corresponds to the center.
+ predicted_marker->search_region = last_marker.search_region;
+ predicted_marker->search_region.Offset(delta);
+}
+
+} // namespace
+
+bool PredictMarkerPosition(const Tracks& tracks, Marker* marker) {
+ // Get all markers for this clip and track.
+ vector<Marker> markers;
+ tracks.GetMarkersForTrackInClip(marker->clip, marker->track, &markers);
+
+ if (markers.empty()) {
+ LG << "No markers to predict from for " << *marker;
+ return false;
+ }
+
+ // Order the markers by frame within the clip.
+ vector<Marker*> boxed_markers(markers.size());
+ for (int i = 0; i < markers.size(); ++i) {
+ boxed_markers[i] = &markers[i];
+ }
+ std::sort(boxed_markers.begin(), boxed_markers.end(), OrderByFrameLessThan);
+
+ // Find the insertion point for this marker among the returned ones.
+ int insert_at = -1; // If we find the exact frame
+ int insert_before = -1; // Otherwise...
+ for (int i = 0; i < boxed_markers.size(); ++i) {
+ if (boxed_markers[i]->frame == marker->frame) {
+ insert_at = i;
+ break;
+ }
+ if (boxed_markers[i]->frame > marker->frame) {
+ insert_before = i;
+ break;
+ }
+ }
+
+ // Forward starts at the marker or insertion point, and goes forward.
+ int forward_scan_begin, forward_scan_end;
+
+ // Backward scan starts at the marker or insertion point, and goes backward.
+ int backward_scan_begin, backward_scan_end;
+
+ // Determine the scanning ranges.
+ if (insert_at == -1 && insert_before == -1) {
+ // Didn't find an insertion point except the end.
+ forward_scan_begin = forward_scan_end = 0;
+ backward_scan_begin = markers.size() - 1;
+ backward_scan_end = 0;
+ } else if (insert_at != -1) {
+ // Found existing marker; scan before and after it.
+ forward_scan_begin = insert_at + 1;
+ forward_scan_end = markers.size() - 1;;
+ backward_scan_begin = insert_at - 1;
+ backward_scan_end = 0;
+ } else {
+ // Didn't find existing marker but found an insertion point.
+ forward_scan_begin = insert_before;
+ forward_scan_end = markers.size() - 1;;
+ backward_scan_begin = insert_before - 1;
+ backward_scan_end = 0;
+ }
+
+ const int num_consecutive_needed = 2;
+
+ if (forward_scan_begin <= forward_scan_end &&
+ forward_scan_end - forward_scan_begin > num_consecutive_needed) {
+ // TODO(keir): Finish this.
+ }
+
+ bool predict_forward = false;
+ if (backward_scan_end <= backward_scan_begin) {
+ // TODO(keir): Add smarter handling and detecting of consecutive frames!
+ predict_forward = true;
+ }
+
+ const int max_frames_to_predict_from = 20;
+ if (predict_forward) {
+ if (backward_scan_begin - backward_scan_end < num_consecutive_needed) {
+ // Not enough information to do a prediction.
+ LG << "Predicting forward impossible, not enough information";
+ return false;
+ }
+ LG << "Predicting forward";
+ int predict_begin =
+ std::max(backward_scan_begin - max_frames_to_predict_from, 0);
+ int predict_end = backward_scan_begin;
+ vector<Marker*> previous_markers;
+ for (int i = predict_begin; i <= predict_end; ++i) {
+ previous_markers.push_back(boxed_markers[i]);
+ }
+ RunPrediction(previous_markers, marker);
+ return true;
+ } else {
+ if (forward_scan_end - forward_scan_begin < num_consecutive_needed) {
+ // Not enough information to do a prediction.
+ LG << "Predicting backward impossible, not enough information";
+ return false;
+ }
+ LG << "Predicting backward";
+ int predict_begin =
+ std::min(forward_scan_begin + max_frames_to_predict_from,
+ forward_scan_end);
+ int predict_end = forward_scan_begin;
+ vector<Marker*> previous_markers;
+ for (int i = predict_begin; i >= predict_end; --i) {
+ previous_markers.push_back(boxed_markers[i]);
+ }
+ RunPrediction(previous_markers, marker);
+ return false;
+ }
+
+}
+
+} // namespace mv
diff --git a/extern/libmv/libmv/autotrack/predict_tracks.h b/extern/libmv/libmv/autotrack/predict_tracks.h
new file mode 100644
index 00000000000..0a176d08378
--- /dev/null
+++ b/extern/libmv/libmv/autotrack/predict_tracks.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2014 libmv authors.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//
+// Author: mierle@gmail.com (Keir Mierle)
+
+#ifndef LIBMV_AUTOTRACK_PREDICT_TRACKS_H_
+#define LIBMV_AUTOTRACK_PREDICT_TRACKS_H_
+
+namespace mv {
+
+class Tracks;
+struct Marker;
+
+// Predict the position of the given marker, and update it accordingly. The
+// existing position will be overwritten.
+bool PredictMarkerPosition(const Tracks& tracks, Marker* marker);
+
+} // namespace mv
+
+#endif // LIBMV_AUTOTRACK_PREDICT_TRACKS_H_
diff --git a/extern/libmv/libmv/autotrack/predict_tracks_test.cc b/extern/libmv/libmv/autotrack/predict_tracks_test.cc
new file mode 100644
index 00000000000..fc90e260d94
--- /dev/null
+++ b/extern/libmv/libmv/autotrack/predict_tracks_test.cc
@@ -0,0 +1,201 @@
+// Copyright (c) 2014 libmv authors.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//
+// Author: mierle@gmail.com (Keir Mierle)
+
+#include "libmv/autotrack/predict_tracks.h"
+
+#include "libmv/autotrack/marker.h"
+#include "libmv/autotrack/tracks.h"
+#include "libmv/logging/logging.h"
+#include "testing/testing.h"
+
+namespace mv {
+
+void AddMarker(int frame, float x, float y, Tracks* tracks) {
+ Marker marker;
+ marker.clip = marker.track = 0;
+ marker.frame = frame;
+ marker.center.x() = x;
+ marker.center.y() = y;
+ marker.patch.coordinates << x - 1, y - 1,
+ x + 1, y - 1,
+ x + 1, y + 1,
+ x - 1, y + 1;
+ tracks->AddMarker(marker);
+}
+
+TEST(PredictMarkerPosition, EasyLinearMotion) {
+ Tracks tracks;
+ AddMarker(0, 1.0, 0.0, &tracks);
+ AddMarker(1, 2.0, 5.0, &tracks);
+ AddMarker(2, 3.0, 10.0, &tracks);
+ AddMarker(3, 4.0, 15.0, &tracks);
+ AddMarker(4, 5.0, 20.0, &tracks);
+ AddMarker(5, 6.0, 25.0, &tracks);
+ AddMarker(6, 7.0, 30.0, &tracks);
+ AddMarker(7, 8.0, 35.0, &tracks);
+
+ Marker predicted;
+ predicted.clip = 0;
+ predicted.track = 0;
+ predicted.frame = 8;
+
+ PredictMarkerPosition(tracks, &predicted);
+ double error = (libmv::Vec2f(9.0, 40.0) - predicted.center).norm();
+ LG << "Got error: " << error;
+ EXPECT_LT(error, 0.1);
+
+ // Check the patch coordinates as well.
+ double x = 9, y = 40.0;
+ Quad2Df expected_patch;
+ expected_patch.coordinates << x - 1, y - 1,
+ x + 1, y - 1,
+ x + 1, y + 1,
+ x - 1, y + 1;
+
+ error = (expected_patch.coordinates - predicted.patch.coordinates).norm();
+ LG << "Patch error: " << error;
+ EXPECT_LT(error, 0.1);
+}
+
+TEST(PredictMarkerPosition, EasyBackwardLinearMotion) {
+ Tracks tracks;
+ AddMarker(8, 1.0, 0.0, &tracks);
+ AddMarker(7, 2.0, 5.0, &tracks);
+ AddMarker(6, 3.0, 10.0, &tracks);
+ AddMarker(5, 4.0, 15.0, &tracks);
+ AddMarker(4, 5.0, 20.0, &tracks);
+ AddMarker(3, 6.0, 25.0, &tracks);
+ AddMarker(2, 7.0, 30.0, &tracks);
+ AddMarker(1, 8.0, 35.0, &tracks);
+
+ Marker predicted;
+ predicted.clip = 0;
+ predicted.track = 0;
+ predicted.frame = 0;
+
+ PredictMarkerPosition(tracks, &predicted);
+ LG << predicted;
+ double error = (libmv::Vec2f(9.0, 40.0) - predicted.center).norm();
+ LG << "Got error: " << error;
+ EXPECT_LT(error, 0.1);
+
+ // Check the patch coordinates as well.
+ double x = 9.0, y = 40.0;
+ Quad2Df expected_patch;
+ expected_patch.coordinates << x - 1, y - 1,
+ x + 1, y - 1,
+ x + 1, y + 1,
+ x - 1, y + 1;
+
+ error = (expected_patch.coordinates - predicted.patch.coordinates).norm();
+ LG << "Patch error: " << error;
+ EXPECT_LT(error, 0.1);
+}
+
+TEST(PredictMarkerPosition, TwoFrameGap) {
+ Tracks tracks;
+ AddMarker(0, 1.0, 0.0, &tracks);
+ AddMarker(1, 2.0, 5.0, &tracks);
+ AddMarker(2, 3.0, 10.0, &tracks);
+ AddMarker(3, 4.0, 15.0, &tracks);
+ AddMarker(4, 5.0, 20.0, &tracks);
+ AddMarker(5, 6.0, 25.0, &tracks);
+ AddMarker(6, 7.0, 30.0, &tracks);
+ // Missing frame 7!
+
+ Marker predicted;
+ predicted.clip = 0;
+ predicted.track = 0;
+ predicted.frame = 8;
+
+ PredictMarkerPosition(tracks, &predicted);
+ double error = (libmv::Vec2f(9.0, 40.0) - predicted.center).norm();
+ LG << "Got error: " << error;
+ EXPECT_LT(error, 0.1);
+}
+
+TEST(PredictMarkerPosition, FourFrameGap) {
+ Tracks tracks;
+ AddMarker(0, 1.0, 0.0, &tracks);
+ AddMarker(1, 2.0, 5.0, &tracks);
+ AddMarker(2, 3.0, 10.0, &tracks);
+ AddMarker(3, 4.0, 15.0, &tracks);
+ // Missing frames 4, 5, 6, 7.
+
+ Marker predicted;
+ predicted.clip = 0;
+ predicted.track = 0;
+ predicted.frame = 8;
+
+ PredictMarkerPosition(tracks, &predicted);
+ double error = (libmv::Vec2f(9.0, 40.0) - predicted.center).norm();
+ LG << "Got error: " << error;
+ EXPECT_LT(error, 2.0); // Generous error due to larger prediction window.
+}
+
+TEST(PredictMarkerPosition, MultipleGaps) {
+ Tracks tracks;
+ AddMarker(0, 1.0, 0.0, &tracks);
+ AddMarker(1, 2.0, 5.0, &tracks);
+ AddMarker(2, 3.0, 10.0, &tracks);
+ // AddMarker(3, 4.0, 15.0, &tracks); // Note the 3-frame gap.
+ // AddMarker(4, 5.0, 20.0, &tracks);
+ // AddMarker(5, 6.0, 25.0, &tracks);
+ AddMarker(6, 7.0, 30.0, &tracks); // Intermediate measurement.
+ // AddMarker(7, 8.0, 35.0, &tracks);
+
+ Marker predicted;
+ predicted.clip = 0;
+ predicted.track = 0;
+ predicted.frame = 8;
+
+ PredictMarkerPosition(tracks, &predicted);
+ double error = (libmv::Vec2f(9.0, 40.0) - predicted.center).norm();
+ LG << "Got error: " << error;
+ EXPECT_LT(error, 1.0); // Generous error due to larger prediction window.
+}
+
+TEST(PredictMarkerPosition, MarkersInRandomOrder) {
+ Tracks tracks;
+
+ // This is the same as the easy, except that the tracks are randomly ordered.
+ AddMarker(0, 1.0, 0.0, &tracks);
+ AddMarker(2, 3.0, 10.0, &tracks);
+ AddMarker(7, 8.0, 35.0, &tracks);
+ AddMarker(5, 6.0, 25.0, &tracks);
+ AddMarker(4, 5.0, 20.0, &tracks);
+ AddMarker(3, 4.0, 15.0, &tracks);
+ AddMarker(6, 7.0, 30.0, &tracks);
+ AddMarker(1, 2.0, 5.0, &tracks);
+
+ Marker predicted;
+ predicted.clip = 0;
+ predicted.track = 0;
+ predicted.frame = 8;
+
+ PredictMarkerPosition(tracks, &predicted);
+ double error = (libmv::Vec2f(9.0, 40.0) - predicted.center).norm();
+ LG << "Got error: " << error;
+ EXPECT_LT(error, 0.1);
+}
+
+} // namespace mv
diff --git a/extern/libmv/libmv/autotrack/quad.h b/extern/libmv/libmv/autotrack/quad.h
new file mode 100644
index 00000000000..0c70f9882da
--- /dev/null
+++ b/extern/libmv/libmv/autotrack/quad.h
@@ -0,0 +1,57 @@
+// Copyright (c) 2014 libmv authors.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//
+// Author: mierle@gmail.com (Keir Mierle)
+
+#ifndef LIBMV_AUTOTRACK_QUAD_H_
+#define LIBMV_AUTOTRACK_QUAD_H_
+
+#include <Eigen/Core>
+
+namespace mv {
+
+template<typename T, int D>
+struct Quad {
+ // A quad is 4 points; generally in 2D or 3D.
+ //
+ // +----------> x
+ // |\.
+ // | \.
+ // | z (z goes into screen)
+ // |
+ // | r0----->r1
+ // | ^ |
+ // | | . |
+ // | | V
+ // | r3<-----r2
+ // | \.
+ // | \.
+ // v normal goes away (right handed).
+ // y
+ //
+ // Each row is one of the corners coordinates; either (x, y) or (x, y, z).
+ Eigen::Matrix<T, 4, D> coordinates;
+};
+
+typedef Quad<float, 2> Quad2Df;
+
+} // namespace mv
+
+#endif // LIBMV_AUTOTRACK_QUAD_H_
diff --git a/extern/libmv/libmv/autotrack/reconstruction.h b/extern/libmv/libmv/autotrack/reconstruction.h
new file mode 100644
index 00000000000..e1d4e882cbd
--- /dev/null
+++ b/extern/libmv/libmv/autotrack/reconstruction.h
@@ -0,0 +1,89 @@
+// Copyright (c) 2014 libmv authors.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//
+// Author: mierle@gmail.com (Keir Mierle)
+
+#ifndef LIBMV_AUTOTRACK_RECONSTRUCTION_H_
+#define LIBMV_AUTOTRACK_RECONSTRUCTION_H_
+
+#include "libmv/base/vector.h"
+#include "libmv/numeric/numeric.h"
+#include "libmv/simple_pipeline/camera_intrinsics.h"
+
+namespace mv {
+
+using libmv::CameraIntrinsics;
+using libmv::vector;
+
+class Model;
+
+class CameraPose {
+ int clip;
+ int frame;
+ int intrinsics;
+ Mat3 R;
+ Vec3 t;
+};
+
+class Point {
+ int track;
+
+ // The coordinates of the point. Note that not all coordinates are always
+ // used; for example points on a plane only use the first two coordinates.
+ Vec3 X;
+};
+
+// A reconstruction for a set of tracks. The indexing for clip, frame, and
+// track should match that of a Tracs object, stored elsewhere.
+class Reconstruction {
+ public:
+ // All methods copy their input reference or take ownership of the pointer.
+ void AddCameraPose(const CameraPose& pose);
+ int AddCameraIntrinsics(CameraIntrinsics* intrinsics);
+ int AddPoint(const Point& point);
+ int AddModel(Model* model);
+
+ // Returns the corresponding pose or point or NULL if missing.
+ CameraPose* CameraPoseForFrame(int clip, int frame);
+ const CameraPose* CameraPoseForFrame(int clip, int frame) const;
+ Point* PointForTrack(int track);
+ const Point* PointForTrack(int track) const;
+
+ const vector<vector<CameraPose> >& camera_poses() const {
+ return camera_poses_;
+ }
+
+ private:
+ // Indexed by CameraPose::intrinsics. Owns the intrinsics objects.
+ vector<CameraIntrinsics*> camera_intrinsics_;
+
+ // Indexed by Marker::clip then by Marker::frame.
+ vector<vector<CameraPose> > camera_poses_;
+
+ // Indexed by Marker::track.
+ vector<Point> points_;
+
+ // Indexed by Marker::model_id. Owns model objects.
+ vector<Model*> models_;
+};
+
+} // namespace mv
+
+#endif // LIBMV_AUTOTRACK_RECONSTRUCTION_H_
diff --git a/extern/libmv/libmv/autotrack/region.h b/extern/libmv/libmv/autotrack/region.h
new file mode 100644
index 00000000000..b35d99eb60d
--- /dev/null
+++ b/extern/libmv/libmv/autotrack/region.h
@@ -0,0 +1,67 @@
+// Copyright (c) 2014 libmv authors.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//
+// Author: mierle@gmail.com (Keir Mierle)
+
+#ifndef LIBMV_AUTOTRACK_REGION_H_
+#define LIBMV_AUTOTRACK_REGION_H_
+
+#include "libmv/numeric/numeric.h"
+
+namespace mv {
+
+using libmv::Vec2f;
+
+// A region is a bounding box within an image.
+//
+// +----------> x
+// |
+// | (min.x, min.y) (max.x, min.y)
+// | +-------------------------+
+// | | |
+// | | |
+// | | |
+// | +-------------------------+
+// v (min.x, max.y) (max.x, max.y)
+// y
+//
+struct Region {
+ Vec2f min;
+ Vec2f max;
+
+ template<typename T>
+ void Offset(const T& offset) {
+ min += offset.template cast<float>();
+ max += offset.template cast<float>();
+ }
+
+ Region Rounded() const {
+ Region result;
+ result.min(0) = ceil(this->min(0));
+ result.min(1) = ceil(this->min(1));
+ result.max(0) = ceil(this->max(0));
+ result.max(1) = ceil(this->max(1));
+ return result;
+ }
+};
+
+} // namespace mv
+
+#endif // LIBMV_AUTOTRACK_REGION_H_
diff --git a/extern/libmv/libmv/autotrack/tracks.cc b/extern/libmv/libmv/autotrack/tracks.cc
new file mode 100644
index 00000000000..174f264f3f2
--- /dev/null
+++ b/extern/libmv/libmv/autotrack/tracks.cc
@@ -0,0 +1,193 @@
+// Copyright (c) 2014 libmv authors.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//
+// Author: mierle@gmail.com (Keir Mierle)
+
+#include "libmv/autotrack/tracks.h"
+
+#include <algorithm>
+#include <vector>
+#include <iterator>
+
+#include "libmv/numeric/numeric.h"
+
+namespace mv {
+
+Tracks::Tracks(const Tracks& other) {
+ markers_ = other.markers_;
+}
+
+Tracks::Tracks(const vector<Marker>& markers) : markers_(markers) {}
+
+bool Tracks::GetMarker(int clip, int frame, int track, Marker* marker) const {
+ for (int i = 0; i < markers_.size(); ++i) {
+ if (markers_[i].clip == clip &&
+ markers_[i].frame == frame &&
+ markers_[i].track == track) {
+ *marker = markers_[i];
+ return true;
+ }
+ }
+ return false;
+}
+
+void Tracks::GetMarkersForTrack(int track, vector<Marker>* markers) const {
+ for (int i = 0; i < markers_.size(); ++i) {
+ if (track == markers_[i].track) {
+ markers->push_back(markers_[i]);
+ }
+ }
+}
+
+void Tracks::GetMarkersForTrackInClip(int clip,
+ int track,
+ vector<Marker>* markers) const {
+ for (int i = 0; i < markers_.size(); ++i) {
+ if (clip == markers_[i].clip &&
+ track == markers_[i].track) {
+ markers->push_back(markers_[i]);
+ }
+ }
+}
+
+void Tracks::GetMarkersInFrame(int clip,
+ int frame,
+ vector<Marker>* markers) const {
+ for (int i = 0; i < markers_.size(); ++i) {
+ if (markers_[i].clip == clip &&
+ markers_[i].frame == frame) {
+ markers->push_back(markers_[i]);
+ }
+ }
+}
+
+void Tracks::GetMarkersForTracksInBothImages(int clip1, int frame1,
+ int clip2, int frame2,
+ vector<Marker>* markers) const {
+ std::vector<int> image1_tracks;
+ std::vector<int> image2_tracks;
+
+ // Collect the tracks in each of the two images.
+ for (int i = 0; i < markers_.size(); ++i) {
+ int clip = markers_[i].clip;
+ int frame = markers_[i].frame;
+ if (clip == clip1 && frame == frame1) {
+ image1_tracks.push_back(markers_[i].track);
+ } else if (clip == clip2 && frame == frame2) {
+ image2_tracks.push_back(markers_[i].track);
+ }
+ }
+
+ // Intersect the two sets to find the tracks of interest.
+ std::sort(image1_tracks.begin(), image1_tracks.end());
+ std::sort(image2_tracks.begin(), image2_tracks.end());
+ std::vector<int> intersection;
+ std::set_intersection(image1_tracks.begin(), image1_tracks.end(),
+ image2_tracks.begin(), image2_tracks.end(),
+ std::back_inserter(intersection));
+
+ // Scan through and get the relevant tracks from the two images.
+ for (int i = 0; i < markers_.size(); ++i) {
+ // Save markers that are in either frame and are in our candidate set.
+ if (((markers_[i].clip == clip1 &&
+ markers_[i].frame == frame1) ||
+ (markers_[i].clip == clip2 &&
+ markers_[i].frame == frame2)) &&
+ std::binary_search(intersection.begin(),
+ intersection.end(),
+ markers_[i].track)) {
+ markers->push_back(markers_[i]);
+ }
+ }
+}
+
+void Tracks::AddMarker(const Marker& marker) {
+ // TODO(keir): This is quadratic for repeated insertions. Fix this by adding
+ // a smarter data structure like a set<>.
+ for (int i = 0; i < markers_.size(); ++i) {
+ if (markers_[i].clip == marker.clip &&
+ markers_[i].frame == marker.frame &&
+ markers_[i].track == marker.track) {
+ markers_[i] = marker;
+ return;
+ }
+ }
+ markers_.push_back(marker);
+}
+
+void Tracks::SetMarkers(vector<Marker>* markers) {
+ std::swap(markers_, *markers);
+}
+
+bool Tracks::RemoveMarker(int clip, int frame, int track) {
+ int size = markers_.size();
+ for (int i = 0; i < markers_.size(); ++i) {
+ if (markers_[i].clip == clip &&
+ markers_[i].frame == frame &&
+ markers_[i].track == track) {
+ markers_[i] = markers_[size - 1];
+ markers_.resize(size - 1);
+ return true;
+ }
+ }
+ return false;
+}
+
+void Tracks::RemoveMarkersForTrack(int track) {
+ int size = 0;
+ for (int i = 0; i < markers_.size(); ++i) {
+ if (markers_[i].track != track) {
+ markers_[size++] = markers_[i];
+ }
+ }
+ markers_.resize(size);
+}
+
+int Tracks::MaxClip() const {
+ int max_clip = 0;
+ for (int i = 0; i < markers_.size(); ++i) {
+ max_clip = std::max(markers_[i].clip, max_clip);
+ }
+ return max_clip;
+}
+
+int Tracks::MaxFrame(int clip) const {
+ int max_frame = 0;
+ for (int i = 0; i < markers_.size(); ++i) {
+ if (markers_[i].clip == clip) {
+ max_frame = std::max(markers_[i].frame, max_frame);
+ }
+ }
+ return max_frame;
+}
+
+int Tracks::MaxTrack() const {
+ int max_track = 0;
+ for (int i = 0; i < markers_.size(); ++i) {
+ max_track = std::max(markers_[i].track, max_track);
+ }
+ return max_track;
+}
+
+int Tracks::NumMarkers() const {
+ return markers_.size();
+}
+
+} // namespace mv
diff --git a/extern/libmv/libmv/autotrack/tracks.h b/extern/libmv/libmv/autotrack/tracks.h
new file mode 100644
index 00000000000..0b7de91d211
--- /dev/null
+++ b/extern/libmv/libmv/autotrack/tracks.h
@@ -0,0 +1,82 @@
+// Copyright (c) 2014 libmv authors.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//
+// Author: mierle@gmail.com (Keir Mierle)
+
+#ifndef LIBMV_AUTOTRACK_TRACKS_H_
+#define LIBMV_AUTOTRACK_TRACKS_H_
+
+#include "libmv/base/vector.h"
+#include "libmv/autotrack/marker.h"
+
+namespace mv {
+
+using libmv::vector;
+
+// The Tracks container stores correspondences between frames.
+class Tracks {
+ public:
+ Tracks() { }
+ Tracks(const Tracks &other);
+
+ // Create a tracks object with markers already initialized. Copies markers.
+ explicit Tracks(const vector<Marker>& markers);
+
+ // All getters append to the output argument vector.
+ bool GetMarker(int clip, int frame, int track, Marker* marker) const;
+ void GetMarkersForTrack(int track, vector<Marker>* markers) const;
+ void GetMarkersForTrackInClip(int clip,
+ int track,
+ vector<Marker>* markers) const;
+ void GetMarkersInFrame(int clip, int frame, vector<Marker>* markers) const;
+
+ // Get the markers in frame1 and frame2 which have a common track.
+ //
+ // This is not the same as the union of the markers in frame1 and
+ // frame2; each marker is for a track that appears in both images.
+ void GetMarkersForTracksInBothImages(int clip1, int frame1,
+ int clip2, int frame2,
+ vector<Marker>* markers) const;
+
+ void AddMarker(const Marker& marker);
+
+ // Moves the contents of *markers over top of the existing markers. This
+ // destroys *markers in the process (but avoids copies).
+ void SetMarkers(vector<Marker>* markers);
+ bool RemoveMarker(int clip, int frame, int track);
+ void RemoveMarkersForTrack(int track);
+
+ int MaxClip() const;
+ int MaxFrame(int clip) const;
+ int MaxTrack() const;
+ int NumMarkers() const;
+
+ const vector<Marker>& markers() const { return markers_; }
+
+ private:
+ vector<Marker> markers_;
+
+ // TODO(keir): Consider adding access-map data structures to avoid all the
+ // linear lookup penalties for the accessors.
+};
+
+} // namespace mv
+
+#endif // LIBMV_AUTOTRACK_TRACKS_H_
diff --git a/extern/libmv/libmv/autotrack/tracks_test.cc b/extern/libmv/libmv/autotrack/tracks_test.cc
new file mode 100644
index 00000000000..028b4a10913
--- /dev/null
+++ b/extern/libmv/libmv/autotrack/tracks_test.cc
@@ -0,0 +1,52 @@
+// Copyright (c) 2014 libmv authors.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//
+// Author: mierle@gmail.com (Keir Mierle)
+
+#include "libmv/autotrack/tracks.h"
+
+#include "testing/testing.h"
+#include "libmv/logging/logging.h"
+
+namespace mv {
+
+TEST(Tracks, MaxFrame) {
+ Marker marker;
+ Tracks tracks;
+
+ // Add some markers to clip 0.
+ marker.clip = 0;
+ marker.frame = 1;
+ tracks.AddMarker(marker);
+
+ // Add some markers to clip 1.
+ marker.clip = 1;
+ marker.frame = 1;
+ tracks.AddMarker(marker);
+
+ marker.clip = 1;
+ marker.frame = 12;
+ tracks.AddMarker(marker);
+
+ EXPECT_EQ(1, tracks.MaxFrame(0));
+ EXPECT_EQ(12, tracks.MaxFrame(1));
+}
+
+} // namespace mv