@ -0,0 +1,286 @@ |
||||
// 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 __OPENCV_ARUCO_CALIB_POSE_HPP__ |
||||
#define __OPENCV_ARUCO_CALIB_POSE_HPP__ |
||||
#include <opencv2/aruco/board.hpp> |
||||
#include <opencv2/calib3d.hpp> |
||||
|
||||
namespace cv { |
||||
namespace aruco { |
||||
|
||||
//! @addtogroup aruco
|
||||
//! @{
|
||||
|
||||
/** @brief rvec/tvec define the right handed coordinate system of the marker.
|
||||
* PatternPos defines center this system and axes direction. |
||||
* Axis X (red color) - first coordinate, axis Y (green color) - second coordinate, |
||||
* axis Z (blue color) - third coordinate. |
||||
* @sa estimatePoseSingleMarkers(), @ref tutorial_aruco_detection |
||||
*/ |
||||
enum PatternPos { |
||||
/** @brief The marker coordinate system is centered on the middle of the marker.
|
||||
* The coordinates of the four corners (CCW order) of the marker in its own coordinate system are: |
||||
* (-markerLength/2, markerLength/2, 0), (markerLength/2, markerLength/2, 0), |
||||
* (markerLength/2, -markerLength/2, 0), (-markerLength/2, -markerLength/2, 0). |
||||
* |
||||
* These pattern points define this coordinate system: |
||||
*  |
||||
*/ |
||||
ARUCO_CCW_CENTER, |
||||
/** @brief The marker coordinate system is centered on the top-left corner of the marker.
|
||||
* The coordinates of the four corners (CW order) of the marker in its own coordinate system are: |
||||
* (0, 0, 0), (markerLength, 0, 0), |
||||
* (markerLength, markerLength, 0), (0, markerLength, 0). |
||||
* |
||||
* These pattern points define this coordinate system: |
||||
*  |
||||
* |
||||
* These pattern dots are convenient to use with a chessboard/ChArUco board. |
||||
*/ |
||||
ARUCO_CW_TOP_LEFT_CORNER |
||||
}; |
||||
|
||||
/** @brief Pose estimation parameters
|
||||
* @param pattern Defines center this system and axes direction (default PatternPos::ARUCO_CCW_CENTER). |
||||
* @param useExtrinsicGuess Parameter used for SOLVEPNP_ITERATIVE. If true (1), the function uses the provided |
||||
* rvec and tvec values as initial approximations of the rotation and translation vectors, respectively, and further |
||||
* optimizes them (default false). |
||||
* @param solvePnPMethod Method for solving a PnP problem: see @ref calib3d_solvePnP_flags (default SOLVEPNP_ITERATIVE). |
||||
* @sa PatternPos, solvePnP(), @ref tutorial_aruco_detection |
||||
*/ |
||||
struct CV_EXPORTS_W EstimateParameters { |
||||
CV_PROP_RW PatternPos pattern; |
||||
CV_PROP_RW bool useExtrinsicGuess; |
||||
CV_PROP_RW SolvePnPMethod solvePnPMethod; |
||||
|
||||
EstimateParameters(): pattern(ARUCO_CCW_CENTER), useExtrinsicGuess(false), |
||||
solvePnPMethod(SOLVEPNP_ITERATIVE) {} |
||||
|
||||
CV_WRAP static Ptr<EstimateParameters> create() { |
||||
return makePtr<EstimateParameters>(); |
||||
} |
||||
}; |
||||
|
||||
|
||||
/**
|
||||
* @brief Pose estimation for single markers |
||||
* |
||||
* @param corners vector of already detected markers corners. For each marker, its four corners |
||||
* are provided, (e.g std::vector<std::vector<cv::Point2f> > ). For N detected markers, |
||||
* the dimensions of this array should be Nx4. The order of the corners should be clockwise. |
||||
* @sa detectMarkers |
||||
* @param markerLength the length of the markers' side. The returning translation vectors will |
||||
* be in the same unit. Normally, unit is meters. |
||||
* @param cameraMatrix input 3x3 floating-point camera matrix |
||||
* \f$A = \vecthreethree{f_x}{0}{c_x}{0}{f_y}{c_y}{0}{0}{1}\f$ |
||||
* @param distCoeffs vector of distortion coefficients |
||||
* \f$(k_1, k_2, p_1, p_2[, k_3[, k_4, k_5, k_6],[s_1, s_2, s_3, s_4]])\f$ of 4, 5, 8 or 12 elements |
||||
* @param rvecs array of output rotation vectors (@sa Rodrigues) (e.g. std::vector<cv::Vec3d>). |
||||
* Each element in rvecs corresponds to the specific marker in imgPoints. |
||||
* @param tvecs array of output translation vectors (e.g. std::vector<cv::Vec3d>). |
||||
* Each element in tvecs corresponds to the specific marker in imgPoints. |
||||
* @param objPoints array of object points of all the marker corners |
||||
* @param estimateParameters set the origin of coordinate system and the coordinates of the four corners of the marker |
||||
* (default estimateParameters.pattern = PatternPos::ARUCO_CCW_CENTER, estimateParameters.useExtrinsicGuess = false, |
||||
* estimateParameters.solvePnPMethod = SOLVEPNP_ITERATIVE). |
||||
* |
||||
* This function receives the detected markers and returns their pose estimation respect to |
||||
* the camera individually. So for each marker, one rotation and translation vector is returned. |
||||
* The returned transformation is the one that transforms points from each marker coordinate system |
||||
* to the camera coordinate system. |
||||
* The marker coordinate system is centered on the middle (by default) or on the top-left corner of the marker, |
||||
* with the Z axis perpendicular to the marker plane. |
||||
* estimateParameters defines the coordinates of the four corners of the marker in its own coordinate system (by default) are: |
||||
* (-markerLength/2, markerLength/2, 0), (markerLength/2, markerLength/2, 0), |
||||
* (markerLength/2, -markerLength/2, 0), (-markerLength/2, -markerLength/2, 0) |
||||
* @sa use cv::drawFrameAxes to get world coordinate system axis for object points |
||||
* @sa @ref tutorial_aruco_detection |
||||
* @sa EstimateParameters |
||||
* @sa PatternPos |
||||
*/ |
||||
CV_EXPORTS_W void estimatePoseSingleMarkers(InputArrayOfArrays corners, float markerLength, |
||||
InputArray cameraMatrix, InputArray distCoeffs, |
||||
OutputArray rvecs, OutputArray tvecs, OutputArray objPoints = noArray(), |
||||
const Ptr<EstimateParameters>& estimateParameters = EstimateParameters::create()); |
||||
|
||||
/**
|
||||
* @brief Pose estimation for a board of markers |
||||
* |
||||
* @param corners vector of already detected markers corners. For each marker, its four corners |
||||
* are provided, (e.g std::vector<std::vector<cv::Point2f> > ). For N detected markers, the |
||||
* dimensions of this array should be Nx4. The order of the corners should be clockwise. |
||||
* @param ids list of identifiers for each marker in corners |
||||
* @param board layout of markers in the board. The layout is composed by the marker identifiers |
||||
* and the positions of each marker corner in the board reference system. |
||||
* @param cameraMatrix input 3x3 floating-point camera matrix |
||||
* \f$A = \vecthreethree{f_x}{0}{c_x}{0}{f_y}{c_y}{0}{0}{1}\f$ |
||||
* @param distCoeffs vector of distortion coefficients |
||||
* \f$(k_1, k_2, p_1, p_2[, k_3[, k_4, k_5, k_6],[s_1, s_2, s_3, s_4]])\f$ of 4, 5, 8 or 12 elements |
||||
* @param rvec Output vector (e.g. cv::Mat) corresponding to the rotation vector of the board |
||||
* (see cv::Rodrigues). Used as initial guess if not empty. |
||||
* @param tvec Output vector (e.g. cv::Mat) corresponding to the translation vector of the board. |
||||
* @param useExtrinsicGuess defines whether initial guess for \b rvec and \b tvec will be used or not. |
||||
* Used as initial guess if not empty. |
||||
* |
||||
* This function receives the detected markers and returns the pose of a marker board composed |
||||
* by those markers. |
||||
* A Board of marker has a single world coordinate system which is defined by the board layout. |
||||
* The returned transformation is the one that transforms points from the board coordinate system |
||||
* to the camera coordinate system. |
||||
* Input markers that are not included in the board layout are ignored. |
||||
* The function returns the number of markers from the input employed for the board pose estimation. |
||||
* Note that returning a 0 means the pose has not been estimated. |
||||
* @sa use cv::drawFrameAxes to get world coordinate system axis for object points |
||||
*/ |
||||
CV_EXPORTS_W int estimatePoseBoard(InputArrayOfArrays corners, InputArray ids, const Ptr<Board> &board, |
||||
InputArray cameraMatrix, InputArray distCoeffs, InputOutputArray rvec, |
||||
InputOutputArray tvec, bool useExtrinsicGuess = false); |
||||
|
||||
/**
|
||||
* @brief Given a board configuration and a set of detected markers, returns the corresponding |
||||
* image points and object points to call solvePnP |
||||
* |
||||
* @param board Marker board layout. |
||||
* @param detectedCorners List of detected marker corners of the board. |
||||
* @param detectedIds List of identifiers for each marker. |
||||
* @param objPoints Vector of vectors of board marker points in the board coordinate space. |
||||
* @param imgPoints Vector of vectors of the projections of board marker corner points. |
||||
*/ |
||||
CV_EXPORTS_W void getBoardObjectAndImagePoints(const Ptr<Board> &board, InputArrayOfArrays detectedCorners, |
||||
InputArray detectedIds, OutputArray objPoints, OutputArray imgPoints); |
||||
|
||||
/**
|
||||
* @brief Calibrate a camera using aruco markers |
||||
* |
||||
* @param corners vector of detected marker corners in all frames. |
||||
* The corners should have the same format returned by detectMarkers (see #detectMarkers). |
||||
* @param ids list of identifiers for each marker in corners |
||||
* @param counter number of markers in each frame so that corners and ids can be split |
||||
* @param board Marker Board layout |
||||
* @param imageSize Size of the image used only to initialize the intrinsic camera matrix. |
||||
* @param cameraMatrix Output 3x3 floating-point camera matrix |
||||
* \f$A = \vecthreethree{f_x}{0}{c_x}{0}{f_y}{c_y}{0}{0}{1}\f$ . If CV\_CALIB\_USE\_INTRINSIC\_GUESS |
||||
* and/or CV_CALIB_FIX_ASPECT_RATIO are specified, some or all of fx, fy, cx, cy must be |
||||
* initialized before calling the function. |
||||
* @param distCoeffs Output vector of distortion coefficients |
||||
* \f$(k_1, k_2, p_1, p_2[, k_3[, k_4, k_5, k_6],[s_1, s_2, s_3, s_4]])\f$ of 4, 5, 8 or 12 elements |
||||
* @param rvecs Output vector of rotation vectors (see Rodrigues ) estimated for each board view |
||||
* (e.g. std::vector<cv::Mat>>). That is, each k-th rotation vector together with the corresponding |
||||
* k-th translation vector (see the next output parameter description) brings the board pattern |
||||
* from the model coordinate space (in which object points are specified) to the world coordinate |
||||
* space, that is, a real position of the board pattern in the k-th pattern view (k=0.. *M* -1). |
||||
* @param tvecs Output vector of translation vectors estimated for each pattern view. |
||||
* @param stdDeviationsIntrinsics Output vector of standard deviations estimated for intrinsic parameters. |
||||
* Order of deviations values: |
||||
* \f$(f_x, f_y, c_x, c_y, k_1, k_2, p_1, p_2, k_3, k_4, k_5, k_6 , s_1, s_2, s_3, |
||||
* s_4, \tau_x, \tau_y)\f$ If one of parameters is not estimated, it's deviation is equals to zero. |
||||
* @param stdDeviationsExtrinsics Output vector of standard deviations estimated for extrinsic parameters. |
||||
* Order of deviations values: \f$(R_1, T_1, \dotsc , R_M, T_M)\f$ where M is number of pattern views, |
||||
* \f$R_i, T_i\f$ are concatenated 1x3 vectors. |
||||
* @param perViewErrors Output vector of average re-projection errors estimated for each pattern view. |
||||
* @param flags flags Different flags for the calibration process (see #calibrateCamera for details). |
||||
* @param criteria Termination criteria for the iterative optimization algorithm. |
||||
* |
||||
* This function calibrates a camera using an Aruco Board. The function receives a list of |
||||
* detected markers from several views of the Board. The process is similar to the chessboard |
||||
* calibration in calibrateCamera(). The function returns the final re-projection error. |
||||
*/ |
||||
CV_EXPORTS_AS(calibrateCameraArucoExtended) |
||||
double calibrateCameraAruco(InputArrayOfArrays corners, InputArray ids, InputArray counter, const Ptr<Board> &board, |
||||
Size imageSize, InputOutputArray cameraMatrix, InputOutputArray distCoeffs, |
||||
OutputArrayOfArrays rvecs, OutputArrayOfArrays tvecs, OutputArray stdDeviationsIntrinsics, |
||||
OutputArray stdDeviationsExtrinsics, OutputArray perViewErrors, int flags = 0, |
||||
const TermCriteria& criteria = TermCriteria(TermCriteria::COUNT + TermCriteria::EPS, 30, DBL_EPSILON)); |
||||
|
||||
/** @overload
|
||||
* @brief It's the same function as #calibrateCameraAruco but without calibration error estimation. |
||||
*/ |
||||
CV_EXPORTS_W double calibrateCameraAruco(InputArrayOfArrays corners, InputArray ids, InputArray counter, |
||||
const Ptr<Board> &board, Size imageSize, InputOutputArray cameraMatrix, |
||||
InputOutputArray distCoeffs, OutputArrayOfArrays rvecs = noArray(), |
||||
OutputArrayOfArrays tvecs = noArray(), int flags = 0, |
||||
const TermCriteria& criteria = TermCriteria(TermCriteria::COUNT + TermCriteria::EPS, |
||||
30, DBL_EPSILON)); |
||||
|
||||
/**
|
||||
* @brief Pose estimation for a ChArUco board given some of their corners |
||||
* @param charucoCorners vector of detected charuco corners |
||||
* @param charucoIds list of identifiers for each corner in charucoCorners |
||||
* @param board layout of ChArUco board. |
||||
* @param cameraMatrix input 3x3 floating-point camera matrix |
||||
* \f$A = \vecthreethree{f_x}{0}{c_x}{0}{f_y}{c_y}{0}{0}{1}\f$ |
||||
* @param distCoeffs vector of distortion coefficients |
||||
* \f$(k_1, k_2, p_1, p_2[, k_3[, k_4, k_5, k_6],[s_1, s_2, s_3, s_4]])\f$ of 4, 5, 8 or 12 elements |
||||
* @param rvec Output vector (e.g. cv::Mat) corresponding to the rotation vector of the board |
||||
* (see cv::Rodrigues). |
||||
* @param tvec Output vector (e.g. cv::Mat) corresponding to the translation vector of the board. |
||||
* @param useExtrinsicGuess defines whether initial guess for \b rvec and \b tvec will be used or not. |
||||
* |
||||
* This function estimates a Charuco board pose from some detected corners. |
||||
* The function checks if the input corners are enough and valid to perform pose estimation. |
||||
* If pose estimation is valid, returns true, else returns false. |
||||
* @sa use cv::drawFrameAxes to get world coordinate system axis for object points |
||||
*/ |
||||
CV_EXPORTS_W bool estimatePoseCharucoBoard(InputArray charucoCorners, InputArray charucoIds, |
||||
const Ptr<CharucoBoard> &board, InputArray cameraMatrix, |
||||
InputArray distCoeffs, InputOutputArray rvec, |
||||
InputOutputArray tvec, bool useExtrinsicGuess = false); |
||||
|
||||
/**
|
||||
* @brief Calibrate a camera using Charuco corners |
||||
* |
||||
* @param charucoCorners vector of detected charuco corners per frame |
||||
* @param charucoIds list of identifiers for each corner in charucoCorners per frame |
||||
* @param board Marker Board layout |
||||
* @param imageSize input image size |
||||
* @param cameraMatrix Output 3x3 floating-point camera matrix |
||||
* \f$A = \vecthreethree{f_x}{0}{c_x}{0}{f_y}{c_y}{0}{0}{1}\f$ . If CV\_CALIB\_USE\_INTRINSIC\_GUESS |
||||
* and/or CV_CALIB_FIX_ASPECT_RATIO are specified, some or all of fx, fy, cx, cy must be |
||||
* initialized before calling the function. |
||||
* @param distCoeffs Output vector of distortion coefficients |
||||
* \f$(k_1, k_2, p_1, p_2[, k_3[, k_4, k_5, k_6],[s_1, s_2, s_3, s_4]])\f$ of 4, 5, 8 or 12 elements |
||||
* @param rvecs Output vector of rotation vectors (see Rodrigues ) estimated for each board view |
||||
* (e.g. std::vector<cv::Mat>>). That is, each k-th rotation vector together with the corresponding |
||||
* k-th translation vector (see the next output parameter description) brings the board pattern |
||||
* from the model coordinate space (in which object points are specified) to the world coordinate |
||||
* space, that is, a real position of the board pattern in the k-th pattern view (k=0.. *M* -1). |
||||
* @param tvecs Output vector of translation vectors estimated for each pattern view. |
||||
* @param stdDeviationsIntrinsics Output vector of standard deviations estimated for intrinsic parameters. |
||||
* Order of deviations values: |
||||
* \f$(f_x, f_y, c_x, c_y, k_1, k_2, p_1, p_2, k_3, k_4, k_5, k_6 , s_1, s_2, s_3, |
||||
* s_4, \tau_x, \tau_y)\f$ If one of parameters is not estimated, it's deviation is equals to zero. |
||||
* @param stdDeviationsExtrinsics Output vector of standard deviations estimated for extrinsic parameters. |
||||
* Order of deviations values: \f$(R_1, T_1, \dotsc , R_M, T_M)\f$ where M is number of pattern views, |
||||
* \f$R_i, T_i\f$ are concatenated 1x3 vectors. |
||||
* @param perViewErrors Output vector of average re-projection errors estimated for each pattern view. |
||||
* @param flags flags Different flags for the calibration process (see #calibrateCamera for details). |
||||
* @param criteria Termination criteria for the iterative optimization algorithm. |
||||
* |
||||
* This function calibrates a camera using a set of corners of a Charuco Board. The function |
||||
* receives a list of detected corners and its identifiers from several views of the Board. |
||||
* The function returns the final re-projection error. |
||||
*/ |
||||
CV_EXPORTS_AS(calibrateCameraCharucoExtended) |
||||
double calibrateCameraCharuco(InputArrayOfArrays charucoCorners, InputArrayOfArrays charucoIds, |
||||
const Ptr<CharucoBoard> &board, Size imageSize, InputOutputArray cameraMatrix, |
||||
InputOutputArray distCoeffs, OutputArrayOfArrays rvecs, OutputArrayOfArrays tvecs, |
||||
OutputArray stdDeviationsIntrinsics, OutputArray stdDeviationsExtrinsics, |
||||
OutputArray perViewErrors, int flags = 0, const TermCriteria& criteria = TermCriteria( |
||||
TermCriteria::COUNT + TermCriteria::EPS, 30, DBL_EPSILON)); |
||||
|
||||
/**
|
||||
* @brief It's the same function as #calibrateCameraCharuco but without calibration error estimation. |
||||
*/ |
||||
CV_EXPORTS_W double calibrateCameraCharuco(InputArrayOfArrays charucoCorners, InputArrayOfArrays charucoIds, |
||||
const Ptr<CharucoBoard> &board, Size imageSize, |
||||
InputOutputArray cameraMatrix, InputOutputArray distCoeffs, |
||||
OutputArrayOfArrays rvecs = noArray(), |
||||
OutputArrayOfArrays tvecs = noArray(), int flags = 0, |
||||
const TermCriteria& criteria=TermCriteria(TermCriteria::COUNT + |
||||
TermCriteria::EPS, 30, DBL_EPSILON)); |
||||
//! @}
|
||||
|
||||
} |
||||
} |
||||
#endif |
@ -0,0 +1,243 @@ |
||||
// 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 __OPENCV_ARUCO_BOARD_HPP__ |
||||
#define __OPENCV_ARUCO_BOARD_HPP__ |
||||
|
||||
#include <opencv2/core.hpp> |
||||
#include <vector> |
||||
|
||||
namespace cv { |
||||
namespace aruco { |
||||
//! @addtogroup aruco
|
||||
//! @{
|
||||
|
||||
class Dictionary; |
||||
|
||||
/**
|
||||
* @brief Board of markers |
||||
* |
||||
* A board is a set of markers in the 3D space with a common coordinate system. |
||||
* The common form of a board of marker is a planar (2D) board, however any 3D layout can be used. |
||||
* A Board object is composed by: |
||||
* - The object points of the marker corners, i.e. their coordinates respect to the board system. |
||||
* - The dictionary which indicates the type of markers of the board |
||||
* - The identifier of all the markers in the board. |
||||
*/ |
||||
class CV_EXPORTS_W Board { |
||||
public: |
||||
CV_WRAP Board(); |
||||
|
||||
/** @brief Provide way to create Board by passing necessary data. Specially needed in Python.
|
||||
* @param objPoints array of object points of all the marker corners in the board |
||||
* @param dictionary the dictionary of markers employed for this board |
||||
* @param ids vector of the identifiers of the markers in the board |
||||
*/ |
||||
CV_WRAP static Ptr<Board> create(InputArrayOfArrays objPoints, const Ptr<Dictionary> &dictionary, InputArray ids); |
||||
|
||||
/** @brief Set ids vector
|
||||
* @param ids vector of the identifiers of the markers in the board (should be the same size |
||||
* as objPoints) |
||||
* |
||||
* Recommended way to set ids vector, which will fail if the size of ids does not match size |
||||
* of objPoints. |
||||
*/ |
||||
CV_WRAP void setIds(InputArray ids); |
||||
|
||||
/** @brief change id for ids[index]
|
||||
* @param index - element index in ids |
||||
* @param newId - new value for ids[index], should be less than Dictionary size |
||||
*/ |
||||
CV_WRAP void changeId(int index, int newId); |
||||
/** @brief return ids
|
||||
*/ |
||||
CV_WRAP const std::vector<int>& getIds() const; |
||||
|
||||
/** @brief set dictionary
|
||||
*/ |
||||
CV_WRAP void setDictionary(const Ptr<Dictionary> &dictionary); |
||||
|
||||
/** @brief return dictionary
|
||||
*/ |
||||
CV_WRAP Ptr<Dictionary> getDictionary() const; |
||||
|
||||
/** @brief set objPoints
|
||||
*/ |
||||
CV_WRAP void setObjPoints(const std::vector<std::vector<Point3f> > &objPoints); |
||||
|
||||
/** @brief get objPoints
|
||||
*/ |
||||
CV_WRAP const std::vector<std::vector<Point3f> >& getObjPoints() const; |
||||
|
||||
/** @brief get rightBottomBorder
|
||||
*/ |
||||
CV_WRAP const Point3f& getRightBottomBorder() const; |
||||
|
||||
protected: |
||||
/** @brief array of object points of all the marker corners in the board each marker include its 4 corners in this order:
|
||||
* - objPoints[i][0] - left-top point of i-th marker |
||||
* - objPoints[i][1] - right-top point of i-th marker |
||||
* - objPoints[i][2] - right-bottom point of i-th marker |
||||
* - objPoints[i][3] - left-bottom point of i-th marker |
||||
* |
||||
* Markers are placed in a certain order - row by row, left to right in every row. |
||||
* For M markers, the size is Mx4. |
||||
*/ |
||||
CV_PROP std::vector<std::vector<Point3f> > objPoints; |
||||
|
||||
/// the dictionary of markers employed for this board
|
||||
CV_PROP Ptr<Dictionary> dictionary; |
||||
|
||||
/// coordinate of the bottom right corner of the board, is set when calling the function create()
|
||||
CV_PROP Point3f rightBottomBorder; |
||||
|
||||
/** @brief vector of the identifiers of the markers in the board (same size than objPoints)
|
||||
* The identifiers refers to the board dictionary |
||||
*/ |
||||
CV_PROP_RW std::vector<int> ids; |
||||
}; |
||||
|
||||
/**
|
||||
* @brief Draw a planar board |
||||
* @sa drawPlanarBoard |
||||
* |
||||
* @param board layout of the board that will be drawn. The board should be planar, |
||||
* z coordinate is ignored |
||||
* @param outSize size of the output image in pixels. |
||||
* @param img output image with the board. The size of this image will be outSize |
||||
* and the board will be on the center, keeping the board proportions. |
||||
* @param marginSize minimum margins (in pixels) of the board in the output image |
||||
* @param borderBits width of the marker borders. |
||||
* |
||||
* This function return the image of a planar board, ready to be printed. It assumes |
||||
* the Board layout specified is planar by ignoring the z coordinates of the object points. |
||||
*/ |
||||
CV_EXPORTS_W void drawPlanarBoard(const Ptr<Board> &board, Size outSize, OutputArray img, |
||||
int marginSize = 0, int borderBits = 1); |
||||
|
||||
/**
|
||||
* @brief Planar board with grid arrangement of markers |
||||
* More common type of board. All markers are placed in the same plane in a grid arrangement. |
||||
* The board can be drawn using drawPlanarBoard() function (@sa drawPlanarBoard) |
||||
*/ |
||||
|
||||
class CV_EXPORTS_W GridBoard : public Board { |
||||
public: |
||||
CV_WRAP GridBoard(); |
||||
/**
|
||||
* @brief Draw a GridBoard |
||||
* |
||||
* @param outSize size of the output image in pixels. |
||||
* @param img output image with the board. The size of this image will be outSize |
||||
* and the board will be on the center, keeping the board proportions. |
||||
* @param marginSize minimum margins (in pixels) of the board in the output image |
||||
* @param borderBits width of the marker borders. |
||||
* |
||||
* This function return the image of the GridBoard, ready to be printed. |
||||
*/ |
||||
CV_WRAP void draw(Size outSize, OutputArray img, int marginSize = 0, int borderBits = 1); |
||||
|
||||
/**
|
||||
* @brief Create a GridBoard object |
||||
* |
||||
* @param markersX number of markers in X direction |
||||
* @param markersY number of markers in Y direction |
||||
* @param markerLength marker side length (normally in meters) |
||||
* @param markerSeparation separation between two markers (same unit as markerLength) |
||||
* @param dictionary dictionary of markers indicating the type of markers |
||||
* @param firstMarker id of first marker in dictionary to use on board. |
||||
* @return the output GridBoard object |
||||
* |
||||
* This functions creates a GridBoard object given the number of markers in each direction and |
||||
* the marker size and marker separation. |
||||
*/ |
||||
CV_WRAP static Ptr<GridBoard> create(int markersX, int markersY, float markerLength, float markerSeparation, |
||||
const Ptr<Dictionary> &dictionary, int firstMarker = 0); |
||||
|
||||
CV_WRAP Size getGridSize() const; |
||||
CV_WRAP float getMarkerLength() const; |
||||
CV_WRAP float getMarkerSeparation() const; |
||||
|
||||
protected: |
||||
struct GridImpl; |
||||
Ptr<GridImpl> gridImpl; |
||||
friend class CharucoBoard; |
||||
}; |
||||
|
||||
/**
|
||||
* @brief ChArUco board |
||||
* Specific class for ChArUco boards. A ChArUco board is a planar board where the markers are placed |
||||
* inside the white squares of a chessboard. The benefits of ChArUco boards is that they provide |
||||
* both, ArUco markers versatility and chessboard corner precision, which is important for |
||||
* calibration and pose estimation. |
||||
* This class also allows the easy creation and drawing of ChArUco boards. |
||||
*/ |
||||
class CV_EXPORTS_W CharucoBoard : public Board { |
||||
public: |
||||
CV_WRAP CharucoBoard(); |
||||
|
||||
// vector of chessboard 3D corners precalculated
|
||||
CV_PROP std::vector<Point3f> chessboardCorners; |
||||
|
||||
// for each charuco corner, nearest marker id and nearest marker corner id of each marker
|
||||
CV_PROP std::vector<std::vector<int> > nearestMarkerIdx; |
||||
CV_PROP std::vector<std::vector<int> > nearestMarkerCorners; |
||||
|
||||
/** @brief Draw a ChArUco board
|
||||
* |
||||
* @param outSize size of the output image in pixels. |
||||
* @param img output image with the board. The size of this image will be outSize |
||||
* and the board will be on the center, keeping the board proportions. |
||||
* @param marginSize minimum margins (in pixels) of the board in the output image |
||||
* @param borderBits width of the marker borders. |
||||
* |
||||
* This function return the image of the ChArUco board, ready to be printed. |
||||
*/ |
||||
CV_WRAP void draw(Size outSize, OutputArray img, int marginSize = 0, int borderBits = 1); |
||||
|
||||
|
||||
/** @brief Create a CharucoBoard object
|
||||
* @param squaresX number of chessboard squares in X direction |
||||
* @param squaresY number of chessboard squares in Y direction |
||||
* @param squareLength chessboard square side length (normally in meters) |
||||
* @param markerLength marker side length (same unit than squareLength) |
||||
* @param dictionary dictionary of markers indicating the type of markers. |
||||
* The first markers in the dictionary are used to fill the white chessboard squares. |
||||
* @return the output CharucoBoard object |
||||
* |
||||
* This functions creates a CharucoBoard object given the number of squares in each direction |
||||
* and the size of the markers and chessboard squares. |
||||
*/ |
||||
CV_WRAP static Ptr<CharucoBoard> create(int squaresX, int squaresY, float squareLength, |
||||
float markerLength, const Ptr<Dictionary> &dictionary); |
||||
|
||||
CV_WRAP Size getChessboardSize() const; |
||||
CV_WRAP float getSquareLength() const; |
||||
CV_WRAP float getMarkerLength() const; |
||||
|
||||
protected: |
||||
struct CharucoImpl; |
||||
Ptr<CharucoImpl> charucoImpl; |
||||
}; |
||||
|
||||
/**
|
||||
* @brief test whether the ChArUco markers are collinear |
||||
* |
||||
* @param board layout of ChArUco board. |
||||
* @param charucoIds list of identifiers for each corner in charucoCorners per frame. |
||||
* @return bool value, 1 (true) if detected corners form a line, 0 (false) if they do not. |
||||
* solvePnP, calibration functions will fail if the corners are collinear (true). |
||||
* |
||||
* The number of ids in charucoIDs should be <= the number of chessboard corners in the board. |
||||
* This functions checks whether the charuco corners are on a straight line (returns true, if so), or not (false). |
||||
* Axis parallel, as well as diagonal and other straight lines detected. Degenerate cases: |
||||
* for number of charucoIDs <= 2,the function returns true. |
||||
*/ |
||||
CV_EXPORTS_W bool testCharucoCornersCollinear(const Ptr<CharucoBoard> &board, InputArray charucoIds); |
||||
|
||||
//! @}
|
||||
|
||||
} |
||||
} |
||||
|
||||
#endif |
@ -0,0 +1,436 @@ |
||||
// 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 __OPENCV_ARUCO_DETECTOR_HPP__ |
||||
#define __OPENCV_ARUCO_DETECTOR_HPP__ |
||||
#include <opencv2/aruco/board.hpp> |
||||
#include <opencv2/aruco/dictionary.hpp> |
||||
|
||||
/**
|
||||
* @defgroup aruco ArUco Marker Detection |
||||
* This module is dedicated to square fiducial markers (also known as Augmented Reality Markers) |
||||
* These markers are useful for easy, fast and robust camera pose estimation. |
||||
* |
||||
* The main functionality of ArucoDetector class is: |
||||
* - Detection of markers in an image |
||||
* |
||||
* There are even more functionalities implemented in charuco.hpp and aruco_calib_pose.hpp: |
||||
* - Pose estimation from a single marker or from a board/set of markers |
||||
* - Detection of ChArUco board for high subpixel accuracy |
||||
* - Camera calibration from both, ArUco boards and ChArUco boards. |
||||
* - Detection of ChArUco diamond markers |
||||
* The samples directory includes easy examples of how to use the module. |
||||
* |
||||
* The implementation is based on the ArUco Library by R. Muñoz-Salinas and S. Garrido-Jurado @cite Aruco2014. |
||||
* |
||||
* Markers can also be detected based on the AprilTag 2 @cite wang2016iros fiducial detection method. |
||||
* |
||||
* @sa S. Garrido-Jurado, R. Muñoz-Salinas, F. J. Madrid-Cuevas, and M. J. Marín-Jiménez. 2014. |
||||
* "Automatic generation and detection of highly reliable fiducial markers under occlusion". |
||||
* Pattern Recogn. 47, 6 (June 2014), 2280-2292. DOI=10.1016/j.patcog.2014.01.005 |
||||
* |
||||
* @sa http://www.uco.es/investiga/grupos/ava/node/26
|
||||
* |
||||
* This module has been originally developed by Sergio Garrido-Jurado as a project |
||||
* for Google Summer of Code 2015 (GSoC 15). |
||||
* |
||||
* |
||||
*/ |
||||
|
||||
namespace cv { |
||||
namespace aruco { |
||||
|
||||
//! @addtogroup aruco
|
||||
//! @{
|
||||
|
||||
enum CornerRefineMethod{ |
||||
CORNER_REFINE_NONE, ///< Tag and corners detection based on the ArUco approach
|
||||
CORNER_REFINE_SUBPIX, ///< ArUco approach and refine the corners locations using corner subpixel accuracy
|
||||
CORNER_REFINE_CONTOUR, ///< ArUco approach and refine the corners locations using the contour-points line fitting
|
||||
CORNER_REFINE_APRILTAG, ///< Tag and corners detection based on the AprilTag 2 approach @cite wang2016iros
|
||||
}; |
||||
|
||||
/**
|
||||
* @brief struct DetectorParameters is used by ArucoDetector |
||||
*/ |
||||
struct CV_EXPORTS_W DetectorParameters { |
||||
DetectorParameters() { |
||||
adaptiveThreshWinSizeMin = 3; |
||||
adaptiveThreshWinSizeMax = 23; |
||||
adaptiveThreshWinSizeStep = 10; |
||||
adaptiveThreshConstant = 7; |
||||
minMarkerPerimeterRate = 0.03; |
||||
maxMarkerPerimeterRate = 4.; |
||||
polygonalApproxAccuracyRate = 0.03; |
||||
minCornerDistanceRate = 0.05; |
||||
minDistanceToBorder = 3; |
||||
minMarkerDistanceRate = 0.05; |
||||
cornerRefinementMethod = CORNER_REFINE_NONE; |
||||
cornerRefinementWinSize = 5; |
||||
cornerRefinementMaxIterations = 30; |
||||
cornerRefinementMinAccuracy = 0.1; |
||||
markerBorderBits = 1; |
||||
perspectiveRemovePixelPerCell = 4; |
||||
perspectiveRemoveIgnoredMarginPerCell = 0.13; |
||||
maxErroneousBitsInBorderRate = 0.35; |
||||
minOtsuStdDev = 5.0; |
||||
errorCorrectionRate = 0.6; |
||||
aprilTagQuadDecimate = 0.0; |
||||
aprilTagQuadSigma = 0.0; |
||||
aprilTagMinClusterPixels = 5; |
||||
aprilTagMaxNmaxima = 10; |
||||
aprilTagCriticalRad = (float)(10* CV_PI /180); |
||||
aprilTagMaxLineFitMse = 10.0; |
||||
aprilTagMinWhiteBlackDiff = 5; |
||||
aprilTagDeglitch = 0; |
||||
detectInvertedMarker = false; |
||||
useAruco3Detection = false; |
||||
minSideLengthCanonicalImg = 32; |
||||
minMarkerLengthRatioOriginalImg = 0.0; |
||||
}; |
||||
|
||||
/** @brief Create a new set of DetectorParameters with default values.
|
||||
*/ |
||||
CV_WRAP static Ptr<DetectorParameters> create() { |
||||
Ptr<DetectorParameters> params = makePtr<DetectorParameters>(); |
||||
return params; |
||||
} |
||||
|
||||
/**
|
||||
* @brief Read a new set of DetectorParameters from FileNode (use FileStorage.root()). |
||||
*/ |
||||
CV_WRAP bool readDetectorParameters(const FileNode& fn); |
||||
|
||||
/**
|
||||
* @brief Write a set of DetectorParameters to FileStorage |
||||
*/ |
||||
CV_WRAP bool writeDetectorParameters(const Ptr<FileStorage>& fs); |
||||
|
||||
/// minimum window size for adaptive thresholding before finding contours (default 3).
|
||||
CV_PROP_RW int adaptiveThreshWinSizeMin; |
||||
|
||||
/// maximum window size for adaptive thresholding before finding contours (default 23).
|
||||
CV_PROP_RW int adaptiveThreshWinSizeMax; |
||||
|
||||
/// increments from adaptiveThreshWinSizeMin to adaptiveThreshWinSizeMax during the thresholding (default 10).
|
||||
CV_PROP_RW int adaptiveThreshWinSizeStep; |
||||
|
||||
/// constant for adaptive thresholding before finding contours (default 7)
|
||||
CV_PROP_RW double adaptiveThreshConstant; |
||||
|
||||
/** @brief determine minimum perimeter for marker contour to be detected. This is defined as a rate respect to the
|
||||
* maximum dimension of the input image (default 0.03). |
||||
*/ |
||||
CV_PROP_RW double minMarkerPerimeterRate; |
||||
|
||||
/** @brief determine maximum perimeter for marker contour to be detected. This is defined as a rate respect to
|
||||
* the maximum dimension of the input image (default 4.0). |
||||
*/ |
||||
CV_PROP_RW double maxMarkerPerimeterRate; |
||||
|
||||
/// minimum accuracy during the polygonal approximation process to determine which contours are squares. (default 0.03)
|
||||
CV_PROP_RW double polygonalApproxAccuracyRate; |
||||
|
||||
/// minimum distance between corners for detected markers relative to its perimeter (default 0.05)
|
||||
CV_PROP_RW double minCornerDistanceRate; |
||||
|
||||
/// minimum distance of any corner to the image border for detected markers (in pixels) (default 3)
|
||||
CV_PROP_RW int minDistanceToBorder; |
||||
|
||||
/** @brief minimum mean distance beetween two marker corners to be considered imilar, so that the
|
||||
* smaller one is removed. The rate is relative to the smaller perimeter of the two markers (default 0.05). |
||||
*/ |
||||
CV_PROP_RW double minMarkerDistanceRate; |
||||
|
||||
/** @brief default CORNER_REFINE_NONE.
|
||||
* 0:CORNER_REFINE_NONE, no refinement. |
||||
* 1: CORNER_REFINE_SUBPIX, do subpixel refinement. |
||||
* 2: CORNER_REFINE_CONTOUR use contour-Points, |
||||
* 3: CORNER_REFINE_APRILTAG use the AprilTag2 approach). |
||||
*/ |
||||
CV_PROP_RW int cornerRefinementMethod; |
||||
|
||||
/// window size for the corner refinement process (in pixels) (default 5).
|
||||
CV_PROP_RW int cornerRefinementWinSize; |
||||
|
||||
/// maximum number of iterations for stop criteria of the corner refinement process (default 30).
|
||||
CV_PROP_RW int cornerRefinementMaxIterations; |
||||
|
||||
/// minimum error for the stop cristeria of the corner refinement process (default: 0.1)
|
||||
CV_PROP_RW double cornerRefinementMinAccuracy; |
||||
|
||||
/// number of bits of the marker border, i.e. marker border width (default 1).
|
||||
CV_PROP_RW int markerBorderBits; |
||||
|
||||
/// number of bits (per dimension) for each cell of the marker when removing the perspective (default 4).
|
||||
CV_PROP_RW int perspectiveRemovePixelPerCell; |
||||
|
||||
/** @brief width of the margin of pixels on each cell not considered for the
|
||||
* determination of the cell bit. Represents the rate respect to the total size of the cell, i.e. |
||||
* perspectiveRemovePixelPerCell (default 0.13) |
||||
*/ |
||||
CV_PROP_RW double perspectiveRemoveIgnoredMarginPerCell; |
||||
|
||||
/** @brief maximum number of accepted erroneous bits in the border (i.e. number of allowed
|
||||
* white bits in the border). Represented as a rate respect to the total number of bits per marker (default 0.35). |
||||
*/ |
||||
CV_PROP_RW double maxErroneousBitsInBorderRate; |
||||
|
||||
/** @brief minimun standard deviation in pixels values during the decodification step to apply Otsu
|
||||
* thresholding (otherwise, all the bits are set to 0 or 1 depending on mean higher than 128 or not) (default 5.0) |
||||
*/ |
||||
CV_PROP_RW double minOtsuStdDev; |
||||
|
||||
/// error correction rate respect to the maximun error correction capability for each dictionary (default 0.6).
|
||||
CV_PROP_RW double errorCorrectionRate; |
||||
|
||||
/** @brief April :: User-configurable parameters.
|
||||
* detection of quads can be done on a lower-resolution image, improving speed at a cost of |
||||
* pose accuracy and a slight decrease in detection rate. Decoding the binary payload is still |
||||
*/ |
||||
CV_PROP_RW float aprilTagQuadDecimate; |
||||
|
||||
/// what Gaussian blur should be applied to the segmented image (used for quad detection?)
|
||||
CV_PROP_RW float aprilTagQuadSigma; |
||||
|
||||
// April :: Internal variables
|
||||
/// reject quads containing too few pixels (default 5).
|
||||
CV_PROP_RW int aprilTagMinClusterPixels; |
||||
|
||||
/// how many corner candidates to consider when segmenting a group of pixels into a quad (default 10).
|
||||
CV_PROP_RW int aprilTagMaxNmaxima; |
||||
|
||||
/** @brief reject quads where pairs of edges have angles that are close to straight or close to 180 degrees.
|
||||
* Zero means that no quads are rejected. (In radians) (default 10*PI/180) |
||||
*/ |
||||
CV_PROP_RW float aprilTagCriticalRad; |
||||
|
||||
/// when fitting lines to the contours, what is the maximum mean squared error
|
||||
CV_PROP_RW float aprilTagMaxLineFitMse; |
||||
|
||||
/** @brief when we build our model of black & white pixels, we add an extra check that the
|
||||
* white model must be (overall) brighter than the black model. How much brighter? (in pixel values, [0,255]). |
||||
* (default 5) |
||||
*/ |
||||
CV_PROP_RW int aprilTagMinWhiteBlackDiff; |
||||
|
||||
/// should the thresholded image be deglitched? Only useful for very noisy images (default 0).
|
||||
CV_PROP_RW int aprilTagDeglitch; |
||||
|
||||
/** @brief to check if there is a white marker. In order to generate a "white" marker just invert a
|
||||
* normal marker by using a tilde, ~markerImage. (default false) |
||||
*/ |
||||
CV_PROP_RW bool detectInvertedMarker; |
||||
|
||||
/** @brief new Aruco functionality proposed in the paper:
|
||||
* Romero-Ramirez et al: Speeded up detection of squared fiducial markers (2018) |
||||
* https://www.researchgate.net/publication/325787310_Speeded_Up_Detection_of_Squared_Fiducial_Markers
|
||||
*/ |
||||
|
||||
/// to enable the new and faster Aruco detection strategy.
|
||||
CV_PROP_RW bool useAruco3Detection; |
||||
|
||||
/// minimum side length of a marker in the canonical image. Latter is the binarized image in which contours are searched.
|
||||
CV_PROP_RW int minSideLengthCanonicalImg; |
||||
|
||||
/// range [0,1], eq (2) from paper. The parameter tau_i has a direct influence on the processing speed.
|
||||
CV_PROP_RW float minMarkerLengthRatioOriginalImg; |
||||
}; |
||||
|
||||
/**
|
||||
* @brief struct RefineParameters is used by ArucoDetector |
||||
*/ |
||||
struct CV_EXPORTS_W RefineParameters { |
||||
RefineParameters() { |
||||
minRepDistance = 10.f; |
||||
errorCorrectionRate = 3.f; |
||||
checkAllOrders = true; |
||||
} |
||||
|
||||
RefineParameters(float _minRepDistance, float _errorCorrectionRate, bool _checkAllOrders): |
||||
minRepDistance(_minRepDistance), errorCorrectionRate(_errorCorrectionRate), checkAllOrders(_checkAllOrders) {} |
||||
|
||||
CV_WRAP static Ptr<RefineParameters> create(float _minRepDistance = 10.f, float _errorCorrectionRate = 3.f, |
||||
bool _checkAllOrders = true) { |
||||
return makePtr<RefineParameters>(_minRepDistance, _errorCorrectionRate, _checkAllOrders); |
||||
} |
||||
|
||||
|
||||
/**
|
||||
* @brief Read a new set of RefineParameters from FileNode (use FileStorage.root()). |
||||
*/ |
||||
CV_WRAP bool readRefineParameters(const FileNode& fn); |
||||
|
||||
/** @brief Write a set of RefineParameters to FileStorage
|
||||
*/ |
||||
CV_WRAP bool writeRefineParameters(const Ptr<FileStorage>& fs); |
||||
|
||||
/** @brief minRepDistance minimum distance between the corners of the rejected candidate and the reprojected marker in
|
||||
* order to consider it as a correspondence. |
||||
*/ |
||||
CV_PROP_RW float minRepDistance; |
||||
/** @brief minRepDistance rate of allowed erroneous bits respect to the error correction
|
||||
* capability of the used dictionary. -1 ignores the error correction step. |
||||
*/ |
||||
CV_PROP_RW float errorCorrectionRate; |
||||
/** @brief checkAllOrders consider the four posible corner orders in the rejectedCorners array.
|
||||
* If it set to false, only the provided corner order is considered (default true). |
||||
*/ |
||||
CV_PROP_RW bool checkAllOrders; |
||||
}; |
||||
|
||||
/**
|
||||
* @brief The main functionality of ArucoDetector class is detection of markers in an image with detectMarkers() method. |
||||
* After detecting some markers in the image, you can try to find undetected markers from this dictionary with |
||||
* refineDetectedMarkers() method. |
||||
* @see DetectorParameters, RefineParameters |
||||
*/ |
||||
class CV_EXPORTS_W ArucoDetector : public Algorithm |
||||
{ |
||||
public: |
||||
/// dictionary indicates the type of markers that will be searched
|
||||
CV_PROP_RW Ptr<Dictionary> dictionary; |
||||
|
||||
/// marker detection parameters, check DetectorParameters docs to see available settings
|
||||
CV_PROP_RW Ptr<DetectorParameters> params; |
||||
|
||||
/// marker refine parameters
|
||||
CV_PROP_RW Ptr<RefineParameters> refineParams; |
||||
|
||||
/**
|
||||
* @brief Basic ArucoDetector constructor |
||||
* @param _dictionary indicates the type of markers that will be searched |
||||
* @param _params marker detection parameters |
||||
* @param _refineParams marker refine detection parameters |
||||
*/ |
||||
CV_WRAP ArucoDetector(const Ptr<Dictionary> &_dictionary = getPredefinedDictionary(DICT_4X4_50), |
||||
const Ptr<DetectorParameters> &_params = DetectorParameters::create(), |
||||
const Ptr<RefineParameters> &_refineParams = RefineParameters::create()): |
||||
dictionary(_dictionary), params(_params), refineParams(_refineParams) {} |
||||
|
||||
CV_WRAP static Ptr<ArucoDetector> create(const Ptr<Dictionary> &_dictionary, const Ptr<DetectorParameters> &_params) { |
||||
return makePtr<ArucoDetector>(_dictionary, _params); |
||||
} |
||||
|
||||
/**
|
||||
* @brief Basic marker detection |
||||
* |
||||
* @param image input image |
||||
* @param corners vector of detected marker corners. For each marker, its four corners |
||||
* are provided, (e.g std::vector<std::vector<cv::Point2f> > ). For N detected markers, |
||||
* the dimensions of this array is Nx4. The order of the corners is clockwise. |
||||
* @param ids vector of identifiers of the detected markers. The identifier is of type int |
||||
* (e.g. std::vector<int>). For N detected markers, the size of ids is also N. |
||||
* The identifiers have the same order than the markers in the imgPoints array. |
||||
* @param rejectedImgPoints contains the imgPoints of those squares whose inner code has not a |
||||
* correct codification. Useful for debugging purposes. |
||||
* |
||||
* Performs marker detection in the input image. Only markers included in the specific dictionary |
||||
* are searched. For each detected marker, it returns the 2D position of its corner in the image |
||||
* and its corresponding identifier. |
||||
* Note that this function does not perform pose estimation. |
||||
* @note The function does not correct lens distortion or takes it into account. It's recommended to undistort |
||||
* input image with corresponging camera model, if camera parameters are known |
||||
* @sa undistort, estimatePoseSingleMarkers, estimatePoseBoard |
||||
*/ |
||||
CV_WRAP void detectMarkers(InputArray image, OutputArrayOfArrays corners, OutputArray ids, |
||||
OutputArrayOfArrays rejectedImgPoints = noArray()); |
||||
|
||||
/**
|
||||
* @brief Refind not detected markers based on the already detected and the board layout |
||||
* |
||||
* @param image input image |
||||
* @param board layout of markers in the board. |
||||
* @param detectedCorners vector of already detected marker corners. |
||||
* @param detectedIds vector of already detected marker identifiers. |
||||
* @param rejectedCorners vector of rejected candidates during the marker detection process. |
||||
* @param cameraMatrix optional input 3x3 floating-point camera matrix |
||||
* \f$A = \vecthreethree{f_x}{0}{c_x}{0}{f_y}{c_y}{0}{0}{1}\f$ |
||||
* @param distCoeffs optional vector of distortion coefficients |
||||
* \f$(k_1, k_2, p_1, p_2[, k_3[, k_4, k_5, k_6],[s_1, s_2, s_3, s_4]])\f$ of 4, 5, 8 or 12 elements |
||||
* @param recoveredIdxs Optional array to returns the indexes of the recovered candidates in the |
||||
* original rejectedCorners array. |
||||
* |
||||
* This function tries to find markers that were not detected in the basic detecMarkers function. |
||||
* First, based on the current detected marker and the board layout, the function interpolates |
||||
* the position of the missing markers. Then it tries to find correspondence between the reprojected |
||||
* markers and the rejected candidates based on the minRepDistance and errorCorrectionRate |
||||
* parameters. |
||||
* If camera parameters and distortion coefficients are provided, missing markers are reprojected |
||||
* using projectPoint function. If not, missing marker projections are interpolated using global |
||||
* homography, and all the marker corners in the board must have the same Z coordinate. |
||||
*/ |
||||
CV_WRAP void refineDetectedMarkers(InputArray image, const Ptr<Board> &board, |
||||
InputOutputArrayOfArrays detectedCorners, |
||||
InputOutputArray detectedIds, InputOutputArrayOfArrays rejectedCorners, |
||||
InputArray cameraMatrix = noArray(), InputArray distCoeffs = noArray(), |
||||
OutputArray recoveredIdxs = noArray()); |
||||
|
||||
/** @brief Stores algorithm parameters in a file storage
|
||||
*/ |
||||
virtual void write(FileStorage& fs) const override { |
||||
Ptr<FileStorage> pfs = makePtr<FileStorage>(fs); |
||||
dictionary->writeDictionary(pfs); |
||||
params->writeDetectorParameters(pfs); |
||||
refineParams->writeRefineParameters(pfs); |
||||
} |
||||
|
||||
/** @brief simplified API for language bindings
|
||||
* @overload |
||||
*/ |
||||
CV_WRAP void write(const String& fileName) const { |
||||
FileStorage fs(fileName, FileStorage::WRITE); |
||||
write(fs); |
||||
} |
||||
|
||||
/** @brief Reads algorithm parameters from a file storage
|
||||
*/ |
||||
CV_WRAP virtual void read(const FileNode& fn) override { |
||||
dictionary->readDictionary(fn); |
||||
params->readDetectorParameters(fn); |
||||
refineParams->readRefineParameters(fn); |
||||
} |
||||
}; |
||||
|
||||
/**
|
||||
* @brief Draw detected markers in image |
||||
* |
||||
* @param image input/output image. It must have 1 or 3 channels. The number of channels is not |
||||
* altered. |
||||
* @param corners positions of marker corners on input image. |
||||
* (e.g std::vector<std::vector<cv::Point2f> > ). For N detected markers, the dimensions of |
||||
* this array should be Nx4. The order of the corners should be clockwise. |
||||
* @param ids vector of identifiers for markers in markersCorners . |
||||
* Optional, if not provided, ids are not painted. |
||||
* @param borderColor color of marker borders. Rest of colors (text color and first corner color) |
||||
* are calculated based on this one to improve visualization. |
||||
* |
||||
* Given an array of detected marker corners and its corresponding ids, this functions draws |
||||
* the markers in the image. The marker borders are painted and the markers identifiers if provided. |
||||
* Useful for debugging purposes. |
||||
* |
||||
*/ |
||||
CV_EXPORTS_W void drawDetectedMarkers(InputOutputArray image, InputArrayOfArrays corners, |
||||
InputArray ids = noArray(), Scalar borderColor = Scalar(0, 255, 0)); |
||||
|
||||
/**
|
||||
* @brief Draw a canonical marker image |
||||
* |
||||
* @param dictionary dictionary of markers indicating the type of markers |
||||
* @param id identifier of the marker that will be returned. It has to be a valid id |
||||
* in the specified dictionary. |
||||
* @param sidePixels size of the image in pixels |
||||
* @param img output image with the marker |
||||
* @param borderBits width of the marker border. |
||||
* |
||||
* This function returns a marker image in its canonical form (i.e. ready to be printed) |
||||
*/ |
||||
CV_EXPORTS_W void drawMarker(const Ptr<Dictionary> &dictionary, int id, int sidePixels, OutputArray img, |
||||
int borderBits = 1); |
||||
|
||||
//! @}
|
||||
|
||||
} |
||||
} |
||||
|
||||
#endif |
@ -0,0 +1,58 @@ |
||||
package org.opencv.test.aruco; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.List; |
||||
|
||||
import org.opencv.test.OpenCVTestCase; |
||||
import org.opencv.core.Scalar; |
||||
import org.opencv.core.Mat; |
||||
import org.opencv.core.CvType; |
||||
import org.opencv.aruco.*; |
||||
|
||||
|
||||
public class ArucoTest extends OpenCVTestCase { |
||||
|
||||
public void testArucoIssue3133() { |
||||
byte[][] marker = {{0,1,1},{1,1,1},{0,1,1}}; |
||||
Dictionary dictionary = Dictionary.create(1, 3); |
||||
dictionary.set_maxCorrectionBits(0); |
||||
Mat markerBits = new Mat(3, 3, CvType.CV_8UC1); |
||||
for (int i = 0; i < 3; i++) { |
||||
for (int j = 0; j < 3; j++) { |
||||
markerBits.put(i, j, marker[i][j]); |
||||
} |
||||
} |
||||
|
||||
Mat markerCompressed = Dictionary.getByteListFromBits(markerBits); |
||||
assertMatNotEqual(markerCompressed, dictionary.get_bytesList()); |
||||
|
||||
dictionary.set_bytesList(markerCompressed); |
||||
assertMatEqual(markerCompressed, dictionary.get_bytesList()); |
||||
} |
||||
|
||||
public void testArucoDetector() { |
||||
Dictionary dictionary = Dictionary.get(0); |
||||
DetectorParameters detectorParameters = DetectorParameters.create(); |
||||
ArucoDetector detector = ArucoDetector.create(dictionary, detectorParameters); |
||||
|
||||
Mat markerImage = new Mat(); |
||||
int id = 1, offset = 5, size = 40; |
||||
Aruco.drawMarker(dictionary, id, size, markerImage, detectorParameters.get_markerBorderBits()); |
||||
|
||||
Mat image = new Mat(markerImage.rows() + 2*offset, markerImage.cols() + 2*offset, |
||||
CvType.CV_8UC1, new Scalar(255)); |
||||
Mat m = image.submat(offset, size+offset, offset, size+offset); |
||||
markerImage.copyTo(m); |
||||
|
||||
List<Mat> corners = new ArrayList(); |
||||
Mat ids = new Mat(); |
||||
detector.detectMarkers(image, corners, ids); |
||||
|
||||
assertEquals(1, corners.size()); |
||||
Mat res = corners.get(0); |
||||
assertArrayEquals(new double[]{offset, offset}, res.get(0, 0), 0.0); |
||||
assertArrayEquals(new double[]{size + offset - 1, offset}, res.get(0, 1), 0.0); |
||||
assertArrayEquals(new double[]{size + offset - 1, size + offset - 1}, res.get(0, 2), 0.0); |
||||
assertArrayEquals(new double[]{offset, size + offset - 1}, res.get(0, 3), 0.0); |
||||
} |
||||
} |
@ -0,0 +1,257 @@ |
||||
// 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
|
||||
|
||||
#include <opencv2/aruco/aruco_calib_pose.hpp> |
||||
|
||||
namespace cv { |
||||
namespace aruco { |
||||
using namespace std; |
||||
|
||||
void getBoardObjectAndImagePoints(const Ptr<Board> &board, InputArrayOfArrays detectedCorners, InputArray detectedIds, |
||||
OutputArray objPoints, OutputArray imgPoints) { |
||||
CV_Assert(board->getIds().size() == board->getObjPoints().size()); |
||||
CV_Assert(detectedIds.total() == detectedCorners.total()); |
||||
|
||||
size_t nDetectedMarkers = detectedIds.total(); |
||||
|
||||
vector<Point3f > objPnts; |
||||
objPnts.reserve(nDetectedMarkers); |
||||
|
||||
vector<Point2f > imgPnts; |
||||
imgPnts.reserve(nDetectedMarkers); |
||||
|
||||
// look for detected markers that belong to the board and get their information
|
||||
for(unsigned int i = 0; i < nDetectedMarkers; i++) { |
||||
int currentId = detectedIds.getMat().ptr< int >(0)[i]; |
||||
for(unsigned int j = 0; j < board->getIds().size(); j++) { |
||||
if(currentId == board->getIds()[j]) { |
||||
for(int p = 0; p < 4; p++) { |
||||
objPnts.push_back(board->getObjPoints()[j][p]); |
||||
imgPnts.push_back(detectedCorners.getMat(i).ptr< Point2f >(0)[p]); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
// create output
|
||||
Mat(objPnts).copyTo(objPoints); |
||||
Mat(imgPnts).copyTo(imgPoints); |
||||
} |
||||
|
||||
/**
|
||||
* @brief Return object points for the system centered in a middle (by default) or in a top left corner of single |
||||
* marker, given the marker length |
||||
*/ |
||||
static Mat _getSingleMarkerObjectPoints(float markerLength, const EstimateParameters& estimateParameters) { |
||||
CV_Assert(markerLength > 0); |
||||
Mat objPoints(4, 1, CV_32FC3); |
||||
// set coordinate system in the top-left corner of the marker, with Z pointing out
|
||||
if (estimateParameters.pattern == ARUCO_CW_TOP_LEFT_CORNER) { |
||||
objPoints.ptr<Vec3f>(0)[0] = Vec3f(0.f, 0.f, 0); |
||||
objPoints.ptr<Vec3f>(0)[1] = Vec3f(markerLength, 0.f, 0); |
||||
objPoints.ptr<Vec3f>(0)[2] = Vec3f(markerLength, markerLength, 0); |
||||
objPoints.ptr<Vec3f>(0)[3] = Vec3f(0.f, markerLength, 0); |
||||
} |
||||
else if (estimateParameters.pattern == ARUCO_CCW_CENTER) { |
||||
objPoints.ptr<Vec3f>(0)[0] = Vec3f(-markerLength/2.f, markerLength/2.f, 0); |
||||
objPoints.ptr<Vec3f>(0)[1] = Vec3f(markerLength/2.f, markerLength/2.f, 0); |
||||
objPoints.ptr<Vec3f>(0)[2] = Vec3f(markerLength/2.f, -markerLength/2.f, 0); |
||||
objPoints.ptr<Vec3f>(0)[3] = Vec3f(-markerLength/2.f, -markerLength/2.f, 0); |
||||
} |
||||
else |
||||
CV_Error(Error::StsBadArg, "Unknown estimateParameters pattern"); |
||||
return objPoints; |
||||
} |
||||
|
||||
void estimatePoseSingleMarkers(InputArrayOfArrays _corners, float markerLength, |
||||
InputArray _cameraMatrix, InputArray _distCoeffs, |
||||
OutputArray _rvecs, OutputArray _tvecs, OutputArray _objPoints, |
||||
const Ptr<EstimateParameters>& estimateParameters) { |
||||
CV_Assert(markerLength > 0); |
||||
|
||||
Mat markerObjPoints = _getSingleMarkerObjectPoints(markerLength, *estimateParameters); |
||||
int nMarkers = (int)_corners.total(); |
||||
_rvecs.create(nMarkers, 1, CV_64FC3); |
||||
_tvecs.create(nMarkers, 1, CV_64FC3); |
||||
|
||||
Mat rvecs = _rvecs.getMat(), tvecs = _tvecs.getMat(); |
||||
|
||||
//// for each marker, calculate its pose
|
||||
parallel_for_(Range(0, nMarkers), [&](const Range& range) { |
||||
const int begin = range.start; |
||||
const int end = range.end; |
||||
|
||||
for (int i = begin; i < end; i++) { |
||||
solvePnP(markerObjPoints, _corners.getMat(i), _cameraMatrix, _distCoeffs, rvecs.at<Vec3d>(i), |
||||
tvecs.at<Vec3d>(i), estimateParameters->useExtrinsicGuess, estimateParameters->solvePnPMethod); |
||||
} |
||||
}); |
||||
|
||||
if(_objPoints.needed()){ |
||||
markerObjPoints.convertTo(_objPoints, -1); |
||||
} |
||||
} |
||||
|
||||
int estimatePoseBoard(InputArrayOfArrays _corners, InputArray _ids, const Ptr<Board> &board, |
||||
InputArray _cameraMatrix, InputArray _distCoeffs, InputOutputArray _rvec, |
||||
InputOutputArray _tvec, bool useExtrinsicGuess) { |
||||
CV_Assert(_corners.total() == _ids.total()); |
||||
|
||||
// get object and image points for the solvePnP function
|
||||
Mat objPoints, imgPoints; |
||||
getBoardObjectAndImagePoints(board, _corners, _ids, objPoints, imgPoints); |
||||
|
||||
CV_Assert(imgPoints.total() == objPoints.total()); |
||||
|
||||
if(objPoints.total() == 0) // 0 of the detected markers in board
|
||||
return 0; |
||||
|
||||
solvePnP(objPoints, imgPoints, _cameraMatrix, _distCoeffs, _rvec, _tvec, useExtrinsicGuess); |
||||
|
||||
// divide by four since all the four corners are concatenated in the array for each marker
|
||||
return (int)objPoints.total() / 4; |
||||
} |
||||
|
||||
/**
|
||||
* Check if a set of 3d points are enough for calibration. Z coordinate is ignored. |
||||
* Only axis parallel lines are considered |
||||
*/ |
||||
static bool _arePointsEnoughForPoseEstimation(const vector<Point3f> &points) { |
||||
if(points.size() < 4) return false; |
||||
|
||||
vector<double> sameXValue; // different x values in points
|
||||
vector<int> sameXCounter; // number of points with the x value in sameXValue
|
||||
for(unsigned int i = 0; i < points.size(); i++) { |
||||
bool found = false; |
||||
for(unsigned int j = 0; j < sameXValue.size(); j++) { |
||||
if(sameXValue[j] == points[i].x) { |
||||
found = true; |
||||
sameXCounter[j]++; |
||||
} |
||||
} |
||||
if(!found) { |
||||
sameXValue.push_back(points[i].x); |
||||
sameXCounter.push_back(1); |
||||
} |
||||
} |
||||
|
||||
// count how many x values has more than 2 points
|
||||
int moreThan2 = 0; |
||||
for(unsigned int i = 0; i < sameXCounter.size(); i++) { |
||||
if(sameXCounter[i] >= 2) moreThan2++; |
||||
} |
||||
|
||||
// if we have more than 1 two xvalues with more than 2 points, calibration is ok
|
||||
if(moreThan2 > 1) |
||||
return true; |
||||
return false; |
||||
} |
||||
|
||||
bool estimatePoseCharucoBoard(InputArray _charucoCorners, InputArray _charucoIds, |
||||
const Ptr<CharucoBoard> &_board, InputArray _cameraMatrix, InputArray _distCoeffs, |
||||
InputOutputArray _rvec, InputOutputArray _tvec, bool useExtrinsicGuess) { |
||||
CV_Assert((_charucoCorners.getMat().total() == _charucoIds.getMat().total())); |
||||
|
||||
// need, at least, 4 corners
|
||||
if(_charucoIds.getMat().total() < 4) return false; |
||||
|
||||
vector<Point3f> objPoints; |
||||
objPoints.reserve(_charucoIds.getMat().total()); |
||||
for(unsigned int i = 0; i < _charucoIds.getMat().total(); i++) { |
||||
int currId = _charucoIds.getMat().at< int >(i); |
||||
CV_Assert(currId >= 0 && currId < (int)_board->chessboardCorners.size()); |
||||
objPoints.push_back(_board->chessboardCorners[currId]); |
||||
} |
||||
|
||||
// points need to be in different lines, check if detected points are enough
|
||||
if(!_arePointsEnoughForPoseEstimation(objPoints)) return false; |
||||
|
||||
solvePnP(objPoints, _charucoCorners, _cameraMatrix, _distCoeffs, _rvec, _tvec, useExtrinsicGuess); |
||||
return true; |
||||
} |
||||
|
||||
double calibrateCameraAruco(InputArrayOfArrays _corners, InputArray _ids, InputArray _counter, |
||||
const Ptr<Board> &board, Size imageSize, InputOutputArray _cameraMatrix, |
||||
InputOutputArray _distCoeffs, OutputArrayOfArrays _rvecs, |
||||
OutputArrayOfArrays _tvecs, |
||||
OutputArray _stdDeviationsIntrinsics, |
||||
OutputArray _stdDeviationsExtrinsics, |
||||
OutputArray _perViewErrors, |
||||
int flags, const TermCriteria& criteria) { |
||||
// for each frame, get properly processed imagePoints and objectPoints for the calibrateCamera
|
||||
// function
|
||||
vector<Mat> processedObjectPoints, processedImagePoints; |
||||
size_t nFrames = _counter.total(); |
||||
int markerCounter = 0; |
||||
for(size_t frame = 0; frame < nFrames; frame++) { |
||||
int nMarkersInThisFrame = _counter.getMat().ptr< int >()[frame]; |
||||
vector<Mat> thisFrameCorners; |
||||
vector<int> thisFrameIds; |
||||
|
||||
CV_Assert(nMarkersInThisFrame > 0); |
||||
|
||||
thisFrameCorners.reserve((size_t) nMarkersInThisFrame); |
||||
thisFrameIds.reserve((size_t) nMarkersInThisFrame); |
||||
for(int j = markerCounter; j < markerCounter + nMarkersInThisFrame; j++) { |
||||
thisFrameCorners.push_back(_corners.getMat(j)); |
||||
thisFrameIds.push_back(_ids.getMat().ptr< int >()[j]); |
||||
} |
||||
markerCounter += nMarkersInThisFrame; |
||||
Mat currentImgPoints, currentObjPoints; |
||||
getBoardObjectAndImagePoints(board, thisFrameCorners, thisFrameIds, currentObjPoints, |
||||
currentImgPoints); |
||||
if(currentImgPoints.total() > 0 && currentObjPoints.total() > 0) { |
||||
processedImagePoints.push_back(currentImgPoints); |
||||
processedObjectPoints.push_back(currentObjPoints); |
||||
} |
||||
} |
||||
return calibrateCamera(processedObjectPoints, processedImagePoints, imageSize, _cameraMatrix, _distCoeffs, _rvecs, |
||||
_tvecs, _stdDeviationsIntrinsics, _stdDeviationsExtrinsics, _perViewErrors, flags, criteria); |
||||
} |
||||
|
||||
double calibrateCameraAruco(InputArrayOfArrays _corners, InputArray _ids, InputArray _counter, const Ptr<Board> &board, |
||||
Size imageSize, InputOutputArray _cameraMatrix, InputOutputArray _distCoeffs, |
||||
OutputArrayOfArrays _rvecs, OutputArrayOfArrays _tvecs, int flags, const TermCriteria& criteria) { |
||||
return calibrateCameraAruco(_corners, _ids, _counter, board, imageSize, _cameraMatrix, _distCoeffs, |
||||
_rvecs, _tvecs, noArray(), noArray(), noArray(), flags, criteria); |
||||
} |
||||
|
||||
double calibrateCameraCharuco(InputArrayOfArrays _charucoCorners, InputArrayOfArrays _charucoIds, |
||||
const Ptr<CharucoBoard> &_board, Size imageSize, |
||||
InputOutputArray _cameraMatrix, InputOutputArray _distCoeffs, |
||||
OutputArrayOfArrays _rvecs, OutputArrayOfArrays _tvecs, |
||||
OutputArray _stdDeviationsIntrinsics, |
||||
OutputArray _stdDeviationsExtrinsics, |
||||
OutputArray _perViewErrors, |
||||
int flags, const TermCriteria& criteria) { |
||||
CV_Assert(_charucoIds.total() > 0 && (_charucoIds.total() == _charucoCorners.total())); |
||||
|
||||
// Join object points of charuco corners in a single vector for calibrateCamera() function
|
||||
vector<vector<Point3f> > allObjPoints; |
||||
allObjPoints.resize(_charucoIds.total()); |
||||
for(unsigned int i = 0; i < _charucoIds.total(); i++) { |
||||
unsigned int nCorners = (unsigned int)_charucoIds.getMat(i).total(); |
||||
CV_Assert(nCorners > 0 && nCorners == _charucoCorners.getMat(i).total()); |
||||
allObjPoints[i].reserve(nCorners); |
||||
|
||||
for(unsigned int j = 0; j < nCorners; j++) { |
||||
int pointId = _charucoIds.getMat(i).at< int >(j); |
||||
CV_Assert(pointId >= 0 && pointId < (int)_board->chessboardCorners.size()); |
||||
allObjPoints[i].push_back(_board->chessboardCorners[pointId]); |
||||
} |
||||
} |
||||
return calibrateCamera(allObjPoints, _charucoCorners, imageSize, _cameraMatrix, _distCoeffs, _rvecs, _tvecs, |
||||
_stdDeviationsIntrinsics, _stdDeviationsExtrinsics, _perViewErrors, flags, criteria); |
||||
} |
||||
|
||||
double calibrateCameraCharuco(InputArrayOfArrays _charucoCorners, InputArrayOfArrays _charucoIds, |
||||
const Ptr<CharucoBoard> &_board, Size imageSize, InputOutputArray _cameraMatrix, |
||||
InputOutputArray _distCoeffs, OutputArrayOfArrays _rvecs, OutputArrayOfArrays _tvecs, |
||||
int flags, const TermCriteria& criteria) { |
||||
return calibrateCameraCharuco(_charucoCorners, _charucoIds, _board, imageSize, _cameraMatrix, _distCoeffs, _rvecs, |
||||
_tvecs, noArray(), noArray(), noArray(), flags, criteria); |
||||
} |
||||
|
||||
} |
||||
} |
@ -0,0 +1,50 @@ |
||||
// 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
|
||||
|
||||
#include "aruco_utils.hpp" |
||||
#include <opencv2/imgproc.hpp> |
||||
|
||||
namespace cv { |
||||
namespace aruco { |
||||
using namespace std; |
||||
|
||||
void _copyVector2Output(std::vector<std::vector<Point2f> > &vec, OutputArrayOfArrays out, const float scale) { |
||||
out.create((int)vec.size(), 1, CV_32FC2); |
||||
if(out.isMatVector()) { |
||||
for (unsigned int i = 0; i < vec.size(); i++) { |
||||
out.create(4, 1, CV_32FC2, i); |
||||
Mat &m = out.getMatRef(i); |
||||
Mat(Mat(vec[i]).t()*scale).copyTo(m); |
||||
} |
||||
} |
||||
else if(out.isUMatVector()) { |
||||
for (unsigned int i = 0; i < vec.size(); i++) { |
||||
out.create(4, 1, CV_32FC2, i); |
||||
UMat &m = out.getUMatRef(i); |
||||
Mat(Mat(vec[i]).t()*scale).copyTo(m); |
||||
} |
||||
} |
||||
else if(out.kind() == _OutputArray::STD_VECTOR_VECTOR){ |
||||
for (unsigned int i = 0; i < vec.size(); i++) { |
||||
out.create(4, 1, CV_32FC2, i); |
||||
Mat m = out.getMat(i); |
||||
Mat(Mat(vec[i]).t()*scale).copyTo(m); |
||||
} |
||||
} |
||||
else { |
||||
CV_Error(cv::Error::StsNotImplemented, |
||||
"Only Mat vector, UMat vector, and vector<vector> OutputArrays are currently supported."); |
||||
} |
||||
} |
||||
|
||||
void _convertToGrey(InputArray _in, OutputArray _out) { |
||||
CV_Assert(_in.type() == CV_8UC1 || _in.type() == CV_8UC3); |
||||
if(_in.type() == CV_8UC3) |
||||
cvtColor(_in, _out, COLOR_BGR2GRAY); |
||||
else |
||||
_in.copyTo(_out); |
||||
} |
||||
|
||||
} |
||||
} |
@ -0,0 +1,44 @@ |
||||
// 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 __OPENCV_ARUCO_UTILS_HPP__ |
||||
#define __OPENCV_ARUCO_UTILS_HPP__ |
||||
|
||||
#include <opencv2/core.hpp> |
||||
#include <vector> |
||||
|
||||
namespace cv { |
||||
namespace aruco { |
||||
|
||||
/**
|
||||
* @brief Copy the contents of a corners vector to an OutputArray, settings its size. |
||||
*/ |
||||
void _copyVector2Output(std::vector<std::vector<Point2f> > &vec, OutputArrayOfArrays out, const float scale = 1.f); |
||||
|
||||
/**
|
||||
* @brief Convert input image to gray if it is a 3-channels image |
||||
*/ |
||||
void _convertToGrey(InputArray _in, OutputArray _out); |
||||
|
||||
template<typename T> |
||||
inline bool readParameter(const std::string& name, T& parameter, const FileNode& node) |
||||
{ |
||||
if (!node.empty() && !node[name].empty()) { |
||||
node[name] >> parameter; |
||||
return true; |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
template<typename T> |
||||
inline bool readWriteParameter(const std::string& name, T& parameter, const Ptr<FileNode> readNode = nullptr, |
||||
const Ptr<FileStorage> writeStorage = nullptr) { |
||||
if (!readNode.empty()) |
||||
return readParameter(name, parameter, *readNode); |
||||
*writeStorage << name << parameter; |
||||
return true; |
||||
} |
||||
|
||||
} |
||||
} |
||||
#endif |
@ -0,0 +1,467 @@ |
||||
// 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
|
||||
|
||||
#include <opencv2/imgproc.hpp> |
||||
#include <opencv2/aruco/board.hpp> |
||||
#include <opencv2/aruco/dictionary.hpp> |
||||
|
||||
namespace cv { |
||||
namespace aruco { |
||||
using namespace std; |
||||
|
||||
/** @brief Implementation of drawPlanarBoard that accepts a raw Board pointer.
|
||||
*/ |
||||
static void _drawPlanarBoardImpl(Board *_board, Size outSize, OutputArray _img, int marginSize, int borderBits) { |
||||
CV_Assert(!outSize.empty()); |
||||
CV_Assert(marginSize >= 0); |
||||
|
||||
_img.create(outSize, CV_8UC1); |
||||
Mat out = _img.getMat(); |
||||
out.setTo(Scalar::all(255)); |
||||
out.adjustROI(-marginSize, -marginSize, -marginSize, -marginSize); |
||||
|
||||
// calculate max and min values in XY plane
|
||||
CV_Assert(_board->getObjPoints().size() > 0); |
||||
float minX, maxX, minY, maxY; |
||||
minX = maxX = _board->getObjPoints()[0][0].x; |
||||
minY = maxY = _board->getObjPoints()[0][0].y; |
||||
|
||||
for(unsigned int i = 0; i < _board->getObjPoints().size(); i++) { |
||||
for(int j = 0; j < 4; j++) { |
||||
minX = min(minX, _board->getObjPoints()[i][j].x); |
||||
maxX = max(maxX, _board->getObjPoints()[i][j].x); |
||||
minY = min(minY, _board->getObjPoints()[i][j].y); |
||||
maxY = max(maxY, _board->getObjPoints()[i][j].y); |
||||
} |
||||
} |
||||
|
||||
float sizeX = maxX - minX; |
||||
float sizeY = maxY - minY; |
||||
|
||||
// proportion transformations
|
||||
float xReduction = sizeX / float(out.cols); |
||||
float yReduction = sizeY / float(out.rows); |
||||
|
||||
// determine the zone where the markers are placed
|
||||
if(xReduction > yReduction) { |
||||
int nRows = int(sizeY / xReduction); |
||||
int rowsMargins = (out.rows - nRows) / 2; |
||||
out.adjustROI(-rowsMargins, -rowsMargins, 0, 0); |
||||
} else { |
||||
int nCols = int(sizeX / yReduction); |
||||
int colsMargins = (out.cols - nCols) / 2; |
||||
out.adjustROI(0, 0, -colsMargins, -colsMargins); |
||||
} |
||||
|
||||
// now paint each marker
|
||||
Dictionary &dictionary = *(_board->getDictionary()); |
||||
Mat marker; |
||||
Point2f outCorners[3]; |
||||
Point2f inCorners[3]; |
||||
for(unsigned int m = 0; m < _board->getObjPoints().size(); m++) { |
||||
// transform corners to markerZone coordinates
|
||||
for(int j = 0; j < 3; j++) { |
||||
Point2f pf = Point2f(_board->getObjPoints()[m][j].x, _board->getObjPoints()[m][j].y); |
||||
// move top left to 0, 0
|
||||
pf -= Point2f(minX, minY); |
||||
pf.x = pf.x / sizeX * float(out.cols); |
||||
pf.y = pf.y / sizeY * float(out.rows); |
||||
outCorners[j] = pf; |
||||
} |
||||
|
||||
// get marker
|
||||
Size dst_sz(outCorners[2] - outCorners[0]); // assuming CCW order
|
||||
dst_sz.width = dst_sz.height = std::min(dst_sz.width, dst_sz.height); //marker should be square
|
||||
dictionary.drawMarker(_board->getIds()[m], dst_sz.width, marker, borderBits); |
||||
|
||||
if((outCorners[0].y == outCorners[1].y) && (outCorners[1].x == outCorners[2].x)) { |
||||
// marker is aligned to image axes
|
||||
marker.copyTo(out(Rect(outCorners[0], dst_sz))); |
||||
continue; |
||||
} |
||||
|
||||
// interpolate tiny marker to marker position in markerZone
|
||||
inCorners[0] = Point2f(-0.5f, -0.5f); |
||||
inCorners[1] = Point2f(marker.cols - 0.5f, -0.5f); |
||||
inCorners[2] = Point2f(marker.cols - 0.5f, marker.rows - 0.5f); |
||||
|
||||
// remove perspective
|
||||
Mat transformation = getAffineTransform(inCorners, outCorners); |
||||
warpAffine(marker, out, transformation, out.size(), INTER_LINEAR, |
||||
BORDER_TRANSPARENT); |
||||
} |
||||
} |
||||
|
||||
void drawPlanarBoard(const Ptr<Board> &_board, Size outSize, OutputArray _img, int marginSize, |
||||
int borderBits) { |
||||
_drawPlanarBoardImpl(_board, outSize, _img, marginSize, borderBits); |
||||
} |
||||
|
||||
struct GridBoard::GridImpl { |
||||
GridImpl(){}; |
||||
// number of markers in X and Y directions
|
||||
int sizeX = 3, sizeY = 3; |
||||
|
||||
// marker side length (normally in meters)
|
||||
float markerLength = 1.f; |
||||
|
||||
// separation between markers in the grid
|
||||
float markerSeparation = .5f; |
||||
}; |
||||
|
||||
GridBoard::GridBoard(): gridImpl(makePtr<GridImpl>()) {} |
||||
|
||||
Board::Board(): dictionary(makePtr<Dictionary>(getPredefinedDictionary(PREDEFINED_DICTIONARY_NAME::DICT_4X4_50))) {} |
||||
|
||||
Ptr<Board> Board::create(InputArrayOfArrays objPoints, const Ptr<Dictionary> &dictionary, InputArray ids) { |
||||
CV_Assert(objPoints.total() == ids.total()); |
||||
CV_Assert(objPoints.type() == CV_32FC3 || objPoints.type() == CV_32FC1); |
||||
|
||||
std::vector<std::vector<Point3f> > obj_points_vector; |
||||
Point3f rightBottomBorder = Point3f(0.f, 0.f, 0.f); |
||||
for (unsigned int i = 0; i < objPoints.total(); i++) { |
||||
std::vector<Point3f> corners; |
||||
Mat corners_mat = objPoints.getMat(i); |
||||
|
||||
if (corners_mat.type() == CV_32FC1) |
||||
corners_mat = corners_mat.reshape(3); |
||||
CV_Assert(corners_mat.total() == 4); |
||||
|
||||
for (int j = 0; j < 4; j++) { |
||||
const Point3f &corner = corners_mat.at<Point3f>(j); |
||||
corners.push_back(corner); |
||||
rightBottomBorder.x = std::max(rightBottomBorder.x, corner.x); |
||||
rightBottomBorder.y = std::max(rightBottomBorder.y, corner.y); |
||||
rightBottomBorder.z = std::max(rightBottomBorder.z, corner.z); |
||||
} |
||||
obj_points_vector.push_back(corners); |
||||
} |
||||
Ptr<Board> res = makePtr<Board>(); |
||||
ids.copyTo(res->ids); |
||||
res->objPoints = obj_points_vector; |
||||
res->dictionary = cv::makePtr<Dictionary>(dictionary); |
||||
res->rightBottomBorder = rightBottomBorder; |
||||
return res; |
||||
} |
||||
|
||||
void Board::setIds(InputArray ids_) { |
||||
CV_Assert(objPoints.size() == ids_.total()); |
||||
ids_.copyTo(this->ids); |
||||
} |
||||
|
||||
Ptr<Dictionary> Board::getDictionary() const { |
||||
return this->dictionary; |
||||
} |
||||
|
||||
void Board::setDictionary(const Ptr<Dictionary> &_dictionary) { |
||||
this->dictionary = _dictionary; |
||||
} |
||||
|
||||
const std::vector<std::vector<Point3f> >& Board::getObjPoints() const { |
||||
return this->objPoints; |
||||
} |
||||
|
||||
void Board::setObjPoints(const vector<std::vector<Point3f>> &_objPoints) { |
||||
CV_Assert(!_objPoints.empty()); |
||||
this->objPoints = _objPoints; |
||||
rightBottomBorder = _objPoints.front().front(); |
||||
for (size_t i = 0; i < this->objPoints.size(); i++) { |
||||
for (int j = 0; j < 4; j++) { |
||||
const Point3f &corner = this->objPoints[i][j]; |
||||
rightBottomBorder.x = std::max(rightBottomBorder.x, corner.x); |
||||
rightBottomBorder.y = std::max(rightBottomBorder.y, corner.y); |
||||
rightBottomBorder.z = std::max(rightBottomBorder.z, corner.z); |
||||
} |
||||
} |
||||
} |
||||
|
||||
const Point3f& Board::getRightBottomBorder() const { |
||||
return this->rightBottomBorder; |
||||
} |
||||
|
||||
const std::vector<int>& Board::getIds() const { |
||||
return this->ids; |
||||
} |
||||
|
||||
void Board::changeId(int index, int newId) { |
||||
CV_Assert(index >= 0 && index < (int)ids.size()); |
||||
CV_Assert(newId >= 0 && newId < dictionary->bytesList.rows); |
||||
this->ids[index] = newId; |
||||
} |
||||
|
||||
Ptr<GridBoard> GridBoard::create(int markersX, int markersY, float markerLength, float markerSeparation, |
||||
const Ptr<Dictionary> &dictionary, int firstMarker) { |
||||
CV_Assert(markersX > 0 && markersY > 0 && markerLength > 0 && markerSeparation > 0); |
||||
Ptr<GridBoard> res = makePtr<GridBoard>(); |
||||
res->gridImpl->sizeX = markersX; |
||||
res->gridImpl->sizeY = markersY; |
||||
res->gridImpl->markerLength = markerLength; |
||||
res->gridImpl->markerSeparation = markerSeparation; |
||||
res->setDictionary(dictionary); |
||||
|
||||
size_t totalMarkers = (size_t) markersX * markersY; |
||||
res->ids.resize(totalMarkers); |
||||
std::vector<std::vector<Point3f> > objPoints; |
||||
objPoints.reserve(totalMarkers); |
||||
|
||||
// fill ids with first identifiers
|
||||
for (unsigned int i = 0; i < totalMarkers; i++) { |
||||
res->ids[i] = i + firstMarker; |
||||
} |
||||
|
||||
// calculate Board objPoints
|
||||
for (int y = 0; y < markersY; y++) { |
||||
for (int x = 0; x < markersX; x++) { |
||||
vector <Point3f> corners(4); |
||||
corners[0] = Point3f(x * (markerLength + markerSeparation), |
||||
y * (markerLength + markerSeparation), 0); |
||||
corners[1] = corners[0] + Point3f(markerLength, 0, 0); |
||||
corners[2] = corners[0] + Point3f(markerLength, markerLength, 0); |
||||
corners[3] = corners[0] + Point3f(0, markerLength, 0); |
||||
objPoints.push_back(corners); |
||||
} |
||||
} |
||||
res->setObjPoints(objPoints); |
||||
res->rightBottomBorder = Point3f(markersX * markerLength + markerSeparation * (markersX - 1), |
||||
markersY * markerLength + markerSeparation * (markersY - 1), 0.f); |
||||
return res; |
||||
} |
||||
|
||||
void GridBoard::draw(Size outSize, OutputArray _img, int marginSize, int borderBits) { |
||||
_drawPlanarBoardImpl((Board*)this, outSize, _img, marginSize, borderBits); |
||||
} |
||||
|
||||
Size GridBoard::getGridSize() const { |
||||
return Size(gridImpl->sizeX, gridImpl->sizeY); |
||||
} |
||||
|
||||
float GridBoard::getMarkerLength() const { |
||||
return gridImpl->markerLength; |
||||
} |
||||
|
||||
float GridBoard::getMarkerSeparation() const { |
||||
return gridImpl->markerSeparation; |
||||
} |
||||
|
||||
struct CharucoBoard::CharucoImpl : GridBoard::GridImpl { |
||||
// size of chessboard squares side (normally in meters)
|
||||
float squareLength; |
||||
|
||||
// marker side length (normally in meters)
|
||||
float markerLength; |
||||
}; |
||||
|
||||
CharucoBoard::CharucoBoard(): charucoImpl(makePtr<CharucoImpl>()) {} |
||||
|
||||
void CharucoBoard::draw(Size outSize, OutputArray _img, int marginSize, int borderBits) { |
||||
CV_Assert(!outSize.empty()); |
||||
CV_Assert(marginSize >= 0); |
||||
|
||||
_img.create(outSize, CV_8UC1); |
||||
_img.setTo(255); |
||||
Mat out = _img.getMat(); |
||||
Mat noMarginsImg = |
||||
out.colRange(marginSize, out.cols - marginSize).rowRange(marginSize, out.rows - marginSize); |
||||
|
||||
double totalLengthX, totalLengthY; |
||||
totalLengthX = charucoImpl->squareLength * charucoImpl->sizeX; |
||||
totalLengthY = charucoImpl->squareLength * charucoImpl->sizeY; |
||||
|
||||
// proportional transformation
|
||||
double xReduction = totalLengthX / double(noMarginsImg.cols); |
||||
double yReduction = totalLengthY / double(noMarginsImg.rows); |
||||
|
||||
// determine the zone where the chessboard is placed
|
||||
Mat chessboardZoneImg; |
||||
if(xReduction > yReduction) { |
||||
int nRows = int(totalLengthY / xReduction); |
||||
int rowsMargins = (noMarginsImg.rows - nRows) / 2; |
||||
chessboardZoneImg = noMarginsImg.rowRange(rowsMargins, noMarginsImg.rows - rowsMargins); |
||||
} else { |
||||
int nCols = int(totalLengthX / yReduction); |
||||
int colsMargins = (noMarginsImg.cols - nCols) / 2; |
||||
chessboardZoneImg = noMarginsImg.colRange(colsMargins, noMarginsImg.cols - colsMargins); |
||||
} |
||||
|
||||
// determine the margins to draw only the markers
|
||||
// take the minimum just to be sure
|
||||
double squareSizePixels = min(double(chessboardZoneImg.cols) / double(charucoImpl->sizeX), |
||||
double(chessboardZoneImg.rows) / double(charucoImpl->sizeY)); |
||||
|
||||
double diffSquareMarkerLength = (charucoImpl->squareLength - charucoImpl->markerLength) / 2; |
||||
int diffSquareMarkerLengthPixels = |
||||
int(diffSquareMarkerLength * squareSizePixels / charucoImpl->squareLength); |
||||
|
||||
// draw markers
|
||||
Mat markersImg; |
||||
_drawPlanarBoardImpl(this, chessboardZoneImg.size(), markersImg, diffSquareMarkerLengthPixels, borderBits); |
||||
markersImg.copyTo(chessboardZoneImg); |
||||
|
||||
// now draw black squares
|
||||
for(int y = 0; y < charucoImpl->sizeY; y++) { |
||||
for(int x = 0; x < charucoImpl->sizeX; x++) { |
||||
|
||||
if(y % 2 != x % 2) continue; // white corner, dont do anything
|
||||
|
||||
double startX, startY; |
||||
startX = squareSizePixels * double(x); |
||||
startY = squareSizePixels * double(y); |
||||
|
||||
Mat squareZone = chessboardZoneImg.rowRange(int(startY), int(startY + squareSizePixels)) |
||||
.colRange(int(startX), int(startX + squareSizePixels)); |
||||
|
||||
squareZone.setTo(0); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/**
|
||||
* Fill nearestMarkerIdx and nearestMarkerCorners arrays |
||||
*/ |
||||
static inline void _getNearestMarkerCorners(CharucoBoard &board, float squareLength) { |
||||
board.nearestMarkerIdx.resize(board.chessboardCorners.size()); |
||||
board.nearestMarkerCorners.resize(board.chessboardCorners.size()); |
||||
|
||||
unsigned int nMarkers = (unsigned int)board.getIds().size(); |
||||
unsigned int nCharucoCorners = (unsigned int)board.chessboardCorners.size(); |
||||
for(unsigned int i = 0; i < nCharucoCorners; i++) { |
||||
double minDist = -1; // distance of closest markers
|
||||
Point3f charucoCorner = board.chessboardCorners[i]; |
||||
for(unsigned int j = 0; j < nMarkers; j++) { |
||||
// calculate distance from marker center to charuco corner
|
||||
Point3f center = Point3f(0, 0, 0); |
||||
for(unsigned int k = 0; k < 4; k++) |
||||
center += board.getObjPoints()[j][k]; |
||||
center /= 4.; |
||||
double sqDistance; |
||||
Point3f distVector = charucoCorner - center; |
||||
sqDistance = distVector.x * distVector.x + distVector.y * distVector.y; |
||||
if(j == 0 || fabs(sqDistance - minDist) < cv::pow(0.01 * squareLength, 2)) { |
||||
// if same minimum distance (or first iteration), add to nearestMarkerIdx vector
|
||||
board.nearestMarkerIdx[i].push_back(j); |
||||
minDist = sqDistance; |
||||
} else if(sqDistance < minDist) { |
||||
// if finding a closest marker to the charuco corner
|
||||
board.nearestMarkerIdx[i].clear(); // remove any previous added marker
|
||||
board.nearestMarkerIdx[i].push_back(j); // add the new closest marker index
|
||||
minDist = sqDistance; |
||||
} |
||||
} |
||||
// for each of the closest markers, search the marker corner index closer
|
||||
// to the charuco corner
|
||||
for(unsigned int j = 0; j < board.nearestMarkerIdx[i].size(); j++) { |
||||
board.nearestMarkerCorners[i].resize(board.nearestMarkerIdx[i].size()); |
||||
double minDistCorner = -1; |
||||
for(unsigned int k = 0; k < 4; k++) { |
||||
double sqDistance; |
||||
Point3f distVector = charucoCorner - board.getObjPoints()[board.nearestMarkerIdx[i][j]][k]; |
||||
sqDistance = distVector.x * distVector.x + distVector.y * distVector.y; |
||||
if(k == 0 || sqDistance < minDistCorner) { |
||||
// if this corner is closer to the charuco corner, assing its index
|
||||
// to nearestMarkerCorners
|
||||
minDistCorner = sqDistance; |
||||
board.nearestMarkerCorners[i][j] = k; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
Ptr<CharucoBoard> CharucoBoard::create(int squaresX, int squaresY, float squareLength, |
||||
float markerLength, const Ptr<Dictionary> &dictionary) { |
||||
CV_Assert(squaresX > 1 && squaresY > 1 && markerLength > 0 && squareLength > markerLength); |
||||
Ptr<CharucoBoard> res = makePtr<CharucoBoard>(); |
||||
|
||||
res->charucoImpl->sizeX = squaresX; |
||||
res->charucoImpl->sizeY = squaresY; |
||||
res->charucoImpl->squareLength = squareLength; |
||||
res->charucoImpl->markerLength = markerLength; |
||||
res->setDictionary(dictionary); |
||||
std::vector<std::vector<Point3f> > objPoints; |
||||
|
||||
float diffSquareMarkerLength = (squareLength - markerLength) / 2; |
||||
// calculate Board objPoints
|
||||
for(int y = 0; y < squaresY; y++) { |
||||
for(int x = 0; x < squaresX; x++) { |
||||
|
||||
if(y % 2 == x % 2) continue; // black corner, no marker here
|
||||
|
||||
vector<Point3f> corners(4); |
||||
corners[0] = Point3f(x * squareLength + diffSquareMarkerLength, |
||||
y * squareLength + diffSquareMarkerLength, 0); |
||||
corners[1] = corners[0] + Point3f(markerLength, 0, 0); |
||||
corners[2] = corners[0] + Point3f(markerLength, markerLength, 0); |
||||
corners[3] = corners[0] + Point3f(0, markerLength, 0); |
||||
objPoints.push_back(corners); |
||||
// first ids in dictionary
|
||||
int nextId = (int)res->ids.size(); |
||||
res->ids.push_back(nextId); |
||||
} |
||||
} |
||||
res->setObjPoints(objPoints); |
||||
|
||||
// now fill chessboardCorners
|
||||
for(int y = 0; y < squaresY - 1; y++) { |
||||
for(int x = 0; x < squaresX - 1; x++) { |
||||
Point3f corner; |
||||
corner.x = (x + 1) * squareLength; |
||||
corner.y = (y + 1) * squareLength; |
||||
corner.z = 0; |
||||
res->chessboardCorners.push_back(corner); |
||||
} |
||||
} |
||||
res->rightBottomBorder = Point3f(squaresX * squareLength, |
||||
squaresY * squareLength, 0.f); |
||||
_getNearestMarkerCorners(*res, res->charucoImpl->squareLength); |
||||
return res; |
||||
} |
||||
|
||||
Size CharucoBoard::getChessboardSize() const { return Size(charucoImpl->sizeX, charucoImpl->sizeY); } |
||||
|
||||
float CharucoBoard::getSquareLength() const { return charucoImpl->squareLength; } |
||||
|
||||
float CharucoBoard::getMarkerLength() const { return charucoImpl->markerLength; } |
||||
|
||||
bool testCharucoCornersCollinear(const Ptr<CharucoBoard> &_board, InputArray _charucoIds) { |
||||
unsigned int nCharucoCorners = (unsigned int)_charucoIds.getMat().total(); |
||||
if (nCharucoCorners <= 2) |
||||
return true; |
||||
|
||||
// only test if there are 3 or more corners
|
||||
CV_Assert( _board->chessboardCorners.size() >= _charucoIds.getMat().total()); |
||||
|
||||
Vec<double, 3> point0( _board->chessboardCorners[_charucoIds.getMat().at< int >(0)].x, |
||||
_board->chessboardCorners[_charucoIds.getMat().at< int >(0)].y, 1); |
||||
|
||||
Vec<double, 3> point1( _board->chessboardCorners[_charucoIds.getMat().at< int >(1)].x, |
||||
_board->chessboardCorners[_charucoIds.getMat().at< int >(1)].y, 1); |
||||
|
||||
// create a line from the first two points.
|
||||
Vec<double, 3> testLine = point0.cross(point1); |
||||
Vec<double, 3> testPoint(0, 0, 1); |
||||
|
||||
double divisor = sqrt(testLine[0]*testLine[0] + testLine[1]*testLine[1]); |
||||
CV_Assert(divisor != 0.0); |
||||
|
||||
// normalize the line with normal
|
||||
testLine /= divisor; |
||||
|
||||
double dotProduct; |
||||
for (unsigned int i = 2; i < nCharucoCorners; i++){ |
||||
testPoint(0) = _board->chessboardCorners[_charucoIds.getMat().at< int >(i)].x; |
||||
testPoint(1) = _board->chessboardCorners[_charucoIds.getMat().at< int >(i)].y; |
||||
|
||||
// if testPoint is on testLine, dotProduct will be zero (or very, very close)
|
||||
dotProduct = testPoint.dot(testLine); |
||||
|
||||
if (std::abs(dotProduct) > 1e-6){ |
||||
return false; |
||||
} |
||||
} |
||||
// no points found that were off of testLine, return true that all points collinear.
|
||||
return true; |
||||
} |
||||
|
||||
} |
||||
} |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
After Width: | Height: | Size: 78 KiB |
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
@ -0,0 +1,28 @@ |
||||
#!/usr/bin/env python |
||||
import os |
||||
import cv2 as cv |
||||
import numpy as np |
||||
|
||||
from tests_common import NewOpenCVTests, unittest |
||||
|
||||
class cudastereo_test(NewOpenCVTests): |
||||
def setUp(self): |
||||
super(cudastereo_test, self).setUp() |
||||
if not cv.cuda.getCudaEnabledDeviceCount(): |
||||
self.skipTest("No CUDA-capable device is detected") |
||||
|
||||
def test_reprojectImageTo3D(self): |
||||
# Test's the functionality but not the results from reprojectImageTo3D |
||||
sz = (128,128) |
||||
np_disparity = np.random.randint(0, 64, sz, dtype=np.int16) |
||||
cu_disparity = cv.cuda_GpuMat(np_disparity) |
||||
np_q = np.random.randint(0, 100, (4, 4)).astype(np.float32) |
||||
stream = cv.cuda.Stream() |
||||
cu_xyz = cv.cuda.reprojectImageTo3D(cu_disparity, np_q, stream = stream) |
||||
self.assertTrue(cu_xyz.type() == cv.CV_32FC4 and cu_xyz.size() == sz) |
||||
cu_xyz1 = cv.cuda.GpuMat(sz, cv.CV_32FC3) |
||||
cv.cuda.reprojectImageTo3D(cu_disparity, np_q, cu_xyz1, 3, stream) |
||||
self.assertTrue(cu_xyz1.type() == cv.CV_32FC3 and cu_xyz1.size() == sz) |
||||
|
||||
if __name__ == '__main__': |
||||
NewOpenCVTests.bootstrap() |
@ -0,0 +1,250 @@ |
||||
// 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.
|
||||
#include "test_precomp.hpp" |
||||
|
||||
namespace opencv_test { namespace { |
||||
|
||||
struct MattypeParams |
||||
{ |
||||
string title; |
||||
int mattype; |
||||
bool expect_success; |
||||
}; |
||||
|
||||
::std::ostream& operator<<(::std::ostream& os, const MattypeParams& prm) { |
||||
return os << prm.title; |
||||
} |
||||
|
||||
const MattypeParams mattype_list[] = |
||||
{ |
||||
{ "CV_8UC1", CV_8UC1, true}, { "CV_8UC2", CV_8UC2, false}, |
||||
{ "CV_8UC3", CV_8UC3, true}, { "CV_8UC4", CV_8UC4, true}, |
||||
|
||||
{ "CV_8SC1", CV_8SC1, false}, { "CV_8SC2", CV_8SC2, false}, |
||||
{ "CV_8SC3", CV_8SC3, false}, { "CV_8SC4", CV_8SC4, false}, |
||||
{ "CV_16UC1", CV_16UC1, false}, { "CV_16UC2", CV_16UC2, false}, |
||||
{ "CV_16UC3", CV_16UC3, false}, { "CV_16UC4", CV_16UC4, false}, |
||||
{ "CV_16SC1", CV_16SC1, false}, { "CV_16SC2", CV_16SC2, false}, |
||||
{ "CV_16SC3", CV_16SC3, false}, { "CV_16SC4", CV_16SC4, false}, |
||||
{ "CV_32SC1", CV_32SC1, false}, { "CV_32SC2", CV_32SC2, false}, |
||||
{ "CV_32SC3", CV_32SC3, false}, { "CV_32SC4", CV_32SC4, false}, |
||||
{ "CV_32FC1", CV_32FC1, false}, { "CV_32FC2", CV_32FC2, false}, |
||||
{ "CV_32FC3", CV_32FC3, false}, { "CV_32FC4", CV_32FC4, false}, |
||||
{ "CV_64FC1", CV_64FC1, false}, { "CV_64FC2", CV_64FC2, false}, |
||||
{ "CV_64FC3", CV_64FC3, false}, { "CV_64FC4", CV_64FC4, false}, |
||||
{ "CV_16FC1", CV_16FC1, false}, { "CV_16FC2", CV_16FC2, false}, |
||||
{ "CV_16FC3", CV_16FC3, false}, { "CV_16FC4", CV_16FC4, false}, |
||||
}; |
||||
|
||||
/******************
|
||||
* Basically usage |
||||
*****************/ |
||||
|
||||
TEST(Freetype_Basic, success ) |
||||
{ |
||||
const string root = cvtest::TS::ptr()->get_data_path(); |
||||
const string fontdata = root + "freetype/mplus/Mplus1-Regular.ttf"; |
||||
|
||||
cv::Ptr<cv::freetype::FreeType2> ft2; |
||||
EXPECT_NO_THROW( ft2 = cv::freetype::createFreeType2() ); |
||||
EXPECT_NO_THROW( ft2->loadFontData( fontdata, 0 ) ); |
||||
|
||||
Mat dst(600,600, CV_8UC3, Scalar::all(255) ); |
||||
Scalar col(128,64,255,192); |
||||
EXPECT_NO_THROW( ft2->putText(dst, "Basic,success", Point( 0, 50), 50, col, -1, LINE_AA, true ) ); |
||||
} |
||||
|
||||
/******************
|
||||
* loadFontData() |
||||
*****************/ |
||||
|
||||
TEST(Freetype_loadFontData, nonexist_file) |
||||
{ |
||||
const string root = cvtest::TS::ptr()->get_data_path(); |
||||
const string fontdata = root + "UNEXITSTFONT"; /* NON EXISTS FONT DATA */ |
||||
|
||||
cv::Ptr<cv::freetype::FreeType2> ft2; |
||||
EXPECT_NO_THROW( ft2 = cv::freetype::createFreeType2() ); |
||||
EXPECT_THROW( ft2->loadFontData( fontdata, 0 ), cv::Exception ); |
||||
Mat dst(600,600, CV_8UC3, Scalar::all(255) ); |
||||
Scalar col(128,64,255,192); |
||||
EXPECT_THROW( ft2->putText(dst, "nonexist_file", Point( 0, 50), 50, col, -1, LINE_AA, true ), cv::Exception ); |
||||
} |
||||
|
||||
TEST(Freetype_loadFontData, forget_calling) |
||||
{ |
||||
cv::Ptr<cv::freetype::FreeType2> ft2; |
||||
EXPECT_NO_THROW( ft2 = cv::freetype::createFreeType2() ); |
||||
|
||||
const string root = cvtest::TS::ptr()->get_data_path(); |
||||
const string fontdata = root + "freetype/mplus/Mplus1-Regular.ttf"; |
||||
// EXPECT_NO_THROW( ft2->loadFontData( fontdata, 0 ) );
|
||||
|
||||
Mat dst(600,600, CV_8UC3, Scalar::all(255) ); |
||||
|
||||
Scalar col(128,64,255,192); |
||||
EXPECT_THROW( ft2->putText(dst, "forget_calling", Point( 0, 50), 50, col, -1, LINE_AA, true ), cv::Exception ); |
||||
} |
||||
|
||||
TEST(Freetype_loadFontData, call_multiple) |
||||
{ |
||||
cv::Ptr<cv::freetype::FreeType2> ft2; |
||||
EXPECT_NO_THROW( ft2 = cv::freetype::createFreeType2() ); |
||||
|
||||
const string root = cvtest::TS::ptr()->get_data_path(); |
||||
const string fontdata = root + "freetype/mplus/Mplus1-Regular.ttf"; |
||||
|
||||
for( int i = 0 ; i < 100 ; i ++ ) |
||||
{ |
||||
EXPECT_NO_THROW( ft2->loadFontData( fontdata, 0 ) ); |
||||
} |
||||
|
||||
Mat dst(600,600, CV_8UC3, Scalar::all(255) ); |
||||
Scalar col(128,64,255,192); |
||||
EXPECT_NO_THROW( ft2->putText(dst, "call_mutilple", Point( 0, 50), 50, col, -1, LINE_AA, true ) ); |
||||
} |
||||
|
||||
typedef testing::TestWithParam<int> idx_range; |
||||
|
||||
TEST_P(idx_range, failed ) |
||||
{ |
||||
cv::Ptr<cv::freetype::FreeType2> ft2; |
||||
EXPECT_NO_THROW( ft2 = cv::freetype::createFreeType2() ); |
||||
|
||||
const string root = cvtest::TS::ptr()->get_data_path(); |
||||
const string fontdata = root + "freetype/mplus/Mplus1-Regular.ttf"; |
||||
EXPECT_THROW( ft2->loadFontData( fontdata, GetParam() ), cv::Exception ); |
||||
} |
||||
|
||||
const int idx_failed_list[] = |
||||
{ |
||||
INT_MIN, |
||||
INT_MIN + 1, |
||||
-1, |
||||
1, |
||||
2, |
||||
INT_MAX - 1, |
||||
INT_MAX |
||||
}; |
||||
|
||||
INSTANTIATE_TEST_CASE_P(Freetype_loadFontData, idx_range, |
||||
testing::ValuesIn(idx_failed_list)); |
||||
|
||||
/******************
|
||||
* setSplitNumber() |
||||
*****************/ |
||||
|
||||
typedef testing::TestWithParam<int> ctol_range; |
||||
|
||||
TEST_P(ctol_range, success) |
||||
{ |
||||
cv::Ptr<cv::freetype::FreeType2> ft2; |
||||
EXPECT_NO_THROW( ft2 = cv::freetype::createFreeType2() ); |
||||
|
||||
const string root = cvtest::TS::ptr()->get_data_path(); |
||||
const string fontdata = root + "freetype/mplus/Mplus1-Regular.ttf"; |
||||
EXPECT_NO_THROW( ft2->loadFontData( fontdata, 0 ) ); |
||||
EXPECT_NO_THROW( ft2->setSplitNumber(GetParam()) ); |
||||
|
||||
Mat dst(600,600, CV_8UC3, Scalar::all(255) ); |
||||
Scalar col(128,64,255,192); |
||||
EXPECT_NO_THROW( ft2->putText(dst, "CtoL", Point( 0, 50), 50, col, 1, LINE_4, true ) ); |
||||
EXPECT_NO_THROW( ft2->putText(dst, "LINE_4: oOpPqQ", Point( 40, 100), 50, col, 1, LINE_4, true ) ); |
||||
EXPECT_NO_THROW( ft2->putText(dst, "LINE_8: oOpPqQ", Point( 40, 150), 50, col, 1, LINE_8, true ) ); |
||||
EXPECT_NO_THROW( ft2->putText(dst, "LINE_AA:oOpPqQ", Point( 40, 150), 50, col, 1, LINE_AA, true ) ); |
||||
} |
||||
|
||||
const int ctol_list[] = |
||||
{ |
||||
1, |
||||
8, |
||||
16, |
||||
32, |
||||
64, |
||||
128, |
||||
// INT_MAX -1, // Hang-up
|
||||
// INT_MAX // Hang-up
|
||||
}; |
||||
|
||||
INSTANTIATE_TEST_CASE_P(Freetype_setSplitNumber, ctol_range, |
||||
testing::ValuesIn(ctol_list)); |
||||
|
||||
|
||||
/********************
|
||||
* putText()::common |
||||
*******************/ |
||||
|
||||
TEST(Freetype_putText, invalid_img ) |
||||
{ |
||||
const string root = cvtest::TS::ptr()->get_data_path(); |
||||
const string fontdata = root + "freetype/mplus/Mplus1-Regular.ttf"; |
||||
|
||||
cv::Ptr<cv::freetype::FreeType2> ft2; |
||||
EXPECT_NO_THROW( ft2 = cv::freetype::createFreeType2() ); |
||||
EXPECT_NO_THROW( ft2->loadFontData( fontdata, 0 ) ); |
||||
|
||||
Scalar col(128,64,255,192); |
||||
{ /* empty mat */ |
||||
Mat dst; |
||||
EXPECT_THROW( ft2->putText(dst, "Invalid_img(empty Mat)", Point( 0, 50), 50, col, -1, LINE_AA, true ), cv::Exception ); |
||||
} |
||||
{ /* not mat(scalar) */ |
||||
Scalar dst; |
||||
EXPECT_THROW( ft2->putText(dst, "Invalid_img(Scalar)", Point( 0, 50), 50, col, -1, LINE_AA, true ), cv::Exception ); |
||||
} |
||||
} |
||||
|
||||
typedef testing::TestWithParam<MattypeParams> MatType_Test; |
||||
|
||||
TEST_P(MatType_Test, default) |
||||
{ |
||||
const string root = cvtest::TS::ptr()->get_data_path(); |
||||
const string fontdata = root + "freetype/mplus/Mplus1-Regular.ttf"; |
||||
|
||||
const MattypeParams params = static_cast<MattypeParams>(GetParam()); |
||||
const string title = params.title; |
||||
const int mattype = params.mattype; |
||||
const bool expect_success = params.expect_success; |
||||
|
||||
cv::Ptr<cv::freetype::FreeType2> ft2; |
||||
EXPECT_NO_THROW( ft2 = cv::freetype::createFreeType2() ); |
||||
EXPECT_NO_THROW( ft2->loadFontData( fontdata, 0 ) ); |
||||
|
||||
Mat dst(600,600, mattype, Scalar::all(255) ); |
||||
|
||||
Scalar col(128,64,255,192); |
||||
|
||||
if ( expect_success == false ) |
||||
{ |
||||
EXPECT_THROW( ft2->putText(dst, title, Point( 0, 50), 50, col, -1, LINE_AA, true ), cv::Exception ); |
||||
return; |
||||
} |
||||
|
||||
EXPECT_NO_THROW( ft2->putText(dst, title, Point( 0, 50), 50, col, -1, LINE_AA, true ) ); |
||||
EXPECT_NO_THROW( ft2->putText(dst, "LINE_4 FILL(mono)", Point(40, 100), 50, col, -1, LINE_4, true ) ); |
||||
EXPECT_NO_THROW( ft2->putText(dst, "LINE_8 FILL(mono)", Point(40, 150), 50, col, -1, LINE_8, true ) ); |
||||
EXPECT_NO_THROW( ft2->putText(dst, "LINE_AA FILL(blend)",Point(40, 200), 50, col, -1, LINE_AA, true ) ); |
||||
EXPECT_NO_THROW( ft2->putText(dst, "LINE_4 OUTLINE(1)", Point(40, 250), 50, col, 1, LINE_4, true ) ); |
||||
EXPECT_NO_THROW( ft2->putText(dst, "LINE_8 OUTLINE(1)", Point(40, 300), 50, col, 1, LINE_8, true ) ); |
||||
EXPECT_NO_THROW( ft2->putText(dst, "LINE_AA OUTLINE(1)", Point(40, 350), 50, col, 1, LINE_AA, true ) ); |
||||
EXPECT_NO_THROW( ft2->putText(dst, "LINE_4 OUTLINE(5)", Point(40, 400), 50, col, 5, LINE_4, true ) ); |
||||
EXPECT_NO_THROW( ft2->putText(dst, "LINE_8 OUTLINE(5)", Point(40, 450), 50, col, 5, LINE_8, true ) ); |
||||
EXPECT_NO_THROW( ft2->putText(dst, "LINE_AA OUTLINE(5)", Point(40, 500), 50, col, 5, LINE_AA, true ) ); |
||||
putText(dst, "LINE_4 putText(th=1)" , Point( 40,550), FONT_HERSHEY_SIMPLEX, 0.5, col, 1, LINE_4); |
||||
putText(dst, "LINE_8 putText(th=1)" , Point( 40,565), FONT_HERSHEY_SIMPLEX, 0.5, col, 1, LINE_8); |
||||
putText(dst, "LINE_AA putText(th=1)", Point( 40,580), FONT_HERSHEY_SIMPLEX, 0.5, col, 1, LINE_AA); |
||||
putText(dst, "LINE_4 putText(th=2)" , Point( 240,550),FONT_HERSHEY_SIMPLEX, 0.5, col, 2, LINE_4); |
||||
putText(dst, "LINE_8 putText(th=2)" , Point( 240,565),FONT_HERSHEY_SIMPLEX, 0.5, col, 2, LINE_8); |
||||
putText(dst, "LINE_AA putText(th=2)", Point( 240,580),FONT_HERSHEY_SIMPLEX, 0.5, col, 2, LINE_AA); |
||||
|
||||
if (cvtest::debugLevel > 0 ) |
||||
{ |
||||
imwrite( cv::format("%s-MatType.png", title.c_str()), dst ); |
||||
} |
||||
} |
||||
|
||||
INSTANTIATE_TEST_CASE_P(Freetype_putText, MatType_Test, |
||||
testing::ValuesIn(mattype_list)); |
||||
|
||||
}} // namespace
|
@ -0,0 +1,6 @@ |
||||
// 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.
|
||||
#include "test_precomp.hpp" |
||||
|
||||
CV_TEST_MAIN("cv") |
@ -0,0 +1,10 @@ |
||||
// 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 __OPENCV_TEST_PRECOMP_HPP__ |
||||
#define __OPENCV_TEST_PRECOMP_HPP__ |
||||
|
||||
#include "opencv2/freetype.hpp" |
||||
#include "opencv2/ts.hpp" |
||||
|
||||
#endif |
@ -0,0 +1,201 @@ |
||||
// 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.
|
||||
#include "test_precomp.hpp" |
||||
|
||||
namespace opencv_test { namespace { |
||||
|
||||
struct DrawingParams |
||||
{ |
||||
string title; |
||||
int mattype; |
||||
string fontname; |
||||
}; |
||||
|
||||
::std::ostream& operator<<(::std::ostream& os, const DrawingParams& prm) { |
||||
return os << prm.title; |
||||
} |
||||
|
||||
const DrawingParams drawing_list[] = |
||||
{ |
||||
{ "CV_8UC1-Mplus1-Regular", CV_8UC1, "freetype/mplus/Mplus1-Regular.ttf"}, |
||||
{ "CV_8UC3-Mplus1-Regular", CV_8UC3, "freetype/mplus/Mplus1-Regular.ttf"}, |
||||
{ "CV_8UC4-Mplus1-Regular", CV_8UC4, "freetype/mplus/Mplus1-Regular.ttf"}, |
||||
}; |
||||
|
||||
/********************
|
||||
* putText()::boundry |
||||
*******************/ |
||||
typedef testing::TestWithParam<DrawingParams> BoundaryTest; |
||||
|
||||
TEST_P(BoundaryTest, default) |
||||
{ |
||||
const DrawingParams params = GetParam(); |
||||
const string title = params.title; |
||||
const int mattype = params.mattype; |
||||
const string fontname = params.fontname; |
||||
|
||||
const string root = cvtest::TS::ptr()->get_data_path(); |
||||
const string fontdata = root + fontname; |
||||
|
||||
cv::Ptr<cv::freetype::FreeType2> ft2; |
||||
EXPECT_NO_THROW( ft2 = cv::freetype::createFreeType2() ); |
||||
EXPECT_NO_THROW( ft2->loadFontData( fontdata, 0 ) ); |
||||
|
||||
Mat dst(600,600, mattype, Scalar::all(255) ); |
||||
|
||||
Scalar col(128,64,255,192); |
||||
EXPECT_NO_THROW( ft2->putText(dst, title, Point( 100, 200), 20, col, -1, LINE_AA, true ) ); |
||||
|
||||
const int textHeight = 30; |
||||
for ( int iy = -50 ; iy <= +50 ; iy++ ) |
||||
{ |
||||
Point textOrg( 50, iy ); |
||||
const string text = "top boundary"; |
||||
EXPECT_NO_THROW( ft2->putText(dst, text, textOrg, textHeight, col, -1, LINE_4, true ) ); |
||||
EXPECT_NO_THROW( ft2->putText(dst, text, textOrg, textHeight, col, -1, LINE_8, true ) ); |
||||
EXPECT_NO_THROW( ft2->putText(dst, text, textOrg, textHeight, col, -1, LINE_AA, true ) ); |
||||
} |
||||
|
||||
for ( int iy = -50 ; iy <= +50 ; iy++ ) |
||||
{ |
||||
Point textOrg( 400, dst.cols + iy ); |
||||
const string text = "bottom boundary"; |
||||
EXPECT_NO_THROW( ft2->putText(dst, text, textOrg, textHeight, col, -1, LINE_4, true ) ); |
||||
EXPECT_NO_THROW( ft2->putText(dst, text, textOrg, textHeight, col, -1, LINE_8, true ) ); |
||||
EXPECT_NO_THROW( ft2->putText(dst, text, textOrg, textHeight, col, -1, LINE_AA, true ) ); |
||||
} |
||||
|
||||
for ( int ix = -50 ; ix <= +50 ; ix++ ) |
||||
{ |
||||
Point textOrg( ix, 100 ); |
||||
const string text = "left boundary"; |
||||
EXPECT_NO_THROW( ft2->putText(dst, text, textOrg, textHeight, col, -1, LINE_4, true ) ); |
||||
EXPECT_NO_THROW( ft2->putText(dst, text, textOrg, textHeight, col, -1, LINE_8, true ) ); |
||||
EXPECT_NO_THROW( ft2->putText(dst, text, textOrg, textHeight, col, -1, LINE_AA, true ) ); |
||||
} |
||||
|
||||
for ( int ix = -50 ; ix <= +50 ; ix++ ) |
||||
{ |
||||
Point textOrg( dst.rows + ix, 500 ); |
||||
const string text = "bottom boundary"; |
||||
EXPECT_NO_THROW( ft2->putText(dst, text, textOrg, textHeight, col, -1, LINE_4, true ) ); |
||||
EXPECT_NO_THROW( ft2->putText(dst, text, textOrg, textHeight, col, -1, LINE_8, true ) ); |
||||
EXPECT_NO_THROW( ft2->putText(dst, text, textOrg, textHeight, col, -1, LINE_AA, true ) ); |
||||
} |
||||
|
||||
if (cvtest::debugLevel > 0 ) |
||||
{ |
||||
imwrite( cv::format("%s-boundary.png", title.c_str()), dst ); |
||||
} |
||||
} |
||||
|
||||
INSTANTIATE_TEST_CASE_P(Freetype_putText, BoundaryTest, |
||||
testing::ValuesIn(drawing_list)) ; |
||||
|
||||
/*********************
|
||||
* putText()::Ligature |
||||
*********************/ |
||||
|
||||
// See https://github.com/opencv/opencv_contrib/issues/2627
|
||||
|
||||
static Mat clipRoiAs8UC1( Mat &dst, Rect roi_rect ) |
||||
{ |
||||
Mat roi = Mat(dst, roi_rect).clone(); |
||||
switch( roi.type() ){ |
||||
case CV_8UC4: cvtColor(roi,roi,COLOR_BGRA2GRAY); break; |
||||
case CV_8UC3: cvtColor(roi,roi,COLOR_BGR2GRAY); break; |
||||
case CV_8UC1: default: break; // Do nothing
|
||||
} |
||||
return roi; |
||||
} |
||||
|
||||
typedef testing::TestWithParam<DrawingParams> LigatureTest; |
||||
TEST_P(LigatureTest, regression2627) |
||||
{ |
||||
const DrawingParams params = GetParam(); |
||||
const string title = params.title; |
||||
const int mattype = params.mattype; |
||||
const string fontname = params.fontname; |
||||
|
||||
const string root = cvtest::TS::ptr()->get_data_path(); |
||||
const string fontdata = root + fontname; |
||||
|
||||
cv::Ptr<cv::freetype::FreeType2> ft2; |
||||
EXPECT_NO_THROW( ft2 = cv::freetype::createFreeType2() ); |
||||
EXPECT_NO_THROW( ft2->loadFontData( fontdata, 0 ) ); |
||||
|
||||
Mat dst(600,600, mattype, Scalar(0,0,0,255) ); |
||||
Scalar col(255,255,255,255); |
||||
EXPECT_NO_THROW( ft2->putText(dst, title, Point( 0, 50), 30, col, -1, LINE_AA, true ) ); |
||||
|
||||
vector<string> texts = { |
||||
"ffi", // ff will be combined to single glyph.
|
||||
"fs", |
||||
"fi", |
||||
"ff", |
||||
"ae", |
||||
"tz", |
||||
"oe", |
||||
"\xE3\x81\xAF", // HA ( HIRAGANA )
|
||||
"\xE3\x81\xAF\xE3\x82\x99", // BA ( HA + VOICED SOUND MARK )
|
||||
"\xE3\x81\xAF\xE3\x82\x9A", // PA ( HA + SEMI-VOICED SOUND MARK )
|
||||
"\xE3\x83\x8F", // HA ( KATAKANA )
|
||||
"\xE3\x83\x8F\xE3\x82\x99", // BA ( HA + VOICED SOUND MARK )
|
||||
"\xE3\x83\x8F\xE3\x82\x9A", // PA ( HA + SEMI-VOICED SOUND MARK )
|
||||
}; |
||||
|
||||
const int fontHeight = 20; |
||||
const int margin = fontHeight / 2; // for current glyph right edgeto next glyph left edge
|
||||
|
||||
const int start_x = 40; |
||||
const int start_y = 100; |
||||
const int skip_x = 100; |
||||
const int skip_y = 25; |
||||
|
||||
int tx = start_x; |
||||
int ty = start_y; |
||||
|
||||
for (auto it = texts.begin(); it != texts.end(); it++ ) |
||||
{ |
||||
if ( ty + fontHeight * 3 > dst.rows ) { |
||||
ty = start_y; |
||||
tx = tx + skip_x; |
||||
} |
||||
|
||||
EXPECT_NO_THROW( ft2->putText(dst, *it, Point(tx,ty), fontHeight, col, -1, LINE_4, true ) ); |
||||
|
||||
{ // Check for next glyph area.
|
||||
const Rect roi_rect = Rect( tx + fontHeight + margin, ty - fontHeight, fontHeight, fontHeight ); |
||||
const Mat roi = clipRoiAs8UC1(dst, roi_rect); |
||||
EXPECT_EQ(0, countNonZero(roi) ); |
||||
} |
||||
ty += skip_y; |
||||
|
||||
EXPECT_NO_THROW( ft2->putText(dst, *it, Point(tx,ty), fontHeight, col, -1, LINE_8, true ) ); |
||||
{ // Check for next glyph area.
|
||||
const Rect roi_rect = Rect( tx + fontHeight + margin, ty - fontHeight, fontHeight, fontHeight ); |
||||
const Mat roi = clipRoiAs8UC1(dst, roi_rect); |
||||
EXPECT_EQ(0, countNonZero(roi) ); |
||||
} |
||||
ty += skip_y; |
||||
|
||||
EXPECT_NO_THROW( ft2->putText(dst, *it, Point(tx,ty), fontHeight, col, 1, LINE_AA, true ) ); |
||||
{ // Check for next glyph area.
|
||||
const Rect roi_rect = Rect( tx + fontHeight + margin, ty - fontHeight, fontHeight, fontHeight ); |
||||
const Mat roi = clipRoiAs8UC1(dst, roi_rect); |
||||
EXPECT_EQ(0, countNonZero(roi) ); |
||||
} |
||||
ty += skip_y; |
||||
} |
||||
|
||||
if (cvtest::debugLevel > 0 ) |
||||
{ |
||||
imwrite( cv::format("%s-contrib2627.png", title.c_str()), dst ); |
||||
} |
||||
} |
||||
|
||||
INSTANTIATE_TEST_CASE_P(Freetype_putText, LigatureTest, |
||||
testing::ValuesIn(drawing_list)); |
||||
|
||||
}} // namespace
|
@ -0,0 +1,36 @@ |
||||
// 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.
|
||||
#include "perf_precomp.hpp" |
||||
|
||||
namespace opencv_test { namespace { |
||||
|
||||
typedef perf::TestBaseWithParam<std::string> teblid; |
||||
|
||||
#define TEBLID_IMAGES \ |
||||
"cv/detectors_descriptors_evaluation/images_datasets/leuven/img1.png",\
|
||||
"stitching/a3.png" |
||||
|
||||
#ifdef OPENCV_ENABLE_NONFREE |
||||
PERF_TEST_P(teblid, extract, testing::Values(TEBLID_IMAGES)) |
||||
{ |
||||
string filename = getDataPath(GetParam()); |
||||
Mat frame = imread(filename, IMREAD_GRAYSCALE); |
||||
ASSERT_FALSE(frame.empty()) << "Unable to load source image " << filename; |
||||
|
||||
Mat mask; |
||||
declare.in(frame).time(90); |
||||
|
||||
Ptr<SURF> detector = SURF::create(); |
||||
vector<KeyPoint> points; |
||||
detector->detect(frame, points, mask); |
||||
|
||||
Ptr<TEBLID> descriptor = TEBLID::create(6.25f); |
||||
cv::Mat descriptors; |
||||
TEST_CYCLE() descriptor->compute(frame, points, descriptors); |
||||
|
||||
SANITY_CHECK_NOTHING(); |
||||
} |
||||
#endif // NONFREE
|
||||
|
||||
}} // namespace
|