void MarkerDetector::recognizeMarkers(const cv::Mat& grayscale, std::vector<Marker>& detectedMarkers) { std::vector<Marker> goodMarkers; // Identify the markers for (size_t i=0;i<detectedMarkers.size();i++) { Marker& marker = detectedMarkers[i]; // Find the perspective transformation that brings current marker to rectangular form cv::Mat markerTransform = cv::getPerspectiveTransform(marker.points, m_markerCorners2d); // Transform image to get a canonical marker image cv::warpPerspective(grayscale, canonicalMarkerImage, markerTransform, markerSize); #ifdef SHOW_DEBUG_IMAGES { cv::Mat markerImage = grayscale.clone(); marker.drawContour(markerImage); cv::Mat markerSubImage = markerImage(cv::boundingRect(marker.points)); cv::showAndSave("Source marker" + ToString(i), markerSubImage); cv::showAndSave("Marker " + ToString(i) + " after warp", canonicalMarkerImage); } #endif int nRotations; int id = Marker::getMarkerId(canonicalMarkerImage, nRotations); if (id !=- 1) { marker.id = id; //sort the points so that they are always in the same order no matter the camera orientation std::rotate(marker.points.begin(), marker.points.begin() + 4 - nRotations, marker.points.end()); goodMarkers.push_back(marker); } } // Refine marker corners using sub pixel accuracy if (goodMarkers.size() > 0) { std::vector<cv::Point2f> preciseCorners(4 * goodMarkers.size()); for (size_t i=0; i<goodMarkers.size(); i++) { const Marker& marker = goodMarkers[i]; for (int c = 0; c <4; c++) { preciseCorners[i*4 + c] = marker.points[c]; } } cv::TermCriteria termCriteria = cv::TermCriteria(cv::TermCriteria::MAX_ITER | cv::TermCriteria::EPS, 30, 0.01); cv::cornerSubPix(grayscale, preciseCorners, cvSize(5,5), cvSize(-1,-1), termCriteria); // Copy refined corners position back to markers for (size_t i=0; i<goodMarkers.size(); i++) { Marker& marker = goodMarkers[i]; for (int c=0;c<4;c++) { marker.points[c] = preciseCorners[i*4 + c]; } } } #if SHOW_DEBUG_IMAGES { cv::Mat markerCornersMat(grayscale.size(), grayscale.type()); markerCornersMat = cv::Scalar(0); for (size_t i=0; i<goodMarkers.size(); i++) { goodMarkers[i].drawContour(markerCornersMat, cv::Scalar(255)); } cv::showAndSave("Markers refined edges", grayscale * 0.5 + markerCornersMat); } #endif detectedMarkers = goodMarkers; }
// This function takes the input list of parallelepipeds and verifies wheter they are markers or not. // The following are the main steps performed: // 1. Removal of the perspective projection to obtain a frontal view of the rectangle area. // 2. Perform a thresholding of the image using the Otsu algorithm (see: Marker::getMarkerId). // 3. Identify the marker internal code: the marker is divided into a 7x7 grid. // The internal 5x5 cells contain ID information, and the rest represent the external black border. void MarkerDetector::detectMarkers(const cv::Mat& grayscale, std::vector<Marker>& detectedMarkers) { std::vector<Marker> goodMarkers; // Identify the markers. for (size_t i = 0; i < detectedMarkers.size(); i++) { Marker& marker = detectedMarkers[i]; // To get the rectangle marker image we have to unwarp the input image using the proper perspective transformation. // This matrix can be retrieved from pairs of corresponding points (marker coords in image space + square marker image coords). // Find the perspective transformation that brings current marker to rectangular form. // See: http://docs.opencv.org/modules/imgproc/doc/geometric_transformations.html#getperspectivetransform cv::Mat markerTransform = cv::getPerspectiveTransform(marker.points, markerCorners2D); // Transform image to get a canonical marker image. // See: http://docs.opencv.org/modules/imgproc/doc/geometric_transformations.html#warpperspective cv::warpPerspective(grayscale, canonicalMarkerImage, markerTransform, markerSize); #ifdef _DEBUG cv::Mat markerImage = grayscale.clone(); marker.drawContour(markerImage); cv::Mat markerSubImage = markerImage(cv::boundingRect(marker.points)); cv::imshow("Source Marker", markerSubImage); cv::imshow("Marker After Warp", canonicalMarkerImage); #endif int nRotations; int id = Marker::getMarkerId(canonicalMarkerImage, nRotations); if (id != -1) { // After finding the right marker orientation, // we rotate the corners of the marker respectively to conform to their order. marker.id = id; // Sort the points so that they are always in the same order no matter the camera orientation. std::rotate(marker.points.begin(), marker.points.begin() + 4 - nRotations, marker.points.end()); goodMarkers.push_back(marker); } } // Refine marker corners using sub pixel accuracy. if (goodMarkers.size() > 0) { // After detecting a marker and decoding its ID, we will refine its corners. // This operation will ease the next step consisting in the estimation of the marker 3D position. // To find the corner location with subpixel accuracy we use the cv::cornerSubPix function: // 1. prepare the input data copying the list of vertices, // 2. then we call cv::cornerSubPix with the proper input arguments, // 3. finally we copy the refined locations back to marker corners. // * We do not use cv::cornerSubPix in the earlier stages of marker detection due to its complexity. // It is very computationally expensive to call this function for large numbers of points. // Therefore we do this only for valid markers. std::vector<cv::Point2f> preciseCorners(4 * goodMarkers.size()); for (size_t i = 0; i < goodMarkers.size(); i++) { const Marker& marker = goodMarkers[i]; for (int c = 0; c < 4; c++) { preciseCorners[i * 4 + c] = marker.points[c]; } } cv::TermCriteria termCriteria = cv::TermCriteria(cv::TermCriteria::MAX_ITER | cv::TermCriteria::EPS, 30, 0.01); cv::cornerSubPix(grayscale, preciseCorners, cv::Size(5, 5), cv::Size(-1, -1), termCriteria); // Copy refined corners position back to markers. for (size_t i = 0; i < goodMarkers.size(); i++) { Marker& marker = goodMarkers[i]; for (int c = 0; c < 4; c++) { marker.points[c] = preciseCorners[i * 4 + c]; } } } #ifdef _DEBUG cv::Mat markerCornersMat(grayscale.size(), grayscale.type()); markerCornersMat = cv::Scalar(0); for (size_t i = 0; i < goodMarkers.size(); i++) { goodMarkers[i].drawContour(markerCornersMat, cv::Scalar(255)); } cv::imshow("Markers Refined Edges", grayscale * 0.5 + markerCornersMat); #endif detectedMarkers = goodMarkers; }