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 = &image;
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