1 /* ============================================================
2 *
3 * This file is a part of digiKam project
4 * https://www.digikam.org
5 *
6 * Date : 2012-02-04
7 * Description : a tool to create panorama by fusion of several images.
8 * This parser is based on pto file format described here:
9 * hugin.sourceforge.net/docs/nona/nona.txt, and
10 * on pto files produced by Hugin's tools.
11 *
12 * Copyright (C) 2012-2015 by Benjamin Girault <benjamin dot girault at gmail dot com>
13 *
14 * This program is free software; you can redistribute it
15 * and/or modify it under the terms of the GNU General
16 * Public License as published by the Free Software Foundation;
17 * either version 2, or (at your option) any later version.
18 *
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * ============================================================ */
25
26 #include "ptofile.h"
27
28 // C++ includes
29
30 #include <string>
31 #include <iostream>
32
33 // Qt includes
34
35 #include <QFile>
36
37 // Local includes
38
39 #include "digikam_debug.h"
40
41 extern "C"
42 {
43 #include "tparser.h"
44 #include "tparsergetters.h"
45 }
46
47 namespace Digikam
48 {
49
50 class Q_DECL_HIDDEN PTOFile::Private
51 {
52 public:
53
Private(const QString & huginVersion)54 explicit Private(const QString& huginVersion)
55 : script(nullptr),
56 huginVersion(huginVersion)
57 {
58 }
59
60 pt_script* script;
61 const QString& huginVersion;
62 };
63
PTOFile(const QString & huginVersion)64 PTOFile::PTOFile(const QString& huginVersion)
65 : d(new Private(huginVersion))
66 {
67 }
68
~PTOFile()69 PTOFile::~PTOFile()
70 {
71 if (d->script != nullptr)
72 {
73 panoScriptFree(d->script);
74 delete d->script;
75 }
76
77 delete d;
78 }
79
openFile(const QString & path)80 bool PTOFile::openFile(const QString& path)
81 {
82 /*
83 mtrace();
84 */
85 if (d->script != nullptr)
86 {
87 panoScriptFree(d->script);
88 delete d->script;
89 d->script = nullptr;
90 }
91
92 d->script = new pt_script();
93
94 if (!panoScriptParse(QFile::encodeName(path).constData(), d->script))
95 {
96 return false;
97 }
98 /*
99 muntrace();
100 */
101 return true;
102 }
103
getPTO()104 PTOType* PTOFile::getPTO()
105 {
106 if (d->script == nullptr)
107 {
108 return nullptr;
109 }
110
111 PTOType* const out = new PTOType(d->huginVersion);
112
113 // Project data conversion
114
115 for (int c = 0 ; c < panoScriptGetPanoPrevCommentsCount(d->script) ; ++c)
116 {
117 out->project.previousComments << QString::fromLocal8Bit(panoScriptGetPanoComment(d->script, c));
118 }
119
120 out->project.size.setHeight(panoScriptGetPanoHeight(d->script));
121 out->project.size.setWidth(panoScriptGetPanoWidth(d->script));
122 out->project.crop.setLeft(panoScriptGetPanoCropLeft(d->script));
123 out->project.crop.setRight(panoScriptGetPanoCropRight(d->script));
124 out->project.crop.setTop(panoScriptGetPanoCropTop(d->script));
125 out->project.crop.setBottom(panoScriptGetPanoCropBottom(d->script));
126 out->project.projection = PTOType::Project::ProjectionType(panoScriptGetPanoProjection(d->script));
127 out->project.fieldOfView = panoScriptGetPanoHFOV(d->script);
128 out->project.fileFormat.fileType = PTOType::Project::FileFormat::FileType(panoScriptGetPanoOutputFormat(d->script));
129
130 switch (out->project.fileFormat.fileType)
131 {
132 case PTOType::Project::FileFormat::TIFF_m:
133 case PTOType::Project::FileFormat::TIFF_multilayer:
134 {
135 out->project.fileFormat.savePositions = panoScriptGetPanoOutputSaveCoordinates(d->script) != 0;
136 out->project.fileFormat.cropped = panoScriptGetPanoOutputCropped(d->script) != 0;
137 break;
138 }
139
140 case PTOType::Project::FileFormat::TIFF:
141 {
142 int method = panoScriptGetPanoOutputCompression(d->script);
143
144 if (method != -1)
145 out->project.fileFormat.compressionMethod = PTOType::Project::FileFormat::CompressionMethod(method);
146
147 break;
148 }
149
150 case PTOType::Project::FileFormat::JPEG:
151 {
152 int quality = panoScriptGetPanoOutputQuality(d->script);
153
154 if (quality >= 0)
155 out->project.fileFormat.quality = quality;
156
157 break;
158 }
159
160 default:
161 {
162 break;
163 }
164 }
165
166 out->project.exposure = panoScriptGetPanoExposure(d->script);
167 out->project.hdr = panoScriptGetPanoIsHDR(d->script) != 0;
168 out->project.bitDepth = PTOType::Project::BitDepth(panoScriptGetPanoBitDepth(d->script));
169 out->project.photometricReferenceId = panoScriptGetPanoImageReference(d->script);
170
171 // NOTE: there should not be any unmatched parameters at this point, because the parsing would otherwise have failed
172
173 // Stitcher
174
175 for (int c = 0 ; c < panoScriptGetOptimizePrevCommentsCount(d->script) ; ++c)
176 {
177 out->stitcher.previousComments << QString::fromLocal8Bit(panoScriptGetOptimizeComment(d->script, c));
178 }
179
180 out->stitcher.gamma = panoScriptGetOptimizeGamma(d->script);
181 out->stitcher.interpolator = PTOType::Stitcher::Interpolator(panoScriptGetOptimizeInterpolator(d->script));
182 out->stitcher.speedUp = PTOType::Stitcher::SpeedUp(panoScriptGetOptimizeSpeedUp(d->script));
183 out->stitcher.huberSigma = panoScriptGetOptimizeHuberSigma(d->script);
184 out->stitcher.photometricHuberSigma = panoScriptGetOptimizePhotometricHuberSigma(d->script);
185
186 // Images
187
188 out->images.clear();
189
190 for (int i = 0 ; i < panoScriptGetImagesCount(d->script) ; ++i)
191 {
192 out->images.insert(i, PTOType::Image());
193 PTOType::Image& image = out->images.last();
194
195 int tmpRef;
196
197 for (int c = 0 ; c < panoScriptGetImagePrevCommentsCount(d->script, i) ; ++c)
198 {
199 image.previousComments << QString::fromLocal8Bit(panoScriptGetImageComment(d->script, i, c));
200 }
201
202 image.size.setWidth(panoScriptGetImageWidth(d->script, i));
203 image.size.setHeight(panoScriptGetImageHeight(d->script, i));
204 image.id = i;
205 image.lensProjection = PTOType::Image::LensProjection(panoScriptGetImageProjection(d->script, i));
206 tmpRef = panoScriptGetImageHFOVRef(d->script, i);
207
208 if (tmpRef == -1)
209 {
210 image.fieldOfView.value = panoScriptGetImageHFOV(d->script, i);
211 }
212 else
213 {
214 image.fieldOfView.referenceId = tmpRef;
215 }
216
217 image.yaw = panoScriptGetImageYaw(d->script, i);
218 image.pitch = panoScriptGetImagePitch(d->script, i);
219 image.roll = panoScriptGetImageRoll(d->script, i);
220 tmpRef = panoScriptGetImageCoefARef(d->script, i);
221
222 if (tmpRef == -1)
223 {
224 image.lensBarrelCoefficientA.value = panoScriptGetImageCoefA(d->script, i);
225 }
226 else
227 {
228 image.lensBarrelCoefficientA.referenceId = tmpRef;
229 }
230
231 tmpRef = panoScriptGetImageCoefBRef(d->script, i);
232
233 if (tmpRef == -1)
234 {
235 image.lensBarrelCoefficientB.value = panoScriptGetImageCoefB(d->script, i);
236 }
237 else
238 {
239 image.lensBarrelCoefficientB.referenceId = tmpRef;
240 }
241
242 tmpRef = panoScriptGetImageCoefCRef(d->script, i);
243
244 if (tmpRef == -1)
245 {
246 image.lensBarrelCoefficientC.value = panoScriptGetImageCoefC(d->script, i);
247 }
248 else
249 {
250 image.lensBarrelCoefficientC.referenceId = tmpRef;
251 }
252
253 tmpRef = panoScriptGetImageCoefDRef(d->script, i);
254
255 if (tmpRef == -1)
256 {
257 image.lensCenterOffsetX.value = panoScriptGetImageCoefD(d->script, i);
258 }
259 else
260 {
261 image.lensCenterOffsetX.referenceId = tmpRef;
262 }
263
264 tmpRef = panoScriptGetImageCoefERef(d->script, i);
265
266 if (tmpRef == -1)
267 {
268 image.lensCenterOffsetY.value = panoScriptGetImageCoefE(d->script, i);
269 }
270 else
271 {
272 image.lensCenterOffsetY.referenceId = tmpRef;
273 }
274
275 tmpRef = panoScriptGetImageSheerXRef(d->script, i);
276
277 if (tmpRef == -1)
278 {
279 image.lensShearX.value = panoScriptGetImageSheerX(d->script, i);
280 }
281 else
282 {
283 image.lensShearX.referenceId = tmpRef;
284 }
285
286 tmpRef = panoScriptGetImageSheerYRef(d->script, i);
287
288 if (tmpRef == -1)
289 {
290 image.lensShearY.value = panoScriptGetImageSheerY(d->script, i);
291 }
292 else
293 {
294 image.lensShearY.referenceId = tmpRef;
295 }
296
297 tmpRef = panoScriptGetImageExposureRef(d->script, i);
298
299 if (tmpRef == -1)
300 {
301 image.exposure.value = panoScriptGetImageExposure(d->script, i);
302 }
303 else
304 {
305 image.exposure.referenceId = tmpRef;
306 }
307
308 tmpRef = panoScriptGetImageWBRedRef(d->script, i);
309
310 if (tmpRef == -1)
311 {
312 image.whiteBalanceRed.value = panoScriptGetImageWBRed(d->script, i);
313 }
314 else
315 {
316 image.whiteBalanceRed.referenceId = tmpRef;
317 }
318
319 tmpRef = panoScriptGetImageWBBlueRef(d->script, i);
320
321 if (tmpRef == -1)
322 {
323 image.whiteBalanceBlue.value = panoScriptGetImageWBBlue(d->script, i);
324 }
325 else
326 {
327 image.whiteBalanceBlue.referenceId = tmpRef;
328 }
329
330 tmpRef = panoScriptGetImageVignettingModeRef(d->script, i);
331
332 if (tmpRef == -1)
333 {
334 image.vignettingMode.value = PTOType::Image::VignettingMode(panoScriptGetImageVignettingMode(d->script, i));
335 }
336 else
337 {
338 image.vignettingMode.referenceId = tmpRef;
339 }
340
341 tmpRef = panoScriptGetImageVignettingCoeffARef(d->script, i);
342
343 if (tmpRef == -1)
344 {
345 image.vignettingCorrectionI.value = panoScriptGetImageVignettingCoeffA(d->script, i);
346 }
347 else
348 {
349 image.vignettingCorrectionI.referenceId = tmpRef;
350 }
351
352 tmpRef = panoScriptGetImageVignettingCoeffBRef(d->script, i);
353
354 if (tmpRef == -1)
355 {
356 image.vignettingCorrectionJ.value = panoScriptGetImageVignettingCoeffB(d->script, i);
357 }
358 else
359 {
360 image.vignettingCorrectionJ.referenceId = tmpRef;
361 }
362
363 tmpRef = panoScriptGetImageVignettingCoeffCRef(d->script, i);
364
365 if (tmpRef == -1)
366 {
367 image.vignettingCorrectionK.value = panoScriptGetImageVignettingCoeffC(d->script, i);
368 }
369 else
370 {
371 image.vignettingCorrectionK.referenceId = tmpRef;
372 }
373
374 tmpRef = panoScriptGetImageVignettingCoeffDRef(d->script, i);
375
376 if (tmpRef == -1)
377 {
378 image.vignettingCorrectionL.value = panoScriptGetImageVignettingCoeffD(d->script, i);
379 }
380 else
381 {
382 image.vignettingCorrectionL.referenceId = tmpRef;
383 }
384
385 tmpRef = panoScriptGetImageVignettingCoeffXRef(d->script, i);
386
387 if (tmpRef == -1)
388 {
389 image.vignettingOffsetX.value = panoScriptGetImageVignettingCoeffX(d->script, i);
390 }
391 else
392 {
393 image.vignettingOffsetX.referenceId = tmpRef;
394 }
395
396 tmpRef = panoScriptGetImageVignettingCoeffYRef(d->script, i);
397
398 if (tmpRef == -1)
399 {
400 image.vignettingOffsetY.value = panoScriptGetImageVignettingCoeffY(d->script, i);
401 }
402 else
403 {
404 image.vignettingOffsetY.referenceId = tmpRef;
405 }
406
407 char* const flatfield = panoScriptGetImageVignettingFlatField(d->script, i);
408
409 if (flatfield != nullptr)
410 {
411 image.vignettingFlatfieldImageName = QString::fromLocal8Bit(flatfield);
412 }
413
414 tmpRef = panoScriptGetImagePhotometricCoeffARef(d->script, i);
415
416 if (tmpRef == -1)
417 {
418 image.photometricEMoRA.value = panoScriptGetImagePhotometricCoeffA(d->script, i);
419 }
420 else
421 {
422 image.photometricEMoRA.referenceId = tmpRef;
423 }
424
425 tmpRef = panoScriptGetImagePhotometricCoeffBRef(d->script, i);
426
427 if (tmpRef == -1)
428 {
429 image.photometricEMoRB.value = panoScriptGetImagePhotometricCoeffB(d->script, i);
430 }
431 else
432 {
433 image.photometricEMoRB.referenceId = tmpRef;
434 }
435
436 tmpRef = panoScriptGetImagePhotometricCoeffCRef(d->script, i);
437
438 if (tmpRef == -1)
439 {
440 image.photometricEMoRC.value = panoScriptGetImagePhotometricCoeffC(d->script, i);
441 }
442 else
443 {
444 image.photometricEMoRC.referenceId = tmpRef;
445 }
446
447 tmpRef = panoScriptGetImagePhotometricCoeffDRef(d->script, i);
448
449 if (tmpRef == -1)
450 {
451 image.photometricEMoRD.value = panoScriptGetImagePhotometricCoeffD(d->script, i);
452 }
453 else
454 {
455 image.photometricEMoRD.referenceId = tmpRef;
456 }
457
458 tmpRef = panoScriptGetImagePhotometricCoeffERef(d->script, i);
459
460 if (tmpRef == -1)
461 {
462 image.photometricEMoRE.value = panoScriptGetImagePhotometricCoeffE(d->script, i);
463 }
464 else
465 {
466 image.photometricEMoRE.referenceId = tmpRef;
467 }
468
469 image.mosaicCameraPositionX = panoScriptGetImageCameraTranslationX(d->script, i);
470 image.mosaicCameraPositionY = panoScriptGetImageCameraTranslationY(d->script, i);
471 image.mosaicCameraPositionZ = panoScriptGetImageCameraTranslationZ(d->script, i);
472 image.mosaicProjectionPlaneYaw = panoScriptGetImageProjectionPlaneYaw(d->script, i);
473 image.mosaicProjectionPlanePitch = panoScriptGetImageProjectionPlanePitch(d->script, i);
474 image.crop.setLeft(panoScriptGetImageCropLeft(d->script, i));
475 image.crop.setRight(panoScriptGetImageCropRight(d->script, i));
476 image.crop.setTop(panoScriptGetImageCropTop(d->script, i));
477 image.crop.setBottom(panoScriptGetImageCropBottom(d->script, i));
478 tmpRef = panoScriptGetImageStackRef(d->script, i);
479
480 if (tmpRef == -1)
481 {
482 image.stackNumber.value = panoScriptGetImageStack(d->script, i);
483 }
484 else
485 {
486 image.stackNumber.referenceId = tmpRef;
487 }
488
489 image.fileName = QString::fromLocal8Bit(panoScriptGetImageName(d->script, i));
490 }
491
492 // Masks
493
494 for (int m = 0 ; m < panoScriptGetMaskCount(d->script) ; ++m)
495 {
496 int image = panoScriptGetMaskImage(d->script, m);
497 out->images[image].masks.append(PTOType::Mask());
498 PTOType::Mask& mask = out->images[image].masks.last();
499
500 for (int c = 0 ; c < panoScriptGetMaskPrevCommentCount(d->script, m) ; ++c)
501 {
502 mask.previousComments << QString::fromLocal8Bit(panoScriptGetMaskComment(d->script, m, c));
503 }
504
505 mask.type = PTOType::Mask::MaskType(panoScriptGetMaskType(d->script, m));
506
507 for (int pt = 0 ; pt < panoScriptGetMaskPointCount(d->script, m) ; ++pt)
508 {
509 int x = panoScriptGetMaskPointX(d->script, m, pt);
510 int y = panoScriptGetMaskPointY(d->script, m, pt);
511 mask.hull.append(QPoint(x, y));
512 }
513 }
514
515 // Variable optimization
516
517 for (int v = 0 ; v < panoScriptGetVarsToOptimizeCount(d->script) ; ++v)
518 {
519 int image = panoScriptGetVarsToOptimizeImageId(d->script, v);
520 out->images[image].optimizationParameters.append(PTOType::Optimization());
521 PTOType::Optimization& o = out->images[image].optimizationParameters.last();
522
523 for (int c = 0 ; c < panoScriptGetVarsToOptimizePrevCommentCount(d->script, v) ; ++c)
524 {
525 o.previousComments << QString::fromLocal8Bit(panoScriptGetVarsToOptimizeComment(d->script, v, c));
526 }
527
528 o.parameter = PTOType::Optimization::Parameter(panoScriptGetVarsToOptimizeName(d->script, v));
529 }
530
531 // Control Points
532
533 for (int cp = 0 ; cp < panoScriptGetCtrlPointCount(d->script) ; ++cp)
534 {
535 out->controlPoints.append(PTOType::ControlPoint());
536 PTOType::ControlPoint& ctrlPoint = out->controlPoints.last();
537
538 for (int c = 0 ; c < panoScriptGetCtrlPointPrevCommentCount(d->script, cp) ; ++c)
539 {
540 ctrlPoint.previousComments << QString::fromLocal8Bit(panoScriptGetCtrlPointComment(d->script, cp, c));
541 }
542
543 ctrlPoint.image1Id = panoScriptGetCtrlPointImage1(d->script, cp);
544 ctrlPoint.image2Id = panoScriptGetCtrlPointImage2(d->script, cp);
545 ctrlPoint.p1_x = panoScriptGetCtrlPointX1(d->script, cp);
546 ctrlPoint.p1_y = panoScriptGetCtrlPointY1(d->script, cp);
547 ctrlPoint.p2_x = panoScriptGetCtrlPointX2(d->script, cp);
548 ctrlPoint.p2_y = panoScriptGetCtrlPointY2(d->script, cp);
549 }
550
551 // Ending comments
552
553 for (int c = 0 ; c < panoScriptGetEndingCommentCount(d->script) ; ++c)
554 {
555 out->lastComments << QString::fromLocal8Bit(panoScriptGetEndingComment(d->script, c));
556 }
557
558 return out;
559 }
560
561 } // namespace Digikam
562