00001 #include <cpl_visual_features/features/shape_context.h>
00002 #include <cpl_visual_features/extern/lap_cpp/lap.h>
00003 #include <cpl_visual_features/helpers.h>
00004 #include <math.h>
00005 #include <string>
00006 #include <iostream>
00007
00008 namespace cpl_visual_features
00009 {
00010
00011 void swapImages(cv::Mat& a, cv::Mat& b)
00012 {
00013 cv::Mat c = a;
00014 a = b;
00015 b = c;
00016 }
00017
00018 void swapSamples(Samples& a, Samples& b)
00019 {
00020 Samples c = a;
00021 a = b;
00022 b = c;
00023 }
00024
00025 double compareShapes(cv::Mat& imageA, cv::Mat& imageB, double epsilonCost, bool write_images,
00026 std::string filePath, int max_displacement, std::string filePostFix)
00027 {
00028 cv::Mat edge_imageA(imageA.size(), imageA.type());
00029 cv::Mat edge_imageB(imageB.size(), imageB.type());
00030 cv::Mat edge_imageA_raw;
00031 cv::Mat edge_imageB_raw;
00032
00033
00034 cv::Canny(imageA, edge_imageA, 0.05, 0.5);
00035 cv::Canny(imageB, edge_imageB, 0.05, 0.5);
00036 edge_imageA.copyTo(edge_imageA_raw);
00037 edge_imageB.copyTo(edge_imageB_raw);
00038
00039
00040
00041 Samples samplesA = samplePoints(edge_imageA);
00042
00043 Samples samplesB = samplePoints(edge_imageB);
00044
00045
00046
00047
00048 if (samplesA.size() > samplesB.size())
00049 {
00050 swapImages(imageA, imageB);
00051 swapImages(edge_imageA, edge_imageB);
00052 swapImages(edge_imageA_raw, edge_imageB_raw);
00053 swapSamples(samplesA, samplesB);
00054 }
00055
00056
00057
00058 ShapeDescriptors descriptorsA = constructDescriptors<Samples>(samplesA);
00059
00060 ShapeDescriptors descriptorsB = constructDescriptors<Samples>(samplesB);
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086 cv::Mat cost_matrix = computeCostMatrix(descriptorsA, descriptorsB,
00087 epsilonCost, write_images, filePath, filePostFix);
00088
00089
00090 if (write_images)
00091 {
00092 std::stringstream image_a_path_raw;
00093 image_a_path_raw << filePath << "/edge_imageA_raw" << filePostFix << ".bmp";
00094 std::stringstream image_b_path_raw;
00095 image_b_path_raw << filePath << "/edge_imageB_raw" << filePostFix << ".bmp";
00096 cv::imwrite(image_a_path_raw.str().c_str(), edge_imageA_raw);
00097 cv::imwrite(image_b_path_raw.str().c_str(), edge_imageB_raw);
00098 std::stringstream image_a_path;
00099 image_a_path << filePath << "/edge_imageA" << filePostFix << ".bmp";
00100 std::stringstream image_b_path;
00101 image_b_path << filePath << "/edge_imageB" << filePostFix << ".bmp";
00102 cv::imwrite(image_a_path.str().c_str(), edge_imageA);
00103 cv::imwrite(image_b_path.str().c_str(), edge_imageB);
00104 }
00105
00106
00107
00108 Path min_path;
00109 double score = getMinimumCostPath(cost_matrix, min_path);
00110 displayMatch(edge_imageA, edge_imageB, samplesA, samplesB, min_path, max_displacement,
00111 filePath, filePostFix);
00112 int sizeA = samplesA.size();
00113 int sizeB = samplesB.size();
00114
00115
00116 return (score-(fabs(sizeA-sizeB)*epsilonCost));
00117 }
00118
00119 ShapeDescriptors extractDescriptors(cv::Mat& image)
00120 {
00121 cv::Mat edge_image(image.size(), image.type());
00122 cv::Mat edge_image_raw;
00123
00124
00125 cv::Canny(image, edge_image, 0.05, 0.5);
00126 edge_image.copyTo(edge_image_raw);
00127
00128
00129 Samples samples = samplePoints(edge_image);
00130
00131
00132 ShapeDescriptors descriptors = constructDescriptors<Samples>(samples);
00133 return descriptors;
00134 }
00135
00136 Samples samplePoints(cv::Mat& edge_image, double percentage)
00137 {
00138 Samples samples;
00139 Samples all_points;
00140 cv::Scalar pixel;
00141 for (int y=0; y < edge_image.rows; y++)
00142 {
00143 for (int x=0; x < edge_image.cols; x++)
00144 {
00145 if (edge_image.at<uchar>(y, x) > 0)
00146 {
00147 all_points.push_back(cv::Point(x, y));
00148 }
00149 }
00150 }
00151
00152
00153 edge_image = cv::Scalar(0);
00154
00155
00156 int scale = 1 / percentage;
00157 for (unsigned int i=0; i < all_points.size(); i++)
00158 {
00159 if (i%scale == 0)
00160 {
00161 samples.push_back(all_points.at(i));
00162 edge_image.at<uchar>(samples.back().y, samples.back().x) = 255;
00163 }
00164 }
00165 return samples;
00166 }
00167
00168 int getHistogramIndex(double radius, double theta, int radius_bins, int theta_bins)
00169 {
00170 if (theta > 1.0) std::cout << "theta is too big: " << theta << std::endl;
00171 if (theta < 0.0) std::cout << "theta is too big: " << theta << std::endl;
00172 int radius_idx = std::max(std::min((int)std::floor(radius*radius_bins), radius_bins-1), 0);
00173 int theta_idx = std::max(std::min((int)std::floor(theta*theta_bins), theta_bins-1),0);
00174
00175 int idx = theta_idx*radius_bins+radius_idx;
00176 return idx;
00177 }
00178
00179 ShapeDescriptors constructDescriptors(Samples2f& samples,
00180 cv::Point2f& center,
00181 bool use_center,
00182 int radius_bins,
00183 int theta_bins,
00184 double max_radius,
00185 double scale)
00186 {
00187 ShapeDescriptors descriptors;
00188 ShapeDescriptor descriptor;
00189
00190 double radius, theta;
00191 double x1, x2, y1, y2;
00192 unsigned int i, j, k, m;
00193
00194
00195 if (max_radius == 0.0)
00196 {
00197 for (i=0; i < samples.size(); i++)
00198 {
00199 x1 = samples.at(i).x;
00200 y1 = samples.at(i).y;
00201 for (k=0; k < samples.size(); k++)
00202 {
00203 if (k != i)
00204 {
00205 x2 = samples.at(k).x;
00206 y2 = samples.at(k).y;
00207
00208 radius = sqrt(pow(x1-x2,2) + pow(y1-y2,2))*scale;
00209 if (radius > max_radius)
00210 {
00211 max_radius = radius;
00212 }
00213 }
00214 }
00215 }
00216 }
00217 else
00218 {
00219 max_radius *= scale;
00220 }
00221
00222 max_radius = log(max_radius);
00223
00224
00225
00226 for (i=0; i < samples.size(); i++)
00227 {
00228
00229 descriptor.clear();
00230 for (j=0; j < radius_bins*theta_bins; j++)
00231 {
00232 descriptor.push_back(0);
00233 }
00234
00235
00236 double center_angle = atan2(samples[i].y - center.y, samples[i].x- center.x);
00237 x1 = samples.at(i).x;
00238 y1 = samples.at(i).y;
00239
00240
00241 for (m=0; m < samples.size(); m++)
00242 {
00243 if (m != i)
00244 {
00245 x2 = samples.at(m).x;
00246 y2 = samples.at(m).y;
00247 radius = sqrt(pow(x1-x2,2) + pow(y1-y2,2))*scale;
00248 radius = log(radius);
00249 radius /= max_radius;
00250 theta = atan2(y1-y2,x1-x2);
00251
00252 if (use_center)
00253 {
00254
00255 theta = cpl_visual_features::subPIAngle(theta-center_angle);
00256
00257 }
00258
00259 theta += M_PI;
00260
00261
00262 theta /= 2*M_PI;
00263
00264 int idx = getHistogramIndex(radius, theta, radius_bins, theta_bins);
00265
00266 descriptor[idx]++;
00267 }
00268 }
00269
00270
00271 descriptors.push_back(descriptor);
00272 }
00273
00274 return descriptors;
00275 }
00276
00277
00278
00279
00280
00281
00282
00283
00284
00285
00286
00287
00288
00289
00290
00291
00292
00293
00294
00295
00296
00297
00298
00299
00300
00301
00302
00303
00304
00305
00306
00307
00308
00309
00310
00311
00312
00313
00314
00315
00316
00317
00318
00319
00320
00321
00322
00323
00324
00325
00326
00327
00328
00329
00330
00331
00332
00333
00334
00335
00336
00337
00338
00339
00340
00341
00342
00343
00344
00345
00346
00347
00348
00349
00350
00351
00352
00353
00354
00355
00356 cv::Mat computeCostMatrix(ShapeDescriptors& descriptorsA,
00357 ShapeDescriptors& descriptorsB,
00358 double epsilonCost,
00359 bool write_images,
00360 std::string filePath, std::string filePostFix)
00361 {
00362 int mat_size = std::max(descriptorsA.size(), descriptorsB.size());
00363 cv::Mat cost_matrix(mat_size, mat_size, CV_64FC1, 0.0f);
00364 double d_cost, hi, hj;
00365 ShapeDescriptor& descriptorA = descriptorsA.front();
00366 ShapeDescriptor& descriptorB = descriptorsB.front();
00367
00368
00369 for (int i=0; i < cost_matrix.rows; i++)
00370 {
00371 for (int j=0; j < cost_matrix.cols; j++)
00372 {
00373 cost_matrix.at<double>(i,j) = epsilonCost;
00374 }
00375 }
00376
00377 for (unsigned int i=0; i < descriptorsA.size(); i++)
00378 {
00379 descriptorA = descriptorsA.at(i);
00380 for (unsigned int j=0; j < descriptorsB.size(); j++)
00381 {
00382 descriptorB = descriptorsB.at(j);
00383 d_cost = 0;
00384
00385
00386
00387 for (unsigned int k=0; k < descriptorA.size(); k++)
00388 {
00389 hi = descriptorA.at(k) / (descriptorsA.size() - 1);
00390 hj = descriptorB.at(k) / (descriptorsB.size() - 1);
00391 if (hi + hj > 0)
00392 {
00393 d_cost += pow(hi-hj, 2) / (hi + hj);
00394 }
00395 }
00396 d_cost /= 2;
00397 cost_matrix.at<double>(i,j) = d_cost;
00398 }
00399 }
00400 if (write_images)
00401 {
00402 cv::Mat int_cost_matrix;
00403 cost_matrix.convertTo(int_cost_matrix, CV_8UC1, 255);
00404 std::stringstream cost_matrix_path;
00405 cost_matrix_path << filePath << "/cost_matrix" << filePostFix << ".bmp";
00406 cv::imwrite(cost_matrix_path.str().c_str(), int_cost_matrix);
00407 }
00408
00409
00410
00411 return cost_matrix;
00412 }
00413
00414 double getMinimumCostPath(cv::Mat& cost_matrix, Path& path)
00415 {
00416 const int dim = cost_matrix.rows;
00417 LapCost **cost_mat;
00418 cost_mat = new LapCost*[dim];
00419
00420 for (int r = 0; r < dim; ++r)
00421 {
00422 cost_mat[r] = new LapCost[dim];
00423 }
00424
00425 for (int r = 0; r < dim; ++r)
00426 {
00427 for (int c = 0; c < dim; ++c)
00428 {
00429 cost_mat[r][c] = (float)cost_matrix.at<double>(r,c);
00430 }
00431 }
00432 LapRow* rowsol;
00433 LapCol* colsol;
00434 LapCost* u;
00435 LapCost* v;
00436 rowsol = new LapCol[dim];
00437 colsol = new LapRow[dim];
00438 u = new LapCost[dim];
00439 v = new LapCost[dim];
00440
00441 LapCost match_cost = lap(dim, cost_mat, rowsol, colsol, u, v);
00442
00443 for (int r = 0; r < dim; ++r)
00444 {
00445 int c = rowsol[r];
00446 path.push_back(c);
00447 }
00448
00449 for (int r = 0; r < dim; ++r)
00450 {
00451 delete cost_mat[r];
00452 }
00453 delete cost_mat;
00454 delete u;
00455 delete v;
00456 delete rowsol;
00457 delete colsol;
00458 return match_cost;
00459 }
00460
00461 void displayMatch(cv::Mat& edge_imageA, cv::Mat& edge_imageB,
00462 Samples& samplesA, Samples& samplesB,
00463 Path& path, int max_displacement, std::string filePath,
00464 std::string filePostFix)
00465 {
00466 int a_row_offset = 0;
00467 int b_row_offset = 0;
00468 int a_col_offset = 0;
00469 int b_col_offset = 0;
00470 if (edge_imageA.rows > edge_imageB.rows)
00471 {
00472 b_row_offset = (edge_imageA.rows - edge_imageB.rows)/2;
00473 }
00474 else if (edge_imageA.rows < edge_imageB.rows)
00475 {
00476 a_row_offset = (edge_imageB.rows - edge_imageA.rows)/2;
00477 }
00478 if (edge_imageA.cols > edge_imageB.cols)
00479 {
00480 b_col_offset = (edge_imageA.cols - edge_imageB.cols)/2;
00481 }
00482 else if (edge_imageA.cols < edge_imageB.cols)
00483 {
00484 a_col_offset = (edge_imageB.cols - edge_imageA.cols)/2;
00485 }
00486 cv::Mat disp_img(std::max(edge_imageA.rows, edge_imageB.rows),
00487 std::max(edge_imageA.cols, edge_imageB.cols), CV_8UC3, cv::Scalar(0,0,0));
00488
00489 for (int r=0; r < edge_imageA.rows; r++)
00490 {
00491 for (int c=0; c < edge_imageA.cols; c++)
00492 {
00493 if (edge_imageA.at<uchar>(r,c) > 0)
00494 disp_img.at<cv::Vec3b>(r+a_row_offset, c+a_col_offset) = cv::Vec3b(255,0,0);
00495 }
00496 }
00497
00498
00499 for (int r=0; r < edge_imageB.rows; r++)
00500 {
00501 for (int c=0; c < edge_imageB.cols; c++)
00502 {
00503 if (edge_imageB.at<uchar>(r,c) > 0)
00504 disp_img.at<cv::Vec3b>(r+b_row_offset, c+b_col_offset) = cv::Vec3b(0,0,255);
00505 }
00506 }
00507
00508 for (unsigned int i = 0; i < samplesA.size(); ++i)
00509 {
00510 cv::Point start_point = samplesA[i];
00511 start_point.x += a_col_offset;
00512 start_point.y += a_row_offset;
00513 cv::Point end_point = samplesB[path[i]];
00514 end_point.x += b_col_offset;
00515 end_point.y += b_row_offset;
00516 if (std::abs(start_point.x - end_point.x) +
00517 std::abs(start_point.y - end_point.y) < max_displacement &&
00518 end_point.x > 0 && end_point.x < disp_img.rows &&
00519 end_point.y > 0 && end_point.x < disp_img.cols)
00520 {
00521 cv::line(disp_img, start_point, end_point, cv::Scalar(0,255,0));
00522 }
00523 }
00524 std::stringstream match_path;
00525 match_path << filePath << "/matches" << filePostFix << ".bmp";
00526 cv::imwrite(match_path.str().c_str(), disp_img);
00527 cv::imshow("match", disp_img);
00528 cv::imshow("edgesA", edge_imageA);
00529 cv::imshow("edgesB", edge_imageB);
00530 cv::waitKey();
00531 }
00532 };