Hugintrunk  0.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
CalculateOptimalROI.cpp
Go to the documentation of this file.
1 // -*- c-basic-offset: 4 -*-
25 #include "CalculateOptimalROI.h"
26 #include <algorithm>
27 #include "LayerStacks.h"
29 
30 namespace HuginBase {
31 // uncomment the follow line to print more progress
32 //#define PRINT_DEBUG_VALUES
33 
36 {
37  activeImages=panorama.getActiveImages();
38  if (activeImages.empty())
39  {
40  return false;
41  };
42  // check that all images are visible
43  const HuginBase::UIntSet visibleImages = HuginBase::getImagesinROI(panorama, activeImages, vigra::Rect2D(vigra::Point2D(0, 0), panorama.getOptions().getSize()));
44  if (visibleImages.size() != activeImages.size())
45  {
46  // ignore invisible images
47  HuginBase::UIntSet updatedImages;
48  std::set_intersection(activeImages.begin(), activeImages.end(), visibleImages.begin(), visibleImages.end(), std::inserter(updatedImages, updatedImages.begin()));
49  activeImages = updatedImages;
50  if (activeImages.empty())
51  {
52  return false;
53  };
54  };
55 
56  PanoramaOptions opt = panorama.getOptions();
57  o_optimalSize=opt.getSize();
58  if (o_optimalSize.x == 0 || o_optimalSize.y == 0)
59  {
60  return false;
61  };
62  m_bestRect = vigra::Rect2D();
63  try
64  {
65  testedPixels.resize(static_cast<long long>(o_optimalSize.x) * o_optimalSize.y,false);
66  pixels.resize(static_cast<long long>(o_optimalSize.x) * o_optimalSize.y,false);
67  }
68  catch(std::bad_alloc&)
69  {
70  //could not allocate enough memory
71  return false;
72  };
73 
74  for (UIntSet::const_iterator it=activeImages.begin(); it!=activeImages.end(); ++it)
75  {
76  const SrcPanoImage &img=panorama.getImage(*it);
78  transf->createTransform(img,opt);
79  transfMap.insert(std::pair<unsigned int,PTools::Transform*>(*it,transf));
80  }
81 
82  if (!getProgressDisplay()->updateDisplay("Calculate the cropping region"))
83  {
84  CleanUp();
85  return false;
86  };
87  if (!autocrop())
88  {
89  CleanUp();
90  return false;
91  };
92 
93  //clean up on demand
94  CleanUp();
95  return true;
96 };
97 
99 {
100  for (std::map<unsigned int, PTools::Transform*>::iterator it = transfMap.begin(); it != transfMap.end(); ++it)
101  {
102  delete (*it).second;
103  };
104 };
105 
106 //now you can do dynamic programming, look thinks up on fly
107 bool CalculateOptimalROI::stackPixel(int i, int j, UIntSet &stack)
108 {
109  bool inside = intersection; // start with true for intersection mode and with false for union mode
110  //check that pixel at each place
111  for(UIntSet::const_iterator it=stack.begin();it!=stack.end();++it)
112  {
113  double xd,yd;
114  if(transfMap[*it]->transformImgCoord(xd,yd,(double)i,(double)j))
115  {
116  if(o_panorama.getImage(*it).isInside(vigra::Point2D(xd,yd)))
117  {
118  if (!intersection) {
119  //if found in a single image, short cut out
120  inside=true;
121  break;
122  }
123  }
124  else {
125  if (intersection) {
126  //outside of at least one image - return false
127  inside=false;
128  break;
129  }
130  }
131  }
132  }
133 
134  return inside;
135 }
136 
138 {
139  if(!testedPixels[static_cast<long long>(j) * o_optimalSize.x + i])
140  {
141  bool inside;
142 
143  if (stacks.empty())
144  {
145  // no stacks - test all images on union or intersection
146  inside = stackPixel(i, j, activeImages);
147  }
148  else
149  {
150  inside = false;
151  // pixel must be inside of at least one stack
152  for (unsigned s=0; s < stacks.size(); s++)
153  {
154  // images in each stack are tested on intersection
155  if (stackPixel(i, j, stacks[s]))
156  {
157  inside = true;
158  break;
159  }
160  }
161  }
162 
163  testedPixels[static_cast<long long>(j) * o_optimalSize.x + i] = true;
164  pixels[static_cast<long long>(j) * o_optimalSize.x + i] = inside;
165 
166  return inside;
167  }
168  //else it is know if this pixel is covered by at least one image
169  else
170  {
171  return pixels[static_cast<long long>(j) * o_optimalSize.x + i];
172  }
173 }
174 
176 void CalculateOptimalROI::AddCheckingRects(std::list<vigra::Rect2D>& testingRects, const vigra::Rect2D& rect, const long maxvalue)
177 {
178  if(rect.left()<0 || rect.top()<0 || rect.right()>o_optimalSize.x || rect.bottom()>o_optimalSize.y)
179  {
180  return;
181  }
182 
183  if (rect.left() < rect.right() && rect.top() < rect.bottom())
184  {
185  //not big enough
186  if(maxvalue>0 && rect.area()<maxvalue)
187  {
188  return;
189  }
190  // check if rect is already in list
191  std::list<vigra::Rect2D>::iterator it=std::find(testingRects.begin(), testingRects.end(), rect);
192  if (it == testingRects.end())
193  {
194  testingRects.push_back(rect);
195  };
196  }
197 }
198 
200 bool CalculateOptimalROI::CheckRectCoversPano(const vigra::Rect2D& rect)
201 {
202  for (int i = rect.left(); i<rect.right(); i++)
203  {
204  if (imgPixel(i, rect.top()) == 0 || imgPixel(i, rect.bottom() - 1) == 0)
205  {
206  return false;
207  }
208  }
209 
210  for (int j = rect.top(); j<rect.bottom(); j++)
211  {
212  if (imgPixel(rect.left(), j) == 0 || imgPixel(rect.right() - 1, j) == 0)
213  {
214  return false;
215  }
216  }
217  return true;
218 };
219 
220 vigra::Rect2D ModifyRect(const vigra::Rect2D& rect, long deltaLeft, long deltaTop, long deltaRight, long deltaBottom)
221 {
222  vigra::Rect2D newRect(rect);
223  newRect.moveBy(deltaLeft, deltaTop);
224  newRect.addSize(vigra::Size2D(deltaRight - deltaLeft, deltaBottom - deltaTop));
225  return newRect;
226 };
227 
228 void CalculateOptimalROI::nonreccheck(const vigra::Rect2D& rect, int acc, int searchStrategy, long& maxvalue)
229 {
230  std::list<vigra::Rect2D> testRects;
231  testRects.push_back(rect);
232 
233  while(!testRects.empty())
234  {
235  vigra::Rect2D testingRect = *testRects.begin();
236  const bool rectCovers = CheckRectCoversPano(testingRect);
237  switch(searchStrategy)
238  {
239  case 1:
240  if(!rectCovers)
241  {
242  //all directions (shrink only)
243  AddCheckingRects(testRects, ModifyRect(testingRect, 0, acc, 0, 0), maxvalue);
244  AddCheckingRects(testRects, ModifyRect(testingRect, 0, 0, 0, -acc), maxvalue);
245  AddCheckingRects(testRects, ModifyRect(testingRect, acc, 0, 0, 0), maxvalue);
246  AddCheckingRects(testRects, ModifyRect(testingRect, 0, 0, -acc, 0), maxvalue);
247  }
248  //it was good, stop recursing
249  else
250  {
251  if(maxvalue<testingRect.area())
252  {
253  maxvalue=testingRect.area();
254  m_bestRect = testingRect;
255  }
256  }
257  break;
258  case 2:
259  if(!rectCovers)
260  {
261  //all directions (shrink only)
262  AddCheckingRects(testRects, ModifyRect(testingRect, acc >> 1, 0, -(acc >> 1), 0), maxvalue);
263  AddCheckingRects(testRects, ModifyRect(testingRect, 0, acc >> 1, 0, -(acc >> 1)), maxvalue);
264  }
265  //it was good, stop recursing
266  else
267  {
268  if(maxvalue<testingRect.area())
269  {
270  maxvalue = testingRect.area();
271  m_bestRect = testingRect;
272  }
273  }
274  break;
275  case 0:
276  default:
277  if(rectCovers)
278  {
279  //check growth in all 4 directions
280  AddCheckingRects(testRects, ModifyRect(testingRect, -acc, 0, 0, 0), maxvalue);
281  AddCheckingRects(testRects, ModifyRect(testingRect, 0, 0, acc, 0), maxvalue);
282  AddCheckingRects(testRects, ModifyRect(testingRect, 0, -acc, 0, 0), maxvalue);
283  AddCheckingRects(testRects, ModifyRect(testingRect, 0, 0, 0, acc), maxvalue);
284  //check if shrinking in one direction will allow more growth in other direction
285  AddCheckingRects(testRects, ModifyRect(testingRect, -2*acc, acc, 0, 0), maxvalue);
286  AddCheckingRects(testRects, ModifyRect(testingRect, -2*acc, 0, 0, -acc), maxvalue);
287  AddCheckingRects(testRects, ModifyRect(testingRect, 0, acc, 2*acc, 0), maxvalue);
288  AddCheckingRects(testRects, ModifyRect(testingRect, 0, 0, 2*acc, -acc), maxvalue);
289  AddCheckingRects(testRects, ModifyRect(testingRect, acc, -2 * acc, 0, 0), maxvalue);
290  AddCheckingRects(testRects, ModifyRect(testingRect, 0, -2*acc, -acc, 0), maxvalue);
291  AddCheckingRects(testRects, ModifyRect(testingRect, acc, 0, 0, 2*acc), maxvalue);
292  AddCheckingRects(testRects, ModifyRect(testingRect, 0, 0, -acc, 2*acc), maxvalue);
293  AddCheckingRects(testRects, ModifyRect(testingRect, -acc, acc, acc, 0), maxvalue);
294  AddCheckingRects(testRects, ModifyRect(testingRect, -acc, 0, acc, -acc), maxvalue);
295  AddCheckingRects(testRects, ModifyRect(testingRect, acc, -acc, 0, acc), maxvalue);
296  AddCheckingRects(testRects, ModifyRect(testingRect, 0, -acc, -acc, acc), maxvalue);
297  if(maxvalue<testingRect.area())
298  {
299  maxvalue = testingRect.area();
300  m_bestRect = testingRect;
301  }
302  }
303  };
304  testRects.pop_front();
305  }
306 }
307 
309 {
310  long maxvalue=0;
311 
312  //put backwards at the start
313  int startacc = pow(2.0, std::min((int)log2(o_optimalSize.x / 2 - 1), (int)log2(o_optimalSize.y / 2 - 1)) - 1);
314  if (startacc < 1)
315  {
316  startacc = 1;
317  };
318 
319  //start smaller to get biggest initial position
320  if(startacc>64)
321  {
322  //we start searching with a symmetric decrease
323  for(int acc=startacc;acc>=64;acc/=2)
324  {
325  nonreccheck(vigra::Rect2D(vigra::Point2D(), o_optimalSize), acc, 2, maxvalue);
326  if (!getProgressDisplay()->updateDisplayValue())
327  {
328  return false;
329  };
330  if(maxvalue>0)
331  {
332 #ifdef PRINT_DEBUG_VALUES
333  printf("Inner %d %ld: %d %d - %d %d\n", acc, maxvalue, m_bestRect.left(), m_bestRect.right(), m_bestRect.top(), m_bestRect.bottom());
334 #endif
335  break;
336  }
337  }
338  };
339 
340  if(maxvalue==0)
341  {
342  // if the rough symmetric search failed we are using also an asymmetric strategy
343  for(int acc=startacc;acc>=1;acc/=2)
344  {
345  nonreccheck(vigra::Rect2D(vigra::Point2D(), o_optimalSize), acc, 1, maxvalue);
346  if (!getProgressDisplay()->updateDisplayValue())
347  {
348  return false;
349  };
350  if (maxvalue>0)
351  {
352 #ifdef PRINT_DEBUG_VALUES
353  printf("Inner %d %ld: %d %d - %d %d\n", acc, maxvalue, m_bestRect.left(), m_bestRect.right(), m_bestRect.top(), m_bestRect.bottom());
354 #endif
355  break;
356  }
357  }
358  };
359 
360  for(int acc=startacc;acc>=1;acc/=2)
361  {
362 #ifdef PRINT_DEBUG_VALUES
363  printf("Starting %d: %d %d - %d %d\n", acc, m_bestRect.left(), m_bestRect.right(), m_bestRect.top(), m_bestRect.bottom());
364 #endif
365  nonreccheck(m_bestRect, acc, 0, maxvalue);
366  if (!getProgressDisplay()->updateDisplayValue())
367  {
368  return false;
369  };
370  }
371 
372  return true;
373 }
374 
375 void CalculateOptimalROI::setStacks(std::vector<UIntSet> hdr_stacks)
376 {
377  stacks=hdr_stacks;
378  intersection=true;
379 };
380 
381 // implementation of outside crop finding
383 {
384  if (hasRunSuccessfully())
385  {
386  return m_bestRect;
387  }
388  else
389  {
390  return vigra::Rect2D();
391  }
392 }
393 
395 {
396  HuginBase::UIntSet activeImgs = pano.getActiveImages();
397  if (activeImgs.empty())
398  {
399  // no image, return false
400  return false;
401  };
403  // reset roi for calculation
404  opts.setROI(vigra::Rect2D(vigra::Point2D(0, 0), opts.getSize()));
405  m_bestRect = vigra::Rect2D();
406  progress->setMaximum(activeImgs.size());
407  for (auto& img : activeImgs)
408  {
409  // calculate boundary of all images, use a miniatur scale of 800 px for speed reasons
410  m_bestRect |= HuginBase::estimateOutputROI(pano, opts, img, 800);
411  progress->updateDisplayValue();
412  if (progress->wasCancelled())
413  {
414  m_bestRect = vigra::Rect2D();
415  return false;
416  };
417  };
418  return !m_bestRect.isEmpty();
419 }
420 
421 } //namespace
declaration of functions to handle stacks and layers
void AddCheckingRects(std::list< vigra::Rect2D > &testingRects, const vigra::Rect2D &rect, const long maxvalue)
add new rect to list of rects to be check, do some checks before
bool CheckRectCoversPano(const vigra::Rect2D &rect)
check if given rect covers the whole pano
bool CalcOutsideCrop(PanoramaData &pano, AppBase::ProgressDisplay *progress)
the main crop finding algorithm
vigra::Rect2D estimateOutputROI(const PanoramaData &pano, const PanoramaOptions &opts, unsigned i, const double maxLength)
UIntSet getImagesinROI(const PanoramaData &pano, const UIntSet activeImages)
returns set of images which are visible in output ROI
void setMaximum(int newMaximum)
sets the new maximum value of the progress value
bool isInside(vigra::Point2D p, bool ignoreMasks=false) const
check if a coordinate is inside the source image
void nonreccheck(const vigra::Rect2D &rect, int acc, int searchStrategy, long &maxvalue)
void setStacks(std::vector< UIntSet > hdr_stacks)
sets the stack vector
std::set< unsigned int > UIntSet
Definition: PanoramaData.h:51
bool wasCancelled()
return true, if process should be canceled by user e.g.
virtual AppBase::ProgressDisplay * getProgressDisplay() const
virtual UIntSet getActiveImages() const =0
get active images
float pow(float a, double b)
Definition: utils.h:181
bool stackPixel(int i, int j, UIntSet &stack)
vigra::Rect2D ModifyRect(const vigra::Rect2D &rect, long deltaLeft, long deltaTop, long deltaRight, long deltaBottom)
virtual const PanoramaOptions & getOptions() const =0
returns the options for this panorama
Model for a panorama.
Definition: PanoramaData.h:81
virtual const SrcPanoImage & getImage(std::size_t nr) const =0
get a panorama image, counting starts with 0
void setROI(const vigra::Rect2D &val)
virtual vigra::Rect2D getResultOptimalROI()
returns the found crop rect
Holds transformations for Image -&gt; Pano and the other way.
bool calcOptimalROI(PanoramaData &panorama)
std::map< unsigned int, PTools::Transform * > transfMap
All variables of a source image.
Definition: SrcPanoImage.h:194
Panorama image options.
vigra::Size2D getSize() const
get size of output image
static T min(T x, T y)
Definition: svm.cpp:62
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