Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit d83df66

Browse filesBrowse files
authored
Merge pull request opencv#26834 from chacha21:findContours_speedup
Find contours speedup opencv#26834 It is an attempt, as suggested by opencv#26775, to restore lost speed when migrating `findContours()` implementation from C to C++ The patch adds an "Arena" (a pool) of pre-allocated memory so that contours points (and TreeNodes) can be picked from the Arena. The code of `findContours()` is mostly unchanged, the arena usage being implicit through a utility class Arena::Item that provides C++ overloaded operators and construct/destruct logic. As mentioned in opencv#26775, the contour points are allocated and released in order, and can be represented by ranges of indices in their arena. No range subset will be released and drill a hole, that's why the internal representation as a range of indices makes sense. The TreeNodes use another Arena class that does not comply to that range logic. Currently, there is a significant improvement of the run-time on the test mentioned in opencv#26775, but it is still far from the `findContours_legacy()` performance. - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [X] The PR is proposed to the proper branch - [X] There is a reference to the original bug report and related work - [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [ ] The feature is well documented and sample code can be built with the project CMake
1 parent 0db6a49 commit d83df66
Copy full SHA for d83df66

File tree

Expand file treeCollapse file tree

6 files changed

+270
-50
lines changed
Filter options
Expand file treeCollapse file tree

6 files changed

+270
-50
lines changed

‎modules/imgproc/src/contours_approx.cpp

Copy file name to clipboardExpand all lines: modules/imgproc/src/contours_approx.cpp
+11-11Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ static const Point chainCodeDeltas[8] =
3131
// Restores all the digital curve points from the chain code.
3232
// Removes the points (from the resultant polygon)
3333
// that have zero 1-curvature
34-
static vector<ApproxItem> pass_0(const vector<schar>& chain, Point pt, bool isApprox, bool isFull)
34+
static vector<ApproxItem> pass_0(const ContourCodesStorage& chain, Point pt, bool isApprox, bool isFull)
3535
{
3636
vector<ApproxItem> res;
3737
const size_t len = chain.size();
@@ -52,17 +52,14 @@ static vector<ApproxItem> pass_0(const vector<schar>& chain, Point pt, bool isAp
5252
return res;
5353
}
5454

55-
static vector<Point> gatherPoints(const vector<ApproxItem>& ares)
55+
static void gatherPoints(const vector<ApproxItem>& ares, ContourPointsStorage& output)
5656
{
57-
vector<Point> res;
58-
res.reserve(ares.size() / 2);
57+
output.clear();
5958
for (const ApproxItem& item : ares)
6059
{
61-
if (item.removed)
62-
continue;
63-
res.push_back(item.pt);
60+
if (!item.removed)
61+
output.push_back(item.pt);
6462
}
65-
return res;
6663
}
6764

6865
static size_t calc_support(const vector<ApproxItem>& ares, size_t i)
@@ -273,11 +270,14 @@ static void pass_cleanup(vector<ApproxItem>& ares, size_t start_idx)
273270
} // namespace
274271

275272

276-
vector<Point> cv::approximateChainTC89(vector<schar> chain, const Point& origin, const int method)
273+
void cv::approximateChainTC89(const ContourCodesStorage& chain, const Point& origin, const int method,
274+
ContourPointsStorage& output)
277275
{
278276
if (chain.size() == 0)
279277
{
280-
return vector<Point>({origin});
278+
output.clear();
279+
output.push_back(origin);
280+
return;
281281
}
282282

283283
const bool isApprox = method == CHAIN_APPROX_TC89_L1 || method == CHAIN_APPROX_TC89_KCOS;
@@ -349,5 +349,5 @@ vector<Point> cv::approximateChainTC89(vector<schar> chain, const Point& origin,
349349
}
350350
}
351351

352-
return gatherPoints(ares);
352+
gatherPoints(ares, output);
353353
}
+122Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
// This file is part of OpenCV project.
2+
// It is subject to the license terms in the LICENSE file found in the top-level directory
3+
// of this distribution and at http://opencv.org/license.html
4+
5+
#ifndef OPENCV_CONTOURS_BLOCKSTORAGE_HPP
6+
#define OPENCV_CONTOURS_BLOCKSTORAGE_HPP
7+
8+
#include "precomp.hpp"
9+
10+
#include <array>
11+
12+
namespace cv {
13+
14+
// BLOCK_SIZE_ELEM - number of elements in a block
15+
// STATIC_CAPACITY_BYTES - static memory in bytes for preallocated blocks
16+
template <typename T, size_t BLOCK_SIZE_ELEM = 1024, size_t STATIC_CAPACITY_BYTES = 4096>
17+
class BlockStorage {
18+
public:
19+
using value_type = T;
20+
typedef struct {value_type data[BLOCK_SIZE_ELEM];} block_type;
21+
22+
BlockStorage()
23+
{
24+
const size_t minDynamicBlocks = !staticBlocksCount ? 1 : 0;
25+
for(size_t i = 0 ; i<minDynamicBlocks ; ++i)
26+
dynamicBlocks.push_back(new block_type);
27+
}
28+
BlockStorage(const BlockStorage&) = delete;
29+
BlockStorage(BlockStorage&&) noexcept = default;
30+
~BlockStorage() {
31+
for(const auto & block : dynamicBlocks) {
32+
delete block;
33+
}
34+
}
35+
BlockStorage& operator=(const BlockStorage&) = delete;
36+
BlockStorage& operator=(BlockStorage&&) noexcept = default;
37+
38+
void clear(void) {
39+
const size_t minDynamicBlocks = !staticBlocksCount ? 1 : 0;
40+
for(size_t i = minDynamicBlocks, count = dynamicBlocks.size() ; i<count ; ++i ) {
41+
delete dynamicBlocks[i];
42+
}
43+
dynamicBlocks.resize(minDynamicBlocks);
44+
sz = 0;
45+
}
46+
47+
void push_back(const value_type& value) {
48+
const size_t blockIndex = sz / BLOCK_SIZE_ELEM;
49+
const size_t currentBlocksCount = staticBlocksCount+dynamicBlocks.size();
50+
if (blockIndex == currentBlocksCount)
51+
dynamicBlocks.push_back(new block_type);
52+
block_type& cur_block =
53+
(blockIndex < staticBlocksCount) ? staticBlocks[blockIndex] :
54+
*dynamicBlocks[blockIndex-staticBlocksCount];
55+
cur_block.data[sz % BLOCK_SIZE_ELEM] = value;
56+
++sz;
57+
}
58+
59+
size_t size() const { return sz; }
60+
61+
const value_type& at(size_t index) const {
62+
const size_t blockIndex = index / BLOCK_SIZE_ELEM;
63+
const block_type& cur_block =
64+
(blockIndex < staticBlocksCount) ? staticBlocks[blockIndex] :
65+
*dynamicBlocks[blockIndex-staticBlocksCount];
66+
return cur_block.data[index % BLOCK_SIZE_ELEM];
67+
}
68+
value_type& at(size_t index) {
69+
const size_t blockIndex = index / BLOCK_SIZE_ELEM;
70+
block_type& cur_block =
71+
(blockIndex < staticBlocksCount) ? staticBlocks[blockIndex] :
72+
*dynamicBlocks[blockIndex-staticBlocksCount];
73+
return cur_block.data[index % BLOCK_SIZE_ELEM];
74+
}
75+
const value_type& operator[](size_t index) const {return at(index);}
76+
value_type& operator[](size_t index) {return at(index);}
77+
public:
78+
friend class RangeIterator;
79+
class RangeIterator
80+
{
81+
public:
82+
RangeIterator(const BlockStorage* _owner, size_t _first, size_t _last)
83+
:owner(_owner),remaining(_last-_first),
84+
blockIndex(_first/BLOCK_SIZE_ELEM),offset(_first%BLOCK_SIZE_ELEM) {
85+
}
86+
private:
87+
const BlockStorage* owner = nullptr;
88+
size_t remaining = 0;
89+
size_t blockIndex = 0;
90+
size_t offset = 0;
91+
public:
92+
bool done(void) const {return !remaining;}
93+
std::pair<const value_type*, size_t> operator*(void) const {return get();}
94+
std::pair<const value_type*, size_t> get(void) const {
95+
const block_type& cur_block =
96+
(blockIndex < owner->staticBlocksCount) ? owner->staticBlocks[blockIndex] :
97+
*owner->dynamicBlocks[blockIndex-owner->staticBlocksCount];
98+
const value_type* rangeStart = cur_block.data+offset;
99+
const size_t rangeLength = std::min(remaining, BLOCK_SIZE_ELEM-offset);
100+
return std::make_pair(rangeStart, rangeLength);
101+
}
102+
RangeIterator& operator++() {
103+
std::pair<const value_type*, size_t> range = get();
104+
remaining -= range.second;
105+
offset = 0;
106+
++blockIndex;
107+
return *this;
108+
}
109+
};
110+
RangeIterator getRangeIterator(size_t first, size_t last) const {
111+
return RangeIterator(this, first, last);
112+
}
113+
private:
114+
std::array<block_type, STATIC_CAPACITY_BYTES/(BLOCK_SIZE_ELEM*sizeof(value_type))> staticBlocks;
115+
const size_t staticBlocksCount = STATIC_CAPACITY_BYTES/(BLOCK_SIZE_ELEM*sizeof(value_type));
116+
std::vector<block_type*> dynamicBlocks;
117+
size_t sz = 0;
118+
};
119+
120+
} // namespace cv
121+
122+
#endif // OPENCV_CONTOURS_BLOCKSTORAGE_HPP

‎modules/imgproc/src/contours_common.cpp

Copy file name to clipboardExpand all lines: modules/imgproc/src/contours_common.cpp
+8-9Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,11 @@ void cv::contourTreeToResults(CTree& tree,
2222
return;
2323
}
2424

25+
CV_Assert(tree.size() < (size_t)numeric_limits<int>::max());
2526
// mapping for indexes (original -> resulting)
26-
map<int, int> index_mapping;
27-
index_mapping[-1] = -1;
28-
index_mapping[0] = -1;
27+
// -1 - based indexing
28+
vector<int> index_mapping(tree.size() + 1, -1);
2929

30-
CV_Assert(tree.size() < (size_t)numeric_limits<int>::max());
3130
const int total = (int)tree.size() - 1;
3231
_contours.create(total, 1, 0, -1, true);
3332
{
@@ -39,7 +38,7 @@ void cv::contourTreeToResults(CTree& tree,
3938
CV_Assert(elem.self() != -1);
4039
if (elem.self() == 0)
4140
continue;
42-
index_mapping[elem.self()] = i;
41+
index_mapping.at(elem.self() + 1) = i;
4342
CV_Assert(elem.body.size() < (size_t)numeric_limits<int>::max());
4443
const int sz = (int)elem.body.size();
4544
_contours.create(sz, 1, res_type, i, true);
@@ -65,10 +64,10 @@ void cv::contourTreeToResults(CTree& tree,
6564
if (elem.self() == 0)
6665
continue;
6766
Vec4i& h_vec = h_mat.at<Vec4i>(i);
68-
h_vec = Vec4i(index_mapping.at(elem.next),
69-
index_mapping.at(elem.prev),
70-
index_mapping.at(elem.first_child),
71-
index_mapping.at(elem.parent));
67+
h_vec = Vec4i(index_mapping.at(elem.next + 1),
68+
index_mapping.at(elem.prev + 1),
69+
index_mapping.at(elem.first_child + 1),
70+
index_mapping.at(elem.parent + 1));
7271
++i;
7372
}
7473
}

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.