| 1 | // This file is part of OpenCV project. |
| 2 | // It is subject to the license terms in the LICENSE file found in the top-level directory |
| 3 | // of this distribution and at http://opencv.org/license.html. |
| 4 | |
| 5 | #ifndef CHESSBOARD_HPP_ |
| 6 | #define CHESSBOARD_HPP_ |
| 7 | |
| 8 | #include "opencv2/core.hpp" |
| 9 | #include "opencv2/features2d.hpp" |
| 10 | #include <vector> |
| 11 | #include <set> |
| 12 | #include <map> |
| 13 | |
| 14 | namespace cv { |
| 15 | namespace details{ |
| 16 | /** |
| 17 | * \brief Fast point sysmetric cross detector based on a localized radon transformation |
| 18 | */ |
| 19 | class FastX : public cv::Feature2D |
| 20 | { |
| 21 | public: |
| 22 | struct Parameters |
| 23 | { |
| 24 | float strength; //!< minimal strength of a valid junction in dB |
| 25 | float resolution; //!< angle resolution in radians |
| 26 | int branches; //!< the number of branches |
| 27 | int min_scale; //!< scale level [0..8] |
| 28 | int max_scale; //!< scale level [0..8] |
| 29 | bool filter; //!< post filter feature map to improve impulse response |
| 30 | bool super_resolution; //!< up-sample |
| 31 | |
| 32 | Parameters() |
| 33 | { |
| 34 | strength = 40; |
| 35 | resolution = float(CV_PI*0.25); |
| 36 | branches = 2; |
| 37 | min_scale = 2; |
| 38 | max_scale = 5; |
| 39 | super_resolution = true; |
| 40 | filter = true; |
| 41 | } |
| 42 | }; |
| 43 | |
| 44 | public: |
| 45 | FastX(const Parameters &config = Parameters()); |
| 46 | virtual ~FastX(){} |
| 47 | |
| 48 | void reconfigure(const Parameters ¶); |
| 49 | |
| 50 | //declaration to be wrapped by rbind |
| 51 | void detect(cv::InputArray image,std::vector<cv::KeyPoint>& keypoints, cv::InputArray mask=cv::Mat())override |
| 52 | {cv::Feature2D::detect(image: image.getMat(),keypoints,mask: mask.getMat());} |
| 53 | |
| 54 | virtual void detectAndCompute(cv::InputArray image, |
| 55 | cv::InputArray mask, |
| 56 | std::vector<cv::KeyPoint>& keypoints, |
| 57 | cv::OutputArray descriptors, |
| 58 | bool useProvidedKeyPoints = false)override; |
| 59 | |
| 60 | void detectImpl(const cv::Mat& image, |
| 61 | std::vector<cv::KeyPoint>& keypoints, |
| 62 | std::vector<cv::Mat> &feature_maps, |
| 63 | const cv::Mat& mask=cv::Mat())const; |
| 64 | |
| 65 | void detectImpl(const cv::Mat& image, |
| 66 | std::vector<cv::Mat> &rotated_images, |
| 67 | std::vector<cv::Mat> &feature_maps, |
| 68 | const cv::Mat& mask=cv::Mat())const; |
| 69 | |
| 70 | void findKeyPoints(const std::vector<cv::Mat> &feature_map, |
| 71 | std::vector<cv::KeyPoint>& keypoints, |
| 72 | const cv::Mat& mask = cv::Mat())const; |
| 73 | |
| 74 | std::vector<std::vector<float> > calcAngles(const std::vector<cv::Mat> &rotated_images, |
| 75 | std::vector<cv::KeyPoint> &keypoints)const; |
| 76 | // define pure virtual methods |
| 77 | virtual int descriptorSize()const override{return 0;} |
| 78 | virtual int descriptorType()const override{return 0;} |
| 79 | virtual void operator()( cv::InputArray image, cv::InputArray mask, std::vector<cv::KeyPoint>& keypoints, cv::OutputArray descriptors, bool useProvidedKeypoints=false )const |
| 80 | { |
| 81 | descriptors.clear(); |
| 82 | detectImpl(image: image.getMat(),keypoints,mask); |
| 83 | if(!useProvidedKeypoints) // suppress compiler warning |
| 84 | return; |
| 85 | return; |
| 86 | } |
| 87 | |
| 88 | protected: |
| 89 | virtual void computeImpl( const cv::Mat& image, std::vector<cv::KeyPoint>& keypoints, cv::Mat& descriptors)const |
| 90 | { |
| 91 | descriptors = cv::Mat(); |
| 92 | detectImpl(image,keypoints); |
| 93 | } |
| 94 | |
| 95 | private: |
| 96 | void detectImpl(const cv::Mat& _src, std::vector<cv::KeyPoint>& keypoints, const cv::Mat& mask)const; |
| 97 | virtual void detectImpl(cv::InputArray image, std::vector<cv::KeyPoint>& keypoints, cv::InputArray mask=cv::noArray())const; |
| 98 | |
| 99 | void rotate(float angle,cv::InputArray img,cv::Size size,cv::OutputArray out)const; |
| 100 | void calcFeatureMap(const cv::Mat &images,cv::Mat& out)const; |
| 101 | |
| 102 | private: |
| 103 | Parameters parameters; |
| 104 | }; |
| 105 | |
| 106 | /** |
| 107 | * \brief Ellipse class |
| 108 | */ |
| 109 | class Ellipse |
| 110 | { |
| 111 | public: |
| 112 | Ellipse(); |
| 113 | Ellipse(const cv::Point2f ¢er, const cv::Size2f &axes, float angle); |
| 114 | |
| 115 | void draw(cv::InputOutputArray img,const cv::Scalar &color = cv::Scalar::all(v0: v0: 120))const; |
| 116 | bool contains(const cv::Point2f &pt)const; |
| 117 | cv::Point2f getCenter()const; |
| 118 | const cv::Size2f &getAxes()const; |
| 119 | |
| 120 | private: |
| 121 | cv::Point2f center; |
| 122 | cv::Size2f axes; |
| 123 | float angle,cosf,sinf; |
| 124 | }; |
| 125 | |
| 126 | /** |
| 127 | * \brief Chessboard corner detector |
| 128 | * |
| 129 | * The detectors tries to find all chessboard corners of an imaged |
| 130 | * chessboard and returns them as an ordered vector of KeyPoints. |
| 131 | * Thereby, the left top corner has index 0 and the bottom right |
| 132 | * corner n*m-1. |
| 133 | */ |
| 134 | class Chessboard: public cv::Feature2D |
| 135 | { |
| 136 | public: |
| 137 | static const int DUMMY_FIELD_SIZE = 100; // in pixel |
| 138 | |
| 139 | /** |
| 140 | * \brief Configuration of a chessboard corner detector |
| 141 | * |
| 142 | */ |
| 143 | struct Parameters |
| 144 | { |
| 145 | cv::Size chessboard_size; //!< size of the chessboard |
| 146 | int min_scale; //!< scale level [0..8] |
| 147 | int max_scale; //!< scale level [0..8] |
| 148 | int max_points; //!< maximal number of points regarded |
| 149 | int max_tests; //!< maximal number of tested hypothesis |
| 150 | bool super_resolution; //!< use super-repsolution for chessboard detection |
| 151 | bool larger; //!< indicates if larger boards should be returned |
| 152 | bool marker; //!< indicates that valid boards must have a white and black cirlce marker used for orientation |
| 153 | |
| 154 | Parameters() |
| 155 | { |
| 156 | chessboard_size = cv::Size(9,6); |
| 157 | min_scale = 3; |
| 158 | max_scale = 4; |
| 159 | super_resolution = true; |
| 160 | max_points = 200; |
| 161 | max_tests = 50; |
| 162 | larger = false; |
| 163 | marker = false; |
| 164 | } |
| 165 | |
| 166 | Parameters(int scale,int _max_points): |
| 167 | min_scale(scale), |
| 168 | max_scale(scale), |
| 169 | max_points(_max_points) |
| 170 | { |
| 171 | chessboard_size = cv::Size(9,6); |
| 172 | } |
| 173 | }; |
| 174 | |
| 175 | |
| 176 | /** |
| 177 | * \brief Gets the 3D objects points for the chessboard assuming the |
| 178 | * left top corner is located at the origin. |
| 179 | * |
| 180 | * \param[in] pattern_size Number of rows and cols of the pattern |
| 181 | * \param[in] cell_size Size of one cell |
| 182 | * |
| 183 | * \returns Returns the object points as CV_32FC3 |
| 184 | */ |
| 185 | static cv::Mat getObjectPoints(const cv::Size &pattern_size,float cell_size); |
| 186 | |
| 187 | /** |
| 188 | * \brief Class for searching and storing chessboard corners. |
| 189 | * |
| 190 | * The search is based on a feature map having strong pixel |
| 191 | * values at positions where a chessboard corner is located. |
| 192 | * |
| 193 | * The board must be rectangular but supports empty cells |
| 194 | * |
| 195 | */ |
| 196 | class Board |
| 197 | { |
| 198 | public: |
| 199 | /** |
| 200 | * \brief Estimates the position of the next point on a line using cross ratio constrain |
| 201 | * |
| 202 | * cross ratio: |
| 203 | * d12/d34 = d13/d24 |
| 204 | * |
| 205 | * point order on the line: |
| 206 | * p0 --> p1 --> p2 --> p3 |
| 207 | * |
| 208 | * \param[in] p0 First point coordinate |
| 209 | * \param[in] p1 Second point coordinate |
| 210 | * \param[in] p2 Third point coordinate |
| 211 | * \param[out] p3 Forth point coordinate |
| 212 | * |
| 213 | */ |
| 214 | static bool estimatePoint(const cv::Point2f &p0,const cv::Point2f &p1,const cv::Point2f &p2,cv::Point2f &p3); |
| 215 | |
| 216 | // using 1D homography |
| 217 | static bool estimatePoint(const cv::Point2f &p0,const cv::Point2f &p1,const cv::Point2f &p2,const cv::Point2f &p3, cv::Point2f &p4); |
| 218 | |
| 219 | /** |
| 220 | * \brief Checks if all points of a row or column have a valid cross ratio constraint |
| 221 | * |
| 222 | * cross ratio: |
| 223 | * d12/d34 = d13/d24 |
| 224 | * |
| 225 | * point order on the row/column: |
| 226 | * pt1 --> pt2 --> pt3 --> pt4 |
| 227 | * |
| 228 | * \param[in] points THe points of the row/column |
| 229 | * |
| 230 | */ |
| 231 | static bool checkRowColumn(const std::vector<cv::Point2f> &points); |
| 232 | |
| 233 | /** |
| 234 | * \brief Estimates the search area for the next point on the line using cross ratio |
| 235 | * |
| 236 | * point order on the line: |
| 237 | * (p0) --> p1 --> p2 --> p3 --> search area |
| 238 | * |
| 239 | * \param[in] p1 First point coordinate |
| 240 | * \param[in] p2 Second point coordinate |
| 241 | * \param[in] p3 Third point coordinate |
| 242 | * \param[in] p Percentage of d34 used for the search area width and height [0..1] |
| 243 | * \param[out] ellipse The search area |
| 244 | * \param[in] p0 optional point to improve accuracy |
| 245 | * |
| 246 | * \return Returns false if no search area can be calculated |
| 247 | * |
| 248 | */ |
| 249 | static bool estimateSearchArea(const cv::Point2f &p1,const cv::Point2f &p2,const cv::Point2f &p3,float p, |
| 250 | Ellipse &ellipse,const cv::Point2f *p0 =NULL); |
| 251 | |
| 252 | /** |
| 253 | * \brief Estimates the search area for a specific point based on the given homography |
| 254 | * |
| 255 | * \param[in] H homography describing the transformation from ideal board to real one |
| 256 | * \param[in] row Row of the point |
| 257 | * \param[in] col Col of the point |
| 258 | * \param[in] p Percentage [0..1] |
| 259 | * |
| 260 | * \return Returns false if no search area can be calculated |
| 261 | * |
| 262 | */ |
| 263 | static Ellipse estimateSearchArea(cv::Mat H,int row, int col,float p,int field_size = DUMMY_FIELD_SIZE); |
| 264 | |
| 265 | /** |
| 266 | * \brief Searches for the maximum in a given search area |
| 267 | * |
| 268 | * \param[in] map feature map |
| 269 | * \param[in] ellipse search area |
| 270 | * \param[in] min_val Minimum value of the maximum to be accepted as maximum |
| 271 | * |
| 272 | * \return Returns a negative value if all points are outside the ellipse |
| 273 | * |
| 274 | */ |
| 275 | static float findMaxPoint(cv::flann::Index &index,const cv::Mat &data,const Ellipse &ellipse,float white_angle,float black_angle,cv::Point2f &pt); |
| 276 | |
| 277 | /** |
| 278 | * \brief Searches for the next point using cross ratio constrain |
| 279 | * |
| 280 | * \param[in] index flann index |
| 281 | * \param[in] data extended flann data |
| 282 | * \param[in] pt1 |
| 283 | * \param[in] pt2 |
| 284 | * \param[in] pt3 |
| 285 | * \param[in] white_angle |
| 286 | * \param[in] black_angle |
| 287 | * \param[in] min_response |
| 288 | * \param[out] point The resulting point |
| 289 | * |
| 290 | * \return Returns false if no point could be found |
| 291 | * |
| 292 | */ |
| 293 | static bool findNextPoint(cv::flann::Index &index,const cv::Mat &data, |
| 294 | const cv::Point2f &pt1,const cv::Point2f &pt2, const cv::Point2f &pt3, |
| 295 | float white_angle,float black_angle,float min_response,cv::Point2f &point); |
| 296 | |
| 297 | /** |
| 298 | * \brief Creates a new Board object |
| 299 | * |
| 300 | */ |
| 301 | Board(float white_angle=0,float black_angle=0); |
| 302 | Board(const cv::Size &size, const std::vector<cv::Point2f> &points,float white_angle=0,float black_angle=0); |
| 303 | Board(const Chessboard::Board &other); |
| 304 | virtual ~Board(); |
| 305 | |
| 306 | Board& operator=(const Chessboard::Board &other); |
| 307 | |
| 308 | /** |
| 309 | * \brief Draws the corners into the given image |
| 310 | * |
| 311 | * \param[in] m The image |
| 312 | * \param[out] out The resulting image |
| 313 | * \param[in] H optional homography to calculate search area |
| 314 | * |
| 315 | */ |
| 316 | void draw(cv::InputArray m,cv::OutputArray out,cv::InputArray H=cv::Mat())const; |
| 317 | |
| 318 | /** |
| 319 | * \brief Estimates the pose of the chessboard |
| 320 | * |
| 321 | */ |
| 322 | bool estimatePose(const cv::Size2f &real_size,cv::InputArray _K,cv::OutputArray rvec,cv::OutputArray tvec)const; |
| 323 | |
| 324 | /** |
| 325 | * \brief Clears all internal data of the object |
| 326 | * |
| 327 | */ |
| 328 | void clear(); |
| 329 | |
| 330 | /** |
| 331 | * \brief Returns the angle of the black diagnonale |
| 332 | * |
| 333 | */ |
| 334 | float getBlackAngle()const; |
| 335 | |
| 336 | /** |
| 337 | * \brief Returns the angle of the black diagnonale |
| 338 | * |
| 339 | */ |
| 340 | float getWhiteAngle()const; |
| 341 | |
| 342 | /** |
| 343 | * \brief Initializes a 3x3 grid from 9 corner coordinates |
| 344 | * |
| 345 | * All points must be ordered: |
| 346 | * p0 p1 p2 |
| 347 | * p3 p4 p5 |
| 348 | * p6 p7 p8 |
| 349 | * |
| 350 | * \param[in] points vector of points |
| 351 | * |
| 352 | * \return Returns false if the grid could not be initialized |
| 353 | */ |
| 354 | bool init(const std::vector<cv::Point2f> points); |
| 355 | |
| 356 | /** |
| 357 | * \brief Returns true if the board is empty |
| 358 | * |
| 359 | */ |
| 360 | bool isEmpty() const; |
| 361 | |
| 362 | /** |
| 363 | * \brief Returns all board corners as ordered vector |
| 364 | * |
| 365 | * The left top corner has index 0 and the bottom right |
| 366 | * corner rows*cols-1. All corners which only belong to |
| 367 | * empty cells are returned as NaN. |
| 368 | */ |
| 369 | std::vector<cv::Point2f> getCorners(bool ball=true) const; |
| 370 | |
| 371 | /** |
| 372 | * \brief Returns all board corners as ordered vector of KeyPoints |
| 373 | * |
| 374 | * The left top corner has index 0 and the bottom right |
| 375 | * corner rows*cols-1. |
| 376 | * |
| 377 | * \param[in] ball if set to false only non empty points are returned |
| 378 | * |
| 379 | */ |
| 380 | std::vector<cv::KeyPoint> getKeyPoints(bool ball=true) const; |
| 381 | |
| 382 | /** |
| 383 | * \brief Returns the centers of the chessboard cells |
| 384 | * |
| 385 | * The left top corner has index 0 and the bottom right |
| 386 | * corner (rows-1)*(cols-1)-1. |
| 387 | * |
| 388 | */ |
| 389 | std::vector<cv::Point2f> getCellCenters() const; |
| 390 | |
| 391 | /** |
| 392 | * \brief Returns all cells as mats of four points each describing their corners. |
| 393 | * |
| 394 | * The left top cell has index 0 |
| 395 | * |
| 396 | */ |
| 397 | std::vector<cv::Mat> getCells(float shrink_factor = 1.0,bool bwhite=true,bool bblack = true) const; |
| 398 | |
| 399 | /** |
| 400 | * \brief Estimates the homography between an ideal board |
| 401 | * and reality based on the already recovered points |
| 402 | * |
| 403 | * \param[in] rect selecting a subset of the already recovered points |
| 404 | * \param[in] field_size The field size of the ideal board |
| 405 | * |
| 406 | */ |
| 407 | cv::Mat estimateHomography(cv::Rect rect,int field_size = DUMMY_FIELD_SIZE)const; |
| 408 | |
| 409 | /** |
| 410 | * \brief Estimates the homography between an ideal board |
| 411 | * and reality based on the already recovered points |
| 412 | * |
| 413 | * \param[in] field_size The field size of the ideal board |
| 414 | * |
| 415 | */ |
| 416 | cv::Mat estimateHomography(int field_size = DUMMY_FIELD_SIZE)const; |
| 417 | |
| 418 | /** |
| 419 | * \brief Warp image to match ideal checkerboard |
| 420 | * |
| 421 | */ |
| 422 | cv::Mat warpImage(cv::InputArray image)const; |
| 423 | |
| 424 | /** |
| 425 | * \brief Returns the size of the board |
| 426 | * |
| 427 | */ |
| 428 | cv::Size getSize() const; |
| 429 | |
| 430 | /** |
| 431 | * \brief Returns the number of cols |
| 432 | * |
| 433 | */ |
| 434 | size_t colCount() const; |
| 435 | |
| 436 | /** |
| 437 | * \brief Returns the number of rows |
| 438 | * |
| 439 | */ |
| 440 | size_t rowCount() const; |
| 441 | |
| 442 | /** |
| 443 | * \brief Returns the inner contour of the board including only valid corners |
| 444 | * |
| 445 | * \info the contour might be non squared if not all points of the board are defined |
| 446 | * |
| 447 | */ |
| 448 | std::vector<cv::Point2f> getContour()const; |
| 449 | |
| 450 | /** |
| 451 | * \brief Masks the found board in the given image |
| 452 | * |
| 453 | */ |
| 454 | void maskImage(cv::InputOutputArray img,const cv::Scalar &color=cv::Scalar::all(v0: v0: 0))const; |
| 455 | |
| 456 | /** |
| 457 | * \brief Grows the board in all direction until no more corners are found in the feature map |
| 458 | * |
| 459 | * \param[in] data CV_32FC1 data of the flann index |
| 460 | * \param[in] flann_index flann index |
| 461 | * |
| 462 | * \returns the number of grows |
| 463 | */ |
| 464 | int grow(const cv::Mat &data,cv::flann::Index &flann_index); |
| 465 | |
| 466 | /** |
| 467 | * \brief Validates all corners using guided search based on the given homography |
| 468 | * |
| 469 | * \param[in] data CV_32FC1 data of the flann index |
| 470 | * \param[in] flann_index flann index |
| 471 | * \param[in] h Homography describing the transformation from ideal board to the real one |
| 472 | * \param[in] min_response Min response |
| 473 | * |
| 474 | * \returns the number of valid corners |
| 475 | */ |
| 476 | int validateCorners(const cv::Mat &data,cv::flann::Index &flann_index,const cv::Mat &h,float min_response=0); |
| 477 | |
| 478 | /** |
| 479 | * \brief check that no corner is used more than once |
| 480 | * |
| 481 | * \returns Returns false if a corner is used more than once |
| 482 | */ |
| 483 | bool checkUnique()const; |
| 484 | |
| 485 | /** |
| 486 | * \brief Returns false if the angles of the contour are smaller than 35° |
| 487 | * |
| 488 | */ |
| 489 | bool validateContour()const; |
| 490 | |
| 491 | |
| 492 | /** |
| 493 | \brief delete left column of the board |
| 494 | */ |
| 495 | bool shrinkLeft(); |
| 496 | |
| 497 | /** |
| 498 | \brief delete right column of the board |
| 499 | */ |
| 500 | bool shrinkRight(); |
| 501 | |
| 502 | /** |
| 503 | \brief shrink first row of the board |
| 504 | */ |
| 505 | bool shrinkTop(); |
| 506 | |
| 507 | /** |
| 508 | \brief delete last row of the board |
| 509 | */ |
| 510 | bool shrinkBottom(); |
| 511 | |
| 512 | /** |
| 513 | * \brief Grows the board to the left by adding one column. |
| 514 | * |
| 515 | * \param[in] map CV_32FC1 feature map |
| 516 | * |
| 517 | * \returns Returns false if the feature map has no maxima at the requested positions |
| 518 | */ |
| 519 | bool growLeft(const cv::Mat &map,cv::flann::Index &flann_index); |
| 520 | void growLeft(); |
| 521 | |
| 522 | /** |
| 523 | * \brief Grows the board to the top by adding one row. |
| 524 | * |
| 525 | * \param[in] map CV_32FC1 feature map |
| 526 | * |
| 527 | * \returns Returns false if the feature map has no maxima at the requested positions |
| 528 | */ |
| 529 | bool growTop(const cv::Mat &map,cv::flann::Index &flann_index); |
| 530 | void growTop(); |
| 531 | |
| 532 | /** |
| 533 | * \brief Grows the board to the right by adding one column. |
| 534 | * |
| 535 | * \param[in] map CV_32FC1 feature map |
| 536 | * |
| 537 | * \returns Returns false if the feature map has no maxima at the requested positions |
| 538 | */ |
| 539 | bool growRight(const cv::Mat &map,cv::flann::Index &flann_index); |
| 540 | void growRight(); |
| 541 | |
| 542 | /** |
| 543 | * \brief Grows the board to the bottom by adding one row. |
| 544 | * |
| 545 | * \param[in] map CV_32FC1 feature map |
| 546 | * |
| 547 | * \returns Returns false if the feature map has no maxima at the requested positions |
| 548 | */ |
| 549 | bool growBottom(const cv::Mat &map,cv::flann::Index &flann_index); |
| 550 | void growBottom(); |
| 551 | |
| 552 | /** |
| 553 | * \brief Adds one column on the left side |
| 554 | * |
| 555 | * \param[in] points The corner coordinates |
| 556 | * |
| 557 | */ |
| 558 | void addColumnLeft(const std::vector<cv::Point2f> &points); |
| 559 | |
| 560 | /** |
| 561 | * \brief Adds one column at the top |
| 562 | * |
| 563 | * \param[in] points The corner coordinates |
| 564 | * |
| 565 | */ |
| 566 | void addRowTop(const std::vector<cv::Point2f> &points); |
| 567 | |
| 568 | /** |
| 569 | * \brief Adds one column on the right side |
| 570 | * |
| 571 | * \param[in] points The corner coordinates |
| 572 | * |
| 573 | */ |
| 574 | void addColumnRight(const std::vector<cv::Point2f> &points); |
| 575 | |
| 576 | /** |
| 577 | * \brief Adds one row at the bottom |
| 578 | * |
| 579 | * \param[in] points The corner coordinates |
| 580 | * |
| 581 | */ |
| 582 | void addRowBottom(const std::vector<cv::Point2f> &points); |
| 583 | |
| 584 | /** |
| 585 | * \brief Rotates the board 90° degrees to the left |
| 586 | */ |
| 587 | void rotateLeft(); |
| 588 | |
| 589 | /** |
| 590 | * \brief Rotates the board 90° degrees to the right |
| 591 | */ |
| 592 | void rotateRight(); |
| 593 | |
| 594 | /** |
| 595 | * \brief Flips the board along its local x(width) coordinate direction |
| 596 | */ |
| 597 | void flipVertical(); |
| 598 | |
| 599 | /** |
| 600 | * \brief Flips the board along its local y(height) coordinate direction |
| 601 | */ |
| 602 | void flipHorizontal(); |
| 603 | |
| 604 | /** |
| 605 | * \brief Flips and rotates the board so that the angle of |
| 606 | * either the black or white diagonal is bigger than the x |
| 607 | * and y axis of the board and from a right handed |
| 608 | * coordinate system |
| 609 | */ |
| 610 | void normalizeOrientation(bool bblack=true); |
| 611 | |
| 612 | /** |
| 613 | * \brief Flips and rotates the board so that the marker |
| 614 | * is normalized |
| 615 | */ |
| 616 | bool normalizeMarkerOrientation(); |
| 617 | |
| 618 | /** |
| 619 | * \brief Exchanges the stored board with the board stored in other |
| 620 | */ |
| 621 | void swap(Chessboard::Board &other); |
| 622 | |
| 623 | bool operator==(const Chessboard::Board& other) const {return rows*cols == other.rows*other.cols;} |
| 624 | bool operator< (const Chessboard::Board& other) const {return rows*cols < other.rows*other.cols;} |
| 625 | bool operator> (const Chessboard::Board& other) const {return rows*cols > other.rows*other.cols;} |
| 626 | bool operator>= (const cv::Size& size)const { return rows*cols >= size.width*size.height; } |
| 627 | |
| 628 | /** |
| 629 | * \brief Returns a specific corner |
| 630 | * |
| 631 | * \info raises runtime_error if row col does not exists |
| 632 | */ |
| 633 | cv::Point2f& getCorner(int row,int col); |
| 634 | |
| 635 | /** |
| 636 | * \brief Returns true if the cell is empty meaning at least one corner is NaN |
| 637 | */ |
| 638 | bool isCellEmpty(int row,int col); |
| 639 | |
| 640 | /** |
| 641 | * \brief Returns the mapping from all corners idx to only valid corners idx |
| 642 | */ |
| 643 | std::map<int,int> getMapping()const; |
| 644 | |
| 645 | /** |
| 646 | * \brief Returns true if the cell is black |
| 647 | * |
| 648 | */ |
| 649 | bool isCellBlack(int row,int col)const; |
| 650 | |
| 651 | /** |
| 652 | * \brief Returns true if the cell has a round marker at its |
| 653 | * center |
| 654 | * |
| 655 | */ |
| 656 | bool hasCellMarker(int row,int col); |
| 657 | |
| 658 | /** |
| 659 | * \brief Detects round markers in the chessboard fields based |
| 660 | * on the given image and the already recoverd board corners |
| 661 | * |
| 662 | * \returns Returns the number of found markes |
| 663 | * |
| 664 | */ |
| 665 | int detectMarkers(cv::InputArray image); |
| 666 | |
| 667 | /** |
| 668 | * \brief Calculates the average edge sharpness for the chessboard |
| 669 | * |
| 670 | * \param[in] image The image where the chessboard was detected |
| 671 | * \param[in] rise_distance Rise distance 0.8 means 10% ... 90% |
| 672 | * \param[in] vertical by default only edge response for horiontal lines are calculated |
| 673 | * |
| 674 | * \returns Scalar(sharpness, average min_val, average max_val) |
| 675 | * |
| 676 | * \author aduda@krakenrobotik.de |
| 677 | */ |
| 678 | cv::Scalar calcEdgeSharpness(cv::InputArray image,float rise_distance=0.8,bool vertical=false,cv::OutputArray sharpness=cv::noArray()); |
| 679 | |
| 680 | |
| 681 | /** |
| 682 | * \brief Gets the 3D objects points for the chessboard |
| 683 | * assuming the left top corner is located at the origin. In |
| 684 | * case the board as a marker, the white marker cell is at position zero |
| 685 | * |
| 686 | * \param[in] cell_size Size of one cell |
| 687 | * |
| 688 | * \returns Returns the object points as CV_32FC3 |
| 689 | */ |
| 690 | cv::Mat getObjectPoints(float cell_size)const; |
| 691 | |
| 692 | |
| 693 | /** |
| 694 | * \brief Returns the angle the board is rotated agains the x-axis of the image plane |
| 695 | * \returns Returns the object points as CV_32FC3 |
| 696 | */ |
| 697 | float getAngle()const; |
| 698 | |
| 699 | /** |
| 700 | * \brief Returns true if the main direction of the board is close to the image x-axis than y-axis |
| 701 | */ |
| 702 | bool isHorizontal()const; |
| 703 | |
| 704 | /** |
| 705 | * \brief Updates the search angles |
| 706 | */ |
| 707 | void setAngles(float white,float black); |
| 708 | |
| 709 | private: |
| 710 | // stores one cell |
| 711 | // in general a cell is initialized by the Board so that: |
| 712 | // * all corners are always pointing to a valid cv::Point2f |
| 713 | // * depending on the position left,top,right and bottom might be set to NaN |
| 714 | // * A cell is empty if at least one corner is NaN |
| 715 | struct Cell |
| 716 | { |
| 717 | cv::Point2f *top_left,*top_right,*bottom_right,*bottom_left; // corners |
| 718 | Cell *left,*top,*right,*bottom; // neighbouring cells |
| 719 | bool black; // set to true if cell is black |
| 720 | bool marker; // set to true if cell has a round marker in its center |
| 721 | Cell(); |
| 722 | bool empty()const; // indicates if the cell is empty (one of its corners has NaN) |
| 723 | int getRow()const; |
| 724 | int getCol()const; |
| 725 | cv::Point2f getCenter()const; |
| 726 | bool isInside(const cv::Point2f &pt)const; // check if point is inside the cell |
| 727 | }; |
| 728 | |
| 729 | // corners |
| 730 | enum CornerIndex |
| 731 | { |
| 732 | TOP_LEFT, |
| 733 | TOP_RIGHT, |
| 734 | BOTTOM_RIGHT, |
| 735 | BOTTOM_LEFT |
| 736 | }; |
| 737 | |
| 738 | Cell* getCell(int row,int column); // returns a specific cell |
| 739 | const Cell* getCell(int row,int column)const; // returns a specific cell |
| 740 | void drawEllipses(const std::vector<Ellipse> &ellipses); |
| 741 | |
| 742 | // Iterator for iterating over board corners |
| 743 | class PointIter |
| 744 | { |
| 745 | public: |
| 746 | PointIter(Cell *cell,CornerIndex corner_index); |
| 747 | PointIter(const PointIter &other); |
| 748 | void operator=(const PointIter &other); |
| 749 | bool valid() const; // returns if the pointer is pointing to a cell |
| 750 | |
| 751 | bool left(bool check_empty=false); // moves one corner to the left or returns false |
| 752 | bool right(bool check_empty=false); // moves one corner to the right or returns false |
| 753 | bool bottom(bool check_empty=false); // moves one corner to the bottom or returns false |
| 754 | bool top(bool check_empty=false); // moves one corner to the top or returns false |
| 755 | bool checkCorner()const; // returns true if the current corner belongs to at least one |
| 756 | // none empty cell |
| 757 | bool isNaN()const; // returns true if the current corner is NaN |
| 758 | |
| 759 | const cv::Point2f* operator*() const; // current corner coordinate |
| 760 | cv::Point2f* operator*(); // current corner coordinate |
| 761 | const cv::Point2f* operator->() const; // current corner coordinate |
| 762 | cv::Point2f* operator->(); // current corner coordinate |
| 763 | |
| 764 | Cell *getCell(); // current cell |
| 765 | private: |
| 766 | CornerIndex corner_index; |
| 767 | Cell *cell; |
| 768 | }; |
| 769 | |
| 770 | std::vector<Cell*> cells; // storage for all board cells |
| 771 | std::vector<cv::Point2f*> corners; // storage for all corners |
| 772 | Cell *top_left; // pointer to the top left corner of the board in its local coordinate system |
| 773 | int rows; // number of inner pattern rows |
| 774 | int cols; // number of inner pattern cols |
| 775 | float white_angle,black_angle; |
| 776 | }; |
| 777 | public: |
| 778 | |
| 779 | /** |
| 780 | * \brief Creates a chessboard corner detectors |
| 781 | * |
| 782 | * \param[in] config Configuration used to detect chessboard corners |
| 783 | * |
| 784 | */ |
| 785 | Chessboard(const Parameters &config = Parameters()); |
| 786 | virtual ~Chessboard(); |
| 787 | void reconfigure(const Parameters &config = Parameters()); |
| 788 | Parameters getPara()const; |
| 789 | |
| 790 | /* |
| 791 | * \brief Detects chessboard corners in the given image. |
| 792 | * |
| 793 | * The detectors tries to find all chessboard corners of an imaged |
| 794 | * chessboard and returns them as an ordered vector of KeyPoints. |
| 795 | * Thereby, the left top corner has index 0 and the bottom right |
| 796 | * corner n*m-1. |
| 797 | * |
| 798 | * \param[in] image The image |
| 799 | * \param[out] keypoints The detected corners as a vector of ordered KeyPoints |
| 800 | * \param[in] mask Currently not supported |
| 801 | * |
| 802 | */ |
| 803 | void detect(cv::InputArray image,std::vector<cv::KeyPoint>& keypoints, cv::InputArray mask=cv::Mat())override |
| 804 | {cv::Feature2D::detect(image: image.getMat(),keypoints,mask: mask.getMat());} |
| 805 | |
| 806 | virtual void detectAndCompute(cv::InputArray image,cv::InputArray mask, std::vector<cv::KeyPoint>& keypoints,cv::OutputArray descriptors, |
| 807 | bool useProvidedKeyPoints = false)override; |
| 808 | |
| 809 | /* |
| 810 | * \brief Detects chessboard corners in the given image. |
| 811 | * |
| 812 | * The detectors tries to find all chessboard corners of an imaged |
| 813 | * chessboard and returns them as an ordered vector of KeyPoints. |
| 814 | * Thereby, the left top corner has index 0 and the bottom right |
| 815 | * corner n*m-1. |
| 816 | * |
| 817 | * \param[in] image The image |
| 818 | * \param[out] keypoints The detected corners as a vector of ordered KeyPoints |
| 819 | * \param[out] feature_maps The feature map generated by LRJT and used to find the corners |
| 820 | * \param[in] mask Currently not supported |
| 821 | * |
| 822 | */ |
| 823 | void detectImpl(const cv::Mat& image, std::vector<cv::KeyPoint>& keypoints,std::vector<cv::Mat> &feature_maps,const cv::Mat& mask)const; |
| 824 | Chessboard::Board detectImpl(const cv::Mat& image,std::vector<cv::Mat> &feature_maps,const cv::Mat& mask)const; |
| 825 | |
| 826 | // define pure virtual methods |
| 827 | virtual int descriptorSize()const override{return 0;} |
| 828 | virtual int descriptorType()const override{return 0;} |
| 829 | virtual void operator()( cv::InputArray image, cv::InputArray mask, std::vector<cv::KeyPoint>& keypoints, cv::OutputArray descriptors, bool useProvidedKeypoints=false )const |
| 830 | { |
| 831 | descriptors.clear(); |
| 832 | detectImpl(image: image.getMat(),keypoints,mask); |
| 833 | if(!useProvidedKeypoints) // suppress compiler warning |
| 834 | return; |
| 835 | return; |
| 836 | } |
| 837 | |
| 838 | protected: |
| 839 | virtual void computeImpl( const cv::Mat& image, std::vector<cv::KeyPoint>& keypoints, cv::Mat& descriptors)const |
| 840 | { |
| 841 | descriptors = cv::Mat(); |
| 842 | detectImpl(image,keypoints); |
| 843 | } |
| 844 | |
| 845 | // indicates why a board could not be initialized for a certain keypoint |
| 846 | enum BState |
| 847 | { |
| 848 | MISSING_POINTS = 0, // at least 5 points are needed |
| 849 | MISSING_PAIRS = 1, // at least two pairs are needed |
| 850 | WRONG_PAIR_ANGLE = 2, // angle between pairs is too small |
| 851 | WRONG_CONFIGURATION = 3, // point configuration is wrong and does not belong to a board |
| 852 | FOUND_BOARD = 4 // board was found |
| 853 | }; |
| 854 | |
| 855 | void findKeyPoints(const cv::Mat& image, std::vector<cv::KeyPoint>& keypoints,std::vector<cv::Mat> &feature_maps, |
| 856 | std::vector<std::vector<float> > &angles ,const cv::Mat& mask)const; |
| 857 | cv::Mat buildData(const std::vector<cv::KeyPoint>& keypoints)const; |
| 858 | 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; |
| 859 | BState generateBoards(cv::flann::Index &flann_index,const cv::Mat &data, const cv::KeyPoint ¢er, |
| 860 | float white_angle,float black_angle,float min_response,const cv::Mat &img, |
| 861 | std::vector<Chessboard::Board> &boards)const; |
| 862 | |
| 863 | private: |
| 864 | void detectImpl(const cv::Mat&,std::vector<cv::KeyPoint>&, const cv::Mat& mast =cv::Mat())const; |
| 865 | virtual void detectImpl(cv::InputArray image, std::vector<cv::KeyPoint>& keypoints, cv::InputArray mask=cv::noArray())const; |
| 866 | |
| 867 | private: |
| 868 | Parameters parameters; // storing the configuration of the detector |
| 869 | }; |
| 870 | }} // end namespace details and cv |
| 871 | |
| 872 | #endif |
| 873 | |