Camera tracking: expose progress and status from camera solver into interface

Reporting progress isn't really accurate, but trying to make it more linear
can lead to spending more effort on it than having benefit. Also, changing
status in the information line helps to understand that blender isn't hang
up and solving is till working nicely.

Main changes in code:
- libmv_solveReconstruction now accepts additional parameters:

  * progress_update_callback - a function which is getting called
    from solver algorithm to report progress back to Blender.
  * callback_customdata - a user-defined context which is passing
    to progress_update_callback so progress can be updated in needed
    blender-side data structures.

  This parameters are optional.

- Added structure MovieTrackingStats which is placed in MovieTracking
  structure. It's supposed to be used for displaying information about
  different operations (currently it's only camera solver, but can be
  easily used for something else in the future) in clip editor.
  This statistics structure is getting allocated for time operator is
  working and not saving into .blend file.

- Clip Editor now displays statistics stored in MovieTrackingStats structure
  like it's done for rendering.
This commit is contained in:
Sergey Sharybin 2011-11-22 14:45:22 +00:00
parent f48e1b5572
commit e228849666
10 changed files with 106 additions and 40 deletions

@ -31,6 +31,8 @@
#include "libmv-capi.h" #include "libmv-capi.h"
#include "glog/logging.h" #include "glog/logging.h"
#include "libmv/logging/logging.h"
#include "Math/v3d_optimization.h" #include "Math/v3d_optimization.h"
#include "libmv/tracking/esm_region_tracker.h" #include "libmv/tracking/esm_region_tracker.h"
@ -356,7 +358,8 @@ int libmv_refineParametersAreValid(int parameters) {
libmv_Reconstruction *libmv_solveReconstruction(libmv_Tracks *tracks, int keyframe1, int keyframe2, libmv_Reconstruction *libmv_solveReconstruction(libmv_Tracks *tracks, int keyframe1, int keyframe2,
int refine_intrinsics, double focal_length, double principal_x, double principal_y, double k1, double k2, double k3) int refine_intrinsics, double focal_length, double principal_x, double principal_y, double k1, double k2, double k3,
reconstruct_progress_update_cb progress_update_callback, void *callback_customdata)
{ {
/* Invert the camera intrinsics. */ /* Invert the camera intrinsics. */
libmv::vector<libmv::Marker> markers = ((libmv::Tracks*)tracks)->AllMarkers(); libmv::vector<libmv::Marker> markers = ((libmv::Tracks*)tracks)->AllMarkers();
@ -377,15 +380,17 @@ libmv_Reconstruction *libmv_solveReconstruction(libmv_Tracks *tracks, int keyfra
libmv::Tracks normalized_tracks(markers); libmv::Tracks normalized_tracks(markers);
// printf("frames to init from: %d, %d\n", keyframe1, keyframe2); LG << "frames to init from: " << keyframe1 << " " << keyframe2;
libmv::vector<libmv::Marker> keyframe_markers = libmv::vector<libmv::Marker> keyframe_markers =
normalized_tracks.MarkersForTracksInBothImages(keyframe1, keyframe2); normalized_tracks.MarkersForTracksInBothImages(keyframe1, keyframe2);
// printf("number of markers for init: %d\n", keyframe_markers.size()); LG << "number of markers for init: " << keyframe_markers.size();
progress_update_callback(callback_customdata, 0, "Initial reconstruction");
libmv::EuclideanReconstructTwoFrames(keyframe_markers, reconstruction); libmv::EuclideanReconstructTwoFrames(keyframe_markers, reconstruction);
libmv::EuclideanBundle(normalized_tracks, reconstruction); libmv::EuclideanBundle(normalized_tracks, reconstruction);
libmv::EuclideanCompleteReconstruction(normalized_tracks, reconstruction); libmv::EuclideanCompleteReconstruction(normalized_tracks, reconstruction, progress_update_callback, callback_customdata);
if (refine_intrinsics) { if (refine_intrinsics) {
/* only a few combinations are supported but trust the caller */ /* only a few combinations are supported but trust the caller */
@ -402,9 +407,12 @@ libmv_Reconstruction *libmv_solveReconstruction(libmv_Tracks *tracks, int keyfra
if (refine_intrinsics & LIBMV_REFINE_RADIAL_DISTORTION_K2) { if (refine_intrinsics & LIBMV_REFINE_RADIAL_DISTORTION_K2) {
libmv_refine_flags |= libmv::BUNDLE_RADIAL_K2; libmv_refine_flags |= libmv::BUNDLE_RADIAL_K2;
} }
progress_update_callback(callback_customdata, 0, "Refining solution");
libmv::EuclideanBundleCommonIntrinsics(*(libmv::Tracks *)tracks, libmv_refine_flags, reconstruction, intrinsics); libmv::EuclideanBundleCommonIntrinsics(*(libmv::Tracks *)tracks, libmv_refine_flags, reconstruction, intrinsics);
} }
progress_update_callback(callback_customdata, 0, "Finishing solution");
libmv_reconstruction->tracks = *(libmv::Tracks *)tracks; libmv_reconstruction->tracks = *(libmv::Tracks *)tracks;
libmv_reconstruction->error = libmv::EuclideanReprojectionError(*(libmv::Tracks *)tracks, *reconstruction, *intrinsics); libmv_reconstruction->error = libmv::EuclideanReprojectionError(*(libmv::Tracks *)tracks, *reconstruction, *intrinsics);

@ -64,10 +64,14 @@ void libmv_tracksDestroy(struct libmv_Tracks *libmv_tracks);
#define LIBMV_REFINE_PRINCIPAL_POINT (1<<1) #define LIBMV_REFINE_PRINCIPAL_POINT (1<<1)
#define LIBMV_REFINE_RADIAL_DISTORTION_K1 (1<<2) #define LIBMV_REFINE_RADIAL_DISTORTION_K1 (1<<2)
#define LIBMV_REFINE_RADIAL_DISTORTION_K2 (1<<4) #define LIBMV_REFINE_RADIAL_DISTORTION_K2 (1<<4)
typedef void (*reconstruct_progress_update_cb) (void *customdata, double progress, const char *message);
int libmv_refineParametersAreValid(int parameters); int libmv_refineParametersAreValid(int parameters);
struct libmv_Reconstruction *libmv_solveReconstruction(struct libmv_Tracks *tracks, int keyframe1, int keyframe2, struct libmv_Reconstruction *libmv_solveReconstruction(struct libmv_Tracks *tracks, int keyframe1, int keyframe2,
int refine_intrinsics, double focal_length, double principal_x, double principal_y, double k1, double k2, double k3); int refine_intrinsics, double focal_length, double principal_x, double principal_y, double k1, double k2, double k3,
reconstruct_progress_update_cb progress_update_callback, void *callback_customdata);
int libmv_reporojectionPointForTrack(struct libmv_Reconstruction *libmv_reconstruction, int track, double pos[3]); int libmv_reporojectionPointForTrack(struct libmv_Reconstruction *libmv_reconstruction, int track, double pos[3]);
double libmv_reporojectionErrorForTrack(struct libmv_Reconstruction *libmv_reconstruction, int track); double libmv_reporojectionErrorForTrack(struct libmv_Reconstruction *libmv_reconstruction, int track);
double libmv_reporojectionErrorForImage(struct libmv_Reconstruction *libmv_reconstruction, int image); double libmv_reporojectionErrorForImage(struct libmv_Reconstruction *libmv_reconstruction, int image);

@ -21,6 +21,7 @@
#include <cstdio> #include <cstdio>
#include "libmv/logging/logging.h" #include "libmv/logging/logging.h"
#include "libmv/simple_pipeline/pipeline.h"
#include "libmv/simple_pipeline/bundle.h" #include "libmv/simple_pipeline/bundle.h"
#include "libmv/simple_pipeline/intersect.h" #include "libmv/simple_pipeline/intersect.h"
#include "libmv/simple_pipeline/resect.h" #include "libmv/simple_pipeline/resect.h"
@ -120,11 +121,14 @@ struct ProjectivePipelineRoutines {
template<typename PipelineRoutines> template<typename PipelineRoutines>
void InternalCompleteReconstruction( void InternalCompleteReconstruction(
const Tracks &tracks, const Tracks &tracks,
typename PipelineRoutines::Reconstruction *reconstruction) { typename PipelineRoutines::Reconstruction *reconstruction,
progress_update_callback update_callback,
void *update_customdata) {
int max_track = tracks.MaxTrack(); int max_track = tracks.MaxTrack();
int max_image = tracks.MaxImage(); int max_image = tracks.MaxImage();
int num_resects = -1; int num_resects = -1;
int num_intersects = -1; int num_intersects = -1;
int tot_resects = 0;
LG << "Max track: " << max_track; LG << "Max track: " << max_track;
LG << "Max image: " << max_image; LG << "Max image: " << max_image;
LG << "Number of markers: " << tracks.NumMarkers(); LG << "Number of markers: " << tracks.NumMarkers();
@ -180,6 +184,9 @@ void InternalCompleteReconstruction(
if (reconstructed_markers.size() >= 5) { if (reconstructed_markers.size() >= 5) {
if (PipelineRoutines::Resect(reconstructed_markers, reconstruction, false)) { if (PipelineRoutines::Resect(reconstructed_markers, reconstruction, false)) {
num_resects++; num_resects++;
tot_resects++;
if(update_callback)
update_callback(update_customdata, (float)tot_resects/(max_image), "Completing solution");
LG << "Ran Resect() for image " << image; LG << "Ran Resect() for image " << image;
} else { } else {
LG << "Failed Resect() for image " << image; LG << "Failed Resect() for image " << image;
@ -211,6 +218,8 @@ void InternalCompleteReconstruction(
if (PipelineRoutines::Resect(reconstructed_markers, reconstruction, true)) { if (PipelineRoutines::Resect(reconstructed_markers, reconstruction, true)) {
num_resects++; num_resects++;
LG << "Ran Resect() for image " << image; LG << "Ran Resect() for image " << image;
if(update_callback)
update_callback(update_customdata, (float)tot_resects/(max_image), "Completing solution");
} else { } else {
LG << "Failed Resect() for image " << image; LG << "Failed Resect() for image " << image;
} }
@ -289,15 +298,20 @@ double ProjectiveReprojectionError(
} }
void EuclideanCompleteReconstruction(const Tracks &tracks, void EuclideanCompleteReconstruction(const Tracks &tracks,
EuclideanReconstruction *reconstruction) { EuclideanReconstruction *reconstruction,
progress_update_callback update_callback,
void *update_customdata) {
InternalCompleteReconstruction<EuclideanPipelineRoutines>(tracks, InternalCompleteReconstruction<EuclideanPipelineRoutines>(tracks,
reconstruction); reconstruction,
update_callback,
update_customdata);
} }
void ProjectiveCompleteReconstruction(const Tracks &tracks, void ProjectiveCompleteReconstruction(const Tracks &tracks,
ProjectiveReconstruction *reconstruction) { ProjectiveReconstruction *reconstruction) {
InternalCompleteReconstruction<ProjectivePipelineRoutines>(tracks, InternalCompleteReconstruction<ProjectivePipelineRoutines>(tracks,
reconstruction); reconstruction,
NULL, NULL);
} }
void InvertIntrinsicsForTracks(const Tracks &raw_tracks, void InvertIntrinsicsForTracks(const Tracks &raw_tracks,

@ -26,6 +26,8 @@
namespace libmv { namespace libmv {
typedef void (*progress_update_callback) (void *customdata, double progress, const char *message);
/*! /*!
Estimate camera poses and scene 3D coordinates for all frames and tracks. Estimate camera poses and scene 3D coordinates for all frames and tracks.
@ -46,7 +48,9 @@ namespace libmv {
\sa EuclideanResect, EuclideanIntersect, EuclideanBundle \sa EuclideanResect, EuclideanIntersect, EuclideanBundle
*/ */
void EuclideanCompleteReconstruction(const Tracks &tracks, void EuclideanCompleteReconstruction(const Tracks &tracks,
EuclideanReconstruction *reconstruction); EuclideanReconstruction *reconstruction,
progress_update_callback update_callback,
void *update_customdata);
/*! /*!
Estimate camera matrices and homogeneous 3D coordinates for all frames and Estimate camera matrices and homogeneous 3D coordinates for all frames and

@ -97,7 +97,7 @@ struct MovieReconstructContext* BKE_tracking_reconstruction_context_new(struct M
int keyframe1, int keyframe2, int width, int height); int keyframe1, int keyframe2, int width, int height);
void BKE_tracking_reconstruction_context_free(struct MovieReconstructContext *context); void BKE_tracking_reconstruction_context_free(struct MovieReconstructContext *context);
void BKE_tracking_solve_reconstruction(struct MovieReconstructContext *context, void BKE_tracking_solve_reconstruction(struct MovieReconstructContext *context,
short *stop, short *do_update, float *progress); short *stop, short *do_update, float *progress, char *stats_message, int message_size);
int BKE_tracking_finish_reconstruction(struct MovieReconstructContext *context, struct MovieTracking *tracking); int BKE_tracking_finish_reconstruction(struct MovieReconstructContext *context, struct MovieTracking *tracking);
struct MovieReconstructedCamera *BKE_tracking_get_reconstructed_camera(struct MovieTracking *tracking, int framenr); struct MovieReconstructedCamera *BKE_tracking_get_reconstructed_camera(struct MovieTracking *tracking, int framenr);

@ -1328,6 +1328,8 @@ typedef struct ReconstructProgressData {
short *stop; short *stop;
short *do_update; short *do_update;
float *progress; float *progress;
char *stats_message;
int message_size;
} ReconstructProgressData; } ReconstructProgressData;
#if WITH_LIBMV #if WITH_LIBMV
@ -1616,20 +1618,7 @@ void BKE_tracking_reconstruction_context_free(MovieReconstructContext *context)
MEM_freeN(context); MEM_freeN(context);
} }
#if 0 static void solve_reconstruction_update_cb(void *customdata, double progress, const char *message)
/* TODO: this two callbacks are supposed to be used to make solving more
interactive with the interface, so approximated progress would be
displayed and it's also can be nice to have option to break solving
(would fit other jobs design in blender)
customdata is used to pass some context data to libmv which can be used
later for set progress came form libmv to job
keir, it's not necessary that progress is linear and it's not necessary
that breaking happens immediately */
static void solve_reconstruction_update_cb(void *customdata, float progress)
{ {
ReconstructProgressData *progressdata= customdata; ReconstructProgressData *progressdata= customdata;
@ -1637,8 +1626,15 @@ static void solve_reconstruction_update_cb(void *customdata, float progress)
*progressdata->progress= progress; *progressdata->progress= progress;
*progressdata->do_update= 1; *progressdata->do_update= 1;
} }
if(progress) {
BLI_snprintf(progressdata->stats_message, progressdata->message_size, "%s | %d%%", message, (int)(progress*100));
} else {
BLI_strncpy(progressdata->stats_message, message, progressdata->message_size);
}
} }
#if 0
static int solve_reconstruction_testbreak_cb(void *customdata) static int solve_reconstruction_testbreak_cb(void *customdata)
{ {
ReconstructProgressData *progressdata= customdata; ReconstructProgressData *progressdata= customdata;
@ -1650,7 +1646,8 @@ static int solve_reconstruction_testbreak_cb(void *customdata)
} }
#endif #endif
void BKE_tracking_solve_reconstruction(MovieReconstructContext *context, short *stop, short *do_update, float *progress) void BKE_tracking_solve_reconstruction(MovieReconstructContext *context, short *stop,
short *do_update, float *progress, char *stats_message, int message_size)
{ {
#ifdef WITH_LIBMV #ifdef WITH_LIBMV
float error; float error;
@ -1660,13 +1657,16 @@ void BKE_tracking_solve_reconstruction(MovieReconstructContext *context, short *
progressdata.stop= stop; progressdata.stop= stop;
progressdata.do_update= do_update; progressdata.do_update= do_update;
progressdata.progress= progress; progressdata.progress= progress;
progressdata.stats_message= stats_message;
progressdata.message_size= message_size;
context->reconstruction = libmv_solveReconstruction(context->tracks, context->reconstruction = libmv_solveReconstruction(context->tracks,
context->keyframe1, context->keyframe2, context->keyframe1, context->keyframe2,
context->refine_flags, context->refine_flags,
context->focal_length, context->focal_length,
context->principal_point[0], context->principal_point[1], context->principal_point[0], context->principal_point[1],
context->k1, context->k2, context->k3); context->k1, context->k2, context->k3,
solve_reconstruction_update_cb, &progressdata);
error= libmv_reprojectionError(context->reconstruction); error= libmv_reprojectionError(context->reconstruction);

@ -5966,6 +5966,7 @@ static void direct_link_movieclip(FileData *fd, MovieClip *clip)
clip->anim= NULL; clip->anim= NULL;
clip->tracking_context= NULL; clip->tracking_context= NULL;
clip->tracking.stats= NULL;
clip->tracking.stabilization.ok= 0; clip->tracking.stabilization.ok= 0;
clip->tracking.stabilization.scaleibuf= NULL; clip->tracking.stabilization.scaleibuf= NULL;

@ -191,27 +191,38 @@ static void draw_movieclip_cache(SpaceClip *sc, ARegion *ar, MovieClip *clip, Sc
static void draw_movieclip_notes(SpaceClip *sc, ARegion *ar) static void draw_movieclip_notes(SpaceClip *sc, ARegion *ar)
{ {
MovieClip *clip= ED_space_clip(sc);
MovieTracking *tracking= &clip->tracking;
char str[256]= {0}; char str[256]= {0};
int block= 0;
if(sc->flag&SC_LOCK_SELECTION) if(tracking->stats) {
strcpy(str, "Locked"); BLI_strncpy(str, tracking->stats->message, sizeof(str));
block= 1;
} else {
if(sc->flag&SC_LOCK_SELECTION)
strcpy(str, "Locked");
}
if(str[0]) { if(str[0]) {
uiStyle *style= UI_GetStyle(); uiStyle *style= UI_GetStyle();
int fontsize, fontwidth;
int fontid= style->widget.uifont_id; int fontid= style->widget.uifont_id;
int fontwidth;
BLF_size(fontid, 11.0f, U.dpi); BLF_size(fontid, 11.0f, 72);
fontsize= BLF_height(fontid, str);
fontwidth= BLF_width(fontid, str); if(block)
fontwidth= ar->winx;
else
fontwidth= BLF_width(fontid, str);
glEnable(GL_BLEND); glEnable(GL_BLEND);
glColor4f(0.0f, 0.0f, 0.0f, 0.6f); glColor4f(0.0f, 0.0f, 0.0f, 0.6f);
glRecti(0, ar->winy-fontsize-9, fontwidth+12, ar->winy); glRecti(0, ar->winy-17, fontwidth+12, ar->winy);
glColor3f(1.0f, 1.0f, 1.0f); glColor3f(1.0f, 1.0f, 1.0f);
BLF_position(fontid, 6.0f, ar->winy-fontsize-5.0f, 0.0f); BLF_position(fontid, 6.0f, ar->winy-13.0f, 0.0f);
BLF_draw(fontid, str, strlen(str)); BLF_draw(fontid, str, strlen(str));
glDisable(GL_BLEND); glDisable(GL_BLEND);

@ -1511,13 +1511,13 @@ void CLIP_OT_track_markers(wmOperatorType *ot)
/********************** solve camera operator *********************/ /********************** solve camera operator *********************/
typedef struct { typedef struct {
MovieClip *clip;
Scene *scene; Scene *scene;
MovieClip *clip;
MovieClipUser user;
ReportList *reports; ReportList *reports;
MovieClipUser user; char stats_message[256];
struct MovieReconstructContext *context; struct MovieReconstructContext *context;
} SolveCameraJob; } SolveCameraJob;
@ -1544,14 +1544,25 @@ static int solve_camera_initjob(bContext *C, SolveCameraJob *scj, wmOperator *op
scj->context= BKE_tracking_reconstruction_context_new(tracking, scj->context= BKE_tracking_reconstruction_context_new(tracking,
settings->keyframe1, settings->keyframe2, width, height); settings->keyframe1, settings->keyframe2, width, height);
tracking->stats= MEM_callocN(sizeof(MovieTrackingStats), "solve camera stats");
return 1; return 1;
} }
static void solve_camera_updatejob(void *scv)
{
SolveCameraJob *scj= (SolveCameraJob *)scv;
MovieTracking *tracking= &scj->clip->tracking;
BLI_strncpy(tracking->stats->message, scj->stats_message, sizeof(tracking->stats->message));
}
static void solve_camera_startjob(void *scv, short *stop, short *do_update, float *progress) static void solve_camera_startjob(void *scv, short *stop, short *do_update, float *progress)
{ {
SolveCameraJob *scj= (SolveCameraJob *)scv; SolveCameraJob *scj= (SolveCameraJob *)scv;
BKE_tracking_solve_reconstruction(scj->context, stop, do_update, progress); BKE_tracking_solve_reconstruction(scj->context, stop, do_update, progress,
scj->stats_message, sizeof(scj->stats_message));
} }
static void solve_camera_freejob(void *scv) static void solve_camera_freejob(void *scv)
@ -1597,6 +1608,9 @@ static void solve_camera_freejob(void *scv)
WM_main_add_notifier(NC_OBJECT, camera); WM_main_add_notifier(NC_OBJECT, camera);
} }
MEM_freeN(tracking->stats);
tracking->stats= NULL;
DAG_id_tag_update(&clip->id, 0); DAG_id_tag_update(&clip->id, 0);
WM_main_add_notifier(NC_MOVIECLIP|NA_EVALUATED, clip); WM_main_add_notifier(NC_MOVIECLIP|NA_EVALUATED, clip);
@ -1637,6 +1651,7 @@ static int solve_camera_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(even
ScrArea *sa= CTX_wm_area(C); ScrArea *sa= CTX_wm_area(C);
SpaceClip *sc= CTX_wm_space_clip(C); SpaceClip *sc= CTX_wm_space_clip(C);
MovieClip *clip= ED_space_clip(sc); MovieClip *clip= ED_space_clip(sc);
MovieTracking *tracking= &clip->tracking;
wmJob *steve; wmJob *steve;
char error_msg[256]= "\0"; char error_msg[256]= "\0";
@ -1650,6 +1665,8 @@ static int solve_camera_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(even
return OPERATOR_CANCELLED; return OPERATOR_CANCELLED;
} }
BLI_strncpy(tracking->stats->message, "Preparing solve", sizeof(tracking->stats->message));
/* hide reconstruction statistics from previous solve */ /* hide reconstruction statistics from previous solve */
clip->tracking.reconstruction.flag&= ~TRACKING_RECONSTRUCTED; clip->tracking.reconstruction.flag&= ~TRACKING_RECONSTRUCTED;
WM_event_add_notifier(C, NC_MOVIECLIP|NA_EVALUATED, clip); WM_event_add_notifier(C, NC_MOVIECLIP|NA_EVALUATED, clip);
@ -1658,7 +1675,7 @@ static int solve_camera_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(even
steve= WM_jobs_get(CTX_wm_manager(C), CTX_wm_window(C), sa, "Solve Camera", WM_JOB_PROGRESS); steve= WM_jobs_get(CTX_wm_manager(C), CTX_wm_window(C), sa, "Solve Camera", WM_JOB_PROGRESS);
WM_jobs_customdata(steve, scj, solve_camera_freejob); WM_jobs_customdata(steve, scj, solve_camera_freejob);
WM_jobs_timer(steve, 0.2, NC_MOVIECLIP|NA_EVALUATED, 0); WM_jobs_timer(steve, 0.2, NC_MOVIECLIP|NA_EVALUATED, 0);
WM_jobs_callbacks(steve, solve_camera_startjob, NULL, NULL, NULL); WM_jobs_callbacks(steve, solve_camera_startjob, NULL, solve_camera_updatejob, NULL);
G.afbreek= 0; G.afbreek= 0;

@ -165,6 +165,11 @@ typedef struct MovieTrackingReconstruction {
struct MovieReconstructedCamera *cameras; /* reconstructed cameras */ struct MovieReconstructedCamera *cameras; /* reconstructed cameras */
} MovieTrackingReconstruction; } MovieTrackingReconstruction;
typedef struct MovieTrackingStats {
float progress;
char message[256];
} MovieTrackingStats;
typedef struct MovieTracking { typedef struct MovieTracking {
MovieTrackingSettings settings; /* different tracking-related settings */ MovieTrackingSettings settings; /* different tracking-related settings */
char pad2[4]; char pad2[4];
@ -174,6 +179,8 @@ typedef struct MovieTracking {
MovieTrackingReconstruction reconstruction; /* reconstruction data */ MovieTrackingReconstruction reconstruction; /* reconstruction data */
MovieTrackingStabilization stabilization; /* stabilization data */ MovieTrackingStabilization stabilization; /* stabilization data */
MovieTrackingTrack *act_track; /* active track */ MovieTrackingTrack *act_track; /* active track */
MovieTrackingStats *stats; /* statistics displaying in clip editor */
} MovieTracking; } MovieTracking;
/* MovieTrackingCamera->units */ /* MovieTrackingCamera->units */