00001
00002
00003
00004
00005
00006
00007
00008
00009
00010 #include "checkerboard_pose_estimation/cvcalibinit_lowres.h"
00011
00012 #include <stdio.h>
00013 #include <highgui.h>
00014
00015 #include <outlet_pose_estimation/detail/features.h>
00016
00017 using namespace std;
00018
00019
00020
00021 struct chessboard_feature_t : public feature_t
00022 {
00023 int idx1, idx2;
00024 };
00025
00026 void SelectNeighborFeatures(const vector<feature_t>& features, vector<feature_t>& neighbors, cv::Point2f point, float max_dist)
00027 {
00028 neighbors.resize(0);
00029 for(int i = 0; i < (int)features.size(); i++)
00030 {
00031 if(length(features[i].pt - point) < max_dist)
00032 {
00033 neighbors.push_back(features[i]);
00034 }
00035 }
00036 }
00037
00038 template <class T>
00039 int Find1NN(const vector<T>& features, cv::Point2f point, int exclude_point = 0)
00040 {
00041 int min_idx = -1;
00042 float min_dist = 1e10;
00043 const float min_flt_dist = 1e-5;
00044 for(size_t i = 0; i < features.size(); i++)
00045 {
00046 if(exclude_point && norm(features[i].pt - point) < min_flt_dist)
00047 continue;
00048 float dist = length(features[i].pt - point);
00049 if(dist < min_dist)
00050 {
00051 min_dist = dist;
00052 min_idx = i;
00053 }
00054 }
00055
00056 return min_idx;
00057 }
00058
00059 int find(const vector<int>& indices, int idx)
00060 {
00061 for(size_t j = 0; j < indices.size(); j++)
00062 {
00063 if(indices[j] == idx)
00064 {
00065 return j;
00066 }
00067 }
00068
00069 return -1;
00070 }
00071
00072 int Find1NNEx(const vector<feature_t>& features, cv::Point2f point, const vector<int>& exclude_points)
00073 {
00074 int min_idx = -1;
00075 float min_dist = 1e10;
00076 const float min_flt_dist = 1e-5;
00077 for(size_t i = 0; i < features.size(); i++)
00078 {
00079 if(norm(features[i].pt - point) < min_flt_dist)
00080 continue;
00081 if(find(exclude_points, i) >= 0) continue;
00082
00083 float dist = length(features[i].pt - point);
00084 if(dist < min_dist)
00085 {
00086 min_dist = dist;
00087 min_idx = i;
00088 }
00089 }
00090
00091 return min_idx;
00092 }
00093
00094 int Find2NNPerp(const vector<feature_t>& features, cv::Point2f point, cv::Point2f dir)
00095 {
00096 int idx = -1;
00097 float min_dist = 1e10;
00098 float dir_norm = norm(dir);
00099 const float min_perp_dist = dir_norm*0.5f;
00100
00101 for(size_t i = 0; i < features.size(); i++)
00102 {
00103 if(norm(features[i].pt - point) < 1e-5)
00104 continue;
00105 cv::Point2f pdir = features[i].pt - point;
00106 float perp_dist = norm(pdir - dir*pdir.dot(dir)*(1.0f/(dir_norm*dir_norm)));
00107 if(perp_dist < min_perp_dist) continue;
00108
00109 float dist = norm(pdir);
00110 if(dist < min_dist)
00111 {
00112 min_dist = dist;
00113 idx = i;
00114 }
00115 }
00116
00117 return idx;
00118 }
00119
00120 int CountPoints(const vector<feature_t>& features, cv::Point2f point, cv::Point2f dir,
00121 int dir_sign = 0, int* border_point_idx = 0)
00122 {
00123 if(dir_sign == 0)
00124 {
00125 int count1 = CountPoints(features, point, dir, 1);
00126 int count2 = CountPoints(features, point, dir, -1);
00127 return count1 + count2;
00128 }
00129
00130 int count = 1;
00131 float dir_norm = norm(dir);
00132 const float min_dist = dir_norm*0.2f;
00133 for(;;count++)
00134 {
00135 cv::Point2f new_point = point + dir*float(count*dir_sign);
00136 int idx = Find1NN(features, new_point);
00137 cv::Point2f offset = new_point - features[idx].pt;
00138
00139 if(norm(offset) > min_dist)
00140 {
00141 break;
00142 }
00143 else
00144 {
00145 if(border_point_idx) *border_point_idx = idx;
00146 }
00147 }
00148
00149 return count - 1;
00150 }
00151
00152 int IsBorderPoint(const vector<feature_t>& features, cv::Point2f point, cv::Point2f dir_border, cv::Point2f dir_second)
00153 {
00154 cv::Point2f dir1 = (dir_border - dir_second)*0.5f;
00155 cv::Point2f dir2 = (dir_border + dir_second)*0.5f;
00156 float min_dist = 0.2f*MAX(norm(dir1), norm(dir2));
00157
00158 int idx1 = Find1NN(features, point + dir1);
00159 int idx2 = Find1NN(features, point + dir2);
00160 cv::Point2f offset1 = features[idx1].pt - point - dir1;
00161 cv::Point2f offset2 = features[idx2].pt - point - dir2;
00162 if(norm(offset1) > min_dist && norm(offset2) > min_dist)
00163 {
00164 return 1;
00165 }
00166 else
00167 {
00168 return 0;
00169 }
00170 }
00171
00172 bool helper_corner_less(chessboard_feature_t f1, chessboard_feature_t f2)
00173 {
00174 return f1.angle < f2.angle;
00175 }
00176
00177 float calc_corner_weight(const cv::Point2f& point, const cv::Point2f& origin, const cv::Point2f& dir1, const cv::Point2f& dir2,
00178 float weight_coeff)
00179 {
00180 cv::Point2f cp = point - origin;
00181 cv::Point2f corner_pointm = cv::Point2f(-cp.y, cp.x);
00182 float sprod1 = corner_pointm.dot(dir2)/(- dir2.x*dir1.y + dir2.y*dir1.x)*2;
00183 float sprod2 = corner_pointm.dot(dir1)/(dir2.x*dir1.y - dir2.y*dir1.x)*2;
00184
00185
00186 sprod1 = round(sprod1);
00187 sprod2 = round(sprod2);
00188
00189 float weight = sprod2*weight_coeff + sprod1;
00190 return weight;
00191 }
00192
00193 void ShowFeatures(IplImage* img, const vector<feature_t>& features)
00194 {
00195 IplImage* test = cvCloneImage(img);
00196
00197 for(size_t i = 0; i < features.size(); i++)
00198 {
00199 cvCircle(test, features[i].pt, features[i].size, cvScalar(255));
00200 printf("feature %d: %f,%f\n", (int)i, features[i].pt.x, features[i].pt.y);
00201 }
00202 cvNamedWindow("1", 1);
00203 cvShowImage("1", test);
00204 cvWaitKey(0);
00205
00206 cvSaveImage("features.jpg", test);
00207
00208 cvReleaseImage(&test);
00209 }
00210
00211 void FilterOutliers(vector<chessboard_feature_t>& corners, cv::Point2f dir1, cv::Point2f dir2, float min_dist)
00212 {
00213 vector<chessboard_feature_t> corners_filtered;
00214 for(size_t i = 0; i < corners.size(); i++)
00215 {
00216 cv::Point2f p = corners[i].pt;
00217 int count_neighbors = 0;
00218
00219 for(int sign2 = -1; sign2 <= 1; sign2 += 2)
00220 {
00221 for(int sign1 = -1; sign1 <= 1; sign1 += 2)
00222 {
00223 int _sign1 = (sign1 + sign2)/2;
00224 int _sign2 = (sign1 - sign2)/2;
00225 cv::Point2f pnn = p + dir1*float(_sign1) + dir2*float(_sign2);
00226 int idx = Find1NN(corners, pnn);
00227 cv::Point2f offset = corners[idx].pt - pnn;
00228 if(norm(offset) < min_dist)
00229 {
00230 count_neighbors++;
00231 }
00232 }
00233 }
00234
00235 if(count_neighbors > 1)
00236 {
00237 corners_filtered.push_back(corners[i]);
00238 }
00239 }
00240
00241 corners = corners_filtered;
00242 }
00243
00244 void updateSpanVector(vector<chessboard_feature_t>::const_iterator it_begin, vector<chessboard_feature_t>::const_iterator it_end,
00245 vector<chessboard_feature_t>::const_iterator& it_origin, cv::Point2f& dir)
00246 {
00247 vector<chessboard_feature_t>::const_iterator it_min = it_end, it_max = it_end;
00248 float min_weight = 1e10;
00249 float max_weight = -1e10;
00250 cv::Point2f origin = it_begin->pt;
00251 for(vector<chessboard_feature_t>::const_iterator it = it_begin; it != it_end; it++)
00252 {
00253 cv::Point2f offset = it->pt - origin;
00254 float weight = dir.dot(offset);
00255 if(weight < min_weight)
00256 {
00257 min_weight = weight;
00258 it_min = it;
00259 }
00260 if(weight > max_weight)
00261 {
00262 max_weight = weight;
00263 it_max = it;
00264 }
00265 }
00266
00267 dir = it_max->pt - it_min->pt;
00268 it_origin = it_min;
00269 }
00270
00271 float sortSpannedFeatures(vector<chessboard_feature_t>::iterator it_begin, vector<chessboard_feature_t>::iterator it_end,
00272 cv::Point2f origin, cv::Point2f dir)
00273 {
00274 float max_dist = 0;
00275 for(vector<chessboard_feature_t>::iterator it = it_begin; it != it_end; it++)
00276 {
00277 cv::Point2f p = it->pt - origin;
00278 cv::Point2f p_dir = dir*p.dot(dir)*(1.0f/(dir.dot(dir)));
00279 it->angle = p.dot(dir)/sqrt(dir.dot(dir));
00280 float dist = sqrt((p - p_dir).dot(p - p_dir));
00281 max_dist = MAX(max_dist, dist);
00282 }
00283
00284 sort(it_begin, it_end, helper_corner_less);
00285
00286 return max_dist;
00287 }
00288
00289 int CountBorderPoints(const vector<feature_t>& features, cv::Point2f origin, cv::Point2f dir)
00290 {
00291 int counts[2] = {0, 0};
00292 for(size_t i = 0; i < features.size(); i++)
00293 {
00294 cv::Point2f offset = features[i].pt - origin;
00295 float prod = offset.x*dir.y - offset.y*dir.x;
00296 counts[prod > 0]++;
00297 }
00298
00299 return MIN(counts[0], counts[1]);
00300 }
00301
00302 int cvFindChessboardCornersLowres(IplImage* img, CvSize size, CvPoint2D32f* corners, int* corner_count)
00303 {
00304 vector<feature_t> features;
00305 const float contrast = 1.2f;
00306 IplImage* smoothed = cvCloneImage(img);
00307 cvSmooth(img, smoothed);
00308 GetHoleFeatures(smoothed, features, contrast);
00309 vector<feature_t> filtered_features;
00310 FilterFeaturesOnEdges(img, features, filtered_features, 2, 10);
00311 cvReleaseImage(&smoothed);
00312
00313 features = filtered_features;
00314
00315 #if defined(_DEBUG_WINDOWS)
00316 ShowFeatures(img, features);
00317 #endif
00318 int board_length = MAX(size.width, size.height);
00319 float weight_coeff = board_length*1.0f;
00320
00321 const float max_square_size = 20.0;
00322
00323 for(size_t i = 0; i < features.size(); i++)
00324 {
00325 vector<feature_t> neighbors;
00326 SelectNeighborFeatures(features, neighbors, features[i].pt, max_square_size*board_length*sqrt(2.0f));
00327
00328
00329 if(neighbors.size() < (size_t)(size.width - 1)*(size.height - 1))
00330 {
00331
00332 continue;
00333 }
00334
00335 if(abs(features[i].pt.x - 413) < 5 && abs(features[i].pt.y - 214) < 5)
00336 {
00337 int w = 1;
00338 }
00339
00340 int idx1 = Find1NN(neighbors, features[i].pt, 1);
00341 assert(idx1 >= 0);
00342 cv::Point2f dir1_diag = neighbors[idx1].pt - features[i].pt;
00343
00344 vector<int> exclude_points;
00345 exclude_points.push_back(idx1);
00346 int idx1m = Find1NN(neighbors, features[i].pt - dir1_diag, 1);
00347 if(idx1m >= 0)
00348 {
00349 exclude_points.push_back(idx1m);
00350 }
00351 for(int k = 0; k < 3; k++)
00352 {
00353 int idx2 = Find1NNEx(neighbors, features[i].pt, exclude_points);
00354
00355
00356 if(idx2 < 0) continue;
00357 exclude_points.push_back(idx2);
00358 cv::Point2f dir2_diag = neighbors[idx2].pt - features[i].pt;
00359
00360
00361 cv::Point2f dir1 = dir1_diag + dir2_diag;
00362 cv::Point2f dir2 = dir1_diag - dir2_diag;
00363
00364 if(length(dir1) < 1.0f || length(dir2) < 1.0f) continue;
00365
00366 int border1plus = -1, border1minus = -1, border2plus = -1, border2minus = -1;
00367 int count1plus = CountPoints(neighbors, features[i].pt, dir1, 1, &border1plus);
00368 int count1minus = CountPoints(neighbors, features[i].pt, dir1, -1, &border1minus);
00369 int count2plus = CountPoints(neighbors, features[i].pt, dir2, 1, &border2plus);
00370 int count2minus = CountPoints(neighbors, features[i].pt, dir2, -1, &border2minus);
00371
00372 int count1 = count1plus + count1minus + 1;
00373 int count2 = count2plus + count2minus + 1;
00374
00375 int count_corner1 = 2*count1;
00376
00377 cv::Point2f point1plus = features[i].pt + dir1*float(count1plus);
00378 if(IsBorderPoint(neighbors, point1plus, dir1, dir2))
00379 {
00380 count_corner1--;
00381 }
00382 cv::Point2f point1minus = features[i].pt - dir1*float(count1minus);
00383 if(IsBorderPoint(neighbors, point1minus, -dir1, dir2))
00384 {
00385 count_corner1--;
00386 }
00387
00388 int count_corner2 = 2*count2;
00389 cv::Point2f point2plus = features[i].pt + dir2*float(count2plus);
00390 if(IsBorderPoint(neighbors, point2plus, dir2, dir1))
00391 {
00392 count_corner2--;
00393 }
00394 cv::Point2f point2minus = features[i].pt - dir2*float(count2minus);
00395 if(IsBorderPoint(neighbors, point2minus, -dir2, dir1))
00396 {
00397 count_corner2--;
00398 }
00399
00400
00401 if(border1plus >= 0 && border1minus >= 0)
00402 {
00403 dir1 = (neighbors[border1plus].pt - neighbors[border1minus].pt)*(1.0f/(count1 - 1));
00404 }
00405 if(border2plus >= 0 && border2minus >= 0)
00406 {
00407 dir2 = (neighbors[border2plus].pt - neighbors[border2minus].pt)*(1.0f/(count2 - 1));
00408 }
00409 if(count_corner1 == size.height && count_corner2 == size.width)
00410 {
00411 count_corner1 = size.width;
00412 count_corner2 = size.height;
00413 cv::Point2f _dir = dir1;
00414 dir1 = dir2;
00415 dir2 = _dir;
00416 }
00417 else if(count_corner1 != size.width || count_corner2 != size.height)
00418 {
00419 continue;
00420 }
00421
00422 if(dir1.x < 0) dir1 = -dir1;
00423 if(dir2.y < 0) dir2 = -dir2;
00424 float min_dist = 0.3*MAX(norm(dir1), norm(dir2));
00425
00426
00427
00428
00429 vector<chessboard_feature_t> corner_points;
00430 for(size_t j1 = 0; j1 < neighbors.size(); j1++)
00431 {
00432 cv::Point2f p = neighbors[j1].pt;
00433
00434 for(int sign2 = -1; sign2 <= 1; sign2 += 2)
00435 {
00436 for(int sign1 = -1; sign1 <= 1; sign1 += 2)
00437 {
00438 cv::Point2f pnn = p + (dir1*float(sign1) + dir2*float(sign2))*0.5f;
00439 int idx = Find1NN(neighbors, pnn);
00440 cv::Point2f offset = neighbors[idx].pt - pnn;
00441 if(norm(offset) < min_dist)
00442 {
00443 chessboard_feature_t corner_point;
00444 corner_point.pt = (p + pnn)*0.5f;
00445 if(corner_points.size() == 0)
00446 {
00447 corner_point.angle = 0;
00448 }
00449 else
00450 {
00451 corner_point.angle = calc_corner_weight(corner_point.pt, corner_points[0].pt,
00452 dir1, dir2, weight_coeff);
00453 }
00454
00455 int corner_idx = Find1NN(corner_points, corner_point.pt);
00456 if(corner_idx >= 0)
00457 {
00458 cv::Point2f offset = corner_point.pt - corner_points[corner_idx].pt;
00459 if(norm(offset) < min_dist)
00460 {
00461 continue;
00462 }
00463 }
00464
00465 corner_point.idx1 = j1;
00466 corner_point.idx2 = idx;
00467 corner_points.push_back(corner_point);
00468
00469
00470
00471 }
00472 }
00473 }
00474 }
00475
00476 #if defined(_DEBUG_WINDOWS)
00477 IplImage* test = cvCreateImage(cvGetSize(img), IPL_DEPTH_8U, 3);
00478 cvCvtColor(img, test, CV_GRAY2BGR);
00479
00480 for(size_t i = 0; i < corner_points.size(); i++)
00481 {
00482 cvCircle(test, corner_points[i].pt, 3, cvScalar(255, 0, 0));
00483 }
00484 cvNamedWindow("1", 1);
00485 cvShowImage("1", test);
00486 cvWaitKey(0);
00487
00488 cvSaveImage("test.png", test);
00489
00490 cvReleaseImage(&test);
00491 #endif //_DEBUG_WINDOWS
00492
00493 FilterOutliers(corner_points, dir1*0.5f, dir2*0.5f, min_dist);
00494
00495 #if defined(_DEBUG_WINDOWS)
00496 test = cvCreateImage(cvGetSize(img), IPL_DEPTH_8U, 3);
00497 cvCvtColor(img, test, CV_GRAY2BGR);
00498
00499 for(size_t i = 0; i < corner_points.size(); i++)
00500 {
00501 cvCircle(test, corner_points[i].pt, 3, cvScalar(0, 255, 0));
00502 }
00503 cvNamedWindow("1", 1);
00504 cvShowImage("1", test);
00505 cvWaitKey(0);
00506
00507 cvReleaseImage(&test);
00508 #endif //_DEBUG_WINDOWS
00509
00510
00511 if(corner_points.size() != size.width*size.height)
00512 {
00513 continue;
00514 }
00515
00516 sort(corner_points.begin(), corner_points.end(), helper_corner_less);
00517
00518 #if 1
00519 vector<chessboard_feature_t>::const_iterator it_origin;
00520 updateSpanVector(corner_points.begin(), corner_points.begin() + 4, it_origin, dir1);
00521 int origin_idx = it_origin - corner_points.begin();
00522
00523
00524
00525
00526
00527 dir1 = dir1*(1.0f/(size.width - 1));
00528
00529 dir2 = dir2*0.5f;
00530
00531
00532
00533 if(size.height % 2 == 0)
00534 {
00535 if(dir1.x < 0) dir1 = -dir1;
00536
00537 if(dir1.x*dir2.y - dir1.y*dir2.x < 0) dir2 = -dir2;
00538 }
00539 else
00540 {
00541 cv::Point2f black_diag = neighbors[corner_points[0].idx1].pt -
00542 neighbors[corner_points[0].idx2].pt;
00543 if(black_diag.dot(dir1) < 0) black_diag = -black_diag;
00544 if(black_diag.dot(dir2) < 0) dir2 = -dir2;
00545 if(dir1.x*dir2.y - dir1.y*dir2.x < 0) dir1 = -dir1;
00546
00547
00548
00549 }
00550
00551
00552 corner_points[origin_idx].angle = 0.0f;
00553 for(size_t j1 = 0; j1 < corner_points.size(); j1++)
00554 {
00555 if(j1 == origin_idx) continue;
00556 corner_points[j1].angle = calc_corner_weight(corner_points[j1].pt,
00557 corner_points[origin_idx].pt, dir1, dir2, weight_coeff);
00558 }
00559
00560 sort(corner_points.begin(), corner_points.end(), helper_corner_less);
00561
00562
00563 int valid_flag = 1;
00564 cv::Point2f origin = it_origin->pt;
00565 for(int jh = 0; jh < size.height; jh++)
00566 {
00567 float offset = sortSpannedFeatures(corner_points.begin() + size.width*jh,
00568 corner_points.begin() + size.width*(jh + 1), origin, dir1);
00569 if(offset > min_dist)
00570 {
00571 valid_flag = 0;
00572 break;
00573 }
00574
00575
00576 cv::Point2f new_origin = origin + dir2;
00577 int idx = Find1NN(corner_points, new_origin);
00578 if(length(new_origin - corner_points[idx].pt) > min_dist && jh < size.height - 1)
00579 {
00580 valid_flag = 0;
00581 break;
00582 }
00583
00584
00585 origin = corner_points[idx].pt;
00586 }
00587
00588 if(!valid_flag)
00589 {
00590
00591 continue;
00592 }
00593
00594
00595 if(size.height % 2 != 0)
00596 {
00597 cv::Point2f black_diag = neighbors[corner_points[0].idx1].pt -
00598 neighbors[corner_points[0].idx2].pt;
00599 if(black_diag.dot(dir1) < 0) black_diag = -black_diag;
00600 assert(dir1.x*dir2.y - dir1.y*dir2.x > 0);
00601 if(black_diag.dot(dir2) < 0)
00602 {
00603 continue;
00604 }
00605
00606 }
00607 #endif
00608
00609 for(int jh = 0; jh < size.height; jh++)
00610 {
00611 for(int jw = 0; jw < size.width; jw++)
00612 {
00613 corners[jh*size.width + jw] = corner_points[jh*size.width + jw].pt;
00614 }
00615 }
00616
00617 if(corner_count) *corner_count = size.width*size.height;
00618
00619
00620
00621 return 1;
00622 }
00623 }
00624
00625 if(corner_count) *corner_count = 0;
00626 return 0;
00627 }
00628