RansacShapeDetector.cpp
Go to the documentation of this file.
1#ifdef WIN32
2#include <windows.h>
3#endif
4#include <algorithm>
5#include <ctime>
6#include <deque>
7#include <functional>
8#include <iostream>
9
10#include "Candidate.h"
12#include "Octree.h"
13#include "RansacShapeDetector.h"
15#include <MiscLib/Performance.h>
16#include <MiscLib/Random.h>
17#ifdef DOPARALLEL
18#include <omp.h>
19#endif
20#undef max
21#undef min
22
23using namespace MiscLib;
24
26 m_maxCandTries(20), m_reqSamples(0), m_autoAcceptSize(0)
27{
28}
29
31 m_options(options), m_maxCandTries(20), m_reqSamples(0), m_autoAcceptSize(0)
32{
33}
34
36{
37 for (ConstructorsType::iterator i = m_constructors.begin(), iend = m_constructors.end();
38 i != iend;
39 ++i)
40 {
41 (*i)->Release();
42 }
43}
44
45void
47{
48 c->AddRef();
49 m_constructors.push_back(c);
50 if (c->RequiredSamples() > m_reqSamples)
51 {
52 m_reqSamples = c->RequiredSamples();
53 }
54}
55
56size_t
57RansacShapeDetector::StatBucket(float score) const
58{
59 return (size_t)std::max(
60 0.f,
61 std::floor((std::log(score) - std::log((float)m_options.m_minSupport)) / std::log(1.21f)) +
62 1);
63}
64
65/*
66 * Function Detect !!!!
67 */
68
69void
70RansacShapeDetector::UpdateLevelWeights(
71 float factor,
72 const MiscLib::Vector<std::pair<float, size_t>>& levelScores,
73 MiscLib::Vector<double>* sampleLevelProbability) const
74{
75 MiscLib::Vector<double> newSampleLevelProbability(sampleLevelProbability->size());
76 double newSampleLevelProbabilitySum = 0;
77 for (size_t i = 0; i < newSampleLevelProbability.size(); ++i)
78 {
79 if ((*sampleLevelProbability)[i] > 0)
80 {
81 newSampleLevelProbability[i] = (levelScores[i].first / (*sampleLevelProbability)[i]);
82 }
83 else
84 {
85 newSampleLevelProbability[i] = 0;
86 }
87 newSampleLevelProbabilitySum += newSampleLevelProbability[i];
88 }
89 double newSum = 0;
90 for (size_t i = 0; i < newSampleLevelProbability.size(); ++i)
91 {
92 newSampleLevelProbability[i] = .9f * newSampleLevelProbability[i] +
93 .1f * newSampleLevelProbabilitySum / levelScores.size();
94 newSum += newSampleLevelProbability[i];
95 }
96 for (size_t i = 0; i < sampleLevelProbability->size(); ++i)
97 {
98 (*sampleLevelProbability)[i] = (1.f - factor) * (*sampleLevelProbability)[i] +
99 factor * (newSampleLevelProbability[i] / newSum);
100 }
101}
102
103template <class ScoreVisitorT>
104void
105RansacShapeDetector::GenerateCandidates(
106 const IndexedOctreeType& globalOctree,
108 const PointCloud& pc,
109 ScoreVisitorT& scoreVisitor,
110 size_t currentSize,
111 size_t numInvalid,
112 const MiscLib::Vector<double>& sampleLevelProbSum,
113 size_t* drawnCandidates,
114 MiscLib::Vector<std::pair<float, size_t>>* sampleLevelScores,
115 float* bestExpectedValue,
116 CandidatesType* candidates) const
117{
118 size_t genCands = 0;
119
120#pragma omp parallel
121 {
122 ScoreVisitorT scoreVisitorCopy(scoreVisitor);
123#pragma omp for schedule(dynamic, 10) reduction(+ : genCands)
124 for (int candIter = 0; candIter < 200; ++candIter)
125 {
126 // pick a sample level
127 double s = ((double)rand()) / (double)RAND_MAX;
128 size_t sampleLevel = 0;
129 for (; sampleLevel < sampleLevelProbSum.size() - 1; ++sampleLevel)
130 if (sampleLevelProbSum[sampleLevel] >= s)
131 {
132 break;
133 }
134 // draw samples on current sample level in octree
135 MiscLib::Vector<size_t> samples;
136 const IndexedOctreeType::CellType* node;
137 if (!DrawSamplesStratified(globalOctree,
138 m_reqSamples,
139 sampleLevel,
140 scoreVisitorCopy.GetShapeIndex(),
141 &samples,
142 &node))
143 {
144 continue;
145 }
146 ++genCands;
147 // construct the candidates
148 size_t c = samples.size();
149 MiscLib::Vector<Vec3f> samplePoints(samples.size() << 1);
150 for (size_t i = 0; i < samples.size(); ++i)
151 {
152 samplePoints[i] = globalOctree.at(samples[i]).pos;
153 samplePoints[i + c] = globalOctree.at(samples[i]).normal;
154 }
155 // construct the different primitive shapes
156 PrimitiveShape* shape;
157 for (ConstructorsType::const_iterator i = m_constructors.begin(),
158 iend = m_constructors.end();
159 i != iend;
160 ++i)
161 {
162 if ((*i)->RequiredSamples() > samples.size() ||
163 !(shape = (*i)->Construct(samplePoints)))
164 {
165 continue;
166 }
167 // verify shape
168 std::pair<float, float> dn;
169 bool verified = true;
170 for (size_t i = 0; i < c; ++i)
171 {
172 shape->DistanceAndNormalDeviation(samplePoints[i], samplePoints[i + c], &dn);
173 if (!scoreVisitorCopy.PointCompFunc()(dn.first, dn.second))
174 {
175 verified = false;
176 break;
177 }
178 }
179 if (!verified)
180 {
181 shape->Release();
182 continue;
183 }
184 Candidate cand(shape, node->Level());
185 cand.Indices(new MiscLib::RefCounted<MiscLib::Vector<size_t>>);
186 cand.Indices()->Release();
187 shape->Release();
188 cand.ImproveBounds(
189 octrees, pc, scoreVisitorCopy, currentSize, m_options.m_bitmapEpsilon, 1);
190 if (cand.UpperBound() < m_options.m_minSupport)
191 {
192#pragma omp critical
193 {
194 (*sampleLevelScores)[node->Level()].first += cand.ExpectedValue();
195 ++(*sampleLevelScores)[node->Level()].second;
196 }
197 continue;
198 }
199
200#pragma omp critical
201 {
202 (*sampleLevelScores)[node->Level()].first += cand.ExpectedValue();
203 ++(*sampleLevelScores)[node->Level()].second;
204 candidates->push_back(cand);
205 if (cand.ExpectedValue() > *bestExpectedValue)
206 {
207 *bestExpectedValue = cand.ExpectedValue();
208 }
209 }
210 }
211 }
212 }
213 *drawnCandidates += genCands;
214}
215
217{
218 bool
219 operator()(const Candidate* a, const Candidate* b) const
220 {
221 return *a < *b;
222 }
223};
224
225template <class ScoreVisitorT>
226bool
227RansacShapeDetector::FindBestCandidate(CandidatesType& candidates,
229 const PointCloud& pc,
230 ScoreVisitorT& scoreVisitor,
231 size_t currentSize,
232 size_t drawnCandidates,
233 size_t numInvalid,
234 size_t minSize,
235 float numLevels,
236 float* maxForgottenCandidate,
237 float* candidateFailProb) const
238{
239 if (!candidates.size())
240 {
241 return false;
242 }
243 size_t maxImproveSubsetDuringMaxSearch = octrees.size();
244 // sort by expected value
245 std::sort(candidates.begin(), candidates.end());
246 // check if max is smaller than forgotten candidate
247 if (candidates.size() && candidates.back().ExpectedValue() < *maxForgottenCandidate)
248 {
249 // drawn candidates is wrong!
250 // need to correct the value
251 drawnCandidates = std::max(candidates.size(), (size_t)1);
252 *maxForgottenCandidate = 0;
253 }
254
255 MiscLib::Vector<Candidate*> candHeap;
256 for (size_t i = candidates.size() - 1; i != -1; --i)
257 {
258 if (CandidateFailureProbability(candidates[i].ExpectedValue(),
259 currentSize - numInvalid,
260 drawnCandidates,
261 numLevels) > m_options.m_probability)
262 {
263 break;
264 }
265 candHeap.push_back(&candidates[i]);
266 }
267
268 if (!candHeap.size())
269 {
270 return false;
271 }
272
273 std::make_heap(candHeap.begin(), candHeap.end(), CandidateHeapPred());
274
275 MiscLib::Vector<Candidate*> beatenCands;
276 Candidate* trial = candHeap.front();
277 std::pop_heap(candHeap.begin(), candHeap.end(), CandidateHeapPred());
278 candHeap.pop_back();
279 float bestCandidateFailureProbability;
280 while (candHeap.size())
281 {
282 if (trial->IsEquivalent(
283 *candHeap.front(), pc, m_options.m_epsilon, m_options.m_normalThresh))
284 {
285 std::pop_heap(candHeap.begin(), candHeap.end(), CandidateHeapPred());
286 candHeap.pop_back();
287 continue;
288 }
289 bool isEquivalent = false;
290 for (size_t j = 0; j < beatenCands.size(); ++j)
291 {
292 if (beatenCands[j]->IsEquivalent(
293 *candHeap.front(), pc, m_options.m_epsilon, m_options.m_normalThresh))
294 {
295 isEquivalent = true;
296 break;
297 }
298 }
299 if (isEquivalent)
300 {
301 std::pop_heap(candHeap.begin(), candHeap.end(), CandidateHeapPred());
302 candHeap.pop_back();
303 continue;
304 }
305 bestCandidateFailureProbability = CandidateFailureProbability(
306 trial->ExpectedValue(), currentSize - numInvalid, drawnCandidates, numLevels);
307 while (
308 (bestCandidateFailureProbability <= m_options.m_probability) &&
309 (*trial >= *candHeap.front()) && (trial->UpperBound() >= minSize) &&
310 trial->ImproveBounds(
311 octrees, pc, scoreVisitor, currentSize, m_options.m_bitmapEpsilon, octrees.size()))
312 {
313 bestCandidateFailureProbability = CandidateFailureProbability(
314 trial->ExpectedValue(), currentSize - numInvalid, drawnCandidates, numLevels);
315 }
316 if (bestCandidateFailureProbability <= m_options.m_probability &&
317 trial->UpperBound() >= minSize && trial->ComputedSubsets() >= octrees.size() &&
318 *trial >= *candHeap.front())
319 {
320 break;
321 }
322 if (bestCandidateFailureProbability <= m_options.m_probability &&
323 trial->UpperBound() >= minSize)
324 {
325 candHeap.push_back(trial);
326 std::push_heap(candHeap.begin(), candHeap.end(), CandidateHeapPred());
327 }
328 else if ((int)trial->ComputedSubsets() > std::max(2, ((int)octrees.size()) - 2))
329 {
330 beatenCands.push_back(trial);
331 }
332 trial = candHeap.front();
333 std::pop_heap(candHeap.begin(), candHeap.end(), CandidateHeapPred());
334 candHeap.pop_back();
335 }
336 bestCandidateFailureProbability = CandidateFailureProbability(
337 trial->ExpectedValue(), currentSize - numInvalid, drawnCandidates, numLevels);
338 while (bestCandidateFailureProbability <= m_options.m_probability &&
339 trial->UpperBound() >= minSize &&
340 trial->ImproveBounds(
341 octrees, pc, scoreVisitor, currentSize, m_options.m_bitmapEpsilon, octrees.size()))
342 {
343 bestCandidateFailureProbability = CandidateFailureProbability(
344 trial->ExpectedValue(), currentSize - numInvalid, drawnCandidates, numLevels);
345 }
346 if ((bestCandidateFailureProbability > m_options.m_probability ||
347 trial->UpperBound() < minSize) &&
348 (!m_autoAcceptSize || trial->UpperBound() < m_autoAcceptSize))
349 {
350 return false;
351 }
352 std::sort(candidates.begin(), candidates.end());
353
354 size_t bestCandidate = candidates.size() - 1;
355 bestCandidateFailureProbability = CandidateFailureProbability(
356 candidates.back().ExpectedValue(), currentSize - numInvalid, drawnCandidates, numLevels);
357
358 for (size_t i = bestCandidate - 1; i != -1; --i)
359 {
360 float iFailProb = CandidateFailureProbability(
361 candidates[i].ExpectedValue(), currentSize - numInvalid, drawnCandidates, numLevels);
362 if (iFailProb > m_options.m_probability || candidates[i].UpperBound() < minSize ||
363 candidates[i].UpperBound() < candidates[bestCandidate].LowerBound())
364 {
365 break;
366 }
367 // check if this is an identical candidate
368 if (candidates[bestCandidate].IsEquivalent(
369 candidates[i], pc, m_options.m_epsilon, m_options.m_normalThresh))
370 {
371 continue;
372 }
373 bool isEquivalent = false;
374 for (size_t j = 0; j < beatenCands.size(); ++j)
375 {
376 if (beatenCands[j]->IsEquivalent(
377 candidates[i], pc, m_options.m_epsilon, m_options.m_normalThresh))
378 {
379 isEquivalent = true;
380 break;
381 }
382 }
383 if (isEquivalent)
384 {
385 continue;
386 }
387 do
388 {
389 if (candidates[i].UpperBound() > candidates[bestCandidate].UpperBound() &&
390 candidates[i].LowerBound() < candidates[bestCandidate].LowerBound())
391 {
392 bool dontBreak = candidates[i].ImproveBounds(octrees,
393 pc,
394 scoreVisitor,
395 currentSize,
396 m_options.m_bitmapEpsilon,
397 maxImproveSubsetDuringMaxSearch);
398 iFailProb = CandidateFailureProbability(candidates[i].ExpectedValue(),
399 currentSize - numInvalid,
400 drawnCandidates,
401 numLevels);
402 if (!dontBreak)
403 {
404 break;
405 }
406 }
407 else if (candidates[bestCandidate].UpperBound() > candidates[i].UpperBound() &&
408 candidates[bestCandidate].LowerBound() < candidates[i].LowerBound())
409 {
410 bool dontBreak =
411 candidates[bestCandidate].ImproveBounds(octrees,
412 pc,
413 scoreVisitor,
414 currentSize,
415 m_options.m_bitmapEpsilon,
416 maxImproveSubsetDuringMaxSearch);
417 bestCandidateFailureProbability =
418 CandidateFailureProbability(candidates[bestCandidate].ExpectedValue(),
419 currentSize - numInvalid,
420 drawnCandidates,
421 numLevels);
422 if (!dontBreak)
423 {
424 break;
425 }
426 }
427 else
428 {
429 bool dontBreak =
430 candidates[bestCandidate].ImproveBounds(octrees,
431 pc,
432 scoreVisitor,
433 currentSize,
434 m_options.m_bitmapEpsilon,
435 maxImproveSubsetDuringMaxSearch);
436 dontBreak = candidates[i].ImproveBounds(octrees,
437 pc,
438 scoreVisitor,
439 currentSize,
440 m_options.m_bitmapEpsilon,
441 maxImproveSubsetDuringMaxSearch) ||
442 dontBreak;
443 iFailProb = CandidateFailureProbability(candidates[i].ExpectedValue(),
444 currentSize - numInvalid,
445 drawnCandidates,
446 numLevels);
447 bestCandidateFailureProbability =
448 CandidateFailureProbability(candidates[bestCandidate].ExpectedValue(),
449 currentSize - numInvalid,
450 drawnCandidates,
451 numLevels);
452 if (!dontBreak)
453 {
454 break;
455 }
456 }
457 } while (bestCandidateFailureProbability <= m_options.m_probability &&
458 iFailProb <= m_options.m_probability && candidates[i].UpperBound() >= minSize &&
459 candidates[bestCandidate].UpperBound() >= minSize &&
460 candidates[i].UpperBound() > candidates[bestCandidate].LowerBound() &&
461 candidates[i].LowerBound() < candidates[bestCandidate].UpperBound());
462 if ((candidates[i] > candidates[bestCandidate] ||
463 bestCandidateFailureProbability > m_options.m_probability ||
464 candidates[bestCandidate].UpperBound() < minSize) &&
465 (iFailProb <= m_options.m_probability && candidates[i].UpperBound() >= minSize))
466 {
467 while (iFailProb <= m_options.m_probability && candidates[i].UpperBound() >= minSize &&
468 candidates[i] > candidates[bestCandidate] &&
469 candidates[i].ImproveBounds(octrees,
470 pc,
471 scoreVisitor,
472 currentSize,
473 m_options.m_bitmapEpsilon,
474 octrees.size()))
475 {
476 iFailProb = CandidateFailureProbability(candidates[i].ExpectedValue(),
477 currentSize - numInvalid,
478 drawnCandidates,
479 numLevels);
480 }
481 if (candidates[i] > candidates[bestCandidate])
482 {
483 beatenCands.push_back(&candidates[bestCandidate]);
484 bestCandidate = i;
485 bestCandidateFailureProbability = iFailProb;
486 }
487 else
488 {
489 beatenCands.push_back(&candidates[i]);
490 }
491 }
492 else
493 {
494 beatenCands.push_back(&candidates[i]);
495 }
496 if (bestCandidateFailureProbability > m_options.m_probability ||
497 candidates[bestCandidate].UpperBound() < minSize)
498 {
499 break;
500 }
501 } // end for
502
503 while (candidates[bestCandidate].ImproveBounds(
504 octrees, pc, scoreVisitor, currentSize, m_options.m_bitmapEpsilon, octrees.size()))
505 ;
506
507 bestCandidateFailureProbability =
508 CandidateFailureProbability(candidates[bestCandidate].ExpectedValue(),
509 currentSize - numInvalid,
510 drawnCandidates,
511 numLevels);
512
513 if ((bestCandidateFailureProbability <= m_options.m_probability &&
514 candidates[bestCandidate].UpperBound() >= minSize) ||
515 (m_autoAcceptSize && candidates[bestCandidate].UpperBound() >= m_autoAcceptSize))
516 {
517 std::swap(candidates.back(), candidates[bestCandidate]);
518 *candidateFailProb = bestCandidateFailureProbability;
519 return true;
520 }
521 std::sort(candidates.begin(), candidates.end() /*, std::greater< Candidate >()*/);
522 return false;
523}
524
525size_t
527 size_t beginIdx,
528 size_t endIdx,
529 MiscLib::Vector<std::pair<RefCountPtr<PrimitiveShape>, size_t>>* shapes)
530{
531 const auto endtime = std::chrono::steady_clock::now() + m_options.m_maxruntime;
532 const auto timeout = [&]
533 {
534 return m_options.m_maxruntime.count() != Options::invalidRuntime &&
535 endtime <= std::chrono::steady_clock::now();
536 };
537 size_t pcSize = endIdx - beginIdx;
538 /*
539 * Initialization part
540 */
541 srand((unsigned int)time(NULL));
542 rn_setseed((size_t)time(NULL));
543
544 CandidatesType candidates;
545
547 subsetScoreVisitor(m_options.m_epsilon, m_options.m_normalThresh);
549 globalScoreVisitor(3 * m_options.m_epsilon, m_options.m_normalThresh);
550
551 // construct random subsets
552 size_t subsets = std::max(int(std::floor(std::log((float)pcSize) / std::log(2.f))) - 9, 2);
554 bcube.Bound(pc.begin() + beginIdx, pc.begin() + endIdx);
555
556 // construct stratified subsets
558 for (size_t i = octrees.size(); i;)
559 {
560 --i;
561 size_t subsetSize = pcSize;
562 if (i)
563 {
564 subsetSize = subsetSize >> 1;
565 MiscLib::Vector<size_t> subsetIndices(subsetSize);
566 size_t bucketSize = pcSize / subsetSize;
567 for (size_t j = 0; j < subsetSize; ++j)
568 {
569 size_t index = rn_rand() % bucketSize;
570 index += j * bucketSize;
571 if (index >= pcSize)
572 {
573 index = pcSize - 1;
574 }
575 subsetIndices[j] = index + beginIdx;
576 }
577 // move all the indices to the end
578 std::sort(subsetIndices.begin(), subsetIndices.end(), std::greater<size_t>());
579 for (size_t j = pcSize - 1, i = 0; i < subsetIndices.size(); --j, ++i)
580 {
581 std::swap(pc[j + beginIdx], pc[subsetIndices[i]]);
582 }
583 }
584 octrees[i] = new ImmediateOctreeType;
585 octrees[i]->ContainedData(&pc);
586 octrees[i]->DataRange(pcSize - subsetSize + beginIdx, pcSize + beginIdx);
587 octrees[i]->MaxBucketSize() = 20;
588 octrees[i]->MaxSubdivisionLevel() = 10;
589 octrees[i]->Build(bcube);
590 pcSize -= subsetSize;
591 }
592
593 pcSize = endIdx - beginIdx;
594
595 // construct one global octree
596 MiscLib::Vector<size_t> globalOctreeIndices(pcSize);
597 for (size_t i = 0; i < pcSize; ++i)
598 {
599 globalOctreeIndices[i] = i + beginIdx;
600 }
601 IndexedOctreeType globalOctree;
602 globalOctree.MaxBucketSize() = 20;
603 globalOctree.MaxSubdivisionLevel() = 10;
604 globalOctree.IndexedData(globalOctreeIndices.begin(), globalOctreeIndices.end(), pc.begin());
605 globalOctree.Build(bcube);
606 size_t globalOctTreeMaxNodeDepth = globalOctree.MaxDepth();
607
608 MiscLib::Vector<double> sampleLevelProbability(globalOctTreeMaxNodeDepth + 1);
609 for (size_t i = 0; i < sampleLevelProbability.size(); ++i)
610 {
611 sampleLevelProbability[i] = 1.0 / sampleLevelProbability.size();
612 }
613 size_t drawnCandidates = 0; // keep track of number of candidates
614 // that have been generated
615 float maxForgottenCandidate = 0; // the maximum size of a canidate
616 // that has been forgotten
617 size_t numTries = 0; // number of loops since last successful candidate
618 size_t numShapes = 0; // number of shapes found since the
619 // last housekeeping
620 size_t numInvalid = 0; // number of points that have been assigned
621 // to a shape since the last housekeeping
622 MiscLib::Vector<int> shapeIndex(pc.size(), -1); // maintains for every
623 // point the index of the shape it has been assigned to or
624 // -1 if the point is not assigned yet
625 subsetScoreVisitor.SetShapeIndex(shapeIndex);
626 globalScoreVisitor.SetShapeIndex(shapeIndex);
627 size_t currentSize = pcSize;
628 do
629 {
630 MiscLib::Vector<std::pair<float, size_t>> sampleLevelScores(sampleLevelProbability.size());
631 for (size_t i = 0; i < sampleLevelScores.size(); ++i)
632 {
633 sampleLevelScores[i] = std::make_pair(0.f, 0u);
634 }
635 MiscLib::Vector<double> sampleLevelProbSum(sampleLevelProbability.size());
636 sampleLevelProbSum[0] = sampleLevelProbability[0];
637 for (size_t i = 1; i < sampleLevelProbSum.size() - 1; ++i)
638 sampleLevelProbSum[i] = sampleLevelProbability[i] + sampleLevelProbSum[i - 1];
639 sampleLevelProbSum[sampleLevelProbSum.size() - 1] = 1;
640 // generate candidates
641 float bestExpectedValue = 0;
642 if (candidates.size())
643 {
644 bestExpectedValue = candidates.back().ExpectedValue();
645 }
646 bestExpectedValue = std::min((float)(currentSize - numInvalid), bestExpectedValue);
647 do
648 {
649 GenerateCandidates(globalOctree,
650 octrees,
651 pc,
652 subsetScoreVisitor,
653 currentSize,
654 numInvalid,
655 sampleLevelProbSum,
656 &drawnCandidates,
657 &sampleLevelScores,
658 &bestExpectedValue,
659 &candidates);
660 } while (CandidateFailureProbability(bestExpectedValue,
661 currentSize - numInvalid,
662 drawnCandidates,
663 globalOctTreeMaxNodeDepth) > m_options.m_probability &&
664 CandidateFailureProbability(m_options.m_minSupport,
665 currentSize - numInvalid,
666 drawnCandidates,
667 globalOctTreeMaxNodeDepth) > m_options.m_probability &&
668 !timeout());
669 // find the best candidate:
670 float bestCandidateFailureProbability;
671 float failureProbability = std::numeric_limits<float>::infinity();
672 bool foundCandidate = false;
673 size_t firstCandidateSize = 0;
674 while (FindBestCandidate(
675 candidates,
676 octrees,
677 pc,
678 subsetScoreVisitor,
679 currentSize,
680 drawnCandidates,
681 numInvalid,
682 std::max((size_t)m_options.m_minSupport, (size_t)(0.8f * firstCandidateSize)),
683 globalOctTreeMaxNodeDepth,
684 &maxForgottenCandidate,
685 &bestCandidateFailureProbability))
686 {
687 if (!foundCandidate)
688 {
689 // this is the first candidate
690 firstCandidateSize = (size_t)candidates.back().LowerBound();
691 // rescore levels
692 for (size_t i = 0; i < sampleLevelScores.size(); ++i)
693 {
694 sampleLevelScores[i] = std::make_pair(0.f, 0u);
695 }
696 for (size_t i = 0; i < candidates.size(); ++i)
697 {
698 if (candidates[i].ExpectedValue() * 1.4f > candidates.back().ExpectedValue())
699 {
700 size_t candLevel =
701 std::min(sampleLevelScores.size() - 1, candidates[i].Level());
702 ++sampleLevelScores[candLevel].first;
703 ++sampleLevelScores[candLevel].second;
704 }
705 }
706 UpdateLevelWeights(0.5f, sampleLevelScores, &sampleLevelProbability);
707 }
708 foundCandidate = true;
709 if (bestCandidateFailureProbability < failureProbability)
710 {
711 failureProbability = bestCandidateFailureProbability;
712 }
713 std::string candidateDescription;
714 candidates.back().Shape()->Description(&candidateDescription);
715 // do fitting
716 if (m_options.m_fitting != Options::NO_FITTING)
717 {
718 candidates.back().GlobalScore(globalScoreVisitor, globalOctree);
719 candidates.back().ConnectedComponent(pc, m_options.m_bitmapEpsilon);
720 Candidate clone;
721 candidates.back().Clone(&clone);
722 float oldScore, newScore;
723 size_t oldSize, newSize;
724 // get the weight once
725 newScore = clone.GlobalWeightedScore(globalScoreVisitor,
726 globalOctree,
727 pc,
728 3 * m_options.m_epsilon,
729 m_options.m_normalThresh,
730 m_options.m_bitmapEpsilon);
731 newSize = std::max(clone.Size(), candidates.back().Size());
732 bool allowDifferentShapes = false;
733 size_t fittingIter = 0;
734 do
735 {
736 ++fittingIter;
737 oldScore = newScore;
738 oldSize = newSize;
739 std::pair<size_t, float> score;
740 PrimitiveShape* shape = Fit(allowDifferentShapes,
741 *clone.Shape(),
742 pc,
743 clone.Indices()->begin(),
744 clone.Indices()->end(),
745 &score);
746 if (shape)
747 {
748 clone.Shape(shape);
749 newScore = clone.GlobalWeightedScore(globalScoreVisitor,
750 globalOctree,
751 pc,
752 3 * m_options.m_epsilon,
753 m_options.m_normalThresh,
754 m_options.m_bitmapEpsilon);
755 newSize = clone.Size();
756 shape->Release();
757 if (newScore > oldScore && newSize > m_options.m_minSupport)
758 {
759 clone.Clone(&candidates.back());
760 }
761 }
762 allowDifferentShapes = false;
763 } while (newScore > oldScore && fittingIter < 3);
764 }
765 else
766 {
767 candidates.back().GlobalScore(globalScoreVisitor, globalOctree);
768 candidates.back().ConnectedComponent(pc, m_options.m_bitmapEpsilon);
769 }
770 if (candidates.back().Size() == 0)
771 {
772 std::cout << "ERROR: candidate size == 0 after fitting" << std::endl;
773 }
774 // best candidate is ok!
775 // remove the points
776 shapes->push_back(std::make_pair(RefCountPtr<PrimitiveShape>(candidates.back().Shape()),
777 candidates.back().Indices()->size()));
778 for (size_t i = 0; i < candidates.back().Indices()->size(); ++i)
779 {
780 shapeIndex[(*(candidates.back().Indices()))[i]] = numShapes;
781 }
782 ++numShapes;
783 // update drawn candidates to reflect removal of points
784 // get the percentage of candidates that are invalid
785 drawnCandidates = std::pow(1.f - (candidates.back().Indices()->size() /
786 float(currentSize - numInvalid)),
787 3.f) *
788 drawnCandidates;
789 numInvalid += candidates.back().Indices()->size();
790 candidates.pop_back();
791 if (numInvalid > currentSize / 4) // more than half of the points assigned?
792 {
793 // do a housekeeping step
794 //this is a two stage procedure:
795 // 1) determine the new address of each Point and store it in the shapeIndex array
796 // 2) swap Points according to new addresses
797 size_t begin = beginIdx, end = beginIdx + currentSize;
798
799 // these hold the address ranges for the lately detected shapes
800 MiscLib::Vector<size_t> shapeIterators(numShapes);
801 int shapeIt = shapes->size() - numShapes;
802 for (size_t i = 0; i < numShapes; ++i, ++shapeIt)
803 {
804 shapeIterators[i] = end -= ((*shapes)[shapeIt]).second;
805 }
806
807 MiscLib::Vector<size_t> subsetSizes(octrees.size(), 0);
808 for (size_t i = beginIdx, j = 0; i < beginIdx + currentSize; ++i)
809 if (shapeIndex[i] < 0)
810 {
811 if (i >= octrees[j]->end() - pc.begin() + beginIdx &&
812 j < octrees.size() - 1)
813 {
814 ++j;
815 }
816 shapeIndex[i] = begin++;
817 ++subsetSizes[j];
818 }
819 else
820 {
821 shapeIndex[i] = shapeIterators[shapeIndex[i]]++;
822 }
823
824 // check if small subsets should be merged
825 size_t mergedSubsets = 0;
826 if (subsetSizes[0] < 500 && subsetSizes.size() > 1)
827 {
828 // should be merged
829 while (subsetSizes[0] < 500 && subsetSizes.size() > 1)
830 {
831 subsetSizes[1] += subsetSizes[0];
832 subsetSizes.erase(subsetSizes.begin());
833 delete octrees[0];
834 octrees.erase(octrees.begin());
835 ++mergedSubsets;
836 }
837 }
838
839 // reindex global octree
840 size_t minInvalidIndex = currentSize - numInvalid + beginIdx;
841#pragma parallel for schedule(static)
842 for (intptr_t i = 0, j = 0; i < globalOctreeIndices.size(); ++i)
843 if (shapeIndex[globalOctreeIndices[i]] < minInvalidIndex)
844 {
845 globalOctreeIndices[j++] = shapeIndex[globalOctreeIndices[i]];
846 }
847 globalOctreeIndices.resize(currentSize - numInvalid);
848
849 // reindex candidates (this also recomputes the bounds)
850#pragma parallel for schedule(static)
851 for (intptr_t i = 0; i < candidates.size(); ++i)
852 candidates[i].Reindex(shapeIndex,
853 minInvalidIndex,
854 mergedSubsets,
855 subsetSizes,
856 pc,
857 currentSize - numInvalid,
858 m_options.m_epsilon,
859 m_options.m_normalThresh,
860 m_options.m_bitmapEpsilon);
861
862 //regarding swapping it is best to swap both the addresses and the data
863 for (size_t i = beginIdx; i < beginIdx + currentSize; ++i)
864 while (i != shapeIndex[i])
865 {
866 pc.swapPoints(i, shapeIndex[i]);
867 std::swap(shapeIndex[i], shapeIndex[shapeIndex[i]]);
868 }
869
870 numInvalid = 0;
871
872 // rebuild subset octrees
873 if (mergedSubsets) // the octree for the first subset has to be constructed
874 {
875 //std::cout << "Attention: Merged " << mergedSubsets << " subsets!" << std::endl;
876 MiscLib::Vector<size_t> shuffleIndices(beginIdx + subsetSizes[0]),
877 reindex(beginIdx + subsetSizes[0]);
878 for (size_t i = 0; i < shuffleIndices.size(); ++i)
879 {
880 shuffleIndices[i] = i;
881 }
882 delete octrees[0];
883 octrees[0] = new ImmediateOctreeType();
884 octrees[0]->ContainedData(&pc);
885 octrees[0]->DataRange(beginIdx, beginIdx + subsetSizes[0]);
886 octrees[0]->MaxBucketSize() = 20;
887 octrees[0]->MaxSubdivisionLevel() = 10;
888 octrees[0]->ShuffleIndices(&shuffleIndices);
889 octrees[0]->Build(bcube);
890 octrees[0]->ShuffleIndices(NULL);
891 for (size_t i = 0; i < shuffleIndices.size(); ++i)
892 {
893 reindex[shuffleIndices[i]] = i;
894 }
895// reindex global octree
896#pragma omp parallel for schedule(static)
897 for (intptr_t i = 0; i < globalOctreeIndices.size(); ++i)
898 if (globalOctreeIndices[i] < reindex.size())
899 {
900 globalOctreeIndices[i] = reindex[globalOctreeIndices[i]];
901 }
902// reindex candidates
903#pragma omp parallel for schedule(static, 100)
904 for (intptr_t i = 0; i < candidates.size(); ++i)
905 {
906 candidates[i].Reindex(reindex);
907 }
908 for (size_t i = 1, begin = subsetSizes[0] + beginIdx; i < octrees.size();
909 begin += subsetSizes[i], ++i)
910 {
911 octrees[i]->DataRange(begin, begin + subsetSizes[i]);
912 octrees[i]->Rebuild();
913 if (octrees[i]->Root()->Size() != subsetSizes[i])
914 {
915 std::cout << "ERROR IN REBUILD!!!!" << std::endl;
916 }
917 }
918 }
919 else
920 for (size_t i = 0, begin = beginIdx; i < octrees.size();
921 begin += subsetSizes[i], ++i)
922 {
923 octrees[i]->DataRange(begin, begin + subsetSizes[i]);
924 octrees[i]->Rebuild();
925 }
926
927 //so everything is in its correct place, but we need to update the global octree ranges
928 currentSize = globalOctreeIndices.size();
929
930 globalOctree.IndexedRange(globalOctreeIndices.begin(), globalOctreeIndices.end());
931 globalOctTreeMaxNodeDepth = globalOctree.Rebuild();
932 if (globalOctree.Root()->Size() != globalOctreeIndices.size())
933 {
934 std::cout << "ERROR IN GLOBAL REBUILD!" << std::endl;
935 }
936 sampleLevelProbability.resize(globalOctTreeMaxNodeDepth + 1);
937
938 //shapeIndex.resize(globalOctreeIndices.size());
939 std::fill(
940 shapeIndex.begin() + beginIdx, shapeIndex.begin() + beginIdx + currentSize, -1);
941 numShapes = 0;
942 }
943 else
944 {
945 // the bounds of the candidates have become invalid and have to be
946 // recomputed
947#pragma parallel for schedule(static, 100)
948 for (size_t i = 0; i < candidates.size(); ++i)
949 candidates[i].RecomputeBounds(octrees,
950 pc,
951 subsetScoreVisitor,
952 currentSize - numInvalid,
953 m_options.m_epsilon,
954 m_options.m_normalThresh,
955 m_options.m_bitmapEpsilon);
956 }
957 // remove all candidates that have become obsolete
958 std::sort(candidates.begin(), candidates.end(), std::greater<Candidate>());
959 size_t remainingCandidates = 0;
960 for (size_t i = 0; i < candidates.size(); ++i)
961 if (candidates[i].ExpectedValue() >= m_options.m_minSupport &&
962 candidates[i].Size() > 0)
963 {
964 candidates[remainingCandidates++] = candidates[i];
965 }
966 candidates.resize(remainingCandidates);
967 } // Ende abgrasen
968 if (foundCandidate)
969 {
970 std::sort(candidates.begin(), candidates.end(), std::greater<Candidate>());
971 size_t remainingCandidates = 0;
972 size_t nonConnectedCount = 0;
973 for (size_t i = 0; i < candidates.size(); ++i)
974 if (candidates[i].ExpectedValue() >= m_options.m_minSupport &&
975 (candidates[i].ComputedSubsets() > octrees.size() - 3 ||
976 nonConnectedCount++ < 500))
977 {
978 candidates[remainingCandidates++] = candidates[i];
979 }
980 else if (candidates[i].ExpectedValue() > maxForgottenCandidate)
981 {
982 maxForgottenCandidate = candidates[i].ExpectedValue();
983 }
984 candidates.resize(remainingCandidates);
985
986 numTries = 0;
987 }
988 else
989 {
990 numTries++;
991 }
992 } while (CandidateFailureProbability(m_options.m_minSupport,
993 currentSize - numInvalid,
994 drawnCandidates,
995 globalOctTreeMaxNodeDepth) > m_options.m_probability &&
996 (currentSize - numInvalid) >= m_options.m_minSupport && !timeout());
997
998 if (numInvalid)
999 {
1000 // rearrange the last shapes
1001 //this is a two stage procedure:
1002 // 1) determine the new address of each Point and store it in the shapeIndex array
1003 // 2) swap Points according to new addresses
1004 size_t begin = beginIdx, end = beginIdx + currentSize;
1005 // these hold the address ranges for the lately detected shapes
1006 MiscLib::Vector<size_t> shapeIterators(numShapes);
1007 int shapeIt = shapes->size() - numShapes;
1008 for (size_t i = 0; i < numShapes; ++i, ++shapeIt)
1009 {
1010 shapeIterators[i] = end -= ((*shapes)[shapeIt]).second;
1011 }
1012
1013 for (size_t i = beginIdx; i < beginIdx + currentSize; ++i)
1014 if (shapeIndex[i] < 0)
1015 {
1016 shapeIndex[i] = begin++;
1017 }
1018 else
1019 {
1020 shapeIndex[i] = shapeIterators[shapeIndex[i]]++;
1021 }
1022
1023 //regarding swapping it is best to swap both the addresses and the data
1024 for (size_t i = beginIdx; i < beginIdx + currentSize; ++i)
1025 while (i != shapeIndex[i])
1026 {
1027 pc.swapPoints(i, shapeIndex[i]);
1028 std::swap(shapeIndex[i], shapeIndex[shapeIndex[i]]);
1029 }
1030 }
1031 // clean up subset octrees
1032 for (size_t i = 0; i < octrees.size(); ++i)
1033 {
1034 delete octrees[i];
1035 }
1036 // optimize parametrizations
1037 size_t eidx = endIdx;
1038 for (size_t i = 0; i < shapes->size(); ++i)
1039 {
1040 size_t bidx = eidx - (*shapes)[i].second;
1041 (*shapes)[i].first->OptimizeParametrization(pc, bidx, eidx, m_options.m_bitmapEpsilon);
1042 eidx = bidx;
1043 }
1044
1045 // prune nonsense shapes
1046 for (size_t i = shapes->size(); i != 0; --i)
1047 {
1048 if (shapes->at(i - 1).second == 0)
1049 {
1050 shapes->erase(shapes->begin() + i - 1);
1051 }
1052 }
1053 return currentSize - numInvalid;
1054}
1055
1056bool
1057RansacShapeDetector::DrawSamplesStratified(const IndexedOctreeType& oct,
1058 size_t numSamples,
1059 size_t depth,
1060 const MiscLib::Vector<int>& shapeIndex,
1061 MiscLib::Vector<size_t>* samples,
1062 const IndexedOctreeType::CellType** node) const
1063{
1064 for (size_t tries = 0; tries < m_maxCandTries; tries++)
1065 {
1066 samples->clear();
1067 //get first point, which also determines octree cell
1068 size_t first;
1069 do
1070 {
1071 first = oct.Dereference(rn_rand() % oct.size());
1072 } while (shapeIndex[first] != -1);
1073 samples->push_back(first);
1074
1075 std::pair<size_t, size_t> nodeRange;
1076 *node = oct.NodeContainingPoint(oct.at(first), depth, numSamples, &nodeRange);
1077
1078 if ((*node)->Size() < numSamples)
1079 {
1080 continue;
1081 }
1082
1083 while (samples->size() < numSamples)
1084 {
1085 size_t i, iter = 0;
1086 do
1087 {
1088 i = oct.Dereference(rn_rand() % (*node)->Size() + nodeRange.first);
1089 } while ((shapeIndex[i] != -1 ||
1090 std::find(samples->begin(), samples->end(), i) != samples->end()) &&
1091 iter++ < 40);
1092 if (iter >= 40)
1093 {
1094 break;
1095 }
1096 samples->push_back(i);
1097 }
1098
1099 if (samples->size() == numSamples)
1100 {
1101 return true;
1102 }
1103 }
1104 return false;
1105}
1106
1108RansacShapeDetector::Fit(bool allowDifferentShapes,
1109 const PrimitiveShape& initialShape,
1110 const PointCloud& pc,
1113 std::pair<size_t, float>* score) const
1114{
1115 if (!m_constructors.size())
1116 {
1117 return NULL;
1118 }
1119 PrimitiveShape* bestShape = NULL;
1120 if (m_options.m_fitting == Options::LS_FITTING)
1121 bestShape = initialShape.LSFit(
1122 pc, m_options.m_epsilon, m_options.m_normalThresh, begin, end, score);
1123 return bestShape;
1124}
uint8_t index
GfxTL::AACubeTree< 3, ScoreAACubeTreeStrategy< 3, RebuildAACubeTreeStrategy< GfxTL::BucketSizeMaxLevelSubdivisionTreeStrategy< GfxTL::CellLevelTreeStrategy< GfxTL::CellCenterAACubeTreeStrategy< 3, GfxTL::BaseAACubeTreeStrategy< GfxTL::CellRangeDataTreeStrategy< GfxTL::NullTreeStrategy, GfxTL::IteratedIndexedIteratorTreeDataKernel< MiscLib::Vector< size_t >::iterator, PointCloud::const_iterator > > > > > > > > > IndexedOctreeType
Definition Octree.h:44
GfxTL::AACubeTree< 3, ScoreAACubeTreeStrategy< 3, RebuildAACubeTreeStrategy< GfxTL::BucketSizeMaxLevelSubdivisionTreeStrategy< GfxTL::CellLevelTreeStrategy< GfxTL::CellCenterAACubeTreeStrategy< 3, GfxTL::BaseAACubeTreeStrategy< GfxTL::CellRangeDataTreeStrategy< GfxTL::NullTreeStrategy, GfxTL::ShuffleIndicesTreeDataKernelStrategy< GfxTL::ImmediateRangeTreeDataKernel< PointCloud >, MiscLib::Vector< size_t > > > > > > > > > > ImmediateOctreeType
Definition Octree.h:31
constexpr T c
bool IsEquivalent(const Candidate &c, const PointCloud &pc, float epsilon, float normalThresh) const
PrimitiveShape * Shape()
Definition Candidate.h:26
void GlobalScore(ScoreVisitorT &scoreVisitor, const IndexedOctreeType &oct)
Definition Candidate.h:392
float UpperBound() const
Definition Candidate.h:44
MiscLib::RefCounted< MiscLib::Vector< size_t > > * Indices()
Definition Candidate.h:50
float LowerBound() const
Definition Candidate.h:38
size_t Size() const
Definition Candidate.h:132
void ConnectedComponent(const PointCloud &pc, float bitmapEpsilon, float *borderRatio=0)
float ExpectedValue() const
Definition Candidate.h:68
size_t ComputedSubsets() const
Definition Candidate.h:62
void Clone(Candidate *c) const
Definition Candidate.h:193
bool ImproveBounds(const MiscLib::Vector< ImmediateOctreeType * > &octrees, const PointCloud &pc, ScoreVisitorT &scoreVisitor, size_t currentSize, float bitmapEpsilon, size_t maxSubset, size_t minPoints=500)
Definition Candidate.h:245
float GlobalWeightedScore(ScoreVisitorT &scoreVisitor, const IndexedOctreeType &oct, const PointCloud &pc, float epsilon, float normalThresh, float bitmapEpsilon)
Definition Candidate.h:403
const CellType * NodeContainingPoint(const PointT &point, size_t maxLevel, size_t minSize, CellRange *range) const
Definition AACubeTree.h:442
AACubeTreeCell< DimT, typename ScoreAACubeTreeStrategy< 3, RebuildAACubeTreeStrategy< GfxTL::BucketSizeMaxLevelSubdivisionTreeStrategy< GfxTL::CellLevelTreeStrategy< GfxTL::CellCenterAACubeTreeStrategy< 3, GfxTL::BaseAACubeTreeStrategy< GfxTL::CellRangeDataTreeStrategy< GfxTL::NullTreeStrategy, GfxTL::IteratedIndexedIteratorTreeDataKernel< MiscLib::Vector< size_t >::iterator, PointCloud::const_iterator > > > > > > > >::CellData > CellType
Definition AACubeTree.h:345
void Bound(const Points &points, size_t size)
Definition AACube.h:86
void resize(size_type s, const value_type &v)
size_type size() const
unsigned int Release() const
Definition RefCount.h:43
void pop_back()
Definition Vector.h:451
void resize(size_type s, const value_type &v)
Definition Vector.h:227
const PrimitiveShapeConstructor ** const_iterator
Definition Vector.h:25
PrimitiveShapeConstructor ** iterator
Definition Vector.h:24
size_type size() const
Definition Vector.h:215
void erase(T *where)
Definition Vector.h:426
void push_back(const T &v)
Definition Vector.h:354
void clear()
Definition Vector.h:175
void swapPoints(unsigned int i, unsigned int j)
Definition PointCloud.h:107
PrimtiveShape is a shape primitive in conjunction with a parametrization.
virtual PrimitiveShape * LSFit(const PointCloud &pc, float epsilon, float normalThresh, MiscLib::Vector< size_t >::const_iterator begin, MiscLib::Vector< size_t >::const_iterator end, std::pair< size_t, float > *score) const =0
virtual void Description(std::string *s) const =0
virtual void DistanceAndNormalDeviation(const Vec3f &p, const Vec3f &n, std::pair< float, float > *dn) const =0
size_t Detect(PointCloud &pc, size_t begin, size_t end, MiscLib::Vector< std::pair< MiscLib::RefCountPtr< PrimitiveShape >, size_t > > *shapes)
void Add(PrimitiveShapeConstructor *c)
void SetShapeIndex(const MiscLib::Vector< int > &shapeIndex)
size_t rn_rand()
Definition Random.h:22
void rn_setseed(size_t)
Definition Random.cpp:27
double s(double t, double s0, double v0, double a0, double j)
Definition CtrlUtil.h:33
bool operator()(const Candidate *a, const Candidate *b) const
static constexpr auto invalidRuntime