1 /*
2 * PTcommon.c
3 *
4 * Many of the routines are based on the program PTStitcher by Helmut
5 * Dersch.
6 *
7 * Copyright Helmut Dersch and Daniel M. German
8 *
9 * Dec 2005
10 *
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public
13 * License as published by the Free Software Foundation; either
14 * version 2 of the License, or (at your option) any later version.
15 *
16 * This software is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this software; see the file COPYING. If not, a copy
23 * can be downloaded from http://www.gnu.org/licenses/gpl.html, or
24 * obtained by writing to the Free Software Foundation, Inc.,
25 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
26 *
27 *
28 * Author: Daniel M German dmgerman at uvic doooot ca
29 *
30 */
31
32 #include "filter.h"
33 #include "PTcommon.h"
34 #include "ColourBrightness.h"
35 #include "pttiff.h"
36 #include "file.h"
37 #include "PTcommon.h"
38 #include "ptstitch.h"
39
40 #include <assert.h>
41 #include <float.h>
42 #include <math.h>
43
44 //#include <stdio.h>
45 //#include <stdlib.h>
46 //#include <sys/types.h>
47 //#include <dirent.h>
48 //#include <unistd.h>
49 //#include <stdint.h>
50 //#include <math.h>
51
52 #ifndef _MSC_VER
53 #include <unistd.h>
54 #else
55 #if _MSC_VER<1900
56 #define isnan _isnan
57 #endif
58 #include "tools/compat_win32/getopt.h"
59 #endif
60
61
62 // Uncomment following line to enable testing of inverses in getROI
63 //#define PANO_TEST_INVERSE
64
65
66 int panoFlattenTIFF(fullPath * fullPathImages, int counterImageFiles,
67 fullPath * outputFileName, int removeOriginals);
68
69
70 //declare functions
71 void getCropInformationFromTiff(TIFF * tif, CropInfo * c);
72 void getROI(TrformStr * TrPtr, aPrefs * aP, PTRect * ROIRect);
73
74 int ptQuietFlag = 0;
75
InsertFileName(fullPath * fp,char * fname)76 void InsertFileName(fullPath * fp, char *fname)
77 {
78 char *c = strrchr((char *) (fp->name), PATH_SEP);
79 if (c != NULL)
80 c++;
81 else
82 c = fp->name;
83 strcpy(c, fname);
84 }
85
86
panoPSDCreate(fullPath * fullPathImages,int numberImages,fullPath * outputFileName,pano_flattening_parms * flatteningParms)87 int panoPSDCreate(fullPath * fullPathImages, int numberImages,
88 fullPath * outputFileName, pano_flattening_parms *flatteningParms)
89 {
90 Image *ptrImage;
91 int i;
92 stBuf stitchInfo;
93 fullPath tempFile;
94 char tempString[128];
95 Image image;
96 Boolean bBig = FALSE;
97
98
99 assert(numberImages > 0);
100 assert(fullPathImages != NULL);
101 assert(outputFileName != NULL);
102
103 if (ptQuietFlag == 0) {
104 Progress(_initProgress, "Converting TIFF to PSD");
105 sprintf(tempString, "%d", 100 / numberImages);
106 Progress(_setProgress, tempString);
107 }
108
109 // Process background of PSD
110 SetImageDefaults(&image);
111
112 if (panoTiffRead(&image, fullPathImages[0].name) == 0) {
113
114 PrintError("Could not read TIFF image No 0 %s", fullPathImages[0].name);
115 if (ptQuietFlag == 0)
116 Progress(_disposeProgress, tempString);
117
118 return -1;
119 }
120
121 // Check to see if we need to create PSB instead of PSD file
122 if(image.height > 30000 || image.width > 30000 || flatteningParms->forceBig == 1)
123 bBig = TRUE;
124
125 if (!(image.bitsPerPixel == 64 || image.bitsPerPixel == 32)) {
126 PrintError("Image type not supported (%d bits per pixel)\n",
127 image.bitsPerPixel);
128 return -1;
129 }
130
131 // New versions of Photoshop can handle multilayer 16bit files
132 // Add an option to down sample to 8bit only if user request
133 if (numberImages > 1 && image.bitsPerPixel != 32) {
134 if (image.bitsPerPixel == 64 && flatteningParms->force8bit == 1) {
135 //PrintError
136 // ("Panotools is not able to save 16bit PSD images. Downsampling to 8 bit");
137 TwoToOneByte(&image); //we need to downsample to 8 bit if we are provided 16 bit images
138 }
139 }
140
141 if (numberImages == 1) {
142 if (writePS(&image, outputFileName, bBig) != 0) {
143 PrintError("Could not write PSD-file");
144 if (ptQuietFlag != 0)
145 Progress(_disposeProgress, tempString);
146 return -1;
147 }
148 return 0;
149 }
150
151 //////////////////////////////////////////////////////////////////////
152 //current algorithm is O(n2) which means it will take _forever_ to create
153 // a huge Photoshop files with many layers.
154
155 //Write out the first image as the base layer in the PSD file
156
157 if (writePSwithLayer(&image, outputFileName,bBig) != 0) {
158 PrintError("Could not write PSD-file");
159 if (ptQuietFlag != 0)
160 Progress(_disposeProgress, tempString);
161 return -1;
162 }
163 panoImageDispose(&image);
164
165 ptrImage = ℑ
166
167 //Now iterate over all other images and add them as layers to the PSD file
168 for (i = 1; i < numberImages; i++) {
169
170 if (ptQuietFlag == 0) {
171 sprintf(tempString, "%d", i * 100 / numberImages);
172 if (Progress(_setProgress, tempString) == 0) {
173 remove(outputFileName->name);
174 return -1;
175 }
176 }
177
178 if (panoTiffRead(ptrImage, fullPathImages[i].name) == 0) {
179
180 PrintError("Could not read TIFF image No &d", i);
181 if (ptQuietFlag == 0)
182 Progress(_disposeProgress, tempString);
183 return -1;
184 }
185
186 // We can't process 16 bit TIFFs. We have to downsample to 8 bit if necessary
187 if (image.bitsPerPixel == 64 && flatteningParms->force8bit == 1)
188 TwoToOneByte(ptrImage);
189
190 // Create a new file with the result PSD, then delete the current one
191
192 strcpy(tempFile.name, outputFileName->name);
193
194 if (panoFileMakeTemp(&tempFile) == 0) {
195 PrintError("Could not make Tempfile");
196 return -1;
197
198 }
199
200 stitchInfo.seam = 1;
201 stitchInfo.feather = 0;
202 if (flatteningParms->stacked)
203 stitchInfo.psdOpacity = (unsigned char) (255.0/ (i + 1));
204 else
205 stitchInfo.psdOpacity = 255;
206
207 stitchInfo.psdBlendingMode = flatteningParms->psdBlendingMode;
208
209 if (addLayerToFile(ptrImage, outputFileName, &tempFile, &stitchInfo) != 0) {
210 PrintError("Could not write Panorama File");
211 return -1;
212 }
213
214 remove(outputFileName->name);
215 rename(tempFile.name, outputFileName->name);
216
217 panoImageDispose(ptrImage);
218 }
219
220 if (!ptQuietFlag) {
221 Progress(_setProgress, "100");
222 Progress(_disposeProgress, tempString);
223 }
224
225 return 0;
226 }
227
228
229
230 #ifdef __Win__
231 //void InsertFileName( fullPath *fp, char *fname ){
232 // char *c = strrchr((char*)(fp->name), PATH_SEP);
233 // if(c != NULL) c++;
234 // else c = fp->name;
235 // strcpy( c, fname );
236 //}
237 #endif
238
239
ARGtoRGBAImage(Image * im)240 void ARGtoRGBAImage(Image * im)
241 {
242 int right;
243 int left;
244 int bottom;
245 int top;
246 int width;
247 int i;
248
249
250 if (im->selection.bottom == 0 && im->selection.right == 0) {
251
252 top = 0;
253 left = 0;
254 bottom = im->height;
255 right = im->width;
256
257
258 }
259 else {
260
261 top = im->selection.top;
262 bottom = im->selection.bottom;
263 left = im->selection.left;
264 right = im->selection.right;
265 }
266
267 width = right - left;
268
269 //fprintf(stderr, "\nWidth %10d Top: %10d bottom %10d Right %10d Left %10d-------", width, top, bottom, right, left);
270
271 assert(width >= 0);
272 assert(bottom >= top);
273
274 for (i = 0; i < bottom - top; i++) {
275
276 ARGBtoRGBA(*(im->data) + i * im->bytesPerLine, width,
277 im->bitsPerPixel);
278
279 } // for
280
281 }
282
283
284
Clear_Area_Outside_Selected_Region(Image * image)285 void Clear_Area_Outside_Selected_Region(Image * image)
286 {
287 // This function clears (i.e. sets to zero) the area outside the
288 // selection region
289
290 int right;
291 int left;
292 int bottom;
293 int top;
294 // int width;
295 // int var24;
296 int bytesPerPixel; // 32
297 unsigned char *dataPtr;
298 unsigned char *pixelPtr;
299
300 int currentRow;
301 int currentColumn;
302
303 // Only works for 8/16 bit per channel (32/64 bits per pixel) images
304 assert(image->bitsPerPixel == 32 || image->bitsPerPixel == 64);
305
306 top = image->selection.top;
307 bottom = image->selection.bottom;
308 left = image->selection.left;
309 right = image->selection.right;
310
311 if (bottom == 0)
312 bottom = image->height;
313
314 if (right == 0)
315 right = image->width;
316
317 if (image->bitsPerPixel == 32) {
318 bytesPerPixel = 4;
319 }
320 else if (image->bitsPerPixel == 64) {
321 bytesPerPixel = 8;
322 }
323 else {
324 PrintError("Invalid bits per pixel in image %d", image->bitsPerPixel);
325 exit(1);
326 }
327
328
329 if (image->format == _fisheye_circ || image->format == _thoby) {
330
331 // TODO
332 // This routine works only in fisheyes in portrait mode
333 // it probably fails in landscape mode
334
335 int horCenter, verCenter;
336 int horRadious;
337 int horRadious2;
338
339 horCenter = (left + right) / 2;
340 verCenter = (top + bottom) / 2;
341
342 // Compute the horizontal width divided by 2,
343 // let us call it horizontal radios
344
345 horRadious = (right - left) / 2;
346 // Square it, so we don't have to compute this
347 // every time
348 horRadious2 = horRadious * horRadious;
349
350 dataPtr = *(image->data);
351
352 // Scan the image from top to bottom
353 for (currentRow = 0; currentRow < image->height; currentRow++) {
354 int verDistance;
355 int verDistance2;
356
357 // The algorith it simple. Find the distance of each from from the
358 // center of the image. If the point is farther than horRadious
359 // then set mask to zero
360
361 currentColumn = 0;
362 pixelPtr = dataPtr;
363
364 // Compute the square of the vertical distance to this row from center
365 verDistance = (currentRow - verCenter);
366 verDistance2 = verDistance * verDistance;
367
368 for (currentColumn = 0; currentColumn < image->width; currentColumn ++) {
369 int horDistance;
370 int horDistance2;
371
372 // Compute square of distance of this point to center
373 // the old Pythagoras way
374 // distance^2 = horDistance^2 + verDistance^2
375
376 horDistance = (currentColumn - horCenter);
377 horDistance2 = horDistance * horDistance;
378
379 if (horDistance2 + verDistance2 > horRadious2) {
380
381 // Point falls outside the circle defined its horizontal maximum distance
382
383 // Set mask to zero
384 if (bytesPerPixel == 4)
385 *pixelPtr = 0;
386 else if (bytesPerPixel == 8) {
387 *pixelPtr = 0;
388 *(pixelPtr+1) = 0;
389 }
390
391 }
392 pixelPtr += bytesPerPixel;
393
394 } // for column
395 dataPtr += image->bytesPerLine;
396 } // for row
397 return;
398 }
399
400
401
402 // Clear the area at above the image
403 dataPtr = *(image->data);
404
405 for (currentRow = 0; currentRow < top; currentRow++) {
406 pixelPtr = dataPtr;
407
408 for (currentColumn = 0; currentColumn < image->width; currentColumn++) {
409 assert(sizeof(int) == bytesPerPixel);
410 memset(pixelPtr, 0, bytesPerPixel);
411 pixelPtr += bytesPerPixel;
412 }
413
414 dataPtr += image->bytesPerLine;
415 }
416
417 // Clear area below the picture
418 dataPtr = bottom * image->bytesPerLine + *(image->data);
419
420 for (currentRow = bottom; currentRow < image->height; currentRow++) {
421 pixelPtr = dataPtr;
422 for (currentColumn = 0; currentColumn < image->width; currentColumn++) {
423 memset(pixelPtr, 0, bytesPerPixel);
424 pixelPtr += bytesPerPixel;
425 }
426
427 dataPtr += image->bytesPerLine;
428
429 } // for ( ; %currentColumn < image->width ; currentColumn++,pixelPtr += bytesPerPixel) {
430
431
432 /* Clear the area to the left of the picture */
433
434 dataPtr = *(image->data);
435 for (currentRow = 0; currentRow < image->height; currentRow++) {
436
437 pixelPtr = dataPtr;
438 for (currentColumn = 0; currentColumn < left; currentColumn++) {
439 memset(pixelPtr, 0, bytesPerPixel);
440 pixelPtr += bytesPerPixel;
441 }
442
443 dataPtr += image->bytesPerLine;
444 }
445
446 /* Clear the area to the right of the picture */
447
448 dataPtr = *(image->data);
449
450 for (currentRow = 0; currentRow < image->height; currentRow++) {
451
452 pixelPtr = dataPtr + bytesPerPixel * right;
453
454 for (currentColumn = right; currentColumn < image->width;
455 currentColumn++) {
456
457 memset(pixelPtr, 0, bytesPerPixel);
458
459 pixelPtr += bytesPerPixel;
460
461 }
462
463 dataPtr += image->bytesPerLine;
464
465 }
466
467 return;
468
469 }
470
471
472 /**
473 * This function computes the minimal rectangle needed to encompass
474 * the region of the output image (TrPtr->dest) that will be populated with
475 * data from the input image (TrPtr->src) using the options specified
476 * in aP. The ROIRect is populated with the left/right/top/bottom values
477 * that define this ROI within the output image
478 */
getROI(TrformStr * TrPtr,aPrefs * aP,PTRect * ROIRect)479 void getROI(TrformStr * TrPtr, aPrefs * aP, PTRect * ROIRect)
480 {
481 struct MakeParams mpinv;
482 fDesc invstack[15], finvD;
483 struct MakeParams mp;
484 fDesc stack[15], fD;
485 int color = 0;
486
487 int x, y, x_jump;
488 double x_d, y_d; // Cartesian Coordinates of point in source (i.e. input) image
489 double Dx, Dy; // Coordinates of corresponding point in destination (i.e. output) image
490 double Dx2, Dy2; // Coordinates of corresponding point in destination (i.e. output) image
491
492 double w2 = (double) TrPtr->dest->width / 2.0 - 0.5; //half destination image width
493 double h2 = (double) TrPtr->dest->height / 2.0 - 0.5; //half destination image height
494 double sw2 = (double) TrPtr->src->width / 2.0 - 0.5; //half source image width
495 double sh2 = (double) TrPtr->src->height / 2.0 - 0.5; //half source image height
496
497 //Set initial values for ROI to be adjusted during this function
498 ROIRect->left = TrPtr->dest->width - 1;
499 ROIRect->right = 0;
500 ROIRect->top = TrPtr->dest->height - 1;
501 ROIRect->bottom = 0;
502
503 //The "forward" transform allows us to map pixel
504 //coordinates in the output image to their location in the source image.
505 // We use it to test the inverse functions of libpano
506 printf("-------------------------------------------\n");
507 SetMakeParams( stack, &mp, &(aP->im) , &(aP->pano), color );
508 fD.func = execute_stack_new;
509 fD.param = stack;
510
511 //The "inverse" transform allows us to map pixel coordinates in each source image
512 //to their location in the output image.
513 // printf("INV-------------------------------------------\n");
514 SetInvMakeParams(invstack, &mpinv, &(aP->im), &(aP->pano), color);
515
516 // printf("-------------------------------------------\n");
517 finvD.func = execute_stack_new;
518 finvD.param = invstack;
519
520 //iterate over edges of input image and compute left/right/top/bottom-most coordinate
521 //in output image
522 //For equirectangular output projection covering 360/180, iterating over the
523 //edges of each input image isn't sufficient to determine ROI because an
524 //an interior point in an input image can be at the edge of ROI. More research
525 //needed here, but for now include some representative interior points as well.
526 for (y = 0; y <= TrPtr->src->height; y += 1) {
527
528 x_jump = (y==0 || y==TrPtr->src->height || abs(y - TrPtr->src->height/2)<=5) ? 1 : TrPtr->src->width/2;
529
530 for (x = 0; x <= TrPtr->src->width; x += x_jump) {
531 //convert source coordinates to cartesian coordinates (i.e. origin at center of image)
532 x_d = (double) x - sw2 ;
533 y_d = (double) y - sh2 ;
534
535 //Map the source image cartesian coordinate to the destination image cartesian coordinate
536 finvD.func( x_d, y_d, &Dx, &Dy, finvD.param);
537
538 #ifdef PANO_TEST_INVERSE
539 fD.func( Dx, Dy, &Dx2, &Dy2, fD.param);
540 {
541 int newX, newY;
542 if (!isnan(Dx2) && !isnan(Dy2)) {
543
544 newX = (int)(Dx2 + 0.5 + sw2);
545 newY = (int)(Dy2 + 0.5 + sh2);
546
547 #ifdef XXXXX
548 if (newX != x || newY != y) {
549 printf(" X,Y: %7.1f,%7.1f (%5d,%5d) -> OUT: %9.1f, %9.1f inv -> %9.1f %9.1f (%5d, %5d) -- error %5d,%5d\n",
550 x_d, y_d,
551 x,y,
552 Dx, Dy,
553 Dx2, Dy2,
554 newX, newY,
555 newX - x,
556 newY - y
557 );
558 }
559 #endif
560 // If this assertion fails, there is an error. The question is, how big? See the values above.
561 // it is possible that the error is so small that it does not matter.
562 /*
563 assert(fabs(newX-x) <= 20.0);
564 assert(fabs(newY-y) <= 20.0);
565 */
566 }
567
568 }
569 //printf(" IN1: %f,%f -> OUT: %f, %f inv -> %f %f (%d, %d)\n", x_d, y_d, Dx, Dy, Dx2, Dy2, (int)(Dx +0.5), (int)(Dy+0.5));
570 //printf(" IN3: %d,%d -> OUT: %f, %f (%d, %d)\n", x, y, Dx, Dy, (int)(Dx +0.5), (int)(Dy+0.5));
571 #endif
572
573 //Convert destination cartesian coordinate back to destination "screen" coordinates (i.e. origin at top left of image)
574 Dx += w2;
575 Dy += h2;
576
577 //Expand ROI if necessary
578 //I've observed that in some cases, the mapping function returns
579 //a value of "-1.#IND00". This is not a number, and probably indicates
580 //a divide by zero error somewhere in the mapping function. This should
581 //be solved, but, for now, discard this value and keep going
582 if (!isnan(Dx)) {
583 if ((int32_t)Dx < ROIRect->left) ROIRect->left = (int32_t)(Dx + 0.5);
584 if ((int32_t)Dx > ROIRect->right) ROIRect->right = (int32_t)(Dx + 0.5);
585 }
586 if (!isnan(Dy)){
587 if ((int32_t)Dy < ROIRect->top) ROIRect->top = (int32_t)(Dy + 0.5);
588 if ((int32_t)Dy > ROIRect->bottom) ROIRect->bottom = (int32_t)(Dy + 0.5);
589 }
590 }
591 }
592
593 // printf("ROI1: %ld,%ld - %ld, %ld\n", ROIRect->left, ROIRect->top, ROIRect->right, ROIRect->bottom);
594
595 // If the destination is wrapped and the ROI is at least 95% of the width
596 // Then ensure it is of the full width so no missing data is removed from either side.
597 if ( (TrPtr->mode & _wrapX) &&
598 ( (ROIRect->right-ROIRect->left) > (TrPtr->dest->width * 0.95) ) )
599 {
600 ROIRect->left = 0;
601 ROIRect->right = TrPtr->dest->width-1;
602 }
603
604 // Test for a zenith shot
605 if(ROIRect->top != 0)
606 {
607 Dx = 0;
608 Dy = - h2;
609 fD.func( Dx, Dy, &Dx2, &Dy2, fD.param);
610 {
611 if (!isnan(Dx2) && !isnan(Dy2)) {
612
613 x_d = (int)(Dx2 + 0.5 + sw2);
614 y_d = (int)(Dy2 + 0.5 + sh2);
615 // if x_d, y_d is inside the image then this is a zenith shot and should extent to the top
616 if (y_d >= 0 && y_d <= TrPtr->src->height &&
617 x_d >= 0 && x_d <= TrPtr->src->width )
618 {
619 ROIRect->top = 0;
620 }
621 }
622 }
623 }
624
625 // Test for a nadir shot
626 if(ROIRect->bottom != TrPtr->dest->height-1)
627 {
628 Dx = 0;
629 Dy = h2;
630 fD.func( Dx, Dy, &Dx2, &Dy2, fD.param);
631 {
632 if (!isnan(Dx2) && !isnan(Dy2)) {
633
634 x_d = (int)(Dx2 + 0.5 + sw2);
635 y_d = (int)(Dy2 + 0.5 + sh2);
636 // if x_d, y_d is inside the image then this is a zenith shot and should extent to the top
637 if (y_d >= 0 && y_d <= TrPtr->src->height &&
638 x_d >= 0 && x_d <= TrPtr->src->width )
639 {
640 ROIRect->bottom = TrPtr->dest->height-1;
641 }
642 }
643 }
644 }
645
646 //Reduce ROI if it extends beyond boundaries of final panorama region
647 if (ROIRect->left < 0) ROIRect->left =0;
648 if (ROIRect->top < 0) ROIRect->top =0;
649 if (ROIRect->right > (TrPtr->dest->width-1)) ROIRect->right = TrPtr->dest->width-1;
650 if (ROIRect->bottom > (TrPtr->dest->height-1)) ROIRect->bottom = TrPtr->dest->height-1;
651
652 // printf("ROI2: %ld,%ld - %ld, %ld\n", ROIRect->left, ROIRect->top, ROIRect->right, ROIRect->bottom);
653 }
654
655
656 // NO LONGER NEEDED
657
658 /**
659 * Populates the CropInfo struct with data about cropping of
660 * the TIFF file specified by filename
661 */
getCropInformation(char * filename,CropInfo * c)662 void getCropInformation(char *filename, CropInfo * c)
663 {
664
665 TIFF *tif = TIFFOpen(filename, "r");
666 if (tif == NULL) {
667 PrintError("getCropInformation: Could not open TIFF file");
668 }
669 else {
670 getCropInformationFromTiff(tif, c);
671 TIFFClose(tif);
672 }
673
674 }
675
676
677 #if 0
678
679 void setFullSizeImageParameters(pt_tiff_parms * imageParameters,
680 CropInfo * crop_info)
681 {
682 // Update the imageParameters so that the dimensions reflect the
683 // the size of the full-sized output image, (recorded in the crop_info struct)
684 imageParameters->imageLength = crop_info->full_height;
685 imageParameters->imageWidth = crop_info->full_width;
686 imageParameters->bytesPerLine =
687 imageParameters->imageWidth * (imageParameters->bitsPerPixel / 8);
688 }
689
690 #endif
691
692
panoCreatePanorama(fullPath ptrImageFileNames[],int counterImageFiles,fullPath * panoFileName,fullPath * scriptFileName)693 int panoCreatePanorama(fullPath ptrImageFileNames[], int counterImageFiles,
694 fullPath * panoFileName, fullPath * scriptFileName)
695 {
696
697 Image *currentImagePtr;
698 aPrefs *prefs;
699 int var01;
700 int var00;
701 int colourCorrection;
702
703 int lines;
704 fullPath *fullPathImages;
705 int loopCounter;
706 char var40[8];
707 char *tempString; // It looks like a char *temp;
708 char outputFileName[512];
709 #if 0
710 VRPanoOptions defaultVRPanoOptions;
711 #endif
712 char tmpStr[64]; // string
713 fullPath currentFullPath;
714 fullPath panoName; // according to documention: QTVR, PNG, PICT, TIFF, etc plus options...*/
715 fullPath tempScriptFile;
716 char output_file_format[256];
717 Image resultPanorama; //Output Image
718 Image image1; //Input Image
719
720 FILE *regFile;
721 char *regScript;
722 unsigned int regLen;
723 unsigned int regWritten;
724
725 pano_Tiff *tiffFile; //Output file...will be written during this function
726 TrformStr transform; //structure holds pointers to input and output images and misc other info
727
728 int ebx;
729
730 int croppedTIFFIntermediate = 0;
731 int croppedWidth = 0, croppedHeight = 0;
732 PTRect ROIRect;
733 unsigned int outputScanlineNumber = 0;
734
735 pano_ImageMetadata metadata;
736
737 /* Variables */
738 colourCorrection = 0; // can have values of 1 2 or 3
739 var00 = 0;
740 var01 = 0;
741
742 //Copy script line for line into a new temporary file
743 memcpy(&tempScriptFile, scriptFileName, sizeof(fullPath));
744 if (panoFileMakeTemp(&tempScriptFile) == 0) {
745 PrintError("Unable to create temporary file");
746 goto mainError;
747 }
748
749 panoTiffSetErrorHandler();
750
751 if ((regFile = fopen(tempScriptFile.name, "w")) == NULL) {
752 PrintError("Could not open temporary Scriptfile");
753 goto mainError;
754 }
755
756 if ((regScript = LoadScript(scriptFileName)) == 0) {
757 PrintError("Could not load ScriptFile");
758 fclose(regFile);
759 goto mainError;
760 }
761
762 regLen = strlen(regScript);
763
764 // Write script to temp file
765 regWritten = fwrite(regScript, 1, regLen, regFile);
766
767 // Make sure script was written completely
768 if (regWritten != strlen(regScript)) {
769 PrintError("Could not write temporary script");
770 fclose(regFile);
771 goto mainError;
772 }
773
774 fclose(regFile);
775
776 //Initialize members to zero
777 SetImageDefaults(&image1);
778 SetImageDefaults(&resultPanorama);
779
780 //transform structure holds input and output images, and some miscellaneous other information
781 memset(&transform, 0, sizeof(TrformStr));
782 transform.src = &image1; // Input image
783 transform.dest = &resultPanorama; // Output image
784 transform.mode = _honor_valid; // How to run transformation
785 transform.success = 1; // 1 success 0 failure
786
787 //Allocate space to hold fully qualified names of input images
788 if ((fullPathImages = malloc(counterImageFiles * 512)) == NULL) {
789 PrintError("Not enough memory");
790 goto mainError;
791 }
792
793 // This is the main processing loop...it iterates over each input image
794 // and maps the pixels in these input images into the output image(s)
795 for (loopCounter = 0; loopCounter < counterImageFiles; loopCounter++) {
796
797 // TODO
798
799 // the original PTstitcher logic is strange
800
801 // It processes a lot of data more than once. This part should really be done once for all images
802
803
804 currentImagePtr = &image1;
805
806 // Read the next adjust line (contains yaw, pitch, roll and other information)
807 // for one input image from the script file
808 if ((prefs = readAdjustLine(&tempScriptFile)) == 0) {
809 PrintError("Could not read Scriptfile");
810 goto mainError;
811 }
812
813 // printf("*********cut frame: should be zero for S and no crop %d\n", prefs->im.cP.cutFrame);
814
815 //New for PTMender...PTMender uses "cropped" TIFFs as its intermediate file
816 //format for all processing. In contrast, PTStitcher used full-size TIFF
817 //images for all intermediate processing. PTMender can still write "uncropped"
818 //final as needed.
819 //
820 //To the end user, PTMender appears to behave similarly to PTStitcher for
821 //TIFF_m and TIFF_mask formats, outputting a one "full-size" TIFF for each
822 //layer. However, the internal processing is done on cropped TIFFs which
823 //speeds things up considerably.
824 //
825 //An important improvement over PTStitcher is that the user can also explicitly
826 //requests cropped output for multi layer TIFF output by inlcluding
827 //"r:CROP" as part of the "p" line (e.g. n"TIFF_m r:CROP")
828 //
829 //Using cropped TIFF as the intermediate format significantly speeds up
830 //processing, with larger panos showing more dramatic increases in speed.
831 //It should also mean that the creation of the "flattened" formats will
832 //be significantly more memory-friendly, as the masking steps and PSD
833 //assembly steps won't need to load images the size of the output file
834 //into memory at once. Unless the PTMender is fed extremely large input
835 //images, all memory constraints should now be a thing of the past (MRDL - May 2006).
836
837 //croppedTIFFIntermediate determines if all intermediate processing is done
838 //with cropped or full size TIFF. There probably isn't much of a reason
839 //to ever disable this feature, other than for testing/debugging purposes.
840
841
842
843 colourCorrection = prefs->sBuf.colcorrect;
844 // This is a strange value:
845 // colourCorrection == (i & 3) + (i+1)*4;
846 // where i is the number of the reference image
847
848 assert(colourCorrection >= 0
849 && colourCorrection < (counterImageFiles + 1) * 4);
850 if (prefs->pano.cP.radial != 0) {
851
852 var00 = prefs->pano.cP.radial_params[0][2]; // what is this for, I have NO idea.
853 var00++;
854
855 } // begins 804a00e
856
857
858 if (prefs->pano.cP.horizontal != 0) {
859
860 // Colour correction in color only, not brightness
861
862 var01 = prefs->pano.cP.horizontal_params[0]; // 0x75c //[3] 3 colours x horizontal shift value
863 // at this point var01 contains the index to the image to use as anchor fo
864
865 var01++;
866
867 }
868
869 // Copy output pano name to panoName
870 memcpy(&panoName, &prefs->pano.name, sizeof(fullPath));
871 //memcpy(&global5640, &prefs->sBuf, sizeof(stBuf));
872
873 //panoName.name contains the n"XXX" value from the script "p" lines (e.g. n"TIFF_m" or n"QTVR w400 h300 c1")
874 tempString = panoName.name;
875 --tempString; /* nextWord does ++ before testing anything, this guarantess proper execution */
876 nextWord(output_file_format, &tempString);
877
878 if (strcmp(output_file_format, "TIFF_m") == 0 ) {
879 // CHeck if we are suppose to do cropped or uncropped
880 croppedTIFFIntermediate = 1;
881 if(strcmp(tempString, "") != 0) {
882 nextWord(output_file_format, &tempString);
883 if (strcmp(output_file_format, "") == 0 ||
884 strcmp(output_file_format, "r:CROP") == 0
885 ) {
886 // DO uncropped by default unless we are fisheye
887 if (prefs->im.format == _fisheye_circ) {
888 PrintError("Cropped output is unsupported for circular fisheye lenses. Ignored");
889 }
890 } else if (strcmp(output_file_format, "r:UNCROP") == 0) {
891 croppedTIFFIntermediate = 0;
892 } else {
893 PrintError("Unsupported option in TIFF_m output (%s). Ignored", output_file_format);
894 }
895 }
896 // DO uncropped by default unless we are fisheye
897 if (prefs->im.format == _fisheye_circ) {
898 croppedTIFFIntermediate = 0;
899 }
900 } else {
901 PrintError("No support for this output image format (%s). Output will be TIFF_m", output_file_format);
902 }
903 // enable this to avoid cropped tiffs. usually for testing
904 //croppedTIFFIntermediate = 0;
905
906
907
908 transform.interpolator = prefs->interpolator;
909 transform.gamma = prefs->gamma;
910 transform.fastStep = prefs->fastStep;
911 if( prefs->pano.hfov == 360.0 )
912 transform.mode |= _wrapX;
913
914 if (ptQuietFlag == 0) {
915 sprintf(tmpStr, "Converting Image %d / %d", (loopCounter + 1),
916 counterImageFiles);
917 Progress(_initProgress, tmpStr);
918 }
919
920 //Read input image into transform.src
921 if (panoImageRead(currentImagePtr, &ptrImageFileNames[loopCounter]) == 0) {
922 PrintError("Could not read input image [%s]", ptrImageFileNames[loopCounter].name);
923 goto mainError;
924 }
925
926 // printf("Ended reading INPUT image\n");
927
928 //This "masks" the input image so that some pixels are excluded from
929 //transformation routine during pixel remapping/interpolation
930
931 if (prefs->im.cP.cutFrame != 0) { // remove frame? 0 - no; 1 - yes
932 // THIS CODE is executed in crop C type only, but not in S type
933 // printf("To crop image\n");
934 if (CropImage(currentImagePtr, &(prefs->im.selection)) == 0) {
935 prefs->im.selection.left = 0;
936 prefs->im.selection.right = 0;
937 prefs->im.selection.bottom = 0;
938 prefs->im.selection.top = 0;
939 }
940 }
941 //setup width/height of input image
942 prefs->im.width = image1.width;
943 prefs->im.height = image1.height;
944
945 //Try to set reasonable values for output pano width and/or height if not
946 //specified as part of input (Do this only when processing first image in script)
947 if (loopCounter == 0) {
948
949 if (prefs->pano.width == 0) {
950 // if the pano did not set the width, then try to set it
951 if (prefs->im.hfov != 0.0) {
952 prefs->pano.width = prefs->im.width * prefs->pano.hfov / prefs->im.hfov;
953 prefs->pano.width /= 10; // Round to multiple of 10
954 prefs->pano.width *= 10;
955 }
956 }
957
958 if (prefs->pano.height == 0)
959 prefs->pano.height = prefs->pano.width / 2;
960
961 resultPanorama.height = prefs->pano.height;
962 resultPanorama.width = prefs->pano.width;
963
964 if (resultPanorama.height == 0 || resultPanorama.width == 0) {
965 PrintError("Please set Panorama width/height");
966 goto mainError;
967 }
968 } //End attempt at setting reasonable values for pano width/height
969
970
971 //printf("to set metadata\n");
972
973 //////////////////////////////////////////////////////////////////////
974 // Set metadata for output file
975
976 panoMetadataCopy(&metadata, &image1.metadata);
977
978 // The size of the image will change, so we have to update all the
979 // fields accordingly.
980 panoMetadataResetSize(&metadata,
981 resultPanorama.width,
982 resultPanorama.height);
983
984 metadata.imageNumber = loopCounter;
985 metadata.imageTotalNumber = counterImageFiles;
986 metadata.imageDescription = strdup(regScript);
987
988
989 // Set output width/height for output file
990 if (croppedTIFFIntermediate) {
991 getROI(&transform, prefs, &ROIRect);
992 //Dimensions determine size of TIFF file
993 croppedWidth = (ROIRect.right - ROIRect.left) + 1;
994 croppedHeight = (ROIRect.bottom - ROIRect.top) + 1;
995
996 panoMetadataSetAsCropped(&metadata,
997 croppedWidth, croppedHeight,
998 ROIRect.left, ROIRect.top);
999 }
1000
1001 panoMetadataSetCompression(&metadata, prefs->pano.name);
1002
1003 //The resultPanorama.selection determines which region of the output image
1004 //is iterated over during the main pixel-remapping processing logic. Much
1005 //of the image will be empty (black space) for any given input image. However,
1006 //if cropped output is selected, then only the region of interest (ROI) into
1007 //which this input image will be mapped is processed...this significantly
1008 //speeds up processing
1009 if (croppedTIFFIntermediate) {
1010 resultPanorama.selection.left = ROIRect.left;
1011 resultPanorama.selection.right = ROIRect.right + 1; // the right edge is actually the pixel NOT in the pano
1012 resultPanorama.selection.top = ROIRect.top;
1013 }
1014 else {
1015 resultPanorama.selection.left = 0;
1016 resultPanorama.selection.right = resultPanorama.width;
1017 resultPanorama.selection.top = 0;
1018 }
1019
1020 resultPanorama.bitsPerPixel = image1.bitsPerPixel;
1021 resultPanorama.bytesPerLine = metadata.bytesPerLine;
1022
1023 panoMetadataCopy(&resultPanorama.metadata, &metadata);
1024
1025 panoMetadataFree(&metadata);
1026
1027 //////End of set metadata
1028
1029
1030 /// CREATE OUTPUT FILE
1031
1032 // Copy the current output file name to he fullPathImages[loopCounter]
1033 memcpy(&fullPathImages[loopCounter], panoFileName, sizeof(fullPath));
1034
1035 // Create temporary file where output data wil be written
1036 if (panoFileMakeTemp(&fullPathImages[loopCounter]) == 0) {
1037 PrintError("Could not make Tempfile");
1038 goto mainError;
1039 }
1040
1041 // Populate currentFullPath.name with output file name
1042 GetFullPath(&fullPathImages[loopCounter], currentFullPath.name);
1043
1044 // Open up output file for writing...data will be written in TIFF format
1045
1046 if ((tiffFile = panoTiffCreate(currentFullPath.name,
1047 &resultPanorama.metadata)) == 0) {
1048 PrintError("Could not open %s for writing", currentFullPath.name);
1049 goto mainError;
1050 }
1051
1052 if (ptQuietFlag == 0) {
1053 if (Progress(_setProgress, "5") == 0) {
1054 panoTiffClose(tiffFile);
1055 remove(fullPathImages[loopCounter].name);
1056 return (-1);
1057 }
1058 }
1059
1060 //The output image is generated a few lines at a time to make efficient use
1061 //of limited memory...compute a reasonable number of lines to process (must
1062 //be at least 1, but no more than output height)
1063 lines = 500000 / resultPanorama.bytesPerLine;
1064
1065 if (lines == 0)
1066 lines = 1;
1067
1068 //Don't process more lines than are available
1069 if (lines >
1070 (croppedTIFFIntermediate ? croppedHeight : resultPanorama.height))
1071 lines =
1072 (croppedTIFFIntermediate ? croppedHeight : resultPanorama.
1073 height);
1074
1075 if ((resultPanorama.data =
1076 (unsigned char **) mymalloc(lines *
1077 resultPanorama.bytesPerLine)) ==
1078 NULL) {
1079 PrintError("Not enough memory for output panorama buffer");
1080 exit(1);
1081 }
1082 //NB resultPanorama.selection.bottom is actually one pixel beyond last row with data.
1083 resultPanorama.selection.bottom =
1084 resultPanorama.selection.top + lines;
1085
1086 // printf("bits per pixel %d\n", resultPanorama.bitsPerPixel);
1087 // printf("cropped %d\n", croppedTIFFIntermediate);
1088
1089 if (resultPanorama.bitsPerPixel != image1.bitsPerPixel) {
1090 PrintError
1091 ("All source images must have the same number of bits per pixel.");
1092 exit(1);
1093 }
1094
1095 //Copy all position related data (yaw, pitch, roll, etc) for input image to currentImagePtr
1096 CopyPosition(currentImagePtr, &(prefs->im));
1097
1098 //image1.selection determines how much of the input image to be
1099 //included during main pixel remapping logic
1100 image1.selection.top = prefs->im.selection.top;
1101 image1.selection.bottom = prefs->im.selection.bottom;
1102 image1.selection.left = prefs->im.selection.left;
1103 image1.selection.right = prefs->im.selection.right;
1104
1105 /*
1106 printf("****** Image selection hfov %f, %d %d %d %d \n", image1.hfov, image1.selection.top,
1107 image1.selection.bottom,
1108 image1.selection.left,
1109 image1.selection.right);
1110 */
1111
1112 CopyPosition(&resultPanorama, &(prefs->pano));
1113
1114 //Set image data outside selection region to zeros
1115
1116 Clear_Area_Outside_Selected_Region(currentImagePtr);
1117
1118 //pano.width and height must be equal to the full canvas size (not the
1119 //size of the cropped output image...if selected) in order for the pixel
1120 //remapping logic to work correctly.
1121 prefs->pano.width = resultPanorama.width;
1122 prefs->pano.height = resultPanorama.height;
1123
1124 //Iterate over the output image multiple lines at a time, remapping pixels
1125 //from the input image into the output image, and writing data to an
1126 //output TIFF file. Finish iterating when we reach the bottom of the
1127 //output image (or, in the case of a cropped file, the bottom of the
1128 //output ROI).
1129 outputScanlineNumber = 0;
1130 while (resultPanorama.selection.top <
1131 (croppedTIFFIntermediate ? ROIRect.bottom +
1132 1 : resultPanorama.height)) {
1133
1134 // Call the main pixel remapping routine...all the interpolation happens here
1135
1136 /*
1137 printf("Prefs: %f\n", prefs->pano.hfov);
1138 printf("Prefs im: hvof %f, yaw %f pitch %f, roll %f\n", prefs->im.hfov, prefs->im.yaw, prefs->im.pitch, prefs->im.roll);
1139 printf("Prefs pano: hvof %f, vfov %f pitch %f, roll %f\n", prefs->pano.hfov, prefs->pano.yaw, prefs->pano.pitch, prefs->pano.roll);
1140 printf("Prefs Interpolator %d:\n", prefs->interpolator);
1141 printf("Prefs Gamma %d:\n", prefs->gamma);
1142 printf("Prefs FastT %d:/n, prefs->fastStep);
1143 */
1144
1145 MyMakePano(&transform, prefs, loopCounter);
1146
1147 if (transform.success == 0) { // Error
1148 PrintError("Error converting image");
1149 goto mainError;
1150 }
1151
1152 //Reverse byte order before writing out to TIFF file
1153 ARGtoRGBAImage(&resultPanorama);
1154
1155 //Write calculated data rows to TIFF file one row (aka "scanline") at a time
1156 for (ebx = 0;
1157 ebx <
1158 resultPanorama.selection.bottom -
1159 resultPanorama.selection.top; ebx++) {
1160 if (TIFFWriteScanline(tiffFile->tiff,
1161 *resultPanorama.data + (resultPanorama.bytesPerLine * ebx),
1162 outputScanlineNumber, 1) != 1) {
1163 PrintError("Unable to write to TIFF file\n");
1164 return -1;
1165 }
1166
1167 outputScanlineNumber++;
1168 }
1169
1170 if (ptQuietFlag == 0) {
1171
1172 //Update progress bar
1173 if (croppedTIFFIntermediate)
1174 sprintf(tmpStr, "%d",
1175 (int) ((resultPanorama.selection.bottom -
1176 ROIRect.top) * 100 / croppedHeight));
1177 else
1178 sprintf(tmpStr, "%d",
1179 (int) (resultPanorama.selection.bottom * 100 /
1180 resultPanorama.height));
1181
1182 if (Progress(_setProgress, tmpStr) == 0) {
1183 // Cancelled by the user
1184 panoTiffClose(tiffFile);
1185 remove(tempScriptFile.name);
1186 remove(fullPathImages[loopCounter].name);
1187 return (-1);
1188 }
1189 }
1190
1191 //specify the next batch of rows to be processed
1192 resultPanorama.selection.top = resultPanorama.selection.bottom;
1193 resultPanorama.selection.bottom =
1194 resultPanorama.selection.top + lines;
1195
1196 //Be careful at boundary...end of image
1197 if (resultPanorama.selection.bottom >
1198 (croppedTIFFIntermediate ? ROIRect.bottom +
1199 1 : resultPanorama.height))
1200 resultPanorama.selection.bottom =
1201 (croppedTIFFIntermediate ? ROIRect.bottom +
1202 1 : resultPanorama.height);
1203 }
1204
1205 panoTiffClose(tiffFile);
1206
1207 #ifdef UNCROP_FISHEYES
1208 if (croppedTIFFIntermediate == 0) {
1209 // We can't process (yet) all files in cropped mode
1210 // To quite the roar from the masses let them think we
1211 // do. I wonder how long it will take for them to notice. Placebo effect?
1212 pano_cropping_parms croppingParms;
1213 bzero(&croppingParms, sizeof(croppingParms));
1214
1215 if (panoTiffCrop(currentFullPath.name, currentFullPath.name, &croppingParms) == 0) {
1216 PrintError("Unable to write output file %s", currentFullPath.name);
1217 remove(tempScriptFile.name);
1218 return (-1);
1219 }
1220 }
1221 #endif
1222
1223 //////////////////////////////////////////////////////////////////////
1224 panoImageDispose(&image1);
1225
1226 // The memory for td and ts was allocated in morpher.c with malloc
1227 // (not myMalloc), so we need to use free (not myFree)
1228 if (prefs->td != NULL) {
1229 free((void **) prefs->td);
1230 }
1231
1232 if (prefs->ts != NULL) {
1233 free((void **) prefs->ts);
1234 }
1235 free(prefs);
1236
1237 panoImageDispose(&resultPanorama);
1238
1239 } //End of main image processing loop
1240
1241 if (!ptQuietFlag)
1242 Progress(_disposeProgress, "");
1243
1244 // This is the end of the pixel remapping for all input images.
1245 // At this point we should have a collection of TIFF files containing
1246 // the warped input images. For TIFF_m format this is all we need. For
1247 // other formats, we may need to do extra work (feathering, flattening, etc.)
1248
1249 //----------------------------------------------------------------------
1250
1251 remove(tempScriptFile.name);
1252
1253 panoImageDispose(&resultPanorama);
1254 panoImageDispose(&image1);
1255
1256 #if 0
1257 // NO LONGER SUPPORTED IN THIS FUNCTION. IT SHOULD BE REMOVED IN THE FUTURE
1258
1259 // These functions are to correct and/or brightness. They are not required for
1260 // panoramas that do not need any brightness adjustments. Moreover, Dersch
1261 // was not fully satisfied with the quality of results obtained from
1262 // using these functions, and knew that they could be significantly
1263 // improved. In general, I think it best to avoid using these features,
1264 // and doing any color/brightness adjustments manually either before
1265 // or after stitching. While these functions work OK for some images, some
1266 // of the time, they can produce some obviously wrong results in some
1267 // circumstances...perhaps an area for future improvement, but probably not
1268 // as important a feature (now that we have multi-resolution splining
1269 // software like Enblend) as when Desrch first added these (MRDL).
1270
1271 if (var00 != 0) {
1272 ColourBrightness(fullPathImages, fullPathImages, counterImageFiles,
1273 var00 - 1, 1, 0);
1274 }
1275
1276 if (var01 != 0) { //
1277 // fprintf(stderr, "This type of correction... 1\n");
1278 ColourBrightness(fullPathImages, fullPathImages, counterImageFiles,
1279 var01 - 1, 2, 0);
1280 } //
1281
1282 if (colourCorrection != 0) {
1283 // fprintf(stderr, "This type of correction... 2\n");
1284 ColourBrightness(fullPathImages, fullPathImages, counterImageFiles,
1285 (colourCorrection / 4) - 1, 0, 0);
1286 }
1287
1288 SetVRPanoOptionsDefaults(&defaultVRPanoOptions);
1289
1290 /* Soo, at this point we have skipped the first word of the panorama:
1291 # n"QTVR w400 h300 c1" additional viewer options in a quoted string together with format
1292 # the following options are recognized:
1293 # w(width) and h(height) of viewer window (only QTVR on Macs)
1294 # c(codec: 0-JPEG, 1-Cinepak, 2-Sorenson) (only QTVR on Macs)
1295 # q(codec quality):
1296 # 0-high,1-normal,2-low QTVR on Macs
1297 # 0-100(highest) on other jpeg-formats (PAN, IVR, IVR_java, VRML)
1298 # g progressive jpeg (0-no, 1-yes) (PAN, IVR, IVR_java, VRML)
1299 # Optimized JPEG (0-on(default), 2-disabled), (3-progressive with optimized disabled)
1300 # p initial pan angle ( QTVR on Macs, VRML, IVR)
1301 # v field of view (QTVR, VRML, IVR)
1302 # Many more options can be set by editing the viewer scripts
1303 */
1304 //int getVRPanoOptions( VRPanoOptions *v, char *line )
1305
1306 getVRPanoOptions(&defaultVRPanoOptions, tempString);
1307 #endif
1308
1309 // We have to add "masks" to the images before finishing...
1310
1311 if (ptQuietFlag == 0)
1312 Progress(_initProgress, "Writing Output Images");
1313
1314 for (loopCounter = 0; loopCounter < counterImageFiles; loopCounter++) {
1315
1316 if (ptQuietFlag == 0) {
1317 sprintf(tmpStr, "%d",
1318 (100 * loopCounter) / counterImageFiles);
1319 if (Progress(_setProgress, tmpStr) == 0) {
1320 return (1);
1321 }
1322 }
1323
1324 strcpy(outputFileName, panoFileName->name);
1325 sprintf(var40, "%04d", loopCounter);
1326 strcat(outputFileName, var40);
1327 panoReplaceExt(outputFileName, ".tif");
1328 // remove output file, if it exists.
1329 if (panoSingleFileExists(outputFileName)) {
1330 remove(outputFileName);
1331 }
1332 rename(fullPathImages[loopCounter].name, outputFileName);
1333
1334 }
1335 free(fullPathImages);
1336
1337
1338 if (ptQuietFlag == 0) {
1339 Progress(_setProgress, "100%");
1340 Progress(_disposeProgress, "");
1341 }
1342
1343 free(regScript);
1344 return (0);
1345
1346 // FUNCTION ENDS HERE
1347
1348 mainError:
1349 free(regScript);
1350
1351 return (-1);
1352 }
1353
1354
1355 #if 0
1356
1357 // THIS_CODE_IS_NO_LONGER_SUPPORTED. IT SHOULD BE REMOVED IN THE FUTURE
1358
1359 if (counterImageFiles > 1) {
1360
1361 // There is no point in adding stitching masks for just one image
1362 //printf("Creating seams******************\n");
1363
1364 if (panoStitchReplaceMasks(fullPathImages, fullPathImages, counterImageFiles,
1365 feather) != 0) {
1366 PrintError("Could not create stitching masks");
1367 goto mainError;
1368 }
1369 }
1370
1371
1372
1373 /************ OUTPUT FORMATS: Multiple TIFF ***************/
1374 // TIFF_m and TIFF_mask...just rename the intermediate files
1375 // that we've already computed with numbers (e.g. img0000.tif, img0001.tif, etc.)
1376 // and we are finished processing.
1377 if (strcmp(output_file_format, "TIFF_m") == 0
1378 || strcmp(output_file_format, "TIFF_mask") == 0) {
1379
1380
1381 if ((croppedTIFFIntermediate != 0 && croppedTIFFOutput != 0) ||
1382 (croppedTIFFIntermediate == 0 && croppedTIFFOutput == 0)) {
1383 // if intermediate and output formats are the same, then just rename and quit
1384 rename(fullPathImages[loopCounter].name, outputFileName);
1385 }
1386 else if (croppedTIFFIntermediate != 0 && croppedTIFFOutput == 0) {
1387 // if cropped intermediate, but we want uncropped output, then uncrop
1388 if (!panoUnCropTiff
1389 (fullPathImages[loopCounter].name, outputFileName)) {
1390 return (1);
1391 }
1392 remove(fullPathImages[loopCounter].name);
1393 }
1394 else {
1395 // only other option is to use uncropped files as intermediate, and want
1396 // cropped as output. This is (a) a waste of time and (b) not supported.
1397 // Show error, but be nice and rename existing images anyway
1398 PrintError
1399 ("Cropped output files cannot be created from uncropped intermediate files\n\nWriting uncropped output: %s",
1400 outputFileName);
1401 rename(fullPathImages[loopCounter].name, outputFileName);
1402 }
1403
1404
1405 } // end of for loop
1406 free(fullPathImages);
1407
1408 if (ptQuietFlag == 0) {
1409 Progress(_setProgress, "100%");
1410 Progress(_disposeProgress, "");
1411 }
1412 return (0);
1413 }
1414
1415 //printf("To start creating the output files\n");
1416
1417 /************ OUTPUT FORMATS: Layered PSD ***************/
1418 // Layered PSD is less simple...we need to assemble the existing
1419 // intermediate files into a layered photoshop document
1420 if (strcmp(output_file_format, "PSD_nomask") == 0
1421 || strcmp(output_file_format, "PSD_mask") == 0
1422 ) {
1423 panoReplaceExt(panoFileName->name, ".psd");
1424
1425 if (panoCreatePSD(fullPathImages, counterImageFiles, panoFileName, 0) != 0) {
1426 PrintError("Error creating PSD file");
1427 return (-1);
1428 }
1429
1430 for (loopCounter = 0; loopCounter < counterImageFiles; loopCounter++) {
1431 remove(fullPathImages[loopCounter].name);
1432 }
1433
1434 free(fullPathImages);
1435 return (0);
1436 }
1437
1438
1439 /************ OUTPUT FORMATS: Flattened files ***************/
1440 // All other formats require us to "flatten" the intermediate layers into
1441 // one final document...general approach is to flatten to a single TIFF file,
1442 // and then convert this to the desired output file format (e.g. JPEG, PNG, etc.)
1443 if (counterImageFiles > 1)
1444 {
1445
1446 if (!panoFlattenTIFF
1447 (fullPathImages, counterImageFiles, &fullPathImages[0], TRUE))
1448 {
1449 PrintError("Error while flattening TIFF-image");
1450 goto mainError;
1451 }
1452
1453 }
1454 panoReplaceExt(panoFileName->name, ".tif");
1455 rename(fullPathImages[0].name, panoFileName->name);
1456
1457 free(fullPathImages);
1458
1459 //Desired output format is TIFF...no further conversion needed
1460 if (strcmp(output_file_format, "TIFF") == 0
1461 || strcmp(output_file_format, "TIF") == 0)
1462 return (0);
1463
1464
1465 //Read back in again so we can convert to final desired format
1466 if (panoImageRead(&resultPanorama, panoFileName) == 0) {
1467 PrintError("Could not read result image %s", panoFileName->name);
1468 goto mainError;
1469 }
1470
1471 remove(panoFileName->name);
1472
1473 if (strcmp(output_file_format, "QTVR") == 0)
1474 return Create_QTVR(&resultPanorama, panoFileName);
1475
1476 if (strcmp(output_file_format, "IVR_java") == 0) {
1477 if (panoProjection == 1)
1478 return Unknown03(&resultPanorama, panoFileName);
1479 else
1480 return Unknown02(&resultPanorama, panoFileName);
1481 }
1482
1483 if (strcmp(output_file_format, "VRML") == 0)
1484 return Unknown05(&resultPanorama, panoFileName);
1485
1486 if (strncmp(output_file_format, "IVR", 3) == 0) { // compare first 3 characters of it // end at 804ae10
1487 if (panoProjection == 1)
1488 return Unknown01(&resultPanorama, panoFileName);
1489 else
1490 return Create_LP_ivr(&resultPanorama, panoFileName);
1491 }
1492
1493 if (strcmp(output_file_format, "PAN") == 0) { //
1494 return Unknown04(&resultPanorama, panoFileName);
1495 } // 804ae10
1496
1497 if (strcmp(output_file_format, "JPEG") == 0
1498 || strcmp(output_file_format, "JPG") == 0) {
1499 if (!ptQuietFlag) {
1500 char temp[100];
1501
1502 sprintf(temp, "Creating JPEG (quality %d jpegProgressive %d)\n",
1503 defaultVRPanoOptions.cquality,
1504 defaultVRPanoOptions.progressive);
1505
1506 Progress(_initProgress, temp);
1507 }
1508 panoReplaceExt(panoFileName->name, ".jpg");
1509 return writeJPEG(&resultPanorama, panoFileName,
1510 defaultVRPanoOptions.cquality,
1511 defaultVRPanoOptions.progressive);
1512 }
1513
1514
1515 if (strcmp(output_file_format, "PSD") == 0) {
1516 panoReplaceExt(panoFileName->name, ".psd");
1517 return (writePSD(&resultPanorama, panoFileName));
1518
1519 }
1520
1521 if (strcmp(output_file_format, "PNG") == 0) {
1522 panoReplaceExt(panoFileName->name, ".PNG");
1523 return (writePNG(&resultPanorama, panoFileName));
1524 }
1525
1526 PrintError("Panorama output format not supported: %s",
1527 output_file_format);
1528
1529 #endif
1530
1531
1532
1533 /*
1534 * Because this function can be called with a directory name with a period
1535 * inside it (e.g. "c:\dir\another.dir\filewithoutextension") then we need to
1536 * make sure that the . happens after the last \ otherwise we'd truncate
1537 * the directory name rather than replacing the extension
1538 */
panoReplaceExt(char * filename,char * extension)1539 void panoReplaceExt(char *filename, char *extension)
1540 {
1541 char *dot_pos = strrchr(filename, '.');
1542 char *path_sep_win = strrchr(filename, '\\');
1543 char *path_sep_unix = strrchr(filename, '/');
1544 char *path_sep = (path_sep_unix == NULL ? path_sep_win : path_sep_unix );
1545
1546 if (dot_pos != NULL && (path_sep == NULL || dot_pos>path_sep)) {
1547 strcpy(dot_pos, extension);
1548 }
1549 else {
1550 strcat(filename, extension);
1551 }
1552 return;
1553 }
1554
1555
1556
1557
1558
panoFlattenTIFF(fullPath * fullPathImages,int counterImageFiles,fullPath * outputFileName,int removeOriginals)1559 int panoFlattenTIFF(fullPath * fullPathImages, int counterImageFiles,
1560 fullPath * outputFileName, int removeOriginals)
1561 {
1562
1563 pano_Tiff **tiffFileHandles;
1564 pano_Tiff *outputFile;
1565
1566 unsigned char **imageDataBuffers;
1567 unsigned char *resultBuffer;
1568
1569
1570 fullPath tmpFullPath;
1571 char tmpFilename[512];
1572
1573
1574 pano_CropInfo *cropInfo;
1575 unsigned int linesPerPass;
1576 pano_ImageMetadata *outputMetadata;
1577
1578 unsigned int i;
1579 unsigned int offsetBeforeThisPass = 0;
1580 int linesLeft = 0;
1581 unsigned int linesToRead;
1582 int rowInPass;
1583 int inputImageRowIndex;
1584 int outputImageRowIndex;
1585 unsigned char *pixelPtr;
1586
1587 //Open up all intermediate TIFF files at once
1588
1589 assert(fullPathImages != NULL);
1590 assert(counterImageFiles > 1);
1591 assert(outputFileName != NULL);
1592
1593 tiffFileHandles = calloc(counterImageFiles, sizeof(pano_Tiff));
1594
1595 if (tiffFileHandles == NULL) {
1596 PrintError("Not enough memory");
1597 return 0;
1598 }
1599
1600 for (i = 0; (int) i < counterImageFiles; i++) {
1601
1602 if (GetFullPath(&fullPathImages[i], tmpFilename) != 0) {
1603 PrintError("Could not get filename");
1604 return 0;
1605 }
1606
1607 if ((tiffFileHandles[i] = panoTiffOpen(tmpFilename)) == NULL) {
1608 PrintError("Could not open TIFF-Layer %d", i);
1609 return 0;
1610 }
1611
1612 }
1613
1614 //////////////////////////////////////////////////////////////////////
1615
1616
1617 //modify "tmpFullPath" to contain the name of a new, empty temp file
1618 if (panoFileMakeTemp(&tmpFullPath) == 0) {
1619 PrintError("Could not make Tempfile");
1620 return 0;
1621 }
1622
1623 //copy the name of this new tmpFullPath into a string (tmpFilename)
1624 if (GetFullPath(&tmpFullPath, tmpFilename) != 0) {
1625 PrintError("Could not get filename");
1626 return 0;
1627 }
1628
1629 // Because the 0th intermediate TIFF file might be a "cropped" file, we
1630 // need to update the imageParameters so that the dimensions reflect the
1631 // the size of the full-sized output image, rather than one of the
1632 // (potentially) cropped intermediate files
1633
1634 if ((outputFile =
1635 panoTiffCreateUnCropped(tmpFilename,
1636 &tiffFileHandles[0]->metadata)) == 0) {
1637 PrintError("Could not create TIFF file");
1638 return 0;
1639 }
1640
1641 // Calculate number of lines to read at a time so that we are reading
1642 // approximately 500 KB at a time from each input file. This could be
1643 // memory intensive if we have an awful lot of images and not much memory,
1644 // but probably not a big problem for 99.9% of cases on 99.9% of machines.
1645 linesPerPass = 500000 / outputFile->metadata.bytesPerLine;
1646
1647 if (linesPerPass == 0)
1648 linesPerPass = 1;
1649
1650 outputMetadata = &outputFile->metadata;
1651
1652 // We dont need to read more lines that the size of the file
1653 if (outputMetadata->imageHeight < linesPerPass) {
1654 linesPerPass = outputMetadata->imageHeight;
1655 if (linesPerPass == 0) {
1656 PrintError
1657 ("Invalid image length in TIFF file. It might be corrupted");
1658 return -1;
1659 }
1660 }
1661
1662 // Create as many image data buffers as we have input files. Note that the
1663 // input buffers are as wide as the final output image, which may be more
1664 // than we technically need if the input images are cropped...it makes the
1665 // code simpler, however.
1666 imageDataBuffers = calloc(counterImageFiles, sizeof(unsigned char *));
1667
1668 for (i = 0; (int) i < counterImageFiles; i++) {
1669 imageDataBuffers[i] =
1670 calloc(linesPerPass * outputMetadata->bytesPerLine, 1);
1671 if (imageDataBuffers[i] == NULL) {
1672 PrintError("Not enough memory to allocate input buffers");
1673 return -1;
1674 }
1675 }
1676
1677 //we need one buffer to store output result
1678 resultBuffer = calloc(linesPerPass * outputMetadata->bytesPerLine, 1);
1679
1680 if (resultBuffer == NULL) {
1681 PrintError("Not enough memory to allocate output buffer");
1682 return -1;
1683 }
1684
1685 offsetBeforeThisPass = 0;
1686
1687 if (ptQuietFlag == 0) {
1688 Progress(_initProgress, "Flattening Image");
1689 }
1690
1691 // printf("To do %d lines\n", outputMetadata->imageHeight);
1692
1693 linesLeft = outputMetadata->imageHeight;
1694
1695 // Main flattening loop...iterate over input files, read some data from each,
1696 // combine into output buffer, write to file
1697 while (linesLeft > 0) {
1698
1699 linesToRead = (linesLeft > (int)linesPerPass) ? linesPerPass : linesLeft;
1700
1701 // iterate over each input file
1702 for (i = 0; (int) i < counterImageFiles; i++) {
1703 cropInfo = &(tiffFileHandles[i]->metadata.cropInfo);
1704
1705 // Get a few lines of data from this input file one row at a time
1706 for (rowInPass = 0; rowInPass < (int) linesToRead; rowInPass++) {
1707
1708 //figure out which row to read/write from input/output images
1709 outputImageRowIndex = offsetBeforeThisPass + rowInPass;
1710 inputImageRowIndex = outputImageRowIndex - cropInfo->yOffset;
1711
1712 //point to first byte on this row of the input buffer
1713 pixelPtr =
1714 imageDataBuffers[i] +
1715 (outputMetadata->bytesPerLine * rowInPass);
1716
1717 //clear out any old data, and fill with empty space (zeros)
1718 memset(pixelPtr, 0, outputMetadata->bytesPerLine);
1719
1720 // Only try to read data if we are reading from a row that exists in the
1721 // input image
1722 if (inputImageRowIndex >= 0
1723 && inputImageRowIndex < cropInfo->croppedHeight) {
1724 if (TIFFReadScanline
1725 (tiffFileHandles[i]->tiff,
1726 pixelPtr +
1727 (cropInfo->xOffset * outputMetadata->bytesPerPixel),
1728 inputImageRowIndex, 0) != 1) {
1729 PrintError("Error reading tiff file\n");
1730 return 0;
1731 }
1732 }
1733 }
1734 }
1735
1736 // printf("Passing offsetAfterThisPass [%d] of [%d] linesPerPass %d \n",offsetAfterThisPass, outputMetadata->imageHeight, linesPerPass);
1737
1738 if (ptQuietFlag == 0) {
1739 sprintf(tmpFilename, "%d",
1740 (offsetBeforeThisPass +
1741 linesToRead) * 100 / outputMetadata->imageHeight);
1742 if (Progress(_setProgress, tmpFilename) == 0)
1743 return 0;
1744 }
1745
1746 // FlattenImageSection
1747 panoStitchBlendLayers(imageDataBuffers, counterImageFiles, resultBuffer,
1748 linesToRead, outputMetadata->imageWidth,
1749 outputMetadata->bitsPerPixel,
1750 outputMetadata->bytesPerLine);
1751
1752 for (i = 0; i < linesToRead; i++) {
1753 if (TIFFWriteScanline
1754 (outputFile->tiff,
1755 resultBuffer + outputMetadata->bytesPerLine * i,
1756 offsetBeforeThisPass + i, 0) != 1) {
1757 PrintError("Unable to write TIFF to file\n");
1758 return 0;
1759 }
1760 }
1761
1762 offsetBeforeThisPass += linesToRead;
1763 linesLeft -= linesToRead;
1764
1765 }
1766
1767 if (!ptQuietFlag)
1768 Progress(_disposeProgress, "Done flattening.");
1769
1770 // printf("Lines read %d from %d\n", offsetBeforeThisPass,outputMetadata->imageHeight);
1771
1772 for (i = 0; (int) i < counterImageFiles; i++) {
1773 free(imageDataBuffers[i]);
1774 panoTiffClose(tiffFileHandles[i]);
1775 }
1776
1777 panoTiffClose(outputFile);
1778
1779 if (removeOriginals) {
1780 for (i = 0; (int) i < counterImageFiles; i++) {
1781 remove(fullPathImages[i].name);
1782 }
1783 }
1784
1785 rename(tmpFullPath.name, outputFileName->name);
1786
1787 free(tiffFileHandles);
1788
1789 free(imageDataBuffers);
1790 free(resultBuffer);
1791
1792 return 1;
1793
1794 }
1795
1796
1797
1798
1799 // the functionality of PTcrop and PTuncrop is essentially identical, except for
1800 // the function that they call
1801
panoCroppingMain(int argc,char * argv[],int operation,char * version,char * usage,char * defaultPrefix)1802 int panoCroppingMain(int argc,char *argv[], int operation, char *version, char *usage, char *defaultPrefix)
1803 {
1804 int opt;
1805 int ptForceProcessing = 0;
1806 int filesCount;
1807 int retVal;
1808 pano_cropping_parms croppingParms;
1809 char outputPrefix[MAX_PATH_LENGTH];
1810 int ptDeleteSources = 0;
1811 fullPath *ptrInputFiles = NULL;
1812 fullPath *ptrOutputFiles = NULL;
1813 int base;
1814 int i;
1815
1816 // Set defaults
1817 strcpy(outputPrefix, defaultPrefix);
1818 bzero(&croppingParms, sizeof(croppingParms));
1819
1820 printf("%s", version);
1821
1822 //Need enough space for a message to be returned if something goes wrong
1823
1824 while ((opt = getopt(argc, argv, "p:fqhx")) != -1) {
1825
1826 // o overwrite
1827 // h -> help
1828 // q -> quiet?
1829
1830 switch(opt) { // fhoqs f: 102 h:104 111 113 115 o:f:hsq
1831 case 'p':
1832 if (strlen(optarg) < MAX_PATH_LENGTH) {
1833 strcpy(outputPrefix, optarg);
1834 } else {
1835 PrintError("Illegal length for output prefix");
1836 return -1;
1837 }
1838 break;
1839 case 'f':
1840 ptForceProcessing = 1;
1841 break;
1842 case 'x':
1843 ptDeleteSources = 1;
1844 break;
1845 case 'q':
1846 ptQuietFlag = 1;
1847 break;
1848 case 'h':
1849 printf("%s",usage);
1850 exit(0);
1851 default:
1852 break;
1853 }
1854 }
1855 filesCount = argc - optind;
1856
1857 if (filesCount < 1) {
1858 PrintError("No files specified in the command line");
1859 printf("%s",usage);
1860 exit(0);
1861 }
1862 // Allocate memory for filenames
1863 if ((ptrInputFiles = calloc(filesCount, sizeof(fullPath))) == NULL ||
1864 (ptrOutputFiles = calloc(filesCount, sizeof(fullPath))) == NULL) {
1865 PrintError("Not enough memory");
1866 free(ptrInputFiles);
1867 free(ptrOutputFiles);
1868 return -1;
1869 }
1870
1871 // GET input file names
1872 base = optind;
1873 for (; optind < argc; optind++) {
1874 char *currentParm;
1875
1876 currentParm = argv[optind];
1877
1878 if (StringtoFullPath(&ptrInputFiles[optind-base], currentParm) !=0) { // success
1879 PrintError("Syntax error: Not a valid pathname");
1880 return(-1);
1881 }
1882 }
1883 // Generate output file names
1884 if (panoFileOutputNamesCreate(ptrOutputFiles, filesCount, outputPrefix) == 0) {
1885 return -1;
1886 }
1887
1888 if (!ptForceProcessing) {
1889 char *temp;
1890 if ((temp = panoFileExists(ptrOutputFiles, filesCount)) != NULL) {
1891 PrintError("Output filename exists %s. Use -f to overwrite", temp);
1892 return -1;
1893 }
1894 }
1895 if (! ptQuietFlag)
1896 printf("Cropping %d files\n", filesCount);
1897
1898 for (i=0; i< filesCount; i++) {
1899
1900 if (!ptQuietFlag) {
1901 PrintError("Processing %d reading %s creating %s", i, ptrInputFiles[i].name, ptrOutputFiles[i].name);
1902 }
1903 croppingParms.forceProcessing = ptForceProcessing;
1904 switch (operation) {
1905 case PANO_CROPPING_CROP:
1906 retVal = panoTiffCrop(ptrInputFiles[i].name, ptrOutputFiles[i].name, &croppingParms);
1907 break;
1908 case PANO_CROPPING_UNCROP:
1909 retVal = panoTiffUnCrop(ptrInputFiles[i].name, ptrOutputFiles[i].name, &croppingParms);
1910 break;
1911 default:
1912 PrintError("Illegal operation in panoCroppingMain. Programming error");
1913 exit(0);
1914 }
1915
1916
1917 if (! retVal ) {
1918 PrintError("Error cropping file %s", ptrInputFiles[i].name);
1919 return -1;
1920 }
1921 }
1922 if (ptDeleteSources) {
1923 panoFileDeleteMultiple(ptrInputFiles, filesCount);
1924 }
1925 if (ptrInputFiles != NULL)
1926 free(ptrInputFiles);
1927 if (ptrOutputFiles != NULL)
1928 free(ptrOutputFiles);
1929
1930 return 0;
1931 }
1932
1933
1934
panoPrintImage(char * msg,Image * im)1935 void panoPrintImage(char *msg, Image *im)
1936 {
1937 printf("-------------%s\n", msg);
1938 if (im != NULL) {
1939 printf(">>>Image format %d\n", (int)im->format);
1940 printf(">>>Roll %f\n", im->roll);
1941 printf(">>>Pitch %f\n", im->pitch);
1942 printf(">>>Yaw %f\n", im->yaw);
1943
1944 printf(">>>im->cP.shear %d\n", im->cP.shear);
1945 printf(">>>im->cP.tilt %d\n", im->cP.tilt);
1946 printf(">>>im->cP.tilt_x %f\n", im->cP.tilt_x);
1947 printf(">>>im->cP.tilt_y %f\n", im->cP.tilt_y);
1948 printf(">>>im->cP.tilt_z %f\n", im->cP.tilt_z);
1949 printf(">>>im->cP.tilt_scale %f\n", im->cP.tilt_scale);
1950
1951 printf(">>>im->cP.translation %d\n", im->cP.trans);
1952 printf(">>>im->cP.trans_x %f\n", im->cP.trans_x);
1953 printf(">>>im->cP.trans_y %f\n", im->cP.trans_y);
1954 printf(">>>im->cP.trans_z %f\n", im->cP.trans_z);
1955 printf(">>>im->cP.trans_yaw %f\n", im->cP.trans_yaw);
1956 printf(">>>im->cP.trans_pitch %f\n", im->cP.trans_pitch);
1957
1958 printf(">>>im->cP.test %d\n", im->cP.test);
1959 printf(">>>im->cP.test parm1 %f\n", im->cP.test_p0);
1960 printf(">>>im->cP.test parm2 %f\n", im->cP.test_p1);
1961 printf(">>>im->cP.test parm3 %f\n", im->cP.test_p2);
1962 printf(">>>im->cP.test parm4 %f\n", im->cP.test_p3);
1963 }
1964 printf("\n\n");
1965
1966 }
1967
1968