78 #define MAX_CONTOUR_APPROX 7 83 CImage& thresh_img,
const int dilations, IplConvKernel* kernel_cross,
84 IplConvKernel* kernel_rect, IplConvKernel* kernel_diag1,
85 IplConvKernel* kernel_diag2, IplConvKernel* kernel_horz,
86 IplConvKernel* kernel_vert)
97 cvDilate(ipl, ipl, kernel_cross, 1);
101 cvErode(ipl, ipl, kernel_rect, 1);
104 cvDilate(ipl, ipl, kernel_vert, 1);
107 cvDilate(ipl, ipl, kernel_vert, 1);
110 cvDilate(ipl, ipl, kernel_vert, 1);
113 cvDilate(ipl, ipl, kernel_vert, 1);
116 cvDilate(ipl, ipl, kernel_vert, 1);
120 cvDilate(ipl, ipl, kernel_cross, 1);
123 cvErode(ipl, ipl, kernel_rect, 1);
126 cvDilate(ipl, ipl, kernel_horz, 1);
129 cvDilate(ipl, ipl, kernel_horz, 1);
132 cvDilate(ipl, ipl, kernel_horz, 1);
135 cvDilate(ipl, ipl, kernel_horz, 1);
138 cvDilate(ipl, ipl, kernel_horz, 1);
142 cvDilate(ipl, ipl, kernel_diag2, 1);
145 cvDilate(ipl, ipl, kernel_diag1, 1);
148 cvDilate(ipl, ipl, kernel_diag2, 1);
151 cvDilate(ipl, ipl, kernel_diag1, 1);
154 cvDilate(ipl, ipl, kernel_diag2, 1);
157 cvDilate(ipl, ipl, kernel_diag1, 1);
161 cvDilate(ipl, ipl, kernel_diag2, 1);
164 cvDilate(ipl, ipl, kernel_diag2, 1);
167 cvDilate(ipl, ipl, kernel_diag2, 1);
170 cvDilate(ipl, ipl, kernel_diag2, 1);
174 cvDilate(ipl, ipl, kernel_diag1, 1);
177 cvDilate(ipl, ipl, kernel_diag1, 1);
180 cvDilate(ipl, ipl, kernel_diag1, 1);
183 cvDilate(ipl, ipl, kernel_diag1, 1);
187 cvDilate(ipl, ipl, kernel_cross, 1);
190 cvErode(ipl, ipl, kernel_rect, 1);
193 cvDilate(ipl, ipl, kernel_cross, 1);
196 cvDilate(ipl, ipl, kernel_diag2, 1);
200 cvDilate(ipl, ipl, kernel_diag1, 1);
203 cvDilate(ipl, ipl, kernel_rect, 1);
206 cvErode(ipl, ipl, kernel_cross, 1);
209 cvDilate(ipl, ipl, kernel_rect, 1);
212 cvDilate(ipl, ipl, kernel_cross, 1);
226 const CImage& img_, CvSize pattern_size,
227 std::vector<CvPoint2D32f>& out_corners)
233 size_t max_count = 0;
234 int max_dilation_run_ID = -1;
239 vector<CvCBQuad::Ptr> quads;
240 vector<CvCBQuad::Ptr> quad_group;
241 vector<CvCBCorner::Ptr> corners;
242 vector<CvCBQuad::Ptr>
249 int quad_count, group_idx;
251 if (pattern_size.width < 2 || pattern_size.height < 2)
253 std::cerr <<
"Pattern should have at least 2x2 size" << endl;
256 if (pattern_size.width > 127 || pattern_size.height > 127)
258 std::cerr <<
"Pattern should not have a size larger than 127 x 127" 274 IplConvKernel* kernel_cross =
275 cvCreateStructuringElementEx(3, 3, 1, 1, CV_SHAPE_CROSS,
nullptr);
276 IplConvKernel* kernel_rect =
277 cvCreateStructuringElementEx(3, 3, 1, 1, CV_SHAPE_RECT,
nullptr);
279 static int kernel_diag1_vals[9] = {1, 0, 0, 0, 1, 0, 0, 0, 1};
280 IplConvKernel* kernel_diag1 = cvCreateStructuringElementEx(
281 3, 3, 1, 1, CV_SHAPE_CUSTOM, kernel_diag1_vals);
282 static int kernel_diag2_vals[9] = {0, 0, 1, 0, 1, 0, 1, 0, 0};
283 IplConvKernel* kernel_diag2 = cvCreateStructuringElementEx(
284 3, 3, 1, 1, CV_SHAPE_CUSTOM, kernel_diag2_vals);
285 static int kernel_horz_vals[9] = {0, 0, 0, 1, 1, 1, 0, 0, 0};
286 IplConvKernel* kernel_horz = cvCreateStructuringElementEx(
287 3, 3, 1, 1, CV_SHAPE_CUSTOM, kernel_horz_vals);
288 static int kernel_vert_vals[9] = {0, 1, 0, 0, 1, 0, 0, 1, 0};
289 IplConvKernel* kernel_vert = cvCreateStructuringElementEx(
290 3, 3, 1, 1, CV_SHAPE_CUSTOM, kernel_vert_vals);
298 cv::adaptiveThreshold(
301 CV_ADAPTIVE_THRESH_GAUSSIAN_C, CV_THRESH_BINARY, block_size, 0);
303 thresh_img_save = thresh_img.makeDeepCopy();
314 bool last_dilation =
false;
316 for (
int dilations = 0; !last_dilation; dilations++)
320 thresh_img = thresh_img_save.makeDeepCopy();
324 thresh_img, dilations, kernel_cross, kernel_rect, kernel_diag1,
325 kernel_diag2, kernel_horz, kernel_vert);
332 thresh_img.asCvMatRef(), cv::Point(0, 0),
333 cv::Point(thresh_img.getWidth() - 1, thresh_img.getHeight() - 1),
334 CV_RGB(255, 255, 255), 3, 8);
339 quads, corners, thresh_img, flags, dilations,
true);
340 if (quad_count <= 0)
continue;
355 for (group_idx = 0;; group_idx++)
359 if (quad_group.empty())
break;
362 size_t count = quad_group.size();
368 if (count > max_count)
375 max_dilation_run_ID = dilations;
398 output_quad_group = quad_group;
410 found =
myQuads2Points(output_quad_group, pattern_size, out_corners);
412 if (found != -1 && found != 1)
424 last_dilation =
false;
425 for (
int dilations = 0; !last_dilation; dilations++)
432 thresh_img = thresh_img_save.makeDeepCopy();
436 thresh_img, dilations, kernel_cross, kernel_rect, kernel_diag1,
437 kernel_diag2, kernel_horz, kernel_vert);
440 thresh_img.asCvMatRef(), cv::Point(0, 0),
442 thresh_img.getWidth() - 1, thresh_img.getHeight() - 1),
443 CV_RGB(255, 255, 255), 3, 8);
446 quads, corners, thresh_img, flags, dilations,
false);
447 if (quad_count <= 0)
continue;
462 while (feedBack == -1)
465 quads, dilations, output_quad_group, max_dilation_run_ID);
471 max_count = max_count + 1;
477 output_quad_group, pattern_size, out_corners);
480 if (found == -1 || found == 1)
496 cvReleaseStructuringElement(&kernel_cross);
497 cvReleaseStructuringElement(&kernel_rect);
498 cvReleaseStructuringElement(&kernel_diag1);
499 cvReleaseStructuringElement(&kernel_diag2);
500 cvReleaseStructuringElement(&kernel_horz);
501 cvReleaseStructuringElement(&kernel_vert);
512 std::cerr <<
"While linking the corners a problem was encountered. No " 513 "corner sequence is returned. " 527 double x0,
double y0,
double x1,
double y1,
double x2,
double y2)
529 return std::abs(0.5 * (x0 * (y1 - y2) + x1 * (y2 - y0) + x2 * (y0 - y1)));
532 double median(
const std::vector<double>& vec)
534 std::vector<double> v = vec;
535 const size_t n = v.size() / 2;
536 nth_element(v.begin(), v.begin() + n, v.end());
546 std::vector<CvCBQuad::Ptr>& quad_group,
const CvSize& pattern_size)
548 cv::MemStorage temp_storage;
550 CvPoint2D32f center = cvPoint2D32f(0, 0);
553 const size_t expected_quads_count =
554 ((pattern_size.width + 1) * (pattern_size.height + 1) + 1) / 2;
559 const size_t nQuads = quad_group.size();
560 if (nQuads <= expected_quads_count)
return;
563 vector<CvPoint2D32f> centers(nQuads);
564 temp_storage = cv::MemStorage(cvCreateMemStorage(0));
569 std::vector<double> quad_areas(nQuads);
570 double min_area = DBL_MAX, max_area = -DBL_MAX, mean_area = 0.0;
572 for (
size_t i = 0; i < nQuads; i++)
574 CvPoint2D32f ci = cvPoint2D32f(0, 0);
577 for (
size_t j = 0; j < 4; j++)
579 CvPoint2D32f pt = q->corners[j]->pt;
590 q->corners[0]->pt.x, q->corners[0]->pt.y, q->corners[1]->pt.x,
591 q->corners[1]->pt.y, q->corners[2]->pt.x, q->corners[2]->pt.y) +
593 q->corners[0]->pt.x, q->corners[0]->pt.y, q->corners[2]->pt.x,
594 q->corners[2]->pt.y, q->corners[3]->pt.x, q->corners[3]->pt.y);
599 if (a < min_area) min_area = a;
600 if (a > max_area) max_area = a;
611 const double median_area =
median(quad_areas);
614 for (
size_t i = 0; i < nQuads; i++)
616 quad_group[i]->area_ratio = quad_group[i]->area / median_area;
631 while (quad_group.size() > expected_quads_count)
633 double min_box_area = DBL_MAX;
634 int min_box_area_index = -1;
637 int most_outlier_idx = -1;
638 double most_outlier_ratio = 1.0;
639 for (
size_t skip = 0; skip < quad_group.size(); skip++)
641 double ar = quad_group[skip]->area_ratio;
642 if (ar > 1.0) ar = 1.0 / ar;
644 if (ar < most_outlier_ratio)
646 most_outlier_ratio = ar;
647 most_outlier_idx = skip;
651 if (most_outlier_idx >= 0)
653 min_box_area_index = most_outlier_idx;
656 if (min_box_area_index == -1)
659 for (
size_t skip = 0; skip < quad_group.size(); skip++)
662 CvPoint2D32f temp = centers[skip];
663 centers[skip] = center;
665 cvMat(1, quad_group.size(), CV_32FC2, ¢ers[0]);
667 cvConvexHull2(&pointMat, temp_storage, CV_CLOCKWISE, 1);
668 centers[skip] = temp;
669 double hull_area = fabs(cvContourArea(hull, CV_WHOLE_SEQ));
672 if (hull_area < min_box_area)
674 min_box_area = hull_area;
675 min_box_area_index = skip;
677 cvClearMemStorage(temp_storage);
684 for (
size_t i = 0; i < quad_group.size(); i++)
688 for (
size_t j = 0; j < 4; j++)
690 if (q->neighbors[j] == q0)
692 q->neighbors[j].reset();
694 for (
size_t k = 0; k < 4; k++)
695 if (q0->neighbors[k] == q)
697 q0->neighbors[k].reset();
707 quad_group.erase(quad_group.begin() + min_box_area_index);
708 centers.erase(centers.begin() + min_box_area_index);
716 std::vector<CvCBQuad::Ptr>& quad, std::vector<CvCBQuad::Ptr>& out_group,
717 const int group_idx, [[maybe_unused]]
const int dilation)
722 const size_t quad_count = quad.size();
725 for (
size_t i = 0; i < quad_count; i++)
727 if (quad[i]->count < 0 || quad[i]->group_idx >= 0)
continue;
733 std::stack<CvCBQuad::Ptr> seqStack;
737 q->group_idx = group_idx;
738 out_group.push_back(q);
740 while (!seqStack.empty())
745 for (
size_t k = 0; k < 4; k++)
751 if (neighbor && neighbor->count > 0 && neighbor->group_idx < 0)
753 neighbor->group_idx = group_idx;
754 seqStack.push(neighbor);
769 std::vector<CvCBQuad::Ptr>& quad_group,
const CvSize& pattern_size,
772 const size_t count = quad_group.size();
775 if (firstRun ==
true)
781 for (
size_t i = 0; i < count; i++)
784 if (q->
count > max_number)
786 max_number = q->
count;
789 if (max_number == 4)
break;
796 CvCBQuad* q = quad_group[max_id].get();
810 bool flag_changed =
true;
811 while (flag_changed ==
true)
814 flag_changed =
false;
818 for (
int i =
int(count - 1); i >= 0; i--)
821 if ((quad_group[i])->labeled ==
false)
825 for (
size_t j = 0; j < 4; j++)
829 if ((quad_group[i])->neighbors[j])
832 quad_group[i]->neighbors[j];
835 if (quadNeighborJ->labeled ==
true)
841 int connectedNeighborCornerId = -1;
842 for (
int k = 0; k < 4; k++)
844 if (quadNeighborJ->neighbors[k] ==
847 connectedNeighborCornerId = k;
860 ->corners[connectedNeighborCornerId];
862 quadNeighborJ->corners
863 [(connectedNeighborCornerId + 1) % 4];
865 quadNeighborJ->corners
866 [(connectedNeighborCornerId + 2) % 4];
868 quadNeighborJ->corners
869 [(connectedNeighborCornerId + 3) % 4];
871 (quad_group[i])->corners[j]->row = conCorner->row;
872 (quad_group[i])->corners[j]->column =
874 (quad_group[i])->corners[(j + 1) % 4]->row =
875 conCorner->row - conCornerCW2->row +
877 (quad_group[i])->corners[(j + 1) % 4]->column =
878 conCorner->column - conCornerCW2->column +
879 conCornerCW3->column;
880 (quad_group[i])->corners[(j + 2) % 4]->row =
881 conCorner->row + conCorner->row -
883 (quad_group[i])->corners[(j + 2) % 4]->column =
884 conCorner->column + conCorner->column -
885 conCornerCW2->column;
886 (quad_group[i])->corners[(j + 3) % 4]->row =
887 conCorner->row - conCornerCW2->row +
889 (quad_group[i])->corners[(j + 3) % 4]->column =
890 conCorner->column - conCornerCW2->column +
891 conCornerCW1->column;
894 (quad_group[i])->labeled =
true;
916 int min_column = 127;
917 int max_column = -127;
919 for (
size_t i = 0; i < count; i++)
923 for (
size_t j = 0; j < 4; j++)
925 if ((q->corners[j])->row > max_row) max_row = (q->corners[j])->row;
927 if ((q->corners[j])->row < min_row) min_row = (q->corners[j])->row;
929 if ((q->corners[j])->column > max_column)
930 max_column = (q->corners[j])->column;
932 if ((q->corners[j])->column < min_column)
933 min_column = (q->corners[j])->column;
940 for (
int i = min_row; i <= max_row; i++)
942 for (
int j = min_column; j <= max_column; j++)
952 for (
size_t k = 0; k < count; k++)
954 for (
size_t l = 0; l < 4; l++)
956 if (((quad_group[k])->corners[l]->row == i) &&
957 ((quad_group[k])->corners[l]->column == j))
962 (quad_group[k])->corners[l]->needsNeighbor =
false;
965 ->needsNeighbor =
false;
971 (quad_group[k])->corners[l]->needsNeighbor =
true;
988 for (
int i = min_row; i <= max_row; i++)
990 for (
int j = min_column; j <= max_column; j++)
1003 for (
size_t k = 0; k < count; k++)
1005 for (
size_t l = 0; l < 4; l++)
1007 if (((quad_group[k])->corners[l]->row == i) &&
1008 ((quad_group[k])->corners[l]->column == j))
1017 else if (number == 2)
1023 (quad_group[k])->corners[l]->pt.x -
1024 (quad_group[quadID])->corners[cornerID]->pt.x;
1026 (quad_group[k])->corners[l]->pt.y -
1027 (quad_group[quadID])->corners[cornerID]->pt.y;
1029 if (delta_x != 0 || delta_y != 0)
1032 (quad_group[k])->corners[l]->pt.x =
1033 (quad_group[k])->corners[l]->pt.x -
1035 (quad_group[quadID])->corners[cornerID]->pt.x =
1036 (quad_group[quadID])
1040 (quad_group[k])->corners[l]->pt.y =
1041 (quad_group[k])->corners[l]->pt.y -
1043 (quad_group[quadID])->corners[cornerID]->pt.y =
1044 (quad_group[quadID])
1050 else if (number > 2)
1058 number = number + 1;
1074 int largerDimPattern = max(pattern_size.height, pattern_size.width);
1075 int smallerDimPattern = min(pattern_size.height, pattern_size.width);
1076 bool flagSmallerDim1 =
false;
1077 bool flagSmallerDim2 =
false;
1079 if ((largerDimPattern + 1) == max_column - min_column)
1081 flagSmallerDim1 =
true;
1086 for (
size_t k = 0; k < count; k++)
1088 for (
size_t l = 0; l < 4; l++)
1090 if ((quad_group[k])->corners[l]->column == min_column ||
1091 (quad_group[k])->corners[l]->column == max_column)
1094 (quad_group[k])->corners[l]->needsNeighbor =
false;
1100 if ((largerDimPattern + 1) == max_row - min_row)
1102 flagSmallerDim2 =
true;
1107 for (
size_t k = 0; k < count; k++)
1109 for (
size_t l = 0; l < 4; l++)
1111 if ((quad_group[k])->corners[l]->row == min_row ||
1112 (quad_group[k])->corners[l]->row == max_row)
1115 (quad_group[k])->corners[l]->needsNeighbor =
false;
1133 if ((flagSmallerDim1 ==
false && flagSmallerDim2 ==
true))
1138 if ((smallerDimPattern + 1) == max_column - min_column)
1140 for (
size_t k = 0; k < count; k++)
1142 for (
int l = 0; l < 4; l++)
1144 if ((quad_group[k])->corners[l]->column == min_column ||
1145 (quad_group[k])->corners[l]->column == max_column)
1148 (quad_group[k])->corners[l]->needsNeighbor =
false;
1155 if ((flagSmallerDim1 ==
true && flagSmallerDim2 ==
false))
1160 if ((smallerDimPattern + 1) == max_row - min_row)
1162 for (
size_t k = 0; k < count; k++)
1164 for (
size_t l = 0; l < 4; l++)
1166 if ((quad_group[k])->corners[l]->row == min_row ||
1167 (quad_group[k])->corners[l]->row == max_row)
1170 (quad_group[k])->corners[l]->needsNeighbor =
false;
1177 if ((flagSmallerDim1 ==
false && flagSmallerDim2 ==
false) &&
1178 smallerDimPattern + 1 < max_column - min_column)
1183 if ((smallerDimPattern + 1) == max_row - min_row)
1185 for (
size_t k = 0; k < count; k++)
1187 for (
size_t l = 0; l < 4; l++)
1189 if ((quad_group[k])->corners[l]->row == min_row ||
1190 (quad_group[k])->corners[l]->row == max_row)
1193 (quad_group[k])->corners[l]->needsNeighbor =
false;
1200 if ((flagSmallerDim1 ==
false && flagSmallerDim2 ==
false) &&
1201 smallerDimPattern + 1 < max_row - min_row)
1206 if ((smallerDimPattern + 1) == max_column - min_column)
1208 for (
size_t k = 0; k < count; k++)
1210 for (
size_t l = 0; l < 4; l++)
1212 if ((quad_group[k])->corners[l]->column == min_column ||
1213 (quad_group[k])->corners[l]->column == max_column)
1216 (quad_group[k])->corners[l]->needsNeighbor =
false;
1236 const float thresh_dilation = (float)(2 * dilation + 3) *
1237 (2 * dilation + 3) *
1242 const size_t quad_count = quads.size();
1245 for (
size_t idx = 0; idx < quad_count; idx++)
1251 for (
size_t i = 0; i < 4; i++)
1254 float min_dist = FLT_MAX;
1255 int closest_corner_idx = -1;
1258 if (cur_quad->neighbors[i])
continue;
1260 pt = cur_quad->corners[i]->pt;
1263 for (
size_t k = 0; k < quad_count; k++)
1265 if (k == idx)
continue;
1267 for (
size_t j = 0; j < 4; j++)
1270 if (quads[k]->neighbors[j])
continue;
1272 dx = pt.x - quads[k]->corners[j]->pt.x;
1273 dy = pt.y - quads[k]->corners[j]->pt.y;
1274 dist = dx * dx + dy * dy;
1279 if (dist < min_dist &&
1280 dist <= (cur_quad->edge_len + thresh_dilation) &&
1281 dist <= (quads[k]->edge_len + thresh_dilation))
1286 float x1 = (cur_quad->corners[i]->pt.x +
1287 cur_quad->corners[(i + 1) % 4]->pt.x) /
1289 float y1 = (cur_quad->corners[i]->pt.y +
1290 cur_quad->corners[(i + 1) % 4]->pt.y) /
1292 float x2 = (cur_quad->corners[(i + 2) % 4]->pt.x +
1293 cur_quad->corners[(i + 3) % 4]->pt.x) /
1295 float y2 = (cur_quad->corners[(i + 2) % 4]->pt.y +
1296 cur_quad->corners[(i + 3) % 4]->pt.y) /
1299 float x3 = (cur_quad->corners[i]->pt.x +
1300 cur_quad->corners[(i + 3) % 4]->pt.x) /
1302 float y3 = (cur_quad->corners[i]->pt.y +
1303 cur_quad->corners[(i + 3) % 4]->pt.y) /
1305 float x4 = (cur_quad->corners[(i + 1) % 4]->pt.x +
1306 cur_quad->corners[(i + 2) % 4]->pt.x) /
1308 float y4 = (cur_quad->corners[(i + 1) % 4]->pt.y +
1309 cur_quad->corners[(i + 2) % 4]->pt.y) /
1320 float c11 = cur_quad->corners[i]->pt.x - x2;
1321 float d11 = cur_quad->corners[i]->pt.y - y2;
1323 float c12 = quads[k]->corners[j]->pt.x - x2;
1324 float d12 = quads[k]->corners[j]->pt.y - y2;
1325 float sign11 =
a1 * d11 - c11 *
b1;
1326 float sign12 =
a1 * d12 - c12 *
b1;
1331 float c21 = cur_quad->corners[i]->pt.x - x4;
1332 float d21 = cur_quad->corners[i]->pt.y - y4;
1334 float c22 = quads[k]->corners[j]->pt.x - x4;
1335 float d22 = quads[k]->corners[j]->pt.y - y4;
1336 float sign21 =
a2 * d21 - c21 *
b2;
1337 float sign22 =
a2 * d22 - c22 *
b2;
1347 float c13 = quads[k]->corners[(j + 2) % 4]->pt.x - x2;
1348 float d13 = quads[k]->corners[(j + 2) % 4]->pt.y - y2;
1349 float c23 = quads[k]->corners[(j + 2) % 4]->pt.x - x4;
1350 float d23 = quads[k]->corners[(j + 2) % 4]->pt.y - y4;
1351 float sign13 =
a1 * d13 - c13 *
b1;
1352 float sign23 =
a2 * d23 - c23 *
b2;
1357 float u1 = (quads[k]->corners[j]->pt.x +
1358 quads[k]->corners[(j + 1) % 4]->pt.x) /
1360 float v1 = (quads[k]->corners[j]->pt.y +
1361 quads[k]->corners[(j + 1) % 4]->pt.y) /
1363 float u2 = (quads[k]->corners[(j + 2) % 4]->pt.x +
1364 quads[k]->corners[(j + 3) % 4]->pt.x) /
1366 float v2 = (quads[k]->corners[(j + 2) % 4]->pt.y +
1367 quads[k]->corners[(j + 3) % 4]->pt.y) /
1370 float u3 = (quads[k]->corners[j]->pt.x +
1371 quads[k]->corners[(j + 3) % 4]->pt.x) /
1373 float v3 = (quads[k]->corners[j]->pt.y +
1374 quads[k]->corners[(j + 3) % 4]->pt.y) /
1376 float u4 = (quads[k]->corners[(j + 1) % 4]->pt.x +
1377 quads[k]->corners[(j + 2) % 4]->pt.x) /
1379 float v4 = (quads[k]->corners[(j + 1) % 4]->pt.y +
1380 quads[k]->corners[(j + 2) % 4]->pt.y) /
1392 float c31 = cur_quad->corners[i]->pt.x - u2;
1393 float d31 = cur_quad->corners[i]->pt.y - v2;
1395 float c32 = quads[k]->corners[j]->pt.x - u2;
1396 float d32 = quads[k]->corners[j]->pt.y - v2;
1397 float sign31 =
a3 * d31 - c31 *
b3;
1398 float sign32 =
a3 * d32 - c32 *
b3;
1403 float c41 = cur_quad->corners[i]->pt.x - u4;
1404 float d41 = cur_quad->corners[i]->pt.y - v4;
1406 float c42 = quads[k]->corners[j]->pt.x - u4;
1407 float d42 = quads[k]->corners[j]->pt.y - v4;
1408 float sign41 = a4 * d41 - c41 * b4;
1409 float sign42 = a4 * d42 - c42 * b4;
1419 float c33 = cur_quad->corners[(i + 2) % 4]->pt.x - u2;
1420 float d33 = cur_quad->corners[(i + 2) % 4]->pt.y - v2;
1421 float c43 = cur_quad->corners[(i + 2) % 4]->pt.x - u4;
1422 float d43 = cur_quad->corners[(i + 2) % 4]->pt.y - v4;
1423 float sign33 =
a3 * d33 - c33 *
b3;
1424 float sign43 = a4 * d43 - c43 * b4;
1427 if (((sign11 < 0 && sign12 < 0) ||
1428 (sign11 > 0 && sign12 > 0)) &&
1429 ((sign21 < 0 && sign22 < 0) ||
1430 (sign21 > 0 && sign22 > 0)) &&
1431 ((sign31 < 0 && sign32 < 0) ||
1432 (sign31 > 0 && sign32 > 0)) &&
1433 ((sign41 < 0 && sign42 < 0) ||
1434 (sign41 > 0 && sign42 > 0)) &&
1435 ((sign11 < 0 && sign13 < 0) ||
1436 (sign11 > 0 && sign13 > 0)) &&
1437 ((sign21 < 0 && sign23 < 0) ||
1438 (sign21 > 0 && sign23 > 0)) &&
1439 ((sign31 < 0 && sign33 < 0) ||
1440 (sign31 > 0 && sign33 > 0)) &&
1441 ((sign41 < 0 && sign43 < 0) ||
1442 (sign41 > 0 && sign43 > 0)))
1445 closest_corner_idx = j;
1446 closest_quad = quads[k];
1454 if (closest_corner_idx >= 0 && min_dist < FLT_MAX)
1457 closest_quad->corners[closest_corner_idx];
1462 for (
size_t j = 0; !skip && j < 4; j++)
1463 skip = closest_quad->neighbors[j] == cur_quad;
1468 closest_corner->pt.x = (pt.x + closest_corner->pt.x) * 0.5f;
1469 closest_corner->pt.y = (pt.y + closest_corner->pt.y) * 0.5f;
1472 cur_quad->neighbors[i] = closest_quad;
1473 cur_quad->corners[i] = closest_corner;
1475 closest_quad->count++;
1476 closest_quad->neighbors[closest_corner_idx] = cur_quad;
1477 closest_quad->corners[closest_corner_idx] = closest_corner;
1491 std::vector<CvCBQuad::Ptr>& new_quads,
int new_dilation,
1492 std::vector<CvCBQuad::Ptr>& old_quads,
int old_dilation)
1502 const float thresh_dilation =
1503 (float)(2 * new_dilation + 3) * (2 * old_dilation + 3) * 2;
1507 for (
size_t idx = 0; idx < old_quads.size(); idx++)
1512 for (
int i = 0; i < 4; i++)
1515 float min_dist = FLT_MAX;
1516 int closest_corner_idx = -1;
1521 if (cur_quad->corners[i]->needsNeighbor ==
false)
continue;
1523 pt = cur_quad->corners[i]->pt;
1526 for (
size_t k = 0; k < new_quads.size(); k++)
1529 if (new_quads[k]->labeled ==
true)
continue;
1531 for (
int j = 0; j < 4; j++)
1535 dx = pt.x - new_quads[k]->corners[j]->pt.x;
1536 dy = pt.y - new_quads[k]->corners[j]->pt.y;
1537 dist = dx * dx + dy * dy;
1539 if ((dist < min_dist) &&
1540 dist <= (cur_quad->edge_len + thresh_dilation) &&
1541 dist <= (new_quads[k]->edge_len + thresh_dilation))
1546 float x1 = (cur_quad->corners[i]->pt.x +
1547 cur_quad->corners[(i + 1) % 4]->pt.x) /
1549 float y1 = (cur_quad->corners[i]->pt.y +
1550 cur_quad->corners[(i + 1) % 4]->pt.y) /
1552 float x2 = (cur_quad->corners[(i + 2) % 4]->pt.x +
1553 cur_quad->corners[(i + 3) % 4]->pt.x) /
1555 float y2 = (cur_quad->corners[(i + 2) % 4]->pt.y +
1556 cur_quad->corners[(i + 3) % 4]->pt.y) /
1559 float x3 = (cur_quad->corners[i]->pt.x +
1560 cur_quad->corners[(i + 3) % 4]->pt.x) /
1562 float y3 = (cur_quad->corners[i]->pt.y +
1563 cur_quad->corners[(i + 3) % 4]->pt.y) /
1565 float x4 = (cur_quad->corners[(i + 1) % 4]->pt.x +
1566 cur_quad->corners[(i + 2) % 4]->pt.x) /
1568 float y4 = (cur_quad->corners[(i + 1) % 4]->pt.y +
1569 cur_quad->corners[(i + 2) % 4]->pt.y) /
1580 float c11 = cur_quad->corners[i]->pt.x - x2;
1581 float d11 = cur_quad->corners[i]->pt.y - y2;
1583 float c12 = new_quads[k]->corners[j]->pt.x - x2;
1584 float d12 = new_quads[k]->corners[j]->pt.y - y2;
1585 float sign11 =
a1 * d11 - c11 *
b1;
1586 float sign12 =
a1 * d12 - c12 *
b1;
1591 float c21 = cur_quad->corners[i]->pt.x - x4;
1592 float d21 = cur_quad->corners[i]->pt.y - y4;
1594 float c22 = new_quads[k]->corners[j]->pt.x - x4;
1595 float d22 = new_quads[k]->corners[j]->pt.y - y4;
1596 float sign21 =
a2 * d21 - c21 *
b2;
1597 float sign22 =
a2 * d22 - c22 *
b2;
1608 new_quads[k]->corners[(j + 2) % 4]->pt.x - x2;
1610 new_quads[k]->corners[(j + 2) % 4]->pt.y - y2;
1612 new_quads[k]->corners[(j + 2) % 4]->pt.x - x4;
1614 new_quads[k]->corners[(j + 2) % 4]->pt.y - y4;
1615 float sign13 =
a1 * d13 - c13 *
b1;
1616 float sign23 =
a2 * d23 - c23 *
b2;
1621 float u1 = (new_quads[k]->corners[j]->pt.x +
1622 new_quads[k]->corners[(j + 1) % 4]->pt.x) /
1624 float v1 = (new_quads[k]->corners[j]->pt.y +
1625 new_quads[k]->corners[(j + 1) % 4]->pt.y) /
1627 float u2 = (new_quads[k]->corners[(j + 2) % 4]->pt.x +
1628 new_quads[k]->corners[(j + 3) % 4]->pt.x) /
1630 float v2 = (new_quads[k]->corners[(j + 2) % 4]->pt.y +
1631 new_quads[k]->corners[(j + 3) % 4]->pt.y) /
1634 float u3 = (new_quads[k]->corners[j]->pt.x +
1635 new_quads[k]->corners[(j + 3) % 4]->pt.x) /
1637 float v3 = (new_quads[k]->corners[j]->pt.y +
1638 new_quads[k]->corners[(j + 3) % 4]->pt.y) /
1640 float u4 = (new_quads[k]->corners[(j + 1) % 4]->pt.x +
1641 new_quads[k]->corners[(j + 2) % 4]->pt.x) /
1643 float v4 = (new_quads[k]->corners[(j + 1) % 4]->pt.y +
1644 new_quads[k]->corners[(j + 2) % 4]->pt.y) /
1655 float c31 = cur_quad->corners[i]->pt.x - u2;
1656 float d31 = cur_quad->corners[i]->pt.y - v2;
1658 float c32 = new_quads[k]->corners[j]->pt.x - u2;
1659 float d32 = new_quads[k]->corners[j]->pt.y - v2;
1660 float sign31 =
a3 * d31 - c31 *
b3;
1661 float sign32 =
a3 * d32 - c32 *
b3;
1666 float c41 = cur_quad->corners[i]->pt.x - u4;
1667 float d41 = cur_quad->corners[i]->pt.y - v4;
1669 float c42 = new_quads[k]->corners[j]->pt.x - u4;
1670 float d42 = new_quads[k]->corners[j]->pt.y - v4;
1671 float sign41 = a4 * d41 - c41 * b4;
1672 float sign42 = a4 * d42 - c42 * b4;
1682 float c33 = cur_quad->corners[(i + 2) % 4]->pt.x - u2;
1683 float d33 = cur_quad->corners[(i + 2) % 4]->pt.y - v2;
1684 float c43 = cur_quad->corners[(i + 2) % 4]->pt.x - u4;
1685 float d43 = cur_quad->corners[(i + 2) % 4]->pt.y - v4;
1686 float sign33 =
a3 * d33 - c33 *
b3;
1687 float sign43 = a4 * d43 - c43 * b4;
1695 float x5 = cur_quad->corners[i]->pt.x;
1696 float y5 = cur_quad->corners[i]->pt.y;
1697 float x6 = cur_quad->corners[(i + 1) % 4]->pt.x;
1698 float y6 = cur_quad->corners[(i + 1) % 4]->pt.y;
1702 float x8 = cur_quad->corners[(i + 3) % 4]->pt.x;
1703 float y8 = cur_quad->corners[(i + 3) % 4]->pt.y;
1714 float c51 = cur_quad->corners[(i + 2) % 4]->pt.x - x5;
1715 float d51 = cur_quad->corners[(i + 2) % 4]->pt.y - y5;
1717 float c52 = new_quads[k]->corners[j]->pt.x - x5;
1718 float d52 = new_quads[k]->corners[j]->pt.y - y5;
1719 float sign51 = a5 * d51 - c51 * b5;
1720 float sign52 = a5 * d52 - c52 * b5;
1725 float c61 = cur_quad->corners[(i + 2) % 4]->pt.x - x7;
1726 float d61 = cur_quad->corners[(i + 2) % 4]->pt.y - y7;
1728 float c62 = new_quads[k]->corners[j]->pt.x - x7;
1729 float d62 = new_quads[k]->corners[j]->pt.y - y7;
1730 float sign61 = a6 * d61 - c61 * b6;
1731 float sign62 = a6 * d62 - c62 * b6;
1736 float u5 = new_quads[k]->corners[j]->pt.x;
1737 float v5 = new_quads[k]->corners[j]->pt.y;
1738 float u6 = new_quads[k]->corners[(j + 1) % 4]->pt.x;
1739 float v6 = new_quads[k]->corners[(j + 1) % 4]->pt.y;
1743 float u8 = new_quads[k]->corners[(j + 3) % 4]->pt.x;
1744 float v8 = new_quads[k]->corners[(j + 3) % 4]->pt.y;
1755 float c71 = cur_quad->corners[i]->pt.x - u5;
1756 float d71 = cur_quad->corners[i]->pt.y - v5;
1759 new_quads[k]->corners[(j + 2) % 4]->pt.x - u5;
1761 new_quads[k]->corners[(j + 2) % 4]->pt.y - v5;
1762 float sign71 = a7 * d71 - c71 * b7;
1763 float sign72 = a7 * d72 - c72 * b7;
1768 float c81 = cur_quad->corners[i]->pt.x - u7;
1769 float d81 = cur_quad->corners[i]->pt.y - v7;
1772 new_quads[k]->corners[(j + 2) % 4]->pt.x - u7;
1774 new_quads[k]->corners[(j + 2) % 4]->pt.y - v7;
1775 float sign81 = a8 * d81 - c81 * b8;
1776 float sign82 = a8 * d82 - c82 * b8;
1779 if (((sign11 < 0 && sign12 < 0) ||
1780 (sign11 > 0 && sign12 > 0)) &&
1781 ((sign21 < 0 && sign22 < 0) ||
1782 (sign21 > 0 && sign22 > 0)) &&
1783 ((sign31 < 0 && sign32 < 0) ||
1784 (sign31 > 0 && sign32 > 0)) &&
1785 ((sign41 < 0 && sign42 < 0) ||
1786 (sign41 > 0 && sign42 > 0)) &&
1787 ((sign11 < 0 && sign13 < 0) ||
1788 (sign11 > 0 && sign13 > 0)) &&
1789 ((sign21 < 0 && sign23 < 0) ||
1790 (sign21 > 0 && sign23 > 0)) &&
1791 ((sign31 < 0 && sign33 < 0) ||
1792 (sign31 > 0 && sign33 > 0)) &&
1793 ((sign41 < 0 && sign43 < 0) ||
1794 (sign41 > 0 && sign43 > 0)) &&
1795 ((sign51 < 0 && sign52 > 0) ||
1796 (sign51 > 0 && sign52 < 0)) &&
1797 ((sign61 < 0 && sign62 > 0) ||
1798 (sign61 > 0 && sign62 < 0)) &&
1799 ((sign71 < 0 && sign72 > 0) ||
1800 (sign71 > 0 && sign72 < 0)) &&
1801 ((sign81 < 0 && sign82 > 0) ||
1802 (sign81 > 0 && sign82 < 0)))
1804 closest_corner_idx = j;
1805 closest_quad = new_quads[k];
1813 if (closest_corner_idx >= 0 && min_dist < FLT_MAX)
1816 closest_quad->corners[closest_corner_idx];
1817 closest_corner->pt.x = (pt.x + closest_corner->pt.x) * 0.5f;
1818 closest_corner->pt.y = (pt.y + closest_corner->pt.y) * 0.5f;
1823 cur_quad->corners[i]->pt.x = closest_corner->pt.x;
1824 cur_quad->corners[i]->pt.y = closest_corner->pt.y;
1825 cur_quad->neighbors[i] = closest_quad;
1826 closest_quad->corners[closest_corner_idx]->pt.x =
1827 closest_corner->pt.x;
1828 closest_quad->corners[closest_corner_idx]->pt.y =
1829 closest_corner->pt.y;
1833 closest_quad->labeled =
true;
1839 newQuad->edge_len = closest_quad->edge_len;
1840 newQuad->group_idx =
1841 cur_quad->group_idx;
1842 newQuad->labeled =
false;
1846 newQuad->neighbors[closest_corner_idx] = cur_quad;
1847 newQuad->neighbors[(closest_corner_idx + 1) % 4]
1849 newQuad->neighbors[(closest_corner_idx + 2) % 4]
1851 newQuad->neighbors[(closest_corner_idx + 3) % 4]
1854 for (
int j = 0; j < 4; j++)
1857 newQuad->corners[j]->pt.x = closest_quad->corners[j]->pt.x;
1858 newQuad->corners[j]->pt.y = closest_quad->corners[j]->pt.y;
1861 old_quads.push_back(newQuad);
1862 cur_quad->neighbors[i] = newQuad;
1878 vector<CvCBQuad::Ptr>& out_quads, vector<CvCBCorner::Ptr>& out_corners,
1879 const CImage& image,
int flags, [[maybe_unused]]
int dilation,
1887 cv::MemStorage temp_storage = cv::MemStorage(cvCreateMemStorage(0));
1889 CvSeq* src_contour =
nullptr;
1892 CvContourScanner scanner;
1896 const int min_size =
1899 root = cvCreateSeq(0,
sizeof(CvSeq),
sizeof(CvSeq*), temp_storage);
1903 IplImage im_ipl = cvIplImage(im_mat);
1904 scanner = cvStartFindContours(
1905 &im_ipl, temp_storage,
sizeof(
CvContourEx), CV_RETR_CCOMP,
1906 CV_CHAIN_APPROX_SIMPLE);
1909 while ((src_contour = cvFindNextContour(scanner)) !=
nullptr)
1911 CvSeq* dst_contour =
nullptr;
1912 CvRect rect = ((CvContour*)src_contour)->rect;
1925 if (CV_IS_SEQ_HOLE(src_contour) && rect.width * rect.height >= min_size)
1927 int min_approx_level = 2, max_approx_level;
1928 if (firstRun ==
true)
1929 max_approx_level = 3;
1933 for (approx_level = min_approx_level;
1934 approx_level <= max_approx_level; approx_level++)
1936 dst_contour = cvApproxPoly(
1937 src_contour,
sizeof(CvContour), temp_storage,
1938 CV_POLY_APPROX_DP, (
float)approx_level);
1942 dst_contour = cvApproxPoly(
1943 dst_contour,
sizeof(CvContour), temp_storage,
1944 CV_POLY_APPROX_DP, (
float)approx_level);
1946 if (dst_contour->total == 4)
break;
1950 if (dst_contour->total == 4 && cvCheckContourConvexity(dst_contour))
1957 for (
int i = 0; i < 4; i++)
1958 pt[i] = *(CvPoint*)cvGetSeqElem(dst_contour, i);
1982 auto* parent = (
CvContourEx*)(src_contour->v_prev);
1984 if (!board || board->
counter < parent->counter)
1986 dst_contour->v_prev = (CvSeq*)parent;
1987 cvSeqPush(root, &dst_contour);
1994 cvEndFindContours(&scanner);
1998 for (
int q = 0; q < root->total; q++)
2001 out_corners.clear();
2002 for (
int q = 0; q < 4 * root->total; q++)
2006 for (
int idx = 0; idx < root->total; idx++)
2009 src_contour = *(CvSeq**)cvGetSeqElem(root, idx);
2010 if ((flags & cv::CALIB_CB_FILTER_QUADS) &&
2011 src_contour->v_prev != (CvSeq*)board)
2017 assert(src_contour->total == 4);
2018 for (
int i = 0; i < 4; i++)
2021 cvPointTo32f(*(CvPoint*)cvGetSeqElem(src_contour, i));
2023 [quad_count * 4 + i];
2028 q->corners[i] = corner;
2030 q->edge_len = FLT_MAX;
2031 for (
int i = 0; i < 4; i++)
2033 float dx = q->corners[i]->pt.x - q->corners[(i + 1) & 3]->pt.x;
2034 float dy = q->corners[i]->pt.y - q->corners[(i + 1) & 3]->pt.y;
2035 float d = dx * dx + dy * dy;
2036 if (q->edge_len > d) q->edge_len = d;
2041 if (cvGetErrStatus() < 0)
2045 out_corners.clear();
2056 const std::vector<CvCBQuad::Ptr>& output_quads,
const CvSize& pattern_size,
2057 std::vector<CvPoint2D32f>& out_corners)
2060 out_corners.clear();
2062 bool flagRow =
false;
2063 bool flagColumn =
false;
2064 int maxPattern_sizeRow = -1;
2065 int maxPattern_sizeColumn = -1;
2071 int min_column = 127;
2072 int max_column = -127;
2074 for (
size_t i = 0; i < output_quads.size(); i++)
2078 for (
int j = 0; j < 4; j++)
2080 if ((q->corners[j])->row > max_row) max_row = (q->corners[j])->row;
2081 if ((q->corners[j])->row < min_row) min_row = (q->corners[j])->row;
2082 if ((q->corners[j])->column > max_column)
2083 max_column = (q->corners[j])->column;
2084 if ((q->corners[j])->column < min_column)
2085 min_column = (q->corners[j])->column;
2093 for (
size_t i = 0; i < output_quads.size(); i++)
2097 for (
int j = 0; j < 4; j++)
2099 if ((q->corners[j])->column == max_column &&
2100 (q->corners[j])->row != min_row &&
2101 (q->corners[j])->row != max_row)
2103 if ((q->corners[j]->needsNeighbor) ==
false)
2110 if ((q->corners[j])->row == max_row &&
2111 (q->corners[j])->column != min_column &&
2112 (q->corners[j])->column != max_column)
2114 if ((q->corners[j]->needsNeighbor) ==
false)
2124 if (flagColumn ==
true)
2126 if (max_column - min_column == pattern_size.width + 1)
2128 maxPattern_sizeColumn = pattern_size.width;
2129 maxPattern_sizeRow = pattern_size.height;
2133 maxPattern_sizeColumn = pattern_size.height;
2134 maxPattern_sizeRow = pattern_size.width;
2137 else if (flagRow ==
true)
2139 if (max_row - min_row == pattern_size.width + 1)
2141 maxPattern_sizeRow = pattern_size.width;
2142 maxPattern_sizeColumn = pattern_size.height;
2146 maxPattern_sizeRow = pattern_size.height;
2147 maxPattern_sizeColumn = pattern_size.width;
2155 maxPattern_sizeColumn = max(pattern_size.width, pattern_size.height);
2156 maxPattern_sizeRow = max(pattern_size.width, pattern_size.height);
2160 if (maxPattern_sizeRow * maxPattern_sizeColumn !=
2161 pattern_size.width * pattern_size.height)
2165 bool do_swap_col_row = maxPattern_sizeRow != pattern_size.height;
2167 if (do_swap_col_row)
2169 std::swap(min_row, min_column);
2170 std::swap(maxPattern_sizeRow, maxPattern_sizeColumn);
2175 for (
int i = min_row + 1; i < maxPattern_sizeRow + min_row + 1; i++)
2177 for (
int j = min_column + 1; j < maxPattern_sizeColumn + min_column + 1;
2183 for (
size_t k = 0; k < output_quads.size(); k++)
2185 for (
int l = 0; l < 4; l++)
2187 int r = output_quads[k]->corners[l]->row;
2188 int c = output_quads[k]->corners[l]->column;
2189 if (do_swap_col_row) std::swap(r, c);
2191 if (r == i && c == j)
2200 out_corners.push_back(
2201 output_quads[k]->corners[l]->pt);
2211 if (iter > 2)
return -1;
2220 if (iter == 1 || iter == 2)
2231 return (out_corners.size() ==
2232 size_t(pattern_size.width) * size_t(pattern_size.height))
2241 std::map<CvCBQuad*, size_t> pointer2index;
2242 for (
size_t i = 0; i < quads.size(); i++) pointer2index[quads[i].
get()] = i;
2244 vector<std::array<size_t, 4>> neig_indices(quads.size());
2245 for (
size_t i = 0; i < quads.size(); i++)
2246 for (
size_t j = 0; j < 4; j++)
2247 neig_indices[i][j] = pointer2index[quads[i]->neighbors[j].
get()];
2249 std::vector<CvCBQuad::Ptr> new_quads = quads;
2250 std::for_each(new_quads.begin(), new_quads.end(), [](
CvCBQuad::Ptr& ptr) {
2253 for (
size_t i = 0; i < new_quads.size(); i++)
2254 for (
size_t j = 0; j < 4; j++)
2255 new_quads[i]->neighbors[j] = new_quads[neig_indices[i][j]];
2262 #endif // MRPT_HAS_OPENCV void mrFindQuadNeighbors2(std::vector< CvCBQuad::Ptr > &quads, int dilation)
std::shared_ptr< CvCBCorner > Ptr
Shallow copy: the copied object is a reference to the original one.
void mrLabelQuadGroup(std::vector< CvCBQuad::Ptr > &quad_group, const CvSize &pattern_size, bool firstRun)
void icvCleanFoundConnectedQuads(std::vector< CvCBQuad::Ptr > &quad_group, const CvSize &pattern_size)
size_t getHeight() const override
Returns the height of the image in pixels.
void asCvMat(cv::Mat &out_img, copy_type_t copy_type) const
Makes a shallow or deep copy of this image into the provided cv::Mat.
int mrAugmentBestRun(std::vector< CvCBQuad::Ptr > &new_quads, int new_dilation, std::vector< CvCBQuad::Ptr > &old_quads, int old_dilation)
This base provides a set of functions for maths stuff.
size_t getWidth() const override
Returns the width of the image in pixels.
double median(const std::vector< double > &vec)
bool do_special_dilation(CImage &thresh_img, const int dilations, IplConvKernel *kernel_cross, IplConvKernel *kernel_rect, IplConvKernel *kernel_diag1, IplConvKernel *kernel_diag2, IplConvKernel *kernel_horz, IplConvKernel *kernel_vert)
double triangleArea(double x0, double y0, double x1, double y1, double x2, double y2)
int myQuads2Points(const std::vector< CvCBQuad::Ptr > &output_quads, const CvSize &pattern_size, std::vector< CvPoint2D32f > &out_corners)
#define MAX_CONTOUR_APPROX
This is the global namespace for all Mobile Robot Programming Toolkit (MRPT) libraries.
struct _IplImage IplImage
std::shared_ptr< CvCBQuad > Ptr
CvCBCorner::Ptr corners[4]
void quadListMakeUnique(std::vector< CvCBQuad::Ptr > &quads)
void icvFindConnectedQuads(std::vector< CvCBQuad::Ptr > &quad, std::vector< CvCBQuad::Ptr > &out_group, const int group_idx, [[maybe_unused]] const int dilation)
int cvFindChessboardCorners3(const CImage &img_, CvSize pattern_size, std::vector< CvPoint2D32f > &out_corners)
A class for storing images as grayscale or RGB bitmaps.
int icvGenerateQuads(vector< CvCBQuad::Ptr > &out_quads, vector< CvCBCorner::Ptr > &out_corners, const CImage &image, int flags, [[maybe_unused]] int dilation, bool firstRun)