The unit being "pixels". Before this change the solve errors were unitless in the UI. With this change in place, the UI is now clear on that the unit of the reprojection errors is pixels (px). Differential Revision: https://developer.blender.org/D8000
332 lines
9.7 KiB
C
332 lines
9.7 KiB
C
/*
|
|
* 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) 2016 Blender Foundation.
|
|
* All rights reserved.
|
|
*/
|
|
|
|
/** \file
|
|
* \ingroup spclip
|
|
*/
|
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
#include "DNA_camera_types.h"
|
|
#include "DNA_object_types.h" /* SELECT */
|
|
#include "DNA_screen_types.h"
|
|
#include "DNA_space_types.h"
|
|
|
|
#include "BLI_string.h"
|
|
#include "BLI_utildefines.h"
|
|
|
|
#include "BKE_context.h"
|
|
#include "BKE_global.h"
|
|
#include "BKE_lib_id.h"
|
|
#include "BKE_movieclip.h"
|
|
#include "BKE_report.h"
|
|
#include "BKE_tracking.h"
|
|
|
|
#include "DEG_depsgraph.h"
|
|
|
|
#include "WM_api.h"
|
|
#include "WM_types.h"
|
|
|
|
#include "ED_clip.h"
|
|
|
|
#include "clip_intern.h"
|
|
|
|
/********************** solve camera operator *********************/
|
|
|
|
typedef struct {
|
|
struct wmWindowManager *wm;
|
|
Scene *scene;
|
|
MovieClip *clip;
|
|
MovieClipUser user;
|
|
|
|
ReportList *reports;
|
|
|
|
char stats_message[256];
|
|
|
|
struct MovieReconstructContext *context;
|
|
} SolveCameraJob;
|
|
|
|
static bool solve_camera_initjob(
|
|
bContext *C, SolveCameraJob *scj, wmOperator *op, char *error_msg, int max_error)
|
|
{
|
|
SpaceClip *sc = CTX_wm_space_clip(C);
|
|
MovieClip *clip = ED_space_clip_get_clip(sc);
|
|
Scene *scene = CTX_data_scene(C);
|
|
MovieTracking *tracking = &clip->tracking;
|
|
MovieTrackingObject *object = BKE_tracking_object_get_active(tracking);
|
|
int width, height;
|
|
|
|
if (!BKE_tracking_reconstruction_check(tracking, object, error_msg, max_error)) {
|
|
return false;
|
|
}
|
|
|
|
/* Could fail if footage uses images with different sizes. */
|
|
BKE_movieclip_get_size(clip, &sc->user, &width, &height);
|
|
|
|
scj->wm = CTX_wm_manager(C);
|
|
scj->clip = clip;
|
|
scj->scene = scene;
|
|
scj->reports = op->reports;
|
|
scj->user = sc->user;
|
|
|
|
scj->context = BKE_tracking_reconstruction_context_new(
|
|
clip, object, object->keyframe1, object->keyframe2, width, height);
|
|
|
|
tracking->stats = MEM_callocN(sizeof(MovieTrackingStats), "solve camera stats");
|
|
|
|
WM_set_locked_interface(scj->wm, true);
|
|
|
|
return true;
|
|
}
|
|
|
|
static void solve_camera_updatejob(void *scv)
|
|
{
|
|
SolveCameraJob *scj = (SolveCameraJob *)scv;
|
|
MovieTracking *tracking = &scj->clip->tracking;
|
|
|
|
BLI_strncpy(tracking->stats->message, scj->stats_message, sizeof(tracking->stats->message));
|
|
}
|
|
|
|
static void solve_camera_startjob(void *scv, short *stop, short *do_update, float *progress)
|
|
{
|
|
SolveCameraJob *scj = (SolveCameraJob *)scv;
|
|
BKE_tracking_reconstruction_solve(
|
|
scj->context, stop, do_update, progress, scj->stats_message, sizeof(scj->stats_message));
|
|
}
|
|
|
|
static void solve_camera_freejob(void *scv)
|
|
{
|
|
SolveCameraJob *scj = (SolveCameraJob *)scv;
|
|
MovieTracking *tracking = &scj->clip->tracking;
|
|
Scene *scene = scj->scene;
|
|
MovieClip *clip = scj->clip;
|
|
int solved;
|
|
|
|
/* WindowManager is missing in the job when initialization is incomplete.
|
|
* In this case the interface is not locked either. */
|
|
if (scj->wm != NULL) {
|
|
WM_set_locked_interface(scj->wm, false);
|
|
}
|
|
|
|
if (!scj->context) {
|
|
/* job weren't fully initialized due to some error */
|
|
MEM_freeN(scj);
|
|
return;
|
|
}
|
|
|
|
solved = BKE_tracking_reconstruction_finish(scj->context, tracking);
|
|
if (!solved) {
|
|
const char *error_message = BKE_tracking_reconstruction_error_message_get(scj->context);
|
|
if (error_message[0]) {
|
|
BKE_report(scj->reports, RPT_ERROR, error_message);
|
|
}
|
|
else {
|
|
BKE_report(
|
|
scj->reports, RPT_WARNING, "Some data failed to reconstruct (see console for details)");
|
|
}
|
|
}
|
|
else {
|
|
BKE_reportf(scj->reports,
|
|
RPT_INFO,
|
|
"Average re-projection error: %.2f px",
|
|
tracking->reconstruction.error);
|
|
}
|
|
|
|
/* Set currently solved clip as active for scene. */
|
|
if (scene->clip != NULL) {
|
|
id_us_min(&clip->id);
|
|
}
|
|
scene->clip = clip;
|
|
id_us_plus(&clip->id);
|
|
|
|
/* Set blender camera focal length so result would look fine there. */
|
|
if (scene->camera != NULL && scene->camera->data &&
|
|
GS(((ID *)scene->camera->data)->name) == ID_CA) {
|
|
Camera *camera = (Camera *)scene->camera->data;
|
|
int width, height;
|
|
BKE_movieclip_get_size(clip, &scj->user, &width, &height);
|
|
BKE_tracking_camera_to_blender(tracking, scene, camera, width, height);
|
|
DEG_id_tag_update(&camera->id, ID_RECALC_COPY_ON_WRITE);
|
|
WM_main_add_notifier(NC_OBJECT, camera);
|
|
}
|
|
|
|
MEM_freeN(tracking->stats);
|
|
tracking->stats = NULL;
|
|
|
|
DEG_id_tag_update(&clip->id, 0);
|
|
|
|
WM_main_add_notifier(NC_MOVIECLIP | NA_EVALUATED, clip);
|
|
WM_main_add_notifier(NC_OBJECT | ND_TRANSFORM, NULL);
|
|
|
|
/* Update active clip displayed in scene buttons. */
|
|
WM_main_add_notifier(NC_SCENE, scene);
|
|
|
|
BKE_tracking_reconstruction_context_free(scj->context);
|
|
MEM_freeN(scj);
|
|
}
|
|
|
|
static int solve_camera_exec(bContext *C, wmOperator *op)
|
|
{
|
|
SolveCameraJob *scj;
|
|
char error_msg[256] = "\0";
|
|
scj = MEM_callocN(sizeof(SolveCameraJob), "SolveCameraJob data");
|
|
if (!solve_camera_initjob(C, scj, op, error_msg, sizeof(error_msg))) {
|
|
if (error_msg[0]) {
|
|
BKE_report(op->reports, RPT_ERROR, error_msg);
|
|
}
|
|
solve_camera_freejob(scj);
|
|
return OPERATOR_CANCELLED;
|
|
}
|
|
solve_camera_startjob(scj, NULL, NULL, NULL);
|
|
solve_camera_freejob(scj);
|
|
return OPERATOR_FINISHED;
|
|
}
|
|
|
|
static int solve_camera_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
|
|
{
|
|
SolveCameraJob *scj;
|
|
SpaceClip *sc = CTX_wm_space_clip(C);
|
|
MovieClip *clip = ED_space_clip_get_clip(sc);
|
|
MovieTracking *tracking = &clip->tracking;
|
|
MovieTrackingReconstruction *reconstruction = BKE_tracking_get_active_reconstruction(tracking);
|
|
wmJob *wm_job;
|
|
char error_msg[256] = "\0";
|
|
|
|
if (WM_jobs_test(CTX_wm_manager(C), CTX_data_scene(C), WM_JOB_TYPE_CLIP_SOLVE_CAMERA)) {
|
|
/* only one solve is allowed at a time */
|
|
return OPERATOR_CANCELLED;
|
|
}
|
|
|
|
scj = MEM_callocN(sizeof(SolveCameraJob), "SolveCameraJob data");
|
|
if (!solve_camera_initjob(C, scj, op, error_msg, sizeof(error_msg))) {
|
|
if (error_msg[0]) {
|
|
BKE_report(op->reports, RPT_ERROR, error_msg);
|
|
}
|
|
solve_camera_freejob(scj);
|
|
return OPERATOR_CANCELLED;
|
|
}
|
|
|
|
BLI_strncpy(tracking->stats->message,
|
|
"Solving camera | Preparing solve",
|
|
sizeof(tracking->stats->message));
|
|
|
|
/* Hide reconstruction statistics from previous solve. */
|
|
reconstruction->flag &= ~TRACKING_RECONSTRUCTED;
|
|
WM_event_add_notifier(C, NC_MOVIECLIP | NA_EVALUATED, clip);
|
|
|
|
/* Setup job. */
|
|
wm_job = WM_jobs_get(CTX_wm_manager(C),
|
|
CTX_wm_window(C),
|
|
CTX_data_scene(C),
|
|
"Solve Camera",
|
|
WM_JOB_PROGRESS,
|
|
WM_JOB_TYPE_CLIP_SOLVE_CAMERA);
|
|
WM_jobs_customdata_set(wm_job, scj, solve_camera_freejob);
|
|
WM_jobs_timer(wm_job, 0.1, NC_MOVIECLIP | NA_EVALUATED, 0);
|
|
WM_jobs_callbacks(wm_job, solve_camera_startjob, NULL, solve_camera_updatejob, NULL);
|
|
|
|
G.is_break = false;
|
|
|
|
WM_jobs_start(CTX_wm_manager(C), wm_job);
|
|
WM_cursor_wait(0);
|
|
|
|
/* add modal handler for ESC */
|
|
WM_event_add_modal_handler(C, op);
|
|
|
|
return OPERATOR_RUNNING_MODAL;
|
|
}
|
|
|
|
static int solve_camera_modal(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
|
|
{
|
|
/* No running solver, remove handler and pass through. */
|
|
if (0 == WM_jobs_test(CTX_wm_manager(C), CTX_wm_area(C), WM_JOB_TYPE_CLIP_SOLVE_CAMERA)) {
|
|
return OPERATOR_FINISHED | OPERATOR_PASS_THROUGH;
|
|
}
|
|
|
|
/* Running solver. */
|
|
switch (event->type) {
|
|
case EVT_ESCKEY:
|
|
return OPERATOR_RUNNING_MODAL;
|
|
}
|
|
|
|
return OPERATOR_PASS_THROUGH;
|
|
}
|
|
|
|
void CLIP_OT_solve_camera(wmOperatorType *ot)
|
|
{
|
|
/* identifiers */
|
|
ot->name = "Solve Camera";
|
|
ot->description = "Solve camera motion from tracks";
|
|
ot->idname = "CLIP_OT_solve_camera";
|
|
|
|
/* api callbacks */
|
|
ot->exec = solve_camera_exec;
|
|
ot->invoke = solve_camera_invoke;
|
|
ot->modal = solve_camera_modal;
|
|
ot->poll = ED_space_clip_tracking_poll;
|
|
|
|
/* flags */
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
|
}
|
|
|
|
/********************** clear solution operator *********************/
|
|
|
|
static int clear_solution_exec(bContext *C, wmOperator *UNUSED(op))
|
|
{
|
|
SpaceClip *sc = CTX_wm_space_clip(C);
|
|
MovieClip *clip = ED_space_clip_get_clip(sc);
|
|
MovieTracking *tracking = &clip->tracking;
|
|
ListBase *tracksbase = BKE_tracking_get_active_tracks(&clip->tracking);
|
|
MovieTrackingReconstruction *reconstruction = BKE_tracking_get_active_reconstruction(tracking);
|
|
|
|
for (MovieTrackingTrack *track = tracksbase->first; track != NULL; track = track->next) {
|
|
track->flag &= ~TRACK_HAS_BUNDLE;
|
|
}
|
|
|
|
if (reconstruction->cameras != NULL) {
|
|
MEM_freeN(reconstruction->cameras);
|
|
reconstruction->cameras = NULL;
|
|
}
|
|
|
|
reconstruction->camnr = 0;
|
|
reconstruction->flag &= ~TRACKING_RECONSTRUCTED;
|
|
|
|
DEG_id_tag_update(&clip->id, 0);
|
|
|
|
WM_event_add_notifier(C, NC_MOVIECLIP | NA_EVALUATED, clip);
|
|
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, NULL);
|
|
|
|
return OPERATOR_FINISHED;
|
|
}
|
|
|
|
void CLIP_OT_clear_solution(wmOperatorType *ot)
|
|
{
|
|
/* identifiers */
|
|
ot->name = "Clear Solution";
|
|
ot->description = "Clear all calculated data";
|
|
ot->idname = "CLIP_OT_clear_solution";
|
|
|
|
/* api callbacks */
|
|
ot->exec = clear_solution_exec;
|
|
ot->poll = ED_space_clip_tracking_poll;
|
|
|
|
/* flags */
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
|
}
|