Apollo 10.0
自动驾驶开放平台
camera_ground_plane.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
18#include <utility>
19
22
23namespace apollo {
24namespace perception {
25namespace camera {
26
27void ConvertGround3ToGround4(const float &baseline,
28 const std::vector<float> &k_mat,
29 const std::vector<float> &ground3,
30 std::vector<float> *ground4) {
31 CHECK_GT(baseline, 0.0f);
32 CHECK_EQ(k_mat.size(), 9U);
33 CHECK_EQ(ground3.size(), 3U);
34 CHECK_NOTNULL(ground4);
35 CHECK_EQ(ground4->size(), 4U);
36 const float &b = baseline;
37 const float &fx = k_mat[0];
38 const float &fy = k_mat[4];
39 const float &cy = k_mat[5];
40 ground4->data()[0] = 0.0f;
41 ground4->data()[1] = ground3[0] * fy;
42 ground4->data()[2] = ground3[0] * cy + ground3[2];
43 ground4->data()[3] = ground3[1] * b * fx;
44 float norm = algorithm::ISqrt(algorithm::ISquaresum3(ground4->data()));
45 algorithm::IScale4(ground4->data(), algorithm::IRec(norm));
46}
47
48bool ConvertGround4ToGround3(const float &baseline,
49 const std::vector<float> &k_mat,
50 const std::vector<float> &ground4,
51 std::vector<float> *ground3) {
52 CHECK_GT(baseline, 0.0f);
53 CHECK_EQ(k_mat.size(), 9U);
54 CHECK_EQ(ground4.size(), 4U);
55 CHECK_NOTNULL(ground3);
56 CHECK_EQ(ground3->size(), 3U);
57 // normalization
58 float p[4] = {ground4[0], ground4[1], ground4[2], ground4[3]};
61 if (p[0] > 1e-3) {
62 AERROR << "Have roll in the ground plane: " << p[0];
63 ground3->assign(3, 0.f);
64 return false;
65 }
66 // no roll
67 const float &b = baseline;
68 const float &fx = k_mat[0];
69 const float &fy = k_mat[4];
70 const float &cy = k_mat[5];
71 ground3->data()[0] = p[1] * algorithm::IRec(fy);
72 ground3->data()[1] = p[3] * algorithm::IRec(b * fx);
73 ground3->data()[2] = p[2] - ground3->data()[0] * cy;
74 return true;
75}
76
77void GetGroundPlanePitchHeight(const float &baseline,
78 const std::vector<float> &k_mat,
79 const std::vector<float> &ground3, float *pitch,
80 float *cam_height) {
81 CHECK_GT(baseline, 0.0f);
82 CHECK_EQ(k_mat.size(), 9U);
83 CHECK_EQ(ground3.size(), 3U);
84 CHECK_NOTNULL(pitch);
85 CHECK_NOTNULL(cam_height);
86 std::vector<float> ground4(4, 0.0f);
87 ConvertGround3ToGround4(baseline, k_mat, ground3, &ground4);
88 if (ground4[3] > 0.0f) {
89 algorithm::IScale4(ground4.data(), -1.0f);
90 }
91 *cam_height = -ground4[3];
92 double cos_pitch = ground4[1];
93 double sin_pitch = -ground4[2];
94 *pitch = static_cast<float>(atan2(sin_pitch, cos_pitch));
95}
96
97void GetGround3FromPitchHeight(const std::vector<float> &k_mat,
98 const float &baseline, const float &pitch,
99 const float &cam_height,
100 std::vector<float> *ground3) {
101 CHECK_EQ(k_mat.size(), 9U);
102 CHECK_GT(baseline, 0.0f);
103 CHECK_GT(cam_height, 0.0f);
104 CHECK_NOTNULL(ground3);
105 CHECK_EQ(ground3->size(), 3U);
106 float sin_pitch = static_cast<float>(sin(pitch));
107 float cos_pitch = static_cast<float>(cos(pitch));
108 std::vector<float> ground4 = {0, cos_pitch, -sin_pitch, -cam_height};
109 ConvertGround4ToGround3(baseline, k_mat, ground4, ground3);
110}
111
113 if (track_length <= 0) {
114 AERROR << "track_length, " << track_length << ", should be positive";
115 }
116 pitch_height_inlier_tracks_.resize(track_length * 3);
117 const_weight_temporal_.resize(track_length, 0.0f);
118
119 weight_.resize(track_length, 0.0f);
120 for (int i = track_length - 1; i >= 0; --i) {
121 const_weight_temporal_.at(i) =
122 algorithm::IPow(algorithm::ISqrt(2.0f), track_length - 1 - i);
123 }
124
125 // normalize
126 float accm_sum = algorithm::ISum(const_weight_temporal_.data(), track_length);
127 algorithm::IScale(const_weight_temporal_.data(), track_length,
128 algorithm::IRec(accm_sum));
129
130 head_ = track_length;
131}
132
133void GroundPlaneTracker::Push(const std::vector<float> &ph,
134 const float &inlier_ratio) {
135 CHECK_EQ(ph.size(), 2U);
136 int i = 0;
137 int length = static_cast<int>(pitch_height_inlier_tracks_.size());
138 if (head_ == 0) {
139 if (length > 3) { // move backwards
140 for (i = length - 1; i >= 3; i--) { // 3: pitch, height, inlier-number
141 pitch_height_inlier_tracks_[i] = pitch_height_inlier_tracks_[i - 3];
142 }
143 }
144 } else { // _head >= zero
145 head_ -= 1;
146 }
147
148 // fill the head_ (fresh input)
149 int head3 = head_ * 3;
150 pitch_height_inlier_tracks_[head3] = ph[0];
151 pitch_height_inlier_tracks_[head3 + 1] = ph[1];
152 pitch_height_inlier_tracks_[head3 + 2] = inlier_ratio;
153}
154
155void GroundPlaneTracker::GetGround(float *pitch, float *cam_height) {
156 CHECK_NOTNULL(pitch);
157 CHECK_NOTNULL(cam_height);
158 int i = 0;
159 int length = static_cast<int>(pitch_height_inlier_tracks_.size() / 3);
160 if (!length) {
161 *pitch = *cam_height = 0.0f;
162 return;
163 } else if (length == 1) {
164 *pitch = pitch_height_inlier_tracks_[0];
165 *cam_height = pitch_height_inlier_tracks_[1];
166 return;
167 }
168
169 float ph[2] = {0};
170 float w = 0.0f;
171 for (i = head_; i < length; i++) {
172 w = pitch_height_inlier_tracks_.at(i * 3 + 2);
173 weight_.at(i) = const_weight_temporal_.at(i) * w;
174 }
175
176 // normalize factor
177 float accm_wei = algorithm::ISum(weight_.data() + head_, (length - head_));
178
179 ph[0] = ph[1] = 0.0f;
180 for (i = head_; i < length; i++) {
181 w = weight_.at(i);
182 int i3 = i * 3;
183 ph[0] += pitch_height_inlier_tracks_.at(i3) * w;
184 ph[1] += pitch_height_inlier_tracks_.at(i3 + 1) * w;
185 }
186 ph[0] = algorithm::IDiv(ph[0], accm_wei);
187 ph[1] = algorithm::IDiv(ph[1], accm_wei);
188 *pitch = ph[0];
189 *cam_height = ph[1];
190}
191
193 unsigned int track_length =
194 static_cast<unsigned int>(pitch_height_inlier_tracks_.size() / 3);
195 auto &data = pitch_height_inlier_tracks_;
196 unsigned int i = 0;
197 for (; i < track_length; ++i) {
198 int i3 = i * 3;
199 data.at(i3) = data.at(i3 + 1) = data.at(i3 + 2) = 0.0f;
200 weight_.at(i) = 0.0f;
201 }
202 head_ = track_length; // reset to init value
203}
204
206 min_nr_samples = 6; // 40
207 nr_frames_track = 3; // 2
210 min_inlier_ratio = 0.5f;
211 /*
212 thres_inlier_plane_fitting = 1.5f; // in pixel
213 */
214 thres_inlier_plane_fitting = 0.0035f; // in reversed depth, 3m@30m
215}
216
217bool CameraGroundPlaneDetector::DetetGround(float pitch, float camera_height,
218 float *vd, int count_vd,
219 const std::vector<float> &plane) {
220 ground_is_valid_ = false;
221
222 std::vector<float> ground3(l_, l_ + 3);
223 std::vector<float> k_mat(k_mat_, k_mat_ + 9);
224
225 if (!plane.empty()) { // assigned from outside
226 // GetGeneralGroundPlaneModelReversed(k_mat_, baseline_, plane.data(),
227 // l_);
228 ConvertGround4ToGround3(baseline_, k_mat, plane, &ground3);
229 FillGroundModel(ground3);
230 AINFO << "set ground plane from outside: " << plane[0] << ", " << plane[1]
231 << ", " << plane[2] << ", " << plane[3];
232 ground_is_valid_ = true;
233 return true;
234 } else {
235 bool success = false;
236 float inlier_ratio = 0.0f;
237 std::vector<float> ph(2, 0);
238 if (CameraGroundPlaneDetector::DetectGroundFromSamples(vd, count_vd,
239 &inlier_ratio)) {
240 ADEBUG << "l: " << l_[0] << ", " << l_[1] << ", " << l_[2];
241 ground3.assign(l_, l_ + 3);
242 GetGroundPlanePitchHeight(baseline_, k_mat, ground3, &ph[0], &ph[1]);
243 ADEBUG << "ph: " << ph[0] << ", " << ph[1];
244 success = fabs(ph[0]) < params_.max_tilt_angle &&
245 ph[1] < params_.max_camera_ground_height;
246 if (success) {
247 ground_plane_tracker_->Push(ph, inlier_ratio);
248 ground_plane_tracker_->GetGround(&ph[0], &ph[1]);
249 GetGround3FromPitchHeight(k_mat, baseline_, ph[0], ph[1], &ground3);
250 FillGroundModel(ground3);
251 ADEBUG << "l tracked: " << l_[0] << ", " << l_[1] << ", " << l_[2];
252 ADEBUG << "ph tracked: " << ph[0] << ", " << ph[1];
253 }
254 }
255
256 if (success) {
257 ADEBUG << "succeed with inlier ratio: " << inlier_ratio;
258 ground_is_valid_ = true;
259 return true;
260 }
261
262 // backup using last successful frame or given pitch & height
263 if (ground_plane_tracker_->GetCurTrackLength() > 0) {
264 ground_plane_tracker_->GetGround(&ph[0], &ph[1]);
265 GetGround3FromPitchHeight(k_mat, baseline_, ph[0], ph[1], &ground3);
266 FillGroundModel(ground3);
267 } else {
268 ACHECK(fabs(pitch) < params_.max_tilt_angle);
269 ACHECK(camera_height < params_.max_camera_ground_height);
270 CHECK_GT(camera_height, 0.f);
271 GetGround3FromPitchHeight(k_mat, baseline_, pitch, camera_height,
272 &ground3);
273 FillGroundModel(ground3);
274 }
275 ground_plane_tracker_->Restart();
276 return false;
277 }
278}
279
280bool CameraGroundPlaneDetector::DetectGroundFromSamples(float *vd, int count_vd,
281 float *inlier_ratio) {
282 if (vd == nullptr) {
283 AERROR << "vd is nullptr";
284 return false;
285 }
286 if (inlier_ratio == nullptr) {
287 AERROR << "inlier_ratio is nullptr";
288 return false;
289 }
290 *inlier_ratio = 0.0f;
291 if (count_vd < params_.min_nr_samples) {
292 l_[0] = l_[1] = l_[2] = 0.0f;
293 return false;
294 }
295
296 double kMinInlierRatio = params_.min_inlier_ratio;
297 float kThresInlier = params_.thres_inlier_plane_fitting;
298 /*standard RANSAC solution*/
299 float *vs = ss_flt_.data();
300 float *ds = vs + count_vd;
301 for (int i = 0; i < count_vd; ++i) {
302 int i2 = i << 1;
303 vs[i] = vd[i2];
304 ds[i] = vd[i2 + 1];
305 }
306 int nr_inliers = 0;
307 int *inliers = ss_int_.data();
308 memset(inliers, 0, sizeof(int) * count_vd * 2);
309 float p[2] = {0};
310
311 if (!algorithm::RobustBinaryFitRansac<float, 1, 1, 2, 2,
312 GroundHypoGenFunc<float>,
313 GroundFittingCostFunc<float>, nullptr>(
314 vs, ds, count_vd, p, &nr_inliers, inliers, kThresInlier, false, true,
315 0.99f, kMinInlierRatio)) {
316 memset(l_, 0, sizeof(float) * 3);
317 return false;
318 } else {
319 *inlier_ratio = static_cast<float>(nr_inliers) *
320 algorithm::IRec(static_cast<float>(count_vd));
321 }
322
323 if (*inlier_ratio < kMinInlierRatio) {
324 *inlier_ratio = 0.0f;
325 memset(l_, 0, sizeof(float) * 3);
326 return false;
327 }
328
329 // re-fit using inliers
330 int count = 0;
331 for (int i = 0; i < nr_inliers; ++i) {
332 int i2 = inliers[i] << 1;
333 int count2 = count << 1;
334 std::swap(vd[i2], vd[count2]);
335 std::swap(vd[i2 + 1], vd[count2 + 1]);
336 ++count;
337 }
338 float l_best[3] = {0};
339 algorithm::ILineFit2dTotalLeastSquare(vd, l_best, count);
340 memcpy(l_, l_best, sizeof(float) * 3);
341 return true;
342}
343
344} // namespace camera
345} // namespace perception
346} // namespace apollo
bool DetetGround(float pitch, float camera_height, float *vd, int count_vd, const std::vector< float > &plane={})
void GetGround(float *pitch, float *cam_height)
void Push(const std::vector< float > &ph, const float &inlier_ratio)
#define ACHECK(cond)
Definition log.h:80
#define ADEBUG
Definition log.h:41
#define AERROR
Definition log.h:44
#define AINFO
Definition log.h:42
float IDegreeToRadians(float d)
Definition i_basic.h:266
void ILineFit2dTotalLeastSquare(T *x, T *l, int n)
Definition i_line.h:36
float IDiv(float a, float b)
Definition i_basic.h:35
T ISquaresum3(const T x[3])
Definition i_blas.h:2536
void IScale(T *x, int n, T sf)
Definition i_blas.h:1853
T ISum(const T *x, int n)
Definition i_blas.h:2344
float IPow(float a, float b)
Definition i_basic.h:142
bool RobustBinaryFitRansac(T *x, T *xp, int n, T *model, int *consensus_size, int *inliers, T error_tol, bool re_est_model_w_inliers=false, bool adaptive_trial_count=false, double confidence=0.99, double inlierprob=0.5, int min_nr_inliers=s, bool random_shuffle_inputs=false)
Definition i_ransac.h:46
void IScale4(T x[4], T sf)
Definition i_blas.h:1874
bool ConvertGround4ToGround3(const float &baseline, const std::vector< float > &k_mat, const std::vector< float > &ground4, std::vector< float > *ground3)
void GetGroundPlanePitchHeight(const float &baseline, const std::vector< float > &k_mat, const std::vector< float > &ground3, float *pitch, float *cam_height)
void ConvertGround3ToGround4(const float &baseline, const std::vector< float > &k_mat, const std::vector< float > &ground3, std::vector< float > *ground4)
void GetGround3FromPitchHeight(const std::vector< float > &k_mat, const float &baseline, const float &pitch, const float &cam_height, std::vector< float > *ground3)
class register implement
Definition arena_queue.h:37