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:
authorSergey Sharybin <sergey.vfx@gmail.com>2011-11-07 16:55:18 +0400
committerSergey Sharybin <sergey.vfx@gmail.com>2011-11-07 16:55:18 +0400
commit27d42c63d9b507b1771ed5a7923c389c719b877b (patch)
tree8dd4ca61e197a7053633f62b4a5d8091957724c4 /source/blender/blenkernel/intern
parente122dc0748f6a4d77b236e26beba93e2a9a36bf0 (diff)
Camera tracking integration
=========================== Commiting camera tracking integration gsoc project into trunk. This commit includes: - Bundled version of libmv library (with some changes against official repo, re-sync with libmv repo a bit later) - New datatype ID called MovieClip which is optimized to work with movie clips (both of movie files and image sequences) and doing camera/motion tracking operations. - New editor called Clip Editor which is currently used for motion/tracking stuff only, but which can be easily extended to work with masks too. This editor supports: * Loading movie files/image sequences * Build proxies with different size for loaded movie clip, also supports building undistorted proxies to increase speed of playback in undistorted mode. * Manual lens distortion mode calibration using grid and grease pencil * Supervised 2D tracking using two different algorithms KLT and SAD. * Basic algorithm for feature detection * Camera motion solving. scene orientation - New constraints to "link" scene objects with solved motions from clip: * Follow Track (make object follow 2D motion of track with given name or parent object to reconstructed 3D position of track) * Camera Solver to make camera moving in the same way as reconstructed camera This commit NOT includes changes from tomato branch: - New nodes (they'll be commited as separated patch) - Automatic image offset guessing for image input node and image editor (need to do more tests and gather more feedback) - Code cleanup in libmv-capi. It's not so critical cleanup, just increasing readability and understanadability of code. Better to make this chaneg when Keir will finish his current patch. More details about this project can be found on this page: http://wiki.blender.org/index.php/User:Nazg-gul/GSoC-2011 Further development of small features would be done in trunk, bigger/experimental features would first be implemented in tomato branch.
Diffstat (limited to 'source/blender/blenkernel/intern')
-rw-r--r--source/blender/blenkernel/intern/constraint.c206
-rw-r--r--source/blender/blenkernel/intern/context.c12
-rw-r--r--source/blender/blenkernel/intern/depsgraph.c77
-rw-r--r--source/blender/blenkernel/intern/idcode.c1
-rw-r--r--source/blender/blenkernel/intern/library.c11
-rw-r--r--source/blender/blenkernel/intern/movieclip.c1033
-rw-r--r--source/blender/blenkernel/intern/object.c84
-rw-r--r--source/blender/blenkernel/intern/tracking.c2168
8 files changed, 3569 insertions, 23 deletions
diff --git a/source/blender/blenkernel/intern/constraint.c b/source/blender/blenkernel/intern/constraint.c
index 8b5cf3bd196..f904d6e66df 100644
--- a/source/blender/blenkernel/intern/constraint.c
+++ b/source/blender/blenkernel/intern/constraint.c
@@ -44,6 +44,7 @@
#include "BLI_utildefines.h"
#include "DNA_armature_types.h"
+#include "DNA_camera_types.h"
#include "DNA_constraint_types.h"
#include "DNA_modifier_types.h"
#include "DNA_object_types.h"
@@ -51,15 +52,19 @@
#include "DNA_curve_types.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
+
#include "DNA_lattice_types.h"
#include "DNA_scene_types.h"
#include "DNA_text_types.h"
+#include "DNA_tracking_types.h"
+#include "DNA_movieclip_types.h"
#include "BKE_action.h"
#include "BKE_anim.h" /* for the curve calculation part */
#include "BKE_armature.h"
#include "BKE_blender.h"
+#include "BKE_camera.h"
#include "BKE_constraint.h"
#include "BKE_displist.h"
#include "BKE_deform.h"
@@ -72,6 +77,8 @@
#include "BKE_idprop.h"
#include "BKE_shrinkwrap.h"
#include "BKE_mesh.h"
+#include "BKE_tracking.h"
+#include "BKE_movieclip.h"
#ifdef WITH_PYTHON
#include "BPY_extern.h"
@@ -3924,6 +3931,203 @@ static bConstraintTypeInfo CTI_PIVOT = {
pivotcon_evaluate /* evaluate */
};
+/* ----------- Follow Track ------------- */
+
+static void followtrack_new_data (void *cdata)
+{
+ bFollowTrackConstraint *data= (bFollowTrackConstraint *)cdata;
+
+ data->clip= NULL;
+ data->flag|= FOLLOWTRACK_ACTIVECLIP;
+ data->reference= FOLLOWTRACK_TRACK;
+}
+
+static void followtrack_id_looper (bConstraint *con, ConstraintIDFunc func, void *userdata)
+{
+ bFollowTrackConstraint *data= con->data;
+
+ func(con, (ID**)&data->clip, userdata);
+}
+
+static void followtrack_evaluate (bConstraint *con, bConstraintOb *cob, ListBase *UNUSED(targets))
+{
+ Scene *scene= cob->scene;
+ bFollowTrackConstraint *data= con->data;
+ MovieClip *clip= data->clip;
+ MovieTrackingTrack *track;
+
+ if(data->flag&FOLLOWTRACK_ACTIVECLIP)
+ clip= scene->clip;
+
+ if(!clip || !data->track[0])
+ return;
+
+ track= BKE_tracking_named_track(&clip->tracking, data->track);
+
+ if(!track)
+ return;
+
+ if(data->reference==FOLLOWTRACK_BUNDLE) {
+ if(track->flag&TRACK_HAS_BUNDLE) {
+ float pos[3], mat[4][4], obmat[4][4];
+
+ copy_m4_m4(obmat, cob->matrix);
+
+ BKE_get_tracking_mat(cob->scene, NULL, mat);
+ mul_v3_m4v3(pos, mat, track->bundle_pos);
+
+ cob->matrix[3][0]+= pos[0];
+ cob->matrix[3][1]+= pos[1];
+ cob->matrix[3][2]+= pos[2];
+ }
+ } else {
+ Object *camob= cob->scene->camera;
+
+ if(camob) {
+ MovieClipUser user;
+ MovieTrackingMarker *marker;
+ float vec[3], disp[3], axis[3], mat[4][4];
+ float aspect= (scene->r.xsch*scene->r.xasp) / (scene->r.ysch*scene->r.yasp);
+ float sensor_x, sensor_y, lens, len, d, ortho_scale= 1.f;
+
+ where_is_object_mat(scene, camob, mat);
+
+ /* camera axis */
+ vec[0]= 0.f;
+ vec[1]= 0.f;
+ vec[2]= 1.f;
+ mul_v3_m4v3(axis, mat, vec);
+
+ /* distance to projection plane */
+ copy_v3_v3(vec, cob->matrix[3]);
+ sub_v3_v3(vec, mat[3]);
+ project_v3_v3v3(disp, vec, axis);
+
+ len= len_v3(disp);
+
+ if(len>FLT_EPSILON) {
+ float pos[2], rmat[4][4], shiftx= 0.0f, shifty= 0.0f, clipsta= 0.0f, clipend= 0.0f;
+ short is_ortho= 0, sensor_fit= CAMERA_SENSOR_FIT_AUTO;
+ Camera *cam= NULL;
+
+ user.framenr= scene->r.cfra;
+ marker= BKE_tracking_get_marker(track, user.framenr);
+
+ add_v2_v2v2(pos, marker->pos, track->offset);
+
+ object_camera_intrinsics(camob, &cam, &is_ortho, &shiftx, &shifty, &clipsta, &clipend, &lens, &sensor_x, &sensor_y, &sensor_fit);
+
+ if(is_ortho) {
+ if(cam)
+ ortho_scale= cam->ortho_scale;
+
+ vec[0]= ortho_scale * (pos[0]-0.5f+shiftx);
+ vec[1]= ortho_scale * (pos[1]-0.5f+shifty);
+ vec[2]= -len;
+
+ if(aspect>1.f) vec[1]/= aspect;
+ else vec[0]*= aspect;
+
+ mul_v3_m4v3(disp, camob->obmat, vec);
+
+ copy_m4_m4(rmat, camob->obmat);
+ zero_v3(rmat[3]);
+ mul_m4_m4m4(cob->matrix, rmat, cob->matrix);
+
+ copy_v3_v3(cob->matrix[3], disp);
+ }
+ else {
+ d= (len*sensor_x) / (2.f*lens);
+
+ vec[0]= d*(2.f*(pos[0]+shiftx)-1.f);
+ vec[1]= d*(2.f*(pos[1]+shifty)-1.f);
+ vec[2]= -len;
+
+ if(aspect>1.f) vec[1]/= aspect;
+ else vec[0]*= aspect;
+
+ mul_v3_m4v3(disp, camob->obmat, vec);
+
+ /* apply camera rotation so Z-axis would be co-linear */
+ copy_m4_m4(rmat, camob->obmat);
+ zero_v3(rmat[3]);
+ mul_m4_m4m4(cob->matrix, rmat, cob->matrix);
+
+ copy_v3_v3(cob->matrix[3], disp);
+ }
+ }
+ }
+ }
+}
+
+static bConstraintTypeInfo CTI_FOLLOWTRACK = {
+ CONSTRAINT_TYPE_FOLLOWTRACK, /* type */
+ sizeof(bFollowTrackConstraint), /* size */
+ "Follow Track", /* name */
+ "bFollowTrackConstraint", /* struct name */
+ NULL, /* free data */
+ NULL, /* relink data */
+ followtrack_id_looper, /* id looper */
+ NULL, /* copy data */
+ followtrack_new_data, /* new data */
+ NULL, /* get constraint targets */
+ NULL, /* flush constraint targets */
+ NULL, /* get target matrix */
+ followtrack_evaluate /* evaluate */
+};
+
+/* ----------- Camre Solver ------------- */
+
+static void camerasolver_new_data (void *cdata)
+{
+ bCameraSolverConstraint *data= (bCameraSolverConstraint *)cdata;
+
+ data->clip= NULL;
+ data->flag|= CAMERASOLVER_ACTIVECLIP;
+}
+
+static void camerasolver_id_looper (bConstraint *con, ConstraintIDFunc func, void *userdata)
+{
+ bCameraSolverConstraint *data= con->data;
+
+ func(con, (ID**)&data->clip, userdata);
+}
+
+static void camerasolver_evaluate (bConstraint *con, bConstraintOb *cob, ListBase *UNUSED(targets))
+{
+ Scene *scene= cob->scene;
+ bCameraSolverConstraint *data= con->data;
+ MovieClip *clip= data->clip;
+
+ if(data->flag&CAMERASOLVER_ACTIVECLIP)
+ clip= scene->clip;
+
+ if(clip) {
+ float mat[4][4], obmat[4][4];
+
+ BKE_tracking_get_interpolated_camera(&clip->tracking, scene->r.cfra, mat);
+
+ copy_m4_m4(obmat, cob->matrix);
+ mul_m4_m4m4(cob->matrix, mat, obmat);
+ }
+}
+
+static bConstraintTypeInfo CTI_CAMERASOLVER = {
+ CONSTRAINT_TYPE_CAMERASOLVER, /* type */
+ sizeof(bCameraSolverConstraint), /* size */
+ "Camera Solver", /* name */
+ "bCameraSolverConstraint", /* struct name */
+ NULL, /* free data */
+ NULL, /* relink data */
+ camerasolver_id_looper, /* id looper */
+ NULL, /* copy data */
+ camerasolver_new_data, /* new data */
+ NULL, /* get constraint targets */
+ NULL, /* flush constraint targets */
+ NULL, /* get target matrix */
+ camerasolver_evaluate /* evaluate */
+};
+
/* ************************* Constraints Type-Info *************************** */
/* All of the constraints api functions use bConstraintTypeInfo structs to carry out
* and operations that involve constraint specific code.
@@ -3962,6 +4166,8 @@ static void constraints_init_typeinfo (void)
constraintsTypeInfo[23]= &CTI_TRANSLIKE; /* Copy Transforms Constraint */
constraintsTypeInfo[24]= &CTI_SAMEVOL; /* Maintain Volume Constraint */
constraintsTypeInfo[25]= &CTI_PIVOT; /* Pivot Constraint */
+ constraintsTypeInfo[26]= &CTI_FOLLOWTRACK; /* Follow Track Constraint */
+ constraintsTypeInfo[27]= &CTI_CAMERASOLVER; /* Camera Solver Constraint */
}
/* This function should be used for getting the appropriate type-info when only
diff --git a/source/blender/blenkernel/intern/context.c b/source/blender/blenkernel/intern/context.c
index 18c08b617de..9e9a0ca2d54 100644
--- a/source/blender/blenkernel/intern/context.c
+++ b/source/blender/blenkernel/intern/context.c
@@ -361,6 +361,13 @@ struct SpaceUserPref *CTX_wm_space_userpref(const bContext *C)
return NULL;
}
+struct SpaceClip *CTX_wm_space_clip(const bContext *C)
+{
+ if(C->wm.area && C->wm.area->spacetype==SPACE_CLIP)
+ return C->wm.area->spacedata.first;
+ return NULL;
+}
+
void CTX_wm_manager_set(bContext *C, wmWindowManager *wm)
{
C->wm.manager= wm;
@@ -882,6 +889,11 @@ struct Text *CTX_data_edit_text(const bContext *C)
return ctx_data_pointer_get(C, "edit_text");
}
+struct MovieClip *CTX_data_edit_movieclip(const bContext *C)
+{
+ return ctx_data_pointer_get(C, "edit_movieclip");
+}
+
struct EditBone *CTX_data_active_bone(const bContext *C)
{
return ctx_data_pointer_get(C, "active_bone");
diff --git a/source/blender/blenkernel/intern/depsgraph.c b/source/blender/blenkernel/intern/depsgraph.c
index 51edee9ea71..79930fdd8bf 100644
--- a/source/blender/blenkernel/intern/depsgraph.c
+++ b/source/blender/blenkernel/intern/depsgraph.c
@@ -50,6 +50,7 @@
#include "DNA_scene_types.h"
#include "DNA_screen_types.h"
#include "DNA_windowmanager_types.h"
+#include "DNA_movieclip_types.h"
#include "BKE_animsys.h"
#include "BKE_action.h"
@@ -639,7 +640,26 @@ static void build_dag_object(DagForest *dag, DagNode *scenenode, Scene *scene, O
ListBase targets = {NULL, NULL};
bConstraintTarget *ct;
- if (cti && cti->get_constraint_targets) {
+ if(!cti)
+ continue;
+
+ /* special case for camera tracking -- it doesn't use targets to define relations */
+ if(ELEM(cti->type, CONSTRAINT_TYPE_FOLLOWTRACK, CONSTRAINT_TYPE_CAMERASOLVER)) {
+ if(cti->type==CONSTRAINT_TYPE_FOLLOWTRACK) {
+ bFollowTrackConstraint *data= (bFollowTrackConstraint *)con->data;
+
+ if((data->clip || data->flag&FOLLOWTRACK_ACTIVECLIP) && data->track[0]) {
+ if(scene->camera) {
+ node2 = dag_get_node(dag, scene->camera);
+ dag_add_relation(dag, node2, node, DAG_RL_DATA_OB|DAG_RL_OB_OB, cti->name);
+ }
+ }
+ }
+
+ dag_add_relation(dag,scenenode,node,DAG_RL_SCENE, "Scene Relation");
+ addtoroot = 0;
+ }
+ else if (cti->get_constraint_targets) {
cti->get_constraint_targets(con, &targets);
for (ct= targets.first; ct; ct= ct->next) {
@@ -2127,18 +2147,25 @@ static void dag_object_time_update_flags(Object *ob)
ListBase targets = {NULL, NULL};
bConstraintTarget *ct;
- if (cti && cti->get_constraint_targets) {
- cti->get_constraint_targets(con, &targets);
-
- for (ct= targets.first; ct; ct= ct->next) {
- if (ct->tar) {
- ob->recalc |= OB_RECALC_OB;
- break;
+ if (cti) {
+ /* special case for camera tracking -- it doesn't use targets to define relations */
+ if(ELEM(cti->type, CONSTRAINT_TYPE_FOLLOWTRACK, CONSTRAINT_TYPE_CAMERASOLVER)) {
+ ob->recalc |= OB_RECALC_OB;
+ }
+ else if (cti->get_constraint_targets) {
+ cti->get_constraint_targets(con, &targets);
+
+ for (ct= targets.first; ct; ct= ct->next) {
+ if (ct->tar) {
+ ob->recalc |= OB_RECALC_OB;
+ break;
+ }
}
+
+ if (cti->flush_constraint_targets)
+ cti->flush_constraint_targets(con, &targets, 1);
}
- if (cti->flush_constraint_targets)
- cti->flush_constraint_targets(con, &targets, 1);
}
}
}
@@ -2511,6 +2538,36 @@ static void dag_id_flush_update(Scene *sce, ID *id)
BKE_ptcache_object_reset(sce, obt, PTCACHE_RESET_DEPSGRAPH);
}
+ if(idtype == ID_MC) {
+ for(obt=bmain->object.first; obt; obt= obt->id.next){
+ bConstraint *con;
+ for (con = obt->constraints.first; con; con=con->next) {
+ bConstraintTypeInfo *cti= constraint_get_typeinfo(con);
+ if(ELEM(cti->type, CONSTRAINT_TYPE_FOLLOWTRACK, CONSTRAINT_TYPE_CAMERASOLVER)) {
+ obt->recalc |= OB_RECALC_OB;
+ break;
+ }
+ }
+ }
+
+ if(sce->nodetree) {
+ bNode *node;
+
+ for(node= sce->nodetree->nodes.first; node; node= node->next) {
+ if(node->id==id) {
+ nodeUpdate(sce->nodetree, node);
+ }
+ }
+ }
+ }
+
+ /* camera's matrix is used to orient reconstructed stuff,
+ so it should happen tracking-related constraints recalculation
+ when camera is changing (sergey) */
+ if(sce->camera && &sce->camera->id == id && object_get_movieclip(sce, sce->camera, 1)) {
+ dag_id_flush_update(sce, &sce->clip->id);
+ }
+
/* update editors */
dag_editors_update(bmain, id);
}
diff --git a/source/blender/blenkernel/intern/idcode.c b/source/blender/blenkernel/intern/idcode.c
index d305499d3a1..0824a9f6685 100644
--- a/source/blender/blenkernel/intern/idcode.c
+++ b/source/blender/blenkernel/intern/idcode.c
@@ -78,6 +78,7 @@ static IDType idtypes[]= {
{ ID_VF, "VFont", "fonts", IDTYPE_FLAGS_ISLINKABLE},
{ ID_WO, "World", "worlds", IDTYPE_FLAGS_ISLINKABLE},
{ ID_WM, "WindowManager", "window_managers", 0},
+ { ID_MC, "MovieClip", "movieclips", IDTYPE_FLAGS_ISLINKABLE},
};
static int nidtypes= sizeof(idtypes)/sizeof(idtypes[0]);
diff --git a/source/blender/blenkernel/intern/library.c b/source/blender/blenkernel/intern/library.c
index 878b87759b7..650f85da5b3 100644
--- a/source/blender/blenkernel/intern/library.c
+++ b/source/blender/blenkernel/intern/library.c
@@ -70,6 +70,7 @@
#include "DNA_windowmanager_types.h"
#include "DNA_world_types.h"
#include "DNA_gpencil_types.h"
+#include "DNA_movieclip_types.h"
#include "BLI_blenlib.h"
#include "BLI_dynstr.h"
@@ -111,6 +112,7 @@
#include "BKE_fcurve.h"
#include "BKE_speaker.h"
#include "BKE_utildefines.h"
+#include "BKE_movieclip.h"
#include "RNA_access.h"
@@ -484,6 +486,8 @@ ListBase *which_libbase(Main *mainlib, short type)
return &(mainlib->wm);
case ID_GD:
return &(mainlib->gpencil);
+ case ID_MC:
+ return &(mainlib->movieclip);
}
return NULL;
}
@@ -565,6 +569,7 @@ int set_listbasepointers(Main *main, ListBase **lb)
lb[a++]= &(main->scene);
lb[a++]= &(main->library);
lb[a++]= &(main->wm);
+ lb[a++]= &(main->movieclip);
lb[a]= NULL;
@@ -673,6 +678,9 @@ static ID *alloc_libblock_notest(short type)
case ID_GD:
id = MEM_callocN(sizeof(bGPdata), "Grease Pencil");
break;
+ case ID_MC:
+ id = MEM_callocN(sizeof(MovieClip), "Movie Clip");
+ break;
}
return id;
}
@@ -878,6 +886,9 @@ void free_libblock(ListBase *lb, void *idv)
case ID_GD:
free_gpencil_data((bGPdata *)id);
break;
+ case ID_MC:
+ free_movieclip((MovieClip *)id);
+ break;
}
if (id->properties) {
diff --git a/source/blender/blenkernel/intern/movieclip.c b/source/blender/blenkernel/intern/movieclip.c
new file mode 100644
index 00000000000..18a7ad65dae
--- /dev/null
+++ b/source/blender/blenkernel/intern/movieclip.c
@@ -0,0 +1,1033 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2011 Blender Foundation.
+ * All rights reserved.
+ *
+ * Contributor(s): Blender Foundation,
+ * Sergey Sharybin
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/blenkernel/intern/movieclip.c
+ * \ingroup bke
+ */
+
+
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+
+#ifndef WIN32
+#include <unistd.h>
+#else
+#include <io.h>
+#endif
+
+#include <time.h>
+
+#ifdef _WIN32
+#define open _open
+#define close _close
+#endif
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_constraint_types.h"
+#include "DNA_screen_types.h"
+#include "DNA_space_types.h"
+#include "DNA_movieclip_types.h"
+#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_view3d_types.h"
+
+#include "BLI_utildefines.h"
+
+#include "BLI_blenlib.h"
+#include "BLI_ghash.h"
+#include "BLI_math.h"
+#include "BLI_mempool.h"
+#include "BLI_threads.h"
+
+#include "BKE_constraint.h"
+#include "BKE_library.h"
+#include "BKE_global.h"
+#include "BKE_main.h"
+#include "BKE_utildefines.h"
+#include "BKE_movieclip.h"
+#include "BKE_image.h" /* openanim */
+#include "BKE_tracking.h"
+
+#include "IMB_imbuf_types.h"
+#include "IMB_imbuf.h"
+#include "IMB_moviecache.h"
+
+/*********************** movieclip buffer loaders *************************/
+
+static int sequence_guess_offset(const char *full_name, int head_len, int numlen)
+{
+ char num[FILE_MAX]= {0};
+
+ BLI_strncpy(num, full_name+head_len, numlen+1);
+
+ return atoi(num);
+}
+
+static int rendersize_to_proxy(MovieClipUser *user, int flag)
+{
+ if((flag&MCLIP_USE_PROXY)==0)
+ return IMB_PROXY_NONE;
+
+ switch(user->render_size) {
+ case MCLIP_PROXY_RENDER_SIZE_25:
+ return IMB_PROXY_25;
+
+ case MCLIP_PROXY_RENDER_SIZE_50:
+ return IMB_PROXY_50;
+
+ case MCLIP_PROXY_RENDER_SIZE_75:
+ return IMB_PROXY_75;
+
+ case MCLIP_PROXY_RENDER_SIZE_100:
+ return IMB_PROXY_100;
+
+ case MCLIP_PROXY_RENDER_SIZE_FULL:
+ return IMB_PROXY_NONE;
+ }
+
+ return IMB_PROXY_NONE;
+}
+
+static int rendersize_to_number(int render_size)
+{
+ switch(render_size) {
+ case MCLIP_PROXY_RENDER_SIZE_25:
+ return 25;
+
+ case MCLIP_PROXY_RENDER_SIZE_50:
+ return 50;
+
+ case MCLIP_PROXY_RENDER_SIZE_75:
+ return 75;
+
+ case MCLIP_PROXY_RENDER_SIZE_100:
+ return 100;
+
+ case MCLIP_PROXY_RENDER_SIZE_FULL:
+ return 100;
+ }
+
+ return 100;
+}
+
+static int get_timecode(MovieClip *clip, int flag)
+{
+ if((flag&MCLIP_USE_PROXY)==0)
+ return IMB_TC_NONE;
+
+ return clip->proxy.tc;
+}
+
+static void get_sequence_fname(MovieClip *clip, int framenr, char *name)
+{
+ unsigned short numlen;
+ char head[FILE_MAX], tail[FILE_MAX];
+ int offset;
+
+ BLI_strncpy(name, clip->name, sizeof(clip->name));
+ BLI_stringdec(name, head, tail, &numlen);
+
+ /* movieclips always points to first image from sequence,
+ autoguess offset for now. could be something smarter in the future */
+ offset= sequence_guess_offset(clip->name, strlen(head), numlen);
+
+ if(numlen) BLI_stringenc(name, head, tail, numlen, offset+framenr-1);
+ else strncpy(name, clip->name, sizeof(name));
+
+ if(clip->id.lib)
+ BLI_path_abs(name, clip->id.lib->filepath);
+ else
+ BLI_path_abs(name, G.main->name);
+}
+
+/* supposed to work with sequences only */
+static void get_proxy_fname(MovieClip *clip, int proxy_render_size, int undistorted, int framenr, char *name)
+{
+ int size= rendersize_to_number(proxy_render_size);
+ char dir[FILE_MAX], clipdir[FILE_MAX], clipfile[FILE_MAX];
+
+ BLI_split_dirfile(clip->name, clipdir, clipfile, FILE_MAX, FILE_MAX);
+
+ if(clip->flag&MCLIP_USE_PROXY_CUSTOM_DIR) {
+ strcpy(dir, clip->proxy.dir);
+ } else {
+ BLI_snprintf(dir, FILE_MAX, "%s/BL_proxy", clipdir);
+ }
+
+ if(undistorted)
+ BLI_snprintf(name, FILE_MAX, "%s/%s/proxy_%d_undistorted/%08d", dir, clipfile, size, framenr);
+ else
+ BLI_snprintf(name, FILE_MAX, "%s/%s/proxy_%d/%08d", dir, clipfile, size, framenr);
+
+ BLI_path_abs(name, G.main->name);
+ BLI_path_frame(name, 1, 0);
+
+ strcat(name, ".jpg");
+}
+
+static ImBuf *movieclip_load_sequence_file(MovieClip *clip, MovieClipUser *user, int framenr, int flag)
+{
+ struct ImBuf *ibuf;
+ char name[FILE_MAX];
+ int loadflag, size, undistort;
+
+ size= rendersize_to_number(user->render_size);
+
+ undistort= user->render_flag&MCLIP_PROXY_RENDER_UNDISTORT;
+
+ if((flag&MCLIP_USE_PROXY) && user->render_size != MCLIP_PROXY_RENDER_SIZE_FULL)
+ get_proxy_fname(clip, user->render_size, undistort, framenr, name);
+ else
+ get_sequence_fname(clip, framenr, name);
+
+ loadflag= IB_rect|IB_multilayer;
+
+ /* read ibuf */
+ ibuf= IMB_loadiffname(name, loadflag);
+
+ return ibuf;
+}
+
+static ImBuf *movieclip_load_movie_file(MovieClip *clip, MovieClipUser *user, int framenr, int flag)
+{
+ ImBuf *ibuf= NULL;
+ int tc= get_timecode(clip, flag);
+ int proxy= rendersize_to_proxy(user, flag);
+ char str[FILE_MAX];
+
+ if(!clip->anim) {
+ BLI_strncpy(str, clip->name, FILE_MAX);
+
+ if(clip->id.lib)
+ BLI_path_abs(str, clip->id.lib->filepath);
+ else
+ BLI_path_abs(str, G.main->name);
+
+ /* FIXME: make several stream accessible in image editor, too */
+ clip->anim= openanim(str, IB_rect, 0);
+
+ if(clip->anim) {
+ if(clip->flag&MCLIP_USE_PROXY_CUSTOM_DIR) {
+ char dir[FILE_MAX];
+ strcpy(dir, clip->proxy.dir);
+ BLI_path_abs(dir, G.main->name);
+ IMB_anim_set_index_dir(clip->anim, dir);
+ }
+ }
+ }
+
+ if(clip->anim) {
+ int dur;
+ int fra= framenr-1;
+
+ dur= IMB_anim_get_duration(clip->anim, tc);
+ fra= framenr-1;
+
+ if(fra<0)
+ fra= 0;
+
+ if(fra>(dur-1))
+ fra= dur-1;
+
+ ibuf= IMB_anim_absolute(clip->anim, fra, tc, proxy);
+ }
+
+ return ibuf;
+}
+
+/*********************** image buffer cache *************************/
+
+typedef struct MovieClipCache {
+ /* regular movie cache */
+ struct MovieCache *moviecache;
+
+ /* cache for stable shot */
+ int stable_framenr;
+ float stable_loc[2], stable_scale, stable_angle;
+ ImBuf *stableibuf;
+ int proxy;
+ short render_flag;
+
+ /* cache for undistorted shot */
+ int undist_framenr;
+ float principal[2];
+ float k1, k2, k3;
+ ImBuf *undistibuf;
+} MovieClipCache;
+
+typedef struct MovieClipImBufCacheKey {
+ int framenr;
+ int proxy;
+ short render_flag;
+} MovieClipImBufCacheKey;
+
+static void moviecache_keydata(void *userkey, int *framenr, int *proxy, int *render_flags)
+{
+ MovieClipImBufCacheKey *key= (MovieClipImBufCacheKey*)userkey;
+
+ *framenr= key->framenr;
+ *proxy= key->proxy;
+ *render_flags= key->render_flag;
+}
+
+static unsigned int moviecache_hashhash(const void *keyv)
+{
+ MovieClipImBufCacheKey *key= (MovieClipImBufCacheKey*)keyv;
+ int rval= key->framenr;
+
+ return rval;
+}
+
+static int moviecache_hashcmp(const void *av, const void *bv)
+{
+ const MovieClipImBufCacheKey *a= (MovieClipImBufCacheKey*)av;
+ const MovieClipImBufCacheKey *b= (MovieClipImBufCacheKey*)bv;
+
+ if(a->framenr<b->framenr) return -1;
+ else if(a->framenr>b->framenr) return 1;
+
+ if(a->proxy<b->proxy) return -1;
+ else if(a->proxy>b->proxy) return 1;
+
+ if(a->render_flag<b->render_flag) return -1;
+ else if(a->render_flag>b->render_flag) return 1;
+
+ return 0;
+}
+
+static ImBuf *get_imbuf_cache(MovieClip *clip, MovieClipUser *user, int flag)
+{
+ if(clip->cache) {
+ MovieClipImBufCacheKey key;
+
+ key.framenr= user?user->framenr:clip->lastframe;
+
+ if(flag&MCLIP_USE_PROXY) {
+ key.proxy= rendersize_to_proxy(user, flag);
+ key.render_flag= user->render_flag;
+ }
+ else {
+ key.proxy= IMB_PROXY_NONE;
+ key.render_flag= 0;
+ }
+
+ return IMB_moviecache_get(clip->cache->moviecache, &key);
+ }
+
+ return NULL;
+}
+
+static void put_imbuf_cache(MovieClip *clip, MovieClipUser *user, ImBuf *ibuf, int flag)
+{
+ MovieClipImBufCacheKey key;
+
+ if(!clip->cache) {
+ clip->cache= MEM_callocN(sizeof(MovieClipCache), "movieClipCache");
+
+ clip->cache->moviecache= IMB_moviecache_create(sizeof(MovieClipImBufCacheKey), moviecache_hashhash,
+ moviecache_hashcmp, moviecache_keydata);
+ }
+
+ key.framenr= user?user->framenr:clip->lastframe;
+
+ if(flag&MCLIP_USE_PROXY) {
+ key.proxy= rendersize_to_proxy(user, flag);
+ key.render_flag= user->render_flag;
+ }
+ else {
+ key.proxy= IMB_PROXY_NONE;
+ key.render_flag= 0;
+ }
+
+ IMB_moviecache_put(clip->cache->moviecache, &key, ibuf);
+}
+
+/*********************** common functions *************************/
+
+/* only image block itself */
+static MovieClip *movieclip_alloc(const char *name)
+{
+ MovieClip *clip;
+
+ clip= alloc_libblock(&G.main->movieclip, ID_MC, name);
+
+ clip->aspx= clip->aspy= 1.0f;
+
+ clip->tracking.camera.sensor_width= 35.0f;
+ clip->tracking.camera.pixel_aspect= 1.0f;
+ clip->tracking.camera.units= CAMERA_UNITS_MM;
+
+ clip->tracking.settings.frames_limit= 0;
+ clip->tracking.settings.keyframe1= 1;
+ clip->tracking.settings.keyframe2= 30;
+ clip->tracking.settings.dist= 1;
+
+ clip->tracking.stabilization.scaleinf= 1.0f;
+ clip->tracking.stabilization.locinf= 1.0f;
+ clip->tracking.stabilization.rotinf= 1.0f;
+ clip->tracking.stabilization.maxscale= 2.0f;
+
+ clip->proxy.build_size_flag= IMB_PROXY_25;
+ clip->proxy.build_tc_flag= IMB_TC_RECORD_RUN|IMB_TC_FREE_RUN|IMB_TC_INTERPOLATED_REC_DATE_FREE_RUN;
+ clip->proxy.quality= 90;
+
+ return clip;
+}
+
+/* checks if image was already loaded, then returns same image
+ otherwise creates new.
+ does not load ibuf itself
+ pass on optional frame for #name images */
+MovieClip *BKE_add_movieclip_file(const char *name)
+{
+ MovieClip *clip;
+ MovieClipUser user;
+ int file, len, width, height;
+ const char *libname;
+ char str[FILE_MAX], strtest[FILE_MAX];
+
+ BLI_strncpy(str, name, sizeof(str));
+ BLI_path_abs(str, G.main->name);
+
+ /* exists? */
+ file= open(str, O_BINARY|O_RDONLY);
+ if(file== -1) return NULL;
+ close(file);
+
+ /* ** first search an identical clip ** */
+ for(clip= G.main->movieclip.first; clip; clip= clip->id.next) {
+ BLI_strncpy(strtest, clip->name, sizeof(clip->name));
+ BLI_path_abs(strtest, G.main->name);
+
+ if(strcmp(strtest, str)==0) {
+ BLI_strncpy(clip->name, name, sizeof(clip->name)); /* for stringcode */
+ clip->id.us++; /* officially should not, it doesn't link here! */
+
+ return clip;
+ }
+ }
+
+ /* ** add new movieclip ** */
+
+ /* create a short library name */
+ len= strlen(name);
+
+ while (len > 0 && name[len - 1] != '/' && name[len - 1] != '\\') len--;
+ libname= name+len;
+
+ clip= movieclip_alloc(libname);
+ BLI_strncpy(clip->name, name, sizeof(clip->name));
+
+ if(BLI_testextensie_array(name, imb_ext_movie)) clip->source= MCLIP_SRC_MOVIE;
+ else clip->source= MCLIP_SRC_SEQUENCE;
+
+ user.framenr= 1;
+ BKE_movieclip_get_size(clip, &user, &width, &height);
+ if(width && height) {
+ clip->tracking.camera.principal[0]= ((float)width)/2;
+ clip->tracking.camera.principal[1]= ((float)height)/2;
+
+ clip->tracking.camera.focal= 24.0f*width/clip->tracking.camera.sensor_width;
+ }
+
+ return clip;
+}
+
+static void real_ibuf_size(MovieClip *clip, MovieClipUser *user, ImBuf *ibuf, int *width, int *height)
+{
+ *width= ibuf->x;
+ *height= ibuf->y;
+
+ if(clip->flag&MCLIP_USE_PROXY) {
+ switch(user->render_size) {
+ case MCLIP_PROXY_RENDER_SIZE_25:
+ (*width)*= 4;
+ (*height)*= 4;
+ break;
+
+ case MCLIP_PROXY_RENDER_SIZE_50:
+ (*width)*= 2.0f;
+ (*height)*= 2.0f;
+ break;
+
+ case MCLIP_PROXY_RENDER_SIZE_75:
+ *width= ((float)*width)*4.0f/3.0f;
+ *height= ((float)*height)*4.0f/3.0f;
+ break;
+ }
+ }
+}
+
+static int need_undistorted_cache(MovieClipUser *user, int flag)
+{
+ if (!user)
+ return 0;
+
+ /* only full undistorted render can be used as on-fly undistorting image */
+ if(flag&MCLIP_USE_PROXY) {
+ if(user->render_size != MCLIP_PROXY_RENDER_SIZE_FULL || (user->render_flag&MCLIP_PROXY_RENDER_UNDISTORT)==0)
+ return 0;
+ }
+ else return 0;
+
+ return 1;
+}
+
+static ImBuf *get_undistorted_cache(MovieClip *clip, MovieClipUser *user)
+{
+ MovieClipCache *cache= clip->cache;
+ MovieTrackingCamera *camera= &clip->tracking.camera;
+ int framenr= user?user->framenr:clip->lastframe;
+
+ /* no cache or no cached undistorted image */
+ if(!clip->cache || !clip->cache->undistibuf)
+ return NULL;
+
+ /* undistortion happened for other frame */
+ if(cache->undist_framenr!=framenr)
+ return NULL;
+
+ /* check for distortion model changes */
+ if(!equals_v2v2(camera->principal, cache->principal))
+ return NULL;
+
+ if(!equals_v3v3(&camera->k1, &cache->k1))
+ return NULL;
+
+ IMB_refImBuf(cache->undistibuf);
+
+ return cache->undistibuf;
+}
+
+static ImBuf *get_undistorted_ibuf(MovieClip *clip, struct MovieDistortion *distoriton, ImBuf *ibuf)
+{
+ ImBuf *undistibuf;
+
+ /* XXX: because of #27997 do not use float buffers to undistort,
+ otherwise, undistorted proxy can be darker than it should */
+ imb_freerectfloatImBuf(ibuf);
+
+ if(distoriton)
+ undistibuf= BKE_tracking_distortion_exec(distoriton, &clip->tracking, ibuf, ibuf->x, ibuf->y, 0.0f, 1);
+ else
+ undistibuf= BKE_tracking_undistort(&clip->tracking, ibuf, ibuf->x, ibuf->y, 0.0f);
+
+ if(undistibuf->userflags|= IB_RECT_INVALID) {
+ ibuf->userflags&= ~IB_RECT_INVALID;
+ IMB_rect_from_float(undistibuf);
+ }
+
+ IMB_scaleImBuf(undistibuf, ibuf->x, ibuf->y);
+
+ return undistibuf;
+}
+
+static ImBuf *put_undistorted_cache(MovieClip *clip, MovieClipUser *user, ImBuf *ibuf)
+{
+ MovieClipCache *cache= clip->cache;
+ MovieTrackingCamera *camera= &clip->tracking.camera;
+
+ copy_v2_v2(cache->principal, camera->principal);
+ copy_v3_v3(&cache->k1, &camera->k1);
+ cache->undist_framenr= user?user->framenr:clip->lastframe;
+
+ if(cache->undistibuf)
+ IMB_freeImBuf(cache->undistibuf);
+
+ cache->undistibuf= get_undistorted_ibuf(clip, NULL, ibuf);
+
+ if(cache->stableibuf) {
+ /* force stable buffer be re-calculated */
+ IMB_freeImBuf(cache->stableibuf);
+ cache->stableibuf= NULL;
+ }
+
+ IMB_refImBuf(cache->undistibuf);
+
+ return cache->undistibuf;
+}
+
+ImBuf *BKE_movieclip_get_ibuf(MovieClip *clip, MovieClipUser *user)
+{
+ ImBuf *ibuf= NULL;
+ int framenr= user?user->framenr:clip->lastframe;
+ int cache_undistorted= 0;
+
+ /* cache isn't threadsafe itself and also loading of movies
+ can't happen from concurent threads that's why we use lock here */
+ BLI_lock_thread(LOCK_MOVIECLIP);
+
+ /* try to obtain cached undistorted image first */
+ if(need_undistorted_cache(user, clip->flag)) {
+ ibuf= get_undistorted_cache(clip, user);
+ if(!ibuf)
+ cache_undistorted= 1;
+ }
+
+ if(!ibuf)
+ ibuf= get_imbuf_cache(clip, user, clip->flag);
+
+ if(!ibuf) {
+ int use_sequence= 1;
+
+ /* undistorted proxies for movies should be read as image sequence */
+ use_sequence&= user->render_flag&MCLIP_PROXY_RENDER_UNDISTORT;
+ use_sequence&= user->render_size!=MCLIP_PROXY_RENDER_SIZE_FULL;
+
+ if(clip->source==MCLIP_SRC_SEQUENCE || use_sequence)
+ ibuf= movieclip_load_sequence_file(clip, user, framenr, clip->flag);
+ else {
+ ibuf= movieclip_load_movie_file(clip, user, framenr, clip->flag);
+ }
+
+ if(ibuf)
+ put_imbuf_cache(clip, user, ibuf, clip->flag);
+ }
+
+ if(ibuf) {
+ clip->lastframe= framenr;
+ real_ibuf_size(clip, user, ibuf, &clip->lastsize[0], &clip->lastsize[1]);
+
+ /* put undistorted frame to cache */
+ if(cache_undistorted) {
+ ImBuf *tmpibuf= ibuf;
+ ibuf= put_undistorted_cache(clip, user, tmpibuf);
+ IMB_freeImBuf(tmpibuf);
+ }
+ }
+
+ BLI_unlock_thread(LOCK_MOVIECLIP);
+
+ return ibuf;
+}
+
+ImBuf *BKE_movieclip_get_ibuf_flag(MovieClip *clip, MovieClipUser *user, int flag)
+{
+ ImBuf *ibuf= NULL;
+ int framenr= user?user->framenr:clip->lastframe;
+ int cache_undistorted= 0;
+
+ /* cache isn't threadsafe itself and also loading of movies
+ can't happen from concurent threads that's why we use lock here */
+ BLI_lock_thread(LOCK_MOVIECLIP);
+
+ /* try to obtain cached undistorted image first */
+ if(need_undistorted_cache(user, flag)) {
+ ibuf= get_undistorted_cache(clip, user);
+ if(!ibuf)
+ cache_undistorted= 1;
+ }
+
+ ibuf= get_imbuf_cache(clip, user, flag);
+
+ if(!ibuf) {
+ if(clip->source==MCLIP_SRC_SEQUENCE) {
+ ibuf= movieclip_load_sequence_file(clip, user, framenr, flag);
+ } else {
+ ibuf= movieclip_load_movie_file(clip, user, framenr, flag);
+ }
+
+ if(ibuf) {
+ int bits= MCLIP_USE_PROXY|MCLIP_USE_PROXY_CUSTOM_DIR;
+
+ if((flag&bits)==(clip->flag&bits))
+ put_imbuf_cache(clip, user, ibuf, clip->flag);
+ }
+ }
+
+ /* put undistorted frame to cache */
+ if(ibuf && cache_undistorted) {
+ ImBuf *tmpibuf= ibuf;
+ ibuf= put_undistorted_cache(clip, user, tmpibuf);
+ IMB_freeImBuf(tmpibuf);
+ }
+
+ BLI_unlock_thread(LOCK_MOVIECLIP);
+
+ return ibuf;
+}
+
+ImBuf *BKE_movieclip_get_stable_ibuf(MovieClip *clip, MovieClipUser *user, float loc[2], float *scale, float *angle)
+{
+ ImBuf *ibuf, *stableibuf= NULL;
+ int framenr= user?user->framenr:clip->lastframe;
+
+ ibuf= BKE_movieclip_get_ibuf(clip, user);
+
+ if(!ibuf)
+ return NULL;
+
+ if(clip->tracking.stabilization.flag&TRACKING_2D_STABILIZATION) {
+ float tloc[2], tscale, tangle;
+ short proxy= IMB_PROXY_NONE;
+ int render_flag= 0;
+
+ if(clip->flag&MCLIP_USE_PROXY) {
+ proxy= rendersize_to_proxy(user, clip->flag);
+ render_flag= user->render_flag;
+ }
+
+ if(clip->cache->stableibuf && clip->cache->stable_framenr==framenr) { /* there's cached ibuf */
+ if(clip->cache->render_flag==render_flag && clip->cache->proxy==proxy) { /* cached ibuf used the same proxy settings */
+ stableibuf= clip->cache->stableibuf;
+
+ BKE_tracking_stabilization_data(&clip->tracking, framenr, stableibuf->x, stableibuf->y, tloc, &tscale, &tangle);
+
+ /* check for stabilization parameters */
+ if(!equals_v2v2(tloc, clip->cache->stable_loc) || tscale!=clip->cache->stable_scale || tangle!=clip->cache->stable_angle) {
+ stableibuf= NULL;
+ }
+ }
+ }
+
+ if(!stableibuf) {
+ if(clip->cache->stableibuf)
+ IMB_freeImBuf(clip->cache->stableibuf);
+
+ stableibuf= BKE_tracking_stabilize(&clip->tracking, framenr, ibuf, tloc, &tscale, &tangle);
+
+ copy_v2_v2(clip->cache->stable_loc, tloc);
+ clip->cache->stable_scale= tscale;
+ clip->cache->stable_angle= tangle;
+ clip->cache->stable_framenr= framenr;
+ clip->cache->stableibuf= stableibuf;
+ clip->cache->proxy= proxy;
+ clip->cache->render_flag= render_flag;
+ }
+
+ IMB_refImBuf(stableibuf);
+
+ if(loc) copy_v2_v2(loc, tloc);
+ if(scale) *scale= tscale;
+ if(angle) *angle= tangle;
+ } else {
+ if(loc) zero_v2(loc);
+ if(scale) *scale= 1.0f;
+ if(angle) *angle= 0.0f;
+
+ stableibuf= ibuf;
+ }
+
+ if(stableibuf!=ibuf) {
+ IMB_freeImBuf(ibuf);
+ ibuf= stableibuf;
+ }
+
+ return ibuf;
+
+}
+
+int BKE_movieclip_has_frame(MovieClip *clip, MovieClipUser *user)
+{
+ ImBuf *ibuf= BKE_movieclip_get_ibuf(clip, user);
+
+ if(ibuf) {
+ IMB_freeImBuf(ibuf);
+ return 1;
+ }
+
+ return 0;
+}
+
+void BKE_movieclip_get_size(MovieClip *clip, MovieClipUser *user, int *width, int *height)
+{
+ if(!user || user->framenr==clip->lastframe) {
+ *width= clip->lastsize[0];
+ *height= clip->lastsize[1];
+ } else {
+ ImBuf *ibuf= BKE_movieclip_get_ibuf(clip, user);
+
+ if(ibuf && ibuf->x && ibuf->y) {
+ real_ibuf_size(clip, user, ibuf, width, height);
+ } else {
+ *width= 0;
+ *height= 0;
+ }
+
+ if(ibuf)
+ IMB_freeImBuf(ibuf);
+ }
+}
+
+void BKE_movieclip_aspect(MovieClip *clip, float *aspx, float *aspy)
+{
+ *aspx= *aspy= 1.0;
+
+ /* x is always 1 */
+ *aspy = clip->aspy/clip->aspx/clip->tracking.camera.pixel_aspect;
+}
+
+/* get segments of cached frames. useful for debugging cache policies */
+void BKE_movieclip_get_cache_segments(MovieClip *clip, MovieClipUser *user, int *totseg_r, int **points_r)
+{
+ *totseg_r= 0;
+ *points_r= NULL;
+
+ if(clip->cache) {
+ int proxy= rendersize_to_proxy(user, clip->flag);
+
+ IMB_moviecache_get_cache_segments(clip->cache->moviecache, proxy, user->render_flag, totseg_r, points_r);
+ }
+}
+
+void BKE_movieclip_user_set_frame(MovieClipUser *iuser, int framenr)
+{
+ /* TODO: clamp framenr here? */
+
+ iuser->framenr= framenr;
+}
+
+static void free_buffers(MovieClip *clip)
+{
+ if(clip->cache) {
+ IMB_moviecache_free(clip->cache->moviecache);
+
+ if(clip->cache->stableibuf)
+ IMB_freeImBuf(clip->cache->stableibuf);
+
+ if(clip->cache->undistibuf)
+ IMB_freeImBuf(clip->cache->undistibuf);
+
+ MEM_freeN(clip->cache);
+ clip->cache= NULL;
+ }
+
+ if(clip->anim) {
+ IMB_free_anim(clip->anim);
+ clip->anim= FALSE;
+ }
+}
+
+void BKE_movieclip_reload(MovieClip *clip)
+{
+ /* clear cache */
+ free_buffers(clip);
+
+ clip->tracking.stabilization.ok= 0;
+
+ /* update clip source */
+ if(BLI_testextensie_array(clip->name, imb_ext_movie)) clip->source= MCLIP_SRC_MOVIE;
+ else clip->source= MCLIP_SRC_SEQUENCE;
+}
+
+void BKE_movieclip_update_scopes(MovieClip *clip, MovieClipUser *user, MovieClipScopes *scopes)
+{
+ if(scopes->ok) return;
+
+ if(scopes->track_preview) {
+ IMB_freeImBuf(scopes->track_preview);
+ scopes->track_preview= NULL;
+ }
+
+ scopes->marker= NULL;
+ scopes->track= NULL;
+
+ if(clip) {
+ if(clip->tracking.act_track) {
+ MovieTrackingTrack *track= clip->tracking.act_track;
+ MovieTrackingMarker *marker= BKE_tracking_get_marker(track, user->framenr);
+
+ if(marker->flag&MARKER_DISABLED) {
+ scopes->track_disabled= 1;
+ } else {
+ ImBuf *ibuf= BKE_movieclip_get_ibuf(clip, user);
+
+ scopes->track_disabled= 0;
+
+ if(ibuf && ibuf->rect) {
+ ImBuf *tmpibuf;
+ MovieTrackingMarker undist_marker= *marker;
+
+ if(user->render_flag&MCLIP_PROXY_RENDER_UNDISTORT) {
+ int width, height;
+ float aspy= 1.0f/clip->tracking.camera.pixel_aspect;;
+
+ BKE_movieclip_get_size(clip, user, &width, &height);
+
+ undist_marker.pos[0]*= width;
+ undist_marker.pos[1]*= height*aspy;
+
+ BKE_tracking_invert_intrinsics(&clip->tracking, undist_marker.pos, undist_marker.pos);
+
+ undist_marker.pos[0]/= width;
+ undist_marker.pos[1]/= height*aspy;
+ }
+
+ tmpibuf= BKE_tracking_get_pattern_imbuf(ibuf, track, &undist_marker, 1, 1, scopes->track_pos, NULL);
+
+ if(tmpibuf->rect_float)
+ IMB_rect_from_float(tmpibuf);
+
+ if(tmpibuf->rect)
+ scopes->track_preview= tmpibuf;
+ else
+ IMB_freeImBuf(tmpibuf);
+ }
+
+ IMB_freeImBuf(ibuf);
+ }
+
+ if((track->flag&TRACK_LOCKED)==0) {
+ scopes->marker= marker;
+ scopes->track= track;
+ scopes->slide_scale[0]= track->pat_max[0]-track->pat_min[0];
+ scopes->slide_scale[1]= track->pat_max[1]-track->pat_min[1];
+ }
+ }
+ }
+
+ scopes->framenr= user->framenr;
+ scopes->ok= 1;
+}
+
+static void movieclip_build_proxy_ibuf(MovieClip *clip, ImBuf *ibuf, int cfra, int proxy_render_size, int undistorted)
+{
+ char name[FILE_MAXFILE+FILE_MAXDIR];
+ int quality, rectx, recty;
+ int size= size= rendersize_to_number(proxy_render_size);
+ ImBuf *scaleibuf;
+
+ get_proxy_fname(clip, proxy_render_size, undistorted, cfra, name);
+
+ rectx= ibuf->x*size/100.0f;
+ recty= ibuf->y*size/100.0f;
+
+ scaleibuf= IMB_dupImBuf(ibuf);
+
+ IMB_scaleImBuf(scaleibuf, (short)rectx, (short)recty);
+
+ quality= clip->proxy.quality;
+ scaleibuf->ftype= JPG | quality;
+
+ /* unsupported feature only confuses other s/w */
+ if(scaleibuf->depth==32)
+ scaleibuf->depth= 24;
+
+ BLI_lock_thread(LOCK_MOVIECLIP);
+
+ BLI_make_existing_file(name);
+ if(IMB_saveiff(scaleibuf, name, IB_rect)==0)
+ perror(name);
+
+ BLI_unlock_thread(LOCK_MOVIECLIP);
+
+ IMB_freeImBuf(scaleibuf);
+}
+
+void BKE_movieclip_build_proxy_frame(MovieClip *clip, struct MovieDistortion *distortion,
+ int cfra, int *build_sizes, int build_count, int undistorted)
+{
+ ImBuf *ibuf;
+ MovieClipUser user;
+
+ user.framenr= cfra;
+
+ ibuf= BKE_movieclip_get_ibuf_flag(clip, &user, 0);
+
+ if(ibuf) {
+ ImBuf *tmpibuf= ibuf;
+ int i;
+
+ if(undistorted)
+ tmpibuf= get_undistorted_ibuf(clip, distortion, ibuf);
+
+ for(i= 0; i<build_count; i++)
+ movieclip_build_proxy_ibuf(clip, tmpibuf, cfra, build_sizes[i], undistorted);
+
+ IMB_freeImBuf(ibuf);
+
+ if(tmpibuf!=ibuf)
+ IMB_freeImBuf(tmpibuf);
+ }
+}
+
+void free_movieclip(MovieClip *clip)
+{
+ free_buffers(clip);
+
+ BKE_tracking_free(&clip->tracking);
+}
+
+void unlink_movieclip(Main *bmain, MovieClip *clip)
+{
+ bScreen *scr;
+ ScrArea *area;
+ SpaceLink *sl;
+ Scene *sce;
+ Object *ob;
+
+ for(scr= bmain->screen.first; scr; scr= scr->id.next) {
+ for(area= scr->areabase.first; area; area= area->next) {
+ for(sl= area->spacedata.first; sl; sl= sl->next) {
+ if(sl->spacetype==SPACE_CLIP) {
+ SpaceClip *sc= (SpaceClip *) sl;
+
+ if(sc->clip==clip)
+ sc->clip= NULL;
+ }
+ else if(sl->spacetype==SPACE_VIEW3D) {
+ View3D *v3d= (View3D *) sl;
+ BGpic *bgpic;
+
+ for(bgpic= v3d->bgpicbase.first; bgpic; bgpic= bgpic->next) {
+ if(bgpic->clip==clip)
+ bgpic->clip= NULL;
+ }
+ }
+ }
+ }
+ }
+
+ for(sce= bmain->scene.first; sce; sce= sce->id.next) {
+ if(sce->clip==clip)
+ sce->clip= NULL;
+ }
+
+ for(ob= bmain->object.first; ob; ob= ob->id.next) {
+ bConstraint *con= ob->constraints.first;
+
+ for (con= ob->constraints.first; con; con= con->next) {
+ bConstraintTypeInfo *cti= constraint_get_typeinfo(con);
+
+ if(cti->type==CONSTRAINT_TYPE_FOLLOWTRACK) {
+ bFollowTrackConstraint *data= (bFollowTrackConstraint *) con->data;
+
+ if(data->clip==clip)
+ data->clip= NULL;
+ }
+ else if(cti->type==CONSTRAINT_TYPE_CAMERASOLVER) {
+ bCameraSolverConstraint *data= (bCameraSolverConstraint *) con->data;
+
+ if(data->clip==clip)
+ data->clip= NULL;
+ }
+ }
+ }
+
+ clip->id.us= 0;
+}
diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c
index 1d654839969..45bd8f095a4 100644
--- a/source/blender/blenkernel/intern/object.c
+++ b/source/blender/blenkernel/intern/object.c
@@ -46,6 +46,7 @@
#include "DNA_material_types.h"
#include "DNA_meta_types.h"
#include "DNA_meshdata_types.h"
+#include "DNA_movieclip_types.h"
#include "DNA_scene_types.h"
#include "DNA_screen_types.h"
#include "DNA_sequence_types.h"
@@ -1810,11 +1811,30 @@ void set_no_parent_ipo(int val)
no_parent_ipo= val;
}
-void where_is_object_time(Scene *scene, Object *ob, float ctime)
+static int where_is_object_parslow(Object *ob, float obmat[4][4], float slowmat[4][4])
{
- float *fp1, *fp2, slowmat[4][4] = MAT4_UNITY;
- float stime=ctime, fac1, fac2;
+ float *fp1, *fp2;
+ float fac1, fac2;
int a;
+
+ // include framerate
+ fac1= ( 1.0f / (1.0f + (float)fabs(ob->sf)) );
+ if(fac1 >= 1.0f) return 0;
+ fac2= 1.0f-fac1;
+
+ fp1= obmat[0];
+ fp2= slowmat[0];
+ for(a=0; a<16; a++, fp1++, fp2++) {
+ fp1[0]= fac1*fp1[0] + fac2*fp2[0];
+ }
+
+ return 1;
+}
+
+void where_is_object_time(Scene *scene, Object *ob, float ctime)
+{
+ float slowmat[4][4] = MAT4_UNITY;
+ float stime=ctime;
/* new version: correct parent+vertexparent and track+parent */
/* this one only calculates direct attached parent and track */
@@ -1848,16 +1868,8 @@ void where_is_object_time(Scene *scene, Object *ob, float ctime)
* An old-fashioned hack which probably doesn't really cut it anymore
*/
if(ob->partype & PARSLOW) {
- // include framerate
- fac1= ( 1.0f / (1.0f + (float)fabs(ob->sf)) );
- if(fac1 >= 1.0f) return;
- fac2= 1.0f-fac1;
-
- fp1= ob->obmat[0];
- fp2= slowmat[0];
- for(a=0; a<16; a++, fp1++, fp2++) {
- fp1[0]= fac1*fp1[0] + fac2*fp2[0];
- }
+ if(!where_is_object_parslow(ob, ob->obmat, slowmat))
+ return;
}
}
else {
@@ -1881,6 +1893,27 @@ void where_is_object_time(Scene *scene, Object *ob, float ctime)
else ob->transflag &= ~OB_NEG_SCALE;
}
+/* get object transformation matrix without recalculating dependencies and
+ constraints -- assume dependencies are already solved by depsgraph.
+ no changes to object and it's parent would be done.
+ used for bundles orientation in 3d space relative to parented blender camera */
+void where_is_object_mat(Scene *scene, Object *ob, float obmat[4][4])
+{
+ float slowmat[4][4] = MAT4_UNITY;
+
+ if(ob->parent) {
+ Object *par= ob->parent;
+
+ solve_parenting(scene, ob, par, obmat, slowmat, 1);
+
+ if(ob->partype & PARSLOW)
+ where_is_object_parslow(ob, obmat, slowmat);
+ }
+ else {
+ object_to_mat4(ob, obmat);
+ }
+}
+
static void solve_parenting (Scene *scene, Object *ob, Object *par, float obmat[][4], float slowmat[][4], int simul)
{
float totmat[4][4];
@@ -2811,3 +2844,28 @@ void object_relink(Object *ob)
ID_NEW(ob->proxy);
ID_NEW(ob->proxy_group);
}
+
+MovieClip *object_get_movieclip(Scene *scene, Object *ob, int use_default)
+{
+ MovieClip *clip= use_default ? scene->clip : NULL;
+ bConstraint *con= ob->constraints.first, *scon= NULL;
+
+ while(con){
+ if(con->type==CONSTRAINT_TYPE_CAMERASOLVER){
+ if(scon==NULL || (scon->flag&CONSTRAINT_OFF))
+ scon= con;
+ }
+
+ con= con->next;
+ }
+
+ if(scon) {
+ bCameraSolverConstraint *solver= scon->data;
+ if((solver->flag&CAMERASOLVER_ACTIVECLIP)==0)
+ clip= solver->clip;
+ else
+ clip= scene->clip;
+ }
+
+ return clip;
+}
diff --git a/source/blender/blenkernel/intern/tracking.c b/source/blender/blenkernel/intern/tracking.c
new file mode 100644
index 00000000000..06a985e7d72
--- /dev/null
+++ b/source/blender/blenkernel/intern/tracking.c
@@ -0,0 +1,2168 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2011 Blender Foundation.
+ * All rights reserved.
+ *
+ * Contributor(s): Blender Foundation,
+ * Sergey Sharybin
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/blenkernel/intern/tracking.c
+ * \ingroup bke
+ */
+
+#include <stddef.h>
+#include <limits.h>
+#include <math.h>
+#include <memory.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_gpencil_types.h"
+#include "DNA_camera_types.h"
+#include "DNA_movieclip_types.h"
+#include "DNA_object_types.h" /* SELECT */
+#include "DNA_scene_types.h"
+
+#include "BLI_utildefines.h"
+#include "BLI_math.h"
+#include "BLI_math_base.h"
+#include "BLI_listbase.h"
+#include "BLI_ghash.h"
+#include "BLI_path_util.h"
+
+#include "BKE_global.h"
+#include "BKE_tracking.h"
+#include "BKE_movieclip.h"
+#include "BKE_object.h"
+#include "BKE_scene.h"
+
+#include "IMB_imbuf_types.h"
+#include "IMB_imbuf.h"
+
+#ifdef WITH_LIBMV
+#include "libmv-capi.h"
+#endif
+
+typedef struct MovieDistortion {
+ struct libmv_CameraIntrinsics *intrinsics;
+} MovieDistortion;
+
+/*********************** common functions *************************/
+
+void BKE_tracking_clamp_track(MovieTrackingTrack *track, int event)
+{
+ int a;
+ float pat_min[2];
+ float pat_max[2];
+ float max_pyramid_level_factor = 1.0;
+ if (track->tracker == TRACKER_KLT) {
+ max_pyramid_level_factor = 1 << (track->pyramid_levels - 1);
+ }
+
+ /* sort */
+ for(a= 0; a<2; a++) {
+ if(track->pat_min[a]>track->pat_max[a])
+ SWAP(float, track->pat_min[a], track->pat_max[a]);
+
+ if(track->search_min[a]>track->search_max[a])
+ SWAP(float, track->search_min[a], track->search_max[a]);
+ }
+
+ /* compute the effective pattern size, which differs from the fine resolution
+ * pattern size for the pyramid KLT tracker */
+ for(a= 0; a<2; a++) {
+ pat_min[a] = max_pyramid_level_factor * track->pat_min[a];
+ pat_max[a] = max_pyramid_level_factor * track->pat_max[a];
+ }
+
+ if(event==CLAMP_PAT_DIM) {
+ for(a= 0; a<2; a++) {
+ /* search shouldn't be resized smaller than pattern */
+ track->search_min[a]= MIN2(pat_min[a], track->search_min[a]);
+ track->search_max[a]= MAX2(pat_max[a], track->search_max[a]);
+ }
+ }
+ else if(event==CLAMP_PAT_POS) {
+ float dim[2];
+ sub_v2_v2v2(dim, track->pat_max, pat_min);
+
+ for(a= 0; a<2; a++) {
+ /* pattern shouldn't be moved outside of search */
+ if(pat_min[a] < track->search_min[a]) {
+ track->pat_min[a]= track->search_min[a] - (pat_min[a] - track->pat_min[a]);
+ track->pat_max[a]= (pat_min[a] - track->pat_min[a])+dim[a];
+ }
+ if(track->pat_max[a] > track->search_max[a]) {
+ track->pat_max[a]= track->search_max[a] - (pat_max[a] - track->pat_max[a]);
+ track->pat_min[a]= track->pat_max[a]-dim[a] - (pat_min[a] - track->pat_min[a]);
+ }
+ }
+ }
+ else if(event==CLAMP_SEARCH_DIM) {
+ float max_pyramid_level_factor = 1.0;
+ if (track->tracker == TRACKER_KLT) {
+ max_pyramid_level_factor = 1 << (track->pyramid_levels - 1);
+ }
+ for(a= 0; a<2; a++) {
+ /* search shouldn't be resized smaller than pattern */
+ track->search_min[a]= MIN2(pat_min[a], track->search_min[a]);
+ track->search_max[a]= MAX2(pat_max[a], track->search_max[a]);
+ }
+ }
+ else if(event==CLAMP_SEARCH_POS) {
+ float dim[2];
+ sub_v2_v2v2(dim, track->search_max, track->search_min);
+
+ for(a= 0; a<2; a++) {
+ /* search shouldn't be moved inside pattern */
+ if(track->search_min[a] > pat_min[a]) {
+ track->search_min[a]= pat_min[a];
+ track->search_max[a]= track->search_min[a]+dim[a];
+ }
+ if(track->search_max[a] < pat_max[a]) {
+ track->search_max[a]= pat_max[a];
+ track->search_min[a]= track->search_max[a]-dim[a];
+ }
+ }
+ }
+
+ else if(event==CLAMP_PYRAMID_LEVELS || (event==CLAMP_SEARCH_DIM && track->tracker == TRACKER_KLT)) {
+ float dim[2];
+ sub_v2_v2v2(dim, track->pat_max, track->pat_min);
+ {
+ float search_ratio = 2.3f * max_pyramid_level_factor;
+
+ /* resize the search area to something sensible based
+ * on the number of pyramid levels */
+ for(a= 0; a<2; a++) {
+ track->search_min[a]= search_ratio * track->pat_min[a];
+ track->search_max[a]= search_ratio * track->pat_max[a];
+ }
+ }
+ }
+
+ /* marker's center should be in center of pattern */
+ if(event==CLAMP_PAT_DIM || event==CLAMP_PAT_POS) {
+ float dim[2];
+ sub_v2_v2v2(dim, track->pat_max, track->pat_min);
+
+ for(a= 0; a<2; a++) {
+ track->pat_min[a]= -dim[a]/2.0f;
+ track->pat_max[a]= dim[a]/2.0f;
+ }
+ }
+}
+
+void BKE_tracking_track_flag(MovieTrackingTrack *track, int area, int flag, int clear)
+{
+ if(area==TRACK_AREA_NONE)
+ return;
+
+ if(clear) {
+ if(area&TRACK_AREA_POINT) track->flag&= ~flag;
+ if(area&TRACK_AREA_PAT) track->pat_flag&= ~flag;
+ if(area&TRACK_AREA_SEARCH) track->search_flag&= ~flag;
+ } else {
+ if(area&TRACK_AREA_POINT) track->flag|= flag;
+ if(area&TRACK_AREA_PAT) track->pat_flag|= flag;
+ if(area&TRACK_AREA_SEARCH) track->search_flag|= flag;
+ }
+}
+
+MovieTrackingTrack *BKE_tracking_add_track(MovieTracking *tracking, float x, float y,
+ int framenr, int width, int height)
+{
+ MovieTrackingTrack *track;
+ MovieTrackingMarker marker;
+
+ /* pick reasonable defaults */
+ float pat[2]= {5.5f, 5.5f}, search[2]= {25.5f, 25.5f}; /* TODO: move to default setting? */
+
+ pat[0] /= (float)width;
+ pat[1] /= (float)height;
+
+ search[0] /= (float)width;
+ search[1] /= (float)height;
+
+ track= MEM_callocN(sizeof(MovieTrackingTrack), "add_marker_exec track");
+ strcpy(track->name, "Track");
+
+ /* default to KLT tracker */
+ track->tracker = TRACKER_KLT;
+ track->pyramid_levels = 2;
+
+ /* set SAD defaults even though it's not selected by default */
+ track->minimum_correlation= 0.75f;
+
+ memset(&marker, 0, sizeof(marker));
+ marker.pos[0]= x;
+ marker.pos[1]= y;
+ marker.framenr= framenr;
+
+ copy_v2_v2(track->pat_max, pat);
+ negate_v2_v2(track->pat_min, pat);
+
+ copy_v2_v2(track->search_max, search);
+ negate_v2_v2(track->search_min, search);
+
+ BKE_tracking_insert_marker(track, &marker);
+
+ BLI_addtail(&tracking->tracks, track);
+ BKE_track_unique_name(tracking, track);
+
+ return track;
+}
+
+void BKE_tracking_insert_marker(MovieTrackingTrack *track, MovieTrackingMarker *marker)
+{
+ MovieTrackingMarker *old_marker= BKE_tracking_get_marker(track, marker->framenr);
+
+ if(old_marker && old_marker->framenr==marker->framenr) {
+ *old_marker= *marker;
+ } else {
+ int a= track->markersnr;
+
+ while(a--) {
+ if(track->markers[a].framenr<marker->framenr)
+ break;
+ }
+
+ track->markersnr++;
+
+ if(track->markers) track->markers= MEM_reallocN(track->markers, sizeof(MovieTrackingMarker)*track->markersnr);
+ else track->markers= MEM_callocN(sizeof(MovieTrackingMarker), "MovieTracking markers");
+
+ memmove(track->markers+a+2, track->markers+a+1, (track->markersnr-a-2)*sizeof(MovieTrackingMarker));
+ track->markers[a+1]= *marker;
+
+ track->last_marker= a+1;
+ }
+}
+
+void BKE_tracking_delete_marker(MovieTrackingTrack *track, int framenr)
+{
+ int a= 0;
+
+ while(a<track->markersnr) {
+ if(track->markers[a].framenr==framenr) {
+ if(track->markersnr>1) {
+ memmove(track->markers+a, track->markers+a+1, (track->markersnr-a-1)*sizeof(MovieTrackingMarker));
+ track->markersnr--;
+ track->markers= MEM_reallocN(track->markers, sizeof(MovieTrackingMarker)*track->markersnr);
+ } else {
+ MEM_freeN(track->markers);
+ track->markers= NULL;
+ track->markersnr= 0;
+ }
+
+ break;
+ }
+
+ a++;
+ }
+}
+
+MovieTrackingMarker *BKE_tracking_get_marker(MovieTrackingTrack *track, int framenr)
+{
+ int a= track->markersnr-1;
+
+ if(!track->markersnr)
+ return NULL;
+
+ /* approximate pre-first framenr marker with first marker */
+ if(framenr<track->markers[0].framenr)
+ return &track->markers[0];
+
+ if(track->last_marker<track->markersnr)
+ a= track->last_marker;
+
+ if(track->markers[a].framenr<=framenr) {
+ while(a<track->markersnr && track->markers[a].framenr<=framenr) {
+ if(track->markers[a].framenr==framenr) {
+ track->last_marker= a;
+ return &track->markers[a];
+ }
+ a++;
+ }
+
+ /* if there's no marker for exact position, use nearest marker from left side */
+ return &track->markers[a-1];
+ } else {
+ while(a>=0 && track->markers[a].framenr>=framenr) {
+ if(track->markers[a].framenr==framenr) {
+ track->last_marker= a;
+ return &track->markers[a];
+ }
+
+ a--;
+ }
+
+ /* if there's no marker for exact position, use nearest marker from left side */
+ return &track->markers[a];
+ }
+
+ return NULL;
+}
+
+MovieTrackingMarker *BKE_tracking_ensure_marker(MovieTrackingTrack *track, int framenr)
+{
+ MovieTrackingMarker *marker= BKE_tracking_get_marker(track, framenr);
+
+ if(marker && marker->framenr!=framenr) {
+ MovieTrackingMarker marker_new;
+
+ marker_new= *marker;
+ marker_new.framenr= framenr;
+
+ BKE_tracking_insert_marker(track, &marker_new);
+ marker= BKE_tracking_get_marker(track, framenr);
+ }
+
+ return marker;
+}
+
+MovieTrackingMarker *BKE_tracking_exact_marker(MovieTrackingTrack *track, int framenr)
+{
+ MovieTrackingMarker *marker= BKE_tracking_get_marker(track, framenr);
+
+ if(marker && marker->framenr!=framenr)
+ return NULL;
+
+ return marker;
+}
+
+int BKE_tracking_has_marker(MovieTrackingTrack *track, int framenr)
+{
+ return BKE_tracking_exact_marker(track, framenr) != 0;
+}
+
+void BKE_tracking_free_track(MovieTrackingTrack *track)
+{
+ if(track->markers) MEM_freeN(track->markers);
+}
+
+MovieTrackingTrack *BKE_tracking_copy_track(MovieTrackingTrack *track)
+{
+ MovieTrackingTrack *new_track= MEM_dupallocN(track);
+
+ new_track->next= new_track->prev= NULL;
+
+ if(new_track->markers)
+ new_track->markers= MEM_dupallocN(new_track->markers);
+
+ return new_track;
+}
+
+static void put_disabled_marker(MovieTrackingTrack *track, MovieTrackingMarker *ref_marker, int before, int overwrite)
+{
+ MovieTrackingMarker marker_new;
+
+ marker_new= *ref_marker;
+ marker_new.flag&= ~MARKER_TRACKED;
+ marker_new.flag|= MARKER_DISABLED;
+
+ if(before) marker_new.framenr--;
+ else marker_new.framenr++;
+
+ if(!BKE_tracking_has_marker(track, marker_new.framenr) || overwrite)
+ BKE_tracking_insert_marker(track, &marker_new);
+}
+
+void BKE_tracking_clear_path(MovieTrackingTrack *track, int ref_frame, int action)
+{
+ int a;
+
+ if(action==TRACK_CLEAR_REMAINED) {
+ a= 1;
+ while(a<track->markersnr) {
+ if(track->markers[a].framenr>ref_frame) {
+ track->markersnr= a;
+ track->markers= MEM_reallocN(track->markers, sizeof(MovieTrackingMarker)*track->markersnr);
+
+ break;
+ }
+
+ a++;
+ }
+
+ if(track->markersnr)
+ put_disabled_marker(track, &track->markers[track->markersnr-1], 0, 1);
+ } else if(action==TRACK_CLEAR_UPTO) {
+ a= track->markersnr-1;
+ while(a>=0) {
+ if(track->markers[a].framenr<=ref_frame) {
+ memmove(track->markers, track->markers+a, (track->markersnr-a)*sizeof(MovieTrackingMarker));
+
+ track->markersnr= track->markersnr-a;
+ track->markers= MEM_reallocN(track->markers, sizeof(MovieTrackingMarker)*track->markersnr);
+
+ break;
+ }
+
+ a--;
+ }
+
+ if(track->markersnr)
+ put_disabled_marker(track, &track->markers[0], 1, 1);
+ } else if(action==TRACK_CLEAR_ALL) {
+ MovieTrackingMarker *marker, marker_new;
+
+ marker= BKE_tracking_get_marker(track, ref_frame);
+ marker_new= *marker;
+
+ MEM_freeN(track->markers);
+ track->markers= NULL;
+ track->markersnr= 0;
+
+ BKE_tracking_insert_marker(track, &marker_new);
+
+ put_disabled_marker(track, &marker_new, 1, 1);
+ put_disabled_marker(track, &marker_new, 0, 1);
+ }
+}
+
+int BKE_tracking_test_join_tracks(MovieTrackingTrack *dst_track, MovieTrackingTrack *src_track)
+{
+ int i, a= 0, b= 0, tot= dst_track->markersnr+src_track->markersnr;
+ int count= 0;
+
+ for(i= 0; i<tot; i++) {
+ if(a>=src_track->markersnr) {
+ b++;
+ count++;
+ }
+ else if(b>=dst_track->markersnr) {
+ a++;
+ count++;
+ }
+ else if(src_track->markers[a].framenr<dst_track->markers[b].framenr) {
+ a++;
+ count++;
+ } else if(src_track->markers[a].framenr>dst_track->markers[b].framenr) {
+ b++;
+ count++;
+ } else {
+ if((src_track->markers[a].flag&MARKER_DISABLED)==0 && (dst_track->markers[b].flag&MARKER_DISABLED)==0)
+ return 0;
+
+ a++;
+ b++;
+ count++;
+ }
+ }
+
+ return count;
+}
+
+void BKE_tracking_join_tracks(MovieTrackingTrack *dst_track, MovieTrackingTrack *src_track)
+{
+ int i, a= 0, b= 0, tot;
+ MovieTrackingMarker *markers;
+
+ tot= BKE_tracking_test_join_tracks(dst_track, src_track);
+
+ markers= MEM_callocN(tot*sizeof(MovieTrackingMarker), "tracking joined tracks");
+
+ for(i= 0; i<tot; i++) {
+ if(b>=dst_track->markersnr) {
+ markers[i]= src_track->markers[a++];
+ }
+ else if(a>=src_track->markersnr) {
+ markers[i]= dst_track->markers[b++];
+ }
+ else if(src_track->markers[a].framenr<dst_track->markers[b].framenr) {
+ markers[i]= src_track->markers[a++];
+ } else if(src_track->markers[a].framenr>dst_track->markers[b].framenr) {
+ markers[i]= dst_track->markers[b++];
+ } else {
+ if((src_track->markers[a].flag&MARKER_DISABLED)) markers[i]= dst_track->markers[b];
+ else markers[i]= src_track->markers[a++];
+
+ a++;
+ b++;
+ }
+ }
+
+ MEM_freeN(dst_track->markers);
+
+ dst_track->markers= markers;
+ dst_track->markersnr= tot;
+}
+
+void BKE_tracking_free(MovieTracking *tracking)
+{
+ MovieTrackingTrack *track;
+
+ for(track= tracking->tracks.first; track; track= track->next) {
+ BKE_tracking_free_track(track);
+ }
+
+ BLI_freelistN(&tracking->tracks);
+
+ if(tracking->reconstruction.cameras)
+ MEM_freeN(tracking->reconstruction.cameras);
+
+ if(tracking->stabilization.scaleibuf)
+ IMB_freeImBuf(tracking->stabilization.scaleibuf);
+
+ if(tracking->camera.intrinsics)
+ BKE_tracking_distortion_destroy(tracking->camera.intrinsics);
+}
+
+/*********************** tracking *************************/
+
+typedef struct TrackContext {
+ MovieTrackingTrack *track;
+
+#ifdef WITH_LIBMV
+ float keyframed_pos[2];
+
+ /* ** KLT tracker ** */
+ struct libmv_RegionTracker *region_tracker;
+ float *patch; /* keyframed patch */
+
+ /* ** SAD tracker ** */
+ int patsize; /* size of pattern (currently only 16x16 due to libmv side) */
+ unsigned char *pattern; /* keyframed pattern */
+ unsigned char *warped; /* warped version of reference */
+#endif
+} TrackContext;
+
+typedef struct MovieTrackingContext {
+ MovieClipUser user;
+ MovieClip *clip;
+
+ int first_time, frames;
+
+ TrackContext *track_context;
+ int num_tracks;
+
+ GHash *hash;
+ MovieTrackingSettings settings;
+
+ short backwards, disable_failed;
+ int sync_frame;
+} MovieTrackingContext;
+
+MovieTrackingContext *BKE_tracking_context_new(MovieClip *clip, MovieClipUser *user, short backwards, short disable_failed)
+{
+ MovieTrackingContext *context= MEM_callocN(sizeof(MovieTrackingContext), "trackingContext");
+ MovieTracking *tracking= &clip->tracking;
+ MovieTrackingSettings *settings= &tracking->settings;
+ MovieTrackingTrack *track;
+ TrackContext *track_context;
+
+ context->settings= *settings;
+ context->backwards= backwards;
+ context->disable_failed= disable_failed;
+ context->hash= BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "tracking trackHash");
+ context->sync_frame= user->framenr;
+ context->first_time= 1;
+
+ /* count */
+ track= tracking->tracks.first;
+ while(track) {
+ if(TRACK_SELECTED(track) && (track->flag&TRACK_LOCKED)==0) {
+ MovieTrackingMarker *marker= BKE_tracking_get_marker(track, user->framenr);
+
+ if((marker->flag&MARKER_DISABLED)==0)
+ context->num_tracks++;
+ }
+
+ track= track->next;
+ }
+
+ if(context->num_tracks) {
+ int width, height;
+
+ BKE_movieclip_get_size(clip, user, &width, &height);
+
+ /* create tracking data */
+ context->track_context= MEM_callocN(sizeof(TrackContext)*context->num_tracks, "tracking track_context");
+
+ track_context= context->track_context;
+ track= tracking->tracks.first;
+ while(track) {
+ if(TRACK_SELECTED(track) && (track->flag&TRACK_LOCKED)==0) {
+ MovieTrackingMarker *marker= BKE_tracking_get_marker(track, user->framenr);
+
+ if((marker->flag&MARKER_DISABLED)==0) {
+ MovieTrackingTrack *new_track= BKE_tracking_copy_track(track);
+
+ track_context->track= new_track;
+#ifdef WITH_LIBMV
+ {
+ if(track_context->track->tracker==TRACKER_KLT) {
+ float search_size_x= (track->search_max[0]-track->search_min[0])*width;
+ float search_size_y= (track->search_max[1]-track->search_min[1])*height;
+ float pattern_size_x= (track->pat_max[0]-track->pat_min[0])*width;
+ float pattern_size_y= (track->pat_max[1]-track->pat_min[1])*height;
+
+ /* compute the maximum pyramid size */
+ double search_to_pattern_ratio= MIN2(search_size_x, search_size_y)
+ / MAX2(pattern_size_x, pattern_size_y);
+ double log2_search_to_pattern_ratio = log(floor(search_to_pattern_ratio)) / M_LN2;
+ int max_pyramid_levels= floor(log2_search_to_pattern_ratio + 1);
+
+ /* try to accomodate the user's choice of pyramid level in a way
+ * that doesn't cause the coarsest pyramid pattern to be larger
+ * than the search size */
+ int level= MIN2(track_context->track->pyramid_levels, max_pyramid_levels);
+ track_context->region_tracker= libmv_regionTrackerNew(100, level);
+ }
+ else if(track_context->track->tracker==TRACKER_SAD) {
+ /* nothing to initialize */
+ }
+ }
+#endif
+
+ BLI_ghash_insert(context->hash, new_track, track);
+
+ track_context++;
+ }
+ }
+
+ track= track->next;
+ }
+ }
+
+ context->clip= clip;
+ context->user= *user;
+
+ return context;
+}
+
+void BKE_tracking_context_free(MovieTrackingContext *context)
+{
+ int a;
+ TrackContext *track_context;
+
+ for(a= 0, track_context= context->track_context; a<context->num_tracks; a++, track_context++) {
+ BKE_tracking_free_track(context->track_context[a].track);
+
+#if WITH_LIBMV
+ if(track_context->region_tracker)
+ libmv_regionTrackerDestroy(track_context->region_tracker);
+
+ if(track_context->patch)
+ MEM_freeN(track_context->patch);
+
+ if(track_context->pattern)
+ MEM_freeN(track_context->pattern);
+
+ if(track_context->warped)
+ MEM_freeN(track_context->warped);
+#endif
+
+ MEM_freeN(track_context->track);
+ }
+
+ if(context->track_context)
+ MEM_freeN(context->track_context);
+
+ BLI_ghash_free(context->hash, NULL, NULL);
+
+ MEM_freeN(context);
+}
+
+static void disable_imbuf_channels(ImBuf *ibuf, MovieTrackingTrack *track)
+{
+ int x, y;
+
+ if((track->flag&(TRACK_DISABLE_RED|TRACK_DISABLE_GREEN|TRACK_DISABLE_BLUE))==0)
+ return;
+
+ for(y= 0; y<ibuf->y; y++) {
+ for (x= 0; x<ibuf->x; x++) {
+ int pixel= ibuf->x*y + x;
+
+ if(ibuf->rect_float) {
+ float *rrgbf= ibuf->rect_float + pixel*4;
+
+ if(track->flag&TRACK_DISABLE_RED) rrgbf[0]= 0;
+ if(track->flag&TRACK_DISABLE_GREEN) rrgbf[1]= 0;
+ if(track->flag&TRACK_DISABLE_BLUE) rrgbf[2]= 0;
+ } else {
+ char *rrgb= (char*)ibuf->rect + pixel*4;
+
+ if(track->flag&TRACK_DISABLE_RED) rrgb[0]= 0;
+ if(track->flag&TRACK_DISABLE_GREEN) rrgb[1]= 0;
+ if(track->flag&TRACK_DISABLE_BLUE) rrgb[2]= 0;
+ }
+ }
+ }
+}
+
+static ImBuf *get_area_imbuf(ImBuf *ibuf, MovieTrackingTrack *track, MovieTrackingMarker *marker,
+ float min[2], float max[2], int margin, int anchored, float pos[2], int origin[2])
+{
+ ImBuf *tmpibuf;
+ int x, y;
+ int x1, y1, x2, y2, w, h;
+ float mpos[2];
+
+ copy_v2_v2(mpos, marker->pos);
+ if(anchored)
+ add_v2_v2(mpos, track->offset);
+
+ x= mpos[0]*ibuf->x;
+ y= mpos[1]*ibuf->y;
+ x1= x-(int)(-min[0]*ibuf->x);
+ y1= y-(int)(-min[1]*ibuf->y);
+ x2= x+(int)(max[0]*ibuf->x);
+ y2= y+(int)(max[1]*ibuf->y);
+
+ /* dimensions should be odd */
+ w= (x2-x1)|1;
+ h= (y2-y1)|1;
+
+ tmpibuf= IMB_allocImBuf(w+margin*2, h+margin*2, 32, IB_rect);
+ IMB_rectcpy(tmpibuf, ibuf, 0, 0, x1-margin, y1-margin, w+margin*2, h+margin*2);
+
+ if(pos != NULL) {
+ pos[0]= mpos[0]*ibuf->x-x1+margin;
+ pos[1]= mpos[1]*ibuf->y-y1+margin;
+ }
+
+ if(origin != NULL) {
+ origin[0]= x1-margin;
+ origin[1]= y1-margin;
+ }
+
+ disable_imbuf_channels(tmpibuf, track);
+
+ return tmpibuf;
+}
+
+ImBuf *BKE_tracking_get_pattern_imbuf(ImBuf *ibuf, MovieTrackingTrack *track, MovieTrackingMarker *marker,
+ int margin, int anchored, float pos[2], int origin[2])
+{
+ return get_area_imbuf(ibuf, track, marker, track->pat_min, track->pat_max, margin, anchored, pos, origin);
+}
+
+ImBuf *BKE_tracking_get_search_imbuf(ImBuf *ibuf, MovieTrackingTrack *track, MovieTrackingMarker *marker,
+ int margin, int anchored, float pos[2], int origin[2])
+{
+ return get_area_imbuf(ibuf, track, marker, track->search_min, track->search_max, margin, anchored, pos, origin);
+}
+
+#ifdef WITH_LIBMV
+static float *get_search_floatbuf(ImBuf *ibuf, MovieTrackingTrack *track, MovieTrackingMarker *marker,
+ int *width_r, int *height_r, float pos[2], int origin[2])
+{
+ ImBuf *tmpibuf;
+ float *pixels, *fp;
+ int x, y, width, height;
+
+ width= (track->search_max[0]-track->search_min[0])*ibuf->x;
+ height= (track->search_max[1]-track->search_min[1])*ibuf->y;
+
+ tmpibuf= BKE_tracking_get_search_imbuf(ibuf, track, marker, 0, 0, pos, origin);
+ disable_imbuf_channels(tmpibuf, track);
+
+ *width_r= width;
+ *height_r= height;
+
+ fp= pixels= MEM_callocN(width*height*sizeof(float), "tracking floatBuf");
+ for(y= 0; y<(int)height; y++) {
+ for (x= 0; x<(int)width; x++) {
+ int pixel= tmpibuf->x*y + x;
+
+ if(tmpibuf->rect_float) {
+ float *rrgbf= tmpibuf->rect_float + pixel*4;
+
+ *fp= 0.2126*rrgbf[0] + 0.7152*rrgbf[1] + 0.0722*rrgbf[2];
+ } else {
+ unsigned char *rrgb= (unsigned char*)tmpibuf->rect + pixel*4;
+
+ *fp= (0.2126*rrgb[0] + 0.7152*rrgb[1] + 0.0722*rrgb[2])/255.0f;
+ }
+
+ fp++;
+ }
+ }
+
+ IMB_freeImBuf(tmpibuf);
+
+ return pixels;
+}
+
+static unsigned char *get_ucharbuf(ImBuf *ibuf)
+{
+ int x, y;
+ unsigned char *pixels, *cp;
+
+ cp= pixels= MEM_callocN(ibuf->x*ibuf->y*sizeof(unsigned char), "tracking ucharBuf");
+ for(y= 0; y<ibuf->y; y++) {
+ for (x= 0; x<ibuf->x; x++) {
+ int pixel= ibuf->x*y + x;
+
+ if(ibuf->rect_float) {
+ float *rrgbf= ibuf->rect_float + pixel*4;
+
+ *cp= FTOCHAR(0.2126f*rrgbf[0] + 0.7152f*rrgbf[1] + 0.0722f*rrgbf[2]);
+ } else {
+ unsigned char *rrgb= (unsigned char*)ibuf->rect + pixel*4;
+
+ *cp= 0.2126f*rrgb[0] + 0.7152f*rrgb[1] + 0.0722f*rrgb[2];
+ }
+
+ cp++;
+ }
+ }
+
+ return pixels;
+}
+
+static unsigned char *get_search_bytebuf(ImBuf *ibuf, MovieTrackingTrack *track, MovieTrackingMarker *marker,
+ int *width_r, int *height_r, float pos[2], int origin[2])
+{
+ ImBuf *tmpibuf;
+ unsigned char *pixels;
+
+ tmpibuf= BKE_tracking_get_search_imbuf(ibuf, track, marker, 0, 0, pos, origin);
+ disable_imbuf_channels(tmpibuf, track);
+
+ *width_r= tmpibuf->x;
+ *height_r= tmpibuf->y;
+
+ pixels= get_ucharbuf(tmpibuf);
+
+ IMB_freeImBuf(tmpibuf);
+
+ return pixels;
+}
+
+static ImBuf *get_frame_ibuf(MovieTrackingContext *context, int framenr)
+{
+ ImBuf *ibuf;
+ int framenr_old= context->user.framenr;
+
+ context->user.framenr= framenr;
+
+ ibuf= BKE_movieclip_get_ibuf_flag(context->clip, &context->user, 0);
+
+ context->user.framenr= framenr_old;
+
+ return ibuf;
+}
+
+static ImBuf *get_keyframed_ibuf(MovieTrackingContext *context, MovieTrackingTrack *track,
+ MovieTrackingMarker *marker, MovieTrackingMarker **marker_keyed)
+{
+ int framenr= marker->framenr;
+ int a= marker-track->markers;
+
+ *marker_keyed= marker;
+
+ while(a>=0 && a<track->markersnr) {
+ int next= (context->backwards) ? a+1 : a-1;
+ int is_keyframed= 0;
+ MovieTrackingMarker *marker= &track->markers[a];
+ MovieTrackingMarker *next_marker= NULL;
+
+ if(next>=0 && next<track->markersnr)
+ next_marker= &track->markers[next];
+
+ /* if next mrker is disabled, stop searching keyframe and use current frame as keyframe */
+ if(next_marker && next_marker->flag&MARKER_DISABLED)
+ is_keyframed= 1;
+
+ is_keyframed|= (marker->flag&MARKER_TRACKED)==0;
+
+ if(is_keyframed) {
+ framenr= marker->framenr;
+ *marker_keyed= marker;
+ break;
+ }
+
+ a= next;
+ }
+
+ return get_frame_ibuf(context, framenr);
+}
+
+static ImBuf *get_adjust_ibuf(MovieTrackingContext *context, MovieTrackingTrack *track, MovieTrackingMarker *marker,
+ int curfra, MovieTrackingMarker **marker_keyed)
+{
+ ImBuf *ibuf= NULL;
+
+ if(context->settings.adjframes == 0) {
+ ibuf= get_keyframed_ibuf(context, track, marker, marker_keyed);
+ } else {
+ ibuf= get_frame_ibuf(context, curfra);
+
+ /* use current marker as keyframed position */
+ *marker_keyed= marker;
+ }
+
+ return ibuf;
+}
+
+static void get_warped(TrackContext *track_context, int x, int y, int width, unsigned char *image)
+{
+ int i, j;
+
+ for(i=0; i<track_context->patsize; i++) {
+ for(j=0; j<track_context->patsize; j++) {
+ track_context->warped[i*track_context->patsize+j]=
+ image[(y+i-track_context->patsize/2)*width+x+j-track_context->patsize/2];
+ }
+ }
+}
+
+#endif
+
+void BKE_tracking_sync(MovieTrackingContext *context)
+{
+ TrackContext *track_context;
+ MovieTracking *tracking= &context->clip->tracking;
+ MovieTrackingTrack *track;
+ ListBase tracks= {NULL, NULL}, new_tracks= {NULL, NULL};
+ ListBase *old_tracks= &context->clip->tracking.tracks;
+ int a, newframe;
+
+ /* duplicate currently tracking tracks to temporary list.
+ this is needed to keep names in unique state and it's faster to change names
+ of currently tracking tracks (if needed) */
+ for(a= 0, track_context= context->track_context; a<context->num_tracks; a++, track_context++) {
+ int replace_sel= 0;
+ MovieTrackingTrack *new_track, *old;
+
+ track= track_context->track;
+
+ /* find original of tracking track in list of previously displayed tracks */
+ old= BLI_ghash_lookup(context->hash, track);
+ if(old) {
+ MovieTrackingTrack *cur= old_tracks->first;
+
+ while(cur) {
+ if(cur==old)
+ break;
+
+ cur= cur->next;
+ }
+
+ /* original track was found, re-use flags and remove this track */
+ if(cur) {
+ if(cur==tracking->act_track)
+ replace_sel= 1;
+
+ track->flag= cur->flag;
+ track->pat_flag= cur->pat_flag;
+ track->search_flag= cur->search_flag;
+
+ BKE_tracking_free_track(cur);
+ BLI_freelinkN(old_tracks, cur);
+ }
+ }
+
+ new_track= BKE_tracking_copy_track(track);
+
+ BLI_ghash_remove(context->hash, track, NULL, NULL); /* XXX: are we actually need this */
+ BLI_ghash_insert(context->hash, track, new_track);
+
+ if(replace_sel) /* update current selection in clip */
+ tracking->act_track= new_track;
+
+ BLI_addtail(&tracks, new_track);
+ }
+
+ /* move all tracks, which aren't tracking */
+ track= old_tracks->first;
+ while(track) {
+ MovieTrackingTrack *next= track->next;
+
+ track->next= track->prev= NULL;
+ BLI_addtail(&new_tracks, track);
+
+ track= next;
+ }
+
+ /* now move all tracks which are currently tracking and keep their names unique */
+ track= tracks.first;
+ while(track) {
+ MovieTrackingTrack *next= track->next;
+
+ BLI_remlink(&tracks, track);
+
+ track->next= track->prev= NULL;
+ BLI_addtail(&new_tracks, track);
+
+ BLI_uniquename(&new_tracks, track, "Track", '.', offsetof(MovieTrackingTrack, name), sizeof(track->name));
+
+ track= next;
+ }
+
+ context->clip->tracking.tracks= new_tracks;
+
+ if(context->backwards) newframe= context->user.framenr+1;
+ else newframe= context->user.framenr-1;
+
+ context->sync_frame= newframe;
+}
+
+void BKE_tracking_sync_user(MovieClipUser *user, MovieTrackingContext *context)
+{
+ user->framenr= context->sync_frame;
+}
+
+int BKE_tracking_next(MovieTrackingContext *context)
+{
+ ImBuf *ibuf_new;
+ int curfra= context->user.framenr;
+ int a, ok= 0;
+
+ /* nothing to track, avoid unneeded frames reading to save time and memory */
+ if(!context->num_tracks)
+ return 0;
+
+ if(context->backwards) context->user.framenr--;
+ else context->user.framenr++;
+
+ ibuf_new= BKE_movieclip_get_ibuf_flag(context->clip, &context->user, 0);
+ if(!ibuf_new)
+ return 0;
+
+ #pragma omp parallel for private(a) shared(ibuf_new, ok) if(context->num_tracks>1)
+ for(a= 0; a<context->num_tracks; a++) {
+ TrackContext *track_context= &context->track_context[a];
+ MovieTrackingTrack *track= track_context->track;
+ MovieTrackingMarker *marker= BKE_tracking_get_marker(track, curfra);
+
+ if(marker && (marker->flag&MARKER_DISABLED)==0 && marker->framenr==curfra) {
+#ifdef WITH_LIBMV
+ int width, height, origin[2], tracked= 0, need_readjust= 0;
+ float pos[2], margin[2];
+ double x1, y1, x2, y2;
+ ImBuf *ibuf= NULL;
+ MovieTrackingMarker marker_new, *marker_keyed;
+ int onbound= 0, coords_correct= 0;
+ int nextfra;
+
+ if(!context->settings.adjframes) need_readjust= context->first_time;
+ else need_readjust= context->frames%context->settings.adjframes == 0;
+
+ if(context->backwards) nextfra= curfra-1;
+ else nextfra= curfra+1;
+
+ /* margin from frame boundaries */
+ sub_v2_v2v2(margin, track->pat_max, track->pat_min);
+
+ margin[0]= MAX2(margin[0], (float)context->settings.margin / ibuf_new->x);
+ margin[1]= MAX2(margin[1], (float)context->settings.margin / ibuf_new->y);
+
+ /* do not track markers which are too close to boundary */
+ if(marker->pos[0]<margin[0] || marker->pos[0]>1.0f-margin[0] ||
+ marker->pos[1]<margin[1] || marker->pos[1]>1.0f-margin[1]) {
+ onbound= 1;
+ }
+ else if(track_context->track->tracker==TRACKER_KLT) {
+ int wndx, wndy;
+ float *patch_new;
+
+ if(need_readjust) {
+ /* calculate patch for keyframed position */
+ ibuf= get_adjust_ibuf(context, track, marker, curfra, &marker_keyed);
+
+ if(track_context->patch)
+ MEM_freeN(track_context->patch);
+
+ track_context->patch= get_search_floatbuf(ibuf, track, marker_keyed, &width, &height, track_context->keyframed_pos, origin);
+
+ IMB_freeImBuf(ibuf);
+ }
+
+ patch_new= get_search_floatbuf(ibuf_new, track, marker, &width, &height, pos, origin);
+
+ x1= track_context->keyframed_pos[0];
+ y1= track_context->keyframed_pos[1];
+
+ x2= pos[0];
+ y2= pos[1];
+
+ wndx= (int)((track->pat_max[0]-track->pat_min[0])*ibuf_new->x)/2;
+ wndy= (int)((track->pat_max[1]-track->pat_min[1])*ibuf_new->y)/2;
+
+ tracked= libmv_regionTrackerTrack(track_context->region_tracker, track_context->patch, patch_new,
+ width, height, MAX2(wndx, wndy), x1, y1, &x2, &y2);
+
+ MEM_freeN(patch_new);
+ }
+ else if(track_context->track->tracker==TRACKER_SAD) {
+ unsigned char *image_new;
+ float correlation;
+ float warp[3][2]={{0}};
+
+ if(need_readjust) {
+ unsigned char *image;
+
+ /* calculate pattern for keyframed position */
+ ibuf= get_adjust_ibuf(context, track, marker, curfra, &marker_keyed);
+
+ image= get_search_bytebuf(ibuf, track, marker_keyed, &width, &height, pos, origin);
+
+ memset(warp, 0, sizeof(warp));
+ warp[0][0]= 1;
+ warp[1][1]= 1;
+ warp[2][0]= pos[0];
+ warp[2][1]= pos[1];
+
+ /* pattern size is hardcoded to 16x16px in libmv */
+ track_context->patsize= 16;
+
+ if(!track_context->pattern)
+ track_context->pattern= MEM_callocN(sizeof(unsigned char)*track_context->patsize*track_context->patsize, "trackking pattern");
+
+ libmv_SADSamplePattern(image, width, warp, track_context->pattern);
+
+ MEM_freeN(image);
+ IMB_freeImBuf(ibuf);
+ }
+
+ image_new= get_search_bytebuf(ibuf_new, track, marker, &width, &height, pos, origin);
+
+ if(track_context->warped==NULL) {
+ unsigned char *image_old;
+
+ ibuf= get_frame_ibuf(context, curfra);
+
+ if(track_context->warped==NULL)
+ track_context->warped= MEM_callocN(sizeof(unsigned char)*track_context->patsize*track_context->patsize, "trackking warped");
+
+ image_old= get_search_bytebuf(ibuf, track, marker, &width, &height, pos, origin);
+ get_warped(track_context, pos[0], pos[1], width, image_old);
+ IMB_freeImBuf(ibuf);
+ MEM_freeN(image_old);
+ }
+
+ memset(warp, 0, sizeof(warp));
+ warp[0][0]= 1;
+ warp[1][1]= 1;
+ warp[2][0]= pos[0];
+ warp[2][1]= pos[1];
+
+ correlation= libmv_SADTrackerTrack(track_context->pattern, track_context->warped, image_new, width, width, height, warp);
+
+ x2= warp[2][0];
+ y2= warp[2][1];
+
+ tracked= track_context->track->minimum_correlation < correlation;
+
+ if(tracked)
+ get_warped(track_context, x2, y2, width, image_new);
+
+ MEM_freeN(image_new);
+ }
+
+ coords_correct= !isnan(x2) && !isnan(y2) && finite(x2) && finite(y2);
+ if(coords_correct && (tracked || !context->disable_failed)) {
+ if(context->first_time) {
+ #pragma omp critical
+ {
+ /* check if there's no keyframe/tracked markers before tracking marker.
+ if so -- create disabled marker before currently tracking "segment" */
+ put_disabled_marker(track, marker, 1, 0);
+ }
+ }
+
+ memset(&marker_new, 0, sizeof(marker_new));
+
+ if(!onbound) {
+ marker_new.pos[0]= (origin[0]+x2)/ibuf_new->x;
+ marker_new.pos[1]= (origin[1]+y2)/ibuf_new->y;
+ } else {
+ copy_v2_v2(marker_new.pos, marker->pos);
+ }
+
+ marker_new.flag|= MARKER_TRACKED;
+ marker_new.framenr= nextfra;
+
+ #pragma omp critical
+ {
+ BKE_tracking_insert_marker(track, &marker_new);
+ }
+
+ /* make currently tracked segment be finished with disabled marker */
+ #pragma omp critical
+ {
+ put_disabled_marker(track, &marker_new, 0, 0);
+ }
+ } else {
+ marker_new= *marker;
+
+ marker_new.framenr= nextfra;
+ marker_new.flag|= MARKER_DISABLED;
+
+ #pragma omp critical
+ {
+ BKE_tracking_insert_marker(track, &marker_new);
+ }
+ }
+
+ ok= 1;
+#endif
+ }
+ }
+
+ IMB_freeImBuf(ibuf_new);
+
+ context->first_time= 0;
+ context->frames++;
+
+ return ok;
+}
+
+#if WITH_LIBMV
+static struct libmv_Tracks *create_libmv_tracks(MovieTracking *tracking, int width, int height)
+{
+ int tracknr= 0;
+ MovieTrackingTrack *track;
+ struct libmv_Tracks *tracks= libmv_tracksNew();
+
+ track= tracking->tracks.first;
+ while(track) {
+ int a= 0;
+
+ for(a= 0; a<track->markersnr; a++) {
+ MovieTrackingMarker *marker= &track->markers[a];
+
+ if((marker->flag&MARKER_DISABLED)==0)
+ libmv_tracksInsert(tracks, marker->framenr, tracknr,
+ marker->pos[0]*width, marker->pos[1]*height);
+ }
+
+ track= track->next;
+ tracknr++;
+ }
+
+ return tracks;
+}
+
+static int retrieve_libmv_reconstruct(MovieTracking *tracking, struct libmv_Reconstruction *libmv_reconstruction)
+{
+ int tracknr= 0;
+ int sfra= INT_MAX, efra= INT_MIN, a, origin_set= 0;
+ MovieTrackingTrack *track;
+ MovieTrackingReconstruction *reconstruction= &tracking->reconstruction;
+ MovieReconstructedCamera *reconstructed;
+ float origin[3]= {0.0f, 0.0f, 0.0f};
+ int ok= 1;
+
+ track= tracking->tracks.first;
+ while(track) {
+ double pos[3];
+
+ if(libmv_reporojectionPointForTrack(libmv_reconstruction, tracknr, pos)) {
+ track->bundle_pos[0]= pos[0];
+ track->bundle_pos[1]= pos[1];
+ track->bundle_pos[2]= pos[2];
+
+ track->flag|= TRACK_HAS_BUNDLE;
+ track->error= libmv_reporojectionErrorForTrack(libmv_reconstruction, tracknr);
+ } else {
+ track->flag&= ~TRACK_HAS_BUNDLE;
+ ok= 0;
+
+ printf("No bundle for track #%d '%s'\n", tracknr, track->name);
+ }
+
+ if(track->markersnr) {
+ if(track->markers[0].framenr<sfra) sfra= track->markers[0].framenr;
+ if(track->markers[track->markersnr-1].framenr>efra) efra= track->markers[track->markersnr-1].framenr;
+ }
+
+ track= track->next;
+ tracknr++;
+ }
+
+ if(reconstruction->cameras)
+ MEM_freeN(reconstruction->cameras);
+
+ reconstruction->camnr= 0;
+ reconstruction->cameras= NULL;
+ reconstructed= MEM_callocN((efra-sfra+1)*sizeof(MovieReconstructedCamera), "temp reconstructed camera");
+
+ for(a= sfra; a<=efra; a++) {
+ double matd[4][4];
+
+ if(libmv_reporojectionCameraForImage(libmv_reconstruction, a, matd)) {
+ int i, j;
+ float mat[4][4];
+ float error= libmv_reporojectionErrorForImage(libmv_reconstruction, a);
+
+ for(i=0; i<4; i++)
+ for(j= 0; j<4; j++)
+ mat[i][j]= matd[i][j];
+
+ if(!origin_set) {
+ copy_v3_v3(origin, mat[3]);
+ origin_set= 1;
+ }
+
+ if(origin_set)
+ sub_v3_v3(mat[3], origin);
+
+ copy_m4_m4(reconstructed[reconstruction->camnr].mat, mat);
+ reconstructed[reconstruction->camnr].framenr= a;
+ reconstructed[reconstruction->camnr].error= error;
+ reconstruction->camnr++;
+ } else {
+ ok= 0;
+ printf("No camera for frame %d\n", a);
+ }
+ }
+
+ if(reconstruction->camnr) {
+ reconstruction->cameras= MEM_callocN(reconstruction->camnr*sizeof(MovieReconstructedCamera), "reconstructed camera");
+ memcpy(reconstruction->cameras, reconstructed, reconstruction->camnr*sizeof(MovieReconstructedCamera));
+ }
+
+ if(origin_set) {
+ track= tracking->tracks.first;
+ while(track) {
+ if(track->flag&TRACK_HAS_BUNDLE)
+ sub_v3_v3(track->bundle_pos, origin);
+
+ track= track->next;
+ }
+ }
+
+ MEM_freeN(reconstructed);
+
+ return ok;
+}
+
+#endif
+
+float BKE_tracking_solve_reconstruction(MovieTracking *tracking, int width, int height)
+{
+#if WITH_LIBMV
+ {
+ MovieTrackingCamera *camera= &tracking->camera;
+ float aspy= 1.0f/tracking->camera.pixel_aspect;
+ struct libmv_Tracks *tracks= create_libmv_tracks(tracking, width, height*aspy);
+ struct libmv_Reconstruction *reconstruction = libmv_solveReconstruction(tracks,
+ tracking->settings.keyframe1, tracking->settings.keyframe2,
+ camera->focal,
+ camera->principal[0], camera->principal[1]*aspy,
+ camera->k1, camera->k2, camera->k3);
+ float error= libmv_reprojectionError(reconstruction);
+
+ tracking->reconstruction.error= error;
+
+ if(!retrieve_libmv_reconstruct(tracking, reconstruction))
+ error= -1.0f;
+
+ libmv_destroyReconstruction(reconstruction);
+ libmv_tracksDestroy(tracks);
+
+ tracking->reconstruction.flag|= TRACKING_RECONSTRUCTED;
+
+ return error;
+ }
+#endif
+}
+
+void BKE_track_unique_name(MovieTracking *tracking, MovieTrackingTrack *track)
+{
+ BLI_uniquename(&tracking->tracks, track, "Track", '.', offsetof(MovieTrackingTrack, name), sizeof(track->name));
+}
+
+MovieTrackingTrack *BKE_tracking_named_track(MovieTracking *tracking, const char *name)
+{
+ MovieTrackingTrack *track= tracking->tracks.first;
+
+ while(track) {
+ if(!strcmp(track->name, name))
+ return track;
+
+ track= track->next;
+ }
+
+ return NULL;
+}
+
+static int reconstruction_camera_index(MovieTracking *tracking, int framenr, int nearest)
+{
+ MovieTrackingReconstruction *reconstruction= &tracking->reconstruction;
+ MovieReconstructedCamera *cameras= reconstruction->cameras;
+ int a= 0, d= 1;
+
+ if(!reconstruction->camnr)
+ return -1;
+
+ if(framenr<cameras[0].framenr) {
+ if(nearest) return 0;
+ else return -1;
+ }
+
+ if(framenr>cameras[reconstruction->camnr-1].framenr) {
+ if(nearest) return reconstruction->camnr-1;
+ else return -1;
+ }
+
+ if(reconstruction->last_camera<reconstruction->camnr)
+ a= reconstruction->last_camera;
+
+ if(cameras[a].framenr>=framenr)
+ d= -1;
+
+ while(a>=0 && a<reconstruction->camnr) {
+ int cfra= cameras[a].framenr;
+
+ /* check if needed framenr was "skipped" -- no data for requested frame */
+
+ if(d>0 && cfra>framenr) {
+ /* interpolate with previous position */
+ if(nearest) return a-1;
+ else break;
+ }
+
+ if(d<0 && cfra<framenr) {
+ /* interpolate with next position */
+ if(nearest) return a;
+ else break;
+ }
+
+ if(cfra==framenr) {
+ reconstruction->last_camera= a;
+
+ return a;
+ }
+
+ a+= d;
+ }
+
+ return -1;
+}
+
+MovieReconstructedCamera *BKE_tracking_get_reconstructed_camera(MovieTracking *tracking, int framenr)
+{
+ int a= reconstruction_camera_index(tracking, framenr, 0);
+
+ if(a==-1)
+ return NULL;
+
+ return &tracking->reconstruction.cameras[a];
+}
+
+void BKE_tracking_get_interpolated_camera(MovieTracking *tracking, int framenr, float mat[4][4])
+{
+ MovieTrackingReconstruction *reconstruction= &tracking->reconstruction;
+ MovieReconstructedCamera *cameras= reconstruction->cameras;
+ int a= reconstruction_camera_index(tracking, framenr, 1);
+
+ if(a==-1) {
+ unit_m4(mat);
+ return;
+ }
+
+ if(cameras[a].framenr!=framenr && a>0 && a<reconstruction->camnr-1) {
+ float t= ((float)framenr-cameras[a].framenr) / (cameras[a+1].framenr-cameras[a].framenr);
+
+ blend_m4_m4m4(mat, cameras[a].mat, cameras[a+1].mat, t);
+ } else {
+ copy_m4_m4(mat, cameras[a].mat);
+ }
+}
+
+void BKE_get_tracking_mat(Scene *scene, Object *ob, float mat[4][4])
+{
+ if(!ob) {
+ if(scene->camera) ob= scene->camera;
+ else ob= scene_find_camera(scene);
+ }
+
+ if(ob)
+ where_is_object_mat(scene, ob, mat);
+ else
+ unit_m4(mat);
+}
+
+void BKE_tracking_camera_shift(MovieTracking *tracking, int winx, int winy, float *shiftx, float *shifty)
+{
+ *shiftx= (0.5f*winx-tracking->camera.principal[0]) / winx;
+ *shifty= (0.5f*winy-tracking->camera.principal[1]) / winx;
+}
+
+void BKE_tracking_camera_to_blender(MovieTracking *tracking, Scene *scene, Camera *camera, int width, int height)
+{
+ float focal= tracking->camera.focal;
+
+ camera->sensor_x= tracking->camera.sensor_width;
+ camera->sensor_fit= CAMERA_SENSOR_FIT_AUTO;
+ camera->lens= focal*camera->sensor_x/width;
+
+ scene->r.xsch= width*tracking->camera.pixel_aspect;
+ scene->r.ysch= height;
+
+ scene->r.xasp= 1.0f;
+ scene->r.yasp= 1.0f;
+
+ BKE_tracking_camera_shift(tracking, width, height, &camera->shiftx, &camera->shifty);
+}
+
+void BKE_tracking_projection_matrix(MovieTracking *tracking, int framenr, int winx, int winy, float mat[4][4])
+{
+ MovieReconstructedCamera *camera;
+ float lens= tracking->camera.focal*tracking->camera.sensor_width/(float)winx;
+ float viewfac, pixsize, left, right, bottom, top, clipsta, clipend;
+ float winmat[4][4];
+ float ycor= 1.0f/tracking->camera.pixel_aspect;
+ float shiftx, shifty, winside= MAX2(winx, winy);
+
+ BKE_tracking_camera_shift(tracking, winx, winy, &shiftx, &shifty);
+
+ clipsta= 0.1f;
+ clipend= 1000.0f;
+
+ if(winx >= winy)
+ viewfac= (lens*winx)/tracking->camera.sensor_width;
+ else
+ viewfac= (ycor*lens*winy)/tracking->camera.sensor_width;
+
+ pixsize= clipsta/viewfac;
+
+ left= -0.5f*(float)winx + shiftx*winside;
+ bottom= -0.5f*(ycor)*(float)winy + shifty*winside;
+ right= 0.5f*(float)winx + shiftx*winside;
+ top= 0.5f*(ycor)*(float)winy + shifty*winside;
+
+ left *= pixsize;
+ right *= pixsize;
+ bottom *= pixsize;
+ top *= pixsize;
+
+ perspective_m4(winmat, left, right, bottom, top, clipsta, clipend);
+
+ camera= BKE_tracking_get_reconstructed_camera(tracking, framenr);
+ if(camera) {
+ float imat[4][4];
+
+ invert_m4_m4(imat, camera->mat);
+ mul_m4_m4m4(mat, imat, winmat);
+ } else copy_m4_m4(mat, winmat);
+}
+
+void BKE_tracking_apply_intrinsics(MovieTracking *tracking, float co[2], float nco[2])
+{
+ MovieTrackingCamera *camera= &tracking->camera;
+
+#ifdef WITH_LIBMV
+ double x, y;
+ float aspy= 1.0f/tracking->camera.pixel_aspect;
+
+ /* normalize coords */
+ x= (co[0]-camera->principal[0]) / camera->focal;
+ y= (co[1]-camera->principal[1] * aspy) / camera->focal;
+
+ libmv_applyCameraIntrinsics(camera->focal, camera->principal[0], camera->principal[1] * aspy,
+ camera->k1, camera->k2, camera->k3, x, y, &x, &y);
+
+ /* result is in image coords already */
+ nco[0]= x;
+ nco[1]= y;
+#endif
+}
+
+void BKE_tracking_invert_intrinsics(MovieTracking *tracking, float co[2], float nco[2])
+{
+ MovieTrackingCamera *camera= &tracking->camera;
+
+#ifdef WITH_LIBMV
+ double x= co[0], y= co[1];
+ float aspy= 1.0f/tracking->camera.pixel_aspect;
+
+ libmv_InvertIntrinsics(camera->focal, camera->principal[0], camera->principal[1] * aspy,
+ camera->k1, camera->k2, camera->k3, x, y, &x, &y);
+
+ nco[0]= x * camera->focal + camera->principal[0];
+ nco[1]= y * camera->focal + camera->principal[1] * aspy;
+#endif
+}
+
+static int point_in_stroke(bGPDstroke *stroke, float x, float y)
+{
+ int i, prev;
+ int count= 0;
+ bGPDspoint *points= stroke->points;
+
+ prev= stroke->totpoints-1;
+
+ for(i= 0; i<stroke->totpoints; i++) {
+ if((points[i].y<y && points[prev].y>=y) || (points[prev].y<y && points[i].y>=y)) {
+ float fac= (y-points[i].y)/(points[prev].y-points[i].y);
+
+ if (points[i].x+fac*(points[prev].x-points[i].x)<x)
+ count++;
+ }
+
+ prev= i;
+ }
+
+ return count%2;
+}
+
+static int point_in_layer(bGPDlayer *layer, float x, float y)
+{
+ bGPDframe *frame= layer->frames.first;
+
+ while(frame) {
+ bGPDstroke *stroke= frame->strokes.first;
+ while(stroke) {
+ if(point_in_stroke(stroke, x, y))
+ return 1;
+
+ stroke= stroke->next;
+ }
+ frame= frame->next;
+ }
+
+ return 0;
+}
+
+static void retrieve_libmv_features(MovieTracking *tracking, struct libmv_Features *features,
+ int framenr, int width, int height, bGPDlayer *layer, int place_outside_layer)
+{
+#ifdef WITH_LIBMV
+ int a;
+
+ a= libmv_countFeatures(features);
+ while(a--) {
+ MovieTrackingTrack *track;
+ double x, y, size, score;
+ int ok= 1;
+ float xu, yu;
+
+ libmv_getFeature(features, a, &x, &y, &score, &size);
+
+ xu= x/width;
+ yu= y/height;
+
+ if(layer)
+ ok= point_in_layer(layer, xu, yu)!=place_outside_layer;
+
+ if(ok) {
+ track= BKE_tracking_add_track(tracking, xu, yu, framenr, width, height);
+ track->flag|= SELECT;
+ track->pat_flag|= SELECT;
+ track->search_flag|= SELECT;
+ }
+ }
+#endif
+}
+
+void BKE_tracking_detect_fast(MovieTracking *tracking, ImBuf *ibuf,
+ int framenr, int margin, int min_trackness, int min_distance, bGPDlayer *layer,
+ int place_outside_layer)
+{
+#ifdef WITH_LIBMV
+ struct libmv_Features *features;
+ unsigned char *pixels= get_ucharbuf(ibuf);
+
+ features= libmv_detectFeaturesFAST(pixels, ibuf->x, ibuf->y, ibuf->x, margin, min_trackness, min_distance);
+
+ MEM_freeN(pixels);
+
+ retrieve_libmv_features(tracking, features, framenr, ibuf->x, ibuf->y, layer, place_outside_layer);
+
+ libmv_destroyFeatures(features);
+#endif
+}
+
+MovieTrackingTrack *BKE_tracking_indexed_track(MovieTracking *tracking, int tracknr)
+{
+ MovieTrackingTrack *track= tracking->tracks.first;
+ int cur= 1;
+
+ while(track) {
+ if(track->flag&TRACK_HAS_BUNDLE) {
+ if(cur==tracknr)
+ return track;
+
+ cur++;
+ }
+
+ track= track->next;
+ }
+
+ return NULL;
+}
+
+static int stabilization_median_point(MovieTracking *tracking, int framenr, float median[2])
+{
+ int ok= 0;
+ float min[2], max[2];
+ MovieTrackingTrack *track;
+
+ INIT_MINMAX2(min, max);
+
+ track= tracking->tracks.first;
+ while(track) {
+ if(track->flag&TRACK_USE_2D_STAB) {
+ MovieTrackingMarker *marker= BKE_tracking_get_marker(track, framenr);
+
+ DO_MINMAX2(marker->pos, min, max);
+
+ ok= 1;
+ }
+
+ track= track->next;
+ }
+
+ median[0]= (max[0]+min[0])/2.0f;
+ median[1]= (max[1]+min[1])/2.0f;
+
+ return ok;
+}
+
+static void calculate_stabdata(MovieTracking *tracking, int framenr, float width, float height,
+ float firstmedian[2], float median[2], float loc[2], float *scale, float *angle)
+{
+ MovieTrackingStabilization *stab= &tracking->stabilization;
+
+ *scale= (stab->scale-1.0f)*stab->scaleinf+1.0f;
+ *angle= 0.0f;
+
+ loc[0]= (firstmedian[0]-median[0])*width*(*scale);
+ loc[1]= (firstmedian[1]-median[1])*height*(*scale);
+
+ mul_v2_fl(loc, stab->locinf);
+
+ if(stab->rot_track && stab->rotinf) {
+ MovieTrackingMarker *marker;
+ float a[2], b[2];
+ float x0= (float)width/2.0f, y0= (float)height/2.0f;
+ float x= median[0]*width, y= median[1]*height;
+
+ marker= BKE_tracking_get_marker(stab->rot_track, 1);
+ sub_v2_v2v2(a, marker->pos, firstmedian);
+ a[0]*= width;
+ a[1]*= height;
+
+ marker= BKE_tracking_get_marker(stab->rot_track, framenr);
+ sub_v2_v2v2(b, marker->pos, median);
+ b[0]*= width;
+ b[1]*= height;
+
+ *angle= -atan2(a[0]*b[1]-a[1]*b[0], a[0]*b[0]+a[1]*b[1]);
+ *angle*= stab->rotinf;
+
+ /* convert to rotation around image center */
+ loc[0]-= (x0 + (x-x0)*cos(*angle)-(y-y0)*sin(*angle) - x)*(*scale);
+ loc[1]-= (y0 + (x-x0)*sin(*angle)+(y-y0)*cos(*angle) - y)*(*scale);
+ }
+}
+
+static float stabilization_auto_scale_factor(MovieTracking *tracking, int width, int height)
+{
+ float firstmedian[2];
+ MovieTrackingStabilization *stab= &tracking->stabilization;
+
+ if(stab->ok)
+ return stab->scale;
+
+ if(stabilization_median_point(tracking, 1, firstmedian)) {
+ int sfra= INT_MAX, efra= INT_MIN, cfra;
+ float delta[2]= {0.0f, 0.0f}, scalex= 1.0f, scaley= 1.0f;
+ MovieTrackingTrack *track;
+
+ stab->scale= 1.0f;
+
+ track= tracking->tracks.first;
+ while(track) {
+ if(track->flag&TRACK_USE_2D_STAB || track==stab->rot_track) {
+ if(track->markersnr) {
+ sfra= MIN2(sfra, track->markers[0].framenr);
+ efra= MAX2(efra, track->markers[track->markersnr-1].framenr);
+ }
+ }
+
+ track= track->next;
+ }
+
+ for(cfra=sfra; cfra<=efra; cfra++) {
+ float median[2], near[2];
+ float loc[2], scale, angle;
+
+ stabilization_median_point(tracking, cfra, median);
+
+ calculate_stabdata(tracking, cfra, width, height, firstmedian, median,
+ loc, &scale, &angle);
+
+ if(angle==0.0f) {
+ loc[0]= fabsf(loc[0]);
+ loc[1]= fabsf(loc[1]);
+
+ delta[0]= MAX2(delta[0], loc[0]);
+ delta[1]= MAX2(delta[1], loc[1]);
+
+ near[0]= MIN2(median[0], 1.0f-median[0]);
+ near[1]= MIN2(median[1], 1.0f-median[1]);
+ near[0]= MAX2(near[0], 0.05f);
+ near[1]= MAX2(near[1], 0.05f);
+
+ scalex= 1.0f+delta[0]/(near[0]*width);
+ scaley= 1.0f+delta[1]/(near[1]*height);
+ } else {
+ int i;
+ float mat[4][4];
+ float points[4][2]={{0.0f, 0.0f}, {0.0f, height}, {width, height}, {width, 0.0f}};
+
+ BKE_tracking_stabdata_to_mat4(width, height, loc, scale, angle, mat);
+
+ for(i= 0; i<4; i++) {
+ int j;
+ float a[3]= {0.0f, 0.0f, 0.0f}, b[3]= {0.0f, 0.0f, 0.0f};
+
+ copy_v3_v3(a, points[i]);
+ copy_v3_v3(b, points[(i+1)%4]);
+
+ mul_m4_v3(mat, a);
+ mul_m4_v3(mat, b);
+
+ for(j= 0; j<4; j++) {
+ float point[3]= {points[j][0], points[j][1], 0.0f};
+ float v1[3], v2[3];
+
+ sub_v3_v3v3(v1, b, a);
+ sub_v3_v3v3(v2, point, a);
+
+ if(cross_v2v2(v1, v2) >= 0.0f) {
+ float dist= dist_to_line_v2(point, a, b);
+ if(i%2==0) {
+ scalex= MAX2(scalex, (width+2*dist)/width);
+ } else {
+ scaley= MAX2(scaley, (height+2*dist)/height);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ stab->scale= MAX2(scalex, scaley);
+
+ if(stab->maxscale>0.0f)
+ stab->scale= MIN2(stab->scale, stab->maxscale);
+ } else {
+ stab->scale= 1.0f;
+ }
+
+ stab->ok= 1;
+
+ return stab->scale;
+}
+
+static ImBuf* stabilize_alloc_ibuf(ImBuf *cacheibuf, ImBuf *srcibuf, int fill)
+{
+ int flags;
+
+ if(cacheibuf && (cacheibuf->x != srcibuf->x || cacheibuf->y != srcibuf->y)) {
+ IMB_freeImBuf(cacheibuf);
+ cacheibuf= NULL;
+ }
+
+ flags= IB_rect;
+
+ if(srcibuf->rect_float)
+ flags|= IB_rectfloat;
+
+ if(cacheibuf) {
+ if(fill) {
+ float col[4]= {0.0f, 0.0f, 0.0f, 0.0f};
+ IMB_rectfill(cacheibuf, col);
+ }
+ }
+ else {
+ cacheibuf= IMB_allocImBuf(srcibuf->x, srcibuf->y, srcibuf->depth, flags);
+ cacheibuf->profile= srcibuf->profile;
+ }
+
+ return cacheibuf;
+}
+
+void BKE_tracking_stabilization_data(MovieTracking *tracking, int framenr, int width, int height, float loc[2], float *scale, float *angle)
+{
+ float firstmedian[2], median[2];
+ MovieTrackingStabilization *stab= &tracking->stabilization;
+
+ if((stab->flag&TRACKING_2D_STABILIZATION)==0) {
+ zero_v2(loc);
+ *scale= 1.0f;
+ *angle= 0.0f;
+
+ return;
+ }
+
+ if(stabilization_median_point(tracking, 1, firstmedian)) {
+ stabilization_median_point(tracking, framenr, median);
+
+ if((stab->flag&TRACKING_AUTOSCALE)==0)
+ stab->scale= 1.0f;
+
+ if(!stab->ok) {
+ if(stab->flag&TRACKING_AUTOSCALE)
+ stabilization_auto_scale_factor(tracking, width, height);
+
+ calculate_stabdata(tracking, framenr, width, height, firstmedian, median, loc, scale, angle);
+
+ stab->ok= 1;
+ } else {
+ calculate_stabdata(tracking, framenr, width, height, firstmedian, median, loc, scale, angle);
+ }
+ } else {
+ zero_v2(loc);
+ *scale= 1.0f;
+ *angle= 0.0f;
+ }
+}
+
+ImBuf *BKE_tracking_stabilize(MovieTracking *tracking, int framenr, ImBuf *ibuf, float loc[2], float *scale, float *angle)
+{
+ float tloc[2], tscale, tangle;
+ MovieTrackingStabilization *stab= &tracking->stabilization;
+ ImBuf *tmpibuf;
+ float width= ibuf->x, height= ibuf->y;
+
+ if(loc) copy_v2_v2(tloc, loc);
+ if(scale) tscale= *scale;
+
+ if((stab->flag&TRACKING_2D_STABILIZATION)==0) {
+ if(loc) zero_v2(loc);
+ if(scale) *scale= 1.0f;
+
+ return ibuf;
+ }
+
+ BKE_tracking_stabilization_data(tracking, framenr, width, height, tloc, &tscale, &tangle);
+
+ tmpibuf= stabilize_alloc_ibuf(NULL, ibuf, 1);
+
+ /* scale would be handled by matrix transformation when angle is non-zero */
+ if(tscale!=1.0f && tangle==0.0f) {
+ ImBuf *scaleibuf;
+
+ stabilization_auto_scale_factor(tracking, width, height);
+
+ scaleibuf= stabilize_alloc_ibuf(stab->scaleibuf, ibuf, 0);
+ stab->scaleibuf= scaleibuf;
+
+ IMB_rectcpy(scaleibuf, ibuf, 0, 0, 0, 0, ibuf->x, ibuf->y);
+ IMB_scalefastImBuf(scaleibuf, ibuf->x*tscale, ibuf->y*tscale);
+
+ ibuf= scaleibuf;
+ }
+
+ if(tangle==0.0f) {
+ /* if angle is zero, then it's much faster to use rect copy
+ but could be issues with subpixel precisions */
+ IMB_rectcpy(tmpibuf, ibuf, tloc[0]-(tscale-1.0f)*width/2.0f, tloc[1]-(tscale-1.0f)*height/2.0f, 0, 0, ibuf->x, ibuf->y);
+ } else {
+ float mat[4][4];
+ int i, j;
+
+ BKE_tracking_stabdata_to_mat4(ibuf->x, ibuf->y, tloc, tscale, tangle, mat);
+ invert_m4(mat);
+
+ for(j=0; j<tmpibuf->y; j++) {
+ for(i=0; i<tmpibuf->x;i++) {
+ float vec[3]= {i, j, 0};
+
+ mul_v3_m4v3(vec, mat, vec);
+
+ /* TODO: add selector for interpolation method */
+ neareast_interpolation(ibuf, tmpibuf, vec[0], vec[1], i, j);
+ }
+ }
+ }
+
+ tmpibuf->userflags|= IB_MIPMAP_INVALID;
+
+ if(tmpibuf->rect_float)
+ tmpibuf->userflags|= IB_RECT_INVALID;
+
+ if(loc) copy_v2_v2(loc, tloc);
+ if(scale) *scale= tscale;
+ if(angle) *angle= tangle;
+
+ return tmpibuf;
+}
+
+void BKE_tracking_stabdata_to_mat4(int width, int height, float loc[2], float scale, float angle, float mat[4][4])
+{
+ float lmat[4][4], rmat[4][4], smat[4][4], cmat[4][4], icmat[4][4];
+ float svec[3]= {scale, scale, scale};
+
+ unit_m4(rmat);
+ unit_m4(lmat);
+ unit_m4(smat);
+ unit_m4(cmat);
+
+ /* image center as rotation center */
+ cmat[3][0]= (float)width/2.0f;
+ cmat[3][1]= (float)height/2.0f;
+ invert_m4_m4(icmat, cmat);
+
+ size_to_mat4(smat, svec); /* scale matrix */
+ add_v2_v2(lmat[3], loc); /* tranlation matrix */
+ rotate_m4(rmat, 'Z', angle); /* rotation matrix */
+
+ /* compose transformation matrix */
+ mul_serie_m4(mat, lmat, cmat, rmat, smat, icmat, NULL, NULL, NULL);
+}
+
+MovieDistortion *BKE_tracking_distortion_create(void)
+{
+ MovieDistortion *distortion;
+
+ distortion= MEM_callocN(sizeof(MovieDistortion), "BKE_tracking_distortion_create");
+
+ return distortion;
+}
+
+MovieDistortion *BKE_tracking_distortion_copy(MovieDistortion *distortion)
+{
+ MovieDistortion *new_distortion;
+
+ new_distortion= MEM_callocN(sizeof(MovieDistortion), "BKE_tracking_distortion_create");
+
+#ifdef WITH_LIBMV
+ new_distortion->intrinsics= libmv_CameraIntrinsicsCopy(distortion->intrinsics);
+#endif
+
+ return new_distortion;
+}
+
+void BKE_tracking_distortion_update(MovieDistortion *distortion, MovieTracking *tracking, int width, int height)
+{
+ MovieTrackingCamera *camera= &tracking->camera;
+ float aspy= 1.0f/tracking->camera.pixel_aspect;
+
+#ifdef WITH_LIBMV
+ if(!distortion->intrinsics) {
+ distortion->intrinsics= libmv_CameraIntrinsicsNew(camera->focal,
+ camera->principal[0], camera->principal[1] * aspy,
+ camera->k1, camera->k2, camera->k3, width, height * aspy);
+ } else {
+ libmv_CameraIntrinsicsUpdate(distortion->intrinsics, camera->focal,
+ camera->principal[0], camera->principal[1] * aspy,
+ camera->k1, camera->k2, camera->k3, width, height * aspy);
+ }
+#endif
+}
+
+ImBuf *BKE_tracking_distortion_exec(MovieDistortion *distortion, MovieTracking *tracking,
+ ImBuf *ibuf, int width, int height, float overscan, int undistort)
+{
+ ImBuf *resibuf;
+
+ BKE_tracking_distortion_update(distortion, tracking, width, height);
+
+ resibuf= IMB_dupImBuf(ibuf);
+
+ if(ibuf->rect_float) {
+#ifdef WITH_LIBMV
+ if(undistort) {
+ libmv_CameraIntrinsicsUndistortFloat(distortion->intrinsics,
+ ibuf->rect_float, resibuf->rect_float,
+ ibuf->x, ibuf->y, overscan, ibuf->channels);
+ } else {
+ libmv_CameraIntrinsicsDistortFloat(distortion->intrinsics,
+ ibuf->rect_float, resibuf->rect_float,
+ ibuf->x, ibuf->y, overscan, ibuf->channels);
+ }
+#endif
+
+ ibuf->userflags|= IB_RECT_INVALID;
+ } else {
+#ifdef WITH_LIBMV
+ if(undistort) {
+ libmv_CameraIntrinsicsUndistortByte(distortion->intrinsics,
+ (unsigned char*)ibuf->rect, (unsigned char*)resibuf->rect,
+ ibuf->x, ibuf->y, overscan, ibuf->channels);
+ } else {
+ libmv_CameraIntrinsicsDistortByte(distortion->intrinsics,
+ (unsigned char*)ibuf->rect, (unsigned char*)resibuf->rect,
+ ibuf->x, ibuf->y, overscan, ibuf->channels);
+ }
+#endif
+ }
+
+ return resibuf;
+}
+
+void BKE_tracking_distortion_destroy(MovieDistortion *distortion)
+{
+#ifdef WITH_LIBMV
+ libmv_CameraIntrinsicsDestroy(distortion->intrinsics);
+#endif
+
+ MEM_freeN(distortion);
+}
+
+ImBuf *BKE_tracking_undistort(MovieTracking *tracking, ImBuf *ibuf, int width, int height, float overscan)
+{
+ MovieTrackingCamera *camera= &tracking->camera;
+
+ if(camera->intrinsics == NULL)
+ camera->intrinsics= BKE_tracking_distortion_create();
+
+ return BKE_tracking_distortion_exec(camera->intrinsics, tracking, ibuf, width, height, overscan, 1);
+}
+
+ImBuf *BKE_tracking_distort(MovieTracking *tracking, ImBuf *ibuf, int width, int height, float overscan)
+{
+ MovieTrackingCamera *camera= &tracking->camera;
+
+ if(camera->intrinsics == NULL)
+ camera->intrinsics= BKE_tracking_distortion_create();
+
+ return BKE_tracking_distortion_exec(camera->intrinsics, tracking, ibuf, width, height, overscan, 0);
+}
+
+/* area - which part of marker should be selected. see TRACK_AREA_* constants */
+void BKE_tracking_select_track(MovieTracking *tracking, MovieTrackingTrack *track, int area, int extend)
+{
+ if(extend) {
+ BKE_tracking_track_flag(track, area, SELECT, 0);
+ } else {
+ MovieTrackingTrack *cur= tracking->tracks.first;
+
+ while(cur) {
+ if(cur==track) {
+ BKE_tracking_track_flag(cur, TRACK_AREA_ALL, SELECT, 1);
+ BKE_tracking_track_flag(cur, area, SELECT, 0);
+ }
+ else {
+ BKE_tracking_track_flag(cur, TRACK_AREA_ALL, SELECT, 1);
+ }
+
+ cur= cur->next;
+ }
+ }
+}
+
+void BKE_tracking_deselect_track(MovieTrackingTrack *track, int area)
+{
+ BKE_tracking_track_flag(track, area, SELECT, 1);
+}