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 #include <panodata/Panorama.h>
00031 #include <panodata/StandardImageVariableGroups.h>
00032 #include <lines/FindLines.h>
00033 #include <vigra/impex.hxx>
00034 #include <vigra_ext/impexalpha.hxx>
00035 #include <vigra/functorexpression.hxx>
00036 #include <vigra_ext/utils.h>
00037 #include <hugin_utils/openmp_lock.h>
00038 
00039 extern "C"
00040 {
00041 #include <pano13/filter.h>
00042 }
00043 
00044 static void usage(const char* name)
00045 {
00046     std::cout << name << ": find vertical lines in images" << std::endl
00047               << name << " version " << hugin_utils::GetHuginVersion() << std::endl
00048               << std::endl
00049               << "Usage:  " << name << " [options] input.pto" << std::endl
00050               << std::endl
00051               << "  Options:" << std::endl
00052               << "     -o, --output=file.pto  Output Hugin PTO file. Default: <filename>_lines.pto" << std::endl
00053               << "     -i, --image=IMGNR      Work only on given image numbers" << std::endl
00054               << "     -l, --lines=COUNT      Save maximal COUNT lines (default: 5)" << std::endl
00055               << "     -h, --help             Shows this help" << std::endl
00056               << std::endl;
00057 }
00058 
00059 // dummy panotools progress functions
00060 static int ptProgress( int command, char* argument )
00061 {
00062     return 1;
00063 }
00064 static int ptinfoDlg( int command, char* argument )
00065 {
00066     return 1;
00067 }
00068 
00075 // 2 versions: one for color images, the other for gray images
00076 template <class SrcIMG>
00077 void convertToUInt8(SrcIMG& src, const std::string& origType, vigra::UInt8RGBImage& dest)
00078 {
00079     dest.resize(src.size());
00080     long newMax=vigra_ext::getMaxValForPixelType("UINT8");
00081     // float needs to be from min ... max.
00082     if (origType == "FLOAT" || origType == "DOUBLE")
00083     {
00087         vigra::RGBToGrayAccessor<vigra::RGBValue<float> > ga;
00088         vigra::FindMinMax<float> minmax;   // init functor
00089         vigra::inspectImage(srcImageRange(src, ga),
00090                             minmax);
00091         double minVal = minmax.min;
00092         double maxVal = minmax.max;
00093         vigra_ext::applyMapping(srcImageRange(src), destImage(dest), minVal, maxVal, 0);
00094     }
00095     else
00096     {
00097         vigra::transformImage(srcImageRange(src), destImage(dest),
00098                               vigra::functor::Arg1()*vigra::functor::Param( newMax/ vigra_ext::getMaxValForPixelType(origType)));
00099     };
00100 }
00101 
00102 template <class SrcIMG>
00103 void convertGrayToUInt8(SrcIMG& src, const std::string& origType, vigra::BImage& dest)
00104 {
00105     dest.resize(src.size());
00106     long newMax=vigra_ext::getMaxValForPixelType("UINT8");
00107     // float needs to be from min ... max.
00108     if (origType == "FLOAT" || origType == "DOUBLE")
00109     {
00113         vigra::FindMinMax<float> minmax;   // init functor
00114         vigra::inspectImage(srcImageRange(src), minmax);
00115         double minVal = minmax.min;
00116         double maxVal = minmax.max;
00117         vigra_ext::applyMapping(srcImageRange(src), destImage(dest), minVal, maxVal, 0);
00118     }
00119     else
00120     {
00121         vigra::transformImage(srcImageRange(src), destImage(dest),
00122                               vigra::functor::Arg1()*vigra::functor::Param( newMax/ vigra_ext::getMaxValForPixelType(origType)));
00123     };
00124 }
00125 
00126 template <class SrcIMG>
00127 vigra::BImage LoadGrayImageAndConvert(vigra::ImageImportInfo& info)
00128 {
00129     vigra::BImage image;
00130     SrcIMG imageIn(info.width(),info.height());
00131     if(info.numExtraBands()==1)
00132     {
00133         vigra::BImage mask(info.size());
00134         vigra::importImageAlpha(info,destImage(imageIn),destImage(mask));
00135         mask.resize(0,0);
00136     }
00137     else
00138     {
00139         importImage(info,destImage(imageIn));
00140     };
00141     convertGrayToUInt8(imageIn,info.getPixelType(),image);
00142     imageIn.resize(0,0);
00143     return image;
00144 };
00145 
00146 template <class SrcIMG>
00147 vigra::UInt8RGBImage LoadImageAndConvert(vigra::ImageImportInfo& info)
00148 {
00149     vigra::UInt8RGBImage image;
00150     SrcIMG imageIn(info.width(),info.height());
00151     if(info.numExtraBands()==1)
00152     {
00153         vigra::BImage mask(info.size());
00154         vigra::importImageAlpha(info,destImage(imageIn),destImage(mask));
00155         mask.resize(0,0);
00156     }
00157     else
00158     {
00159         importImage(info,destImage(imageIn));
00160     };
00161     convertToUInt8(imageIn,info.getPixelType(),image);
00162     imageIn.resize(0,0);
00163     return image;
00164 };
00165 
00166 // loads the gray images and finds vertical lines, returns a CPVector with found vertical lines
00167 HuginBase::CPVector LoadGrayImageAndFindLines(vigra::ImageImportInfo info, HuginBase::Panorama& pano, size_t imgNr, int nrLines)
00168 {
00169     vigra::BImage image;
00170     HuginBase::CPVector lineCp;
00171     std::string pixelType=info.getPixelType();
00172     if(pixelType=="UINT8")
00173     {
00174         image.resize(info.width(),info.height());
00175         if(info.numExtraBands()==1)
00176         {
00177             vigra::BImage mask(info.size());
00178             vigra::importImageAlpha(info,destImage(image),destImage(mask));
00179             mask.resize(0,0);
00180         }
00181         else
00182         {
00183             importImage(info,destImage(image));
00184         };
00185     }
00186     else
00187     {
00188         if(pixelType=="UINT16" || pixelType=="INT16")
00189         {
00190             image=LoadGrayImageAndConvert<vigra::UInt16Image>(info);
00191         }
00192         else
00193         {
00194             if(pixelType=="INT32" || pixelType=="UINT32")
00195             {
00196                 image=LoadGrayImageAndConvert<vigra::UInt32Image>(info);
00197             }
00198             else
00199             {
00200                 if(pixelType=="FLOAT" || pixelType=="DOUBLE")
00201                 {
00202                     image=LoadGrayImageAndConvert<vigra::FImage>(info);
00203                 }
00204                 else
00205                 {
00206                     std::cerr << "Unsupported pixel type" << std::endl;
00207                 };
00208             };
00209         };
00210     };
00211     if(image.width()>0 && image.height()>0)
00212     {
00213         lineCp=HuginLines::GetVerticalLines(pano, imgNr, image, nrLines);
00214     };
00215     return lineCp;
00216 };
00217 
00218 // loads the color images and finds vertical lines, returns a CPVector with found vertical lines
00219 HuginBase::CPVector LoadImageAndFindLines(vigra::ImageImportInfo info, HuginBase::Panorama& pano, size_t imgNr, int nrLines)
00220 {
00221     vigra::UInt8RGBImage image;
00222     HuginBase::CPVector lineCp;
00223     std::string pixelType=info.getPixelType();
00224     if(pixelType=="UINT8")
00225     {
00226         image.resize(info.width(),info.height());
00227         if(info.numExtraBands()==1)
00228         {
00229             vigra::BImage mask(info.size());
00230             vigra::importImageAlpha(info,destImage(image),destImage(mask));
00231             mask.resize(0,0);
00232         }
00233         else
00234         {
00235             importImage(info,destImage(image));
00236         };
00237     }
00238     else
00239     {
00240         if(pixelType=="UINT16" || pixelType=="INT16")
00241         {
00242             image=LoadImageAndConvert<vigra::UInt16RGBImage>(info);
00243         }
00244         else
00245         {
00246             if(pixelType=="INT32" || pixelType=="UINT32")
00247             {
00248                 image=LoadImageAndConvert<vigra::UInt32RGBImage>(info);
00249             }
00250             else
00251             {
00252                 if(pixelType=="FLOAT" || pixelType=="DOUBLE")
00253                 {
00254                     image=LoadImageAndConvert<vigra::FRGBImage>(info);
00255                 }
00256                 else
00257                 {
00258                     std::cerr << "Unsupported pixel type" << std::endl;
00259                 };
00260             };
00261         };
00262     };
00263     if(image.width()>0 && image.height()>0)
00264     {
00265         lineCp=HuginLines::GetVerticalLines(pano, imgNr, image, nrLines);
00266     }
00267     return lineCp;
00268 };
00269 
00270 struct SortVectorByExposure
00271 {
00272     explicit SortVectorByExposure(const HuginBase::Panorama& pano) : m_pano(pano) {};
00273     bool operator()(const size_t& img1, const size_t& img2)
00274     {
00275         return m_pano.getImage(img1).getExposureValue() < m_pano.getImage(img2).getExposureValue();
00276     }
00277 private:
00278     const HuginBase::Panorama& m_pano;
00279 };
00280 
00281 static hugin_omp::Lock lock;
00282 
00283 int main(int argc, char* argv[])
00284 {
00285     // parse arguments
00286     const char* optstring = "o:i:l:h";
00287 
00288     static struct option longOptions[] =
00289     {
00290         {"output", required_argument, NULL, 'o' },
00291         {"image", required_argument, NULL, 'i' },
00292         {"lines", required_argument, NULL, 'l' },
00293         {"help", no_argument, NULL, 'h' },
00294         0
00295     };
00296 
00297     HuginBase::UIntSet cmdlineImages;
00298     int c;
00299     int nrLines = 5;
00300     std::string output;
00301     while ((c = getopt_long (argc, argv, optstring, longOptions,nullptr)) != -1)
00302     {
00303         switch (c)
00304         {
00305             case 'o':
00306                 output = optarg;
00307                 break;
00308             case 'h':
00309                 usage(hugin_utils::stripPath(argv[0]).c_str());
00310                 return 0;
00311             case 'i':
00312                 {
00313                     int imgNr=atoi(optarg);
00314                     if((imgNr==0) && (strcmp(optarg,"0")!=0))
00315                     {
00316                         std::cerr << hugin_utils::stripPath(argv[0]) << ": Could not parse image number." << std::endl;
00317                         return 1;
00318                     };
00319                     cmdlineImages.insert(imgNr);
00320                 };
00321                 break;
00322             case 'l':
00323                 nrLines=atoi(optarg);
00324                 if(nrLines<1)
00325                 {
00326                     std::cerr << hugin_utils::stripPath(argv[0]) << ": Could not parse number of lines." << std::endl;
00327                     return 1;
00328                 };
00329                 break;
00330             case ':':
00331             case '?':
00332                 // missing argument or invalid switch
00333                 return 1;
00334                 break;
00335             default:
00336                 // this should not happen
00337                 abort();
00338         }
00339     }
00340 
00341     if (argc - optind != 1)
00342     {
00343         if (argc - optind < 1)
00344         {
00345             std::cerr << hugin_utils::stripPath(argv[0]) << ": No project file given." << std::endl;
00346         }
00347         else
00348         {
00349             std::cerr << hugin_utils::stripPath(argv[0]) << ": Only one project file expected." << std::endl;
00350         };
00351         return 1;
00352     };
00353 
00354 
00355     std::string input=argv[optind];
00356     // read panorama
00357     HuginBase::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     AppBase::DocumentData::ReadWriteError err = pano.readData(prjfile);
00366     if (err != AppBase::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 (HuginBase::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 _WIN32
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 (HuginBase::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     HuginBase::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 2 Dec 2016 for Hugintrunk by  doxygen 1.4.7