mirror of
https://github.com/opencv/opencv.git
synced 2026-07-30 07:43:03 +04:00
Merge pull request #28804 from asmorkalov:as/calib_boards_migration
Migrated chessboard and circles grid detectors to objdetect #28804 OpenCV Contrib: https://github.com/opencv/opencv_contrib/pull/4125 OpenCV Extra: https://github.com/opencv/opencv_extra/pull/1375 ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [ ] The PR is proposed to the proper branch - [ ] There is a reference to the original bug report and related work - [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [ ] The feature is well documented and sample code can be built with the project CMake
This commit is contained in:
committed by
GitHub
parent
dafe7cef97
commit
e19b0ebc5c
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,211 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// Intel License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2000, Intel Corporation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of Intel Corporation may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "precomp.hpp"
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
namespace cv {
|
||||
|
||||
using namespace std;
|
||||
|
||||
static void icvGetQuadrangleHypotheses(const std::vector<std::vector< cv::Point > > & contours, const std::vector< cv::Vec4i > & hierarchy, std::vector<std::pair<float, int> >& quads, int class_id)
|
||||
{
|
||||
const float min_aspect_ratio = 0.3f;
|
||||
const float max_aspect_ratio = 3.0f;
|
||||
const float min_box_size = 10.0f;
|
||||
|
||||
for (size_t i = 0; i < contours.size(); ++i)
|
||||
{
|
||||
if (hierarchy.at(i)[3] != -1)
|
||||
continue; // skip holes
|
||||
|
||||
const std::vector< cv::Point > & c = contours[i];
|
||||
cv::RotatedRect box = cv::minAreaRect(c);
|
||||
|
||||
float box_size = MAX(box.size.width, box.size.height);
|
||||
if(box_size < min_box_size)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
float aspect_ratio = box.size.width/MAX(box.size.height, 1);
|
||||
if(aspect_ratio < min_aspect_ratio || aspect_ratio > max_aspect_ratio)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
quads.emplace_back(box_size, class_id);
|
||||
}
|
||||
}
|
||||
|
||||
static void countClasses(const std::vector<std::pair<float, int> >& pairs, size_t idx1, size_t idx2, std::vector<int>& counts)
|
||||
{
|
||||
counts.assign(2, 0);
|
||||
for(size_t i = idx1; i != idx2; i++)
|
||||
{
|
||||
counts[pairs[i].second]++;
|
||||
}
|
||||
}
|
||||
|
||||
inline bool less_pred(const std::pair<float, int>& p1, const std::pair<float, int>& p2)
|
||||
{
|
||||
return p1.first < p2.first;
|
||||
}
|
||||
|
||||
static void fillQuads(Mat & white, Mat & black, double white_thresh, double black_thresh, vector<pair<float, int> > & quads)
|
||||
{
|
||||
Mat thresh;
|
||||
{
|
||||
vector< vector<Point> > contours;
|
||||
vector< Vec4i > hierarchy;
|
||||
threshold(white, thresh, white_thresh, 255, THRESH_BINARY);
|
||||
findContours(thresh, contours, hierarchy, RETR_CCOMP, CHAIN_APPROX_SIMPLE);
|
||||
icvGetQuadrangleHypotheses(contours, hierarchy, quads, 1);
|
||||
}
|
||||
|
||||
{
|
||||
vector< vector<Point> > contours;
|
||||
vector< Vec4i > hierarchy;
|
||||
threshold(black, thresh, black_thresh, 255, THRESH_BINARY_INV);
|
||||
findContours(thresh, contours, hierarchy, RETR_CCOMP, CHAIN_APPROX_SIMPLE);
|
||||
icvGetQuadrangleHypotheses(contours, hierarchy, quads, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static bool checkQuads(vector<pair<float, int> > & quads, const cv::Size & size)
|
||||
{
|
||||
const size_t min_quads_count = size.width*size.height/2;
|
||||
std::sort(quads.begin(), quads.end(), less_pred);
|
||||
|
||||
// now check if there are many hypotheses with similar sizes
|
||||
// do this by floodfill-style algorithm
|
||||
const float size_rel_dev = 0.4f;
|
||||
|
||||
for(size_t i = 0; i < quads.size(); i++)
|
||||
{
|
||||
size_t j = i + 1;
|
||||
for(; j < quads.size(); j++)
|
||||
{
|
||||
if(quads[j].first/quads[i].first > 1.0f + size_rel_dev)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(j + 1 > min_quads_count + i)
|
||||
{
|
||||
// check the number of black and white squares
|
||||
std::vector<int> counts;
|
||||
countClasses(quads, i, j, counts);
|
||||
const int black_count = cvRound(ceil(size.width/2.0)*ceil(size.height/2.0));
|
||||
const int white_count = cvRound(floor(size.width/2.0)*floor(size.height/2.0));
|
||||
if(counts[0] < black_count*0.75 ||
|
||||
counts[1] < white_count*0.75)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool checkChessboard(InputArray _img, Size size)
|
||||
{
|
||||
Mat img = _img.getMat();
|
||||
CV_Assert(img.channels() == 1 && img.depth() == CV_8U);
|
||||
|
||||
const int erosion_count = 1;
|
||||
const float black_level = 20.f;
|
||||
const float white_level = 130.f;
|
||||
const float black_white_gap = 70.f;
|
||||
|
||||
Mat white;
|
||||
Mat black;
|
||||
erode(img, white, Mat(), Point(-1, -1), erosion_count);
|
||||
dilate(img, black, Mat(), Point(-1, -1), erosion_count);
|
||||
|
||||
bool result = false;
|
||||
for(float thresh_level = black_level; thresh_level < white_level && !result; thresh_level += 20.0f)
|
||||
{
|
||||
vector<pair<float, int> > quads;
|
||||
fillQuads(white, black, thresh_level + black_white_gap, thresh_level, quads);
|
||||
if (checkQuads(quads, size))
|
||||
result = true;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// does a fast check if a chessboard is in the input image. This is a workaround to
|
||||
// a problem of cvFindChessboardCorners being slow on images with no chessboard
|
||||
// - src: input binary image
|
||||
// - size: chessboard size
|
||||
// Returns 1 if a chessboard can be in this image and findChessboardCorners should be called,
|
||||
// 0 if there is no chessboard, -1 in case of error
|
||||
int checkChessboardBinary(const cv::Mat & img, const cv::Size & size)
|
||||
{
|
||||
CV_Assert(img.channels() == 1 && img.depth() == CV_8U);
|
||||
|
||||
Mat white = img.clone();
|
||||
Mat black = img.clone();
|
||||
|
||||
int result = 0;
|
||||
for ( int erosion_count = 0; erosion_count <= 3; erosion_count++ )
|
||||
{
|
||||
if ( 1 == result )
|
||||
break;
|
||||
|
||||
if ( 0 != erosion_count ) // first iteration keeps original images
|
||||
{
|
||||
erode(white, white, Mat(), Point(-1, -1), 1);
|
||||
dilate(black, black, Mat(), Point(-1, -1), 1);
|
||||
}
|
||||
|
||||
vector<pair<float, int> > quads;
|
||||
fillQuads(white, black, 128, 128, quads);
|
||||
if (checkQuads(quads, size))
|
||||
result = 1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,872 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#ifndef CHESSBOARD_HPP_
|
||||
#define CHESSBOARD_HPP_
|
||||
|
||||
#include "opencv2/core.hpp"
|
||||
#include "opencv2/features.hpp"
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <map>
|
||||
|
||||
namespace cv {
|
||||
namespace details{
|
||||
/**
|
||||
* \brief Fast point sysmetric cross detector based on a localized radon transformation
|
||||
*/
|
||||
class FastX : public cv::Feature2D
|
||||
{
|
||||
public:
|
||||
struct Parameters
|
||||
{
|
||||
float strength; //!< minimal strength of a valid junction in dB
|
||||
float resolution; //!< angle resolution in radians
|
||||
int branches; //!< the number of branches
|
||||
int min_scale; //!< scale level [0..8]
|
||||
int max_scale; //!< scale level [0..8]
|
||||
bool filter; //!< post filter feature map to improve impulse response
|
||||
bool super_resolution; //!< up-sample
|
||||
|
||||
Parameters()
|
||||
{
|
||||
strength = 40;
|
||||
resolution = float(CV_PI*0.25);
|
||||
branches = 2;
|
||||
min_scale = 2;
|
||||
max_scale = 5;
|
||||
super_resolution = true;
|
||||
filter = true;
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
FastX(const Parameters &config = Parameters());
|
||||
virtual ~FastX(){}
|
||||
|
||||
void reconfigure(const Parameters ¶);
|
||||
|
||||
//declaration to be wrapped by rbind
|
||||
void detect(cv::InputArray image,std::vector<cv::KeyPoint>& keypoints, cv::InputArray mask=cv::Mat())override
|
||||
{cv::Feature2D::detect(image.getMat(),keypoints,mask.getMat());}
|
||||
|
||||
virtual void detectAndCompute(cv::InputArray image,
|
||||
cv::InputArray mask,
|
||||
std::vector<cv::KeyPoint>& keypoints,
|
||||
cv::OutputArray descriptors,
|
||||
bool useProvidedKeyPoints = false)override;
|
||||
|
||||
void detectImpl(const cv::Mat& image,
|
||||
std::vector<cv::KeyPoint>& keypoints,
|
||||
std::vector<cv::Mat> &feature_maps,
|
||||
const cv::Mat& mask=cv::Mat())const;
|
||||
|
||||
void detectImpl(const cv::Mat& image,
|
||||
std::vector<cv::Mat> &rotated_images,
|
||||
std::vector<cv::Mat> &feature_maps,
|
||||
const cv::Mat& mask=cv::Mat())const;
|
||||
|
||||
void findKeyPoints(const std::vector<cv::Mat> &feature_map,
|
||||
std::vector<cv::KeyPoint>& keypoints,
|
||||
const cv::Mat& mask = cv::Mat())const;
|
||||
|
||||
std::vector<std::vector<float> > calcAngles(const std::vector<cv::Mat> &rotated_images,
|
||||
std::vector<cv::KeyPoint> &keypoints)const;
|
||||
// define pure virtual methods
|
||||
virtual int descriptorSize()const override{return 0;}
|
||||
virtual int descriptorType()const override{return 0;}
|
||||
virtual void operator()( cv::InputArray image, cv::InputArray mask, std::vector<cv::KeyPoint>& keypoints, cv::OutputArray descriptors, bool useProvidedKeypoints=false )const
|
||||
{
|
||||
descriptors.clear();
|
||||
detectImpl(image.getMat(),keypoints,mask);
|
||||
if(!useProvidedKeypoints) // suppress compiler warning
|
||||
return;
|
||||
return;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void computeImpl( const cv::Mat& image, std::vector<cv::KeyPoint>& keypoints, cv::Mat& descriptors)const
|
||||
{
|
||||
descriptors = cv::Mat();
|
||||
detectImpl(image,keypoints);
|
||||
}
|
||||
|
||||
private:
|
||||
void detectImpl(const cv::Mat& _src, std::vector<cv::KeyPoint>& keypoints, const cv::Mat& mask)const;
|
||||
virtual void detectImpl(cv::InputArray image, std::vector<cv::KeyPoint>& keypoints, cv::InputArray mask=cv::noArray())const;
|
||||
|
||||
void rotate(float angle,cv::InputArray img,cv::Size size,cv::OutputArray out)const;
|
||||
void calcFeatureMap(const cv::Mat &images,cv::Mat& out)const;
|
||||
|
||||
private:
|
||||
Parameters parameters;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Ellipse class
|
||||
*/
|
||||
class Ellipse
|
||||
{
|
||||
public:
|
||||
Ellipse();
|
||||
Ellipse(const cv::Point2f ¢er, const cv::Size2f &axes, float angle);
|
||||
|
||||
void draw(cv::InputOutputArray img,const cv::Scalar &color = cv::Scalar::all(120))const;
|
||||
bool contains(const cv::Point2f &pt)const;
|
||||
cv::Point2f getCenter()const;
|
||||
const cv::Size2f &getAxes()const;
|
||||
|
||||
private:
|
||||
cv::Point2f center;
|
||||
cv::Size2f axes;
|
||||
float angle,cosf,sinf;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Chessboard corner detector
|
||||
*
|
||||
* The detectors tries to find all chessboard corners of an imaged
|
||||
* chessboard and returns them as an ordered vector of KeyPoints.
|
||||
* Thereby, the left top corner has index 0 and the bottom right
|
||||
* corner n*m-1.
|
||||
*/
|
||||
class Chessboard: public cv::Feature2D
|
||||
{
|
||||
public:
|
||||
static const int DUMMY_FIELD_SIZE = 100; // in pixel
|
||||
|
||||
/**
|
||||
* \brief Configuration of a chessboard corner detector
|
||||
*
|
||||
*/
|
||||
struct Parameters
|
||||
{
|
||||
cv::Size chessboard_size; //!< size of the chessboard
|
||||
int min_scale; //!< scale level [0..8]
|
||||
int max_scale; //!< scale level [0..8]
|
||||
int max_points; //!< maximal number of points regarded
|
||||
int max_tests; //!< maximal number of tested hypothesis
|
||||
bool super_resolution; //!< use super-repsolution for chessboard detection
|
||||
bool larger; //!< indicates if larger boards should be returned
|
||||
bool marker; //!< indicates that valid boards must have a white and black circle marker used for orientation
|
||||
|
||||
Parameters()
|
||||
{
|
||||
chessboard_size = cv::Size(9,6);
|
||||
min_scale = 3;
|
||||
max_scale = 4;
|
||||
super_resolution = true;
|
||||
max_points = 200;
|
||||
max_tests = 50;
|
||||
larger = false;
|
||||
marker = false;
|
||||
}
|
||||
|
||||
Parameters(int scale,int _max_points):
|
||||
min_scale(scale),
|
||||
max_scale(scale),
|
||||
max_points(_max_points)
|
||||
{
|
||||
chessboard_size = cv::Size(9,6);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \brief Gets the 3D objects points for the chessboard assuming the
|
||||
* left top corner is located at the origin.
|
||||
*
|
||||
* \param[in] pattern_size Number of rows and cols of the pattern
|
||||
* \param[in] cell_size Size of one cell
|
||||
*
|
||||
* \returns Returns the object points as CV_32FC3
|
||||
*/
|
||||
static cv::Mat getObjectPoints(const cv::Size &pattern_size,float cell_size);
|
||||
|
||||
/**
|
||||
* \brief Class for searching and storing chessboard corners.
|
||||
*
|
||||
* The search is based on a feature map having strong pixel
|
||||
* values at positions where a chessboard corner is located.
|
||||
*
|
||||
* The board must be rectangular but supports empty cells
|
||||
*
|
||||
*/
|
||||
class Board
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* \brief Estimates the position of the next point on a line using cross ratio constrain
|
||||
*
|
||||
* cross ratio:
|
||||
* d12/d34 = d13/d24
|
||||
*
|
||||
* point order on the line:
|
||||
* p0 --> p1 --> p2 --> p3
|
||||
*
|
||||
* \param[in] p0 First point coordinate
|
||||
* \param[in] p1 Second point coordinate
|
||||
* \param[in] p2 Third point coordinate
|
||||
* \param[out] p3 Forth point coordinate
|
||||
*
|
||||
*/
|
||||
static bool estimatePoint(const cv::Point2f &p0,const cv::Point2f &p1,const cv::Point2f &p2,cv::Point2f &p3);
|
||||
|
||||
// using 1D homography
|
||||
static bool estimatePoint(const cv::Point2f &p0,const cv::Point2f &p1,const cv::Point2f &p2,const cv::Point2f &p3, cv::Point2f &p4);
|
||||
|
||||
/**
|
||||
* \brief Checks if all points of a row or column have a valid cross ratio constraint
|
||||
*
|
||||
* cross ratio:
|
||||
* d12/d34 = d13/d24
|
||||
*
|
||||
* point order on the row/column:
|
||||
* pt1 --> pt2 --> pt3 --> pt4
|
||||
*
|
||||
* \param[in] points THe points of the row/column
|
||||
*
|
||||
*/
|
||||
static bool checkRowColumn(const std::vector<cv::Point2f> &points);
|
||||
|
||||
/**
|
||||
* \brief Estimates the search area for the next point on the line using cross ratio
|
||||
*
|
||||
* point order on the line:
|
||||
* (p0) --> p1 --> p2 --> p3 --> search area
|
||||
*
|
||||
* \param[in] p1 First point coordinate
|
||||
* \param[in] p2 Second point coordinate
|
||||
* \param[in] p3 Third point coordinate
|
||||
* \param[in] p Percentage of d34 used for the search area width and height [0..1]
|
||||
* \param[out] ellipse The search area
|
||||
* \param[in] p0 optional point to improve accuracy
|
||||
*
|
||||
* \return Returns false if no search area can be calculated
|
||||
*
|
||||
*/
|
||||
static bool estimateSearchArea(const cv::Point2f &p1,const cv::Point2f &p2,const cv::Point2f &p3,float p,
|
||||
Ellipse &ellipse,const cv::Point2f *p0 =NULL);
|
||||
|
||||
/**
|
||||
* \brief Estimates the search area for a specific point based on the given homography
|
||||
*
|
||||
* \param[in] H homography describing the transformation from ideal board to real one
|
||||
* \param[in] row Row of the point
|
||||
* \param[in] col Col of the point
|
||||
* \param[in] p Percentage [0..1]
|
||||
*
|
||||
* \return Returns false if no search area can be calculated
|
||||
*
|
||||
*/
|
||||
static Ellipse estimateSearchArea(cv::Mat H,int row, int col,float p,int field_size = DUMMY_FIELD_SIZE);
|
||||
|
||||
/**
|
||||
* \brief Searches for the maximum in a given search area
|
||||
*
|
||||
* \param[in] map feature map
|
||||
* \param[in] ellipse search area
|
||||
* \param[in] min_val Minimum value of the maximum to be accepted as maximum
|
||||
*
|
||||
* \return Returns a negative value if all points are outside the ellipse
|
||||
*
|
||||
*/
|
||||
static float findMaxPoint(cv::flann::Index &index,const cv::Mat &data,const Ellipse &ellipse,float white_angle,float black_angle,cv::Point2f &pt);
|
||||
|
||||
/**
|
||||
* \brief Searches for the next point using cross ratio constrain
|
||||
*
|
||||
* \param[in] index flann index
|
||||
* \param[in] data extended flann data
|
||||
* \param[in] pt1
|
||||
* \param[in] pt2
|
||||
* \param[in] pt3
|
||||
* \param[in] white_angle
|
||||
* \param[in] black_angle
|
||||
* \param[in] min_response
|
||||
* \param[out] point The resulting point
|
||||
*
|
||||
* \return Returns false if no point could be found
|
||||
*
|
||||
*/
|
||||
static bool findNextPoint(cv::flann::Index &index,const cv::Mat &data,
|
||||
const cv::Point2f &pt1,const cv::Point2f &pt2, const cv::Point2f &pt3,
|
||||
float white_angle,float black_angle,float min_response,cv::Point2f &point);
|
||||
|
||||
/**
|
||||
* \brief Creates a new Board object
|
||||
*
|
||||
*/
|
||||
Board(float white_angle=0,float black_angle=0);
|
||||
Board(const cv::Size &size, const std::vector<cv::Point2f> &points,float white_angle=0,float black_angle=0);
|
||||
Board(const Chessboard::Board &other);
|
||||
virtual ~Board();
|
||||
|
||||
Board& operator=(const Chessboard::Board &other);
|
||||
|
||||
/**
|
||||
* \brief Draws the corners into the given image
|
||||
*
|
||||
* \param[in] m The image
|
||||
* \param[out] out The resulting image
|
||||
* \param[in] H optional homography to calculate search area
|
||||
*
|
||||
*/
|
||||
void draw(cv::InputArray m,cv::OutputArray out,cv::InputArray H=cv::Mat())const;
|
||||
|
||||
/**
|
||||
* \brief Estimates the pose of the chessboard
|
||||
*
|
||||
*/
|
||||
bool estimatePose(const cv::Size2f &real_size,cv::InputArray _K,cv::OutputArray rvec,cv::OutputArray tvec)const;
|
||||
|
||||
/**
|
||||
* \brief Clears all internal data of the object
|
||||
*
|
||||
*/
|
||||
void clear();
|
||||
|
||||
/**
|
||||
* \brief Returns the angle of the black diagnonale
|
||||
*
|
||||
*/
|
||||
float getBlackAngle()const;
|
||||
|
||||
/**
|
||||
* \brief Returns the angle of the black diagnonale
|
||||
*
|
||||
*/
|
||||
float getWhiteAngle()const;
|
||||
|
||||
/**
|
||||
* \brief Initializes a 3x3 grid from 9 corner coordinates
|
||||
*
|
||||
* All points must be ordered:
|
||||
* p0 p1 p2
|
||||
* p3 p4 p5
|
||||
* p6 p7 p8
|
||||
*
|
||||
* \param[in] points vector of points
|
||||
*
|
||||
* \return Returns false if the grid could not be initialized
|
||||
*/
|
||||
bool init(const std::vector<cv::Point2f> points);
|
||||
|
||||
/**
|
||||
* \brief Returns true if the board is empty
|
||||
*
|
||||
*/
|
||||
bool isEmpty() const;
|
||||
|
||||
/**
|
||||
* \brief Returns all board corners as ordered vector
|
||||
*
|
||||
* The left top corner has index 0 and the bottom right
|
||||
* corner rows*cols-1. All corners which only belong to
|
||||
* empty cells are returned as NaN.
|
||||
*/
|
||||
std::vector<cv::Point2f> getCorners(bool ball=true) const;
|
||||
|
||||
/**
|
||||
* \brief Returns all board corners as ordered vector of KeyPoints
|
||||
*
|
||||
* The left top corner has index 0 and the bottom right
|
||||
* corner rows*cols-1.
|
||||
*
|
||||
* \param[in] ball if set to false only non empty points are returned
|
||||
*
|
||||
*/
|
||||
std::vector<cv::KeyPoint> getKeyPoints(bool ball=true) const;
|
||||
|
||||
/**
|
||||
* \brief Returns the centers of the chessboard cells
|
||||
*
|
||||
* The left top corner has index 0 and the bottom right
|
||||
* corner (rows-1)*(cols-1)-1.
|
||||
*
|
||||
*/
|
||||
std::vector<cv::Point2f> getCellCenters() const;
|
||||
|
||||
/**
|
||||
* \brief Returns all cells as mats of four points each describing their corners.
|
||||
*
|
||||
* The left top cell has index 0
|
||||
*
|
||||
*/
|
||||
std::vector<cv::Mat> getCells(float shrink_factor = 1.0,bool bwhite=true,bool bblack = true) const;
|
||||
|
||||
/**
|
||||
* \brief Estimates the homography between an ideal board
|
||||
* and reality based on the already recovered points
|
||||
*
|
||||
* \param[in] rect selecting a subset of the already recovered points
|
||||
* \param[in] field_size The field size of the ideal board
|
||||
*
|
||||
*/
|
||||
cv::Mat estimateHomography(cv::Rect rect,int field_size = DUMMY_FIELD_SIZE)const;
|
||||
|
||||
/**
|
||||
* \brief Estimates the homography between an ideal board
|
||||
* and reality based on the already recovered points
|
||||
*
|
||||
* \param[in] field_size The field size of the ideal board
|
||||
*
|
||||
*/
|
||||
cv::Mat estimateHomography(int field_size = DUMMY_FIELD_SIZE)const;
|
||||
|
||||
/**
|
||||
* \brief Warp image to match ideal checkerboard
|
||||
*
|
||||
*/
|
||||
cv::Mat warpImage(cv::InputArray image)const;
|
||||
|
||||
/**
|
||||
* \brief Returns the size of the board
|
||||
*
|
||||
*/
|
||||
cv::Size getSize() const;
|
||||
|
||||
/**
|
||||
* \brief Returns the number of cols
|
||||
*
|
||||
*/
|
||||
size_t colCount() const;
|
||||
|
||||
/**
|
||||
* \brief Returns the number of rows
|
||||
*
|
||||
*/
|
||||
size_t rowCount() const;
|
||||
|
||||
/**
|
||||
* \brief Returns the inner contour of the board including only valid corners
|
||||
*
|
||||
* \info the contour might be non squared if not all points of the board are defined
|
||||
*
|
||||
*/
|
||||
std::vector<cv::Point2f> getContour()const;
|
||||
|
||||
/**
|
||||
* \brief Masks the found board in the given image
|
||||
*
|
||||
*/
|
||||
void maskImage(cv::InputOutputArray img,const cv::Scalar &color=cv::Scalar::all(0))const;
|
||||
|
||||
/**
|
||||
* \brief Grows the board in all direction until no more corners are found in the feature map
|
||||
*
|
||||
* \param[in] data CV_32FC1 data of the flann index
|
||||
* \param[in] flann_index flann index
|
||||
*
|
||||
* \returns the number of grows
|
||||
*/
|
||||
int grow(const cv::Mat &data,cv::flann::Index &flann_index);
|
||||
|
||||
/**
|
||||
* \brief Validates all corners using guided search based on the given homography
|
||||
*
|
||||
* \param[in] data CV_32FC1 data of the flann index
|
||||
* \param[in] flann_index flann index
|
||||
* \param[in] h Homography describing the transformation from ideal board to the real one
|
||||
* \param[in] min_response Min response
|
||||
*
|
||||
* \returns the number of valid corners
|
||||
*/
|
||||
int validateCorners(const cv::Mat &data,cv::flann::Index &flann_index,const cv::Mat &h,float min_response=0);
|
||||
|
||||
/**
|
||||
* \brief check that no corner is used more than once
|
||||
*
|
||||
* \returns Returns false if a corner is used more than once
|
||||
*/
|
||||
bool checkUnique()const;
|
||||
|
||||
/**
|
||||
* \brief Returns false if the angles of the contour are smaller than 35°
|
||||
*
|
||||
*/
|
||||
bool validateContour()const;
|
||||
|
||||
|
||||
/**
|
||||
\brief delete left column of the board
|
||||
*/
|
||||
bool shrinkLeft();
|
||||
|
||||
/**
|
||||
\brief delete right column of the board
|
||||
*/
|
||||
bool shrinkRight();
|
||||
|
||||
/**
|
||||
\brief shrink first row of the board
|
||||
*/
|
||||
bool shrinkTop();
|
||||
|
||||
/**
|
||||
\brief delete last row of the board
|
||||
*/
|
||||
bool shrinkBottom();
|
||||
|
||||
/**
|
||||
* \brief Grows the board to the left by adding one column.
|
||||
*
|
||||
* \param[in] map CV_32FC1 feature map
|
||||
*
|
||||
* \returns Returns false if the feature map has no maxima at the requested positions
|
||||
*/
|
||||
bool growLeft(const cv::Mat &map,cv::flann::Index &flann_index);
|
||||
void growLeft();
|
||||
|
||||
/**
|
||||
* \brief Grows the board to the top by adding one row.
|
||||
*
|
||||
* \param[in] map CV_32FC1 feature map
|
||||
*
|
||||
* \returns Returns false if the feature map has no maxima at the requested positions
|
||||
*/
|
||||
bool growTop(const cv::Mat &map,cv::flann::Index &flann_index);
|
||||
void growTop();
|
||||
|
||||
/**
|
||||
* \brief Grows the board to the right by adding one column.
|
||||
*
|
||||
* \param[in] map CV_32FC1 feature map
|
||||
*
|
||||
* \returns Returns false if the feature map has no maxima at the requested positions
|
||||
*/
|
||||
bool growRight(const cv::Mat &map,cv::flann::Index &flann_index);
|
||||
void growRight();
|
||||
|
||||
/**
|
||||
* \brief Grows the board to the bottom by adding one row.
|
||||
*
|
||||
* \param[in] map CV_32FC1 feature map
|
||||
*
|
||||
* \returns Returns false if the feature map has no maxima at the requested positions
|
||||
*/
|
||||
bool growBottom(const cv::Mat &map,cv::flann::Index &flann_index);
|
||||
void growBottom();
|
||||
|
||||
/**
|
||||
* \brief Adds one column on the left side
|
||||
*
|
||||
* \param[in] points The corner coordinates
|
||||
*
|
||||
*/
|
||||
void addColumnLeft(const std::vector<cv::Point2f> &points);
|
||||
|
||||
/**
|
||||
* \brief Adds one column at the top
|
||||
*
|
||||
* \param[in] points The corner coordinates
|
||||
*
|
||||
*/
|
||||
void addRowTop(const std::vector<cv::Point2f> &points);
|
||||
|
||||
/**
|
||||
* \brief Adds one column on the right side
|
||||
*
|
||||
* \param[in] points The corner coordinates
|
||||
*
|
||||
*/
|
||||
void addColumnRight(const std::vector<cv::Point2f> &points);
|
||||
|
||||
/**
|
||||
* \brief Adds one row at the bottom
|
||||
*
|
||||
* \param[in] points The corner coordinates
|
||||
*
|
||||
*/
|
||||
void addRowBottom(const std::vector<cv::Point2f> &points);
|
||||
|
||||
/**
|
||||
* \brief Rotates the board 90° degrees to the left
|
||||
*/
|
||||
void rotateLeft();
|
||||
|
||||
/**
|
||||
* \brief Rotates the board 90° degrees to the right
|
||||
*/
|
||||
void rotateRight();
|
||||
|
||||
/**
|
||||
* \brief Flips the board along its local x(width) coordinate direction
|
||||
*/
|
||||
void flipVertical();
|
||||
|
||||
/**
|
||||
* \brief Flips the board along its local y(height) coordinate direction
|
||||
*/
|
||||
void flipHorizontal();
|
||||
|
||||
/**
|
||||
* \brief Flips and rotates the board so that the angle of
|
||||
* either the black or white diagonal is bigger than the x
|
||||
* and y axis of the board and from a right handed
|
||||
* coordinate system
|
||||
*/
|
||||
void normalizeOrientation(bool bblack=true);
|
||||
|
||||
/**
|
||||
* \brief Flips and rotates the board so that the marker
|
||||
* is normalized
|
||||
*/
|
||||
bool normalizeMarkerOrientation();
|
||||
|
||||
/**
|
||||
* \brief Exchanges the stored board with the board stored in other
|
||||
*/
|
||||
void swap(Chessboard::Board &other);
|
||||
|
||||
bool operator==(const Chessboard::Board& other) const {return rows*cols == other.rows*other.cols;}
|
||||
bool operator< (const Chessboard::Board& other) const {return rows*cols < other.rows*other.cols;}
|
||||
bool operator> (const Chessboard::Board& other) const {return rows*cols > other.rows*other.cols;}
|
||||
bool operator>= (const cv::Size& size)const { return rows*cols >= size.width*size.height; }
|
||||
|
||||
/**
|
||||
* \brief Returns a specific corner
|
||||
*
|
||||
* \info raises runtime_error if row col does not exists
|
||||
*/
|
||||
cv::Point2f& getCorner(int row,int col);
|
||||
|
||||
/**
|
||||
* \brief Returns true if the cell is empty meaning at least one corner is NaN
|
||||
*/
|
||||
bool isCellEmpty(int row,int col);
|
||||
|
||||
/**
|
||||
* \brief Returns the mapping from all corners idx to only valid corners idx
|
||||
*/
|
||||
std::map<int,int> getMapping()const;
|
||||
|
||||
/**
|
||||
* \brief Returns true if the cell is black
|
||||
*
|
||||
*/
|
||||
bool isCellBlack(int row,int col)const;
|
||||
|
||||
/**
|
||||
* \brief Returns true if the cell has a round marker at its
|
||||
* center
|
||||
*
|
||||
*/
|
||||
bool hasCellMarker(int row,int col);
|
||||
|
||||
/**
|
||||
* \brief Detects round markers in the chessboard fields based
|
||||
* on the given image and the already recoverd board corners
|
||||
*
|
||||
* \returns Returns the number of found markes
|
||||
*
|
||||
*/
|
||||
int detectMarkers(cv::InputArray image);
|
||||
|
||||
/**
|
||||
* \brief Calculates the average edge sharpness for the chessboard
|
||||
*
|
||||
* \param[in] image The image where the chessboard was detected
|
||||
* \param[in] rise_distance Rise distance 0.8 means 10% ... 90%
|
||||
* \param[in] vertical by default only edge response for horiontal lines are calculated
|
||||
*
|
||||
* \returns Scalar(sharpness, average min_val, average max_val)
|
||||
*
|
||||
* \author aduda@krakenrobotik.de
|
||||
*/
|
||||
cv::Scalar calcEdgeSharpness(cv::InputArray image,float rise_distance=0.8,bool vertical=false,cv::OutputArray sharpness=cv::noArray());
|
||||
|
||||
|
||||
/**
|
||||
* \brief Gets the 3D objects points for the chessboard
|
||||
* assuming the left top corner is located at the origin. In
|
||||
* case the board as a marker, the white marker cell is at position zero
|
||||
*
|
||||
* \param[in] cell_size Size of one cell
|
||||
*
|
||||
* \returns Returns the object points as CV_32FC3
|
||||
*/
|
||||
cv::Mat getObjectPoints(float cell_size)const;
|
||||
|
||||
|
||||
/**
|
||||
* \brief Returns the angle the board is rotated agains the x-axis of the image plane
|
||||
* \returns Returns the object points as CV_32FC3
|
||||
*/
|
||||
float getAngle()const;
|
||||
|
||||
/**
|
||||
* \brief Returns true if the main direction of the board is close to the image x-axis than y-axis
|
||||
*/
|
||||
bool isHorizontal()const;
|
||||
|
||||
/**
|
||||
* \brief Updates the search angles
|
||||
*/
|
||||
void setAngles(float white,float black);
|
||||
|
||||
private:
|
||||
// stores one cell
|
||||
// in general a cell is initialized by the Board so that:
|
||||
// * all corners are always pointing to a valid cv::Point2f
|
||||
// * depending on the position left,top,right and bottom might be set to NaN
|
||||
// * A cell is empty if at least one corner is NaN
|
||||
struct Cell
|
||||
{
|
||||
cv::Point2f *top_left,*top_right,*bottom_right,*bottom_left; // corners
|
||||
Cell *left,*top,*right,*bottom; // neighbouring cells
|
||||
bool black; // set to true if cell is black
|
||||
bool marker; // set to true if cell has a round marker in its center
|
||||
Cell();
|
||||
bool empty()const; // indicates if the cell is empty (one of its corners has NaN)
|
||||
int getRow()const;
|
||||
int getCol()const;
|
||||
cv::Point2f getCenter()const;
|
||||
bool isInside(const cv::Point2f &pt)const; // check if point is inside the cell
|
||||
};
|
||||
|
||||
// corners
|
||||
enum CornerIndex
|
||||
{
|
||||
TOP_LEFT,
|
||||
TOP_RIGHT,
|
||||
BOTTOM_RIGHT,
|
||||
BOTTOM_LEFT
|
||||
};
|
||||
|
||||
Cell* getCell(int row,int column); // returns a specific cell
|
||||
const Cell* getCell(int row,int column)const; // returns a specific cell
|
||||
void drawEllipses(const std::vector<Ellipse> &ellipses);
|
||||
|
||||
// Iterator for iterating over board corners
|
||||
class PointIter
|
||||
{
|
||||
public:
|
||||
PointIter(Cell *cell,CornerIndex corner_index);
|
||||
PointIter(const PointIter &other);
|
||||
void operator=(const PointIter &other);
|
||||
bool valid() const; // returns if the pointer is pointing to a cell
|
||||
|
||||
bool left(bool check_empty=false); // moves one corner to the left or returns false
|
||||
bool right(bool check_empty=false); // moves one corner to the right or returns false
|
||||
bool bottom(bool check_empty=false); // moves one corner to the bottom or returns false
|
||||
bool top(bool check_empty=false); // moves one corner to the top or returns false
|
||||
bool checkCorner()const; // returns true if the current corner belongs to at least one
|
||||
// none empty cell
|
||||
bool isNaN()const; // returns true if the current corner is NaN
|
||||
|
||||
const cv::Point2f* operator*() const; // current corner coordinate
|
||||
cv::Point2f* operator*(); // current corner coordinate
|
||||
const cv::Point2f* operator->() const; // current corner coordinate
|
||||
cv::Point2f* operator->(); // current corner coordinate
|
||||
|
||||
Cell *getCell(); // current cell
|
||||
private:
|
||||
CornerIndex corner_index;
|
||||
Cell *cell;
|
||||
};
|
||||
|
||||
std::vector<Cell*> cells; // storage for all board cells
|
||||
std::vector<cv::Point2f*> corners; // storage for all corners
|
||||
Cell *top_left; // pointer to the top left corner of the board in its local coordinate system
|
||||
int rows; // number of inner pattern rows
|
||||
int cols; // number of inner pattern cols
|
||||
float white_angle,black_angle;
|
||||
};
|
||||
public:
|
||||
|
||||
/**
|
||||
* \brief Creates a chessboard corner detectors
|
||||
*
|
||||
* \param[in] config Configuration used to detect chessboard corners
|
||||
*
|
||||
*/
|
||||
Chessboard(const Parameters &config = Parameters());
|
||||
virtual ~Chessboard();
|
||||
void reconfigure(const Parameters &config = Parameters());
|
||||
Parameters getPara()const;
|
||||
|
||||
/*
|
||||
* \brief Detects chessboard corners in the given image.
|
||||
*
|
||||
* The detectors tries to find all chessboard corners of an imaged
|
||||
* chessboard and returns them as an ordered vector of KeyPoints.
|
||||
* Thereby, the left top corner has index 0 and the bottom right
|
||||
* corner n*m-1.
|
||||
*
|
||||
* \param[in] image The image
|
||||
* \param[out] keypoints The detected corners as a vector of ordered KeyPoints
|
||||
* \param[in] mask Currently not supported
|
||||
*
|
||||
*/
|
||||
void detect(cv::InputArray image,std::vector<cv::KeyPoint>& keypoints, cv::InputArray mask=cv::Mat())override
|
||||
{cv::Feature2D::detect(image.getMat(),keypoints,mask.getMat());}
|
||||
|
||||
virtual void detectAndCompute(cv::InputArray image,cv::InputArray mask, std::vector<cv::KeyPoint>& keypoints,cv::OutputArray descriptors,
|
||||
bool useProvidedKeyPoints = false)override;
|
||||
|
||||
/*
|
||||
* \brief Detects chessboard corners in the given image.
|
||||
*
|
||||
* The detectors tries to find all chessboard corners of an imaged
|
||||
* chessboard and returns them as an ordered vector of KeyPoints.
|
||||
* Thereby, the left top corner has index 0 and the bottom right
|
||||
* corner n*m-1.
|
||||
*
|
||||
* \param[in] image The image
|
||||
* \param[out] keypoints The detected corners as a vector of ordered KeyPoints
|
||||
* \param[out] feature_maps The feature map generated by LRJT and used to find the corners
|
||||
* \param[in] mask Currently not supported
|
||||
*
|
||||
*/
|
||||
void detectImpl(const cv::Mat& image, std::vector<cv::KeyPoint>& keypoints,std::vector<cv::Mat> &feature_maps,const cv::Mat& mask)const;
|
||||
Chessboard::Board detectImpl(const cv::Mat& image,std::vector<cv::Mat> &feature_maps,const cv::Mat& mask)const;
|
||||
|
||||
// define pure virtual methods
|
||||
virtual int descriptorSize()const override{return 0;}
|
||||
virtual int descriptorType()const override{return 0;}
|
||||
virtual void operator()( cv::InputArray image, cv::InputArray mask, std::vector<cv::KeyPoint>& keypoints, cv::OutputArray descriptors, bool useProvidedKeypoints=false )const
|
||||
{
|
||||
descriptors.clear();
|
||||
detectImpl(image.getMat(),keypoints,mask);
|
||||
if(!useProvidedKeypoints) // suppress compiler warning
|
||||
return;
|
||||
return;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void computeImpl( const cv::Mat& image, std::vector<cv::KeyPoint>& keypoints, cv::Mat& descriptors)const
|
||||
{
|
||||
descriptors = cv::Mat();
|
||||
detectImpl(image,keypoints);
|
||||
}
|
||||
|
||||
// indicates why a board could not be initialized for a certain keypoint
|
||||
enum BState
|
||||
{
|
||||
MISSING_POINTS = 0, // at least 5 points are needed
|
||||
MISSING_PAIRS = 1, // at least two pairs are needed
|
||||
WRONG_PAIR_ANGLE = 2, // angle between pairs is too small
|
||||
WRONG_CONFIGURATION = 3, // point configuration is wrong and does not belong to a board
|
||||
FOUND_BOARD = 4 // board was found
|
||||
};
|
||||
|
||||
void findKeyPoints(const cv::Mat& image, std::vector<cv::KeyPoint>& keypoints,std::vector<cv::Mat> &feature_maps,
|
||||
std::vector<std::vector<float> > &angles ,const cv::Mat& mask)const;
|
||||
cv::Mat buildData(const std::vector<cv::KeyPoint>& keypoints)const;
|
||||
std::vector<cv::KeyPoint> getInitialPoints(cv::flann::Index &flann_index,const cv::Mat &data,const cv::KeyPoint ¢er,float white_angle,float black_angle, float min_response = 0)const;
|
||||
BState generateBoards(cv::flann::Index &flann_index,const cv::Mat &data, const cv::KeyPoint ¢er,
|
||||
float white_angle,float black_angle,float min_response,const cv::Mat &img,
|
||||
std::vector<Chessboard::Board> &boards)const;
|
||||
|
||||
private:
|
||||
void detectImpl(const cv::Mat&,std::vector<cv::KeyPoint>&, const cv::Mat& mast =cv::Mat())const;
|
||||
virtual void detectImpl(cv::InputArray image, std::vector<cv::KeyPoint>& keypoints, cv::InputArray mask=cv::noArray())const;
|
||||
|
||||
private:
|
||||
Parameters parameters; // storing the configuration of the detector
|
||||
};
|
||||
}} // end namespace details and cv
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,199 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
|
||||
// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#ifndef CIRCLESGRID_HPP_
|
||||
#define CIRCLESGRID_HPP_
|
||||
|
||||
#include <fstream>
|
||||
#include <set>
|
||||
#include <list>
|
||||
#include <numeric>
|
||||
#include <map>
|
||||
|
||||
namespace cv {
|
||||
|
||||
class CirclesGridClusterFinder
|
||||
{
|
||||
CirclesGridClusterFinder& operator=(const CirclesGridClusterFinder&);
|
||||
CirclesGridClusterFinder(const CirclesGridClusterFinder&);
|
||||
public:
|
||||
CirclesGridClusterFinder(const CirclesGridFinderParameters ¶meters)
|
||||
{
|
||||
isAsymmetricGrid = parameters.gridType == CirclesGridFinderParameters::ASYMMETRIC_GRID;
|
||||
squareSize = parameters.squareSize;
|
||||
maxRectifiedDistance = parameters.maxRectifiedDistance;
|
||||
}
|
||||
void findGrid(const std::vector<Point2f> &points, Size patternSize, std::vector<Point2f>& centers);
|
||||
|
||||
//cluster 2d points by geometric coordinates
|
||||
void hierarchicalClustering(const std::vector<Point2f> &points, const Size &patternSize, std::vector<Point2f> &patternPoints);
|
||||
private:
|
||||
void findCorners(const std::vector<Point2f> &hull2f, std::vector<Point2f> &corners);
|
||||
void findOutsideCorners(const std::vector<Point2f> &corners, std::vector<Point2f> &outsideCorners);
|
||||
void getSortedCorners(const std::vector<Point2f> &hull2f, const std::vector<Point2f> &patternPoints, const std::vector<Point2f> &corners, const std::vector<Point2f> &outsideCorners, std::vector<Point2f> &sortedCorners);
|
||||
void rectifyPatternPoints(const std::vector<Point2f> &patternPoints, const std::vector<Point2f> &sortedCorners, std::vector<Point2f> &rectifiedPatternPoints);
|
||||
void parsePatternPoints(const std::vector<Point2f> &patternPoints, const std::vector<Point2f> &rectifiedPatternPoints, std::vector<Point2f> ¢ers);
|
||||
|
||||
float squareSize, maxRectifiedDistance;
|
||||
bool isAsymmetricGrid;
|
||||
|
||||
Size patternSize;
|
||||
};
|
||||
|
||||
class Graph
|
||||
{
|
||||
public:
|
||||
typedef std::set<size_t> Neighbors;
|
||||
struct Vertex
|
||||
{
|
||||
Neighbors neighbors;
|
||||
};
|
||||
typedef std::map<size_t, Vertex> Vertices;
|
||||
|
||||
Graph(size_t n);
|
||||
void addVertex(size_t id);
|
||||
void addEdge(size_t id1, size_t id2);
|
||||
void removeEdge(size_t id1, size_t id2);
|
||||
bool doesVertexExist(size_t id) const;
|
||||
bool areVerticesAdjacent(size_t id1, size_t id2) const;
|
||||
size_t getVerticesCount() const;
|
||||
size_t getDegree(size_t id) const;
|
||||
const Neighbors& getNeighbors(size_t id) const;
|
||||
void floydWarshall(Mat &distanceMatrix, int infinity = -1) const;
|
||||
private:
|
||||
Vertices vertices;
|
||||
};
|
||||
|
||||
struct Path
|
||||
{
|
||||
int firstVertex;
|
||||
int lastVertex;
|
||||
int length;
|
||||
|
||||
std::vector<size_t> vertices;
|
||||
|
||||
Path(int first = -1, int last = -1, int len = -1)
|
||||
{
|
||||
firstVertex = first;
|
||||
lastVertex = last;
|
||||
length = len;
|
||||
}
|
||||
};
|
||||
|
||||
class CirclesGridFinder
|
||||
{
|
||||
public:
|
||||
CirclesGridFinder(Size patternSize, const std::vector<Point2f> &testKeypoints,
|
||||
const CirclesGridFinderParameters ¶meters = CirclesGridFinderParameters());
|
||||
bool findHoles();
|
||||
static Mat rectifyGrid(Size detectedGridSize, const std::vector<Point2f>& centers, const std::vector<
|
||||
Point2f> &keypoint, std::vector<Point2f> &warpedKeypoints);
|
||||
|
||||
void getHoles(std::vector<Point2f> &holes) const;
|
||||
void getAsymmetricHoles(std::vector<Point2f> &holes) const;
|
||||
Size getDetectedGridSize() const;
|
||||
|
||||
void drawBasis(const std::vector<Point2f> &basis, Point2f origin, Mat &drawImg) const;
|
||||
void drawBasisGraphs(const std::vector<Graph> &basisGraphs, Mat &drawImg, bool drawEdges = true,
|
||||
bool drawVertices = true) const;
|
||||
void drawHoles(const Mat &srcImage, Mat &drawImage) const;
|
||||
private:
|
||||
void computeRNG(Graph &rng, std::vector<Point2f> &vectors, Mat *drawImage = 0) const;
|
||||
void rng2gridGraph(Graph &rng, std::vector<Point2f> &vectors) const;
|
||||
void eraseUsedGraph(std::vector<Graph> &basisGraphs) const;
|
||||
void filterOutliersByDensity(const std::vector<Point2f> &samples, std::vector<Point2f> &filteredSamples);
|
||||
void findBasis(const std::vector<Point2f> &samples, std::vector<Point2f> &basis,
|
||||
std::vector<Graph> &basisGraphs);
|
||||
void findMCS(const std::vector<Point2f> &basis, std::vector<Graph> &basisGraphs);
|
||||
size_t findLongestPath(std::vector<Graph> &basisGraphs, Path &bestPath);
|
||||
float computeGraphConfidence(const std::vector<Graph> &basisGraphs, bool addRow, const std::vector<size_t> &points,
|
||||
const std::vector<size_t> &seeds);
|
||||
void addHolesByGraph(const std::vector<Graph> &basisGraphs, bool addRow, Point2f basisVec);
|
||||
|
||||
size_t findNearestKeypoint(Point2f pt) const;
|
||||
void addPoint(Point2f pt, std::vector<size_t> &points);
|
||||
void findCandidateLine(std::vector<size_t> &line, size_t seedLineIdx, bool addRow, Point2f basisVec, std::vector<
|
||||
size_t> &seeds);
|
||||
void findCandidateHoles(std::vector<size_t> &above, std::vector<size_t> &below, bool addRow, Point2f basisVec,
|
||||
std::vector<size_t> &aboveSeeds, std::vector<size_t> &belowSeeds);
|
||||
static bool areCentersNew(const std::vector<size_t> &newCenters, const std::vector<std::vector<size_t> > &holes);
|
||||
bool isDetectionCorrect();
|
||||
|
||||
static void insertWinner(float aboveConfidence, float belowConfidence, float minConfidence, bool addRow,
|
||||
const std::vector<size_t> &above, const std::vector<size_t> &below, std::vector<std::vector<
|
||||
size_t> > &holes);
|
||||
|
||||
struct Segment
|
||||
{
|
||||
Point2f s;
|
||||
Point2f e;
|
||||
Segment(Point2f _s, Point2f _e);
|
||||
};
|
||||
|
||||
//if endpoint is on a segment then function return false
|
||||
static bool areSegmentsIntersecting(Segment seg1, Segment seg2);
|
||||
static bool doesIntersectionExist(const std::vector<Segment> &corner, const std::vector<std::vector<Segment> > &segments);
|
||||
void getCornerSegments(const std::vector<std::vector<size_t> > &points, std::vector<std::vector<Segment> > &segments,
|
||||
std::vector<Point> &cornerIndices, std::vector<Point> &firstSteps,
|
||||
std::vector<Point> &secondSteps) const;
|
||||
size_t getFirstCorner(std::vector<Point> &largeCornerIndices, std::vector<Point> &smallCornerIndices,
|
||||
std::vector<Point> &firstSteps, std::vector<Point> &secondSteps) const;
|
||||
static double getDirection(Point2f p1, Point2f p2, Point2f p3);
|
||||
|
||||
std::vector<Point2f> keypoints;
|
||||
|
||||
std::vector<std::vector<size_t> > holes;
|
||||
std::vector<std::vector<size_t> > holes2;
|
||||
std::vector<std::vector<size_t> > *largeHoles;
|
||||
std::vector<std::vector<size_t> > *smallHoles;
|
||||
|
||||
const Size_<size_t> patternSize;
|
||||
CirclesGridFinderParameters parameters;
|
||||
bool rotatedGrid = false;
|
||||
|
||||
CirclesGridFinder& operator=(const CirclesGridFinder&);
|
||||
CirclesGridFinder(const CirclesGridFinder&);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* CIRCLESGRID_HPP_ */
|
||||
@@ -48,6 +48,7 @@
|
||||
#include "opencv2/objdetect/barcode.hpp"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
|
||||
#include <opencv2/core/utils/logger.hpp>
|
||||
#include "opencv2/core/utility.hpp"
|
||||
#include "opencv2/core/ocl.hpp"
|
||||
#include "opencv2/core/private.hpp"
|
||||
@@ -56,4 +57,11 @@
|
||||
#include <array>
|
||||
#include <vector>
|
||||
|
||||
namespace cv {
|
||||
|
||||
int checkChessboardBinary(const Mat & img, const Size & size);
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,240 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// Intel License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2000, Intel Corporation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of Intel Corporation may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "precomp.hpp"
|
||||
|
||||
#include <limits>
|
||||
#include <utility>
|
||||
#include <algorithm>
|
||||
|
||||
#include <math.h>
|
||||
|
||||
namespace cv {
|
||||
|
||||
inline bool is_smaller(const std::pair<int, float>& p1, const std::pair<int, float>& p2)
|
||||
{
|
||||
return p1.second < p2.second;
|
||||
}
|
||||
|
||||
static void orderContours(const std::vector<std::vector<Point> >& contours, Point2f point, std::vector<std::pair<int, float> >& order)
|
||||
{
|
||||
order.clear();
|
||||
size_t i, j, n = contours.size();
|
||||
for(i = 0; i < n; i++)
|
||||
{
|
||||
size_t ni = contours[i].size();
|
||||
float min_dist = std::numeric_limits<float>::max();
|
||||
for(j = 0; j < ni; j++)
|
||||
{
|
||||
double dist = norm(Point2f((float)contours[i][j].x, (float)contours[i][j].y) - point);
|
||||
min_dist = (float)MIN((double)min_dist, dist);
|
||||
}
|
||||
order.push_back(std::pair<int, float>((int)i, min_dist));
|
||||
}
|
||||
|
||||
std::sort(order.begin(), order.end(), is_smaller);
|
||||
}
|
||||
|
||||
// fit second order curve to a set of 2D points
|
||||
inline void fitCurve2Order(const std::vector<Point2f>& /*points*/, std::vector<float>& /*curve*/)
|
||||
{
|
||||
// TBD
|
||||
}
|
||||
|
||||
inline void findCurvesCross(const std::vector<float>& /*curve1*/, const std::vector<float>& /*curve2*/, Point2f& /*cross_point*/)
|
||||
{
|
||||
}
|
||||
|
||||
static void findLinesCrossPoint(Point2f origin1, Point2f dir1, Point2f origin2, Point2f dir2, Point2f& cross_point)
|
||||
{
|
||||
float det = dir2.x*dir1.y - dir2.y*dir1.x;
|
||||
Point2f offset = origin2 - origin1;
|
||||
|
||||
float alpha = (dir2.x*offset.y - dir2.y*offset.x)/det;
|
||||
cross_point = origin1 + dir1*alpha;
|
||||
}
|
||||
|
||||
static void findCorner(const std::vector<Point2f>& contour, Point2f point, Point2f& corner)
|
||||
{
|
||||
// find the nearest point
|
||||
double min_dist = std::numeric_limits<double>::max();
|
||||
int min_idx = -1;
|
||||
|
||||
// find corner idx
|
||||
for(size_t i = 0; i < contour.size(); i++)
|
||||
{
|
||||
double dist = norm(contour[i] - point);
|
||||
if(dist < min_dist)
|
||||
{
|
||||
min_dist = dist;
|
||||
min_idx = (int)i;
|
||||
}
|
||||
}
|
||||
CV_Assert(min_idx >= 0);
|
||||
|
||||
// temporary solution, have to make something more precise
|
||||
corner = contour[min_idx];
|
||||
return;
|
||||
}
|
||||
|
||||
static int segment_hist_max(const Mat& hist, int& low_thresh, int& high_thresh)
|
||||
{
|
||||
Mat bw;
|
||||
double total_sum = sum(hist).val[0];
|
||||
|
||||
double quantile_sum = 0.0;
|
||||
//double min_quantile = 0.2;
|
||||
double low_sum = 0;
|
||||
double max_segment_length = 0;
|
||||
int max_start_x = -1;
|
||||
int max_end_x = -1;
|
||||
int start_x = 0;
|
||||
const double out_of_bells_fraction = 0.1;
|
||||
for(int x = 0; x < hist.size[0]; x++)
|
||||
{
|
||||
quantile_sum += hist.at<float>(x);
|
||||
if(quantile_sum < 0.2*total_sum) continue;
|
||||
|
||||
if(quantile_sum - low_sum > out_of_bells_fraction*total_sum)
|
||||
{
|
||||
if(max_segment_length < x - start_x)
|
||||
{
|
||||
max_segment_length = x - start_x;
|
||||
max_start_x = start_x;
|
||||
max_end_x = x;
|
||||
}
|
||||
|
||||
low_sum = quantile_sum;
|
||||
start_x = x;
|
||||
}
|
||||
}
|
||||
|
||||
if(start_x == -1)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
low_thresh = cvRound(max_start_x + 0.25*(max_end_x - max_start_x));
|
||||
high_thresh = cvRound(max_start_x + 0.75*(max_end_x - max_start_x));
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
bool find4QuadCornerSubpix(InputArray _img, InputOutputArray _corners, Size region_size)
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
Mat img = _img.getMat(), cornersM = _corners.getMat();
|
||||
int ncorners = cornersM.checkVector(2, CV_32F);
|
||||
CV_Assert( ncorners >= 0 );
|
||||
Point2f* corners = cornersM.ptr<Point2f>();
|
||||
const int nbins = 256;
|
||||
float ranges[] = {0, 256};
|
||||
const float* _ranges = ranges;
|
||||
Mat hist;
|
||||
|
||||
Mat black_comp, white_comp;
|
||||
for(int i = 0; i < ncorners; i++)
|
||||
{
|
||||
int channels = 0;
|
||||
Rect roi(cvRound(corners[i].x - region_size.width), cvRound(corners[i].y - region_size.height),
|
||||
region_size.width*2 + 1, region_size.height*2 + 1);
|
||||
Mat img_roi = img(roi);
|
||||
calcHist(&img_roi, 1, &channels, Mat(), hist, 1, &nbins, &_ranges);
|
||||
|
||||
int black_thresh = 0, white_thresh = 0;
|
||||
segment_hist_max(hist, black_thresh, white_thresh);
|
||||
|
||||
threshold(img, black_comp, black_thresh, 255.0, THRESH_BINARY_INV);
|
||||
threshold(img, white_comp, white_thresh, 255.0, THRESH_BINARY);
|
||||
|
||||
const int erode_count = 1;
|
||||
erode(black_comp, black_comp, Mat(), Point(-1, -1), erode_count);
|
||||
erode(white_comp, white_comp, Mat(), Point(-1, -1), erode_count);
|
||||
|
||||
std::vector<std::vector<Point> > white_contours, black_contours;
|
||||
findContours(black_comp, black_contours, RETR_LIST, CHAIN_APPROX_SIMPLE);
|
||||
findContours(white_comp, white_contours, RETR_LIST, CHAIN_APPROX_SIMPLE);
|
||||
|
||||
if(black_contours.size() < 5 || white_contours.size() < 5) continue;
|
||||
|
||||
// find two white and black blobs that are close to the input point
|
||||
std::vector<std::pair<int, float> > white_order, black_order;
|
||||
orderContours(black_contours, corners[i], black_order);
|
||||
orderContours(white_contours, corners[i], white_order);
|
||||
|
||||
const float max_dist = 10.0f;
|
||||
if(black_order[0].second > max_dist || black_order[1].second > max_dist ||
|
||||
white_order[0].second > max_dist || white_order[1].second > max_dist)
|
||||
{
|
||||
continue; // there will be no improvement in this corner position
|
||||
}
|
||||
|
||||
const std::vector<Point>* quads[4] = {&black_contours[black_order[0].first], &black_contours[black_order[1].first],
|
||||
&white_contours[white_order[0].first], &white_contours[white_order[1].first]};
|
||||
std::vector<Point2f> quads_approx[4];
|
||||
Point2f quad_corners[4];
|
||||
for(int k = 0; k < 4; k++)
|
||||
{
|
||||
std::vector<Point2f> temp;
|
||||
for(size_t j = 0; j < quads[k]->size(); j++) temp.push_back((*quads[k])[j]);
|
||||
approxPolyDP(Mat(temp), quads_approx[k], 0.5, true);
|
||||
|
||||
findCorner(quads_approx[k], corners[i], quad_corners[k]);
|
||||
quad_corners[k] += Point2f(0.5f, 0.5f);
|
||||
}
|
||||
|
||||
// cross two lines
|
||||
Point2f origin1 = quad_corners[0];
|
||||
Point2f dir1 = quad_corners[1] - quad_corners[0];
|
||||
Point2f origin2 = quad_corners[2];
|
||||
Point2f dir2 = quad_corners[3] - quad_corners[2];
|
||||
double angle = std::acos(dir1.dot(dir2)/(norm(dir1)*norm(dir2)));
|
||||
if(cvIsNaN(angle) || cvIsInf(angle) || angle < 0.5 || angle > CV_PI - 0.5) continue;
|
||||
|
||||
findLinesCrossPoint(origin1, dir1, origin2, dir2, corners[i]);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user