11
11
#include < aliceVision/sfmData/SfMData.hpp>
12
12
#include < aliceVision/sfmDataIO/sfmDataIO.hpp>
13
13
#include < aliceVision/camera/IntrinsicInitMode.hpp>
14
+ #include < aliceVision/dataio/json.hpp>
14
15
15
16
#include < boost/program_options.hpp>
16
17
26
27
namespace po = boost::program_options;
27
28
using namespace aliceVision ;
28
29
29
- int aliceVision_main ( int argc, char ** argv )
30
+ bool applySfmData (sfmData::SfMData & sfmData, const sfmData::SfMData & sfmDataCalibrated )
30
31
{
31
- // command-line parameters
32
- std::string sfmDataFilename;
33
- std::string outSfMDataFilename;
34
- std::string sfmDataCalibratedFilename;
35
-
36
- // clang-format off
37
- po::options_description requiredParams (" Required parameters" );
38
- requiredParams.add_options ()
39
- (" input,i" , po::value<std::string>(&sfmDataFilename)->required (),
40
- " SfMData scene to apply calibration to." )
41
- (" output,o" , po::value<std::string>(&outSfMDataFilename)->required (),
42
- " Output SfMData scene." )
43
- (" calibration,c" , po::value<std::string>(&sfmDataCalibratedFilename)->required (),
44
- " Calibrated SfMData scene." );
45
- // clang-format on
46
-
47
- CmdLine cmdline (" AliceVision applyCalibration" );
48
- cmdline.add (requiredParams);
49
- if (!cmdline.execute (argc, argv))
50
- {
51
- return EXIT_FAILURE;
52
- }
53
-
54
- // Load input scene
55
- sfmData::SfMData sfmData;
56
- if (!sfmDataIO::load (sfmData, sfmDataFilename, sfmDataIO::ESfMData::ALL))
57
- {
58
- ALICEVISION_LOG_ERROR (" The input SfMData file '" << sfmDataFilename << " ' cannot be read" );
59
- return EXIT_FAILURE;
60
- }
61
-
62
- // Special case to handle:
63
- // If calibrated SfMData filename is an empty string
64
- // simply copy the input SfMData
65
- if (sfmDataCalibratedFilename.empty ())
66
- {
67
- // Save sfmData to disk
68
- if (!sfmDataIO::save (sfmData, outSfMDataFilename, sfmDataIO::ESfMData::ALL))
69
- {
70
- ALICEVISION_LOG_ERROR (" The output SfMData file '" << outSfMDataFilename << " ' cannot be written." );
71
- return EXIT_FAILURE;
72
- }
73
-
74
- return EXIT_SUCCESS;
75
- }
76
-
77
- // Load calibrated scene
78
- sfmData::SfMData sfmDataCalibrated;
79
- if (!sfmDataIO::load (sfmDataCalibrated, sfmDataCalibratedFilename, sfmDataIO::ESfMData::ALL))
80
- {
81
- ALICEVISION_LOG_ERROR (" The calibrated SfMData file '" << sfmDataCalibratedFilename << " ' cannot be read" );
82
- return EXIT_FAILURE;
83
- }
84
-
85
32
// Detect calibration setup
86
33
const auto & calibratedIntrinsics = sfmDataCalibrated.getIntrinsics ();
87
34
@@ -91,7 +38,7 @@ int aliceVision_main(int argc, char** argv)
91
38
if (!isMonoCam && !isMultiCam)
92
39
{
93
40
ALICEVISION_LOG_ERROR (" Unknown calibration setup" );
94
- return EXIT_FAILURE ;
41
+ return false ;
95
42
}
96
43
97
44
// Apply rig calibration
@@ -100,7 +47,7 @@ int aliceVision_main(int argc, char** argv)
100
47
if (sfmData.getRigs ().size () != 1 )
101
48
{
102
49
ALICEVISION_LOG_ERROR (" There must be exactly 1 rig to apply rig calibration" );
103
- return EXIT_FAILURE ;
50
+ return false ;
104
51
}
105
52
106
53
// Retrieve calibrated sub-poses
@@ -114,7 +61,7 @@ int aliceVision_main(int argc, char** argv)
114
61
if (subPoses.size () != calibratedSubPoses.size ())
115
62
{
116
63
ALICEVISION_LOG_ERROR (" Incoherent number of sub-poses" );
117
- return EXIT_FAILURE ;
64
+ return false ;
118
65
}
119
66
120
67
// Copy calibrated sub-poses
@@ -192,7 +139,9 @@ int aliceVision_main(int argc, char** argv)
192
139
const bool isDistortionCalibrated = calibratedIntrinsic->getDistortionInitializationMode () == camera::EInitMode::CALIBRATED;
193
140
194
141
if (!isIntrinsicCalibrated && !isDistortionCalibrated)
142
+ {
195
143
continue ;
144
+ }
196
145
197
146
// Aspect ratio of input intrinsic
198
147
const unsigned int width = intrinsic->w ();
@@ -230,7 +179,7 @@ int aliceVision_main(int argc, char** argv)
230
179
if (distortionOnly == false || smaller == false )
231
180
{
232
181
ALICEVISION_LOG_ERROR (" Intrinsic from input SfMData and calibrated SfMData are incompatible." );
233
- return EXIT_FAILURE ;
182
+ return false ;
234
183
}
235
184
}
236
185
@@ -274,6 +223,240 @@ int aliceVision_main(int argc, char** argv)
274
223
intrinsics.at (intrinsicId) = newIntrinsic;
275
224
}
276
225
226
+ return true ;
227
+ }
228
+
229
+ bool applyJson (sfmData::SfMData & sfmData, boost::json::value & input)
230
+ {
231
+ if (input.kind () != boost::json::kind::object)
232
+ {
233
+ return false ;
234
+ }
235
+
236
+ if (sfmData.getIntrinsics ().size () != 1 )
237
+ {
238
+ ALICEVISION_LOG_ERROR (" Number of intrinsics is not one" );
239
+ return false ;
240
+ }
241
+
242
+ auto intrinsic = std::dynamic_pointer_cast<camera::IntrinsicScaleOffsetDisto>(sfmData.getIntrinsics ().begin ()->second );
243
+ if (!camera::isPinhole (intrinsic->getType ()))
244
+ {
245
+ ALICEVISION_LOG_ERROR (" Original intrinsic is not a pinhole" );
246
+ return false ;
247
+ }
248
+
249
+ auto const & obj = input.get_object ();
250
+
251
+ const std::string & model = boost::json::value_to<std::string>(obj.at (" distortionModel" ));
252
+
253
+ const double & focalLength = boost::json::value_to<double >(obj.at (" focalLength" ));
254
+ const double & focusDistance = boost::json::value_to<double >(obj.at (" focusDistance" ));
255
+ const double & filmbackWidth = boost::json::value_to<double >(obj.at (" filmbackWidth" ));
256
+ const double & filmbackHeight = boost::json::value_to<double >(obj.at (" filmbackHeight" ));
257
+ const double & filmbackOffsetX = boost::json::value_to<double >(obj.at (" filmbackOffsetX" ));
258
+ const double & filmbackOffsetY = boost::json::value_to<double >(obj.at (" filmbackOffsetY" ));
259
+ const double & pixelAspect = boost::json::value_to<double >(obj.at (" pixelAspect" ));
260
+
261
+ if (std::abs (filmbackOffsetX) > 1e-12 )
262
+ {
263
+ ALICEVISION_LOG_ERROR (" Unsupported value for filmbackOffsetX" );
264
+ return false ;
265
+ }
266
+
267
+ if (std::abs (filmbackOffsetY) > 1e-12 )
268
+ {
269
+ ALICEVISION_LOG_ERROR (" Unsupported value for filmbackOffsetY" );
270
+ return false ;
271
+ }
272
+
273
+ double w = intrinsic->w ();
274
+ double h = intrinsic->h ();
275
+ double nh = h / pixelAspect;
276
+ double ratio = w / nh;
277
+ double ratioDesqueezed = w / nh;
278
+ double iratio = filmbackWidth / filmbackHeight;
279
+
280
+ // Compare image size ratio and filmback size ratio
281
+ bool isDesqueezed = false ;
282
+ if (std::abs (ratioDesqueezed - iratio) < 1e-2 )
283
+ {
284
+ ALICEVISION_LOG_INFO (" Input image look desqueezed" );
285
+ isDesqueezed = true ;
286
+ }
287
+ else if (std::abs (ratio - iratio) > 1e-2 )
288
+ {
289
+ ALICEVISION_LOG_ERROR (" Incompatible image ratios" );
290
+ return false ;
291
+ }
292
+
293
+ intrinsic->setSensorWidth (filmbackWidth);
294
+ intrinsic->setSensorHeight ((isDesqueezed)?filmbackHeight:filmbackHeight*pixelAspect);
295
+ intrinsic->setDistortionObject (nullptr );
296
+
297
+ if (model == " anamorphic4" )
298
+ {
299
+ std::vector<double > params =
300
+ {
301
+ boost::json::value_to<double >(obj.at (" Cx02Degree2" )),
302
+ boost::json::value_to<double >(obj.at (" Cy02Degree2" )),
303
+ boost::json::value_to<double >(obj.at (" Cx22Degree2" )),
304
+ boost::json::value_to<double >(obj.at (" Cy22Degree2" )),
305
+ boost::json::value_to<double >(obj.at (" Cx04Degree4" )),
306
+ boost::json::value_to<double >(obj.at (" Cy04Degree4" )),
307
+ boost::json::value_to<double >(obj.at (" Cx24Degree4" )),
308
+ boost::json::value_to<double >(obj.at (" Cy24Degree4" )),
309
+ boost::json::value_to<double >(obj.at (" Cx44Degree4" )),
310
+ boost::json::value_to<double >(obj.at (" Cy44Degree4" )),
311
+ degreeToRadian (boost::json::value_to<double >(obj.at (" lensRotation" ))),
312
+ boost::json::value_to<double >(obj.at (" squeezeX" )),
313
+ boost::json::value_to<double >(obj.at (" squeezeY" )),
314
+ };
315
+
316
+ auto undistortion = camera::createUndistortion (camera::EUNDISTORTION::UNDISTORTION_3DEANAMORPHIC4);
317
+ undistortion->setSize (w, h);
318
+ undistortion->setPixelAspectRatio (pixelAspect);
319
+ undistortion->setDesqueezed (isDesqueezed);
320
+ undistortion->setParameters (params);
321
+ intrinsic->setUndistortionObject (undistortion);
322
+ intrinsic->setDistortionInitializationMode (camera::EInitMode::CALIBRATED);
323
+ }
324
+ else if (model == " classicLD" )
325
+ {
326
+ std::vector<double > params =
327
+ {
328
+ boost::json::value_to<double >(obj.at (" distortion" )),
329
+ boost::json::value_to<double >(obj.at (" anamorphicSqueeze" )),
330
+ boost::json::value_to<double >(obj.at (" curvatureX" )),
331
+ boost::json::value_to<double >(obj.at (" curvatureY" )),
332
+ boost::json::value_to<double >(obj.at (" quarticDistortion" ))
333
+ };
334
+
335
+ auto undistortion = camera::createUndistortion (camera::EUNDISTORTION::UNDISTORTION_3DECLASSICLD);
336
+ undistortion->setSize (w, h);
337
+ undistortion->setPixelAspectRatio (pixelAspect);
338
+ undistortion->setDesqueezed (isDesqueezed);
339
+ undistortion->setParameters (params);
340
+ intrinsic->setUndistortionObject (undistortion);
341
+ intrinsic->setDistortionInitializationMode (camera::EInitMode::CALIBRATED);
342
+ }
343
+ else if (model == " radial4" )
344
+ {
345
+ std::vector<double > params =
346
+ {
347
+ boost::json::value_to<double >(obj.at (" distortionDegree2" )),
348
+ boost::json::value_to<double >(obj.at (" uDegree2" )),
349
+ boost::json::value_to<double >(obj.at (" vDegree2" )),
350
+ boost::json::value_to<double >(obj.at (" quarticDistortionDegree4" )),
351
+ boost::json::value_to<double >(obj.at (" uDegree4" )),
352
+ boost::json::value_to<double >(obj.at (" vDegree4" )),
353
+ boost::json::value_to<double >(obj.at (" phiCylindricDirection" )),
354
+ boost::json::value_to<double >(obj.at (" bCylindricBending" )),
355
+ };
356
+
357
+ auto undistortion = camera::createUndistortion (camera::EUNDISTORTION::UNDISTORTION_3DERADIAL4);
358
+ undistortion->setSize (w, h);
359
+ undistortion->setPixelAspectRatio (pixelAspect);
360
+ undistortion->setDesqueezed (isDesqueezed);
361
+ undistortion->setParameters (params);
362
+ intrinsic->setUndistortionObject (undistortion);
363
+ intrinsic->setDistortionInitializationMode (camera::EInitMode::CALIBRATED);
364
+ }
365
+ else
366
+ {
367
+ ALICEVISION_LOG_ERROR (" unknown distortion model" );
368
+ return false ;
369
+ }
370
+
371
+ return true ;
372
+ }
373
+
374
+ int aliceVision_main (int argc, char ** argv)
375
+ {
376
+ // command-line parameters
377
+ std::string sfmDataFilename;
378
+ std::string outSfMDataFilename;
379
+ std::string calibrationFilename;
380
+ bool useJson = true ;
381
+
382
+ // clang-format off
383
+ po::options_description requiredParams (" Required parameters" );
384
+ requiredParams.add_options ()
385
+ (" input,i" , po::value<std::string>(&sfmDataFilename)->required (),
386
+ " SfMData scene to apply calibration to." )
387
+ (" output,o" , po::value<std::string>(&outSfMDataFilename)->required (),
388
+ " Output SfMData scene." )
389
+ (" useJson" , po::value<bool >(&useJson)->default_value (useJson),
390
+ " Calibration is a Lens calibration file generated using 3Dequalizer instead of an sfmData." )
391
+ (" calibration,c" , po::value<std::string>(&calibrationFilename)->required (),
392
+ " Calibration file (SfmData or Lens calibration file)." );
393
+ // clang-format on
394
+
395
+ CmdLine cmdline (" AliceVision applyCalibration" );
396
+ cmdline.add (requiredParams);
397
+ if (!cmdline.execute (argc, argv))
398
+ {
399
+ return EXIT_FAILURE;
400
+ }
401
+
402
+ // Load input scene
403
+ sfmData::SfMData sfmData;
404
+ if (!sfmDataIO::load (sfmData, sfmDataFilename, sfmDataIO::ESfMData::ALL))
405
+ {
406
+ ALICEVISION_LOG_ERROR (" The input SfMData file '" << sfmDataFilename << " ' cannot be read" );
407
+ return EXIT_FAILURE;
408
+ }
409
+
410
+ // Special case to handle:
411
+ // If calibrated SfMData filename is an empty string
412
+ // simply copy the input SfMData
413
+ if (calibrationFilename.empty ())
414
+ {
415
+ // Save sfmData to disk
416
+ if (!sfmDataIO::save (sfmData, outSfMDataFilename, sfmDataIO::ESfMData::ALL))
417
+ {
418
+ ALICEVISION_LOG_ERROR (" The output SfMData file '" << outSfMDataFilename << " ' cannot be written." );
419
+ return EXIT_FAILURE;
420
+ }
421
+
422
+ return EXIT_SUCCESS;
423
+ }
424
+
425
+
426
+ if (useJson)
427
+ {
428
+ boost::json::error_code ec;
429
+ std::ifstream inputfile (calibrationFilename);
430
+ std::vector<boost::json::value> content = readJsons (inputfile, ec);
431
+ if (content.size () != 1 )
432
+ {
433
+ ALICEVISION_LOG_ERROR (" Invalid calibration file" );
434
+ return EXIT_FAILURE;
435
+ }
436
+
437
+ if (!applyJson (sfmData, content[0 ]))
438
+ {
439
+ ALICEVISION_LOG_ERROR (" Error applying calibrated json" );
440
+ return EXIT_FAILURE;
441
+ }
442
+ }
443
+ else
444
+ {
445
+ // Load calibrated scene
446
+ sfmData::SfMData sfmDataCalibrated;
447
+ if (!sfmDataIO::load (sfmDataCalibrated, calibrationFilename, sfmDataIO::ESfMData::ALL))
448
+ {
449
+ ALICEVISION_LOG_ERROR (" The calibrated SfMData file '" << calibrationFilename << " ' cannot be read" );
450
+ return EXIT_FAILURE;
451
+ }
452
+
453
+ if (!applySfmData (sfmData, sfmDataCalibrated))
454
+ {
455
+ ALICEVISION_LOG_ERROR (" Error applying calibrated sfmData" );
456
+ return EXIT_FAILURE;
457
+ }
458
+ }
459
+
277
460
// Save sfmData to disk
278
461
if (!sfmDataIO::save (sfmData, outSfMDataFilename, sfmDataIO::ESfMData (sfmDataIO::ALL)))
279
462
{
0 commit comments