Hugintrunk  0.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
linefind.cpp
Go to the documentation of this file.
1 // -*- c-basic-offset: 4 -*-
2 
11 /* This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public
13  * License as published by the Free Software Foundation; either
14  * version 2 of the License, or (at your option) any later version.
15  *
16  * This software is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19  * General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public
22  * License along with this software. If not, see
23  * <http://www.gnu.org/licenses/>.
24  *
25  */
26 
27 #include <fstream>
28 #include <sstream>
29 #include <getopt.h>
30 #include <panodata/Panorama.h>
32 #include <lines/FindLines.h>
33 #include <vigra/impex.hxx>
34 #include <vigra_ext/impexalpha.hxx>
35 #include <vigra/functorexpression.hxx>
36 #include <vigra_ext/utils.h>
39 
40 extern "C"
41 {
42 #include <pano13/filter.h>
43 }
44 
45 static void usage(const char* name)
46 {
47  std::cout << name << ": find vertical lines in images" << std::endl
48  << name << " version " << hugin_utils::GetHuginVersion() << std::endl
49  << std::endl
50  << "Usage: " << name << " [options] input.pto" << std::endl
51  << std::endl
52  << " Options:" << std::endl
53  << " -o, --output=file.pto Output Hugin PTO file. Default: <filename>_lines.pto" << std::endl
54  << " -i, --image=IMGNR Work only on given image numbers" << std::endl
55  << " -l, --lines=COUNT Save maximal COUNT lines (default: 5)" << std::endl
56  << " -h, --help Shows this help" << std::endl
57  << std::endl;
58 }
59 
60 // dummy panotools progress functions
61 static int ptProgress( int command, char* argument )
62 {
63  return 1;
64 }
65 static int ptinfoDlg( int command, char* argument )
66 {
67  return 1;
68 }
69 
76 // 2 versions: one for color images, the other for gray images
77 template <class SrcIMG>
78 void convertToUInt8(SrcIMG& src, const std::string& origType, vigra::UInt8RGBImage& dest)
79 {
80  dest.resize(src.size());
81  long newMax=vigra_ext::getMaxValForPixelType("UINT8");
82  // float needs to be from min ... max.
83  if (origType == "FLOAT" || origType == "DOUBLE")
84  {
88  vigra::RGBToGrayAccessor<vigra::RGBValue<float> > ga;
89  vigra::FindMinMax<float> minmax; // init functor
90  vigra::inspectImage(srcImageRange(src, ga),
91  minmax);
92  double minVal = minmax.min;
93  double maxVal = minmax.max;
94  vigra_ext::applyMapping(srcImageRange(src), destImage(dest), minVal, maxVal, 0);
95  }
96  else
97  {
99  vigra::functor::Arg1()*vigra::functor::Param( newMax/ vigra_ext::getMaxValForPixelType(origType)));
100  };
101 }
102 
103 template <class SrcIMG>
104 void convertGrayToUInt8(SrcIMG& src, const std::string& origType, vigra::BImage& dest)
105 {
106  dest.resize(src.size());
107  long newMax=vigra_ext::getMaxValForPixelType("UINT8");
108  // float needs to be from min ... max.
109  if (origType == "FLOAT" || origType == "DOUBLE")
110  {
114  vigra::FindMinMax<float> minmax; // init functor
115  vigra::inspectImage(srcImageRange(src), minmax);
116  double minVal = minmax.min;
117  double maxVal = minmax.max;
118  vigra_ext::applyMapping(srcImageRange(src), destImage(dest), minVal, maxVal, 0);
119  }
120  else
121  {
123  vigra::functor::Arg1()*vigra::functor::Param( newMax/ vigra_ext::getMaxValForPixelType(origType)));
124  };
125 }
126 
127 template <class SrcIMG>
128 vigra::BImage LoadGrayImageAndConvert(vigra::ImageImportInfo& info, vigra::BImage& mask)
129 {
130  vigra::BImage image;
131  SrcIMG imageIn(info.width(),info.height());
132  if(info.numExtraBands()==1)
133  {
134  mask.resize(info.width(), info.height());
135  vigra::importImageAlpha(info,destImage(imageIn),destImage(mask));
136  }
137  else
138  {
139  importImage(info,destImage(imageIn));
140  };
141  convertGrayToUInt8(imageIn,info.getPixelType(),image);
142  imageIn.resize(0,0);
143  return image;
144 };
145 
146 template <class SrcIMG>
147 vigra::UInt8RGBImage LoadImageAndConvert(vigra::ImageImportInfo& info, vigra::BImage& mask)
148 {
149  vigra::UInt8RGBImage image;
150  SrcIMG imageIn(info.width(),info.height());
151  if(info.numExtraBands()==1)
152  {
153  mask.resize(info.width(), info.height());
154  vigra::importImageAlpha(info,destImage(imageIn),destImage(mask));
155  }
156  else
157  {
158  importImage(info,destImage(imageIn));
159  };
160  convertToUInt8(imageIn,info.getPixelType(),image);
161  imageIn.resize(0,0);
162  return image;
163 };
164 
165 // loads the gray images and finds vertical lines, returns a CPVector with found vertical lines
166 HuginBase::CPVector LoadGrayImageAndFindLines(vigra::ImageImportInfo info, HuginBase::Panorama& pano, size_t imgNr, int nrLines)
167 {
168  vigra::BImage image;
169  vigra::BImage mask;
170  HuginBase::CPVector lineCp;
171  std::string pixelType=info.getPixelType();
172  if(pixelType=="UINT8")
173  {
174  image.resize(info.width(),info.height());
175  if(info.numExtraBands()==1)
176  {
177  mask.resize(info.width(), info.height());
178  vigra::importImageAlpha(info,destImage(image),destImage(mask));
179  }
180  else
181  {
182  importImage(info,destImage(image));
183  };
184  }
185  else
186  {
187  if(pixelType=="UINT16" || pixelType=="INT16")
188  {
189  image=LoadGrayImageAndConvert<vigra::UInt16Image>(info, mask);
190  }
191  else
192  {
193  if(pixelType=="INT32" || pixelType=="UINT32")
194  {
195  image=LoadGrayImageAndConvert<vigra::UInt32Image>(info, mask);
196  }
197  else
198  {
199  if(pixelType=="FLOAT" || pixelType=="DOUBLE")
200  {
201  image=LoadGrayImageAndConvert<vigra::FImage>(info, mask);
202  }
203  else
204  {
205  std::cerr << "Unsupported pixel type" << std::endl;
206  };
207  };
208  };
209  };
210  if(image.width()>0 && image.height()>0)
211  {
212  lineCp=HuginLines::GetVerticalLines(pano, imgNr, image, mask, nrLines);
213  };
214  return lineCp;
215 };
216 
217 // loads the color images and finds vertical lines, returns a CPVector with found vertical lines
218 HuginBase::CPVector LoadImageAndFindLines(vigra::ImageImportInfo info, HuginBase::Panorama& pano, size_t imgNr, int nrLines)
219 {
220  vigra::UInt8RGBImage image;
221  vigra::BImage mask;
222  HuginBase::CPVector lineCp;
223  std::string pixelType=info.getPixelType();
224  if(pixelType=="UINT8")
225  {
226  image.resize(info.width(),info.height());
227  if(info.numExtraBands()==1)
228  {
229  mask.resize(info.width(), info.height());
230  vigra::importImageAlpha(info,destImage(image),destImage(mask));
231  }
232  else
233  {
234  importImage(info,destImage(image));
235  };
236  }
237  else
238  {
239  if(pixelType=="UINT16" || pixelType=="INT16")
240  {
241  image=LoadImageAndConvert<vigra::UInt16RGBImage>(info, mask);
242  }
243  else
244  {
245  if(pixelType=="INT32" || pixelType=="UINT32")
246  {
247  image=LoadImageAndConvert<vigra::UInt32RGBImage>(info, mask);
248  }
249  else
250  {
251  if(pixelType=="FLOAT" || pixelType=="DOUBLE")
252  {
253  image=LoadImageAndConvert<vigra::FRGBImage>(info, mask);
254  }
255  else
256  {
257  std::cerr << "Unsupported pixel type" << std::endl;
258  };
259  };
260  };
261  };
262  if(image.width()>0 && image.height()>0)
263  {
264  lineCp=HuginLines::GetVerticalLines(pano, imgNr, image, mask, nrLines);
265  }
266  return lineCp;
267 };
268 
270 {
271  explicit SortVectorByExposure(const HuginBase::Panorama& pano) : m_pano(pano) {};
272  bool operator()(const size_t& img1, const size_t& img2)
273  {
274  return m_pano.getImage(img1).getExposureValue() < m_pano.getImage(img2).getExposureValue();
275  }
276 private:
278 };
279 
281 
282 int main(int argc, char* argv[])
283 {
284  // parse arguments
285  const char* optstring = "o:i:l:h";
286 
287  static struct option longOptions[] =
288  {
289  {"output", required_argument, NULL, 'o' },
290  {"image", required_argument, NULL, 'i' },
291  {"lines", required_argument, NULL, 'l' },
292  {"help", no_argument, NULL, 'h' },
293  0
294  };
295 
296  HuginBase::UIntSet cmdlineImages;
297  int c;
298  int nrLines = 5;
299  std::string output;
300  while ((c = getopt_long (argc, argv, optstring, longOptions,nullptr)) != -1)
301  {
302  switch (c)
303  {
304  case 'o':
305  output = optarg;
306  break;
307  case 'h':
308  usage(hugin_utils::stripPath(argv[0]).c_str());
309  return 0;
310  case 'i':
311  {
312  int imgNr=atoi(optarg);
313  if((imgNr==0) && (strcmp(optarg,"0")!=0))
314  {
315  std::cerr << hugin_utils::stripPath(argv[0]) << ": Could not parse image number." << std::endl;
316  return 1;
317  };
318  cmdlineImages.insert(imgNr);
319  };
320  break;
321  case 'l':
322  nrLines=atoi(optarg);
323  if(nrLines<1)
324  {
325  std::cerr << hugin_utils::stripPath(argv[0]) << ": Could not parse number of lines." << std::endl;
326  return 1;
327  };
328  break;
329  case ':':
330  case '?':
331  // missing argument or invalid switch
332  return 1;
333  break;
334  default:
335  // this should not happen
336  abort();
337  }
338  }
339 
340  if (argc - optind != 1)
341  {
342  if (argc - optind < 1)
343  {
344  std::cerr << hugin_utils::stripPath(argv[0]) << ": No project file given." << std::endl;
345  }
346  else
347  {
348  std::cerr << hugin_utils::stripPath(argv[0]) << ": Only one project file expected." << std::endl;
349  };
350  return 1;
351  };
352 
353 
354  std::string input=argv[optind];
355  // read panorama
356  HuginBase::Panorama pano;
357  if (!pano.ReadPTOFile(input, hugin_utils::getPathPrefix(input)))
358  {
359  return 1;
360  };
361 
362  // disable progress messages from libpano optimizer
363  PT_setProgressFcn(ptProgress);
364  PT_setInfoDlgFcn(ptinfoDlg);
365 
366  std::vector<size_t> imagesToProcess;
367  if(cmdlineImages.empty())
368  {
369  //no image given, process one image of each stack
370  HuginBase::ConstStandardImageVariableGroups variable_groups(pano);
371  HuginBase::UIntSetVector imageGroups = variable_groups.getStacks().getPartsSet();
372  //get image with median exposure for search for lines
373  for (size_t imgGroup = 0; imgGroup < imageGroups.size(); ++imgGroup)
374  {
375  HuginBase::UIntVector stackImages(imageGroups[imgGroup].begin(), imageGroups[imgGroup].end());
376  if (pano.getImage(stackImages[0]).YawisLinked())
377  {
378  // position is linked, take only image with median exposure for search
379  std::sort(stackImages.begin(), stackImages.end(), SortVectorByExposure(pano));
380  size_t index = 0;
381  if (pano.getImage(*(stackImages.begin())).getExposureValue() != pano.getImage(*(stackImages.rbegin())).getExposureValue())
382  {
383  index = stackImages.size() / 2;
384  };
385  imagesToProcess.push_back(stackImages[index]);
386  }
387  else
388  {
389  // stacks are unlinked, search all images
390  std::copy(stackImages.begin(), stackImages.end(), std::back_inserter(imagesToProcess));
391  };
392  };
393  if (pano.getNrOfCtrlPoints())
394  {
395  // pano has already control points, so do a pair-wise optimisation to get
396  // rough positions of the images, so we can decide which images are near
397  // the zenit/nadir and ignore these images
398  HuginBase::Panorama optPano = pano.duplicate();
399  // reset translation parameters
400  HuginBase::VariableMapVector varMapVec = optPano.getVariables();
401  for (size_t i = 0; i < varMapVec.size(); i++)
402  {
403  map_get(varMapVec[i], "TrX").setValue(0);
404  map_get(varMapVec[i], "TrY").setValue(0);
405  map_get(varMapVec[i], "TrZ").setValue(0);
406  };
407  optPano.updateVariables(varMapVec);
408  // now do a pairwise optimisation
409  HuginBase::AutoOptimise(optPano).run();
410  std::vector<size_t> imagesToCheck;
411  std::swap(imagesToProcess, imagesToCheck);
412  for (auto& img:imagesToCheck)
413  {
414  // now remove all images from list where pitch > 50 or < -50 -> images near zenit/nadir
415  if (std::abs(optPano.getImage(img).getPitch()) < 50)
416  {
417  imagesToProcess.push_back(img);
418  };
419  };
420  }
421  }
422  else
423  {
424  //check, if given image numbers are valid
425  for (HuginBase::UIntSet::const_iterator it = cmdlineImages.begin(); it != cmdlineImages.end(); ++it)
426  {
427  if((*it)>=0 && (*it)<pano.getNrOfImages())
428  {
429  imagesToProcess.push_back(*it);
430  };
431  };
432  };
433 
434  if(imagesToProcess.empty())
435  {
436  std::cerr << "No image to process found" << std::endl << "Stopping processing" << std::endl;
437  return 1;
438  };
439 
440  std::cout << hugin_utils::stripPath(argv[0]) << " is searching for vertical lines" << std::endl;
441 #if _WIN32
442  //multi threading of image loading results sometime in a race condition
443  //try to prevent this by initialisation of codecManager before
444  //running multi threading part
445  std::string s=vigra::impexListExtensions();
446 #endif
447 
448  const size_t nrCPS=pano.getNrOfCtrlPoints();
449 #pragma omp parallel for schedule(dynamic)
450  for(int i=0; i < imagesToProcess.size(); ++i)
451  {
452  const size_t imgNr = imagesToProcess[i];
453  std::ostringstream buf;
454  buf << "Working on image " << pano.getImage(imgNr).getFilename() << std::endl;
455  std::cout << buf.str();
456  // now load and process all images
457  vigra::ImageImportInfo info(pano.getImage(imgNr).getFilename().c_str());
458  HuginBase::CPVector foundLines;
459  if(info.isGrayscale())
460  {
461  foundLines=LoadGrayImageAndFindLines(info, pano, imgNr, nrLines);
462  }
463  else
464  {
465  if(info.isColor())
466  {
467  //colour images
468  foundLines=LoadImageAndFindLines(info, pano, imgNr, nrLines);
469  }
470  else
471  {
472  std::cerr << "Image " << pano.getImage(imgNr).getFilename().c_str() << " has "
473  << info.numBands() << " channels." << std::endl
474  << "Linefind works only with grayscale or color images." << std::endl
475  << "Skipping image." << std::endl;
476  };
477  };
478  if(!foundLines.empty())
479  {
480  for (HuginBase::CPVector::const_iterator cpIt = foundLines.begin(); cpIt != foundLines.end(); ++cpIt)
481  {
482  hugin_omp::ScopedLock sl(lock);
483  pano.addCtrlPoint(*cpIt);
484  };
485  };
486  }
487  std::cout << std::endl << "Found " << pano.getNrOfCtrlPoints() - nrCPS << " vertical lines" << std::endl << std::endl;
488 
489  //write output
490  // Set output .pto filename if not given
491  output = hugin_utils::GetOutputFilename(output, input, "line");
492  if (pano.WritePTOFile(output, hugin_utils::getPathPrefix(output)))
493  {
494  std::cout << std::endl << "Written output to " << output << std::endl;
495  };
496  return 0;
497 }
static void swap(T &x, T &y)
Definition: svm.cpp:67
double getMaxValForPixelType(const std::string &v)
Definition: utils.h:89
std::vector< UIntSet > UIntSetVector
Definition: PanoramaData.h:56
void transformImage(vigra::triple< SrcImageIterator, SrcImageIterator, SrcAccessor > src, vigra::triple< DestImageIterator, DestImageIterator, DestAccessor > dest, std::pair< AlphaImageIterator, AlphaAccessor > alpha, vigra::Diff2D destUL, TRANSFORM &transform, PixelTransform &pixelTransform, bool warparound, Interpolator interpol, AppBase::ProgressDisplay *progress, bool singleThreaded=false)
Transform an image into the panorama.
std::string GetOutputFilename(const std::string &out, const std::string &in, const std::string &suffix)
construct output filename, if ouput is known return this value otherwise use the input filename and a...
Definition: utils.cpp:420
HuginBase::CPVector GetVerticalLines(const HuginBase::Panorama &pano, const unsigned int imgNr, vigra::UInt8RGBImage &image, vigra::BImage &mask, const unsigned int nrLines)
searches for vertical control points in given image
Definition: FindLines.cpp:574
bool operator()(const size_t &img1, const size_t &img2)
Definition: linefind.cpp:272
ConstImageVariableGroup & getStacks()
Get the ImageVariableGroup representing the group of stack variables.
declaration of functions for finding lines
Somewhere to specify what variables belong to what.
std::size_t getNrOfCtrlPoints() const
number of control points
Definition: Panorama.h:306
virtual void run()
runs the algorithm.
void applyMapping(vigra::triple< SrcIterator, SrcIterator, SrcAccessor > img, vigra::pair< DestIterator, DestAccessor > dest, T min, T max, int mapping)
Definition: utils.h:685
SortVectorByExposure(const HuginBase::Panorama &pano)
Definition: linefind.cpp:271
virtual void updateVariables(const VariableMapVector &vars)
Set the variables.
Definition: Panorama.cpp:171
functions to manage ROI&#39;s
static hugin_omp::Lock lock
Definition: linefind.cpp:280
Panorama duplicate() const
duplicate the panorama
Definition: Panorama.cpp:1653
std::set< unsigned int > UIntSet
Definition: PanoramaData.h:51
helper for OpenMP
std::vector< VariableMap > VariableMapVector
Model for a panorama.
Definition: Panorama.h:152
UIntSetVector getPartsSet() const
return a vector which contains a HuginBase::UIntSet for each group with the corresponding images numb...
unsigned int addCtrlPoint(const ControlPoint &point)
add a new control point.
Definition: Panorama.cpp:381
std::string getPathPrefix(const std::string &filename)
Get the path to a filename.
Definition: utils.cpp:184
vigra::BImage LoadGrayImageAndConvert(vigra::ImageImportInfo &info, vigra::BImage &mask)
Definition: linefind.cpp:128
VariableMapVector getVariables() const
get variables of this panorama
Definition: Panorama.cpp:118
std::size_t getNrOfImages() const
number of images.
Definition: Panorama.h:205
HuginBase::CPVector LoadImageAndFindLines(vigra::ImageImportInfo info, HuginBase::Panorama &pano, size_t imgNr, int nrLines)
Definition: linefind.cpp:218
std::vector< unsigned int > UIntVector
Definition: PanoramaData.h:54
Map::mapped_type & map_get(Map &m, const typename Map::key_type &key)
get a map element.
Definition: stl_utils.h:98
bool ReadPTOFile(const std::string &filename, const std::string &prefix="")
read pto file from the given filename into Panorama object it does some checks on the file and issues...
Definition: Panorama.cpp:2023
void convertToUInt8(SrcIMG &src, const std::string &origType, vigra::UInt8RGBImage &dest)
converts the given image to UInt8RGBImage only this image is correctly processed by linefind ...
Definition: linefind.cpp:78
Make an ImageVariableGroup for lenses and other common concepts.
vigra::pair< typename ROIImage< Image, Alpha >::image_traverser, typename ROIImage< Image, Alpha >::ImageAccessor > destImage(ROIImage< Image, Alpha > &img)
Definition: ROIImage.h:324
vigra::triple< typename ROIImage< Image, Mask >::image_const_traverser, typename ROIImage< Image, Mask >::image_const_traverser, typename ROIImage< Image, Mask >::ImageConstAccessor > srcImageRange(const ROIImage< Image, Mask > &img)
helper function for ROIImages
Definition: ROIImage.h:287
!! from PTOptimise.h 1951
HuginBase::CPVector LoadGrayImageAndFindLines(vigra::ImageImportInfo info, HuginBase::Panorama &pano, size_t imgNr, int nrLines)
Definition: linefind.cpp:166
static int ptinfoDlg(int command, char *argument)
Definition: linefind.cpp:65
void importImageAlpha(const ImageImportInfo &import_info, ImageIterator image_iterator, ImageAccessor image_accessor, AlphaIterator alpha_iterator, AlphaAccessor alpha_accessor)
Read the image specified by the given vigra::ImageImportInfo object including its alpha channel...
Definition: impexalpha.hxx:479
vigra::UInt8RGBImage LoadImageAndConvert(vigra::ImageImportInfo &info, vigra::BImage &mask)
Definition: linefind.cpp:147
static void usage()
Definition: Main.cpp:32
std::string GetHuginVersion()
return a string with version numbers
Definition: utils.cpp:920
std::vector< ControlPoint > CPVector
Definition: ControlPoint.h:99
const HuginBase::Panorama & m_pano
Definition: linefind.cpp:277
bool WritePTOFile(const std::string &filename, const std::string &prefix="")
write data to given pto file
Definition: Panorama.cpp:2059
static void info(const char *fmt,...)
Definition: svm.cpp:95
const SrcPanoImage & getImage(std::size_t nr) const
get a panorama image, counting starts with 0
Definition: Panorama.h:211
void convertGrayToUInt8(SrcIMG &src, const std::string &origType, vigra::BImage &dest)
Definition: linefind.cpp:104
static int ptProgress(int command, char *argument)
Definition: linefind.cpp:61
std::string stripPath(const std::string &filename)
remove the path of a filename (mainly useful for gui display of filenames)
Definition: utils.cpp:160
int main(int argc, char *argv[])
Definition: Main.cpp:167