Basically, SIFT / SURF cannot determine if two images are similar or not. He can only tell you which key point in one image corresponds to that which corresponds to the point of another image.
Fortunately, the two functions also provide you with additional information, such as the "distance" from the match and the total number of matches in the image.
My idea of how similar both images are:
1. Use findHomography() to get the homography of two images 2. Check whether the homography is valid (such as 4 of the points form a rectangle). 3. Count the number of "good" matches (match.distance < SOME_DISTANCE) 4. Use some math to generate a score 5. If the previous homography is valid, increase the score
Here is a piece of code that I once used, but it has some drawbacks (for some specific types of matches, the estimate is unfounded, but this situation is rare).
Please note: MY_****_DISTANCE depends on whether you use SIFT or SURF, and the parameters of the SIFT / SURF functions that you give.
//===========SCORE ============ vector<double> matchDistance; double avg = 0; int avgCount = 0; int goodCount = 0 ; for( unsigned i = 0; i < matches.size(); i++ ) { double dist = matches[i].distance; //This is a "average match" if( dist < MY_AVG_DISTANCE && dist > MY_LEAST_DISTANCE ) { avg += dist; //Count the average of distance of "average matches" avgCount++; } //This is a good match, and number of good match have a great impact on the result //Good matches are also average matches, that is, {GOOD_MATCH} is a subset of {AVERAGE_MATCH} if(dist < MY_GOOD_DISTANCE && dist > MY_LEAST_DISTANCE ){ goodCount++; //Count the number of "good matches" } } if(avgCount > 6){ avg /= avgCount; //Gives the average value if(goodCount < 12){ //If there are too few good matches, make a punishment avg = avg + (12-goodCount) * 4; } }else{ avg = MY_MAX_DISTANCE; } avg = avg > MY_AVG_DISTANCE ? MY_AVG_DISTANCE : avg; avg = avg < MY_MIN_DISTANCE ? MY_MIN_DISTANCE : avg; double score_avg = (MY_AVG_DISTANCE - avg) / ( MY_AVG_DISTANCE - MY_MIN_DISTANCE ) * 100; if(homography_valid){ //If the previous homography is valid, make a reward. score_avg += 40; score_avg = score_avg > 100 ? 100 : score_avg; }else{ score_avg -= 5; //Or, make a little punishment score_avg = score_avg < 0 ? 0 : score_avg; } return score_avg
Update
My _ **** _ DISTANCE
MY_****_DISTANCE is what I defined myself, sorry for not explaining it before. This is the value that I use, but you can change it to better match your code.
#define MY_MIN_DISTANCE 200 #define MY_GOOD_DISTANCE 310 #define MY_AVG_DISTANCE 350 #define MY_MAX_DISTANCE 500 #define MY_MIN_HESSIAN 2000 #define MY_LEAST_DISTANCE 100
When starting SIFT with SiftFeatureDetector detector(400, 3, 0.04, 10.0, 1.6); parameters SiftFeatureDetector detector(400, 3, 0.04, 10.0, 1.6); the result of some "wrong" match compared to some "right" match:
Wrong Right 307.993 330.124 470.419 307.374 219.775 371.026 294.389 400.696 355.321 259.239 331.926 189.042 222.317 457.089 320.718 379.061 423.09 201.95 371.098 200.646 362.427 343.229 441.167 359.32 253.253 382.15 367.191 215.678 405.19 358.686 390.251 343.798 341.905 238.002 341.073 226.519 363.775 262.5 340.742 174.877 320.214 415.802 249.405 195.261 347.357 328.76 343.839 116.331 351.058 383.93 286.224 111.472 352.976 138.701 298.409 238.044 385.34 223.716 264.571 331.115 333.339 208.103 329.588 128.168 372.971 267.83 331.804 222.578 301.935 232.459 351.504 342.524 300.762 379.87 346.872 390.031 374.281 308.198 304.746 401.452 307.184 193.298 229.943 98.0714 286.163 133.978 363.634 171.415 361.656 111.077 357.108 134.186 289.712 123.199 371.496 339.944 318.708 192.164 360.547 425.937 331.225 336.535 297.688 309.419 351.898 162.296 408.206 311.055 309.023 457.352 281.375 337.529 362.266 407.757 229.295 388.567 317.005 161.118 386.907 108.936 363.942 215.311 374.832 343.376 311.264 184.318 364.745 188.963 466.795 308.48 381.667 318.828 381.826 119.591 377.338 105.527 377.333 199.206 279.228 369.394 295.078 387.979 267.408 196.942 386.063 307.815 372.14 184.83 294.927 417.138 348.458 97.8621 234.199 144.094 172.377 131.412 250.503 227.139 233.32 116.258 205.331 354.505 95.0368 108.434 116.46 138.246 406.135 308.2 92.817 194.838 312.103 323.163 312.946 377.798 359.393 396.191 320.272 375.025 309.383 280.826 278.456
Obviously, the average value of the “incorrect” match is greater than the average value of the “correct” match. But if you look at a lot of photos, you will get some exceptions (for example, an incorrect match with fewer matches, but a smaller average distance), some of which I did not understand how to fix. Thus, based on the source data of a comparable distance, you should also make a rather "personal" account.
findHomography
Here is a demo fragment that you may need.
std::vector<Point2f> obj_corners(4); obj_corners[0] = Point(0,0); obj_corners[1] = Point( img_object.cols, 0 ); obj_corners[2] = Point( img_object.cols, img_object.rows ); obj_corners[3] = Point( 0, img_object.rows ); std::vector<Point2f> scene_corners(4); Mat H = findHomography( obj, scene, RANSAC ); perspectiveTransform( obj_corners, scene_corners, H);
And you can find detailed information in this lesson.
Update # 2
Check_Valid_Homography
It depends on how you define "valid." Look at the official example, the image on the left (image "object") is part of the image on the right (image "scene"). Then apply the homography matrix to the vertex vectors of the image “object”, finally, we get a green rectangle on the right, which marks where the “object” image is located.
As I, on the basis of the textbook, my definition of "valid": 1. Four points form a quadrangle 2. None of the sides of the side of the quadrangle should be too small 3. None of the corners in the quadrangles should be too small. 4. The quadrangle should not be up and down.
And, performing some basic math operations, here is one part of the verification process.
bool RectChecker::check_order(const vector<Point2f> &corners, int height, int width){ if(corners[0].y + 5 >= corners[3].y || corners[1].y + 5 >= corners[2].y){ //Point 0 should be above Point 3, so do Point1 and Point2 return false; } if(corners[0].x + 5 >= corners[1].x || corners[3].x + 5 >= corners[2].x){ //Point 0 should be on the left of Point 1, so do Point3 and Point2 return false; } int cnt = 0; for(int i = 0 ; i < corners.size() ; i++){ if(corners[i].x < -30 || corners[i].x > width + 30){ cnt++; continue; } if(corners[i].y < -20 || corners[i].y > height + 20){ //This point is outside the image cnt++; } } if(cnt == 4){ //All points are outside the image return false; } return true; }
Once again, you can create your own verification process, and as for verification, it depends a lot on your requirements. But, in my opinion, this part is the simplest, while the most important part is the scoring process.