verdandi.cpp

Go to the documentation of this file.
00001 // -*- c-basic-offset: 4 -*-
00002 
00011 /*  This program is free software; you can redistribute it and/or
00012 *  modify it under the terms of the GNU General Public
00013 *  License as published by the Free Software Foundation; either
00014 *  version 2 of the License, or (at your option) any later version.
00015 *
00016 *  This software is distributed in the hope that it will be useful,
00017 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00018 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00019 *  General Public License for more details.
00020 *
00021 *  You should have received a copy of the GNU General Public
00022 *  License along with this software. If not, see
00023 *  <http://www.gnu.org/licenses/>.
00024 *
00025 */
00026 
00027 #include <stdio.h>
00028 #include <iostream>
00029 #include <getopt.h>
00030 #include <vigra_ext/impexalpha.hxx>
00031 #include <vigra_ext/StitchWatershed.h>
00032 #include <vigra_ext/utils.h>
00033 #include <hugin_utils/utils.h>
00034 #include <hugin_utils/stl_utils.h>
00035 
00037 template <class ImageType, class MaskType>
00038 bool SaveImage(ImageType& image, MaskType& mask, vigra::ImageExportInfo& exportImageInfo, std::string filetype, std::string pixelType, const vigra::Rect2D& roi, const int inputNumberBands)
00039 {
00040     exportImageInfo.setPixelType(pixelType.c_str());
00041     if (vigra::isBandNumberSupported(filetype, inputNumberBands))
00042     {
00043         try
00044         {
00045             vigra::exportImageAlpha(vigra::srcImageRange(image, roi), vigra::srcImage(mask, roi.upperLeft()), exportImageInfo);
00046         }
00047         catch (std::exception& e)
00048         {
00049             std::cerr << "ERROR: Could not save " << exportImageInfo.getFileName() << std::endl
00050                 << "Cause: " << e.what() << std::endl;
00051             return false;
00052         };
00053         return true;
00054     }
00055     else
00056     {
00057         if (vigra::isBandNumberSupported(filetype, inputNumberBands - 1))
00058         {
00059             std::cout << "Warning: Filetype " << filetype << " does not support alpha channels." << std::endl
00060                 << "Saving image without alpha channel." << std::endl;
00061             try
00062             {
00063                 vigra::exportImage(vigra::srcImageRange(image, roi), exportImageInfo);
00064             }
00065             catch (std::exception& e)
00066             {
00067                 std::cerr << "ERROR: Could not save " << exportImageInfo.getFileName() << std::endl
00068                     << "Cause: " << e.what() << std::endl;
00069                 return false;
00070             };
00071             return true;
00072         }
00073         else
00074         {
00075             std::cerr << "ERROR: Output filetype " << filetype << " does not support " << inputNumberBands << " channels." << std::endl
00076                 << "Can't save image." << std::endl;
00077         };
00078     };
00079     return false;
00080 };
00081 
00083 template <class ImageType, class MaskType>
00084 bool SaveFinalImage(ImageType& image, MaskType& mask, const std::string& inputPixelType, const int inputNumBands, vigra::ImageExportInfo& output, const vigra::Rect2D& roi)
00085 {
00086     VIGRA_UNIQUE_PTR<vigra::Encoder> encoder(vigra::encoder(output));
00087     if (vigra::isPixelTypeSupported(encoder->getFileType(), inputPixelType))
00088     {
00089         return SaveImage(image, mask, output, encoder->getFileType(), inputPixelType, roi, inputNumBands);
00090     }
00091     else
00092     {
00093         if (vigra::isPixelTypeSupported(encoder->getFileType(), "UINT16"))
00094         {
00095             // transform to UINT16
00096             output.setForcedRangeMapping(0, vigra::NumericTraits<typename vigra::NumericTraits<typename ImageType::PixelType>::ValueType>::max(), 0, 65535);
00097             return SaveImage(image, mask, output, encoder->getFileType(), "UINT16", roi, inputNumBands);
00098         }
00099         else
00100         {
00101             if (vigra::isPixelTypeSupported(encoder->getFileType(), "UINT8"))
00102             {
00103                 // transform to UINT8
00104                 output.setForcedRangeMapping(0, vigra::NumericTraits<typename vigra::NumericTraits<typename ImageType::PixelType>::ValueType>::max(), 0, 255);
00105                 return SaveImage(image, mask, output, encoder->getFileType(), "UINT8", roi, inputNumBands);
00106             }
00107             else
00108             {
00109                 std::cerr << "ERROR: Output file type " << encoder->getFileType() << " does not support" << std::endl
00110                     << "requested pixeltype " << inputPixelType << "." << std::endl
00111                     << "Save output in other file format." << std::endl;
00112             };
00113         };
00114     };
00115     return false;
00116 };
00117 
00119 void SetCompression(vigra::ImageExportInfo& output, const std::string& compression)
00120 {
00121     const std::string ext(hugin_utils::toupper(hugin_utils::getExtension(output.getFileName())));
00122     if (!compression.empty())
00123     {
00124         if (ext == "JPEG" || ext == "JPG")
00125         {
00126             output.setCompression(std::string("JPEG QUALITY=" + compression).c_str());
00127         }
00128         else
00129         {
00130             output.setCompression(compression.c_str());
00131         };
00132     };
00133 };
00134 
00136 template <class ImageType>
00137 bool LoadAndMergeImages(std::vector<vigra::ImageImportInfo> imageInfos, const std::string& filename, const std::string& compression, const bool wrap, const bool hardSeam, const bool useBigTiff)
00138 {
00139     if (imageInfos.empty())
00140     {
00141         return false;
00142     };
00143     vigra::Size2D imageSize(imageInfos[0].getCanvasSize());
00144     if (imageSize.area() == 0)
00145     {
00146         // not all images contains the canvas size/full image size
00147         // in this case take also the position into account to get full image size
00148         imageSize = vigra::Size2D(imageInfos[0].width() + imageInfos[0].getPosition().x,
00149             imageInfos[0].height() + imageInfos[0].getPosition().y);
00150     };
00151     ImageType image(imageSize);
00152     vigra::BImage mask(imageSize);
00153     vigra::importImageAlpha(imageInfos[0],
00154         std::pair<typename ImageType::Iterator, typename ImageType::Accessor>(image.upperLeft() + imageInfos[0].getPosition(), image.accessor()),
00155         std::pair<typename vigra::BImage::Iterator, typename vigra::BImage::Accessor>(mask.upperLeft() + imageInfos[0].getPosition(), mask.accessor()));
00156     std::cout << "Loaded " << imageInfos[0].getFileName() << std::endl;
00157     vigra::Rect2D roi(vigra::Point2D(imageInfos[0].getPosition()), imageInfos[0].size());
00158 
00159     for (size_t i = 1; i < imageInfos.size(); ++i)
00160     {
00161         ImageType image2(imageInfos[i].size());
00162         vigra::BImage mask2(image2.size());
00163         vigra::importImageAlpha(imageInfos[i], vigra::destImage(image2), vigra::destImage(mask2));
00164         std::cout << "Loaded " << imageInfos[i].getFileName() << std::endl;
00165         roi |= vigra::Rect2D(vigra::Point2D(imageInfos[i].getPosition()), imageInfos[i].size());
00166 
00167         vigra_ext::MergeImages(image, mask, image2, mask2, imageInfos[i].getPosition(), wrap, hardSeam);
00168     };
00169     // save output
00170     {
00171         vigra::ImageExportInfo exportImageInfo(filename.c_str(), useBigTiff ? "w8" : "w");
00172         exportImageInfo.setXResolution(imageInfos[0].getXResolution());
00173         exportImageInfo.setYResolution(imageInfos[0].getYResolution());
00174         exportImageInfo.setPosition(roi.upperLeft());
00175         exportImageInfo.setCanvasSize(mask.size());
00176         exportImageInfo.setICCProfile(imageInfos[0].getICCProfile());
00177         SetCompression(exportImageInfo, compression);
00178         return SaveFinalImage(image, mask, imageInfos[0].getPixelType(), imageInfos[0].numBands(), exportImageInfo, roi);
00179     };
00180 };
00181 
00183 static void usage(const char* name)
00184 {
00185     std::cout << name << ": blend images using watershed algorithm" << std::endl
00186         << name << " version " << hugin_utils::GetHuginVersion() << std::endl
00187         << std::endl
00188         << "Usage:  " << name << " [options] images" << std::endl
00189         << std::endl
00190         << "     --output=FILE       Set the filename for the output file." << std::endl
00191         << "     --compression=value Compression of the output files" << std::endl
00192         << "                            For jpeg output: 0-100" << std::endl
00193         << "                            For tiff output: PACKBITS, DEFLATE, LZW" << std::endl
00194         << "     -w, --wrap          Wraparound 360 deg border." << std::endl
00195         << "     --seam=hard|blend   Select the blend mode for the seam" << std::endl
00196         << "     --bigtiff           Write output in BigTIFF format" << std::endl
00197         << "                         (only with TIFF output)" << std::endl
00198         << "     -h, --help          Shows this help" << std::endl
00199         << std::endl;
00200 };
00201 
00205 template<class ImageType, class MaskType>
00206 bool ResaveImage(const vigra::ImageImportInfo& importInfo, vigra::ImageExportInfo& exportInfo)
00207 {
00208     ImageType image(importInfo.size());
00209     MaskType mask(image.size());
00210     int numBands = importInfo.numBands();
00211     if (importInfo.numExtraBands() == 0)
00212     {
00213         vigra::importImage(importInfo, vigra::destImage(image));
00214         // init mask
00215         vigra::initImage(vigra::destImageRange(mask), vigra_ext::LUTTraits<typename MaskType::value_type>::max());
00216         ++numBands;
00217     }
00218     else
00219     {
00220         if (importInfo.numExtraBands() == 1)
00221         {
00222             vigra::importImageAlpha(importInfo, vigra::destImage(image), vigra::destImage(mask));
00223         }
00224         else
00225         {
00226             std::cerr << "ERROR: Images with several alpha channels are not supported." << std::endl;
00227             return false;
00228         };
00229     };
00230 
00231     const vigra::Rect2D roi(vigra::Point2D(0, 0), image.size());
00232     return SaveFinalImage(image, mask, importInfo.getPixelType(), numBands, exportInfo, roi);
00233 };
00234 
00235 int main(int argc, char* argv[])
00236 {
00237     // parse arguments
00238     const char* optstring = "o:hw";
00239 
00240     enum
00241     {
00242         OPT_COMPRESSION = 1000,
00243         OPT_SEAMMODE,
00244         OPT_BIGTIFF
00245     };
00246     static struct option longOptions[] =
00247     {
00248         { "output", required_argument, NULL, 'o' },
00249         { "compression", required_argument, NULL, OPT_COMPRESSION},
00250         { "seam", required_argument, NULL, OPT_SEAMMODE},
00251         { "wrap", no_argument, NULL, 'w' },
00252         { "bigtiff", no_argument, NULL, OPT_BIGTIFF},
00253         { "help", no_argument, NULL, 'h' },
00254         0
00255     };
00256 
00257     int c;
00258     std::string output;
00259     std::string compression;
00260     bool wraparound = false;
00261     bool hardSeam = true;
00262     bool useBigTIFF = false;
00263     while ((c = getopt_long(argc, argv, optstring, longOptions, nullptr)) != -1)
00264     {
00265         switch (c)
00266         {
00267         case 'o':
00268             output = optarg;
00269             break;
00270         case 'h':
00271             usage(hugin_utils::stripPath(argv[0]).c_str());
00272             return 0;
00273             break;
00274         case OPT_COMPRESSION:
00275             compression = hugin_utils::toupper(optarg);
00276             break;
00277         case OPT_SEAMMODE:
00278             {
00279                 std::string text(optarg);
00280                 text = hugin_utils::tolower(text);
00281                 if (text == "hard")
00282                 {
00283                     hardSeam = true;
00284                 }
00285                 else
00286                 {
00287                     if (text == "blend")
00288                     {
00289                         hardSeam = false;
00290                     }
00291                     else
00292                     {
00293                         std::cerr << hugin_utils::stripPath(argv[0]) << ": String \"" << text << "\" is not a recognized seam blend mode." << std::endl;
00294                         return 1;
00295                     };
00296                 };
00297             };
00298             break;
00299         case 'w':
00300             wraparound = true;
00301             break;
00302         case OPT_BIGTIFF:
00303             useBigTIFF = true;
00304             break;
00305         case ':':
00306         case '?':
00307             // missing argument or invalid switch
00308             return 1;
00309             break;
00310         default:
00311             // this should not happen
00312             abort();
00313         }
00314     };
00315 
00316     unsigned nFiles = argc - optind;
00317     if (nFiles < 1)
00318     {
00319         std::cerr << hugin_utils::stripPath(argv[0]) << ": at least one image need to be specified" << std::endl;
00320         return 1;
00321     }
00322 
00323     // extract file names
00324     std::vector<std::string> files;
00325     for (size_t i = 0; i < nFiles; i++)
00326     {
00327         std::string currentFile(argv[optind + i]);
00328         // check file existence
00329         if (hugin_utils::FileExists(currentFile))
00330         {
00331             files.push_back(currentFile);
00332         };
00333     }
00334 
00335     if (files.empty())
00336     {
00337         std::cerr << "ERROR: " << hugin_utils::stripPath(argv[0]) << " needs at least one image." << std::endl;
00338         return 1;
00339     };
00340 
00341     if (output.empty())
00342     {
00343         output = "final.tif";
00344     };
00345     hugin_utils::EnforceExtension(output, "tif");
00346     if (!hugin_utils::IsFileTypeSupported(output))
00347     {
00348         std::cerr << "ERROR: Extension \"" << hugin_utils::getExtension(output) << "\" is unknown." << std::endl;
00349         return 1;
00350     };
00351 
00352 
00353     bool success = false;
00354     if (files.size() == 1)
00355     {
00356         //special case, only one image given
00357         vigra::ImageImportInfo imageInfo(files[0].c_str());
00358         vigra::ImageExportInfo exportInfo(output.c_str(), useBigTIFF ? "w8" : "w");
00359         exportInfo.setXResolution(imageInfo.getXResolution());
00360         exportInfo.setYResolution(imageInfo.getYResolution());
00361         exportInfo.setCanvasSize(imageInfo.getCanvasSize());
00362         exportInfo.setPosition(imageInfo.getPosition());
00363         exportInfo.setICCProfile(imageInfo.getICCProfile());
00364         SetCompression(exportInfo, compression);
00365         const std::string pixeltype = imageInfo.getPixelType();
00366         if (imageInfo.isColor())
00367         {
00368             if (pixeltype == "UINT8")
00369             {
00370                 success = ResaveImage<vigra::BRGBImage, vigra::BImage>(imageInfo, exportInfo);
00371             }
00372             else if (pixeltype == "INT16")
00373             {
00374                 success = ResaveImage<vigra::Int16RGBImage, vigra::Int16Image>(imageInfo, exportInfo);
00375             }
00376             else if (pixeltype == "UINT16")
00377             {
00378                 success = ResaveImage<vigra::UInt16RGBImage, vigra::UInt16Image>(imageInfo, exportInfo);
00379             }
00380             else if (pixeltype == "INT32")
00381             {
00382                 success = ResaveImage<vigra::Int32RGBImage, vigra::UInt32Image>(imageInfo, exportInfo);
00383             }
00384             else if (pixeltype == "UINT32")
00385             {
00386                 success = ResaveImage<vigra::UInt32RGBImage, vigra::UInt32Image>(imageInfo, exportInfo);
00387             }
00388             else if (pixeltype == "FLOAT")
00389             {
00390                 success = ResaveImage<vigra::FRGBImage, vigra::FImage>(imageInfo, exportInfo);
00391             }
00392             else
00393             {
00394                 std::cerr << " ERROR: unsupported pixel type: " << pixeltype << std::endl;
00395             };
00396         }
00397         else
00398         {
00399             //grayscale images
00400             if (pixeltype == "UINT8")
00401             {
00402                 success = ResaveImage<vigra::BImage, vigra::BImage>(imageInfo, exportInfo);
00403             }
00404             else if (pixeltype == "INT16")
00405             {
00406                 success = ResaveImage<vigra::Int16Image, vigra::Int16Image>(imageInfo, exportInfo);
00407             }
00408             else if (pixeltype == "UINT16")
00409             {
00410                 success = ResaveImage<vigra::UInt16Image, vigra::UInt16Image>(imageInfo, exportInfo);
00411             }
00412             else if (pixeltype == "INT32")
00413             {
00414                 success = ResaveImage<vigra::Int32Image, vigra::Int32Image>(imageInfo, exportInfo);
00415             }
00416             else if (pixeltype == "UINT32")
00417             {
00418                 success = ResaveImage<vigra::UInt32Image, vigra::UInt32Image>(imageInfo, exportInfo);
00419             }
00420             else if (pixeltype == "FLOAT")
00421             {
00422                 success = ResaveImage<vigra::FImage, vigra::FImage>(imageInfo, exportInfo);
00423             }
00424             else
00425             {
00426                 std::cerr << " ERROR: unsupported pixel type: " << pixeltype << std::endl;
00427             };
00428         };
00429     }
00430     else
00431     {
00432         std::vector<vigra::ImageImportInfo> imageInfos;
00433         for (size_t i = 0; i < files.size(); ++i)
00434         {
00435             vigra::ImageImportInfo imageInfo(files[i].c_str());
00436             imageInfos.push_back(imageInfo);
00437         };
00438         const std::string pixeltype(imageInfos[0].getPixelType());
00439         if (imageInfos[0].numExtraBands() != 1)
00440         {
00441             std::cerr << "ERROR: Image does not contain alpha channel." << std::endl;
00442             return 1;
00443         }
00444         //check, that image information matches
00445         for (size_t i = 1; i < files.size(); ++i)
00446         {
00447             if (imageInfos[0].isColor() != imageInfos[i].isColor())
00448             {
00449                 std::cerr << "ERROR: You can't merge color and grayscale images." << std::endl;
00450                 return 1;
00451             };
00452             if (imageInfos[0].numBands() != imageInfos[i].numBands())
00453             {
00454                 std::cerr << "ERROR: You can't merge image with different number of channels." << std::endl
00455                     << "       Image \"" << imageInfos[0].getFileName() << "\" has " << imageInfos[0].numBands() << " channels," << std::endl
00456                     << "       but image \"" << imageInfos[i].getFileName() << "\" has " << imageInfos[i].numBands() << " channels." << std::endl;
00457                 return 1;
00458             };
00459             if (strcmp(pixeltype.c_str(), imageInfos[i].getPixelType()) != 0)
00460             {
00461                 std::cerr << "ERROR: You can't merge images with different pixel types." << std::endl
00462                     << "       Image \"" << imageInfos[0].getFileName() << "\" has pixel type " << imageInfos[0].getPixelType() << "," << std::endl
00463                     << "       but image \"" << imageInfos[i].getFileName() << "\" has pixel type " << imageInfos[i].getPixelType() << "." << std::endl;
00464                 return 1;
00465             };
00466         };
00467 
00468         if (imageInfos[0].isColor())
00469         {
00470             if (pixeltype == "UINT8")
00471             {
00472                 success = LoadAndMergeImages<vigra::BRGBImage>(imageInfos, output, compression, wraparound, hardSeam, useBigTIFF);
00473             }
00474             else if (pixeltype == "INT16")
00475             {
00476                 success = LoadAndMergeImages<vigra::Int16RGBImage>(imageInfos, output, compression, wraparound, hardSeam, useBigTIFF);
00477             }
00478             else if (pixeltype == "UINT16")
00479             {
00480                 success = LoadAndMergeImages<vigra::UInt16RGBImage>(imageInfos, output, compression, wraparound, hardSeam, useBigTIFF);
00481             }
00482             else if (pixeltype == "INT32")
00483             {
00484                 success = LoadAndMergeImages<vigra::Int32RGBImage>(imageInfos, output, compression, wraparound, hardSeam, useBigTIFF);
00485             }
00486             else if (pixeltype == "UINT32")
00487             {
00488                 success = LoadAndMergeImages<vigra::UInt32RGBImage>(imageInfos, output, compression, wraparound, hardSeam, useBigTIFF);
00489             }
00490             else if (pixeltype == "FLOAT")
00491             {
00492                 success = LoadAndMergeImages<vigra::FRGBImage>(imageInfos, output, compression, wraparound, hardSeam, useBigTIFF);
00493             }
00494             else if (pixeltype == "DOUBLE")
00495             {
00496                 success = LoadAndMergeImages<vigra::DRGBImage>(imageInfos, output, compression, wraparound, hardSeam, useBigTIFF);
00497             }
00498             else
00499             {
00500                 std::cerr << " ERROR: unsupported pixel type: " << pixeltype << std::endl;
00501             };
00502         }
00503         else
00504         {
00505             //grayscale images
00506             if (pixeltype == "UINT8")
00507             {
00508                 success = LoadAndMergeImages<vigra::BImage>(imageInfos, output, compression, wraparound, hardSeam, useBigTIFF);
00509             }
00510             else if (pixeltype == "INT16")
00511             {
00512                 success = LoadAndMergeImages<vigra::Int16Image>(imageInfos, output, compression, wraparound, hardSeam, useBigTIFF);
00513             }
00514             else if (pixeltype == "UINT16")
00515             {
00516                 success = LoadAndMergeImages<vigra::UInt16Image>(imageInfos, output, compression, wraparound, hardSeam, useBigTIFF);
00517             }
00518             else if (pixeltype == "INT32")
00519             {
00520                 success = LoadAndMergeImages<vigra::Int32Image>(imageInfos, output, compression, wraparound, hardSeam, useBigTIFF);
00521             }
00522             else if (pixeltype == "UINT32")
00523             {
00524                 success = LoadAndMergeImages<vigra::UInt32Image>(imageInfos, output, compression, wraparound, hardSeam, useBigTIFF);
00525             }
00526             else if (pixeltype == "FLOAT")
00527             {
00528                 success = LoadAndMergeImages<vigra::FImage>(imageInfos, output, compression, wraparound, hardSeam, useBigTIFF);
00529             }
00530             else if (pixeltype == "DOUBLE")
00531             {
00532                 success = LoadAndMergeImages<vigra::DImage>(imageInfos, output, compression, wraparound, hardSeam, useBigTIFF);
00533             }
00534             else
00535             {
00536                 std::cerr << " ERROR: unsupported pixel type: " << pixeltype << std::endl;
00537             };
00538         };
00539     };
00540 
00541     if (success)
00542     {
00543         std::cout << "Written result to " << output << std::endl;
00544         return 0;
00545     };
00546     return 1;
00547 }

Generated on 23 Jan 2018 for Hugintrunk by  doxygen 1.4.7