Tracking: Implement Nuke/Natron distortion model

Neither Nuke nor Natron support OpenCV's radial distortion model
which makes it impossible to have any kind of interoperability.

The new model is available under the distortion model menu in Lens
settings.

Differential Revision: https://developer.blender.org/D7484
This commit is contained in:
Sergey Sharybin 2020-04-20 17:33:03 +02:00
parent 0cfd2d6f4b
commit b7bcd0a87c
Notes: blender-bot 2023-02-13 22:42:13 +01:00
Referenced by issue #76240, Blender 2.90 crash if rotate viewport in editmode
12 changed files with 555 additions and 13 deletions

View File

@ -24,6 +24,7 @@
using libmv::CameraIntrinsics;
using libmv::DivisionCameraIntrinsics;
using libmv::PolynomialCameraIntrinsics;
using libmv::NukeCameraIntrinsics;
libmv_CameraIntrinsics *libmv_cameraIntrinsicsNew(
const libmv_CameraIntrinsicsOptions* libmv_camera_intrinsics_options) {
@ -55,6 +56,14 @@ libmv_CameraIntrinsics *libmv_cameraIntrinsicsCopy(
*division_intrinsics);
break;
}
case libmv::DISTORTION_MODEL_NUKE:
{
const NukeCameraIntrinsics *nuke_intrinsics =
static_cast<const NukeCameraIntrinsics*>(orig_intrinsics);
new_intrinsics = LIBMV_OBJECT_NEW(NukeCameraIntrinsics,
*nuke_intrinsics);
break;
}
default:
assert(!"Unknown distortion model");
}
@ -136,6 +145,25 @@ void libmv_cameraIntrinsicsUpdate(
break;
}
case LIBMV_DISTORTION_MODEL_NUKE:
{
assert(camera_intrinsics->GetDistortionModelType() ==
libmv::DISTORTION_MODEL_NUKE);
NukeCameraIntrinsics *nuke_intrinsics =
(NukeCameraIntrinsics *) camera_intrinsics;
double k1 = libmv_camera_intrinsics_options->nuke_k1;
double k2 = libmv_camera_intrinsics_options->nuke_k2;
if (nuke_intrinsics->k1() != k1 ||
nuke_intrinsics->k2() != k2) {
nuke_intrinsics->SetDistortion(k1, k2);
}
break;
}
default:
assert(!"Unknown distortion model");
}
@ -189,6 +217,17 @@ void libmv_cameraIntrinsicsExtractOptions(
break;
}
case libmv::DISTORTION_MODEL_NUKE:
{
const NukeCameraIntrinsics *nuke_intrinsics =
static_cast<const NukeCameraIntrinsics *>(camera_intrinsics);
camera_intrinsics_options->distortion_model =
LIBMV_DISTORTION_MODEL_NUKE;
camera_intrinsics_options->nuke_k1 = nuke_intrinsics->k1();
camera_intrinsics_options->nuke_k2 = nuke_intrinsics->k2();
break;
}
default:
assert(!"Unknown distortion model");
}
@ -316,6 +355,17 @@ static void libmv_cameraIntrinsicsFillFromOptions(
break;
}
case LIBMV_DISTORTION_MODEL_NUKE:
{
NukeCameraIntrinsics *nuke_intrinsics =
static_cast<NukeCameraIntrinsics*>(camera_intrinsics);
nuke_intrinsics->SetDistortion(
camera_intrinsics_options->nuke_k1,
camera_intrinsics_options->nuke_k2);
break;
}
default:
assert(!"Unknown distortion model");
}
@ -331,6 +381,9 @@ CameraIntrinsics* libmv_cameraIntrinsicsCreateFromOptions(
case LIBMV_DISTORTION_MODEL_DIVISION:
camera_intrinsics = LIBMV_OBJECT_NEW(DivisionCameraIntrinsics);
break;
case LIBMV_DISTORTION_MODEL_NUKE:
camera_intrinsics = LIBMV_OBJECT_NEW(NukeCameraIntrinsics);
break;
default:
assert(!"Unknown distortion model");
}

View File

