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

Generated on 20 Apr 2018 for Hugintrunk by  doxygen 1.4.7