$search
00001 /* 00002 * constellation.cpp 00003 * online_patch 00004 * 00005 * Created by Victor Eruhimov on 5/3/09. 00006 * Copyright 2009 Argus Corp. All rights reserved. 00007 * 00008 */ 00009 00010 #include "outlet_pose_estimation/detail/constellation.h" 00011 #include "outlet_pose_estimation/detail/outlet_tuple.h" 00012 #include "outlet_pose_estimation/detail/affine_transform.h" 00013 00014 using namespace std; 00015 00016 int iterate_indices(vector<int>& indices, int max_index, int min_valid_indices, int* workspace) 00017 { 00018 int indices_count = indices.size(); 00019 while(1) 00020 { 00021 // increment indices 00022 for(int i = 0; i < indices_count; i++) 00023 { 00024 indices[i]++; 00025 if(indices[i] < max_index) 00026 { 00027 break; 00028 } 00029 else 00030 { 00031 indices[i] = -1; 00032 } 00033 } 00034 00035 // now check indices 00036 int valid_indices = 0; 00037 for(int j = 0; j < max_index; j++) workspace[j] = 0; 00038 int repeat_flag = 0; 00039 00040 for(int i = 0; i < indices_count; i++) 00041 { 00042 if(indices[i] >= 0) 00043 { 00044 valid_indices++; 00045 00046 if(++workspace[indices[i]] > 1) 00047 { 00048 repeat_flag = 1; 00049 } 00050 } 00051 } 00052 00053 if(repeat_flag == 0 && valid_indices >= min_valid_indices) 00054 { 00055 return 0; 00056 } 00057 00058 if(valid_indices == 0) 00059 { 00060 return -1; 00061 } 00062 } 00063 } 00064 00065 int validate_parts(const vector<feature_t>& train, const vector<feature_t>& test, const vector<int>& indices, int* min_part_count) 00066 { 00067 int part_count[] = {0, 0}; 00068 const int min_valid_parts = 5; // since we use homography matching, almost any 4 points will be matched with low error 00069 00070 for(int i = 0; i < (int)indices.size(); i++) 00071 { 00072 if(indices[i] == -1) continue; 00073 00074 if(test[i].class_id != train[indices[i]].class_id) 00075 { 00076 return -1; 00077 } 00078 00079 part_count[train[indices[i]].class_id]++; 00080 } 00081 00082 if(part_count[0] < min_part_count[0] || part_count[1] < min_part_count[1] || 00083 part_count[0] + part_count[1] < min_valid_parts) 00084 { 00085 return -1; 00086 } 00087 00088 return 0; 00089 } 00090 00091 float min_idx(float v1, float v2, int i1, int i2) 00092 { 00093 if(i1 && i2) 00094 { 00095 return MIN(v1, v2); 00096 } 00097 else if(i1) 00098 { 00099 return v1; 00100 } 00101 else return v2; 00102 } 00103 00104 float max_idx(float v1, float v2, int i1, int i2) 00105 { 00106 if(i1 && i2) 00107 { 00108 return MAX(v1, v2); 00109 } 00110 else if(i1) 00111 { 00112 return v1; 00113 } 00114 else return v2; 00115 } 00116 00117 int validate_order(const vector<feature_t>& train, const vector<feature_t>& test, const vector<int>& indices) 00118 { 00119 const int min_offset = 10; 00120 vector<int> parts_index; 00121 00122 const int parts_number = 6; 00123 CvPoint centers[parts_number]; 00124 parts_index.assign(parts_number, 0); 00125 00126 for(int i = 0; i < (int)indices.size(); i++) 00127 { 00128 if(indices[i] == -1) continue; 00129 centers[indices[i]] = test[i].pt; 00130 parts_index[indices[i]] = 1; 00131 } 00132 00133 if((centers[0].x > centers[4].x)*parts_index[0]*parts_index[4] || 00134 (centers[4].x > centers[1].x)*parts_index[4]*parts_index[1] || 00135 (centers[2].x > centers[5].x)*parts_index[2]*parts_index[5] || 00136 (centers[5].x > centers[3].x)*parts_index[5]*parts_index[3]) 00137 { 00138 return -1; 00139 } 00140 00141 if((min_idx(centers[0].y, centers[1].y, parts_index[0], parts_index[1]) < centers[4].y)* 00142 (parts_index[0] | parts_index[1])*parts_index[4] || 00143 (min_idx(centers[2].y, centers[3].y, parts_index[2], parts_index[3]) < centers[5].y)* 00144 (parts_index[2] | parts_index[3])*parts_index[5] || 00145 (centers[5].y < max_idx(centers[0].y, centers[1].y, parts_index[0], parts_index[1]) + min_offset)* 00146 (parts_index[0] | parts_index[1])*parts_index[5] || 00147 (max_idx(centers[0].y, centers[1].y, parts_index[0], parts_index[1]) + min_offset > 00148 min_idx(centers[2].y, centers[3].y, parts_index[2], parts_index[3]))* 00149 (parts_index[0] | parts_index[1])*(parts_index[2] | parts_index[3])) 00150 { 00151 return -1; 00152 } 00153 00154 return 0; 00155 } 00156 00157 float calc_set_std(const vector<feature_t>& features, const vector<int>& indices) 00158 { 00159 CvPoint2D32f sum = cvPoint2D32f(0, 0); 00160 CvPoint2D32f sum2 = cvPoint2D32f(0, 0); 00161 int count = 0; 00162 00163 if(indices.size() > 0) 00164 { 00165 for(int i = 0; i < (int)indices.size(); i++) 00166 { 00167 if(indices[i] < 0) continue; 00168 00169 sum.x += features[i].pt.x; 00170 sum.y += features[i].pt.y; 00171 sum2.x += features[i].pt.x*features[i].pt.x; 00172 sum2.y += features[i].pt.y*features[i].pt.y; 00173 00174 count++; 00175 } 00176 } 00177 else 00178 { 00179 for(int i = 0; i < (int)features.size(); i++) 00180 { 00181 sum.x += features[i].pt.x; 00182 sum.y += features[i].pt.y; 00183 sum2.x += features[i].pt.x*features[i].pt.x; 00184 sum2.y += features[i].pt.y*features[i].pt.y; 00185 } 00186 00187 count = features.size(); 00188 } 00189 00190 float sigmax = sqrt(sum2.x/count - sum.x*sum.x/(count*count)); 00191 float sigmay = sqrt(sum2.y/count - sum.y*sum.y/(count*count)); 00192 00193 return MAX(sigmax, sigmay); 00194 } 00195 00196 float CalcReprojectionError(CvMat* src_points, CvMat* dst_points, CvMat* src_proj_points, CvMat* homography) 00197 { 00198 cvPerspectiveTransform(src_points, src_proj_points, homography); 00199 float error = 0.0f; 00200 for(int i = 0; i < dst_points->rows; i++) 00201 { 00202 float* dst_points_ptr = (float*)(dst_points->data.ptr + i*dst_points->step); 00203 float* src_proj_points_ptr = (float*)(src_proj_points->data.ptr + i*src_proj_points->step); 00204 error += sqrt((dst_points_ptr[0] - src_proj_points_ptr[0])*(dst_points_ptr[0] - src_proj_points_ptr[0]) + 00205 (dst_points_ptr[1] - src_proj_points_ptr[1])*(dst_points_ptr[1] - src_proj_points_ptr[1])); 00206 } 00207 00208 error /= dst_points->rows; 00209 00210 return error; 00211 } 00212 00213 void count_parts(const vector<feature_t>& features, int* min_part_count, int parts_number) 00214 { 00215 int* part_count = new int[parts_number]; 00216 00217 for(int i = 0; i < parts_number; i++) part_count[i] = 0; 00218 00219 for(int i = 0; i < (int)features.size(); i++) 00220 { 00221 part_count[features[i].class_id]++; 00222 } 00223 00224 for(int i = 0; i < parts_number; i++) 00225 { 00226 min_part_count[i] = MIN(min_part_count[i], part_count[i]); 00227 } 00228 00229 delete []part_count; 00230 } 00231 00232 void DetectObjectConstellation(const vector<feature_t>& train, const vector<feature_t>& input, CvMat* homography, vector<int>& indices) 00233 { 00234 int parts_number = train.size(); 00235 int test_feature_count = input.size(); 00236 indices.resize(test_feature_count); 00237 00238 for(int i = 0; i < test_feature_count; i++) indices[i] = -1; 00239 00240 if(test_feature_count > 9) 00241 { 00242 printf("The number of features is %d, exiting...\n", test_feature_count); 00243 return; 00244 } 00245 00246 const int min_valid_indices = 4; 00247 int* workspace = new int[parts_number]; 00248 int ret_flag = 0; 00249 00250 CvMat* _train_points = cvCreateMat(parts_number, 2, CV_32FC1); 00251 CvMat* _test_points = cvCreateMat(test_feature_count, 2, CV_32FC1); 00252 00253 vector<int> min_indices = indices; 00254 float min_error = 1e10; 00255 int min_part_count[] = {3, 1}; 00256 count_parts(input, min_part_count, 2); 00257 CvMat* min_homography = cvCloneMat(homography); 00258 00259 float train_set_size = calc_set_std(train); 00260 // printf("Train set size = %f\n", train_set_size); 00261 const float set_size_factor = 2.0f; 00262 00263 // printf("Starting the parts search...\n"); 00264 while(1) 00265 { 00266 ret_flag = iterate_indices(indices, parts_number, min_valid_indices, workspace); 00267 if(ret_flag == -1) 00268 { 00269 break; 00270 } 00271 00272 if(validate_parts(train, input, indices, min_part_count) == -1) 00273 { 00274 continue; 00275 } 00276 00277 if(validate_order(train, input, indices) == -1) 00278 { 00279 continue; 00280 } 00281 00282 if(calc_set_std(input, indices) > train_set_size*set_size_factor) 00283 { 00284 continue; 00285 } 00286 00287 // printf("Tested indices %d%d%d%d%d%d\n", indices[0], indices[1], indices[2], indices[3], indices[4], indices[5]); 00288 00289 #if defined(_HOMOGRAPHY) 00290 // fill train and test points 00291 int point_count = 0; 00292 for(int i = 0; i < test_feature_count; i++) 00293 { 00294 if(indices[i] == -1) continue; 00295 00296 cvmSet(_train_points, point_count, 0, train[indices[i]].center.x); 00297 cvmSet(_train_points, point_count, 1, train[indices[i]].center.y); 00298 cvmSet(_test_points, point_count, 0, input[i].center.x); 00299 cvmSet(_test_points, point_count, 1, input[i].center.y); 00300 point_count++; 00301 } 00302 00303 CvMat train_points, test_points; 00304 CvRect subrect = cvRect(0, 0, 2, point_count); 00305 cvGetSubRect(_train_points, &train_points, subrect); 00306 cvGetSubRect(_test_points, &test_points, subrect); 00307 CvMat src_proj_points; 00308 cvGetSubRect(_test_points2, &src_proj_points, subrect); 00309 00310 const double ransacReprojThreshold = 5.0; 00311 cvFindHomography(&train_points, &test_points, homography, CV_RANSAC, ransacReprojThreshold); 00312 00313 train_points.cols = 1; 00314 train_points.type = hack->type; 00315 test_points.cols = 1; 00316 test_points.type = hack->type; 00317 src_proj_points.cols = 1; 00318 src_proj_points.type = hack->type; 00319 00320 float error = CalcReprojectionError(&train_points, &test_points, &src_proj_points, homography); 00321 #else 00322 // fill the points 00323 vector<CvPoint> p1, p2; 00324 for(int i = 0; i < test_feature_count; i++) 00325 { 00326 if(indices[i] == -1) continue; 00327 00328 p1.push_back(train[indices[i]].pt); 00329 p2.push_back(input[i].pt); 00330 } 00331 00332 FindAffineTransform(p1, p2, homography); 00333 00334 float error = CalcAffineReprojectionError(p1, p2, homography); 00335 00336 #endif //_HOMOGRAPHY 00337 if(error < min_error) 00338 { 00339 min_error = error; 00340 min_indices = indices; 00341 cvCopy(homography, min_homography); 00342 } 00343 00344 } 00345 // printf("Parts search finished\n"); 00346 // printf("Final set size = %f\n", calc_set_std(input, min_indices)); 00347 00348 indices = min_indices; 00349 cvCopy(min_homography, homography); 00350 00351 delete []workspace; 00352 cvReleaseMat(&min_homography); 00353 00354 cvReleaseMat(&_train_points); 00355 cvReleaseMat(&_test_points); 00356 } 00357 00358 void features2points(const vector<feature_t>& features, vector<CvPoint2D32f>& points) 00359 { 00360 for(int i = 0; i < (int)features.size(); i++) 00361 { 00362 points.push_back(cvPoint2D32f(features[i].pt.x, features[i].pt.y)); 00363 } 00364 } 00365 00366 void points2features(const vector<CvPoint2D32f>& points, vector<feature_t>& features) 00367 { 00368 features.resize(points.size()); 00369 for(int i = 0; i < (int)points.size(); i++) 00370 { 00371 features[i].pt = cvPoint(points[i].x, points[i].y); 00372 } 00373 } 00374 00375 void map_features(const vector<feature_t>& src_features, CvMat* homography, vector<feature_t>& dst_features) 00376 { 00377 vector<CvPoint2D32f> src_points, dst_points; 00378 00379 features2points(src_features, src_points); 00380 map_vector_homography(src_points, homography, dst_points); 00381 dst_features = src_features; 00382 points2features(dst_points, dst_features); 00383 } 00384 00385 void InferMissingObjects(const vector<feature_t>& train, const vector<feature_t>& input, CvMat* homography, const vector<int>& indices, 00386 vector<feature_t>& full) 00387 { 00388 int parts_number = train.size(); 00389 00390 vector<feature_t> train_mapped; 00391 #if defined(_HOMOGRAPHY) 00392 map_features(train, homography, train_mapped); 00393 #else 00394 MapFeaturesAffine(train, train_mapped, homography); //TBD need to unify the transformation or get rid of homography 00395 #endif //_HOMOGRAPHY 00396 00397 // find missing indices 00398 vector<int> miss_indices; 00399 miss_indices.assign(parts_number, 0); 00400 00401 full.resize(parts_number); 00402 for(int i = 0; i < (int)indices.size(); i++) 00403 { 00404 if(indices[i] >= 0) 00405 { 00406 miss_indices[indices[i]] = 1; 00407 full[indices[i]] = input[i]; 00408 } 00409 } 00410 00411 for(int i = 0; i < (int)miss_indices.size(); i++) 00412 { 00413 if(miss_indices[i] == 0) 00414 { 00415 full[i] = train_mapped[i]; 00416 } 00417 } 00418 } 00419 00420 void FilterOutletFeatures(const vector<feature_t>& src_features, vector<feature_t>& dst_features, float max_dist) 00421 { 00422 vector<int> ground_idx; 00423 // find all ground hole candidates 00424 for(int i = 0; i < (int)src_features.size(); i++) 00425 { 00426 if(src_features[i].class_id == 1) 00427 { 00428 ground_idx.push_back(i); 00429 } 00430 } 00431 00432 // find all pairs of ground holes that are close enough to each other and filter out one of them 00433 vector<int> ground_idx_filtered; 00434 for(int i = 0; i < (int)ground_idx.size(); i++) 00435 { 00436 int flag = 0; 00437 for(int j = 0; j < (int)ground_idx_filtered.size(); j++) 00438 { 00439 if(length(src_features[ground_idx[i]].pt - src_features[ground_idx_filtered[j]].pt) < max_dist) 00440 { 00441 flag = 1; 00442 break; 00443 } 00444 } 00445 00446 if(flag == 0) 00447 { 00448 ground_idx_filtered.push_back(ground_idx[i]); 00449 } 00450 } 00451 00452 ground_idx = ground_idx_filtered; 00453 // printf("Found %d clusters\n", (int)ground_idx.size()); 00454 00455 // now filter out all features that are far enough from ground holes 00456 vector<int> indices; 00457 indices.assign(src_features.size(), 0); 00458 for(int i = 0; i < (int)ground_idx.size(); i++) 00459 { 00460 for(int j = 0; j < (int)src_features.size(); j++) 00461 { 00462 if(length(src_features[j].pt - src_features[ground_idx[i]].pt) < max_dist) 00463 { 00464 indices[j] = 1; 00465 } 00466 } 00467 } 00468 00469 // now copy the features 00470 for(int i = 0; i < (int)src_features.size(); i++) 00471 { 00472 if(indices[i]) 00473 { 00474 dst_features.push_back(src_features[i]); 00475 } 00476 } 00477 } 00478 00479 //----------------------- 00480 void FilterOutletFeatures(const vector<feature_t>& src_features, vector<feature_t>& dst_features, vector<int>& dst_indexes, float max_dist) 00481 { 00482 vector<int> ground_idx; 00483 dst_indexes.clear(); 00484 // find all ground hole candidates 00485 for(int i = 0; i < (int)src_features.size(); i++) 00486 { 00487 if(src_features[i].class_id == 1) 00488 { 00489 ground_idx.push_back(i); 00490 } 00491 } 00492 00493 // find all pairs of ground holes that are close enough to each other and filter out one of them 00494 vector<int> ground_idx_filtered; 00495 for(int i = 0; i < (int)ground_idx.size(); i++) 00496 { 00497 int flag = 0; 00498 for(int j = 0; j < (int)ground_idx_filtered.size(); j++) 00499 { 00500 if(length(src_features[ground_idx[i]].pt - src_features[ground_idx_filtered[j]].pt) < max_dist) 00501 { 00502 flag = 1; 00503 break; 00504 } 00505 } 00506 00507 if(flag == 0) 00508 { 00509 ground_idx_filtered.push_back(ground_idx[i]); 00510 } 00511 } 00512 00513 ground_idx = ground_idx_filtered; 00514 vector<int> indices; 00515 indices.assign(src_features.size(), 0); 00516 for(int i = 0; i < (int)ground_idx.size(); i++) 00517 { 00518 for(int j = 0; j < (int)src_features.size(); j++) 00519 { 00520 if(length(src_features[j].pt - src_features[ground_idx[i]].pt) < max_dist) 00521 { 00522 indices[j] = 1; 00523 } 00524 } 00525 } 00526 00527 // now copy the features 00528 for(int i = 0; i < (int)src_features.size(); i++) 00529 { 00530 if(indices[i]) 00531 { 00532 dst_features.push_back(src_features[i]); 00533 dst_indexes.push_back(i); 00534 } 00535 } 00536 } 00537 //---------------------------------- 00538 void ClusterOutletFeatures(const vector<feature_t>& src_features, vector<feature_t>& clusters, float max_dist) 00539 { 00540 vector<int> ground_idx; 00541 // find all ground hole candidates 00542 for(int i = 0; i < (int)src_features.size(); i++) 00543 { 00544 if(src_features[i].class_id == 1) 00545 { 00546 ground_idx.push_back(i); 00547 } 00548 } 00549 00550 // find all pairs of ground holes that are close enough to each other and filter out one of them 00551 vector<int> ground_idx_filtered; 00552 for(int i = 0; i < (int)ground_idx.size(); i++) 00553 { 00554 int flag = 0; 00555 for(int j = 0; j < (int)ground_idx_filtered.size(); j++) 00556 { 00557 if(length(src_features[ground_idx[i]].pt - src_features[ground_idx_filtered[j]].pt) < max_dist) 00558 { 00559 flag = 1; 00560 break; 00561 } 00562 } 00563 00564 if(flag == 0) 00565 { 00566 ground_idx_filtered.push_back(ground_idx[i]); 00567 } 00568 } 00569 00570 ground_idx = ground_idx_filtered; 00571 // printf("Found %d clusters\n", (int)ground_idx.size()); 00572 00573 // now copy the features 00574 for(int i = 0; i < (int)ground_idx.size(); i++) 00575 { 00576 clusters.push_back(src_features[ground_idx[i]]); 00577 } 00578 } 00579 00580 void SelectNeighborFeatures(const vector<feature_t>& src_features, CvPoint center, vector<feature_t>& dst_features, float max_dist) 00581 { 00582 for(int i = 0; i < (int)src_features.size(); i++) 00583 { 00584 if(length(src_features[i].pt - center) < max_dist) 00585 { 00586 dst_features.push_back(src_features[i]); 00587 } 00588 } 00589 }