Bài này viết theo câu hỏi của 1 bạn trong group https://www.facebook.com/groups/thigiacmaytinh/. Sau khi thử nghiệm SURF thấy kết quả tương đối tốt nên mình viết bài này. Tìm đồng xu trong ảnh giống như các phương pháp tìm vật thể 2D khác như logo.
Phân tích yêu cầu
Đây là bộ ảnh của 1 bạn đang học ở châu Âu gửi cho mình. Trong đó gồm nhiều loại khác nhau, góc xoay khác nhau, lại có kích cỡ và màu sắc khác nhau. Đặc biệt cùng 1 hình dạng nhưng lại có màu sắc khác nhau. Do đó loại bỏ phương án tìm theo kích cỡ và màu sắc.
Đồng xu trong này chỉ xoay tròn nên vẫn là vật thể 2D, do đó không cần sử dụng đến Cascade. Các phương pháp như SURF, SIFT cũng đủ để tìm kiếm.
Trong trường hợp có 2 đồng xu trong ảnh, hoặc đồng xu giống nhau sẽ làm sai lệch. Do đó nên tách riêng từng đồng xu để tìm bằng Floodfill vì background màu trắng
Kết quả tìm kiếm
Code
Thuật toán này nhận 2 tham số: ảnh cần tìm & ảnh chứa đối tượng. Sau khi tìm sẽ vẽ keypoints và vị trí của đối tượng cần tìm.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
void SURFmatching(cv::Mat matScene, cv::Mat matObject) { //-- Step 1: Detect the keypoints and extract descriptors using SURF int minHessian = 400; cv::Ptr<SURF> detector = SURF::create(minHessian); std::vector<cv::KeyPoint> keypoints_object, keypoints_scene; cv::Mat descriptors_object, descriptors_scene; detector->detectAndCompute(matObject, cv::Mat(), keypoints_object, descriptors_object); detector->detectAndCompute(matScene, cv::Mat(), keypoints_scene, descriptors_scene); //-- Step 2: cv::Matching descriptor vectors using FLANN cv::Matcher cv::FlannBasedMatcher Matcher; std::vector< cv::DMatch > Matches; Matcher.match(descriptors_object, descriptors_scene, Matches); double max_dist = 0; double min_dist = 100; //-- Quick calculation of max and min distances between keypoints for (int i = 0; i < descriptors_object.rows; i++) { double dist = Matches[i].distance; if (dist < min_dist) min_dist = dist; if (dist > max_dist) max_dist = dist; } printf("-- Max dist : %f \n", max_dist); printf("-- Min dist : %f \n", min_dist); //-- Draw only "good" cv::Matches (i.e. whose distance is less than 3*min_dist ) std::vector< cv::DMatch > good_Matches; for (int i = 0; i < descriptors_object.rows; i++) { if (Matches[i].distance < 3 * min_dist) { good_Matches.push_back(Matches[i]); } } cv::Mat img_Matches; cv::drawMatches(matObject, keypoints_object, matScene, keypoints_scene, good_Matches, img_Matches, cv::Scalar::all(-1), cv::Scalar::all(-1), std::vector<char>(), cv::DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS); //-- Localize the object std::vector<cv::Point2f> obj; std::vector<cv::Point2f> scene; for (size_t i = 0; i < good_Matches.size(); i++) { //-- Get the keypoints from the good cv::Matches obj.push_back(keypoints_object[good_Matches[i].queryIdx].pt); scene.push_back(keypoints_scene[good_Matches[i].trainIdx].pt); } cv::Mat H = cv::findHomography(obj, scene, cv::RANSAC); //-- Get the corners from the image_1 ( the object to be "detected" ) std::vector<cv::Point2f> obj_corners(4); obj_corners[0] = cvPoint(0, 0); obj_corners[1] = cvPoint(matObject.cols, 0); obj_corners[2] = cvPoint(matObject.cols, matObject.rows); obj_corners[3] = cvPoint(0, matObject.rows); std::vector<cv::Point2f> scene_corners(4); cv::perspectiveTransform(obj_corners, scene_corners, H); //-- Draw lines between the corners (the mapped object in the scene - image_2 ) cv::line(img_Matches, scene_corners[0] + cv::Point2f(matObject.cols, 0), scene_corners[1] + cv::Point2f(matObject.cols, 0), cv::Scalar(0, 255, 0), 4); cv::line(img_Matches, scene_corners[1] + cv::Point2f(matObject.cols, 0), scene_corners[2] + cv::Point2f(matObject.cols, 0), cv::Scalar(0, 255, 0), 4); cv::line(img_Matches, scene_corners[2] + cv::Point2f(matObject.cols, 0), scene_corners[3] + cv::Point2f(matObject.cols, 0), cv::Scalar(0, 255, 0), 4); cv::line(img_Matches, scene_corners[3] + cv::Point2f(matObject.cols, 0), scene_corners[0] + cv::Point2f(matObject.cols, 0), cv::Scalar(0, 255, 0), 4); //-- Show detected cv::Matches img_Matches = ResizeByHeight(img_Matches, 600); cv::imshow("Good cv::Matches & Object detection", img_Matches); } |
Download full source code (GitHub)
Các bạn chạy Detection2D.sln để xem kết quả