00001
00026 #include <hugin_config.h>
00027 #include <hugin_version.h>
00028 #include <fstream>
00029 #include <sstream>
00030
00031 #include <algorithm>
00032 #include <cctype>
00033 #include <string>
00034
00035 #include <vigra/error.hxx>
00036 #include <vigra/impex.hxx>
00037
00038 #ifdef WIN32
00039 #include <getopt.h>
00040 #else
00041 #include <unistd.h>
00042 #endif
00043
00044 #include <hugin_basic.h>
00045 #include <hugin_utils/platform.h>
00046 #include <algorithms/nona/NonaFileStitcher.h>
00047 #include <vigra_ext/MultiThreadOperations.h>
00048 #include <vigra_ext/ImageTransformsGPU.h>
00049
00050 #include <tiffio.h>
00051
00052 #if !defined Hugin_shared || !defined _WINDOWS
00053 #define GLEW_STATIC
00054 #endif
00055 #include <GL/glew.h>
00056 #ifdef __APPLE__
00057 #include <GLUT/glut.h>
00058 #else
00059 #include <GL/glut.h>
00060 #endif
00061
00062 using namespace vigra;
00063 using namespace HuginBase;
00064 using namespace hugin_utils;
00065 using namespace std;
00066
00067 GLuint GlutWindowHandle;
00068
00069 static void usage(const char * name)
00070 {
00071 cerr << name << ": stitch a panorama image" << std::endl
00072 << std::endl
00073 << "nona version " << DISPLAY_VERSION << std::endl
00074 << std::endl
00075 << " It uses the transform function from PanoTools, the stitching itself" << std::endl
00076 << " is quite simple, no seam feathering is done." << std::endl
00077 << " only the non-antialiasing interpolators of panotools are supported" << std::endl
00078 << std::endl
00079 << " The following output formats (n option of panotools p script line)" << std::endl
00080 << " are supported:"<< std::endl
00081 << std::endl
00082 << " JPG, TIFF, PNG : Single image formats without feathered blending:"<< std::endl
00083 << " TIFF_m : multiple tiff files"<< std::endl
00084 << " TIFF_multilayer : Multilayer tiff files, readable by The Gimp 2.0" << std::endl
00085 << std::endl
00086 << "Usage: " << name << " [options] -o output project_file (image files)" << std::endl
00087 << " Options: " << std::endl
00088 << " -c create coordinate images (only TIFF_m output)" << std::endl
00089 << " -v quiet, do not output progress indicators" << std::endl
00090 << " -d print detailed output for gpu processing" << std::endl
00091 << " -t num number of threads to be used (default: nr of available cores)" << std::endl
00092 << " -g perform image remapping on the GPU" << std::endl
00093 << std::endl
00094 << " The following options can be used to override settings in the project file:" << std::endl
00095 << " -i num remap only image with number num" << std::endl
00096 << " (can be specified multiple times)" << std::endl
00097 << " -m str set output file format (TIFF, TIFF_m, TIFF_multilayer, EXR, EXR_m)" << std::endl
00098 << " -r ldr/hdr set output mode." << std::endl
00099 << " ldr keep original bit depth and response" << std::endl
00100 << " hdr merge to hdr" << std::endl
00101 << " -e exposure set exposure for ldr mode" << std::endl
00102 << " -p TYPE pixel type of the output. Can be one of:" << std::endl
00103 << " UINT8 8 bit unsigned integer" << std::endl
00104 << " UINT16 16 bit unsigned integer" << std::endl
00105 << " INT16 16 bit signed integer" << std::endl
00106 << " UINT32 32 bit unsigned integer" << std::endl
00107 << " INT32 32 bit signed integer" << std::endl
00108 << " FLOAT 32 bit floating point" << std::endl
00109 << " -z set compression type." << std::endl
00110 << " Possible options for tiff output:" << std::endl
00111 << " NONE no compression" << std::endl
00112 << " PACKBITS packbits compression" << std::endl
00113 << " LZW lzw compression" << std::endl
00114 << " DEFLATE deflate compression" << std::endl
00115 << std::endl;
00116 }
00117
00122 static bool initGPU(int *argcp,char **argv) {
00123 glutInit(argcp,argv);
00124 glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA | GLUT_ALPHA);
00125 GlutWindowHandle = glutCreateWindow("nona");
00126
00127 int err = glewInit();
00128 if (err != GLEW_OK) {
00129 cerr << "nona: an error occured while setting up the GPU:" << endl;
00130 cerr << glewGetErrorString(err) << endl;
00131 cerr << "nona: Switching to CPU calculation." << endl;
00132 glutDestroyWindow(GlutWindowHandle);
00133 return false;
00134 }
00135
00136 cout << "nona: using graphics card: " << glGetString(GL_VENDOR) << " " << glGetString(GL_RENDERER) << endl;
00137
00138 GLboolean has_arb_fragment_shader = glewGetExtension("GL_ARB_fragment_shader");
00139 GLboolean has_arb_vertex_shader = glewGetExtension("GL_ARB_vertex_shader");
00140 GLboolean has_arb_shader_objects = glewGetExtension("GL_ARB_shader_objects");
00141 GLboolean has_arb_shading_language = glewGetExtension("GL_ARB_shading_language_100");
00142 GLboolean has_arb_texture_rectangle = glewGetExtension("GL_ARB_texture_rectangle");
00143 GLboolean has_arb_texture_border_clamp = glewGetExtension("GL_ARB_texture_border_clamp");
00144 GLboolean has_arb_texture_float = glewGetExtension("GL_ARB_texture_float");
00145
00146 if (!(has_arb_fragment_shader && has_arb_vertex_shader && has_arb_shader_objects && has_arb_shading_language && has_arb_texture_rectangle && has_arb_texture_border_clamp && has_arb_texture_float)) {
00147 const char * msg[] = {"false", "true"};
00148 cerr << "nona: extension GL_ARB_fragment_shader = " << msg[has_arb_fragment_shader] << endl;
00149 cerr << "nona: extension GL_ARB_vertex_shader = " << msg[has_arb_vertex_shader] << endl;
00150 cerr << "nona: extension GL_ARB_shader_objects = " << msg[has_arb_shader_objects] << endl;
00151 cerr << "nona: extension GL_ARB_shading_language_100 = " << msg[has_arb_shading_language] << endl;
00152 cerr << "nona: extension GL_ARB_texture_rectangle = " << msg[has_arb_texture_rectangle] << endl;
00153 cerr << "nona: extension GL_ARB_texture_border_clamp = " << msg[has_arb_texture_border_clamp] << endl;
00154 cerr << "nona: extension GL_ARB_texture_float = " << msg[has_arb_texture_float] << endl;
00155 cerr << "nona: This graphics system lacks the necessary extensions for -g." << endl;
00156 cerr << "nona: Switching to CPU calculation." << endl;
00157 glutDestroyWindow(GlutWindowHandle);
00158 return false;
00159 }
00160
00161 return true;
00162 }
00163
00164 static bool wrapupGPU() {
00165 glutDestroyWindow(GlutWindowHandle);
00166 return true;
00167 }
00168
00169 int main(int argc, char *argv[])
00170 {
00171
00172
00173 const char * optstring = "z:cho:i:t:m:p:r:e:vgd";
00174 int c;
00175
00176 opterr = 0;
00177
00178 int nThread = getCPUCount();
00179 if (nThread < 0) nThread = 1;
00180 bool doCoord = false;
00181 UIntSet outputImages;
00182 string basename;
00183 string outputFormat;
00184 bool overrideOutputMode = false;
00185 std::string compression;
00186 PanoramaOptions::OutputMode outputMode = PanoramaOptions::OUTPUT_LDR;
00187 bool overrideExposure = false;
00188 double exposure=0;
00189 int verbose = 0;
00190 bool useGPU = false;
00191 string outputPixelType;
00192
00193 while ((c = getopt (argc, argv, optstring)) != -1)
00194 {
00195 switch (c) {
00196 case 'o':
00197 basename = optarg;
00198 break;
00199 case 'c':
00200 doCoord = true;
00201 break;
00202 case 'i':
00203 outputImages.insert(atoi(optarg));
00204 break;
00205 case 'm':
00206 outputFormat = optarg;
00207 break;
00208 case 'p':
00209 outputPixelType = optarg;
00210 break;
00211 case 'r':
00212 if (string(optarg) == "ldr") {
00213 overrideOutputMode = true;
00214 outputMode = PanoramaOptions::OUTPUT_LDR;
00215 } else if (string(optarg) == "hdr") {
00216 overrideOutputMode = true;
00217 outputMode = PanoramaOptions::OUTPUT_HDR;
00218 } else {
00219 usage(argv[0]);
00220 return 1;
00221 }
00222 break;
00223 case 'e':
00224 overrideExposure = true;
00225 exposure = atof(optarg);
00226 break;
00227 case '?':
00228 case 'h':
00229 usage(argv[0]);
00230 return 0;
00231 case 't':
00232 nThread = atoi(optarg);
00233 break;
00234 case 'v':
00235 ++verbose;
00236 break;
00237 case 'z':
00238 compression = optarg;
00239 std::transform(compression.begin(), compression.end(), compression.begin(), (int(*)(int)) toupper);
00240 break;
00241 case 'g':
00242 useGPU = true;
00243 break;
00244 case 'd':
00245 vigra_ext::SetGPUDebugMessages(true);
00246 break;
00247 default:
00248 usage(argv[0]);
00249 abort ();
00250 }
00251 }
00252
00253 if (basename == "" || argc - optind <1) {
00254 usage(argv[0]);
00255 return 1;
00256 }
00257 unsigned nCmdLineImgs = argc -optind -1;
00258
00259 if (nThread == 0) nThread = 1;
00260 vigra_ext::ThreadManager::get().setNThreads(nThread);
00261
00262 const char * scriptFile = argv[optind];
00263
00264
00265 TIFFSetWarningHandler(0);
00266
00267 AppBase::ProgressDisplay* pdisp = NULL;
00268 if(verbose > 0)
00269 pdisp = new AppBase::StreamProgressDisplay(cout);
00270 else
00271 pdisp = new AppBase::DummyProgressDisplay;
00272
00273 Panorama pano;
00274 ifstream prjfile(scriptFile);
00275 if (prjfile.bad()) {
00276 cerr << "could not open script : " << scriptFile << std::endl;
00277 exit(1);
00278 }
00279 pano.setFilePrefix(hugin_utils::getPathPrefix(scriptFile));
00280 AppBase::DocumentData::ReadWriteError err = pano.readData(prjfile);
00281 if (err != AppBase::DocumentData::SUCCESSFUL) {
00282 cerr << "error while parsing panos tool script: " << scriptFile << std::endl;
00283 exit(1);
00284 }
00285
00286 if ( nCmdLineImgs > 0) {
00287 if (nCmdLineImgs != pano.getNrOfImages()) {
00288 cerr << "Incorrect number of images specified on command line\nProject required " << pano.getNrOfImages() << " but " << nCmdLineImgs << " where given" << std::endl;
00289 exit(1);
00290 }
00291 for (unsigned i=0; i < pano.getNrOfImages(); i++) {
00292 pano.setImageFilename(i, argv[optind+i+1]);
00293 }
00294
00295 }
00296 PanoramaOptions opts = pano.getOptions();
00297
00298 if (compression.size() > 0) {
00299 opts.tiffCompression=compression;
00300 }
00301
00302
00303 opts.saveCoordImgs = doCoord;
00304 if (outputFormat == "TIFF_m") {
00305 opts.outputFormat = PanoramaOptions::TIFF_m;
00306 } else if (outputFormat == "TIFF") {
00307 opts.outputFormat = PanoramaOptions::TIFF;
00308 } else if (outputFormat == "TIFF_multilayer") {
00309 opts.outputFormat = PanoramaOptions::TIFF_multilayer;
00310 } else if (outputFormat == "EXR_m") {
00311 opts.outputFormat = PanoramaOptions::EXR_m;
00312 } else if (outputFormat == "EXR") {
00313 opts.outputFormat = PanoramaOptions::EXR;
00314 } else if (outputFormat != "") {
00315 cerr << "Error: unknown output format: " << outputFormat << endl;
00316 return 1;
00317 }
00318
00319 if (outputPixelType.size() > 0) {
00320 opts.outputPixelType = outputPixelType;
00321 }
00322
00323 if (overrideOutputMode) {
00324 opts.outputMode = outputMode;
00325 }
00326
00327 if (overrideExposure) {
00328 opts.outputExposureValue = exposure;
00329 }
00330
00331 if(outputImages.size()==0)
00332 {
00333 outputImages = pano.getActiveImages();
00334 }
00335 else
00336 {
00337 UIntSet activeImages=pano.getActiveImages();
00338 for(UIntSet::const_iterator it=outputImages.begin(); it!=outputImages.end();it++)
00339 {
00340 if(!set_contains(activeImages,*it))
00341 {
00342 std::cerr << "The project file does not contains an image with number " << *it << std::endl;
00343 return 1;
00344 };
00345 };
00346 };
00347 if(outputImages.size()==0)
00348 {
00349 std::cout << "Project does not contain active images." << std::endl
00350 << "Nothing to do for nona." << std::endl;
00351 return 0;
00352 };
00353 if(useGPU)
00354 {
00355 switch(opts.getProjection())
00356 {
00357
00358 case HuginBase::PanoramaOptions::BIPLANE:
00359 case HuginBase::PanoramaOptions::TRIPLANE:
00360 case HuginBase::PanoramaOptions::PANINI:
00361 case HuginBase::PanoramaOptions::EQUI_PANINI:
00362 case HuginBase::PanoramaOptions::GENERAL_PANINI:
00363 useGPU=false;
00364 std::cout << "Nona-GPU does not support this projection. Switch to CPU calculation."<<std::endl;
00365 break;
00366 };
00367 };
00368
00369 DEBUG_DEBUG("output basename: " << basename);
00370
00371 try {
00372 if (useGPU) {
00373 useGPU = initGPU(&argc, argv);
00374 }
00375 opts.remapUsingGPU = useGPU;
00376 pano.setOptions(opts);
00377
00378
00379 NonaFileOutputStitcher(pano, pdisp, opts, outputImages, basename).run();
00380
00381 if (verbose > 0) {
00382 cout << std::endl;
00383 }
00384
00385 if (useGPU) {
00386 wrapupGPU();
00387 }
00388
00389 } catch (std::exception & e) {
00390 cerr << "caught exception: " << e.what() << std::endl;
00391 return 1;
00392 }
00393
00394 if(pdisp != NULL)
00395 delete pdisp;
00396
00397 return 0;
00398 }