@ -29,6 +29,7 @@ typedef struct libmv_CameraIntrinsics libmv_CameraIntrinsics;
enum {
LIBMV_DISTORTION_MODEL_POLYNOMIAL = 0,
LIBMV_DISTORTION_MODEL_DIVISION = 1,
LIBMV_DISTORTION_MODEL_NUKE = 2,
};
typedef struct libmv_CameraIntrinsicsOptions {
@ -45,6 +46,9 @@ typedef struct libmv_CameraIntrinsicsOptions {
// Division distortion model.
double division_k1, division_k2;
// Nuke distortion model.
double nuke_k1, nuke_k2;
} libmv_CameraIntrinsicsOptions;
libmv_CameraIntrinsics *libmv_cameraIntrinsicsNew(

View File

@ -66,6 +66,12 @@ enum {
namespace {
bool NeedUseInvertIntrinsicsPipeline(const CameraIntrinsics *intrinsics) {
const DistortionModelType distortion_model =
intrinsics->GetDistortionModelType();
return (distortion_model == DISTORTION_MODEL_NUKE);
}
// Apply distortion model (distort the input) on the input point in the
// normalized space to get distorted coordinate in the image space.
//
@ -89,8 +95,6 @@ void ApplyDistortionModelUsingIntrinsicsBlock(
const T& principal_point_x = intrinsics_block[OFFSET_PRINCIPAL_POINT_X];
const T& principal_point_y = intrinsics_block[OFFSET_PRINCIPAL_POINT_Y];
// Apply distortion to the normalized points to get (xd, yd).
//
// TODO(keir): Do early bailouts for zero distortion; these are expensive
// jet operations.
switch (invariant_intrinsics->GetDistortionModelType()) {
@ -127,11 +131,82 @@ void ApplyDistortionModelUsingIntrinsicsBlock(
distorted_x, distorted_y);
return;
}
case DISTORTION_MODEL_NUKE:
{
LOG(FATAL) << "Unsupported distortion model.";
return;
}
}
LOG(FATAL) << "Unknown distortion model.";
}
// Invert distortion model (undistort the input) on the input point in the
// image space to get undistorted coordinate in the normalized space.
//
// Using intrinsics values from the parameter block, which makes this function
// suitable for use from a cost functor.
//
// Only use for distortion models which are analytically defined for their
// Invert() function.
//
// The invariant_intrinsics are used to access intrinsics which are never
// packed into parameter block: for example, distortion model type and image
// dimension.
template<typename T>
void InvertDistortionModelUsingIntrinsicsBlock(
const CameraIntrinsics *invariant_intrinsics,
const T* const intrinsics_block,
const T& image_x, const T& image_y,
T* normalized_x, T* normalized_y) {
// Unpack the intrinsics.
const T& focal_length = intrinsics_block[OFFSET_FOCAL_LENGTH];
const T& principal_point_x = intrinsics_block[OFFSET_PRINCIPAL_POINT_X];
const T& principal_point_y = intrinsics_block[OFFSET_PRINCIPAL_POINT_Y];
// TODO(keir): Do early bailouts for zero distortion; these are expensive
// jet operations.
switch (invariant_intrinsics->GetDistortionModelType()) {
case DISTORTION_MODEL_POLYNOMIAL:
case DISTORTION_MODEL_DIVISION:
LOG(FATAL) << "Unsupported distortion model.";
return;
case DISTORTION_MODEL_NUKE:
{
const T& k1 = intrinsics_block[OFFSET_K1];
const T& k2 = intrinsics_block[OFFSET_K2];
InvertNukeDistortionModel(focal_length,
focal_length,
principal_point_x,
principal_point_y,
invariant_intrinsics->image_width(),
invariant_intrinsics->image_height(),
k1, k2,
image_x, image_y,
normalized_x, normalized_y);
return;
}
}
LOG(FATAL) << "Unknown distortion model.";
}
template<typename T>
void NormalizedToImageSpace(const T* const intrinsics_block,
const T& normalized_x, const T& normalized_y,
T* image_x, T* image_y) {
// Unpack the intrinsics.
const T& focal_length = intrinsics_block[OFFSET_FOCAL_LENGTH];
const T& principal_point_x = intrinsics_block[OFFSET_PRINCIPAL_POINT_X];
const T& principal_point_y = intrinsics_block[OFFSET_PRINCIPAL_POINT_Y];
*image_x = normalized_x * focal_length + principal_point_x;
*image_y = normalized_y * focal_length + principal_point_y;
}
// Cost functor which computes reprojection error of 3D point X on camera
// defined by angle-axis rotation and it's translation (which are in the same
// block due to optimization reasons).
@ -191,6 +266,81 @@ struct ReprojectionErrorApplyIntrinsics {
const double weight_;
};
// Cost functor which computes reprojection error of 3D point X on camera
// defined by angle-axis rotation and it's translation (which are in the same
// block due to optimization reasons).
//
// This functor can only be used for distortion models which have analytically
// defined Invert() function.
struct ReprojectionErrorInvertIntrinsics {
ReprojectionErrorInvertIntrinsics(
const CameraIntrinsics *invariant_intrinsics,
const double observed_distorted_x,
const double observed_distorted_y,
const double weight)
: invariant_intrinsics_(invariant_intrinsics),
observed_distorted_x_(observed_distorted_x),
observed_distorted_y_(observed_distorted_y),
weight_(weight) {}
template <typename T>
bool operator()(const T* const intrinsics,
const T* const R_t, // Rotation denoted by angle axis
// followed with translation
const T* const X, // Point coordinates 3x1.
T* residuals) const {
// Unpack the intrinsics.
const T& focal_length = intrinsics[OFFSET_FOCAL_LENGTH];
const T& principal_point_x = intrinsics[OFFSET_PRINCIPAL_POINT_X];
const T& principal_point_y = intrinsics[OFFSET_PRINCIPAL_POINT_Y];
// Compute projective coordinates: x = RX + t.
T x[3];
ceres::AngleAxisRotatePoint(R_t, X, x);
x[0] += R_t[3];
x[1] += R_t[4];
x[2] += R_t[5];
// Prevent points from going behind the camera.
if (x[2] < T(0)) {
return false;
}
// Compute normalized coordinates: x /= x[2].
T xn = x[0] / x[2];
T yn = x[1] / x[2];
// Compute image space coordinate from normalized.
T predicted_x = focal_length * xn + principal_point_x;
T predicted_y = focal_length * yn + principal_point_y;
T observed_undistorted_normalized_x, observed_undistorted_normalized_y;
InvertDistortionModelUsingIntrinsicsBlock(
invariant_intrinsics_,
intrinsics,
T(observed_distorted_x_), T(observed_distorted_y_),
&observed_undistorted_normalized_x, &observed_undistorted_normalized_y);
T observed_undistorted_image_x, observed_undistorted_image_y;
NormalizedToImageSpace(
intrinsics,
observed_undistorted_normalized_x, observed_undistorted_normalized_y,
&observed_undistorted_image_x, &observed_undistorted_image_y);
// The error is the difference between the predicted and observed position.
residuals[0] = (predicted_x - observed_undistorted_image_x) * weight_;
residuals[1] = (predicted_y - observed_undistorted_image_y) * weight_;
return true;
}
const CameraIntrinsics *invariant_intrinsics_;
const double observed_distorted_x_;
const double observed_distorted_y_;
const double weight_;
};
// Print a message to the log which camera intrinsics are gonna to be optimized.
void BundleIntrinsicsLogMessage(const int bundle_intrinsics) {
if (bundle_intrinsics == BUNDLE_NO_INTRINSICS) {
@ -421,14 +571,25 @@ void AddResidualBlockToProblem(const CameraIntrinsics *invariant_intrinsics,
double *camera_R_t,
EuclideanPoint *point,
ceres::Problem* problem) {
AddResidualBlockToProblemImpl<ReprojectionErrorApplyIntrinsics>(
invariant_intrinsics,
marker.x, marker.y,
marker_weight,
intrinsics_block,
camera_R_t,
point,
problem);
if (NeedUseInvertIntrinsicsPipeline(invariant_intrinsics)) {
AddResidualBlockToProblemImpl<ReprojectionErrorInvertIntrinsics>(
invariant_intrinsics,
marker.x, marker.y,
marker_weight,
intrinsics_block,
camera_R_t,
point,
problem);
} else {
AddResidualBlockToProblemImpl<ReprojectionErrorApplyIntrinsics>(
invariant_intrinsics,
marker.x, marker.y,
marker_weight,
intrinsics_block,
camera_R_t,
point,
problem);
}
}
// This is an utility function to only bundle 3D position of

View File

@ -131,6 +131,8 @@ void CameraIntrinsics::ResetLookupGrids() {
undistort_.Reset();
}
// Polynomial model.
PolynomialCameraIntrinsics::PolynomialCameraIntrinsics()
: CameraIntrinsics() {
SetRadialDistortion(0.0, 0.0, 0.0);
@ -193,6 +195,8 @@ void PolynomialCameraIntrinsics::InvertIntrinsics(
normalized_y);
}
// Division model.
DivisionCameraIntrinsics::DivisionCameraIntrinsics()
: CameraIntrinsics() {
SetDistortion(0.0, 0.0);
@ -241,6 +245,57 @@ void DivisionCameraIntrinsics::InvertIntrinsics(double image_x,
normalized_y);
}
// Nuke model.
NukeCameraIntrinsics::NukeCameraIntrinsics()
: CameraIntrinsics() {
SetDistortion(0.0, 0.0);
}
NukeCameraIntrinsics::NukeCameraIntrinsics(
const NukeCameraIntrinsics &from)
: CameraIntrinsics(from) {
SetDistortion(from.k1(), from.k1());
}
void NukeCameraIntrinsics::SetDistortion(double k1, double k2) {
parameters_[OFFSET_K1] = k1;
parameters_[OFFSET_K2] = k2;
ResetLookupGrids();
}
void NukeCameraIntrinsics::ApplyIntrinsics(double normalized_x,
double normalized_y,
double *image_x,
double *image_y) const {
ApplyNukeDistortionModel(focal_length_x(),
focal_length_y(),
principal_point_x(),
principal_point_y(),
image_width(), image_height(),
k1(), k2(),
normalized_x,
normalized_y,
image_x,
image_y);
}
void NukeCameraIntrinsics::InvertIntrinsics(double image_x,
double image_y,
double *normalized_x,
double *normalized_y) const {
InvertNukeDistortionModel(focal_length_x(),
focal_length_y(),
principal_point_x(),
principal_point_y(),
image_width(), image_height(),
k1(), k2(),
image_x,
image_y,
normalized_x,
normalized_y);
}
std::ostream& operator <<(std::ostream &os,
const CameraIntrinsics &intrinsics) {
if (intrinsics.focal_length_x() == intrinsics.focal_length_x()) {
@ -281,6 +336,14 @@ std::ostream& operator <<(std::ostream &os,
PRINT_NONZERO_COEFFICIENT(division_intrinsics, k2);
break;
}
case DISTORTION_MODEL_NUKE:
{
const NukeCameraIntrinsics *nuke_intrinsics =
static_cast<const NukeCameraIntrinsics *>(&intrinsics);
PRINT_NONZERO_COEFFICIENT(nuke_intrinsics, k1);
PRINT_NONZERO_COEFFICIENT(nuke_intrinsics, k2);
break;
}
default:
LOG(FATAL) << "Unknown distortion model.";
}

View File

@ -276,7 +276,7 @@ class CameraIntrinsics {
class PolynomialCameraIntrinsics : public CameraIntrinsics {
public:
// This constants defines an offset of corresponding coefficients
// in the arameters_ array.
// in the parameters_ array.
enum {
OFFSET_K1,
OFFSET_K2,
@ -342,7 +342,7 @@ class PolynomialCameraIntrinsics : public CameraIntrinsics {
class DivisionCameraIntrinsics : public CameraIntrinsics {
public:
// This constants defines an offset of corresponding coefficients
// in the arameters_ array.
// in the parameters_ array.
enum {
OFFSET_K1,
OFFSET_K2,
@ -393,6 +393,60 @@ class DivisionCameraIntrinsics : public CameraIntrinsics {
double parameters_[NUM_PARAMETERS];
};
class NukeCameraIntrinsics : public CameraIntrinsics {
public:
// This constants defines an offset of corresponding coefficients
// in the parameters_ array.
enum {
OFFSET_K1,
OFFSET_K2,
// This defines the size of array which we need to have in order
// to store all the coefficients.
NUM_PARAMETERS,
};
NukeCameraIntrinsics();
NukeCameraIntrinsics(const NukeCameraIntrinsics &from);
DistortionModelType GetDistortionModelType() const {
return DISTORTION_MODEL_NUKE;
}
int num_distortion_parameters() const { return NUM_PARAMETERS; }
double *distortion_parameters() { return parameters_; };
const double *distortion_parameters() const { return parameters_; };
double k1() const { return parameters_[OFFSET_K1]; }
double k2() const { return parameters_[OFFSET_K2]; }
// Set radial distortion coeffcients.
void SetDistortion(double k1, double k2);
// Apply camera intrinsics to the normalized point to get image coordinates.
//
// This applies the lens distortion to a point which is in normalized
// camera coordinates (i.e. the principal point is at (0, 0)) to get image
// coordinates in pixels.
void ApplyIntrinsics(double normalized_x,
double normalized_y,
double *image_x,
double *image_y) const;
// Invert camera intrinsics on the image point to get normalized coordinates.
//
// This reverses the effect of lens distortion on a point which is in image
// coordinates to get normalized camera coordinates.
void InvertIntrinsics(double image_x,
double image_y,
double *normalized_x,
double *normalized_y) const;
private:
// Double-parameter division distortion model.
double parameters_[NUM_PARAMETERS];
};
/// A human-readable representation of the camera intrinsic parameters.
std::ostream& operator <<(std::ostream &os,
const CameraIntrinsics &intrinsics);

View File

@ -194,4 +194,96 @@ void InvertDivisionDistortionModel(const double focal_length_x,
*normalized_y = normalized(1);
}
struct ApplyNukeIntrinsicsCostFunction {
public:
typedef Vec2 FMatrixType;
typedef Vec2 XMatrixType;
ApplyNukeIntrinsicsCostFunction(const double focal_length_x,
const double focal_length_y,
const double principal_point_x,
const double principal_point_y,
const int image_width,
const int image_height,
const double k1,
const double k2,
const double expected_normalized_x,
const double expected_normalized_y)
: focal_length_x_(focal_length_x),
focal_length_y_(focal_length_y),
principal_point_x_(principal_point_x),
principal_point_y_(principal_point_y),
image_width_(image_width),
image_height_(image_height),
k1_(k1), k2_(k2),
expected_normalized_x_(expected_normalized_x),
expected_normalized_y_(expected_normalized_y) {}
Vec2 operator()(const Vec2 &image_coordinate) const {
double actual_normalized_x, actual_normalized_y;
InvertNukeDistortionModel(focal_length_x_,
focal_length_y_,
principal_point_x_,
principal_point_y_,
image_width_, image_height_,
k1_, k2_,
image_coordinate(0), image_coordinate(1),
&actual_normalized_x, &actual_normalized_y);
Vec2 fx;
fx << (actual_normalized_x - expected_normalized_x_),
(actual_normalized_y - expected_normalized_y_);
return fx;
}
double focal_length_x_;
double focal_length_y_;
double principal_point_x_;
double principal_point_y_;
int image_width_;
int image_height_;
double k1_, k2_;
double expected_normalized_x_, expected_normalized_y_;
};
void ApplyNukeDistortionModel(const double focal_length_x,
const double focal_length_y,
const double principal_point_x,
const double principal_point_y,
const int image_width,
const int image_height,
const double k1,
const double k2,
const double normalized_x,
const double normalized_y,
double *image_x,
double *image_y) {
// Compute the initial guess. For a camera with no distortion, this will also
// be the final answer; the LM iteration will terminate immediately.
Vec2 image;
image(0) = normalized_x * focal_length_x + principal_point_x;
image(1) = normalized_y * focal_length_y + principal_point_y;
// TODO(sergey): Use Ceres minimizer instead.
typedef LevenbergMarquardt<ApplyNukeIntrinsicsCostFunction> Solver;
ApplyNukeIntrinsicsCostFunction intrinsics_cost(focal_length_x,
focal_length_y,
principal_point_x,
principal_point_y,
image_width,
image_height,
k1, k2,
normalized_x, normalized_y);
Solver::SolverParameters params;
Solver solver(intrinsics_cost);
/*Solver::Results results =*/ solver.minimize(params, &image);
// TODO(keir): Better error handling.
*image_x = image(0);
*image_y = image(1);
}
} // namespace libmv

View File

@ -21,11 +21,14 @@
#ifndef LIBMV_SIMPLE_PIPELINE_DISTORTION_MODELS_H_
#define LIBMV_SIMPLE_PIPELINE_DISTORTION_MODELS_H_
#include <algorithm>
namespace libmv {
enum DistortionModelType {
DISTORTION_MODEL_POLYNOMIAL,
DISTORTION_MODEL_DIVISION
DISTORTION_MODEL_DIVISION,
DISTORTION_MODEL_NUKE,
};
// Invert camera intrinsics on the image point to get normalized coordinates.
@ -126,6 +129,79 @@ inline void ApplyDivisionDistortionModel(const T &focal_length_x,
*image_y = focal_length_y * yd + principal_point_y;
}
// Invert camera intrinsics on the image point to get normalized coordinates.
// This inverts the radial lens distortion to a point which is in image pixel
// coordinates to get normalized coordinates.
//
// Uses Nuke distortion model.
template <typename T>
void InvertNukeDistortionModel(const T &focal_length_x,
const T &focal_length_y,
const T &principal_point_x,
const T &principal_point_y,
const int image_width,
const int image_height,
const T &k1,
const T &k2,
const T &image_x,
const T &image_y,
T *normalized_x,
T *normalized_y) {
// According to the documentation:
//
// xu = xd / (1 + k0 * rd^2 + k1 * rd^4)
// yu = yd / (1 + k0 * rd^2 + k1 * rd^4)
//
// Legend:
// (xd, yd) are the distorted cartesian coordinates,
// (rd, phid) are the distorted polar coordinates,
// (xu, yu) are the undistorted cartesian coordinates,
// (ru, phiu) are the undistorted polar coordinates,
// the k-values are the distortion coefficients.
//
// The coordinate systems are relative to the distortion centre.
const int max_image_size = std::max(image_width, image_height);
const double max_half_image_size = max_image_size * 0.5;
if (max_half_image_size == 0.0) {
*normalized_x = image_x * max_half_image_size / focal_length_x;
*normalized_y = image_y * max_half_image_size / focal_length_y;
return;
}
const T xd = (image_x - principal_point_x) / max_half_image_size;
const T yd = (image_y - principal_point_y) / max_half_image_size;
T rd2 = xd*xd + yd*yd;
T rd4 = rd2 * rd2;
T r_coeff = T(1) / (T(1) + k1*rd2 + k2*rd4);
T xu = xd * r_coeff;
T yu = yd * r_coeff;
*normalized_x = xu * max_half_image_size / focal_length_x;
*normalized_y = yu * max_half_image_size / focal_length_y;
}
// Apply camera intrinsics to the normalized point to get image coordinates.
// This applies the radial lens distortion to a point which is in normalized
// camera coordinates (i.e. the principal point is at (0, 0)) to get image
// coordinates in pixels. Templated for use with autodifferentiation.
//
// Uses Nuke distortion model.
void ApplyNukeDistortionModel(const double focal_length_x,
const double focal_length_y,
const double principal_point_x,
const double principal_point_y,
const int image_width,
const int image_height,
const double k1,
const double k2,
const double normalized_x,
const double normalized_y,
double *image_x,
double *image_y);
} // namespace libmv
#endif // LIBMV_SIMPLE_PIPELINE_DISTORTION_MODELS_H_

View File

@ -918,6 +918,10 @@ class CLIP_PT_tracking_lens(Panel):
col = layout.column(align=True)
col.prop(camera, "division_k1")
col.prop(camera, "division_k2")
elif camera.distortion_model == 'NUKE':
col = layout.column(align=True)
col.prop(camera, "nuke_k1")
col.prop(camera, "nuke_k2")
class CLIP_PT_marker(CLIP_PT_tracking_panel, Panel):

View File

@ -460,6 +460,7 @@ typedef struct MovieClipCache {
float principal[2];
float polynomial_k[3];
float division_k[2];
float nuke_k[2];
short distortion_model;
bool undistortion_used;
@ -908,6 +909,9 @@ static bool check_undistortion_cache_flags(const MovieClip *clip)
if (!equals_v2v2(&camera->division_k1, cache->postprocessed.division_k)) {
return false;
}
if (!equals_v2v2(&camera->nuke_k1, cache->postprocessed.nuke_k)) {
return false;
}
return true;
}
@ -1010,6 +1014,7 @@ static void put_postprocessed_frame_to_cache(
copy_v2_v2(cache->postprocessed.principal, camera->principal);
copy_v3_v3(cache->postprocessed.polynomial_k, &camera->k1);
copy_v2_v2(cache->postprocessed.division_k, &camera->division_k1);
copy_v2_v2(cache->postprocessed.nuke_k, &camera->nuke_k1);
cache->postprocessed.undistortion_used = true;
}
else {

View File

@ -451,6 +451,12 @@ static void distortion_model_parameters_from_tracking(
camera_intrinsics_options->division_k1 = camera->division_k1;
camera_intrinsics_options->division_k2 = camera->division_k2;
return;
case TRACKING_DISTORTION_MODEL_NUKE:
camera_intrinsics_options->distortion_model = LIBMV_DISTORTION_MODEL_NUKE;
camera_intrinsics_options->nuke_k1 = camera->nuke_k1;
camera_intrinsics_options->nuke_k2 = camera->nuke_k2;
return;
}
/* Unknown distortion model, which might be due to opening newer file in older Blender.
@ -479,6 +485,12 @@ static void distortion_model_parameters_from_options(
camera->division_k1 = camera_intrinsics_options->division_k1;
camera->division_k2 = camera_intrinsics_options->division_k2;
return;
case LIBMV_DISTORTION_MODEL_NUKE:
camera->distortion_model = TRACKING_DISTORTION_MODEL_NUKE;
camera->nuke_k1 = camera_intrinsics_options->nuke_k1;
camera->nuke_k2 = camera_intrinsics_options->nuke_k2;
return;
}
/* Libmv returned distortion model which is not known to Blender. This is a logical error in code

View File

@ -70,6 +70,9 @@ typedef struct MovieTrackingCamera {
/* Division distortion model coefficients */
float division_k1, division_k2;
/* Nuke distortion model coefficients */
float nuke_k1, nuke_k2;
} MovieTrackingCamera;
typedef struct MovieTrackingMarker {
@ -455,6 +458,7 @@ typedef struct MovieTracking {
enum {
TRACKING_DISTORTION_MODEL_POLYNOMIAL = 0,
TRACKING_DISTORTION_MODEL_DIVISION = 1,
TRACKING_DISTORTION_MODEL_NUKE = 2,
};
/* MovieTrackingCamera->units */

View File

@ -1149,6 +1149,7 @@ static void rna_def_trackingCamera(BlenderRNA *brna)
"Divisions",
"Division distortion model which "
"better represents wide-angle cameras"},
{TRACKING_DISTORTION_MODEL_NUKE, "NUKE", 0, "Nuke", "Nuke distortion model"},
{0, NULL, 0, NULL, NULL},
};
@ -1252,6 +1253,19 @@ static void rna_def_trackingCamera(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "K2", "First coefficient of second order division distortion");
RNA_def_property_update(prop, NC_MOVIECLIP | NA_EDITED, "rna_tracking_flushUpdate");
/* Nuke distortion parameters */
prop = RNA_def_property(srna, "nuke_k1", PROP_FLOAT, PROP_NONE);
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_ui_range(prop, -10, 10, 0.1, 3);
RNA_def_property_ui_text(prop, "K1", "First coefficient of second order Nuke distortion");
RNA_def_property_update(prop, NC_MOVIECLIP | NA_EDITED, "rna_tracking_flushUpdate");
prop = RNA_def_property(srna, "nuke_k2", PROP_FLOAT, PROP_NONE);
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_ui_range(prop, -10, 10, 0.1, 3);
RNA_def_property_ui_text(prop, "K2", "Second coefficient of second order Nuke distortion");
RNA_def_property_update(prop, NC_MOVIECLIP | NA_EDITED, "rna_tracking_flushUpdate");
/* pixel aspect */
prop = RNA_def_property(srna, "pixel_aspect", PROP_FLOAT, PROP_XYZ);
RNA_def_property_float_sdna(prop, NULL, "pixel_aspect");