#include #include #include #include const std::string SCANNER_WINDOW_NAME = "A Scanner Darkly"; const std::string CARD_WINDOW_NAME = "Detected Card"; 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 = 195; const int CANNY_UPPER_THRESHOLD = 255; cv::Mat cropImageToRoi(cv::Mat img, cv::Rect roi) { return img(roi); } cv::Mat detectCardInFrame(cv::Mat frame) { cv::Mat grayscaleFrame, blurFrame, cannyFrame, cardFrame; // Perform edge detection cv::cvtColor(frame, grayscaleFrame, cv::COLOR_BGR2GRAY); cv::GaussianBlur(grayscaleFrame, blurFrame, cv::Size(5, 5), 2, 2); cv::Canny(blurFrame, cannyFrame, g_Canny_lower_threshold, CANNY_UPPER_THRESHOLD); // Perform contour detection std::vector> contours; std::vector hierarchy; cv::findContours(cannyFrame, contours, hierarchy, cv::RETR_TREE, cv::CHAIN_APPROX_SIMPLE); // Detect convex hulls std::vector> hull(contours.size()); for(size_t i = 0; i < contours.size(); i++) { cv::convexHull(contours[i], hull[i]); } // Detect RotatedRects std::vector minRect(contours.size()); for(size_t i = 0; i < contours.size(); i++) { minRect[i] = cv::minAreaRect(contours[i]); } // Draw lines around RotatedRects /* cv::Scalar rectangleColor(255, 0, 0, 0); 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); } */ // Find possible cards std::vector possibleCards(contours.size()); int possibleCount = 0; for (size_t i = 0; i < minRect.size(); i++) { 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::putText(frame, std::to_string(minRect[i].angle), minRect[i].center, cv::FONT_HERSHEY_SIMPLEX, 1, cv::Scalar(255, 0, 0, 0)); possibleCards[possibleCount] = minRect[i]; possibleCount++; } if (possibleCount == 1) { return cropImageToRoi(frame, possibleCards[0].boundingRect()); } else if (possibleCount > 1) { // If we have more than one possible match, take the largest one int largestCardIndex = 0; int largestCardSize = 0; for (size_t i = 0; i < possibleCards.size(); i++) { float size = possibleCards[i].size.width * possibleCards[i].size.height; if (size >= largestCardSize) { largestCardIndex = i; largestCardSize = size; } } // Some basic error checking to ensure the RotatedRect is roughly card-sized if (largestCardSize >= 14400 && largestCardSize <= 200000) { return cropImageToRoi(frame, possibleCards[largestCardIndex].boundingRect()); } } return cv::Mat(); } const char* readTextFromImage(cv::InputArray img) { const char* tempFilename = "temp.bmp"; // Initialize Tesseract api tesseract::TessBaseAPI* tess = new tesseract::TessBaseAPI(); tess->Init(NULL, "eng"); tess->SetPageSegMode(tesseract::PSM_SPARSE_TEXT); // Load image into Tesseract cv::imwrite(tempFilename, img); Pix* pixd = pixRead(tempFilename); tess->SetImage(pixd); tess->Recognize(0); // Perform OCR const char* out = tess->GetUTF8Text(); // Cleanup pixDestroy(&pixd); std::remove(tempFilename); return out; } int main(int argc, char** argv ) { cv::VideoCapture cap; cap.open(0); if (!cap.isOpened()) { std::cerr << "Couldn't open capture" << std::endl; return -1; } cv::namedWindow(SCANNER_WINDOW_NAME); cv::createTrackbar(CANNY_LOWER_THRESHOLD_TRACKBAR_NAME, SCANNER_WINDOW_NAME, &g_Canny_lower_threshold, CANNY_UPPER_THRESHOLD, NULL); cv::createTrackbar(ASPECT_RATIO_TOLERANCE_TRACKBAR_NAME, SCANNER_WINDOW_NAME, &g_aspect_ratio_tolerance, MAX_ASPECT_RATIO_TOLERANCE, NULL); cv::Mat frame, cardFrame; while (true) { cap >> frame; //cardFrame = detectCardInFrame(frame); cv::imshow(SCANNER_WINDOW_NAME, frame); /* if (cardFrame.size().width > 0) { printf("Card detected\n"); while (true) { cv::imshow(CARD_WINDOW_NAME, cardFrame); char c = (char)cv::waitKey(33); if (c == 27) { cv::destroyWindow(CARD_WINDOW_NAME); break; } } } */ char c = (char)cv::waitKey(33); if (c == 27) { break; } } }