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" || origType == "UINT32" || origType == "INT32")
84  {
88  vigra::FindAverageAndVariance<float> mean; // init functor
89  vigra::inspectImage(srcImageRange(src, vigra::RGBToGrayAccessor<typename SrcIMG::PixelType>()), mean);
90  const double minVal = std::max(mean.average() - 3 * sqrt(mean.variance()), 1e-6f);
91  const double maxVal = mean.average() + 3 * sqrt(mean.variance());
92  vigra_ext::applyMapping(srcImageRange(src), destImage(dest), minVal, maxVal, 1);
93  }
94  else
95  {
97  vigra::functor::Arg1()*vigra::functor::Param( newMax/ vigra_ext::getMaxValForPixelType(origType)));
98  };
99 }
100 
101 template <class SrcIMG>
102 void convertGrayToUInt8(SrcIMG& src, const std::string& origType, vigra::BImage& dest)
103 {
104  dest.resize(src.size());
105  long newMax=vigra_ext::getMaxValForPixelType("UINT8");
106  // float needs to be from min ... max.
107  if (origType == "FLOAT" || origType == "DOUBLE" || origType == "UINT32" || origType == "INT32")
108  {
112  vigra::FindAverageAndVariance<float> mean; // init functor
113  vigra::inspectImage(srcImageRange(src), mean);
114  const double minVal = std::max(mean.average() - 3 * sqrt(mean.variance()), 1e-6f);
115  const double maxVal = mean.average() + 3 * sqrt(mean.variance());
116  vigra_ext::applyMapping(srcImageRange(src), destImage(dest), minVal, maxVal, 1);
117  }
118  else
119  {
121  vigra::functor::Arg1()*vigra::functor::Param( newMax/ vigra_ext::getMaxValForPixelType(origType)));
122  };
123 }
124 
125 template <class SrcIMG>
126 vigra::BImage LoadGrayImageAndConvert(vigra::ImageImportInfo& info, vigra::BImage& mask)
127 {
128  vigra::BImage image;
129  SrcIMG imageIn(info.width(),info.height());
130  if(info.numExtraBands()==1)
131  {
132  mask.resize(info.width(), info.height());
133  vigra::importImageAlpha(info,destImage(imageIn),destImage(mask));
134  }
135  else
136  {
137  importImage(info,destImage(imageIn));
138  };
139  convertGrayToUInt8(imageIn,info.getPixelType(),image);
140  imageIn.resize(0,0);
141  return image;
142 };
143 
144 template <class SrcIMG>
145 vigra::UInt8RGBImage LoadImageAndConvert(vigra::ImageImportInfo& info, vigra::BImage& mask)
146 {
147  vigra::UInt8RGBImage image;
148  SrcIMG imageIn(info.width(),info.height());
149  if(info.numExtraBands()==1)
150  {
151  mask.resize(info.width(), info.height());
152  vigra::importImageAlpha(info,destImage(imageIn),destImage(mask));
153  }
154  else
155  {
156  importImage(info,destImage(imageIn));
157  };
158  convertToUInt8(imageIn,info.getPixelType(),image);
159  imageIn.resize(0,0);
160  return image;
161 };
162 
163 // loads the gray images and finds vertical lines, returns a CPVector with found vertical lines
164 HuginBase::CPVector LoadGrayImageAndFindLines(vigra::ImageImportInfo info, HuginBase::Panorama& pano, size_t imgNr, int nrLines)
165 {
166  vigra::BImage image;
167  vigra::BImage mask;
168  HuginBase::CPVector lineCp;
169  std::string pixelType=info.getPixelType();
170  if(pixelType=="UINT8")
171  {
172  image.resize(info.width(),info.height());
173  if(info.numExtraBands()==1)
174  {
175  mask.resize(info.width(), info.height());
176  vigra::importImageAlpha(info,destImage(image),destImage(mask));
177  }
178  else
179  {
180  importImage(info,destImage(image));
181  };
182  }
183  else
184  {
185  if(pixelType=="UINT16" || pixelType=="INT16")
186  {
187  image=LoadGrayImageAndConvert<vigra::UInt16Image>(info, mask);
188  }
189  else
190  {
191  if(pixelType=="INT32" || pixelType=="UINT32")
192  {
193  image=LoadGrayImageAndConvert<vigra::UInt32Image>(info, mask);
194  }
195  else
196  {
197  if(pixelType=="FLOAT" || pixelType=="DOUBLE")
198  {
199  image=LoadGrayImageAndConvert<vigra::FImage>(info, mask);
200  }
201  else
202  {
203  std::cerr << "Unsupported pixel type" << std::endl;
204  };
205  };
206  };
207  };
208  if(image.width()>0 && image.height()>0)
209  {
210  lineCp=HuginLines::GetVerticalLines(pano, imgNr, image, mask, nrLines);
211  };
212  return lineCp;
213 };
214 
215 // loads the color images and finds vertical lines, returns a CPVector with found vertical lines
216 HuginBase::CPVector LoadImageAndFindLines(vigra::ImageImportInfo info, HuginBase::Panorama& pano, size_t imgNr, int nrLines)
217 {
218  vigra::UInt8RGBImage image;
219  vigra::BImage mask;
220  HuginBase::CPVector lineCp;
221  std::string pixelType=info.getPixelType();
222  if(pixelType=="UINT8")
223  {
224  image.resize(info.width(),info.height());
225  if(info.numExtraBands()==1)
226  {
227  mask.resize(info.width(), info.height());
228  vigra::importImageAlpha(info,destImage(image),destImage(mask));
229  }
230  else
231  {
232  importImage(info,destImage(image));
233  };
234  }
235  else
236  {
237  if(pixelType=="UINT16" || pixelType=="INT16")
238  {
239  image=LoadImageAndConvert<vigra::UInt16RGBImage>(info, mask);
240  }
241  else
242  {
243  if(pixelType=="INT32" || pixelType=="UINT32")
244  {
245  image=LoadImageAndConvert<vigra::UInt32RGBImage>(info, mask);
246  }
247  else
248  {
249  if(pixelType=="FLOAT" || pixelType=="DOUBLE")
250  {
251  image=LoadImageAndConvert<vigra::FRGBImage>(info, mask);
252  }
253  else
254  {
255  std::cerr << "Unsupported pixel type" << std::endl;
256  };
257  };
258  };
259  };
260  if(image.width()>0 && image.height()>0)
261  {
262  lineCp=HuginLines::GetVerticalLines(pano, imgNr, image, mask, nrLines);
263  }
264  return lineCp;
265 };
266 
268 {
269  explicit SortVectorByExposure(const HuginBase::Panorama& pano) : m_pano(pano) {};
270  bool operator()(const size_t& img1, const size_t& img2)
271  {
272  return m_pano.getImage(img1).getExposureValue() < m_pano.getImage(img2).getExposureValue();
273  }
274 private:
276 };
277 
279 
280 int main(int argc, char* argv[])
281 {
282  // parse arguments
283  const char* optstring = "o:i:l:h";
284 
285  static struct option longOptions[] =
286  {
287  {"output", required_argument, NULL, 'o' },
288  {"image", required_argument, NULL, 'i' },
289  {"lines", required_argument, NULL, 'l' },
290  {"help", no_argument, NULL, 'h' },
291  0
292  };
293 
294  HuginBase::UIntSet cmdlineImages;
295  int c;
296  int nrLines = 5;
297  std::string output;
298  while ((c = getopt_long (argc, argv, optstring, longOptions,nullptr)) != -1)
299  {
300  switch (c)
301  {
302  case 'o':
303  output = optarg;
304  break;
305  case 'h':
306  usage(hugin_utils::stripPath(argv[0]).c_str());
307  return 0;
308  case 'i':
309  {
310  int imgNr=atoi(optarg);
311  if((imgNr==0) && (strcmp(optarg,"0")!=0))
312  {
313  std::cerr << hugin_utils::stripPath(argv[0]) << ": Could not parse image number." << std::endl;
314  return 1;
315  };
316  cmdlineImages.insert(imgNr);
317  };
318  break;
319  case 'l':
320  nrLines=atoi(optarg);
321  if(nrLines<1)
322  {
323  std::cerr << hugin_utils::stripPath(argv[0]) << ": Could not parse number of lines." << std::endl;
324  return 1;
325  };
326  break;
327  case ':':
328  case '?':
329  // missing argument or invalid switch
330  return 1;
331  break;
332  default:
333  // this should not happen
334  abort();
335  }
336  }
337 
338  if (argc - optind != 1)
339  {
340  if (argc - optind < 1)
341  {
342  std::cerr << hugin_utils::stripPath(argv[0]) << ": No project file given." << std::endl;
343  }
344  else
345  {
346  std::cerr << hugin_utils::stripPath(argv[0]) << ": Only one project file expected." << std::endl;
347  };
348  return 1;
349  };
350 
351 
352  std::string input=argv[optind];
353  // read panorama
354  HuginBase::Panorama pano;
355  if (!pano.ReadPTOFile(input, hugin_utils::getPathPrefix(input)))
356  {
357  return 1;
358  };
359 
360  // disable progress messages from libpano optimizer
361  PT_setProgressFcn(ptProgress);
362  PT_setInfoDlgFcn(ptinfoDlg);
363 
364  std::vector<size_t> imagesToProcess;
365  if(cmdlineImages.empty())
366  {
367  //no image given, process one image of each stack
368  HuginBase::ConstStandardImageVariableGroups variable_groups(pano);
369  HuginBase::UIntSetVector imageGroups = variable_groups.getStacks().getPartsSet();
370  //get image with median exposure for search for lines
371  for (size_t imgGroup = 0; imgGroup < imageGroups.size(); ++imgGroup)
372  {
373  HuginBase::UIntVector stackImages(imageGroups[imgGroup].begin(), imageGroups[imgGroup].end());
374  if (pano.getImage(stackImages[0]).YawisLinked())
375  {
376  // position is linked, take only image with median exposure for search
377  std::sort(stackImages.begin(), stackImages.end(), SortVectorByExposure(pano));
378  size_t index = 0;
379  if (pano.getImage(*(stackImages.begin())).getExposureValue() != pano.getImage(*(stackImages.rbegin())).getExposureValue())
380  {
381  index = stackImages.size() / 2;
382  };
383  imagesToProcess.push_back(stackImages[index]);
384  }
385  else
386  {
387  // stacks are unlinked, search all images
388  std::copy(stackImages.begin(), stackImages.end(), std::back_inserter(imagesToProcess));
389  };
390  };
391  if (pano.getNrOfCtrlPoints())
392  {
393  // pano has already control points, so do a pair-wise optimisation to get
394  // rough positions of the images, so we can decide which images are near
395  // the zenit/nadir and ignore these images
396  HuginBase::Panorama optPano = pano.duplicate();
397  // reset translation parameters
398  HuginBase::VariableMapVector varMapVec = optPano.getVariables();
399  for (size_t i = 0; i < varMapVec.size(); i++)
400  {
401  map_get(varMapVec[i], "TrX").setValue(0);
402  map_get(varMapVec[i], "TrY").setValue(0);
403  map_get(varMapVec[i], "TrZ").setValue(0);
404  };
405  optPano.updateVariables(varMapVec);
406  // now do a pairwise optimisation
407  HuginBase::AutoOptimise(optPano).run();
408  std::vector<size_t> imagesToCheck;
409  std::swap(imagesToProcess, imagesToCheck);
410  for (auto& img:imagesToCheck)
411  {
412  // now remove all images from list where pitch > 50 or < -50 -> images near zenit/nadir
413  if (std::abs(optPano.getImage(img).getPitch()) < 50)
414  {
415  imagesToProcess.push_back(img);
416  };
417  };
418  }
419  }
420  else
421  {
422  //check, if given image numbers are valid
423  for (HuginBase::UIntSet::const_iterator it = cmdlineImages.begin(); it != cmdlineImages.end(); ++it)
424  {
425  if((*it)>=0 && (*it)<pano.getNrOfImages())
426  {
427  imagesToProcess.push_back(*it);
428  };
429  };
430  };
431 
432  if(imagesToProcess.empty())
433  {
434  std::cerr << "No image to process found" << std::endl << "Stopping processing" << std::endl;
435  return 1;
436  };
437 
438  std::cout << hugin_utils::stripPath(argv[0]) << " is searching for vertical lines" << std::endl;
439 #if _WIN32
440  //multi threading of image loading results sometime in a race condition
441  //try to prevent this by initialisation of codecManager before
442  //running multi threading part
443  std::string s=vigra::impexListExtensions();
444 #endif
445 
446  const size_t nrCPS=pano.getNrOfCtrlPoints();
447 #pragma omp parallel for schedule(dynamic)
448  for(int i=0; i < imagesToProcess.size(); ++i)
449  {
450  const size_t imgNr = imagesToProcess[i];
451  std::ostringstream buf;
452  buf << "Working on image " << pano.getImage(imgNr).getFilename() << std::endl;
453  std::cout << buf.str();
454  // now load and process all images
455  vigra::ImageImportInfo info(pano.getImage(imgNr).getFilename().c_str());
456  HuginBase::CPVector foundLines;
457  if(info.isGrayscale())
458  {
459  foundLines=LoadGrayImageAndFindLines(info, pano, imgNr, nrLines);
460  }
461  else
462  {
463  if(info.isColor())
464  {
465  //colour images
466  foundLines=LoadImageAndFindLines(info, pano, imgNr, nrLines);
467  }
468  else
469  {
470  std::cerr << "Image " << pano.getImage(imgNr).getFilename().c_str() << " has "
471  << info.numBands() << " channels." << std::endl
472  << "Linefind works only with grayscale or color images." << std::endl
473  << "Skipping image." << std::endl;
474  };
475  };
476  if(!foundLines.empty())
477  {
478  for (HuginBase::CPVector::const_iterator cpIt = foundLines.begin(); cpIt != foundLines.end(); ++cpIt)
479  {
480  hugin_omp::ScopedLock sl(lock);
481  pano.addCtrlPoint(*cpIt);
482  };
483  };
484  }
485  std::cout << std::endl << "Found " << pano.getNrOfCtrlPoints() - nrCPS << " vertical lines" << std::endl << std::endl;
486 
487  //write output
488  // Set output .pto filename if not given
489  output = hugin_utils::GetOutputFilename(output, input, "line");
490  if (pano.WritePTOFile(output, hugin_utils::getPathPrefix(output)))
491  {
492  std::cout << std::endl << "Written output to " << output << std::endl;
493  };
494  return 0;
495 }
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:270
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:269
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:278
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:126
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:216
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:164
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:145
static T max(T x, T y)
Definition: svm.cpp:65
static void usage()
Definition: Main.cpp:32
std::string GetHuginVersion()
return a string with version numbers
Definition: utils.cpp:907
std::vector< ControlPoint > CPVector
Definition: ControlPoint.h:99
const HuginBase::Panorama & m_pano
Definition: linefind.cpp:275
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:102
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