This repository has been archived on 2023-10-09. You can view files and clone it, but cannot push or open issues or pull requests.
Files
blender-archive/source/blender/blenkernel/intern/tracking_util.c
Sergey Sharybin f312f890f1 Libmv: Support disabled color channels in tracking settings
This was never ported to a new tracking pipeline and now it's done using
FrameAccessor::Transform routines. Quite striaghtforward, but i've changed
order of grayscale conversion in blender side with call of transform callback.

This way it's much easier to perform rescaling in libmv side.
2014-10-30 23:29:53 +05:00

898 lines
28 KiB
C

/*
* ***** 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
* Keir Mierle
*
* ***** END GPL LICENSE BLOCK *****
*/
/** \file blender/blenkernel/intern/tracking_util.c
* \ingroup bke
*
* This file contains implementation of function which are used
* by multiple tracking files but which should not be public.
*/
#include <stddef.h>
#include "MEM_guardedalloc.h"
#include "DNA_movieclip_types.h"
#include "BLI_utildefines.h"
#include "BLI_math.h"
#include "BLI_listbase.h"
#include "BLI_ghash.h"
#include "BLI_path_util.h"
#include "BLI_string.h"
#include "BLF_translation.h"
#include "BKE_movieclip.h"
#include "BKE_tracking.h"
#include "IMB_imbuf_types.h"
#include "IMB_imbuf.h"
#include "IMB_moviecache.h"
#include "tracking_private.h"
#include "libmv-capi.h"
/*********************** Tracks map *************************/
TracksMap *tracks_map_new(const char *object_name, bool is_camera, int num_tracks, int customdata_size)
{
TracksMap *map = MEM_callocN(sizeof(TracksMap), "TrackingsMap");
BLI_strncpy(map->object_name, object_name, sizeof(map->object_name));
map->is_camera = is_camera;
map->num_tracks = num_tracks;
map->customdata_size = customdata_size;
map->tracks = MEM_callocN(sizeof(MovieTrackingTrack) * num_tracks, "TrackingsMap tracks");
if (customdata_size)
map->customdata = MEM_callocN(customdata_size * num_tracks, "TracksMap customdata");
map->hash = BLI_ghash_ptr_new("TracksMap hash");
BLI_spin_init(&map->spin_lock);
return map;
}
int tracks_map_get_size(TracksMap *map)
{
return map->num_tracks;
}
void tracks_map_get_indexed_element(TracksMap *map, int index, MovieTrackingTrack **track, void **customdata)
{
*track = &map->tracks[index];
if (map->customdata)
*customdata = &map->customdata[index * map->customdata_size];
}
void tracks_map_insert(TracksMap *map, MovieTrackingTrack *track, void *customdata)
{
MovieTrackingTrack new_track = *track;
new_track.markers = MEM_dupallocN(new_track.markers);
map->tracks[map->ptr] = new_track;
if (customdata)
memcpy(&map->customdata[map->ptr * map->customdata_size], customdata, map->customdata_size);
BLI_ghash_insert(map->hash, &map->tracks[map->ptr], track);
map->ptr++;
}
void tracks_map_merge(TracksMap *map, MovieTracking *tracking)
{
MovieTrackingTrack *track;
ListBase tracks = {NULL, NULL}, new_tracks = {NULL, NULL};
ListBase *old_tracks;
int a;
if (map->is_camera) {
old_tracks = &tracking->tracks;
}
else {
MovieTrackingObject *object = BKE_tracking_object_get_named(tracking, map->object_name);
if (!object) {
/* object was deleted by user, create new one */
object = BKE_tracking_object_add(tracking, map->object_name);
}
old_tracks = &object->tracks;
}
/* duplicate currently operating tracks to temporary list.
* this is needed to keep names in unique state and it's faster to change names
* of currently operating tracks (if needed)
*/
for (a = 0; a < map->num_tracks; a++) {
MovieTrackingTrack *old_track;
bool mapped_to_old = false;
track = &map->tracks[a];
/* find original of operating track in list of previously displayed tracks */
old_track = BLI_ghash_lookup(map->hash, track);
if (old_track) {
if (BLI_findindex(old_tracks, old_track) != -1) {
BLI_remlink(old_tracks, old_track);
BLI_spin_lock(&map->spin_lock);
/* Copy flags like selection back to the track map. */
track->flag = old_track->flag;
track->pat_flag = old_track->pat_flag;
track->search_flag = old_track->search_flag;
/* Copy all the rest settings back from the map to the actual tracks. */
MEM_freeN(old_track->markers);
*old_track = *track;
old_track->markers = MEM_dupallocN(old_track->markers);
BLI_spin_unlock(&map->spin_lock);
BLI_addtail(&tracks, old_track);
mapped_to_old = true;
}
}
if (mapped_to_old == false) {
MovieTrackingTrack *new_track = BKE_tracking_track_duplicate(track);
/* Update old-new track mapping */
BLI_ghash_remove(map->hash, track, NULL, NULL);
BLI_ghash_insert(map->hash, track, new_track);
BLI_addtail(&tracks, new_track);
}
}
/* move all tracks, which aren't operating */
track = old_tracks->first;
while (track) {
MovieTrackingTrack *next = track->next;
BLI_addtail(&new_tracks, track);
track = next;
}
/* now move all tracks which are currently operating 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, CTX_DATA_(BLF_I18NCONTEXT_ID_MOVIECLIP, "Track"), '.',
offsetof(MovieTrackingTrack, name), sizeof(track->name));
track = next;
}
*old_tracks = new_tracks;
}
void tracks_map_free(TracksMap *map, void (*customdata_free)(void *customdata))
{
int i = 0;
BLI_ghash_free(map->hash, NULL, NULL);
for (i = 0; i < map->num_tracks; i++) {
if (map->customdata && customdata_free)
customdata_free(&map->customdata[i * map->customdata_size]);
BKE_tracking_track_free(&map->tracks[i]);
}
if (map->customdata)
MEM_freeN(map->customdata);
MEM_freeN(map->tracks);
BLI_spin_end(&map->spin_lock);
MEM_freeN(map);
}
/*********************** Space transformation functions *************************/
/* Three coordinate frames: Frame, Search, and Marker
* Two units: Pixels, Unified
* Notation: {coordinate frame}_{unit}; for example, "search_pixel" are search
* window relative coordinates in pixels, and "frame_unified" are unified 0..1
* coordinates relative to the entire frame.
*/
static void unified_to_pixel(int frame_width, int frame_height,
const float unified_coords[2], float pixel_coords[2])
{
pixel_coords[0] = unified_coords[0] * frame_width;
pixel_coords[1] = unified_coords[1] * frame_height;
}
static void marker_to_frame_unified(const MovieTrackingMarker *marker, const float marker_unified_coords[2],
float frame_unified_coords[2])
{
frame_unified_coords[0] = marker_unified_coords[0] + marker->pos[0];
frame_unified_coords[1] = marker_unified_coords[1] + marker->pos[1];
}
static void marker_unified_to_frame_pixel_coordinates(int frame_width, int frame_height,
const MovieTrackingMarker *marker,
const float marker_unified_coords[2],
float frame_pixel_coords[2])
{
marker_to_frame_unified(marker, marker_unified_coords, frame_pixel_coords);
unified_to_pixel(frame_width, frame_height, frame_pixel_coords, frame_pixel_coords);
}
void tracking_get_search_origin_frame_pixel(int frame_width, int frame_height,
const MovieTrackingMarker *marker,
float frame_pixel[2])
{
/* Get the lower left coordinate of the search window and snap to pixel coordinates */
marker_unified_to_frame_pixel_coordinates(frame_width, frame_height, marker, marker->search_min, frame_pixel);
frame_pixel[0] = (int)frame_pixel[0];
frame_pixel[1] = (int)frame_pixel[1];
}
static void pixel_to_unified(int frame_width, int frame_height, const float pixel_coords[2], float unified_coords[2])
{
unified_coords[0] = pixel_coords[0] / frame_width;
unified_coords[1] = pixel_coords[1] / frame_height;
}
static void marker_unified_to_search_pixel(int frame_width, int frame_height,
const MovieTrackingMarker *marker,
const float marker_unified[2], float search_pixel[2])
{
float frame_pixel[2];
float search_origin_frame_pixel[2];
marker_unified_to_frame_pixel_coordinates(frame_width, frame_height, marker, marker_unified, frame_pixel);
tracking_get_search_origin_frame_pixel(frame_width, frame_height, marker, search_origin_frame_pixel);
sub_v2_v2v2(search_pixel, frame_pixel, search_origin_frame_pixel);
}
static void search_pixel_to_marker_unified(int frame_width, int frame_height,
const MovieTrackingMarker *marker,
const float search_pixel[2], float marker_unified[2])
{
float frame_unified[2];
float search_origin_frame_pixel[2];
tracking_get_search_origin_frame_pixel(frame_width, frame_height, marker, search_origin_frame_pixel);
add_v2_v2v2(frame_unified, search_pixel, search_origin_frame_pixel);
pixel_to_unified(frame_width, frame_height, frame_unified, frame_unified);
/* marker pos is in frame unified */
sub_v2_v2v2(marker_unified, frame_unified, marker->pos);
}
/* Each marker has 5 coordinates associated with it that get warped with
* tracking: the four corners ("pattern_corners"), and the center ("pos").
* This function puts those 5 points into the appropriate frame for tracking
* (the "search" coordinate frame).
*/
void tracking_get_marker_coords_for_tracking(int frame_width, int frame_height,
const MovieTrackingMarker *marker,
double search_pixel_x[5], double search_pixel_y[5])
{
int i;
float unified_coords[2];
float pixel_coords[2];
/* Convert the corners into search space coordinates. */
for (i = 0; i < 4; i++) {
marker_unified_to_search_pixel(frame_width, frame_height, marker, marker->pattern_corners[i], pixel_coords);
search_pixel_x[i] = pixel_coords[0] - 0.5f;
search_pixel_y[i] = pixel_coords[1] - 0.5f;
}
/* Convert the center position (aka "pos"); this is the origin */
unified_coords[0] = 0.0f;
unified_coords[1] = 0.0f;
marker_unified_to_search_pixel(frame_width, frame_height, marker, unified_coords, pixel_coords);
search_pixel_x[4] = pixel_coords[0] - 0.5f;
search_pixel_y[4] = pixel_coords[1] - 0.5f;
}
/* Inverse of above. */
void tracking_set_marker_coords_from_tracking(int frame_width, int frame_height, MovieTrackingMarker *marker,
const double search_pixel_x[5], const double search_pixel_y[5])
{
int i;
float marker_unified[2];
float search_pixel[2];
/* Convert the corners into search space coordinates. */
for (i = 0; i < 4; i++) {
search_pixel[0] = search_pixel_x[i] + 0.5;
search_pixel[1] = search_pixel_y[i] + 0.5;
search_pixel_to_marker_unified(frame_width, frame_height, marker, search_pixel, marker->pattern_corners[i]);
}
/* Convert the center position (aka "pos"); this is the origin */
search_pixel[0] = search_pixel_x[4] + 0.5;
search_pixel[1] = search_pixel_y[4] + 0.5;
search_pixel_to_marker_unified(frame_width, frame_height, marker, search_pixel, marker_unified);
/* If the tracker tracked nothing, then "marker_unified" would be zero.
* Otherwise, the entire patch shifted, and that delta should be applied to
* all the coordinates.
*/
for (i = 0; i < 4; i++) {
marker->pattern_corners[i][0] -= marker_unified[0];
marker->pattern_corners[i][1] -= marker_unified[1];
}
marker->pos[0] += marker_unified[0];
marker->pos[1] += marker_unified[1];
}
/*********************** General purpose utility functions *************************/
/* Place a disabled marker before or after specified ref_marker.
*
* If before is truth, disabled marker is placed before reference
* one, and it's placed after it otherwise.
*
* If there's already a marker at the frame where disabled one
* is expected to be placed, nothing will happen if overwrite
* is false.
*/
void tracking_marker_insert_disabled(MovieTrackingTrack *track, const MovieTrackingMarker *ref_marker,
bool before, bool 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 (overwrite || !BKE_tracking_track_has_marker_at_frame(track, marker_new.framenr))
BKE_tracking_marker_insert(track, &marker_new);
}
/* Fill in Libmv C-API camera intrinsics options from tracking structure. */
void tracking_cameraIntrinscisOptionsFromTracking(MovieTracking *tracking,
int calibration_width, int calibration_height,
libmv_CameraIntrinsicsOptions *camera_intrinsics_options)
{
MovieTrackingCamera *camera = &tracking->camera;
float aspy = 1.0f / tracking->camera.pixel_aspect;
camera_intrinsics_options->focal_length = camera->focal;
camera_intrinsics_options->principal_point_x = camera->principal[0];
camera_intrinsics_options->principal_point_y = camera->principal[1] * aspy;
switch (camera->distortion_model) {
case TRACKING_DISTORTION_MODEL_POLYNOMIAL:
camera_intrinsics_options->distortion_model = LIBMV_DISTORTION_MODEL_POLYNOMIAL;
camera_intrinsics_options->polynomial_k1 = camera->k1;
camera_intrinsics_options->polynomial_k2 = camera->k2;
camera_intrinsics_options->polynomial_k3 = camera->k3;
camera_intrinsics_options->polynomial_p1 = 0.0;
camera_intrinsics_options->polynomial_p2 = 0.0;
break;
case TRACKING_DISTORTION_MODEL_DIVISION:
camera_intrinsics_options->distortion_model = LIBMV_DISTORTION_MODEL_DIVISION;
camera_intrinsics_options->division_k1 = camera->division_k1;
camera_intrinsics_options->division_k2 = camera->division_k2;
break;
default:
BLI_assert(!"Unknown distortion model");
}
camera_intrinsics_options->image_width = calibration_width;
camera_intrinsics_options->image_height = (int) (calibration_height * aspy);
}
void tracking_trackingCameraFromIntrinscisOptions(MovieTracking *tracking,
const libmv_CameraIntrinsicsOptions *camera_intrinsics_options)
{
float aspy = 1.0f / tracking->camera.pixel_aspect;
MovieTrackingCamera *camera = &tracking->camera;
camera->focal = camera_intrinsics_options->focal_length;
camera->principal[0] = camera_intrinsics_options->principal_point_x;
camera->principal[1] = camera_intrinsics_options->principal_point_y / (double) aspy;
switch (camera_intrinsics_options->distortion_model) {
case LIBMV_DISTORTION_MODEL_POLYNOMIAL:
camera->distortion_model = TRACKING_DISTORTION_MODEL_POLYNOMIAL;
camera->k1 = camera_intrinsics_options->polynomial_k1;
camera->k2 = camera_intrinsics_options->polynomial_k2;
camera->k3 = camera_intrinsics_options->polynomial_k3;
break;
case LIBMV_DISTORTION_MODEL_DIVISION:
camera->distortion_model = TRACKING_DISTORTION_MODEL_DIVISION;
camera->division_k1 = camera_intrinsics_options->division_k1;
camera->division_k2 = camera_intrinsics_options->division_k2;
break;
default:
BLI_assert(!"Unknown distortion model");
}
}
/* Get previous keyframed marker. */
MovieTrackingMarker *tracking_get_keyframed_marker(MovieTrackingTrack *track,
int current_frame,
bool backwards)
{
MovieTrackingMarker *marker_keyed = NULL;
MovieTrackingMarker *marker_keyed_fallback = NULL;
int a = BKE_tracking_marker_get(track, current_frame) - track->markers;
while (a >= 0 && a < track->markersnr) {
int next = backwards ? a + 1 : a - 1;
bool is_keyframed = false;
MovieTrackingMarker *cur_marker = &track->markers[a];
MovieTrackingMarker *next_marker = NULL;
if (next >= 0 && next < track->markersnr)
next_marker = &track->markers[next];
if ((cur_marker->flag & MARKER_DISABLED) == 0) {
/* If it'll happen so we didn't find a real keyframe marker,
* fallback to the first marker in current tracked segment
* as a keyframe.
*/
if (next_marker && next_marker->flag & MARKER_DISABLED) {
if (marker_keyed_fallback == NULL)
marker_keyed_fallback = cur_marker;
}
is_keyframed |= (cur_marker->flag & MARKER_TRACKED) == 0;
}
if (is_keyframed) {
marker_keyed = cur_marker;
break;
}
a = next;
}
if (marker_keyed == NULL)
marker_keyed = marker_keyed_fallback;
return marker_keyed;
}
/*********************** Frame accessr *************************/
typedef struct AccessCacheKey {
int clip_index;
int frame;
int downscale;
libmv_InputMode input_mode;
int64_t transform_key;
} AccessCacheKey;
static unsigned int accesscache_hashhash(const void *key_v)
{
const AccessCacheKey *key = (const AccessCacheKey *) key_v;
/* TODP(sergey): Need better hasing here for faster frame access. */
return key->clip_index << 16 | key->frame;
}
static bool accesscache_hashcmp(const void *a_v, const void *b_v)
{
const AccessCacheKey *a = (const AccessCacheKey *) a_v;
const AccessCacheKey *b = (const AccessCacheKey *) b_v;
#define COMPARE_FIELD(field)
{ \
if (a->clip_index != b->clip_index) { \
return false; \
} \
} (void) 0
COMPARE_FIELD(clip_index);
COMPARE_FIELD(frame);
COMPARE_FIELD(downscale);
COMPARE_FIELD(input_mode);
COMPARE_FIELD(transform_key);
#undef COMPARE_FIELD
return true;
}
static void accesscache_put(TrackingImageAccessor *accessor,
int clip_index,
int frame,
libmv_InputMode input_mode,
int downscale,
int64_t transform_key,
ImBuf *ibuf)
{
AccessCacheKey key;
key.clip_index = clip_index;
key.frame = frame;
key.input_mode = input_mode;
key.downscale = downscale;
key.transform_key = transform_key;
IMB_moviecache_put(accessor->cache, &key, ibuf);
}
static ImBuf *accesscache_get(TrackingImageAccessor *accessor,
int clip_index,
int frame,
libmv_InputMode input_mode,
int downscale,
int64_t transform_key)
{
AccessCacheKey key;
key.clip_index = clip_index;
key.frame = frame;
key.input_mode = input_mode;
key.downscale = downscale;
key.transform_key = transform_key;
return IMB_moviecache_get(accessor->cache, &key);
}
static ImBuf *accessor_get_preprocessed_ibuf(TrackingImageAccessor *accessor,
int clip_index,
int frame)
{
MovieClip *clip;
MovieClipUser user;
ImBuf *ibuf;
int scene_frame;
BLI_assert(clip_index < accessor->num_clips);
clip = accessor->clips[clip_index];
scene_frame = BKE_movieclip_remap_clip_to_scene_frame(clip, frame);
BKE_movieclip_user_set_frame(&user, scene_frame);
user.render_size = MCLIP_PROXY_RENDER_SIZE_FULL;
user.render_flag = 0;
ibuf = BKE_movieclip_get_ibuf(clip, &user);
return ibuf;
}
static ImBuf *make_grayscale_ibuf_copy(ImBuf *ibuf)
{
ImBuf *grayscale = IMB_allocImBuf(ibuf->x, ibuf->y, 32, 0);
size_t size;
int i;
BLI_assert(ibuf->channels == 3 || ibuf->channels == 4);
/* TODO(sergey): Bummer, currently IMB API only allows to create 4 channels
* float buffer, so we do it manually here.
*
* Will generalize it later.
*/
size = (size_t)grayscale->x * (size_t)grayscale->y * sizeof(float);
grayscale->channels = 1;
if ((grayscale->rect_float = MEM_mapallocN(size, "tracking grayscale image"))) {
grayscale->mall |= IB_rectfloat;
grayscale->flags |= IB_rectfloat;
}
for (i = 0; i < grayscale->x * grayscale->y; ++i) {
const float *pixel = ibuf->rect_float + ibuf->channels * i;
grayscale->rect_float[i] = 0.2126f * pixel[0] +
0.7152f * pixel[1] +
0.0722f * pixel[2];
}
return grayscale;
}
static void ibuf_to_float_image(const ImBuf *ibuf, libmv_FloatImage *float_image)
{
BLI_assert(ibuf->rect_float != NULL);
float_image->buffer = ibuf->rect_float;
float_image->width = ibuf->x;
float_image->height = ibuf->y;
float_image->channels = ibuf->channels;
}
static ImBuf *float_image_to_ibuf(libmv_FloatImage *float_image)
{
ImBuf *ibuf = IMB_allocImBuf(float_image->width, float_image->height, 32, 0);
size_t size = (size_t)ibuf->x * (size_t)ibuf->y *
float_image->channels * sizeof(float);
ibuf->channels = float_image->channels;
if ((ibuf->rect_float = MEM_mapallocN(size, "tracking grayscale image"))) {
ibuf->mall |= IB_rectfloat;
ibuf->flags |= IB_rectfloat;
}
memcpy(ibuf->rect_float, float_image->buffer, size);
return ibuf;
}
static ImBuf *accessor_get_ibuf(TrackingImageAccessor *accessor,
int clip_index,
int frame,
libmv_InputMode input_mode,
int downscale,
const libmv_Region *region,
const libmv_FrameTransform *transform)
{
ImBuf *ibuf, *orig_ibuf, *final_ibuf;
int64_t transform_key = 0;
if (transform != NULL) {
transform_key = libmv_frameAccessorgetTransformKey(transform);
}
/* First try to get fully processed image from the cache. */
ibuf = accesscache_get(accessor,
clip_index,
frame,
input_mode,
downscale,
transform_key);
if (ibuf != NULL) {
return ibuf;
}
/* And now we do postprocessing of the original frame. */
orig_ibuf = accessor_get_preprocessed_ibuf(accessor, clip_index, frame);
if (orig_ibuf == NULL) {
return NULL;
}
if (region != NULL) {
int width = region->max[0] - region->min[0],
height = region->max[1] - region->min[1];
/* If the requested region goes outside of the actual frame we still
* return the requested region size, but only fill it's partially with
* the data we can.
*/
int clamped_origin_x = max_ii((int)region->min[0], 0),
clamped_origin_y = max_ii((int)region->min[1], 0);
int dst_offset_x = clamped_origin_x - (int)region->min[0],
dst_offset_y = clamped_origin_y - (int)region->min[1];
int clamped_width = width - dst_offset_x,
clamped_height = height - dst_offset_y;
clamped_width = min_ii(clamped_width, orig_ibuf->x - clamped_origin_x);
clamped_height = min_ii(clamped_height, orig_ibuf->y - clamped_origin_y);
final_ibuf = IMB_allocImBuf(width, height, 32, IB_rectfloat);
if (orig_ibuf->rect_float != NULL) {
IMB_rectcpy(final_ibuf, orig_ibuf,
dst_offset_x, dst_offset_y,
clamped_origin_x, clamped_origin_y,
clamped_width, clamped_height);
}
else {
int y;
/* TODO(sergey): We don't do any color space or alpha conversion
* here. Probably Libmv is better to work in the linear space,
* but keep sRGB space here for compatibility for now.
*/
for (y = 0; y < clamped_height; ++y) {
int x;
for (x = 0; x < clamped_width; ++x) {
int src_x = x + clamped_origin_x,
src_y = y + clamped_origin_y;
int dst_x = x + dst_offset_x,
dst_y = y + dst_offset_y;
int dst_index = (dst_y * width + dst_x) * 4,
src_index = (src_y * orig_ibuf->x + src_x) * 4;
rgba_uchar_to_float(final_ibuf->rect_float + dst_index,
(unsigned char *)orig_ibuf->rect +
src_index);
}
}
}
}
else {
/* Libmv only works with float images,
*
* This would likely make it so loads of float buffers are being stored
* in the cache which is nice on the one hand (faster re-use of the
* frames) but on the other hand it bumps the memory usage up.
*/
BLI_lock_thread(LOCK_MOVIECLIP);
IMB_float_from_rect(orig_ibuf);
BLI_unlock_thread(LOCK_MOVIECLIP);
final_ibuf = orig_ibuf;
}
if (downscale > 0) {
if (final_ibuf == orig_ibuf) {
final_ibuf = IMB_dupImBuf(orig_ibuf);
}
IMB_scaleImBuf(final_ibuf,
ibuf->x / (1 << downscale),
ibuf->y / (1 << downscale));
}
if (transform != NULL) {
libmv_FloatImage input_image, output_image;
ibuf_to_float_image(final_ibuf, &input_image);
libmv_frameAccessorgetTransformRun(transform,
&input_image,
&output_image);
if (final_ibuf != orig_ibuf) {
IMB_freeImBuf(final_ibuf);
}
final_ibuf = float_image_to_ibuf(&output_image);
libmv_floatImageDestroy(&output_image);
}
if (input_mode == LIBMV_IMAGE_MODE_RGBA) {
BLI_assert(ibuf->channels == 3 || ibuf->channels == 4);
/* pass */
}
else /* if (input_mode == LIBMV_IMAGE_MODE_MONO) */ {
if (final_ibuf->channels != 1) {
ImBuf *grayscale_ibuf = make_grayscale_ibuf_copy(final_ibuf);
if (final_ibuf != orig_ibuf) {
/* We dereference original frame later. */
IMB_freeImBuf(final_ibuf);
}
final_ibuf = grayscale_ibuf;
}
}
/* it's possible processing stil didn't happen at this point,
* but we really need a copy of the buffer to be transformed
* and to be put to the cache.
*/
if (final_ibuf == orig_ibuf) {
final_ibuf = IMB_dupImBuf(orig_ibuf);
}
IMB_freeImBuf(orig_ibuf);
/* We put postprocessed frame to the cache always for now,
* not the smartest thing in the world, but who cares at this point.
*/
/* TODO(sergey): Disable cache for now, because we don't store region
* in the cache key and can't check whether cached version is usable for
* us or not.
*
* Need to think better about what to cache and when.
*/
if (false) {
accesscache_put(accessor,
clip_index,
frame,
input_mode,
downscale,
transform_key,
final_ibuf);
}
return final_ibuf;
}
static libmv_CacheKey accessor_get_image_callback(
struct libmv_FrameAccessorUserData *user_data,
int clip_index,
int frame,
libmv_InputMode input_mode,
int downscale,
const libmv_Region *region,
const libmv_FrameTransform *transform,
float **destination,
int *width,
int *height,
int *channels)
{
TrackingImageAccessor *accessor = (TrackingImageAccessor *) user_data;
ImBuf *ibuf;
BLI_assert(clip_index >= 0 && clip_index < accessor->num_clips);
ibuf = accessor_get_ibuf(accessor,
clip_index,
frame,
input_mode,
downscale,
region,
transform);
if (ibuf) {
*destination = ibuf->rect_float;
*width = ibuf->x;
*height = ibuf->y;
*channels = ibuf->channels;
}
else {
*destination = NULL;
*width = 0;
*height = 0;
*channels = 0;
}
return ibuf;
}
static void accessor_release_image_callback(libmv_CacheKey cache_key)
{
ImBuf *ibuf = (ImBuf *) cache_key;
IMB_freeImBuf(ibuf);
}
TrackingImageAccessor *tracking_image_accessor_new(MovieClip *clips[MAX_ACCESSOR_CLIP],
int num_clips,
int start_frame)
{
TrackingImageAccessor *accessor =
MEM_callocN(sizeof(TrackingImageAccessor), "tracking image accessor");
BLI_assert(num_clips <= MAX_ACCESSOR_CLIP);
accessor->cache = IMB_moviecache_create("frame access cache",
sizeof(AccessCacheKey),
accesscache_hashhash,
accesscache_hashcmp);
memcpy(accessor->clips, clips, num_clips * sizeof(MovieClip*));
accessor->num_clips = num_clips;
accessor->start_frame = start_frame;
accessor->libmv_accessor =
libmv_FrameAccessorNew((libmv_FrameAccessorUserData *) accessor,
accessor_get_image_callback,
accessor_release_image_callback);
return accessor;
}
void tracking_image_accessor_destroy(TrackingImageAccessor *accessor)
{
IMB_moviecache_free(accessor->cache);
libmv_FrameAccessorDestroy(accessor->libmv_accessor);
MEM_freeN(accessor);
}