Skip to content

Commit f94342d

Browse files
Fabien Servantcbentejac
authored andcommitted
Add export of animated distortion in nuke
1 parent 396d82c commit f94342d

File tree

1 file changed

+306
-4
lines changed

1 file changed

+306
-4
lines changed

src/software/export/main_exportDistortion.cpp

Lines changed: 306 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,234 @@ namespace po = boost::program_options;
2929
using namespace aliceVision;
3030
using namespace aliceVision::camera;
3131

32+
/**
33+
* Build nuke animated distortion
34+
* @param content the output string
35+
* @param sfmData the input sfmData
36+
* @param sequence list of views with SAME sensor/image size/pixelaspectratio/undistortiontype
37+
* @return true if succeeded
38+
*/
39+
bool sequenceToNuke(std::string & content, const sfmData::SfMData & sfmData, const std::vector<sfmData::View::sptr> & sequence)
40+
{
41+
if (sequence.size() == 0)
42+
{
43+
return false;
44+
}
45+
46+
//Sort views by increasing frame ID
47+
std::vector<sfmData::View::sptr> sorted = sequence;
48+
std::sort(sorted.begin(), sorted.end(), [](const sfmData::View::sptr & v1, const sfmData::View::sptr & v2)
49+
{
50+
return v1->getFrameId() < v2->getFrameId();
51+
});
52+
53+
std::string vecFocal, vecOffsetx, vecOffsety;
54+
std::vector<std::string> vecParams;
55+
bool first = true;
56+
camera::EUNDISTORTION commonType;
57+
double commonSensorWidth;
58+
double commonSensorHeight;
59+
double commonPixelAspect;
60+
Vec2 commonSize;
61+
62+
//Build strings
63+
for (const auto & viewPtr: sorted)
64+
{
65+
IndexT idIntrinsic = viewPtr->getIntrinsicId();
66+
if (idIntrinsic == UndefinedIndexT)
67+
{
68+
continue;
69+
}
70+
71+
const auto intrinsic = sfmData.getIntrinsicSharedPtr(idIntrinsic);
72+
const auto intrinsicDisto = std::dynamic_pointer_cast<IntrinsicScaleOffsetDisto>(intrinsic);
73+
if (!intrinsicDisto)
74+
{
75+
continue;
76+
}
77+
78+
aliceVision::IndexT frameId = viewPtr->getFrameId();
79+
std::string sfid = "x" + std::to_string(frameId);
80+
81+
const auto undistortion = intrinsicDisto->getUndistortion();
82+
83+
84+
const double focal = intrinsicDisto->getFocalLength();
85+
const double sensorWidth = intrinsicDisto->sensorWidth();
86+
const double sensorHeight = intrinsicDisto->sensorHeight();
87+
const auto& size = undistortion->getSize();
88+
const double pa = undistortion->getPixelAspectRatio();
89+
const double updatedSensorHeight = (undistortion->isDesqueezed())?sensorHeight:sensorHeight/pa;
90+
const Vec2 offset = undistortion->getScaledOffset();
91+
92+
const std::vector<double>& params = undistortion->getParameters();
93+
94+
if (first)
95+
{
96+
commonType = undistortion->getType();
97+
commonSensorWidth = sensorWidth;
98+
commonSensorHeight = updatedSensorHeight;
99+
commonSize = size;
100+
commonPixelAspect = pa;
101+
102+
vecParams.resize(params.size());
103+
}
104+
else
105+
{
106+
bool same = (commonType == undistortion->getType());
107+
same &= (commonSensorWidth == sensorWidth);
108+
same &= (commonSensorHeight == updatedSensorHeight);
109+
same &= (commonSize == size);
110+
same &= (commonPixelAspect == pa);
111+
112+
if (!same)
113+
{
114+
ALICEVISION_LOG_ERROR("Unsupported change of image/sensor size in the sequence");
115+
return false;
116+
}
117+
}
118+
119+
120+
vecFocal += sfid + " " + std::to_string(focal) + " ";
121+
vecOffsetx += sfid + " " + std::to_string(offset.x()) + " ";
122+
vecOffsety += sfid + " " + std::to_string(-offset.y()) + " ";
123+
124+
for (int idParam = 0; idParam < params.size(); idParam++)
125+
{
126+
double param = params[idParam];
127+
128+
if (idParam == 10)
129+
{
130+
if (undistortion->getType() == EUNDISTORTION::UNDISTORTION_3DEANAMORPHIC4)
131+
{
132+
param = radianToDegree(params[10]);
133+
}
134+
}
135+
136+
vecParams[idParam] += sfid + " " + std::to_string(param) + " ";
137+
}
138+
139+
first = false;
140+
}
141+
142+
for (int idParam = 0; idParam < vecParams.size(); idParam++)
143+
{
144+
vecParams[idParam] = "{{curve " + vecParams[idParam] + "}}";
145+
}
146+
147+
vecFocal = "{{curve " + vecFocal + "}}";
148+
std::string vecOffset = "{{curve "+ vecOffsetx+ "} {curve " + vecOffsety + "}}";
149+
150+
std::stringstream ss;
151+
152+
switch (commonType)
153+
{
154+
case EUNDISTORTION::UNDISTORTION_RADIALK3:
155+
ss << "LensDistortion2 {" << "\n"
156+
<< "\n"
157+
<< " distortionModelPreset Custom" << "\n"
158+
<< " distortionModelPreset \"3DEqualizer/3DE4 Anamorphic - Standard, Degree 4\"\n"
159+
<< " distortionNumerator0 " << vecParams[0] << "\n"
160+
<< "\n"
161+
<< " distortionNumerator1 " << vecParams[1] << "\n"
162+
<< " lens Anamorphic\n"
163+
<< " distortionNumerator2 " << vecParams[2] << "\n"
164+
<< " centre " << vecOffset << "\n"
165+
<< " output Undistort" << "\n"
166+
<< " distortionScalingType Format" << "\n"
167+
<< " distortionScalingFormat \"" << commonSize(0) << " " << commonSize(1) << " 0 0 " << commonSize(0) << " " << commonSize(1) << " 1 AV_undist_fmt \""
168+
<< "\n"
169+
<< "\n"
170+
<< " distortionOrder {3 0}" << "\n"
171+
<< " normalisationType Diagonal" << "\n"
172+
<< " distortInFisheyeSpace false" << "\n"
173+
<< "}"
174+
<< "\n";
175+
break;
176+
case EUNDISTORTION::UNDISTORTION_3DEANAMORPHIC4:
177+
ss << "LD_3DE4_Anamorphic_Standard_Degree_4 {\n"
178+
<< "direction undistort \n"
179+
<< "tde4_focal_length_cm " << vecFocal << " \n"
180+
<< "tde4_custom_focus_distance_cm 1.0 \n"
181+
<< "tde4_filmback_width_cm " << commonSensorWidth << " \n"
182+
<< "tde4_filmback_height_cm " << commonSensorWidth << " \n"
183+
<< "tde4_lens_center_offset_x_cm 0.0000000 \n"
184+
<< "tde4_lens_center_offset_y_cm 0.0000000 \n"
185+
<< "tde4_pixel_aspect " << commonPixelAspect << " \n"
186+
<< "field_of_view_xa_unit 0.0000000 \n"
187+
<< "field_of_view_xb_unit 1.0000000 \n"
188+
<< "field_of_view_ya_unit 0.0000000 \n"
189+
<< "field_of_view_yb_unit 1.0000000 \n"
190+
<< "Cx02_Degree_2 " << vecParams[0] << " \n"
191+
<< "Cy02_Degree_2 " << vecParams[1] << " \n"
192+
<< "Cx22_Degree_2 " << vecParams[2] << " \n"
193+
<< "Cy22_Degree_2 " << vecParams[3] << " \n"
194+
<< "Cx04_Degree_4 " << vecParams[4] << " \n"
195+
<< "Cy04_Degree_4 " << vecParams[5] << " \n"
196+
<< "Cx24_Degree_4 " << vecParams[6] << " \n"
197+
<< "Cy24_Degree_4 " << vecParams[7] << " \n"
198+
<< "Cx44_Degree_4 " << vecParams[8] << " \n"
199+
<< "Cy44_Degree_4 " << vecParams[9] << " \n"
200+
<< "Lens_Rotation " << vecParams[10] << " \n"
201+
<< "Squeeze_X " << vecParams[11] << "\n"
202+
<< "Squeeze_Y " << vecParams[12] << "\n"
203+
<< "}\n";
204+
break;
205+
case EUNDISTORTION::UNDISTORTION_3DERADIAL4:
206+
ss << "LD_3DE4_Radial_Standard_Degree_4 {\n"
207+
<< "direction undistort \n"
208+
<< "tde4_focal_length_cm " << vecFocal << " \n"
209+
<< "tde4_custom_focus_distance_cm 1.0 \n"
210+
<< "tde4_filmback_width_cm " << commonSensorWidth << " \n"
211+
<< "tde4_filmback_height_cm " << commonSensorWidth << " \n"
212+
<< "tde4_lens_center_offset_x_cm 0.0000000 \n"
213+
<< "tde4_lens_center_offset_y_cm 0.0000000 \n"
214+
<< "tde4_pixel_aspect " << commonPixelAspect << " \n"
215+
<< "field_of_view_xa_unit 0.0000000 \n"
216+
<< "field_of_view_xb_unit 1.0000000 \n"
217+
<< "field_of_view_ya_unit 0.0000000 \n"
218+
<< "field_of_view_yb_unit 1.0000000 \n"
219+
<< "Distortion_Degree_2 " << vecParams[0] << " \n"
220+
<< "U_Degree_2 " << vecParams[1] << " \n"
221+
<< "V_Degree_2 " << vecParams[2] << " \n"
222+
<< "Quartic_Distortion_Degree_4 " << vecParams[3] << " \n"
223+
<< "U_Degree_4 " << vecParams[4] << " \n"
224+
<< "V_Degree_4 " << vecParams[5] << " \n"
225+
<< "Phi_Cylindric_Direction " << vecParams[6] << " \n"
226+
<< "B_Cylindric_Bending " << vecParams[7] << " \n"
227+
<< "}\n";
228+
break;
229+
case EUNDISTORTION::UNDISTORTION_3DECLASSICLD:
230+
ss << "LD_3DE4_Radial_Standard_Degree_4 {\n"
231+
<< "direction undistort \n"
232+
<< "tde4_focal_length_cm " << vecFocal << " \n"
233+
<< "tde4_custom_focus_distance_cm 1.0 \n"
234+
<< "tde4_filmback_width_cm " << commonSensorWidth << " \n"
235+
<< "tde4_filmback_height_cm " << commonSensorHeight << " \n"
236+
<< "tde4_lens_center_offset_x_cm 0.0000000 \n"
237+
<< "tde4_lens_center_offset_y_cm 0.0000000 \n"
238+
<< "tde4_pixel_aspect " << commonPixelAspect << " \n"
239+
<< "field_of_view_xa_unit 0.0000000 \n"
240+
<< "field_of_view_xb_unit 1.0000000 \n"
241+
<< "field_of_view_ya_unit 0.0000000 \n"
242+
<< "field_of_view_yb_unit 1.0000000 \n"
243+
<< "Distortion " << vecParams[0] << " \n"
244+
<< "Anamorphic_Squeeze " << vecParams[1] << " \n"
245+
<< "Curvature_X " << vecParams[2] << " \n"
246+
<< "Curvature_Y " << vecParams[3] << " \n"
247+
<< "Quartic_Distortion " << vecParams[4] << " \n"
248+
<< "}\n";
249+
break;
250+
default:
251+
ALICEVISION_LOG_ERROR("Unsupported intrinsic type for conversion to Nuke LensDistortion node: " << EUNDISTORTION_enumToString(commonType));
252+
return false;
253+
}
254+
255+
content = ss.str();
256+
257+
return true;
258+
}
259+
32260
std::string toNuke(std::shared_ptr<camera::IntrinsicScaleOffsetDisto> intrinsic)
33261
{
34262
std::stringstream ss;
@@ -50,11 +278,11 @@ std::string toNuke(std::shared_ptr<camera::IntrinsicScaleOffsetDisto> intrinsic)
50278
ss << "LensDistortion2 {" << "\n"
51279
<< "\n"
52280
<< " distortionModelPreset Custom" << "\n"
53-
<< " distortionModelPreset \"3DEqualizer/3DE4 Anamorphic - Standard, Degree 4\""
281+
<< " distortionModelPreset \"3DEqualizer/3DE4 Anamorphic - Standard, Degree 4\"\n"
54282
<< " distortionNumerator0 " << params[0] << "\n"
55283
<< "\n"
56284
<< " distortionNumerator1 " << params[1] << "\n"
57-
<< " lens Anamorphic"
285+
<< " lens Anamorphic\n"
58286
<< " distortionNumerator2 " << params[2] << "\n"
59287
<< " centre {" << offset[0] << " " << -offset[1] << "}" << "\n"
60288
<< " output Undistort" << "\n"
@@ -193,6 +421,7 @@ int aliceVision_main(int argc, char* argv[])
193421
bool exportLensGridsUndistorted = true;
194422
bool exportNukeNode = true;
195423
bool exportSTMaps = true;
424+
bool exportAnimatedNukeNode = true;
196425

197426
// Command line parameters
198427
// clang-format off
@@ -205,9 +434,11 @@ int aliceVision_main(int argc, char* argv[])
205434

206435
po::options_description optionalParams("Optional parameters");
207436
optionalParams.add_options()
208-
("exportNukeNode", po::value<bool>(&exportNukeNode)->default_value(exportLensGridsUndistorted),
437+
("exportNukeNode", po::value<bool>(&exportNukeNode)->default_value(exportNukeNode),
438+
"Export Nuke node as FILE.nk.")
439+
("exportAnimatedNukeNode", po::value<bool>(&exportAnimatedNukeNode)->default_value(exportAnimatedNukeNode),
209440
"Export Nuke node as FILE.nk.")
210-
("exportSTMaps", po::value<bool>(&exportSTMaps)->default_value(exportLensGridsUndistorted),
441+
("exportSTMaps", po::value<bool>(&exportSTMaps)->default_value(exportSTMaps),
211442
"Export STMaps.")
212443
("exportLensGridsUndistorted,e", po::value<bool>(&exportLensGridsUndistorted)->default_value(exportLensGridsUndistorted),
213444
"Export lens grids undistorted for validation.");
@@ -229,6 +460,77 @@ int aliceVision_main(int argc, char* argv[])
229460
return EXIT_FAILURE;
230461
}
231462

463+
if (exportAnimatedNukeNode)
464+
{
465+
//Map is indexed by a tuple of :
466+
//Undistortion type
467+
//sensor width
468+
//sensor height
469+
//image width
470+
//image height
471+
//Pixel aspect ratio
472+
typedef std::tuple<camera::EUNDISTORTION, int, int, int, int, double> Descriptor;
473+
474+
//Group views by unique descriptor
475+
std::map<Descriptor, std::vector<sfmData::View::sptr>> viewsPerUndistortionType;
476+
for (const auto& [viewId, viewPtr] : sfmData.getViews())
477+
{
478+
IndexT idIntrinsic = viewPtr->getIntrinsicId();
479+
if (idIntrinsic == UndefinedIndexT)
480+
{
481+
continue;
482+
}
483+
484+
const auto intrinsic = sfmData.getIntrinsicSharedPtr(idIntrinsic);
485+
const auto intrinsicDisto = std::dynamic_pointer_cast<IntrinsicScaleOffsetDisto>(intrinsic);
486+
if (!intrinsicDisto)
487+
{
488+
ALICEVISION_LOG_INFO("Intrinsic " << idIntrinsic << " has incorrect format");
489+
continue;
490+
}
491+
492+
const auto undistortion = intrinsicDisto->getUndistortion();
493+
494+
const double sensorWidth = intrinsicDisto->sensorWidth();
495+
const double sensorHeight = intrinsicDisto->sensorHeight();
496+
const auto& size = undistortion->getSize();
497+
const double pa = undistortion->getPixelAspectRatio();
498+
499+
Descriptor d = {undistortion->getType(), sensorWidth, sensorHeight, size.x(), size.y(), pa};
500+
viewsPerUndistortionType[d].push_back(viewPtr);
501+
}
502+
503+
for (const auto & [desc, vec]: viewsPerUndistortionType)
504+
{
505+
std::string nukeNodeStr;
506+
507+
if (!sequenceToNuke(nukeNodeStr, sfmData, vec))
508+
{
509+
continue;
510+
}
511+
512+
const std::string typeName = camera::EUNDISTORTION_enumToString(std::get<0>(desc));
513+
514+
ALICEVISION_LOG_INFO("Writing Nuke animated LensDistortion node for " << typeName);
515+
516+
517+
//Create a unique filename
518+
std::stringstream ss;
519+
ss << outputFilePath << "/animated_";
520+
ss << typeName << "_";
521+
ss << std::to_string(std::get<1>(desc)) << "_";
522+
ss << std::to_string(std::get<2>(desc)) << "_";
523+
ss << std::to_string(std::get<3>(desc)) << "_";
524+
ss << std::to_string(std::get<4>(desc)) << "_";
525+
ss << std::to_string(int(std::get<5>(desc) * 100.0)) << ".nk";
526+
527+
//Store file
528+
std::ofstream of(ss.str());
529+
of << nukeNodeStr;
530+
of.close();
531+
}
532+
}
533+
232534
for (const auto& [intrinsicId, intrinsicPtr] : sfmData.getIntrinsics())
233535
{
234536
ALICEVISION_LOG_INFO("Exporting distortion for intrinsic " << intrinsicId);

0 commit comments

Comments
 (0)