$search
00001 /* 00002 * plug.cpp 00003 * 00004 * 00005 * Created by Victor Eruhimov on 9/5/09. 00006 * Copyright 2009 Argus Corp. All rights reserved. 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 //#define _DEBUG_WINDOWS 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; // filter out points on the dir line 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;//*norm(dir1); 00183 float sprod2 = corner_pointm.dot(dir1)/(dir2.x*dir1.y - dir2.y*dir1.x)*2;//*norm(dir2); 00184 // printf("cpm = %f,%f, sprod1 = %f, sprod2 = %f\n", 00185 // corner_point.pt.x*2, corner_point.pt.y*2, sprod1, sprod2); 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 // if(neighbors.size() < size.width*size.height) continue; 00328 // printf("Neighbors number %d\n", neighbors.size()); 00329 if(neighbors.size() < (size_t)(size.width - 1)*(size.height - 1)) 00330 { 00331 // this is not a chessboard point 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 // int idx2 = Find2NNPerp(neighbors, features[i].pt, dir1_diag); 00356 if(idx2 < 0) continue; 00357 exclude_points.push_back(idx2); 00358 cv::Point2f dir2_diag = neighbors[idx2].pt - features[i].pt; 00359 00360 // we have found diagonal directions, now let's convert them to board axes 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 // printf("%d,%d,%d,%d\n", border1plus, border1minus, border2plus, border2minus); 00372 int count1 = count1plus + count1minus + 1; 00373 int count2 = count2plus + count2minus + 1; 00374 00375 int count_corner1 = 2*count1; 00376 // test the borders 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 //printf("dir1 = %f,%f, dir2 = %f,%f\n", dir1.x, dir1.y, dir2.x, dir2.y); 00427 00428 // find the corners 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 // check if this corner is already present 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 // printf("Added point %f,%f, weight %f, sprod1 = %f, sprod2 = %f\n", 00469 // corner_point.pt.x*2, corner_point.pt.y*2, corner_point.angle, 00470 // sprod1, sprod2); 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 // now recalculate the weights and sort again 00524 00525 // dir1 = (corner_points[size.width - 1].pt - corner_points[0].pt); 00526 // dir2 = (corner_points[(size.height - 1)*size.width].pt - corner_points[0].pt); 00527 dir1 = dir1*(1.0f/(size.width - 1)); 00528 // dir2 = dir2*(1.0f/(size.height - 1)); 00529 dir2 = dir2*0.5f; 00530 // if(dir1.y < 0) dir1 = -dir1; 00531 // if(dir2.x > 0) dir2 = -dir2; 00532 00533 if(size.height % 2 == 0) 00534 { 00535 if(dir1.x < 0) dir1 = -dir1; 00536 //if(dir2.y < 0) dir2 = -dir2; 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 // printf("oirgin = %f,%f, black_diag = %f,%f, dir1 = %f,%f, dir2 = %f,%f\n", 00547 // corner_points[0].pt.x, corner_points[0].pt.y, black_diag.x, black_diag.y, 00548 // dir1.x, dir1.y, dir2.x, dir2.y); 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 // now reorder inside each line 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 // looking for the next line 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 // found the new line 00585 origin = corner_points[idx].pt; 00586 } 00587 00588 if(!valid_flag) 00589 { 00590 //printf("invalid\n"); 00591 continue; 00592 } 00593 00594 // test the new origin for ordering 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; // this is not right! 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 //printf("dir1 = %f,%f, dir2 = %f,%f\n", dir1.x, dir1.y, dir2.x, dir2.y); 00619 // printf("Neighbors number: %d\n", neighbors.size()); 00620 00621 return 1; 00622 } 00623 } 00624 00625 if(corner_count) *corner_count = 0; 00626 return 0; 00627 } 00628