Hugintrunk  0.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
geocpset.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>
33 
34 // check if given point x,y is inside both images img1 and img2,
35 // using PTools::Tranform object to prevent creating of this transform for each point
36 bool CheckAndAddPoint(HuginBase::Panorama& pano, size_t img1, size_t img2, HuginBase::PTools::Transform& transform1, HuginBase::PTools::Transform& transform2, double x, double y)
37 {
38  double x1, y1, x2, y2;
39  if(!transform1.transformImgCoord(x1, y1, x, y))
40  {
41  return false;
42  };
43  if(!pano.getImage(img1).isInside(vigra::Point2D(x1,y1), true))
44  {
45  return false;
46  };
47  if(!transform2.transformImgCoord(x2, y2, x, y))
48  {
49  return false;
50  };
51  if(!pano.getImage(img2).isInside(vigra::Point2D(x2,y2), true))
52  {
53  return false;
54  };
55  pano.addCtrlPoint(HuginBase::ControlPoint(img1, x1, y1, img2, x2, y2));
56  return true;
57 };
58 
59 //helper class for sort
61 {
62  return p1.squareLength()<p2.squareLength();
63 };
64 
65 // add geometric control point to overlap of images img1 and img2
66 void AddGeometricControlPoint(HuginBase::Panorama& pano, size_t img1, size_t img2)
67 {
69  //reset ROI to prevent unwanted clipping in this algorithm
70  opts.setROI(vigra::Rect2D(0, 0, opts.getWidth(), opts.getHeight()));
71  vigra::Rect2D rect1=estimateOutputROI(pano, opts, img1);
72  vigra::Rect2D rect2=estimateOutputROI(pano, opts, img2);
73  //get union of both outputs
74  rect1=rect1 & rect2;
75  if(rect1.area()>0)
76  {
77  HuginBase::PTools::Transform transform1, transform2;
78  transform1.createTransform(pano.getImage(img1), opts);
79  transform2.createTransform(pano.getImage(img2), opts);
80 
81  hugin_utils::FDiff2D mid=(rect1.upperLeft()+rect1.lowerRight())/2.0;
82  //create grid of points to check
83  std::vector<hugin_utils::FDiff2D> points;
84  for(int dx=-5; dx<=5; dx++)
85  {
86  for(int dy=-5; dy<=5; dy++)
87  {
88  points.push_back(hugin_utils::FDiff2D(dx*rect1.width()/10.0, dy*rect1.height()/10.0));
89  };
90  };
91  //sort by distance
92  sort(points.begin(), points.end(),sortByDistance);
93  //now check all points in the grid
94  for(size_t i=0; i<points.size(); i++)
95  {
96  if(CheckAndAddPoint(pano, img1, img2, transform1, transform2, mid.x+points[i].x, mid.y+points[i].y))
97  {
98  return;
99  };
100  };
101  };
102 };
103 
104 // only add control points for images without control points
106 {
107  //first test: have image control points?
109  HuginBase::UIntSet imgsWithCp;
110  HuginBase::UIntSet imgsWithoutCp;
111  for(size_t i=0; i<pano.getNrOfImages(); i++)
112  {
113  bool hasControlPoints=false;
114  for (HuginBase::CPVector::const_iterator it = cp.begin(); it != cp.end(); ++it)
115  {
116  if ((it->image1Nr == i || it->image2Nr == i) && (it->mode == HuginBase::ControlPoint::X_Y))
117  {
118  hasControlPoints=true;
119  break;
120  };
121  };
122  if(hasControlPoints)
123  {
124  imgsWithCp.insert(i);
125  }
126  else
127  {
128  imgsWithoutCp.insert(i);
129  };
130  };
131  // now test if images without control points have a linked position with an image with control point
132  HuginBase::UIntSet imgsToTest;
133  for (HuginBase::UIntSet::const_iterator img = imgsWithoutCp.begin(); img != imgsWithoutCp.end(); ++img)
134  {
135  const HuginBase::SrcPanoImage& img1 = pano.getImage(*img);
136  bool connected=false;
137  if(img1.YawisLinked())
138  {
139  for (HuginBase::UIntSet::const_iterator img2 = imgsWithCp.begin(); img2 != imgsWithCp.end(); ++img2)
140  {
141  if(img1.YawisLinkedWith(pano.getImage(*img2)))
142  {
143  imgsWithCp.insert(*img);
144  connected=true;
145  break;
146  };
147  };
148  };
149  if(!connected)
150  {
151  imgsToTest.insert(*img);
152  };
153  };
154 
155  // have we found unconnected images?
156  if(imgsToTest.empty())
157  {
158  std::cout << "No unconnected images found." << std::endl
159  << "No control point added." << std::endl;
160  return;
161  };
162  std::cout << std::endl << "Found " << imgsToTest.size() << " unconnected images." << std::endl;
163 
164  // now find overlapping images
165  HuginBase::CalculateImageOverlap overlap(&pano);
166  overlap.limitToImages(imgsToTest);
167  overlap.calculate(10);
168  std::vector<HuginBase::UIntSet> checkedImgPairs(pano.getNrOfImages());
169  for (HuginBase::UIntSet::const_iterator img = imgsToTest.begin(); img != imgsToTest.end(); ++img)
170  {
171  HuginBase::UIntSet overlappingImgs;
172  // search overlapping images, take linked positions into account
173  for(size_t i=0; i<pano.getNrOfImages(); i++)
174  {
175  if(i==*img)
176  {
177  continue;
178  };
179  if(overlap.getOverlap(*img, i)>minOverlap/100.0f)
180  {
181  //ignore overlap for linked images
182  bool ignoreImage=false;
183  const HuginBase::SrcPanoImage& img2 = pano.getImage(i);
184  if(img2.YawisLinked())
185  {
186  for (HuginBase::UIntSet::const_iterator it = overlappingImgs.begin(); it != overlappingImgs.end(); ++it)
187  {
188  if(img2.YawisLinkedWith(pano.getImage(*it)))
189  {
190  ignoreImage=true;
191  break;
192  }
193  };
194  };
195  if(set_contains(checkedImgPairs[*img], i) || set_contains(checkedImgPairs[i], *img))
196  {
197  ignoreImage=true;
198  };
199  if(!ignoreImage)
200  {
201  overlappingImgs.insert(i);
202  checkedImgPairs[*img].insert(i);
203  checkedImgPairs[i].insert(*img);
204  };
205  };
206  };
207  // now add control points
208  for (HuginBase::UIntSet::const_iterator overlapImg = overlappingImgs.begin(); overlapImg != overlappingImgs.end(); ++overlapImg)
209  {
210  AddGeometricControlPoint(pano, *img, *overlapImg);
211  };
212  };
213  std::cout << "Added " << pano.getCtrlPoints().size() - cp.size() << " control points." << std::endl;
214 };
215 
216 // only add control points for images without control points
217 void SetGeometricControlPointsOverlap(HuginBase::Panorama& pano, const int minOverlap)
218 {
220  // find overlapping images
221  HuginBase::CalculateImageOverlap overlap(&pano);
222  overlap.calculate(10);
223  for(size_t i=0; i<pano.getNrOfImages()-1; i++)
224  {
225  HuginBase::UIntSet overlappingImgs;
226  const HuginBase::SrcPanoImage& img1 = pano.getImage(i);
227  // search overlapping images, take linked positions into account
228  for(size_t j=i+1; j<pano.getNrOfImages(); j++)
229  {
230  //skip linked images
231  if(img1.YawisLinked())
232  {
233  if(img1.YawisLinkedWith(pano.getImage(j)))
234  {
235  continue;
236  };
237  };
238  if(overlap.getOverlap(i, j)>=minOverlap/100.0f)
239  {
240  // we have an overlap, now check if there are control points
241  bool hasControlPoints=false;
242  for (HuginBase::CPVector::const_iterator it = cp.begin(); it != cp.end(); ++it)
243  {
244  if(((it->image1Nr==i && it->image2Nr==j) ||
245  (it->image1Nr==j && it->image2Nr==i) ) &&
246  (it->mode == HuginBase::ControlPoint::X_Y))
247  {
248  hasControlPoints=true;
249  break;
250  };
251  };
252  if(!hasControlPoints)
253  {
254  //ignore overlap for linked images
255  bool ignoreImage=false;
256  const HuginBase::SrcPanoImage& img2 = pano.getImage(j);
257  if(img2.YawisLinked())
258  {
259  for (HuginBase::UIntSet::const_iterator it = overlappingImgs.begin(); it != overlappingImgs.end(); ++it)
260  {
261  if(img2.YawisLinkedWith(pano.getImage(*it)))
262  {
263  ignoreImage=true;
264  break;
265  };
266  };
267  };
268  if(!ignoreImage)
269  {
270  overlappingImgs.insert(j);
271  };
272  };
273  };
274  };
275  // now add control points
276  for (HuginBase::UIntSet::const_iterator overlapImg = overlappingImgs.begin(); overlapImg != overlappingImgs.end(); ++overlapImg)
277  {
278  AddGeometricControlPoint(pano, i, *overlapImg);
279  };
280  };
281  std::cout << std::endl << "Added " << pano.getCtrlPoints().size() - cp.size() << " control points." << std::endl;
282 };
283 
284 static void usage(const char* name)
285 {
286  std::cout << name << ": set geometric control points" << std::endl
287  << name << " version " << hugin_utils::GetHuginVersion() << std::endl
288  << std::endl
289  << "Usage: " << name << " [options] input.pto" << std::endl
290  << std::endl
291  << " Options:" << std::endl
292  << " -o, --output=file.pto Output Hugin PTO file. Default: <filename>_geo.pto" << std::endl
293  << " -e, --each-overlap By default, geocpset will only work on the overlap" << std::endl
294  << " of unconnected images. With this switch it will" << std::endl
295  << " work on all overlaps without control points." << std::endl
296  << " --minimum-overlap=NUM Take only these images into account where the" << std::endl
297  << " overlap is bigger than NUM percent (default 10)." << std::endl
298  << " -h, --help Shows this help" << std::endl
299  << std::endl;
300 }
301 
302 int main(int argc, char* argv[])
303 {
304  // parse arguments
305  const char* optstring = "o:eh";
306  enum
307  {
308  MINOVERLAP=1000
309  };
310 
311  static struct option longOptions[] =
312  {
313  {"output", required_argument, NULL, 'o' },
314  {"each-overlap", no_argument, NULL, 'e' },
315  {"min-overlap", required_argument, NULL, MINOVERLAP},
316  {"help", no_argument, NULL, 'h' },
317  0
318  };
319 
320  int c;
321  bool eachOverlap=false;
322  int minOverlap=10;
323  std::string output;
324  while ((c = getopt_long (argc, argv, optstring, longOptions,nullptr)) != -1)
325  {
326  switch (c)
327  {
328  case 'o':
329  output = optarg;
330  break;
331  case 'e':
332  eachOverlap = true;
333  break;
334  case MINOVERLAP:
335  minOverlap=atoi(optarg);
336  if(minOverlap<1 || minOverlap>99)
337  {
338  std::cerr << hugin_utils::stripPath(argv[0]) << ": Invalid minimum overlap: " << optarg << std::endl
339  << "Minimum overlap have to be between 1 and 99." << std::endl;
340  return 1;
341  };
342  break;
343  case 'h':
344  usage(hugin_utils::stripPath(argv[0]).c_str());
345  return 0;
346  case ':':
347  case '?':
348  // missing argument or invalid switch
349  return 1;
350  break;
351  default:
352  // this should not happen
353  abort ();
354  }
355  }
356 
357  if (argc - optind != 1)
358  {
359  if (argc - optind < 1)
360  {
361  std::cerr << hugin_utils::stripPath(argv[0]) << ": No project file given." << std::endl;
362  }
363  else
364  {
365  std::cerr << hugin_utils::stripPath(argv[0]) << ": Only one project file expected." << std::endl;
366  };
367  return 1;
368  };
369 
370  std::string input=argv[optind];
371  // read panorama
372  HuginBase::Panorama pano;
373  if (!pano.ReadPTOFile(input, hugin_utils::getPathPrefix(input)))
374  {
375  return 1;
376  };
377 
378  if(pano.getNrOfImages()==1)
379  {
380  std::cerr << "error: project file contains only one image" << std::endl
381  << "aborting processing" << std::endl;
382  return 1;
383  };
384 
385  std::cout << "Adding geometric control points..." << std::endl;
386  if(eachOverlap)
387  {
388  SetGeometricControlPointsOverlap(pano, minOverlap);
389  }
390  else
391  {
392  SetGeometricControlPointsUnconnected(pano, minOverlap);
393  };
394 
395  //write output
396  // Set output .pto filename if not given
397  output = hugin_utils::GetOutputFilename(output, input, "geo");
398  if (pano.WritePTOFile(output, hugin_utils::getPathPrefix(output)))
399  {
400  std::cout << std::endl << "Written output to " << output << std::endl;
401  };
402  return 0;
403 }
void SetGeometricControlPointsUnconnected(HuginBase::Panorama &pano, const int minOverlap)
Definition: geocpset.cpp:105
double getOverlap(unsigned int i, unsigned int j) const
returns the overlap for 2 images with number i and j
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
void SetGeometricControlPointsOverlap(HuginBase::Panorama &pano, const int minOverlap)
Definition: geocpset.cpp:217
vigra::Rect2D estimateOutputROI(const PanoramaData &pano, const PanoramaOptions &opts, unsigned i, const double maxLength)
unsigned int getHeight() const
get panorama height
bool CheckAndAddPoint(HuginBase::Panorama &pano, size_t img1, size_t img2, HuginBase::PTools::Transform &transform1, HuginBase::PTools::Transform &transform2, double x, double y)
Definition: geocpset.cpp:36
bool set_contains(const _Container &c, const typename _Container::key_type &key)
Definition: stl_utils.h:74
void calculate(unsigned int steps)
does the calculation, for each image steps*steps points are extracted and tested with all other image...
const CPVector & getCtrlPoints() const
get all control point of this Panorama
Definition: Panorama.h:319
bool isInside(vigra::Point2D p, bool ignoreMasks=false) const
check if a coordinate is inside the source image
represents a control point
Definition: ControlPoint.h:38
std::set< unsigned int > UIntSet
Definition: PanoramaData.h:51
Model for a panorama.
Definition: Panorama.h:152
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
std::size_t getNrOfImages() const
number of images.
Definition: Panorama.h:205
bool sortByDistance(hugin_utils::FDiff2D p1, hugin_utils::FDiff2D p2)
Definition: geocpset.cpp:60
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 setROI(const vigra::Rect2D &val)
T squareLength() const
Return the square of the length of the vector.
Definition: hugin_math.h:141
unsigned int getWidth() const
bool transformImgCoord(double &x_dest, double &y_dest, double x_src, double y_src) const
like transform, but return image coordinates, not cartesian coordinates
const PanoramaOptions & getOptions() const
returns the options for this panorama
Definition: Panorama.h:481
void AddGeometricControlPoint(HuginBase::Panorama &pano, size_t img1, size_t img2)
Definition: geocpset.cpp:66
definitions of classes to calculate overlap between different images
static void usage()
Definition: Main.cpp:32
Holds transformations for Image -&gt; Pano and the other way.
std::string GetHuginVersion()
return a string with version numbers
Definition: utils.cpp:907
std::vector< ControlPoint > CPVector
Definition: ControlPoint.h:99
bool WritePTOFile(const std::string &filename, const std::string &prefix="")
write data to given pto file
Definition: Panorama.cpp:2059
class for calculating overlap of images
const SrcPanoImage & getImage(std::size_t nr) const
get a panorama image, counting starts with 0
Definition: Panorama.h:211
All variables of a source image.
Definition: SrcPanoImage.h:194
Panorama image options.
void createTransform(const vigra::Diff2D &srcSize, VariableMap srcVars, Lens::LensProjectionFormat srcProj, const vigra::Diff2D &destSize, PanoramaOptions::ProjectionFormat destProj, const std::vector< double > &destProjParam, double destHFOV, const vigra::Diff2D &origSrcSize)
initialize pano-&gt;image transformation
std::string stripPath(const std::string &filename)
remove the path of a filename (mainly useful for gui display of filenames)
Definition: utils.cpp:160
void limitToImages(UIntSet img)
limits the calculation of the overlap to given image numbers
int main(int argc, char *argv[])
Definition: Main.cpp:167