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,
const int dilation)
723 const size_t quad_count = quad.size();
726 for (
size_t i = 0; i < quad_count; i++)
728 if (quad[i]->count < 0 || quad[i]->group_idx >= 0)
continue;
734 std::stack<CvCBQuad::Ptr> seqStack;
738 q->group_idx = group_idx;
739 out_group.push_back(q);
741 while (!seqStack.empty())
746 for (
size_t k = 0; k < 4; k++)
752 if (neighbor && neighbor->count > 0 && neighbor->group_idx < 0)
754 neighbor->group_idx = group_idx;
755 seqStack.push(neighbor);
770 std::vector<CvCBQuad::Ptr>& quad_group,
const CvSize& pattern_size,
773 const size_t count = quad_group.size();
776 if (firstRun ==
true)
782 for (
size_t i = 0; i < count; i++)
785 if (q->
count > max_number)
787 max_number = q->
count;
790 if (max_number == 4)
break;
797 CvCBQuad* q = quad_group[max_id].get();
811 bool flag_changed =
true;
812 while (flag_changed ==
true)
815 flag_changed =
false;
819 for (
int i =
int(count - 1); i >= 0; i--)
822 if ((quad_group[i])->labeled ==
false)
826 for (
size_t j = 0; j < 4; j++)
830 if ((quad_group[i])->neighbors[j])
833 quad_group[i]->neighbors[j];
836 if (quadNeighborJ->labeled ==
true)
842 int connectedNeighborCornerId = -1;
843 for (
int k = 0; k < 4; k++)
845 if (quadNeighborJ->neighbors[k] ==
848 connectedNeighborCornerId = k;
861 ->corners[connectedNeighborCornerId];
863 quadNeighborJ->corners
864 [(connectedNeighborCornerId + 1) % 4];
866 quadNeighborJ->corners
867 [(connectedNeighborCornerId + 2) % 4];
869 quadNeighborJ->corners
870 [(connectedNeighborCornerId + 3) % 4];
872 (quad_group[i])->corners[j]->row = conCorner->row;
873 (quad_group[i])->corners[j]->column =
875 (quad_group[i])->corners[(j + 1) % 4]->row =
876 conCorner->row - conCornerCW2->row +
878 (quad_group[i])->corners[(j + 1) % 4]->column =
879 conCorner->column - conCornerCW2->column +
880 conCornerCW3->column;
881 (quad_group[i])->corners[(j + 2) % 4]->row =
882 conCorner->row + conCorner->row -
884 (quad_group[i])->corners[(j + 2) % 4]->column =
885 conCorner->column + conCorner->column -
886 conCornerCW2->column;
887 (quad_group[i])->corners[(j + 3) % 4]->row =
888 conCorner->row - conCornerCW2->row +
890 (quad_group[i])->corners[(j + 3) % 4]->column =
891 conCorner->column - conCornerCW2->column +
892 conCornerCW1->column;
895 (quad_group[i])->labeled =
true;
917 int min_column = 127;
918 int max_column = -127;
920 for (
size_t i = 0; i < count; i++)
924 for (
size_t j = 0; j < 4; j++)
926 if ((q->corners[j])->row > max_row) max_row = (q->corners[j])->row;
928 if ((q->corners[j])->row < min_row) min_row = (q->corners[j])->row;
930 if ((q->corners[j])->column > max_column)
931 max_column = (q->corners[j])->column;
933 if ((q->corners[j])->column < min_column)
934 min_column = (q->corners[j])->column;
941 for (
int i = min_row; i <= max_row; i++)
943 for (
int j = min_column; j <= max_column; j++)
953 for (
size_t k = 0; k < count; k++)
955 for (
size_t l = 0; l < 4; l++)
957 if (((quad_group[k])->corners[l]->row == i) &&
958 ((quad_group[k])->corners[l]->column == j))
963 (quad_group[k])->corners[l]->needsNeighbor =
false;
966 ->needsNeighbor =
false;
972 (quad_group[k])->corners[l]->needsNeighbor =
true;
989 for (
int i = min_row; i <= max_row; i++)
991 for (
int j = min_column; j <= max_column; j++)
1004 for (
size_t k = 0; k < count; k++)
1006 for (
size_t l = 0; l < 4; l++)
1008 if (((quad_group[k])->corners[l]->row == i) &&
1009 ((quad_group[k])->corners[l]->column == j))
1018 else if (number == 2)
1024 (quad_group[k])->corners[l]->pt.x -
1025 (quad_group[quadID])->corners[cornerID]->pt.x;
1027 (quad_group[k])->corners[l]->pt.y -
1028 (quad_group[quadID])->corners[cornerID]->pt.y;
1030 if (delta_x != 0 || delta_y != 0)
1033 (quad_group[k])->corners[l]->pt.x =
1034 (quad_group[k])->corners[l]->pt.x -
1036 (quad_group[quadID])->corners[cornerID]->pt.x =
1037 (quad_group[quadID])
1041 (quad_group[k])->corners[l]->pt.y =
1042 (quad_group[k])->corners[l]->pt.y -
1044 (quad_group[quadID])->corners[cornerID]->pt.y =
1045 (quad_group[quadID])
1051 else if (number > 2)
1059 number = number + 1;
1075 int largerDimPattern = max(pattern_size.height, pattern_size.width);
1076 int smallerDimPattern = min(pattern_size.height, pattern_size.width);
1077 bool flagSmallerDim1 =
false;
1078 bool flagSmallerDim2 =
false;
1080 if ((largerDimPattern + 1) == max_column - min_column)
1082 flagSmallerDim1 =
true;
1087 for (
size_t k = 0; k < count; k++)
1089 for (
size_t l = 0; l < 4; l++)
1091 if ((quad_group[k])->corners[l]->column == min_column ||
1092 (quad_group[k])->corners[l]->column == max_column)
1095 (quad_group[k])->corners[l]->needsNeighbor =
false;
1101 if ((largerDimPattern + 1) == max_row - min_row)
1103 flagSmallerDim2 =
true;
1108 for (
size_t k = 0; k < count; k++)
1110 for (
size_t l = 0; l < 4; l++)
1112 if ((quad_group[k])->corners[l]->row == min_row ||
1113 (quad_group[k])->corners[l]->row == max_row)
1116 (quad_group[k])->corners[l]->needsNeighbor =
false;
1134 if ((flagSmallerDim1 ==
false && flagSmallerDim2 ==
true))
1139 if ((smallerDimPattern + 1) == max_column - min_column)
1141 for (
size_t k = 0; k < count; k++)
1143 for (
int l = 0; l < 4; l++)
1145 if ((quad_group[k])->corners[l]->column == min_column ||
1146 (quad_group[k])->corners[l]->column == max_column)
1149 (quad_group[k])->corners[l]->needsNeighbor =
false;
1156 if ((flagSmallerDim1 ==
true && flagSmallerDim2 ==
false))
1161 if ((smallerDimPattern + 1) == max_row - min_row)
1163 for (
size_t k = 0; k < count; k++)
1165 for (
size_t l = 0; l < 4; l++)
1167 if ((quad_group[k])->corners[l]->row == min_row ||
1168 (quad_group[k])->corners[l]->row == max_row)
1171 (quad_group[k])->corners[l]->needsNeighbor =
false;
1178 if ((flagSmallerDim1 ==
false && flagSmallerDim2 ==
false) &&
1179 smallerDimPattern + 1 < max_column - min_column)
1184 if ((smallerDimPattern + 1) == max_row - min_row)
1186 for (
size_t k = 0; k < count; k++)
1188 for (
size_t l = 0; l < 4; l++)
1190 if ((quad_group[k])->corners[l]->row == min_row ||
1191 (quad_group[k])->corners[l]->row == max_row)
1194 (quad_group[k])->corners[l]->needsNeighbor =
false;
1201 if ((flagSmallerDim1 ==
false && flagSmallerDim2 ==
false) &&
1202 smallerDimPattern + 1 < max_row - min_row)
1207 if ((smallerDimPattern + 1) == max_column - min_column)
1209 for (
size_t k = 0; k < count; k++)
1211 for (
size_t l = 0; l < 4; l++)
1213 if ((quad_group[k])->corners[l]->column == min_column ||
1214 (quad_group[k])->corners[l]->column == max_column)
1217 (quad_group[k])->corners[l]->needsNeighbor =
false;
1237 const float thresh_dilation = (float)(2 * dilation + 3) *
1238 (2 * dilation + 3) *
1243 const size_t quad_count = quads.size();
1246 for (
size_t idx = 0; idx < quad_count; idx++)
1252 for (
size_t i = 0; i < 4; i++)
1255 float min_dist = FLT_MAX;
1256 int closest_corner_idx = -1;
1259 if (cur_quad->neighbors[i])
continue;
1261 pt = cur_quad->corners[i]->pt;
1264 for (
size_t k = 0; k < quad_count; k++)
1266 if (k == idx)
continue;
1268 for (
size_t j = 0; j < 4; j++)
1271 if (quads[k]->neighbors[j])
continue;
1273 dx = pt.x - quads[k]->corners[j]->pt.x;
1274 dy = pt.y - quads[k]->corners[j]->pt.y;
1275 dist = dx * dx + dy * dy;
1280 if (dist < min_dist &&
1281 dist <= (cur_quad->edge_len + thresh_dilation) &&
1282 dist <= (quads[k]->edge_len + thresh_dilation))
1287 float x1 = (cur_quad->corners[i]->pt.x +
1288 cur_quad->corners[(i + 1) % 4]->pt.x) /
1290 float y1 = (cur_quad->corners[i]->pt.y +
1291 cur_quad->corners[(i + 1) % 4]->pt.y) /
1293 float x2 = (cur_quad->corners[(i + 2) % 4]->pt.x +
1294 cur_quad->corners[(i + 3) % 4]->pt.x) /
1296 float y2 = (cur_quad->corners[(i + 2) % 4]->pt.y +
1297 cur_quad->corners[(i + 3) % 4]->pt.y) /
1300 float x3 = (cur_quad->corners[i]->pt.x +
1301 cur_quad->corners[(i + 3) % 4]->pt.x) /
1303 float y3 = (cur_quad->corners[i]->pt.y +
1304 cur_quad->corners[(i + 3) % 4]->pt.y) /
1306 float x4 = (cur_quad->corners[(i + 1) % 4]->pt.x +
1307 cur_quad->corners[(i + 2) % 4]->pt.x) /
1309 float y4 = (cur_quad->corners[(i + 1) % 4]->pt.y +
1310 cur_quad->corners[(i + 2) % 4]->pt.y) /
1321 float c11 = cur_quad->corners[i]->pt.x - x2;
1322 float d11 = cur_quad->corners[i]->pt.y - y2;
1324 float c12 = quads[k]->corners[j]->pt.x - x2;
1325 float d12 = quads[k]->corners[j]->pt.y - y2;
1326 float sign11 =
a1 * d11 - c11 *
b1;
1327 float sign12 =
a1 * d12 - c12 *
b1;
1332 float c21 = cur_quad->corners[i]->pt.x - x4;
1333 float d21 = cur_quad->corners[i]->pt.y - y4;
1335 float c22 = quads[k]->corners[j]->pt.x - x4;
1336 float d22 = quads[k]->corners[j]->pt.y - y4;
1337 float sign21 =
a2 * d21 - c21 *
b2;
1338 float sign22 =
a2 * d22 - c22 *
b2;
1348 float c13 = quads[k]->corners[(j + 2) % 4]->pt.x - x2;
1349 float d13 = quads[k]->corners[(j + 2) % 4]->pt.y - y2;
1350 float c23 = quads[k]->corners[(j + 2) % 4]->pt.x - x4;
1351 float d23 = quads[k]->corners[(j + 2) % 4]->pt.y - y4;
1352 float sign13 =
a1 * d13 - c13 *
b1;
1353 float sign23 =
a2 * d23 - c23 *
b2;
1358 float u1 = (quads[k]->corners[j]->pt.x +
1359 quads[k]->corners[(j + 1) % 4]->pt.x) /
1361 float v1 = (quads[k]->corners[j]->pt.y +
1362 quads[k]->corners[(j + 1) % 4]->pt.y) /
1364 float u2 = (quads[k]->corners[(j + 2) % 4]->pt.x +
1365 quads[k]->corners[(j + 3) % 4]->pt.x) /
1367 float v2 = (quads[k]->corners[(j + 2) % 4]->pt.y +
1368 quads[k]->corners[(j + 3) % 4]->pt.y) /
1371 float u3 = (quads[k]->corners[j]->pt.x +
1372 quads[k]->corners[(j + 3) % 4]->pt.x) /
1374 float v3 = (quads[k]->corners[j]->pt.y +
1375 quads[k]->corners[(j + 3) % 4]->pt.y) /
1377 float u4 = (quads[k]->corners[(j + 1) % 4]->pt.x +
1378 quads[k]->corners[(j + 2) % 4]->pt.x) /
1380 float v4 = (quads[k]->corners[(j + 1) % 4]->pt.y +
1381 quads[k]->corners[(j + 2) % 4]->pt.y) /
1393 float c31 = cur_quad->corners[i]->pt.x - u2;
1394 float d31 = cur_quad->corners[i]->pt.y - v2;
1396 float c32 = quads[k]->corners[j]->pt.x - u2;
1397 float d32 = quads[k]->corners[j]->pt.y - v2;
1398 float sign31 =
a3 * d31 - c31 *
b3;
1399 float sign32 =
a3 * d32 - c32 *
b3;
1404 float c41 = cur_quad->corners[i]->pt.x - u4;
1405 float d41 = cur_quad->corners[i]->pt.y - v4;
1407 float c42 = quads[k]->corners[j]->pt.x - u4;
1408 float d42 = quads[k]->corners[j]->pt.y - v4;
1409 float sign41 = a4 * d41 - c41 * b4;
1410 float sign42 = a4 * d42 - c42 * b4;
1420 float c33 = cur_quad->corners[(i + 2) % 4]->pt.x - u2;
1421 float d33 = cur_quad->corners[(i + 2) % 4]->pt.y - v2;
1422 float c43 = cur_quad->corners[(i + 2) % 4]->pt.x - u4;
1423 float d43 = cur_quad->corners[(i + 2) % 4]->pt.y - v4;
1424 float sign33 =
a3 * d33 - c33 *
b3;
1425 float sign43 = a4 * d43 - c43 * b4;
1428 if (((sign11 < 0 && sign12 < 0) ||
1429 (sign11 > 0 && sign12 > 0)) &&
1430 ((sign21 < 0 && sign22 < 0) ||
1431 (sign21 > 0 && sign22 > 0)) &&
1432 ((sign31 < 0 && sign32 < 0) ||
1433 (sign31 > 0 && sign32 > 0)) &&
1434 ((sign41 < 0 && sign42 < 0) ||
1435 (sign41 > 0 && sign42 > 0)) &&
1436 ((sign11 < 0 && sign13 < 0) ||
1437 (sign11 > 0 && sign13 > 0)) &&
1438 ((sign21 < 0 && sign23 < 0) ||
1439 (sign21 > 0 && sign23 > 0)) &&
1440 ((sign31 < 0 && sign33 < 0) ||
1441 (sign31 > 0 && sign33 > 0)) &&
1442 ((sign41 < 0 && sign43 < 0) ||
1443 (sign41 > 0 && sign43 > 0)))
1446 closest_corner_idx = j;
1447 closest_quad = quads[k];
1455 if (closest_corner_idx >= 0 && min_dist < FLT_MAX)
1458 closest_quad->corners[closest_corner_idx];
1463 for (
size_t j = 0; !skip && j < 4; j++)
1464 skip = closest_quad->neighbors[j] == cur_quad;
1469 closest_corner->pt.x = (pt.x + closest_corner->pt.x) * 0.5f;
1470 closest_corner->pt.y = (pt.y + closest_corner->pt.y) * 0.5f;
1473 cur_quad->neighbors[i] = closest_quad;
1474 cur_quad->corners[i] = closest_corner;
1476 closest_quad->count++;
1477 closest_quad->neighbors[closest_corner_idx] = cur_quad;
1478 closest_quad->corners[closest_corner_idx] = closest_corner;
1492 std::vector<CvCBQuad::Ptr>& new_quads,
int new_dilation,
1493 std::vector<CvCBQuad::Ptr>& old_quads,
int old_dilation)
1503 const float thresh_dilation =
1504 (float)(2 * new_dilation + 3) * (2 * old_dilation + 3) * 2;
1508 for (
size_t idx = 0; idx < old_quads.size(); idx++)
1513 for (
int i = 0; i < 4; i++)
1516 float min_dist = FLT_MAX;
1517 int closest_corner_idx = -1;
1522 if (cur_quad->corners[i]->needsNeighbor ==
false)
continue;
1524 pt = cur_quad->corners[i]->pt;
1527 for (
size_t k = 0; k < new_quads.size(); k++)
1530 if (new_quads[k]->labeled ==
true)
continue;
1532 for (
int j = 0; j < 4; j++)
1536 dx = pt.x - new_quads[k]->corners[j]->pt.x;
1537 dy = pt.y - new_quads[k]->corners[j]->pt.y;
1538 dist = dx * dx + dy * dy;
1540 if ((dist < min_dist) &&
1541 dist <= (cur_quad->edge_len + thresh_dilation) &&
1542 dist <= (new_quads[k]->edge_len + thresh_dilation))
1547 float x1 = (cur_quad->corners[i]->pt.x +
1548 cur_quad->corners[(i + 1) % 4]->pt.x) /
1550 float y1 = (cur_quad->corners[i]->pt.y +
1551 cur_quad->corners[(i + 1) % 4]->pt.y) /
1553 float x2 = (cur_quad->corners[(i + 2) % 4]->pt.x +
1554 cur_quad->corners[(i + 3) % 4]->pt.x) /
1556 float y2 = (cur_quad->corners[(i + 2) % 4]->pt.y +
1557 cur_quad->corners[(i + 3) % 4]->pt.y) /
1560 float x3 = (cur_quad->corners[i]->pt.x +
1561 cur_quad->corners[(i + 3) % 4]->pt.x) /
1563 float y3 = (cur_quad->corners[i]->pt.y +
1564 cur_quad->corners[(i + 3) % 4]->pt.y) /
1566 float x4 = (cur_quad->corners[(i + 1) % 4]->pt.x +
1567 cur_quad->corners[(i + 2) % 4]->pt.x) /
1569 float y4 = (cur_quad->corners[(i + 1) % 4]->pt.y +
1570 cur_quad->corners[(i + 2) % 4]->pt.y) /
1581 float c11 = cur_quad->corners[i]->pt.x - x2;
1582 float d11 = cur_quad->corners[i]->pt.y - y2;
1584 float c12 = new_quads[k]->corners[j]->pt.x - x2;
1585 float d12 = new_quads[k]->corners[j]->pt.y - y2;
1586 float sign11 =
a1 * d11 - c11 *
b1;
1587 float sign12 =
a1 * d12 - c12 *
b1;
1592 float c21 = cur_quad->corners[i]->pt.x - x4;
1593 float d21 = cur_quad->corners[i]->pt.y - y4;
1595 float c22 = new_quads[k]->corners[j]->pt.x - x4;
1596 float d22 = new_quads[k]->corners[j]->pt.y - y4;
1597 float sign21 =
a2 * d21 - c21 *
b2;
1598 float sign22 =
a2 * d22 - c22 *
b2;
1609 new_quads[k]->corners[(j + 2) % 4]->pt.x - x2;
1611 new_quads[k]->corners[(j + 2) % 4]->pt.y - y2;
1613 new_quads[k]->corners[(j + 2) % 4]->pt.x - x4;
1615 new_quads[k]->corners[(j + 2) % 4]->pt.y - y4;
1616 float sign13 =
a1 * d13 - c13 *
b1;
1617 float sign23 =
a2 * d23 - c23 *
b2;
1622 float u1 = (new_quads[k]->corners[j]->pt.x +
1623 new_quads[k]->corners[(j + 1) % 4]->pt.x) /
1625 float v1 = (new_quads[k]->corners[j]->pt.y +
1626 new_quads[k]->corners[(j + 1) % 4]->pt.y) /
1628 float u2 = (new_quads[k]->corners[(j + 2) % 4]->pt.x +
1629 new_quads[k]->corners[(j + 3) % 4]->pt.x) /
1631 float v2 = (new_quads[k]->corners[(j + 2) % 4]->pt.y +
1632 new_quads[k]->corners[(j + 3) % 4]->pt.y) /
1635 float u3 = (new_quads[k]->corners[j]->pt.x +
1636 new_quads[k]->corners[(j + 3) % 4]->pt.x) /
1638 float v3 = (new_quads[k]->corners[j]->pt.y +
1639 new_quads[k]->corners[(j + 3) % 4]->pt.y) /
1641 float u4 = (new_quads[k]->corners[(j + 1) % 4]->pt.x +
1642 new_quads[k]->corners[(j + 2) % 4]->pt.x) /
1644 float v4 = (new_quads[k]->corners[(j + 1) % 4]->pt.y +
1645 new_quads[k]->corners[(j + 2) % 4]->pt.y) /
1656 float c31 = cur_quad->corners[i]->pt.x - u2;
1657 float d31 = cur_quad->corners[i]->pt.y - v2;
1659 float c32 = new_quads[k]->corners[j]->pt.x - u2;
1660 float d32 = new_quads[k]->corners[j]->pt.y - v2;
1661 float sign31 =
a3 * d31 - c31 *
b3;
1662 float sign32 =
a3 * d32 - c32 *
b3;
1667 float c41 = cur_quad->corners[i]->pt.x - u4;
1668 float d41 = cur_quad->corners[i]->pt.y - v4;
1670 float c42 = new_quads[k]->corners[j]->pt.x - u4;
1671 float d42 = new_quads[k]->corners[j]->pt.y - v4;
1672 float sign41 = a4 * d41 - c41 * b4;
1673 float sign42 = a4 * d42 - c42 * b4;
1683 float c33 = cur_quad->corners[(i + 2) % 4]->pt.x - u2;
1684 float d33 = cur_quad->corners[(i + 2) % 4]->pt.y - v2;
1685 float c43 = cur_quad->corners[(i + 2) % 4]->pt.x - u4;
1686 float d43 = cur_quad->corners[(i + 2) % 4]->pt.y - v4;
1687 float sign33 =
a3 * d33 - c33 *
b3;
1688 float sign43 = a4 * d43 - c43 * b4;
1696 float x5 = cur_quad->corners[i]->pt.x;
1697 float y5 = cur_quad->corners[i]->pt.y;
1698 float x6 = cur_quad->corners[(i + 1) % 4]->pt.x;
1699 float y6 = cur_quad->corners[(i + 1) % 4]->pt.y;
1703 float x8 = cur_quad->corners[(i + 3) % 4]->pt.x;
1704 float y8 = cur_quad->corners[(i + 3) % 4]->pt.y;
1715 float c51 = cur_quad->corners[(i + 2) % 4]->pt.x - x5;
1716 float d51 = cur_quad->corners[(i + 2) % 4]->pt.y - y5;
1718 float c52 = new_quads[k]->corners[j]->pt.x - x5;
1719 float d52 = new_quads[k]->corners[j]->pt.y - y5;
1720 float sign51 = a5 * d51 - c51 * b5;
1721 float sign52 = a5 * d52 - c52 * b5;
1726 float c61 = cur_quad->corners[(i + 2) % 4]->pt.x - x7;
1727 float d61 = cur_quad->corners[(i + 2) % 4]->pt.y - y7;
1729 float c62 = new_quads[k]->corners[j]->pt.x - x7;
1730 float d62 = new_quads[k]->corners[j]->pt.y - y7;
1731 float sign61 = a6 * d61 - c61 * b6;
1732 float sign62 = a6 * d62 - c62 * b6;
1737 float u5 = new_quads[k]->corners[j]->pt.x;
1738 float v5 = new_quads[k]->corners[j]->pt.y;
1739 float u6 = new_quads[k]->corners[(j + 1) % 4]->pt.x;
1740 float v6 = new_quads[k]->corners[(j + 1) % 4]->pt.y;
1744 float u8 = new_quads[k]->corners[(j + 3) % 4]->pt.x;
1745 float v8 = new_quads[k]->corners[(j + 3) % 4]->pt.y;
1756 float c71 = cur_quad->corners[i]->pt.x - u5;
1757 float d71 = cur_quad->corners[i]->pt.y - v5;
1760 new_quads[k]->corners[(j + 2) % 4]->pt.x - u5;
1762 new_quads[k]->corners[(j + 2) % 4]->pt.y - v5;
1763 float sign71 = a7 * d71 - c71 * b7;
1764 float sign72 = a7 * d72 - c72 * b7;
1769 float c81 = cur_quad->corners[i]->pt.x - u7;
1770 float d81 = cur_quad->corners[i]->pt.y - v7;
1773 new_quads[k]->corners[(j + 2) % 4]->pt.x - u7;
1775 new_quads[k]->corners[(j + 2) % 4]->pt.y - v7;
1776 float sign81 = a8 * d81 - c81 * b8;
1777 float sign82 = a8 * d82 - c82 * b8;
1780 if (((sign11 < 0 && sign12 < 0) ||
1781 (sign11 > 0 && sign12 > 0)) &&
1782 ((sign21 < 0 && sign22 < 0) ||
1783 (sign21 > 0 && sign22 > 0)) &&
1784 ((sign31 < 0 && sign32 < 0) ||
1785 (sign31 > 0 && sign32 > 0)) &&
1786 ((sign41 < 0 && sign42 < 0) ||
1787 (sign41 > 0 && sign42 > 0)) &&
1788 ((sign11 < 0 && sign13 < 0) ||
1789 (sign11 > 0 && sign13 > 0)) &&
1790 ((sign21 < 0 && sign23 < 0) ||
1791 (sign21 > 0 && sign23 > 0)) &&
1792 ((sign31 < 0 && sign33 < 0) ||
1793 (sign31 > 0 && sign33 > 0)) &&
1794 ((sign41 < 0 && sign43 < 0) ||
1795 (sign41 > 0 && sign43 > 0)) &&
1796 ((sign51 < 0 && sign52 > 0) ||
1797 (sign51 > 0 && sign52 < 0)) &&
1798 ((sign61 < 0 && sign62 > 0) ||
1799 (sign61 > 0 && sign62 < 0)) &&
1800 ((sign71 < 0 && sign72 > 0) ||
1801 (sign71 > 0 && sign72 < 0)) &&
1802 ((sign81 < 0 && sign82 > 0) ||
1803 (sign81 > 0 && sign82 < 0)))
1805 closest_corner_idx = j;
1806 closest_quad = new_quads[k];
1814 if (closest_corner_idx >= 0 && min_dist < FLT_MAX)
1817 closest_quad->corners[closest_corner_idx];
1818 closest_corner->pt.x = (pt.x + closest_corner->pt.x) * 0.5f;
1819 closest_corner->pt.y = (pt.y + closest_corner->pt.y) * 0.5f;
1824 cur_quad->corners[i]->pt.x = closest_corner->pt.x;
1825 cur_quad->corners[i]->pt.y = closest_corner->pt.y;
1826 cur_quad->neighbors[i] = closest_quad;
1827 closest_quad->corners[closest_corner_idx]->pt.x =
1828 closest_corner->pt.x;
1829 closest_quad->corners[closest_corner_idx]->pt.y =
1830 closest_corner->pt.y;
1834 closest_quad->labeled =
true;
1840 newQuad->edge_len = closest_quad->edge_len;
1841 newQuad->group_idx =
1842 cur_quad->group_idx;
1843 newQuad->labeled =
false;
1847 newQuad->neighbors[closest_corner_idx] = cur_quad;
1848 newQuad->neighbors[(closest_corner_idx + 1) % 4]
1850 newQuad->neighbors[(closest_corner_idx + 2) % 4]
1852 newQuad->neighbors[(closest_corner_idx + 3) % 4]
1855 for (
int j = 0; j < 4; j++)
1858 newQuad->corners[j]->pt.x = closest_quad->corners[j]->pt.x;
1859 newQuad->corners[j]->pt.y = closest_quad->corners[j]->pt.y;
1862 old_quads.push_back(newQuad);
1863 cur_quad->neighbors[i] = newQuad;
1879 vector<CvCBQuad::Ptr>& out_quads, vector<CvCBCorner::Ptr>& out_corners,
1880 const CImage& image,
int flags,
int dilation,
bool firstRun)
1888 cv::MemStorage temp_storage = cv::MemStorage(cvCreateMemStorage(0));
1890 CvSeq* src_contour =
nullptr;
1893 CvContourScanner scanner;
1897 const int min_size =
1900 root = cvCreateSeq(0,
sizeof(CvSeq),
sizeof(CvSeq*), temp_storage);
1904 IplImage im_ipl = cvIplImage(im_mat);
1905 scanner = cvStartFindContours(
1906 &im_ipl, temp_storage,
sizeof(
CvContourEx), CV_RETR_CCOMP,
1907 CV_CHAIN_APPROX_SIMPLE);
1910 while ((src_contour = cvFindNextContour(scanner)) !=
nullptr)
1912 CvSeq* dst_contour =
nullptr;
1913 CvRect rect = ((CvContour*)src_contour)->rect;
1926 if (CV_IS_SEQ_HOLE(src_contour) && rect.width * rect.height >= min_size)
1928 int min_approx_level = 2, max_approx_level;
1929 if (firstRun ==
true)
1930 max_approx_level = 3;
1934 for (approx_level = min_approx_level;
1935 approx_level <= max_approx_level; approx_level++)
1937 dst_contour = cvApproxPoly(
1938 src_contour,
sizeof(CvContour), temp_storage,
1939 CV_POLY_APPROX_DP, (
float)approx_level);
1943 dst_contour = cvApproxPoly(
1944 dst_contour,
sizeof(CvContour), temp_storage,
1945 CV_POLY_APPROX_DP, (
float)approx_level);
1947 if (dst_contour->total == 4)
break;
1951 if (dst_contour->total == 4 && cvCheckContourConvexity(dst_contour))
1958 for (
int i = 0; i < 4; i++)
1959 pt[i] = *(CvPoint*)cvGetSeqElem(dst_contour, i);
1983 auto* parent = (
CvContourEx*)(src_contour->v_prev);
1985 if (!board || board->
counter < parent->counter)
1987 dst_contour->v_prev = (CvSeq*)parent;
1988 cvSeqPush(root, &dst_contour);
1995 cvEndFindContours(&scanner);
1999 for (
int q = 0; q < root->total; q++)
2002 out_corners.clear();
2003 for (
int q = 0; q < 4 * root->total; q++)
2007 for (
int idx = 0; idx < root->total; idx++)
2010 src_contour = *(CvSeq**)cvGetSeqElem(root, idx);
2011 if ((flags & cv::CALIB_CB_FILTER_QUADS) &&
2012 src_contour->v_prev != (CvSeq*)board)
2018 assert(src_contour->total == 4);
2019 for (
int i = 0; i < 4; i++)
2022 cvPointTo32f(*(CvPoint*)cvGetSeqElem(src_contour, i));
2024 [quad_count * 4 + i];
2029 q->corners[i] = corner;
2031 q->edge_len = FLT_MAX;
2032 for (
int i = 0; i < 4; i++)
2034 float dx = q->corners[i]->pt.x - q->corners[(i + 1) & 3]->pt.x;
2035 float dy = q->corners[i]->pt.y - q->corners[(i + 1) & 3]->pt.y;
2036 float d = dx * dx + dy * dy;
2037 if (q->edge_len > d) q->edge_len = d;
2042 if (cvGetErrStatus() < 0)
2046 out_corners.clear();
2057 const std::vector<CvCBQuad::Ptr>& output_quads,
const CvSize& pattern_size,
2058 std::vector<CvPoint2D32f>& out_corners)
2061 out_corners.clear();
2063 bool flagRow =
false;
2064 bool flagColumn =
false;
2065 int maxPattern_sizeRow = -1;
2066 int maxPattern_sizeColumn = -1;
2072 int min_column = 127;
2073 int max_column = -127;
2075 for (
size_t i = 0; i < output_quads.size(); i++)
2079 for (
int j = 0; j < 4; j++)
2081 if ((q->corners[j])->row > max_row) max_row = (q->corners[j])->row;
2082 if ((q->corners[j])->row < min_row) min_row = (q->corners[j])->row;
2083 if ((q->corners[j])->column > max_column)
2084 max_column = (q->corners[j])->column;
2085 if ((q->corners[j])->column < min_column)
2086 min_column = (q->corners[j])->column;
2094 for (
size_t i = 0; i < output_quads.size(); i++)
2098 for (
int j = 0; j < 4; j++)
2100 if ((q->corners[j])->column == max_column &&
2101 (q->corners[j])->row != min_row &&
2102 (q->corners[j])->row != max_row)
2104 if ((q->corners[j]->needsNeighbor) ==
false)
2111 if ((q->corners[j])->row == max_row &&
2112 (q->corners[j])->column != min_column &&
2113 (q->corners[j])->column != max_column)
2115 if ((q->corners[j]->needsNeighbor) ==
false)
2125 if (flagColumn ==
true)
2127 if (max_column - min_column == pattern_size.width + 1)
2129 maxPattern_sizeColumn = pattern_size.width;
2130 maxPattern_sizeRow = pattern_size.height;
2134 maxPattern_sizeColumn = pattern_size.height;
2135 maxPattern_sizeRow = pattern_size.width;
2138 else if (flagRow ==
true)
2140 if (max_row - min_row == pattern_size.width + 1)
2142 maxPattern_sizeRow = pattern_size.width;
2143 maxPattern_sizeColumn = pattern_size.height;
2147 maxPattern_sizeRow = pattern_size.height;
2148 maxPattern_sizeColumn = pattern_size.width;
2156 maxPattern_sizeColumn = max(pattern_size.width, pattern_size.height);
2157 maxPattern_sizeRow = max(pattern_size.width, pattern_size.height);
2161 if (maxPattern_sizeRow * maxPattern_sizeColumn !=
2162 pattern_size.width * pattern_size.height)
2166 bool do_swap_col_row = maxPattern_sizeRow != pattern_size.height;
2168 if (do_swap_col_row)
2170 std::swap(min_row, min_column);
2171 std::swap(maxPattern_sizeRow, maxPattern_sizeColumn);
2176 for (
int i = min_row + 1; i < maxPattern_sizeRow + min_row + 1; i++)
2178 for (
int j = min_column + 1; j < maxPattern_sizeColumn + min_column + 1;
2184 for (
size_t k = 0; k < output_quads.size(); k++)
2186 for (
int l = 0; l < 4; l++)
2188 int r = output_quads[k]->corners[l]->row;
2189 int c = output_quads[k]->corners[l]->column;
2190 if (do_swap_col_row) std::swap(r, c);
2192 if (r == i && c == j)
2201 out_corners.push_back(
2202 output_quads[k]->corners[l]->pt);
2212 if (iter > 2)
return -1;
2221 if (iter == 1 || iter == 2)
2232 return (out_corners.size() ==
2233 size_t(pattern_size.width) * size_t(pattern_size.height))
2242 std::map<CvCBQuad*, size_t> pointer2index;
2243 for (
size_t i = 0; i < quads.size(); i++) pointer2index[quads[i].
get()] = i;
2245 vector<std::array<size_t, 4>> neig_indices(quads.size());
2246 for (
size_t i = 0; i < quads.size(); i++)
2247 for (
size_t j = 0; j < 4; j++)
2248 neig_indices[i][j] = pointer2index[quads[i]->neighbors[j].
get()];
2250 std::vector<CvCBQuad::Ptr> new_quads = quads;
2251 std::for_each(new_quads.begin(), new_quads.end(), [](
CvCBQuad::Ptr& ptr) {
2254 for (
size_t i = 0; i < new_quads.size(); i++)
2255 for (
size_t j = 0; j < 4; j++)
2256 new_quads[i]->neighbors[j] = new_quads[neig_indices[i][j]];
2263 #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 icvGenerateQuads(vector< CvCBQuad::Ptr > &out_quads, vector< CvCBCorner::Ptr > &out_corners, const CImage &image, int flags, int dilation, bool firstRun)
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
void icvFindConnectedQuads(std::vector< CvCBQuad::Ptr > &quad, std::vector< CvCBQuad::Ptr > &out_group, const int group_idx, const int dilation)
std::shared_ptr< CvCBQuad > Ptr
CvCBCorner::Ptr corners[4]
void quadListMakeUnique(std::vector< CvCBQuad::Ptr > &quads)
int cvFindChessboardCorners3(const CImage &img_, CvSize pattern_size, std::vector< CvPoint2D32f > &out_corners)
A class for storing images as grayscale or RGB bitmaps.
#define MRPT_UNUSED_PARAM(a)
Determines whether this is an X86 or AMD64 platform.