@@ -29,6 +29,234 @@ namespace po = boost::program_options;
2929using namespace aliceVision ;
3030using 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+
32260std::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