diff --git a/AScannerDarkly/main.cpp b/AScannerDarkly/main.cpp index eb964e4..ee8481a 100644 --- a/AScannerDarkly/main.cpp +++ b/AScannerDarkly/main.cpp @@ -1,11 +1,17 @@ #include +#include const std::string WINDOW_NAME = "A Scanner Darkly"; -const std::string LOWER_THRESHOLD_TRACKBAR_NAME = "Canny: Lower Threshold"; -const std::string UPPER_THRESHOLD_TRACKBAR_NAME = "Canny: Upper Threshold"; + +const std::string CANNY_LOWER_THRESHOLD_TRACKBAR_NAME = "Canny: Lower Threshold"; + +const float CARD_ASPECT_RATIO = 88.0f / 63.0f; +const std::string ASPECT_RATIO_TOLERANCE_TRACKBAR_NAME = "Aspect Ratio Tolerance"; +int g_aspect_ratio_tolerance = 1; +const int MAX_ASPECT_RATIO_TOLERANCE = 100; int g_Canny_lower_threshold = 110; -int g_Canny_upper_threshold = 300; +const int CANNY_UPPER_THRESHOLD = 255; int main(int argc, char** argv ) { cv::VideoCapture cap; @@ -16,22 +22,69 @@ int main(int argc, char** argv ) { return -1; } - cv::namedWindow(WINDOW_NAME); - cv::createTrackbar(LOWER_THRESHOLD_TRACKBAR_NAME, WINDOW_NAME, &g_Canny_lower_threshold, 1000, NULL); - cv::createTrackbar(UPPER_THRESHOLD_TRACKBAR_NAME, WINDOW_NAME, &g_Canny_upper_threshold, 1000, NULL); + std::cout << "Card aspect ratio: " << CARD_ASPECT_RATIO << std::endl; - cv::Mat frame, grayscaleFrame, cannyFrame; + cv::namedWindow(WINDOW_NAME); + + cv::createTrackbar(CANNY_LOWER_THRESHOLD_TRACKBAR_NAME, WINDOW_NAME, &g_Canny_lower_threshold, CANNY_UPPER_THRESHOLD, NULL); + cv::createTrackbar(ASPECT_RATIO_TOLERANCE_TRACKBAR_NAME, WINDOW_NAME, &g_aspect_ratio_tolerance, MAX_ASPECT_RATIO_TOLERANCE, NULL); + + cv::Mat frame, grayscaleFrame, blurFrame, cannyFrame; while (true) { cap >> frame; cv::cvtColor(frame, grayscaleFrame, cv::COLOR_BGR2GRAY); - cv::Canny(grayscaleFrame, cannyFrame, g_Canny_lower_threshold, g_Canny_upper_threshold); + cv::GaussianBlur(grayscaleFrame, blurFrame, cv::Size(5, 5), 2, 2); + cv::Canny(blurFrame, cannyFrame, g_Canny_lower_threshold, CANNY_UPPER_THRESHOLD); - cv::imshow(WINDOW_NAME, cannyFrame); + std::vector > contours; + std::vector hierarchy; + cv::findContours(cannyFrame, contours, hierarchy, cv::RETR_TREE, cv::CHAIN_APPROX_SIMPLE); + + std::vector> hull(contours.size()); + for(size_t i = 0; i < contours.size(); i++) { + cv::convexHull(contours[i], hull[i]); + } + + std::vector minRect(contours.size()); + for(size_t i = 0; i < contours.size(); i++) { + minRect[i] = cv::minAreaRect(contours[i]); + } + + //cv::Scalar contourColor = cv::Scalar(255, 0, 0); + cv::Scalar hullColor = cv::Scalar(0, 255, 0); + cv::Scalar rectangleColor = cv::Scalar(0, 0, 255); + + for (size_t i = 0; i< contours.size(); i++) { + //cv::drawContours(frame, contours, (int)i, contourColor, 2, cv::LINE_8, hierarchy, 0); + //cv::drawContours(frame, hull, (int)i, hullColor, 2, cv::LINE_8); + + // TODO: A purely aspect-ratio-based detection method returns too many false positives + // inside of a card, even after screwing around with various algorithm parameters. + // We have two potential options to fix this: + // - Only take the largest detected RotatedRect with the correct aspect ratio + // - Perform the camera calibration necessary to take accurate real-world measurements + float aspectRatio = minRect[i].size.height / minRect[i].size.width; + float aspectRatioTolerance = g_aspect_ratio_tolerance / 100.0f; + if (aspectRatio < (CARD_ASPECT_RATIO - aspectRatioTolerance) || aspectRatio > (CARD_ASPECT_RATIO + aspectRatioTolerance)) { + continue; + } + + cv::Point2f rect_points[4]; + minRect[i].points(rect_points); + for (int j = 0; j < 4; j++) { + cv::line(frame, rect_points[j], rect_points[(j + 1) % 4], rectangleColor); + } + cv::putText(frame, std::to_string(aspectRatio), minRect[i].center, cv::FONT_HERSHEY_COMPLEX, 1, hullColor); + } + + cv::imshow(WINDOW_NAME, frame); char c = (char)cv::waitKey(33); if (c == 27) { break; + } else if (c == 's') { + cv::imwrite("output.png", frame); } }