void MarkerDetector::detectMarkers(const cv::Mat& grayscale, std::vector<Marker>& detectedMarkers)
{
  cv::Mat canonicalMarker;

  std::vector<Marker> goodMarkers;
  
  // Identify the markers
  for (size_t i=0;i<detectedMarkers.size();i++)
  {
    Marker& marker = detectedMarkers[i];
    
    // Find the perspective transfomation that brings current marker to rectangular form
    cv::Mat M = cv::getPerspectiveTransform(marker.points, m_markerCorners2d);
  
    // Transform image to get a canonical marker image
    cv::warpPerspective(grayscale, canonicalMarker,  M, markerSize);
        
    int nRotations;
    int id = Marker::getMarkerId(canonicalMarker,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 using subpixel accuracy the corners
  if (goodMarkers.size() > 0)
  {
    std::vector<cv::Point2f> preciseCorners(4 * goodMarkers.size());
    
    for (size_t i=0; i<goodMarkers.size(); i++)
    {  
      Marker& marker = goodMarkers[i];      
      
      for (int c=0;c<4;c++)
      {
        preciseCorners[i*4+c] = marker.points[c];
      }
    }
    
    cv::cornerSubPix(grayscale, preciseCorners, cvSize(5,5), cvSize(-1,-1), cvTermCriteria(CV_TERMCRIT_ITER,30,0.1));
    
    //copy back
    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];
      }      
    }
  }
  
  detectedMarkers = goodMarkers;
}
Ejemplo n.º 2
0
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;
}
Ejemplo n.º 4
0
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);
		cv::Mat markerTransform = cv::findHomography(marker.points, m_markerCorners2d);

        // Transform image to get a canonical marker image
        cv::warpPerspective(grayscale, canonicalMarkerImage, markerTransform, cv::Size(markerLength,markerLength));

		
        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::TermCriteria termCriteria = cv::TermCriteria(cv::TermCriteria::MAX_ITER | cv::TermCriteria::EPS, 3, 0.05);
		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];
            }

        }
    }
	//sort by id
    std::sort ( goodMarkers.begin(),goodMarkers.end() );

	//there might be still the case that a marker is detected twice because of the double border indicated earlier,
    //detect and remove these cases
    std::vector<bool> toRemove ( goodMarkers.size(),false );
    for ( int i=0;i<int ( goodMarkers.size() )-1;i++ )
    {
        if ( goodMarkers[i].id==goodMarkers[i+1].id && !toRemove[i+1] )
        {
			//std::cout<<"remove: "<<goodMarkers[i].id<<std::endl;
            //deletes the one with smaller perimeter
            if ( getPerimeter ( goodMarkers[i].points ) >getPerimeter ( goodMarkers[i+1].points ) ) toRemove[i+1]=true;
            else toRemove[i]=true;
        }
    }

	detectedMarkers.clear();
    for (size_t i=0;i<goodMarkers.size();i++)
    {
        if (!toRemove[i])
            detectedMarkers.push_back(goodMarkers[i]);
    }
  
}