19 #include <mrpt/otherlibs/do_opencv_includes.h>
35 const CImage& img_, CvSize pattern_size,
36 std::vector<std::vector<CvPoint2D32f>>& out_corners)
46 const size_t expected_quads_count =
47 ((pattern_size.width + 1) * (pattern_size.height + 1) + 1) / 2;
55 vector<CvCBQuad::Ptr> quads;
56 vector<CvCBCorner::Ptr> corners;
57 list<vector<CvCBQuad::Ptr>>
60 if (pattern_size.width < 2 || pattern_size.height < 2)
62 std::cerr <<
"Pattern should have at least 2x2 size" << endl;
65 if (pattern_size.width > 127 || pattern_size.height > 127)
67 std::cerr <<
"Pattern should not have a size larger than 127 x 127"
73 IplConvKernel* kernel_cross =
74 cvCreateStructuringElementEx(3, 3, 1, 1, CV_SHAPE_CROSS,
nullptr);
75 IplConvKernel* kernel_rect =
76 cvCreateStructuringElementEx(3, 3, 1, 1, CV_SHAPE_RECT,
nullptr);
78 static int kernel_diag1_vals[9] = {1, 0, 0, 0, 1, 0, 0, 0, 1};
79 IplConvKernel* kernel_diag1 = cvCreateStructuringElementEx(
80 3, 3, 1, 1, CV_SHAPE_CUSTOM, kernel_diag1_vals);
81 static int kernel_diag2_vals[9] = {0, 0, 1, 0, 1, 0, 1, 0, 0};
82 IplConvKernel* kernel_diag2 = cvCreateStructuringElementEx(
83 3, 3, 1, 1, CV_SHAPE_CUSTOM, kernel_diag2_vals);
84 static int kernel_horz_vals[9] = {0, 0, 0, 1, 1, 1, 0, 0, 0};
85 IplConvKernel* kernel_horz = cvCreateStructuringElementEx(
86 3, 3, 1, 1, CV_SHAPE_CUSTOM, kernel_horz_vals);
87 static int kernel_vert_vals[9] = {0, 1, 0, 0, 1, 0, 0, 1, 0};
88 IplConvKernel* kernel_vert = cvCreateStructuringElementEx(
89 3, 3, 1, 1, CV_SHAPE_CUSTOM, kernel_vert_vals);
95 int block_size = cvRound(MIN(
img.getWidth(),
img.getHeight()) * 0.2) | 1;
98 img.getAs<IplImage>(), thresh_img.
getAs<IplImage>(), 255,
99 CV_ADAPTIVE_THRESH_GAUSSIAN_C, CV_THRESH_BINARY, block_size, 0);
101 cvCopy(thresh_img.
getAs<IplImage>(), thresh_img_save.
getAs<IplImage>());
112 bool last_dilation =
false;
114 for (
int dilations = 0; !last_dilation; dilations++)
118 cvCopy(thresh_img_save.
getAs<IplImage>(), thresh_img.
getAs<IplImage>());
122 thresh_img, dilations, kernel_cross, kernel_rect, kernel_diag1,
123 kernel_diag2, kernel_horz, kernel_vert);
130 thresh_img.
getAs<IplImage>(), cvPoint(0, 0),
132 CV_RGB(255, 255, 255), 3, 8);
137 quads, corners, thresh_img, flags, dilations,
true);
138 if (quad_count <= 0)
continue;
148 vector<CArrayDouble<2>, Eigen::aligned_allocator<CArrayDouble<2>>>
150 quad_centers.resize(quads.size());
151 for (
size_t i = 0; i < quads.size(); i++)
155 0.25 * (
q->corners[0]->pt.x +
q->corners[1]->pt.x +
156 q->corners[2]->pt.x +
q->corners[3]->pt.x);
158 0.25 * (
q->corners[0]->pt.y +
q->corners[1]->pt.y +
159 q->corners[2]->pt.y +
q->corners[3]->pt.y);
163 static const size_t MAX_NUM_CLUSTERS = 4;
164 for (
size_t nClusters = 1; nClusters < MAX_NUM_CLUSTERS; nClusters++)
166 vector<size_t> num_quads_by_cluster(nClusters);
168 vector<int> assignments;
170 vector<CArrayDouble<2>,
171 Eigen::aligned_allocator<CArrayDouble<2>>>,
172 vector<CArrayDouble<2>,
173 Eigen::aligned_allocator<CArrayDouble<2>>>>(
174 nClusters, quad_centers, assignments);
177 for (
size_t i = 0; i < nClusters; i++)
178 num_quads_by_cluster[i] =
179 std::count(assignments.begin(), assignments.end(), i);
186 "All quads (%u) | %u clusters",
187 (
unsigned)quad_centers.size(), (
unsigned)nClusters));
190 for (
size_t i = 0; i < quad_centers.size(); i++)
192 static const TColor colors[4] = {
196 quad_centers[i][0], quad_centers[i][1],
197 colors[assignments[i] % 4],
'+', 10);
206 for (
size_t i = 0; i < nClusters; i++)
208 if (num_quads_by_cluster[i] <
209 size_t(pattern_size.height * pattern_size.width))
213 vector<CvCBQuad::Ptr> ith_quads;
214 for (
size_t q = 0;
q < quads.size();
q++)
215 if (
size_t(assignments[
q]) == i)
216 ith_quads.push_back(quads[
q]);
233 for (
int group_idx = 0;; group_idx++)
235 vector<CvCBQuad::Ptr> quad_group;
238 ith_quads, quad_group, group_idx, dilations);
239 if (quad_group.empty())
break;
242 size_t count = quad_group.size();
244 if (
count == expected_quads_count)
251 "Candidate group #%i (%i)", (
int)group_idx,
252 (
int)quad_group.size()));
255 for (
size_t i = 0; i < quad_group.size(); i++)
257 static const TColor colors[4] = {
261 0.25 * (quad_group[i]->corners[0]->pt.x +
262 quad_group[i]->corners[1]->pt.x +
263 quad_group[i]->corners[2]->pt.x +
264 quad_group[i]->corners[3]->pt.x);
266 0.25 * (quad_group[i]->corners[0]->pt.y +
267 quad_group[i]->corners[1]->pt.y +
268 quad_group[i]->corners[2]->pt.y +
269 quad_group[i]->corners[3]->pt.y);
270 im.
cross(
x,
y, colors[group_idx % 4],
'+', 10);
284 good_quad_groups.push_back(quad_group);
303 good_quad_groups.begin();
304 it != good_quad_groups.end(); ++it)
308 for (
size_t i = 0; i < it->size(); i++)
312 (*it)[i]->corners[0]->pt.x +
313 (*it)[i]->corners[1]->pt.x + (*it)[i]->corners[2]->pt.x +
314 (*it)[i]->corners[3]->pt.x,
315 (*it)[i]->corners[0]->pt.y +
316 (*it)[i]->corners[1]->pt.y + (*it)[i]->corners[2]->pt.y +
317 (*it)[i]->corners[3]->pt.y);
322 double min_dist = std::numeric_limits<double>::max();
323 for (
size_t b = 0;
b < out_boards_centers.size();
b++)
328 if (out_corners.empty() || min_dist > 80)
330 vector<CvPoint2D32f> pts;
335 out_corners.push_back(pts);
337 out_boards_centers.push_back(boardCenter);
343 cvReleaseStructuringElement(&kernel_cross);
344 cvReleaseStructuringElement(&kernel_rect);
345 cvReleaseStructuringElement(&kernel_diag1);
346 cvReleaseStructuringElement(&kernel_diag2);
347 cvReleaseStructuringElement(&kernel_horz);
348 cvReleaseStructuringElement(&kernel_vert);
350 return !out_corners.empty();
353 #endif // MRPT_HAS_OPENCV