mirror of
https://github.com/blender/blender
synced 2026-09-29 04:37:17 +03:00
Motion Tracking: Add anamorphic coefficients to Nuke lens distortion model
This patch extends the Nuke distortion model to support anamorphic lenses by introducing new anamorphic coefficients. - When the new coefficients are set to zero, the model behaves exactly as before. - The "Tangential Distortion" refine option is used to solve for these new parameters. - A test file is included, tracked from real anamorphic footage (footage itself cannot be shared unfortunately, I included a redistorted rendering of the markers, so that the file can be opened). - The test should show that other distortion models have higher error and fail to properly straighten curved lines. Pull Request: https://projects.blender.org/blender/blender/pulls/149035
This commit is contained in:
parent
2d43ab553c
commit
11e8f332b5
13 changed files with 107 additions and 22 deletions
|
|
@ -138,9 +138,15 @@ void libmv_cameraIntrinsicsUpdate(
|
|||
double k2 = libmv_camera_intrinsics_options->nuke_k2;
|
||||
|
||||
if (nuke_intrinsics->k1() != k1 || nuke_intrinsics->k2() != k2) {
|
||||
nuke_intrinsics->SetDistortion(k1, k2);
|
||||
nuke_intrinsics->SetRadialDistortion(k1, k2);
|
||||
}
|
||||
|
||||
double p1 = libmv_camera_intrinsics_options->nuke_p1;
|
||||
double p2 = libmv_camera_intrinsics_options->nuke_p2;
|
||||
|
||||
if (nuke_intrinsics->p1() != p1 || nuke_intrinsics->p2() != p2) {
|
||||
nuke_intrinsics->SetTangentialDistortion(p1, p2);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -220,6 +226,8 @@ void libmv_cameraIntrinsicsExtractOptions(
|
|||
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();
|
||||
camera_intrinsics_options->nuke_p1 = nuke_intrinsics->p1();
|
||||
camera_intrinsics_options->nuke_p2 = nuke_intrinsics->p2();
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -353,8 +361,11 @@ static void libmv_cameraIntrinsicsFillFromOptions(
|
|||
NukeCameraIntrinsics* nuke_intrinsics =
|
||||
static_cast<NukeCameraIntrinsics*>(camera_intrinsics);
|
||||
|
||||
nuke_intrinsics->SetDistortion(camera_intrinsics_options->nuke_k1,
|
||||
camera_intrinsics_options->nuke_k2);
|
||||
nuke_intrinsics->SetRadialDistortion(camera_intrinsics_options->nuke_k1,
|
||||
camera_intrinsics_options->nuke_k2);
|
||||
nuke_intrinsics->SetTangentialDistortion(
|
||||
camera_intrinsics_options->nuke_p1,
|
||||
camera_intrinsics_options->nuke_p2);
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ typedef struct libmv_CameraIntrinsicsOptions {
|
|||
|
||||
// Nuke distortion model.
|
||||
double nuke_k1, nuke_k2;
|
||||
double nuke_p1, nuke_p2;
|
||||
|
||||
// Brown-Conrady distortion model.
|
||||
double brown_k1, brown_k2, brown_k3, brown_k4;
|
||||
|
|
|
|||
|
|
@ -192,6 +192,8 @@ void InvertDistortionModelUsingIntrinsicsBlock(
|
|||
case DISTORTION_MODEL_NUKE: {
|
||||
const T& k1 = intrinsics_block[PackedIntrinsics::OFFSET_K1];
|
||||
const T& k2 = intrinsics_block[PackedIntrinsics::OFFSET_K2];
|
||||
const T& p1 = intrinsics_block[PackedIntrinsics::OFFSET_P1];
|
||||
const T& p2 = intrinsics_block[PackedIntrinsics::OFFSET_P2];
|
||||
|
||||
InvertNukeDistortionModel(focal_length,
|
||||
focal_length,
|
||||
|
|
@ -201,6 +203,8 @@ void InvertDistortionModelUsingIntrinsicsBlock(
|
|||
invariant_intrinsics->image_height(),
|
||||
k1,
|
||||
k2,
|
||||
p1,
|
||||
p2,
|
||||
image_x,
|
||||
image_y,
|
||||
normalized_x,
|
||||
|
|
|
|||
|
|
@ -286,20 +286,28 @@ void DivisionCameraIntrinsics::Unpack(
|
|||
// Nuke model.
|
||||
|
||||
NukeCameraIntrinsics::NukeCameraIntrinsics() : CameraIntrinsics() {
|
||||
SetDistortion(0.0, 0.0);
|
||||
SetRadialDistortion(0.0, 0.0);
|
||||
SetTangentialDistortion(0.0, 0.0);
|
||||
}
|
||||
|
||||
NukeCameraIntrinsics::NukeCameraIntrinsics(const NukeCameraIntrinsics& from)
|
||||
: CameraIntrinsics(from) {
|
||||
SetDistortion(from.k1(), from.k2());
|
||||
SetRadialDistortion(from.k1(), from.k2());
|
||||
SetTangentialDistortion(from.p1(), from.p2());
|
||||
}
|
||||
|
||||
void NukeCameraIntrinsics::SetDistortion(double k1, double k2) {
|
||||
void NukeCameraIntrinsics::SetRadialDistortion(double k1, double k2) {
|
||||
parameters_[OFFSET_K1] = k1;
|
||||
parameters_[OFFSET_K2] = k2;
|
||||
ResetLookupGrids();
|
||||
}
|
||||
|
||||
void NukeCameraIntrinsics::SetTangentialDistortion(double p1, double p2) {
|
||||
parameters_[OFFSET_P1] = p1;
|
||||
parameters_[OFFSET_P2] = p2;
|
||||
ResetLookupGrids();
|
||||
}
|
||||
|
||||
void NukeCameraIntrinsics::ApplyIntrinsics(double normalized_x,
|
||||
double normalized_y,
|
||||
double* image_x,
|
||||
|
|
@ -312,6 +320,8 @@ void NukeCameraIntrinsics::ApplyIntrinsics(double normalized_x,
|
|||
image_height(),
|
||||
k1(),
|
||||
k2(),
|
||||
p1(),
|
||||
p2(),
|
||||
normalized_x,
|
||||
normalized_y,
|
||||
image_x,
|
||||
|
|
@ -330,6 +340,8 @@ void NukeCameraIntrinsics::InvertIntrinsics(double image_x,
|
|||
image_height(),
|
||||
k1(),
|
||||
k2(),
|
||||
p1(),
|
||||
p2(),
|
||||
image_x,
|
||||
image_y,
|
||||
normalized_x,
|
||||
|
|
@ -341,12 +353,16 @@ void NukeCameraIntrinsics::Pack(PackedIntrinsics* packed_intrinsics) const {
|
|||
|
||||
packed_intrinsics->SetK1(k1());
|
||||
packed_intrinsics->SetK2(k2());
|
||||
|
||||
packed_intrinsics->SetP1(p1());
|
||||
packed_intrinsics->SetP2(p2());
|
||||
}
|
||||
|
||||
void NukeCameraIntrinsics::Unpack(const PackedIntrinsics& packed_intrinsics) {
|
||||
CameraIntrinsics::Unpack(packed_intrinsics);
|
||||
|
||||
SetDistortion(packed_intrinsics.GetK1(), packed_intrinsics.GetK2());
|
||||
SetRadialDistortion(packed_intrinsics.GetK1(), packed_intrinsics.GetK2());
|
||||
SetTangentialDistortion(packed_intrinsics.GetP1(), packed_intrinsics.GetP2());
|
||||
}
|
||||
|
||||
// Brown model.
|
||||
|
|
@ -484,6 +500,8 @@ std::ostream& operator<<(std::ostream& os, const CameraIntrinsics& intrinsics) {
|
|||
static_cast<const NukeCameraIntrinsics*>(&intrinsics);
|
||||
PRINT_NONZERO_COEFFICIENT(nuke_intrinsics, k1);
|
||||
PRINT_NONZERO_COEFFICIENT(nuke_intrinsics, k2);
|
||||
PRINT_NONZERO_COEFFICIENT(nuke_intrinsics, p1);
|
||||
PRINT_NONZERO_COEFFICIENT(nuke_intrinsics, p2);
|
||||
break;
|
||||
}
|
||||
case DISTORTION_MODEL_BROWN: {
|
||||
|
|
|
|||
|
|
@ -389,6 +389,8 @@ class NukeCameraIntrinsics : public CameraIntrinsics {
|
|||
enum {
|
||||
OFFSET_K1,
|
||||
OFFSET_K2,
|
||||
OFFSET_P1,
|
||||
OFFSET_P2,
|
||||
|
||||
// This defines the size of array which we need to have in order
|
||||
// to store all the coefficients.
|
||||
|
|
@ -404,9 +406,14 @@ class NukeCameraIntrinsics : public CameraIntrinsics {
|
|||
|
||||
double k1() const { return parameters_[OFFSET_K1]; }
|
||||
double k2() const { return parameters_[OFFSET_K2]; }
|
||||
double p1() const { return parameters_[OFFSET_P1]; }
|
||||
double p2() const { return parameters_[OFFSET_P2]; }
|
||||
|
||||
// Set radial distortion coeffcients.
|
||||
void SetDistortion(double k1, double k2);
|
||||
void SetRadialDistortion(double k1, double k2);
|
||||
|
||||
// Set tangential distortion coeffcients.
|
||||
void SetTangentialDistortion(double p1, double p2);
|
||||
|
||||
// Apply camera intrinsics to the normalized point to get image coordinates.
|
||||
//
|
||||
|
|
|
|||
|
|
@ -333,6 +333,8 @@ struct ApplyNukeIntrinsicsCostFunction {
|
|||
const int image_height,
|
||||
const double k1,
|
||||
const double k2,
|
||||
const double p1,
|
||||
const double p2,
|
||||
const double expected_normalized_x,
|
||||
const double expected_normalized_y)
|
||||
: focal_length_x_(focal_length_x),
|
||||
|
|
@ -343,6 +345,8 @@ struct ApplyNukeIntrinsicsCostFunction {
|
|||
image_height_(image_height),
|
||||
k1_(k1),
|
||||
k2_(k2),
|
||||
p1_(p1),
|
||||
p2_(p2),
|
||||
expected_normalized_x_(expected_normalized_x),
|
||||
expected_normalized_y_(expected_normalized_y) {}
|
||||
|
||||
|
|
@ -357,6 +361,8 @@ struct ApplyNukeIntrinsicsCostFunction {
|
|||
image_height_,
|
||||
k1_,
|
||||
k2_,
|
||||
p1_,
|
||||
p2_,
|
||||
image_coordinate(0),
|
||||
image_coordinate(1),
|
||||
&actual_normalized_x,
|
||||
|
|
@ -373,7 +379,7 @@ struct ApplyNukeIntrinsicsCostFunction {
|
|||
double principal_point_y_;
|
||||
int image_width_;
|
||||
int image_height_;
|
||||
double k1_, k2_;
|
||||
double k1_, k2_, p1_, p2_;
|
||||
double expected_normalized_x_, expected_normalized_y_;
|
||||
};
|
||||
|
||||
|
|
@ -385,6 +391,8 @@ void ApplyNukeDistortionModel(const double focal_length_x,
|
|||
const int image_height,
|
||||
const double k1,
|
||||
const double k2,
|
||||
const double p1,
|
||||
const double p2,
|
||||
const double normalized_x,
|
||||
const double normalized_y,
|
||||
double* image_x,
|
||||
|
|
@ -406,6 +414,8 @@ void ApplyNukeDistortionModel(const double focal_length_x,
|
|||
image_height,
|
||||
k1,
|
||||
k2,
|
||||
p1,
|
||||
p2,
|
||||
normalized_x,
|
||||
normalized_y);
|
||||
Solver::SolverParameters params;
|
||||
|
|
|
|||
|
|
@ -143,14 +143,16 @@ void InvertNukeDistortionModel(const T& focal_length_x,
|
|||
const int image_height,
|
||||
const T& k1,
|
||||
const T& k2,
|
||||
const T& p1,
|
||||
const T& p2,
|
||||
const T& image_x,
|
||||
const T& image_y,
|
||||
T* normalized_x,
|
||||
T* normalized_y) {
|
||||
// According to the documentation:
|
||||
// According to the documentation for the anamorphic model:
|
||||
//
|
||||
// xu = xd / (1 + k0 * rd^2 + k1 * rd^4)
|
||||
// yu = yd / (1 + k0 * rd^2 + k1 * rd^4)
|
||||
// xu = xd / (1 + k0 * rd^2 + k1 * rd^4 + k2 * yd^2)
|
||||
// yu = yd / (1 + k0 * rd^2 + k1 * rd^4 + k3 * xd^2)
|
||||
//
|
||||
// Legend:
|
||||
// (xd, yd) are the distorted cartesian coordinates,
|
||||
|
|
@ -160,6 +162,8 @@ void InvertNukeDistortionModel(const T& focal_length_x,
|
|||
// the k-values are the distortion coefficients.
|
||||
//
|
||||
// The coordinate systems are relative to the distortion centre.
|
||||
// Nukes k0, k1, k2, k3 correspond to Blenders k1, k2, p1, p2.
|
||||
// The spherical model is identical except p1 and p2 are both zero.
|
||||
|
||||
const int max_image_size = std::max(image_width, image_height);
|
||||
const double max_half_image_size = max_image_size * 0.5;
|
||||
|
|
@ -173,11 +177,12 @@ void InvertNukeDistortionModel(const T& focal_length_x,
|
|||
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 xd2 = xd * xd;
|
||||
T yd2 = yd * yd;
|
||||
T rd2 = xd2 + yd2;
|
||||
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;
|
||||
T xu = xd / (T(1) + k1 * rd2 + k2 * rd4 + p1 * yd2);
|
||||
T yu = yd / (T(1) + k1 * rd2 + k2 * rd4 + p2 * xd2);
|
||||
|
||||
*normalized_x = xu * max_half_image_size / focal_length_x;
|
||||
*normalized_y = yu * max_half_image_size / focal_length_y;
|
||||
|
|
@ -197,6 +202,8 @@ void ApplyNukeDistortionModel(const double focal_length_x,
|
|||
const int image_height,
|
||||
const double k1,
|
||||
const double k2,
|
||||
const double p1,
|
||||
const double p2,
|
||||
const double normalized_x,
|
||||
const double normalized_y,
|
||||
double* image_x,
|
||||
|
|
|
|||
|
|
@ -550,7 +550,7 @@ class CLIP_PT_tools_solve(CLIP_PT_tracking_panel, Panel):
|
|||
col.prop(settings, "refine_intrinsics_radial_distortion", text="Radial Distortion")
|
||||
|
||||
row = col.row()
|
||||
row.active = (camera.distortion_model == 'BROWN')
|
||||
row.active = (camera.distortion_model in ('BROWN', 'NUKE'))
|
||||
row.prop(settings, "refine_intrinsics_tangential_distortion", text="Tangential Distortion")
|
||||
|
||||
col = layout.column(align=True)
|
||||
|
|
@ -940,6 +940,9 @@ class CLIP_PT_tracking_lens(Panel):
|
|||
col = layout.column(align=True)
|
||||
col.prop(camera, "nuke_k1")
|
||||
col.prop(camera, "nuke_k2")
|
||||
col.separator()
|
||||
col.prop(camera, "nuke_p1")
|
||||
col.prop(camera, "nuke_p2")
|
||||
elif camera.distortion_model == 'BROWN':
|
||||
col = layout.column(align=True)
|
||||
col.prop(camera, "brown_k1")
|
||||
|
|
|
|||
|
|
@ -682,6 +682,7 @@ struct MovieClipCache {
|
|||
float polynomial_k[3];
|
||||
float division_k[2];
|
||||
float nuke_k[2];
|
||||
float nuke_p[2];
|
||||
float brown_k[4];
|
||||
float brown_p[2];
|
||||
short distortion_model;
|
||||
|
|
@ -1114,6 +1115,9 @@ static bool check_undistortion_cache_flags(const MovieClip *clip)
|
|||
if (!equals_v2v2(&camera->nuke_k1, cache->postprocessed.nuke_k)) {
|
||||
return false;
|
||||
}
|
||||
if (!equals_v2v2(&camera->nuke_p1, cache->postprocessed.nuke_p)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!equals_v4v4(&camera->brown_k1, cache->postprocessed.brown_k)) {
|
||||
return false;
|
||||
|
|
@ -1224,6 +1228,7 @@ static void put_postprocessed_frame_to_cache(
|
|||
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);
|
||||
copy_v2_v2(cache->postprocessed.nuke_p, &camera->nuke_p1);
|
||||
copy_v4_v4(cache->postprocessed.brown_k, &camera->brown_k1);
|
||||
copy_v2_v2(cache->postprocessed.brown_p, &camera->brown_p1);
|
||||
cache->postprocessed.undistortion_used = true;
|
||||
|
|
|
|||
|
|
@ -2214,7 +2214,8 @@ bool BKE_tracking_camera_distortion_equal(const MovieTrackingCamera *a,
|
|||
case TRACKING_DISTORTION_MODEL_DIVISION:
|
||||
return a->division_k1 == b->division_k1 && a->division_k2 == b->division_k2;
|
||||
case TRACKING_DISTORTION_MODEL_NUKE:
|
||||
return a->nuke_k1 == b->nuke_k1 && a->nuke_k2 == b->nuke_k2;
|
||||
return a->nuke_k1 == b->nuke_k1 && a->nuke_k2 == b->nuke_k2 && a->nuke_p1 == b->nuke_p1 &&
|
||||
a->nuke_p2 == b->nuke_p2;
|
||||
case TRACKING_DISTORTION_MODEL_BROWN:
|
||||
return a->brown_k1 == b->brown_k1 && a->brown_k2 == b->brown_k2 &&
|
||||
a->brown_k3 == b->brown_k3 && a->brown_k4 == b->brown_k4 &&
|
||||
|
|
@ -2240,10 +2241,11 @@ uint64_t BKE_tracking_camera_distortion_hash(const MovieTrackingCamera *camera)
|
|||
float2(camera->principal_point),
|
||||
float2(camera->division_k1, camera->division_k2));
|
||||
case TRACKING_DISTORTION_MODEL_NUKE:
|
||||
return get_default_hash(camera->distortion_model,
|
||||
float2(camera->pixel_aspect, camera->focal),
|
||||
float2(camera->principal_point),
|
||||
float2(camera->nuke_k1, camera->nuke_k2));
|
||||
return get_default_hash(
|
||||
camera->distortion_model,
|
||||
float2(camera->pixel_aspect, camera->focal),
|
||||
float2(camera->principal_point),
|
||||
float4(camera->nuke_k1, camera->nuke_k2, camera->nuke_p1, camera->nuke_p2));
|
||||
case TRACKING_DISTORTION_MODEL_BROWN:
|
||||
return get_default_hash(
|
||||
float2(camera->pixel_aspect, camera->focal),
|
||||
|
|
|
|||
|
|
@ -420,6 +420,8 @@ static void distortion_model_parameters_from_tracking(
|
|||
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;
|
||||
camera_intrinsics_options->nuke_p1 = camera->nuke_p1;
|
||||
camera_intrinsics_options->nuke_p2 = camera->nuke_p2;
|
||||
return;
|
||||
case TRACKING_DISTORTION_MODEL_BROWN:
|
||||
camera_intrinsics_options->distortion_model = LIBMV_DISTORTION_MODEL_BROWN;
|
||||
|
|
@ -463,6 +465,8 @@ static void distortion_model_parameters_from_options(
|
|||
camera->distortion_model = TRACKING_DISTORTION_MODEL_NUKE;
|
||||
camera->nuke_k1 = camera_intrinsics_options->nuke_k1;
|
||||
camera->nuke_k2 = camera_intrinsics_options->nuke_k2;
|
||||
camera->nuke_p1 = camera_intrinsics_options->nuke_p1;
|
||||
camera->nuke_p2 = camera_intrinsics_options->nuke_p2;
|
||||
return;
|
||||
case LIBMV_DISTORTION_MODEL_BROWN:
|
||||
camera->distortion_model = TRACKING_DISTORTION_MODEL_BROWN;
|
||||
|
|
|
|||
|
|
@ -68,6 +68,7 @@ typedef struct MovieTrackingCamera {
|
|||
|
||||
/* Nuke distortion model coefficients */
|
||||
float nuke_k1, nuke_k2;
|
||||
float nuke_p1, nuke_p2;
|
||||
|
||||
/* Brown-Conrady distortion model coefficients */
|
||||
/** Brown-Conrady radial distortion. */
|
||||
|
|
|
|||
|
|
@ -1293,6 +1293,18 @@ static void rna_def_trackingCamera(BlenderRNA *brna)
|
|||
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");
|
||||
|
||||
prop = RNA_def_property(srna, "nuke_p1", 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, "P1", "First coefficient of tangential Nuke distortion");
|
||||
RNA_def_property_update(prop, NC_MOVIECLIP | NA_EDITED, "rna_tracking_flushUpdate");
|
||||
|
||||
prop = RNA_def_property(srna, "nuke_p2", 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, "P2", "Second coefficient of tangential Nuke distortion");
|
||||
RNA_def_property_update(prop, NC_MOVIECLIP | NA_EDITED, "rna_tracking_flushUpdate");
|
||||
|
||||
/* Brown-Conrady distortion parameters */
|
||||
prop = RNA_def_property(srna, "brown_k1", PROP_FLOAT, PROP_NONE);
|
||||
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue