Apollo 10.0
自动驾驶开放平台
hough_transfer.cc
浏览该文件的文档.
1/******************************************************************************
2 * Copyright 2018 The Apollo Authors. All Rights Reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *****************************************************************************/
17
18namespace apollo {
19namespace perception {
20namespace algorithm {
21
23 : prepared_(false),
24 d_r_(0.0),
25 d_theta_(0.0),
26 img_w_(0),
27 img_h_(0),
28 r_size_(0),
29 theta_size_(0),
30 vote_reserve_size_(10),
31 vote_map_(),
32 query_map_(),
33 distribute_map_() {}
34
35// step1
36// @brief: initiate
37// @params[IN] img_w, img_h: width and height of binary image
38// d_r, d_theta: discretization step of r and theta
39// in polar coordinates
40bool HoughTransfer::Init(int img_w, int img_h, float d_r, float d_theta) {
41 img_w_ = img_w;
42 img_h_ = img_h;
43 d_r_ = d_r;
44 d_theta_ = static_cast<float>(M_PI) * d_theta / 180.0f;
45 r_size_ = static_cast<int>(
46 2 * sqrtf(static_cast<float>(img_w_ * img_w_ + img_h_ * img_h_)) / d_r_);
47 theta_size_ = static_cast<int>(M_PI / d_theta_);
48
50 // init vote map
51 vote_map_.resize(r_size_ * theta_size_, 0);
52 // init query map
53 query_map_.resize(img_w_ * img_h_);
54 for (auto& query : query_map_) {
55 query.resize(theta_size_, 0);
56 }
57
58 for (int theta_idx = 0; theta_idx < theta_size_; ++theta_idx) {
59 float cur_theta = d_theta_ * static_cast<float>(theta_idx);
60 for (int img_pos = 0; img_pos < img_w_ * img_h_; ++img_pos) {
61 int w = img_pos % img_w_;
62 int h = img_pos / img_w_;
63 int r =
64 static_cast<int>((cos(cur_theta) * w + sin(cur_theta) * h) / d_r_) +
65 r_size_ / 2;
66 if (0 <= r && r < r_size_) {
67 query_map_[img_pos][theta_idx] = r * theta_size_ + theta_idx;
68 }
69 }
70 }
71 // init distribute map
72 distribute_map_.resize(r_size_ * theta_size_);
73 for (auto& distribute : distribute_map_) {
74 distribute.reserve(vote_reserve_size_);
75 }
76
77 if (CheckPrepared()) {
78 prepared_ = true;
79 } else {
81 prepared_ = false;
82 }
83 return prepared_;
84}
85
86// step2
87// @brief: HoughTransform in 2D binary image
88// @params[IN] image: 2D binary image.
89// with_distribute: flag to control whether to calculate element
90// length,vote_num,pts in HoughLine
91bool HoughTransfer::ImageVote(const std::vector<int>& image,
92 bool with_distribute) {
93 if (image.size() != query_map_.size()) {
94 return false;
95 }
96 ResetMaps(with_distribute);
97 for (size_t i = 0; i < image.size(); ++i) {
98 if (image[i] > 0) {
99 int x = static_cast<int>(i) % img_w_;
100 int y = static_cast<int>(i) / img_w_;
101 PointVote(x, y, with_distribute);
102 }
103 }
104 return true;
105}
106
107// @brief: transform one point to parameter space in polar coodinates and vote
108// @params[IN] x, y: pos in image.
109// with_distribute: flag to control whether to calculate element
110// length,vote_num,pts in HoughLine
111void HoughTransfer::PointVote(int x, int y, bool with_distribute) {
112 const int pos = y * img_w_ + x;
113 PointVote(pos, with_distribute);
114}
115
116// @paramas[IN] pos: pos = y*img_w +x
117void HoughTransfer::PointVote(int pos, bool with_distribute) {
118 for (int theta_idx = 0; theta_idx < theta_size_; ++theta_idx) {
119 ++vote_map_[query_map_[pos][theta_idx]];
120 if (with_distribute) {
121 distribute_map_[query_map_[pos][theta_idx]].push_back(pos);
122 }
123 }
124}
125
126// step3
127// @brief get lines
128// @params[IN] min_pt_num: minimum points on the same line.
129// r_neibor, theta_neibor: query region
130// with_distribute: flag to control whether to calculate element
131// length,vote_num,pts in HoughLine
132// lines: save lines detected.
133bool HoughTransfer::GetLines(int min_pt_num, int r_neibor, int theta_neibor,
134 bool with_distribute,
135 std::vector<HoughLine>* lines) const {
136 if (!lines) {
137 return false;
138 }
139 int r_step = 2 * r_neibor + 1;
140 int theta_step = 2 * theta_neibor + 1;
141
142 // search one vote neighbor for max_vote position in the region
143 std::set<int> max_vote_lines;
144 GetMaxVotes(min_pt_num, r_neibor, theta_neibor, r_step, theta_step,
145 &max_vote_lines);
146
147 // from max vote pos to get line params
148 lines->resize(max_vote_lines.size());
149 int idx = 0;
150 for (auto i = max_vote_lines.begin(); i != max_vote_lines.end(); ++i) {
151 if (!VotePosToHoughLine(*i, with_distribute, &(*lines)[idx++])) {
152 return false;
153 }
154 }
155 return true;
156}
157
158unsigned int HoughTransfer::MemoryConsume() const {
159 unsigned int size = 0;
160 if (is_prepared()) {
161 size +=
162 static_cast<unsigned int>(vote_map_.capacity() * sizeof(vote_map_[0]));
163 size += static_cast<unsigned int>(query_map_.capacity() *
164 sizeof(query_map_[0]));
165 size += static_cast<unsigned int>(theta_size_ * query_map_.size() *
166 sizeof(query_map_[0][0]));
167 size += static_cast<unsigned int>(distribute_map_.capacity() *
168 sizeof(distribute_map_[0]));
169 for (const auto& distribute : distribute_map_) {
170 size += static_cast<unsigned int>(distribute.capacity() *
171 sizeof(distribute[0]));
172 }
173 }
174 return size;
175}
176
177// prepared state not change.
178// when we use hough with with_distribute mode in large image long time,
179// memory consume maybe too large, so use this func to free no used cache.
181 for (auto& distribute : distribute_map_) {
182 distribute.shrink_to_fit();
183 }
184}
185
186void HoughTransfer::ResetMaps(bool with_distribute) {
187 memset(vote_map_.data(), 0, vote_map_.size() * sizeof(vote_map_[0]));
188 if (with_distribute) {
189 for (auto& distribute : distribute_map_) {
190 distribute.clear();
191 }
192 }
193}
194
196 vote_map_.clear();
197 vote_map_.shrink_to_fit();
198 query_map_.clear();
199 query_map_.shrink_to_fit();
200 distribute_map_.clear();
201 distribute_map_.shrink_to_fit();
202 prepared_ = false;
203}
204
205bool HoughTransfer::CheckPrepared() const {
206 if (static_cast<int>(vote_map_.size()) != r_size_ * theta_size_) {
207 return false;
208 }
209 if (static_cast<int>(query_map_.size()) != img_w_ * img_h_) {
210 return false;
211 }
212 if (static_cast<int>(distribute_map_.size()) != r_size_ * theta_size_) {
213 return false;
214 }
215 if (vote_map_.empty() || query_map_.empty() || distribute_map_.empty()) {
216 return false;
217 }
218 return true;
219}
220
221void HoughTransfer::GetMaxVotes(int min_pt_num, int r_neibor, int theta_neibor,
222 int r_step, int theta_step,
223 std::set<int>* max_vote_lines) const {
224 for (int i = r_neibor; i < r_size_ - r_neibor; i += r_step) {
225 for (int j = theta_neibor; j < theta_size_ - theta_neibor;
226 j += theta_step) {
227 int max_pos = -1;
228 int max_vote = min_pt_num;
229 for (int m = -r_neibor; m <= r_neibor; ++m) {
230 for (int n = -theta_neibor; n <= theta_neibor; ++n) {
231 int neibor_pos = (i + m) * theta_size_ + j + n;
232 if (vote_map_[neibor_pos] >= max_vote) {
233 max_pos = neibor_pos;
234 max_vote = vote_map_[neibor_pos];
235 }
236 }
237 }
238 if (max_pos >= 0) {
239 max_vote_lines->insert(max_pos);
240 }
241 }
242 }
243}
244
245bool HoughTransfer::VotePosToHoughLine(int vote_pos, bool with_distribute,
246 HoughLine* out_line) const {
247 if (!out_line) {
248 return false;
249 }
250 out_line->r = static_cast<float>(vote_pos / theta_size_ - r_size_ / 2) * d_r_;
251 out_line->theta = static_cast<float>(vote_pos % theta_size_) * d_theta_;
252 out_line->vote_num = vote_map_[vote_pos];
253 if (with_distribute) {
254 if (out_line->vote_num !=
255 static_cast<int>(distribute_map_[vote_pos].size())) {
256 return false;
257 }
258 out_line->pts = distribute_map_[vote_pos];
259 const int start_pos = distribute_map_[vote_pos][0];
260 const int end_pos = distribute_map_[vote_pos][out_line->vote_num - 1];
261 const int start_x = start_pos % img_w_;
262 const int start_y = start_pos / img_w_;
263 const int end_x = end_pos % img_w_;
264 const int end_y = end_pos / img_w_;
265 out_line->length =
266 sqrtf(static_cast<float>((start_x - end_x) * (start_x - end_x) +
267 (start_y - end_y) * (start_y - end_y)));
268 }
269 return true;
270}
271
272} // namespace algorithm
273} // namespace perception
274} // namespace apollo
bool ImageVote(const std::vector< int > &image, bool with_distribute)
bool Init(int img_w, int img_h, float d_r, float d_theta)
bool GetLines(int min_pt_num, int r_neibor, int theta_neibor, bool with_distribute, std::vector< HoughLine > *lines) const
void PointVote(int x, int y, bool with_distribute)
class register implement
Definition arena_queue.h:37