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