1 /*
2  *  PTmender
3  *
4  *  Based on the program PTStitcher by Helmut Dersch.
5  *
6  *  Dec 2005
7  *
8  *  This program is free software; you can redistribute it and/or
9  *  modify it under the terms of the GNU General Public
10  *  License as published by the Free Software Foundation; either
11  *  version 2 of the License, or (at your option) any later version.
12  *
13  *  This software is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  *  General Public License for more details.
17  *
18  *  You should have received a copy of the GNU General Public License
19  *  along with this software; see the file COPYING.  If not, a copy
20  *  can be downloaded from http://www.gnu.org/licenses/gpl.html, or
21  *  obtained by writing to the Free Software Foundation, Inc.,
22  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23  *
24  *
25  *  Author: Daniel M German dmgerman at uvic doooot ca
26  *
27  */
28 
29 
30 // TODO
31 //    Create_Panorama requires some floating point assembly to be interpreted
32 
33 #define __DEBUG__
34 
35 #include <assert.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <sys/types.h>
40 
41 #ifndef _MSC_VER
42 #include <unistd.h>
43 #else
44 #include "compat_win32/getopt.h"
45 #endif
46 
47 #include "tiffio.h"
48 #include "filter.h"
49 #include "panorama.h"
50 
51 #include "PTmender.h"
52 #include "PTcommon.h"
53 #include "file.h"
54 #include "ColourBrightness.h"
55 
56 
57 // Global variables for the program
58 
59 
60 int ptDebug = 0;
61 
62 #define DEFAULT_OUTPUT_NAME "pano"
63 
64 #define PT_MENDER_VERSION  "PTmender Version " VERSION ", originally written by Helmut Dersch, rewritten by Daniel German\n"
65 
66 #define PT_MENDER_USAGE "PTmender [options] <script filename> <images>*\n\n"\
67                          "Options:\n"\
68                          "\t-o <prefix>\tPrefix for output filename, defaults to " DEFAULT_OUTPUT_NAME "\n"\
69                          "\t-q\t\tQuiet run\n"\
70                          "\t-h\t\tShow this message\n"\
71                          "\t-s\t\tSort the filenames provided in the command line in lexicographical order (and only in the command line)\n"\
72                          "\n\nIf no images are specified in the command line, then the 'i' lines are used. If 'i' lines do not contain "\
73                          "a valid filename then the 'o' lines are used.\n"\
74                          "\n"
75 
76 
77 static int hasPathInfo(char *aName);
78 static int panoMenderSortingFunction(const void *p1, const void *p2);
79 
80 
panoMenderDuplicateScriptFile(char * scriptFileName,char * script,fullPath * scriptPathName)81 static void panoMenderDuplicateScriptFile(char *scriptFileName, char *script, fullPath  *scriptPathName)
82 {
83     FILE* scriptFD;
84     int temp;
85 
86     strcpy(scriptPathName->name, scriptFileName);
87 
88     // Get a temp filename for the copy of the script
89     if (panoFileMakeTemp(scriptPathName) == 0) {
90         PrintError("Could not make Tempfile");
91         exit(1);
92     }
93 
94     // Copy the script
95     if ((scriptFD = fopen(scriptPathName->name, "w")) == NULL) {
96         PrintError("Could not open temporary Scriptfile");
97         exit(1);
98     }
99 
100     temp = fwrite(script, 1, (int) strlen(script), scriptFD);
101 
102     if (strlen(script) != temp) {
103         PrintError("Could not write temporary Scriptfile");
104         exit(1);
105     }
106 
107     fclose(scriptFD);
108 
109 }
110 
panoMenderSetFileName(fullPath * ptrImageFileName,char * name,fullPath * scriptFileName)111 void panoMenderSetFileName(fullPath *ptrImageFileName, char *name, fullPath *scriptFileName)
112 {
113     //Only prepend the path to the script to the filenames if the filenames
114     //don't already have path information
115     assert(ptrImageFileName != NULL);
116     assert(name != NULL);
117     assert(scriptFileName != NULL);
118 
119     if ( (hasPathInfo(name)) == 0 )
120 	strcpy(ptrImageFileName->name, scriptFileName->name);
121     else
122 	strcpy(ptrImageFileName->name, "");
123 
124     InsertFileName(ptrImageFileName, name);
125 }
126 
panoMenderImageFileNamesReadFromScript(fullPath ** ptrImageFileNames,fullPath * scriptFileName)127 static int panoMenderImageFileNamesReadFromScript(fullPath **ptrImageFileNames, fullPath *scriptFileName)
128 {
129     char *script;
130     AlignInfo alignInfo;
131     int counter;
132     int i;
133 
134     // We don't have any images yet. We read the Script and load them from it.
135     if (ptDebug) {
136 	fprintf(stderr, "Loading script [%s]\n", scriptFileName->name);
137     }
138 
139     script = LoadScript(scriptFileName);
140 
141     if (script == NULL) {
142 	PrintError("Could not load script [%s]", scriptFileName->name);
143 	return -1;
144     }
145 
146     // parse input script and set up an array of input file names
147     if (ParseScript(script, &alignInfo) != 0) {
148 	// print error
149 
150 	PrintError("Panorama script parsing error");
151 	return 0;
152     }
153 
154     // The parser of panotools is really broken. To retrieve each
155     // input filename it reads the file,
156     // finds the first filename, then removes it, and writes the rest of the file again
157     // This is done recursively
158 
159     counter = alignInfo.numIm;
160 
161     if (counter != 0) {
162 
163 	// Try to find filenames in input section
164 	if (ptDebug) {
165 	    fprintf(stderr, "Found %d images in script file in INPUT section\n", counter);
166 	}
167 	// Allocate their space
168 	if ((*ptrImageFileNames = malloc(512 * counter)) == NULL) {
169 	    PrintError("Not enough memory");
170 	    exit(1);
171 	}
172 
173 	//Iterate over input images and populate input filename array
174 	for (i = 0; i < counter; i ++) {
175 	    //If the image filenames don't appear to have any path information, then
176 	    //prepend the path to the script (if any) that was specified on the
177 	    //command line (Note: this was the only behavior in the original
178 	    //PTStitcher.  It has been moved into this conditional block because
179 	    //the script path could get prepended to an already fully qualified
180 	    //filename...not very useful.
181 	    if (ptDebug) {
182 		fprintf(stderr, "Processing image [%s] from 'i' line %d\n", alignInfo.im[i].name, i);
183 	    }
184 
185 	    panoMenderSetFileName(&((*ptrImageFileNames)[i]), alignInfo.im[i].name, scriptFileName);
186 
187 	    if (ptDebug) {
188 		fprintf(stderr, "Reading image filename [%s] from 'i' line %d\n", (*ptrImageFileNames)[i].name, i);
189 	    }
190 
191 
192 	}
193 	DisposeAlignInfo(&alignInfo);
194     }
195     if (counter == 0) {
196 	// Sometimes the names of the images are not in the 'o' line, then assume they are
197 	// in the 'i' line.
198 
199 
200 	// create a temporary copy we can overwrite
201 	fullPath scriptPathName;
202 
203 	counter = numLines(script, 'o');
204 
205 	if (counter == 0) {
206 	    PrintError("No images found input file script file (there are no 'o' lines nor 'i' lines");
207 	    exit(1);
208 	}
209 	// Allocate their space
210 	if ((*ptrImageFileNames = malloc(512 * counter)) == NULL) {
211 	    PrintError("Not enough memory");
212 	    exit(1);
213 	}
214 
215 
216 	panoMenderDuplicateScriptFile(scriptFileName->name, script, &scriptPathName);
217 
218 	for (i = 0; i < counter; i++) {
219 	    aPrefs* preferences;
220 	    if ( (preferences = readAdjustLine(&scriptPathName)) == NULL) {
221 		PrintError("No 'i' line for image number %d", i);
222 		exit(1);
223 	    }
224 
225 	    if (ptDebug) {
226 		fprintf(stderr, "Processing image [%s] from 'o' line %d\n", preferences->im.name, i);
227 	    }
228 
229 	    panoMenderSetFileName(&((*ptrImageFileNames)[i]), preferences->im.name, scriptFileName);
230 
231 	    if (ptDebug) {
232 		fprintf(stderr, "Reading image filename [%s] from 'i' line %d\n",
233 			(*ptrImageFileNames)[i].name, i);
234 	    }
235 	    if (preferences->td != NULL)
236 		free(preferences->td);
237 
238 	    if (preferences->ts != NULL)
239 		free(preferences->ts);
240 
241 	    free(preferences);
242 
243 	} // end of for (i = 0; i < counter; i++) {
244 	free(script);
245 
246 	remove(scriptPathName.name);
247 
248     }
249     return counter;
250 }
251 
252 
main(int argc,char * argv[])253 int main(int argc,char *argv[])
254 {
255     int counter;
256     int sort = 0; // do we sort command line filenames?
257     fullPath *ptrImageFileNames;
258 
259     fullPath scriptFileName;
260     fullPath panoFileName;
261 
262     int opt;
263 
264     char *currentParm;
265     ptrImageFileNames = NULL;
266     counter = 0;
267     strcpy(panoFileName.name, DEFAULT_OUTPUT_NAME);
268     strcpy(scriptFileName.name, "");
269 
270     printf(PT_MENDER_VERSION);
271 
272     while ((opt = getopt(argc, argv, "o:f:hsqd")) != -1) {
273 
274 	// o       -> set output file
275 	// h       -> help?
276 	// q       -> quiet?
277 
278 	switch(opt) {
279 
280 	case 'o':   // specifies output file name
281 	    if (StringtoFullPath(&panoFileName, optarg) != 0) {
282 		PrintError("Syntax error: Not a valid pathname");
283 		return(-1);
284 	    }
285 	    break;
286 	case 's':
287 	    sort = 1;
288 	    break;
289 	case 'd':
290 	    ptDebug = 1;
291 	    break;
292 	case 'q':
293 	    ptQuietFlag = 1;
294 	    break;
295 
296 	case 'h':
297 	    PrintError(PT_MENDER_USAGE);
298 	    return -1;
299 
300 	default:
301 	    break;
302 	}
303     }
304 
305 
306     if (ptDebug) {
307 	fprintf(stderr, "Number of options to process %d\n", argc- optind);
308     }
309 
310     if (optind - argc == 0) {
311 	PrintError(PT_MENDER_USAGE);
312 	return -1;
313     }
314 
315     // First we get the name of the script
316     if (StringtoFullPath(&scriptFileName, argv[optind]) !=0) { // success
317 	PrintError("Syntax error: Not a valid pathname");
318 	PrintError(PT_MENDER_USAGE);
319 	return(-1);
320     }
321 
322     // Check if we got a filename
323     if (strlen(scriptFileName.name) == 0) {
324 	PrintError("No script name provided\n");
325 	PrintError(PT_MENDER_USAGE);
326 	return -1;
327 
328     }  // end of if (scriptFileName[0] != 0) {
329 
330     if (ptDebug){
331 	fprintf(stderr,"Script filename %s\n", scriptFileName.name);
332     }
333 
334 
335 
336     // optionally  we receive a list of filenames
337     optind ++;
338     currentParm = NULL;
339     while (optind < argc  ) {
340 	currentParm = argv[optind];
341 	optind++;
342 	counter++;
343 
344 	if((ptrImageFileNames = realloc(ptrImageFileNames, counter * 512)) == NULL) {
345 	    PrintError("Not enough memory");
346 	    exit(1);
347 	}
348 
349 
350 	if (StringtoFullPath(&ptrImageFileNames[counter-1], currentParm) != 0) {
351 	    PrintError("Syntax error: Not a valid pathname");
352 	    return(-1);
353 	}
354 	if (ptDebug){
355 	    fprintf(stderr,"Getting file from command line %s index %d\n", ptrImageFileNames[counter-1].name, counter);
356 	}
357     } // end of while loop  while (optind < argc  ) {
358 
359     if (sort && counter > 0) {
360 	// We have filenames that need to be sorted
361 	qsort(ptrImageFileNames, counter, 512, panoMenderSortingFunction);;
362     }
363 
364 
365     // Handle some lack of information
366     if (strlen(panoFileName.name) == 0) {
367 	PrintError("No output filename specified\n");
368 	PrintError(PT_MENDER_USAGE);
369 	return -1;
370     }
371 
372     if (counter == 0) {
373 	counter = panoMenderImageFileNamesReadFromScript(&ptrImageFileNames, &scriptFileName);
374     }
375     if (counter == 0) {
376 	PrintError("No images found input file script file (there are no 'o' lines nor 'i' lines");
377 	exit(1);
378     }
379 
380     // By now we should have loaded up the input filename array, the output
381     // panorama name, and the name of the script file (copied to a temporary
382     // directory).  Now we can create the output image.
383     return panoCreatePanorama(ptrImageFileNames, counter, &panoFileName, &scriptFileName);
384 
385 }
386 
387 
388 //////////////////////////////////////////////////////////////////////
389 
Filename(fullPath * path)390 char* Filename(fullPath* path)
391 {
392     char *temp;
393     if ((temp = strrchr(path->name, PATH_SEP)) != NULL) {
394 	temp++;
395     } else {
396 	temp = path->name;
397     }
398     return temp;
399 }
400 
401 
hasPathInfo(char * aName)402 static int hasPathInfo(char *aName)
403 {
404     return ((strchr(aName, PATH_SEP) == NULL) ? 0 : 1);
405 }
406 
panoMenderSortingFunction(const void * p1,const void * p2)407 static int panoMenderSortingFunction(const void *p1, const void *p2)
408 {
409   return strcmp(p1, p2);
410 }
411