linefind.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 <fstream>
00028 #include <sstream>
00029 #include <getopt.h>
00030 #ifndef _WIN32
00031 #include <unistd.h>
00032 #endif
00033 #include <panodata/Panorama.h>
00034 #include <panodata/StandardImageVariableGroups.h>
00035 #include <lines/FindLines.h>
00036 #include <vigra/impex.hxx>
00037 #include <vigra_ext/impexalpha.hxx>
00038 #include <vigra/functorexpression.hxx>
00039 #include <vigra_ext/utils.h>
00040 #include <hugin_utils/openmp_lock.h>
00041 
00042 extern "C"
00043 {
00044 #include <pano13/filter.h>
00045 }
00046 
00047 static void usage(const char* name)
00048 {
00049     std::cout << name << ": find vertical lines in images" << std::endl
00050               << name << " version " << hugin_utils::GetHuginVersion() << std::endl
00051               << std::endl
00052               << "Usage:  " << name << " [options] input.pto" << std::endl
00053               << std::endl
00054               << "  Options:" << std::endl
00055               << "     -o, --output=file.pto  Output Hugin PTO file. Default: <filename>_lines.pto" << std::endl
00056               << "     -i, --image=IMGNR      Work only on given image numbers" << std::endl
00057               << "     -l, --lines=COUNT      Save maximal COUNT lines (default: 5)" << std::endl
00058               << "     -h, --help             Shows this help" << std::endl
00059               << std::endl;
00060 }
00061 
00062 // dummy panotools progress functions
00063 static int ptProgress( int command, char* argument )
00064 {
00065     return 1;
00066 }
00067 static int ptinfoDlg( int command, char* argument )
00068 {
00069     return 1;
00070 }
00071 
00078 // 2 versions: one for color images, the other for gray images
00079 template <class SrcIMG>
00080 void convertToUInt8(SrcIMG& src, const std::string& origType, vigra::UInt8RGBImage& dest)
00081 {
00082     dest.resize(src.size());
00083     long newMax=vigra_ext::getMaxValForPixelType("UINT8");
00084     // float needs to be from min ... max.
00085     if (origType == "FLOAT" || origType == "DOUBLE")
00086     {
00090         vigra::RGBToGrayAccessor<vigra::RGBValue<float> > ga;
00091         vigra::FindMinMax<float> minmax;   // init functor
00092         vigra::inspectImage(srcImageRange(src, ga),
00093                             minmax);
00094         double minVal = minmax.min;
00095         double maxVal = minmax.max;
00096         vigra_ext::applyMapping(srcImageRange(src), destImage(dest), minVal, maxVal, 0);
00097     }
00098     else
00099     {
00100         vigra::transformImage(srcImageRange(src), destImage(dest),
00101                               vigra::functor::Arg1()*vigra::functor::Param( newMax/ vigra_ext::getMaxValForPixelType(origType)));
00102     };
00103 }
00104 
00105 template <class SrcIMG>
00106 void convertGrayToUInt8(SrcIMG& src, const std::string& origType, vigra::BImage& dest)
00107 {
00108     dest.resize(src.size());
00109     long newMax=vigra_ext::getMaxValForPixelType("UINT8");
00110     // float needs to be from min ... max.
00111     if (origType == "FLOAT" || origType == "DOUBLE")
00112     {
00116         vigra::FindMinMax<float> minmax;   // init functor
00117         vigra::inspectImage(srcImageRange(src), minmax);
00118         double minVal = minmax.min;
00119         double maxVal = minmax.max;
00120         vigra_ext::applyMapping(srcImageRange(src), destImage(dest), minVal, maxVal, 0);
00121     }
00122     else
00123     {
00124         vigra::transformImage(srcImageRange(src), destImage(dest),
00125                               vigra::functor::Arg1()*vigra::functor::Param( newMax/ vigra_ext::getMaxValForPixelType(origType)));
00126     };
00127 }
00128 
00129 template <class SrcIMG>
00130 vigra::BImage LoadGrayImageAndConvert(vigra::ImageImportInfo& info)
00131 {
00132     vigra::BImage image;
00133     SrcIMG imageIn(info.width(),info.height());
00134     if(info.numExtraBands()==1)
00135     {
00136         vigra::BImage mask(info.size());
00137         vigra::importImageAlpha(info,destImage(imageIn),destImage(mask));
00138         mask.resize(0,0);
00139     }
00140     else
00141     {
00142         importImage(info,destImage(imageIn));
00143     };
00144     convertGrayToUInt8(imageIn,info.getPixelType(),image);
00145     imageIn.resize(0,0);
00146     return image;
00147 };
00148 
00149 template <class SrcIMG>
00150 vigra::UInt8RGBImage LoadImageAndConvert(vigra::ImageImportInfo& info)
00151 {
00152     vigra::UInt8RGBImage image;
00153     SrcIMG imageIn(info.width(),info.height());
00154     if(info.numExtraBands()==1)
00155     {
00156         vigra::BImage mask(info.size());
00157         vigra::importImageAlpha(info,destImage(imageIn),destImage(mask));
00158         mask.resize(0,0);
00159     }
00160     else
00161     {
00162         importImage(info,destImage(imageIn));
00163     };
00164     convertToUInt8(imageIn,info.getPixelType(),image);
00165     imageIn.resize(0,0);
00166     return image;
00167 };
00168 
00169 // loads the gray images and finds vertical lines, returns a CPVector with found vertical lines
00170 HuginBase::CPVector LoadGrayImageAndFindLines(vigra::ImageImportInfo info, HuginBase::Panorama& pano, size_t imgNr, int nrLines)
00171 {
00172     vigra::BImage image;
00173     HuginBase::CPVector lineCp;
00174     std::string pixelType=info.getPixelType();
00175     if(pixelType=="UINT8")
00176     {
00177         image.resize(info.width(),info.height());
00178         if(info.numExtraBands()==1)
00179         {
00180             vigra::BImage mask(info.size());
00181             vigra::importImageAlpha(info,destImage(image),destImage(mask));
00182             mask.resize(0,0);
00183         }
00184         else
00185         {
00186             importImage(info,destImage(image));
00187         };
00188     }
00189     else
00190     {
00191         if(pixelType=="UINT16" || pixelType=="INT16")
00192         {
00193             image=LoadGrayImageAndConvert<vigra::UInt16Image>(info);
00194         }
00195         else
00196         {
00197             if(pixelType=="INT32" || pixelType=="UINT32")
00198             {
00199                 image=LoadGrayImageAndConvert<vigra::UInt32Image>(info);
00200             }
00201             else
00202             {
00203                 if(pixelType=="FLOAT" || pixelType=="DOUBLE")
00204                 {
00205                     image=LoadGrayImageAndConvert<vigra::FImage>(info);
00206                 }
00207                 else
00208                 {
00209                     std::cerr << "Unsupported pixel type" << std::endl;
00210                 };
00211             };
00212         };
00213     };
00214     if(image.width()>0 && image.height()>0)
00215     {
00216         lineCp=HuginLines::GetVerticalLines(pano, imgNr, image, nrLines);
00217     };
00218     return lineCp;
00219 };
00220 
00221 // loads the color images and finds vertical lines, returns a CPVector with found vertical lines
00222 HuginBase::CPVector LoadImageAndFindLines(vigra::ImageImportInfo info, HuginBase::Panorama& pano, size_t imgNr, int nrLines)
00223 {
00224     vigra::UInt8RGBImage image;
00225     HuginBase::CPVector lineCp;
00226     std::string pixelType=info.getPixelType();
00227     if(pixelType=="UINT8")
00228     {
00229         image.resize(info.width(),info.height());
00230         if(info.numExtraBands()==1)
00231         {
00232             vigra::BImage mask(info.size());
00233             vigra::importImageAlpha(info,destImage(image),destImage(mask));
00234             mask.resize(0,0);
00235         }
00236         else
00237         {
00238             importImage(info,destImage(image));
00239         };
00240     }
00241     else
00242     {
00243         if(pixelType=="UINT16" || pixelType=="INT16")
00244         {
00245             image=LoadImageAndConvert<vigra::UInt16RGBImage>(info);
00246         }
00247         else
00248         {
00249             if(pixelType=="INT32" || pixelType=="UINT32")
00250             {
00251                 image=LoadImageAndConvert<vigra::UInt32RGBImage>(info);
00252             }
00253             else
00254             {
00255                 if(pixelType=="FLOAT" || pixelType=="DOUBLE")
00256                 {
00257                     image=LoadImageAndConvert<vigra::FRGBImage>(info);
00258                 }
00259                 else
00260                 {
00261                     std::cerr << "Unsupported pixel type" << std::endl;
00262                 };
00263             };
00264         };
00265     };
00266     if(image.width()>0 && image.height()>0)
00267     {
00268         lineCp=HuginLines::GetVerticalLines(pano, imgNr, image, nrLines);
00269     }
00270     return lineCp;
00271 };
00272 
00273 struct SortVectorByExposure
00274 {
00275     explicit SortVectorByExposure(const HuginBase::Panorama& pano) : m_pano(pano) {};
00276     bool operator()(const size_t& img1, const size_t& img2)
00277     {
00278         return m_pano.getImage(img1).getExposureValue() < m_pano.getImage(img2).getExposureValue();
00279     }
00280 private:
00281     const HuginBase::Panorama& m_pano;
00282 };
00283 
00284 static hugin_omp::Lock lock;
00285 
00286 int main(int argc, char* argv[])
00287 {
00288     // parse arguments
00289     const char* optstring = "o:i:l:h";
00290 
00291     static struct option longOptions[] =
00292     {
00293         {"output", required_argument, NULL, 'o' },
00294         {"image", required_argument, NULL, 'i' },
00295         {"lines", required_argument, NULL, 'l' },
00296         {"help", no_argument, NULL, 'h' },
00297         0
00298     };
00299 
00300     HuginBase::UIntSet cmdlineImages;
00301     int c;
00302     int optionIndex = 0;
00303     int nrLines = 5;
00304     std::string output;
00305     while ((c = getopt_long (argc, argv, optstring, longOptions,&optionIndex)) != -1)
00306     {
00307         switch (c)
00308         {
00309             case 'o':
00310                 output = optarg;
00311                 break;
00312             case 'h':
00313                 usage(hugin_utils::stripPath(argv[0]).c_str());
00314                 return 0;
00315             case 'i':
00316                 {
00317                     int imgNr=atoi(optarg);
00318                     if((imgNr==0) && (strcmp(optarg,"0")!=0))
00319                     {
00320                         std::cerr << "Could not parse image number.";
00321                         return 1;
00322                     };
00323                     cmdlineImages.insert(imgNr);
00324                 };
00325                 break;
00326             case 'l':
00327                 nrLines=atoi(optarg);
00328                 if(nrLines<1)
00329                 {
00330                     std::cerr << "Could not parse number of lines.";
00331                     return 1;
00332                 };
00333                 break;
00334             case ':':
00335                 std::cerr <<"Option " << longOptions[optionIndex].name << " requires a number" << std::endl;
00336                 return 1;
00337                 break;
00338             case '?':
00339                 break;
00340             default:
00341                 abort ();
00342         }
00343     }
00344 
00345     if (argc - optind != 1)
00346     {
00347         std::cout << "Warning: " << argv[0] << " can only work on one project file at one time" << std::endl << std::endl;
00348         usage(hugin_utils::stripPath(argv[0]).c_str());
00349         return 1;
00350     };
00351 
00352     std::string input=argv[optind];
00353     // read panorama
00354     HuginBase::Panorama pano;
00355     std::ifstream prjfile(input.c_str());
00356     if (!prjfile.good())
00357     {
00358         std::cerr << "could not open script : " << input << std::endl;
00359         return 1;
00360     }
00361     pano.setFilePrefix(hugin_utils::getPathPrefix(input));
00362     AppBase::DocumentData::ReadWriteError err = pano.readData(prjfile);
00363     if (err != AppBase::DocumentData::SUCCESSFUL)
00364     {
00365         std::cerr << "error while parsing panos tool script: " << input << std::endl;
00366         std::cerr << "DocumentData::ReadWriteError code: " << err << std::endl;
00367         return 1;
00368     }
00369 
00370     if(pano.getNrOfImages()==0)
00371     {
00372         std::cerr << "error: project file does not contains any image" << std::endl;
00373         std::cerr << "aborting processing" << std::endl;
00374         return 1;
00375     };
00376 
00377     std::vector<size_t> imagesToProcess;
00378     if(cmdlineImages.size()==0)
00379     {
00380         //no image given, process one image of each stack
00381         HuginBase::ConstStandardImageVariableGroups variable_groups(pano);
00382         HuginBase::UIntSetVector imageGroups = variable_groups.getStacks().getPartsSet();
00383         //get image with median exposure for search for lines
00384         for (size_t imgGroup = 0; imgGroup < imageGroups.size(); ++imgGroup)
00385         {
00386             HuginBase::UIntVector stackImages(imageGroups[imgGroup].begin(), imageGroups[imgGroup].end());
00387             if (pano.getImage(stackImages[0]).YawisLinked())
00388             {
00389                 // position is linked, take only image with median exposure for search
00390                 std::sort(stackImages.begin(), stackImages.end(), SortVectorByExposure(pano));
00391                 size_t index = 0;
00392                 if (pano.getImage(*(stackImages.begin())).getExposureValue() != pano.getImage(*(stackImages.rbegin())).getExposureValue())
00393                 {
00394                     index = stackImages.size() / 2;
00395                 };
00396                 imagesToProcess.push_back(stackImages[index]);
00397             }
00398             else
00399             {
00400                 // stacks are unlinked, search all images
00401                 std::copy(stackImages.begin(), stackImages.end(), std::back_inserter(imagesToProcess));
00402             };
00403         };
00404     }
00405     else
00406     {
00407         //check, if given image numbers are valid
00408         for (HuginBase::UIntSet::const_iterator it = cmdlineImages.begin(); it != cmdlineImages.end(); ++it)
00409         {
00410             if((*it)>=0 && (*it)<pano.getNrOfImages())
00411             {
00412                 imagesToProcess.push_back(*it);
00413             };
00414         };
00415     };
00416 
00417     if(imagesToProcess.size()==0)
00418     {
00419         std::cerr << "No image to process found" << std::endl << "Stopping processing" << std::endl;
00420         return 1;
00421     };
00422 
00423     PT_setProgressFcn(ptProgress);
00424     PT_setInfoDlgFcn(ptinfoDlg);
00425 
00426     std::cout << hugin_utils::stripPath(argv[0]) << " is searching for vertical lines" << std::endl;
00427 #if _WIN32
00428     //multi threading of image loading results sometime in a race condition
00429     //try to prevent this by initialisation of codecManager before
00430     //running multi threading part
00431     std::string s=vigra::impexListExtensions();
00432 #endif
00433 
00434     size_t nrCPS=pano.getNrOfCtrlPoints();
00435     #pragma omp parallel for schedule(dynamic)
00436     for(int i=0; i < imagesToProcess.size(); ++i)
00437     {
00438         unsigned int imgNr=imagesToProcess[i];
00439         std::ostringstream buf;
00440         buf << "Working on image " << pano.getImage(imgNr).getFilename() << std::endl;
00441         std::cout << buf.str();
00442         // now load and process all images
00443         vigra::ImageImportInfo info(pano.getImage(imgNr).getFilename().c_str());
00444         HuginBase::CPVector foundLines;
00445         if(info.isGrayscale())
00446         {
00447             foundLines=LoadGrayImageAndFindLines(info, pano, imgNr, nrLines);
00448         }
00449         else
00450         {
00451             if(info.isColor())
00452             {
00453                 //colour images
00454                 foundLines=LoadImageAndFindLines(info, pano, imgNr, nrLines);
00455             }
00456             else
00457             {
00458                 std::cerr << "Image " << pano.getImage(imgNr).getFilename().c_str() << " has "
00459                           << info.numBands() << " channels." << std::endl
00460                           << "Linefind works only with grayscale or color images." << std::endl
00461                           << "Skipping image." << std::endl;
00462             };
00463         };
00464         if(foundLines.size()>0)
00465         {
00466             for (HuginBase::CPVector::const_iterator cpIt = foundLines.begin(); cpIt != foundLines.end(); ++cpIt)
00467             {
00468                 hugin_omp::ScopedLock sl(lock);
00469                 pano.addCtrlPoint(*cpIt);
00470             };
00471         };
00472     }
00473     std::cout << std::endl << "Found " << pano.getNrOfCtrlPoints() - nrCPS << " vertical lines" << std::endl << std::endl;
00474 
00475     //write output
00476     HuginBase::UIntSet imgs;
00477     fill_set(imgs,0, pano.getNrOfImages()-1);
00478     // Set output .pto filename if not given
00479     if (output=="")
00480     {
00481         output=input.substr(0,input.length()-4).append("_lines.pto");
00482     }
00483     std::ofstream of(output.c_str());
00484     pano.printPanoramaScript(of, pano.getOptimizeVector(), pano.getOptions(), imgs, false, hugin_utils::getPathPrefix(input));
00485 
00486     std::cout << std::endl << "Written output to " << output << std::endl;
00487     return 0;
00488 }

Generated on 7 Feb 2016 for Hugintrunk by  doxygen 1.4.7