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

Generated on 31 Aug 2015 for Hugintrunk by  doxygen 1.4.7