1 | // This file is part of OpenCV project. |
2 | // It is subject to the license terms in the LICENSE file found in the top-level directory |
3 | // of this distribution and at http://opencv.org/license.html |
4 | #ifndef OPENCV_OBJDETECT_ARUCO_DETECTOR_HPP |
5 | #define OPENCV_OBJDETECT_ARUCO_DETECTOR_HPP |
6 | |
7 | #include <opencv2/objdetect/aruco_dictionary.hpp> |
8 | #include <opencv2/objdetect/aruco_board.hpp> |
9 | |
10 | namespace cv { |
11 | namespace aruco { |
12 | |
13 | //! @addtogroup objdetect_aruco |
14 | //! @{ |
15 | |
16 | enum CornerRefineMethod{ |
17 | CORNER_REFINE_NONE, ///< Tag and corners detection based on the ArUco approach |
18 | CORNER_REFINE_SUBPIX, ///< ArUco approach and refine the corners locations using corner subpixel accuracy |
19 | CORNER_REFINE_CONTOUR, ///< ArUco approach and refine the corners locations using the contour-points line fitting |
20 | CORNER_REFINE_APRILTAG, ///< Tag and corners detection based on the AprilTag 2 approach @cite wang2016iros |
21 | }; |
22 | |
23 | /** @brief struct DetectorParameters is used by ArucoDetector |
24 | */ |
25 | struct CV_EXPORTS_W_SIMPLE DetectorParameters { |
26 | CV_WRAP DetectorParameters() { |
27 | adaptiveThreshWinSizeMin = 3; |
28 | adaptiveThreshWinSizeMax = 23; |
29 | adaptiveThreshWinSizeStep = 10; |
30 | adaptiveThreshConstant = 7; |
31 | minMarkerPerimeterRate = 0.03; |
32 | maxMarkerPerimeterRate = 4.; |
33 | polygonalApproxAccuracyRate = 0.03; |
34 | minCornerDistanceRate = 0.05; |
35 | minDistanceToBorder = 3; |
36 | minMarkerDistanceRate = 0.125; |
37 | cornerRefinementMethod = (int)CORNER_REFINE_NONE; |
38 | cornerRefinementWinSize = 5; |
39 | relativeCornerRefinmentWinSize = 0.3f; |
40 | cornerRefinementMaxIterations = 30; |
41 | cornerRefinementMinAccuracy = 0.1; |
42 | markerBorderBits = 1; |
43 | perspectiveRemovePixelPerCell = 4; |
44 | perspectiveRemoveIgnoredMarginPerCell = 0.13; |
45 | maxErroneousBitsInBorderRate = 0.35; |
46 | minOtsuStdDev = 5.0; |
47 | errorCorrectionRate = 0.6; |
48 | aprilTagQuadDecimate = 0.0; |
49 | aprilTagQuadSigma = 0.0; |
50 | aprilTagMinClusterPixels = 5; |
51 | aprilTagMaxNmaxima = 10; |
52 | aprilTagCriticalRad = (float)(10* CV_PI /180); |
53 | aprilTagMaxLineFitMse = 10.0; |
54 | aprilTagMinWhiteBlackDiff = 5; |
55 | aprilTagDeglitch = 0; |
56 | detectInvertedMarker = false; |
57 | useAruco3Detection = false; |
58 | minSideLengthCanonicalImg = 32; |
59 | minMarkerLengthRatioOriginalImg = 0.0; |
60 | } |
61 | |
62 | /** @brief Read a new set of DetectorParameters from FileNode (use FileStorage.root()). |
63 | */ |
64 | CV_WRAP bool readDetectorParameters(const FileNode& fn); |
65 | |
66 | /** @brief Write a set of DetectorParameters to FileStorage |
67 | */ |
68 | CV_WRAP bool writeDetectorParameters(FileStorage& fs, const String& name = String()); |
69 | |
70 | /// minimum window size for adaptive thresholding before finding contours (default 3). |
71 | CV_PROP_RW int adaptiveThreshWinSizeMin; |
72 | |
73 | /// maximum window size for adaptive thresholding before finding contours (default 23). |
74 | CV_PROP_RW int adaptiveThreshWinSizeMax; |
75 | |
76 | /// increments from adaptiveThreshWinSizeMin to adaptiveThreshWinSizeMax during the thresholding (default 10). |
77 | CV_PROP_RW int adaptiveThreshWinSizeStep; |
78 | |
79 | /// constant for adaptive thresholding before finding contours (default 7) |
80 | CV_PROP_RW double adaptiveThreshConstant; |
81 | |
82 | /** @brief determine minimum perimeter for marker contour to be detected. |
83 | * |
84 | * This is defined as a rate respect to the maximum dimension of the input image (default 0.03). |
85 | */ |
86 | CV_PROP_RW double minMarkerPerimeterRate; |
87 | |
88 | /** @brief determine maximum perimeter for marker contour to be detected. |
89 | * |
90 | * This is defined as a rate respect to the maximum dimension of the input image (default 4.0). |
91 | */ |
92 | CV_PROP_RW double maxMarkerPerimeterRate; |
93 | |
94 | /// minimum accuracy during the polygonal approximation process to determine which contours are squares. (default 0.03) |
95 | CV_PROP_RW double polygonalApproxAccuracyRate; |
96 | |
97 | /// minimum distance between corners for detected markers relative to its perimeter (default 0.05) |
98 | CV_PROP_RW double minCornerDistanceRate; |
99 | |
100 | /// minimum distance of any corner to the image border for detected markers (in pixels) (default 3) |
101 | CV_PROP_RW int minDistanceToBorder; |
102 | |
103 | /** @brief minimum average distance between the corners of the two markers to be grouped (default 0.125). |
104 | * |
105 | * The rate is relative to the smaller perimeter of the two markers. |
106 | * Two markers are grouped if average distance between the corners of the two markers is less than |
107 | * min(MarkerPerimeter1, MarkerPerimeter2)*minMarkerDistanceRate. |
108 | * |
109 | * default value is 0.125 because 0.125*MarkerPerimeter = (MarkerPerimeter / 4) * 0.5 = half the side of the marker. |
110 | * |
111 | * @note default value was changed from 0.05 after 4.8.1 release, because the filtering algorithm has been changed. |
112 | * Now a few candidates from the same group can be added to the list of candidates if they are far from each other. |
113 | * @sa minGroupDistance. |
114 | */ |
115 | CV_PROP_RW double minMarkerDistanceRate; |
116 | |
117 | /** @brief minimum average distance between the corners of the two markers in group to add them to the list of candidates |
118 | * |
119 | * The average distance between the corners of the two markers is calculated relative to its module size (default 0.21). |
120 | */ |
121 | CV_PROP_RW float minGroupDistance = 0.21f; |
122 | |
123 | /** @brief default value CORNER_REFINE_NONE */ |
124 | CV_PROP_RW int cornerRefinementMethod; |
125 | |
126 | /** @brief maximum window size for the corner refinement process (in pixels) (default 5). |
127 | * |
128 | * The window size may decrease if the ArUco marker is too small, check relativeCornerRefinmentWinSize. |
129 | * The final window size is calculated as: |
130 | * min(cornerRefinementWinSize, averageArucoModuleSize*relativeCornerRefinmentWinSize), |
131 | * where averageArucoModuleSize is average module size of ArUco marker in pixels. |
132 | * (ArUco marker is composed of black and white modules) |
133 | */ |
134 | CV_PROP_RW int cornerRefinementWinSize; |
135 | |
136 | /** @brief Dynamic window size for corner refinement relative to Aruco module size (default 0.3). |
137 | * |
138 | * The final window size is calculated as: |
139 | * min(cornerRefinementWinSize, averageArucoModuleSize*relativeCornerRefinmentWinSize), |
140 | * where averageArucoModuleSize is average module size of ArUco marker in pixels. |
141 | * (ArUco marker is composed of black and white modules) |
142 | * In the case of markers located far from each other, it may be useful to increase the value of the parameter to 0.4-0.5. |
143 | * In the case of markers located close to each other, it may be useful to decrease the parameter value to 0.1-0.2. |
144 | */ |
145 | CV_PROP_RW float relativeCornerRefinmentWinSize; |
146 | |
147 | /// maximum number of iterations for stop criteria of the corner refinement process (default 30). |
148 | CV_PROP_RW int cornerRefinementMaxIterations; |
149 | |
150 | /// minimum error for the stop cristeria of the corner refinement process (default: 0.1) |
151 | CV_PROP_RW double cornerRefinementMinAccuracy; |
152 | |
153 | /// number of bits of the marker border, i.e. marker border width (default 1). |
154 | CV_PROP_RW int markerBorderBits; |
155 | |
156 | /// number of bits (per dimension) for each cell of the marker when removing the perspective (default 4). |
157 | CV_PROP_RW int perspectiveRemovePixelPerCell; |
158 | |
159 | /** @brief width of the margin of pixels on each cell not considered for the determination of the cell bit. |
160 | * |
161 | * Represents the rate respect to the total size of the cell, i.e. perspectiveRemovePixelPerCell (default 0.13) |
162 | */ |
163 | CV_PROP_RW double perspectiveRemoveIgnoredMarginPerCell; |
164 | |
165 | /** @brief maximum number of accepted erroneous bits in the border (i.e. number of allowed white bits in the border). |
166 | * |
167 | * Represented as a rate respect to the total number of bits per marker (default 0.35). |
168 | */ |
169 | CV_PROP_RW double maxErroneousBitsInBorderRate; |
170 | |
171 | /** @brief minimun standard deviation in pixels values during the decodification step to apply Otsu |
172 | * thresholding (otherwise, all the bits are set to 0 or 1 depending on mean higher than 128 or not) (default 5.0) |
173 | */ |
174 | CV_PROP_RW double minOtsuStdDev; |
175 | |
176 | /// error correction rate respect to the maximun error correction capability for each dictionary (default 0.6). |
177 | CV_PROP_RW double errorCorrectionRate; |
178 | |
179 | /** @brief April :: User-configurable parameters. |
180 | * |
181 | * Detection of quads can be done on a lower-resolution image, improving speed at a cost of |
182 | * pose accuracy and a slight decrease in detection rate. Decoding the binary payload is still |
183 | */ |
184 | CV_PROP_RW float aprilTagQuadDecimate; |
185 | |
186 | /// what Gaussian blur should be applied to the segmented image (used for quad detection?) |
187 | CV_PROP_RW float aprilTagQuadSigma; |
188 | |
189 | // April :: Internal variables |
190 | /// reject quads containing too few pixels (default 5). |
191 | CV_PROP_RW int aprilTagMinClusterPixels; |
192 | |
193 | /// how many corner candidates to consider when segmenting a group of pixels into a quad (default 10). |
194 | CV_PROP_RW int aprilTagMaxNmaxima; |
195 | |
196 | /** @brief reject quads where pairs of edges have angles that are close to straight or close to 180 degrees. |
197 | * |
198 | * Zero means that no quads are rejected. (In radians) (default 10*PI/180) |
199 | */ |
200 | CV_PROP_RW float aprilTagCriticalRad; |
201 | |
202 | /// when fitting lines to the contours, what is the maximum mean squared error |
203 | CV_PROP_RW float aprilTagMaxLineFitMse; |
204 | |
205 | /** @brief add an extra check that the white model must be (overall) brighter than the black model. |
206 | * |
207 | * When we build our model of black & white pixels, we add an extra check that the white model must be (overall) |
208 | * brighter than the black model. How much brighter? (in pixel values, [0,255]), (default 5) |
209 | */ |
210 | CV_PROP_RW int aprilTagMinWhiteBlackDiff; |
211 | |
212 | /// should the thresholded image be deglitched? Only useful for very noisy images (default 0). |
213 | CV_PROP_RW int aprilTagDeglitch; |
214 | |
215 | /** @brief to check if there is a white marker. |
216 | * |
217 | * In order to generate a "white" marker just invert a normal marker by using a tilde, ~markerImage. (default false) |
218 | */ |
219 | CV_PROP_RW bool detectInvertedMarker; |
220 | |
221 | /** @brief enable the new and faster Aruco detection strategy. |
222 | * |
223 | * Proposed in the paper: |
224 | * Romero-Ramirez et al: Speeded up detection of squared fiducial markers (2018) |
225 | * https://www.researchgate.net/publication/325787310_Speeded_Up_Detection_of_Squared_Fiducial_Markers |
226 | */ |
227 | CV_PROP_RW bool useAruco3Detection; |
228 | |
229 | /// minimum side length of a marker in the canonical image. Latter is the binarized image in which contours are searched. |
230 | CV_PROP_RW int minSideLengthCanonicalImg; |
231 | |
232 | /// range [0,1], eq (2) from paper. The parameter tau_i has a direct influence on the processing speed. |
233 | CV_PROP_RW float minMarkerLengthRatioOriginalImg; |
234 | }; |
235 | |
236 | /** @brief struct RefineParameters is used by ArucoDetector |
237 | */ |
238 | struct CV_EXPORTS_W_SIMPLE RefineParameters { |
239 | CV_WRAP RefineParameters(float minRepDistance = 10.f, float errorCorrectionRate = 3.f, bool checkAllOrders = true); |
240 | |
241 | |
242 | /** @brief Read a new set of RefineParameters from FileNode (use FileStorage.root()). |
243 | */ |
244 | CV_WRAP bool readRefineParameters(const FileNode& fn); |
245 | |
246 | /** @brief Write a set of RefineParameters to FileStorage |
247 | */ |
248 | CV_WRAP bool writeRefineParameters(FileStorage& fs, const String& name = String()); |
249 | |
250 | /** @brief minRepDistance minimum distance between the corners of the rejected candidate and the reprojected marker |
251 | in order to consider it as a correspondence. |
252 | */ |
253 | CV_PROP_RW float minRepDistance; |
254 | |
255 | /** @brief minRepDistance rate of allowed erroneous bits respect to the error correction capability of the used dictionary. |
256 | * |
257 | * -1 ignores the error correction step. |
258 | */ |
259 | CV_PROP_RW float errorCorrectionRate; |
260 | |
261 | /** @brief checkAllOrders consider the four posible corner orders in the rejectedCorners array. |
262 | * |
263 | * If it set to false, only the provided corner order is considered (default true). |
264 | */ |
265 | CV_PROP_RW bool checkAllOrders; |
266 | }; |
267 | |
268 | /** @brief The main functionality of ArucoDetector class is detection of markers in an image with detectMarkers() method. |
269 | * |
270 | * After detecting some markers in the image, you can try to find undetected markers from this dictionary with |
271 | * refineDetectedMarkers() method. |
272 | * |
273 | * @see DetectorParameters, RefineParameters |
274 | */ |
275 | class CV_EXPORTS_W ArucoDetector : public Algorithm |
276 | { |
277 | public: |
278 | /** @brief Basic ArucoDetector constructor |
279 | * |
280 | * @param dictionary indicates the type of markers that will be searched |
281 | * @param detectorParams marker detection parameters |
282 | * @param refineParams marker refine detection parameters |
283 | */ |
284 | CV_WRAP ArucoDetector(const Dictionary &dictionary = getPredefinedDictionary(name: cv::aruco::DICT_4X4_50), |
285 | const DetectorParameters &detectorParams = DetectorParameters(), |
286 | const RefineParameters& refineParams = RefineParameters()); |
287 | |
288 | /** @brief Basic marker detection |
289 | * |
290 | * @param image input image |
291 | * @param corners vector of detected marker corners. For each marker, its four corners |
292 | * are provided, (e.g std::vector<std::vector<cv::Point2f> > ). For N detected markers, |
293 | * the dimensions of this array is Nx4. The order of the corners is clockwise. |
294 | * @param ids vector of identifiers of the detected markers. The identifier is of type int |
295 | * (e.g. std::vector<int>). For N detected markers, the size of ids is also N. |
296 | * The identifiers have the same order than the markers in the imgPoints array. |
297 | * @param rejectedImgPoints contains the imgPoints of those squares whose inner code has not a |
298 | * correct codification. Useful for debugging purposes. |
299 | * |
300 | * Performs marker detection in the input image. Only markers included in the specific dictionary |
301 | * are searched. For each detected marker, it returns the 2D position of its corner in the image |
302 | * and its corresponding identifier. |
303 | * Note that this function does not perform pose estimation. |
304 | * @note The function does not correct lens distortion or takes it into account. It's recommended to undistort |
305 | * input image with corresponding camera model, if camera parameters are known |
306 | * @sa undistort, estimatePoseSingleMarkers, estimatePoseBoard |
307 | */ |
308 | CV_WRAP void detectMarkers(InputArray image, OutputArrayOfArrays corners, OutputArray ids, |
309 | OutputArrayOfArrays rejectedImgPoints = noArray()) const; |
310 | |
311 | /** @brief Refine not detected markers based on the already detected and the board layout |
312 | * |
313 | * @param image input image |
314 | * @param board layout of markers in the board. |
315 | * @param detectedCorners vector of already detected marker corners. |
316 | * @param detectedIds vector of already detected marker identifiers. |
317 | * @param rejectedCorners vector of rejected candidates during the marker detection process. |
318 | * @param cameraMatrix optional input 3x3 floating-point camera matrix |
319 | * \f$A = \vecthreethree{f_x}{0}{c_x}{0}{f_y}{c_y}{0}{0}{1}\f$ |
320 | * @param distCoeffs optional vector of distortion coefficients |
321 | * \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 |
322 | * @param recoveredIdxs Optional array to returns the indexes of the recovered candidates in the |
323 | * original rejectedCorners array. |
324 | * |
325 | * This function tries to find markers that were not detected in the basic detecMarkers function. |
326 | * First, based on the current detected marker and the board layout, the function interpolates |
327 | * the position of the missing markers. Then it tries to find correspondence between the reprojected |
328 | * markers and the rejected candidates based on the minRepDistance and errorCorrectionRate parameters. |
329 | * If camera parameters and distortion coefficients are provided, missing markers are reprojected |
330 | * using projectPoint function. If not, missing marker projections are interpolated using global |
331 | * homography, and all the marker corners in the board must have the same Z coordinate. |
332 | */ |
333 | CV_WRAP void refineDetectedMarkers(InputArray image, const Board &board, |
334 | InputOutputArrayOfArrays detectedCorners, |
335 | InputOutputArray detectedIds, InputOutputArrayOfArrays rejectedCorners, |
336 | InputArray cameraMatrix = noArray(), InputArray distCoeffs = noArray(), |
337 | OutputArray recoveredIdxs = noArray()) const; |
338 | |
339 | CV_WRAP const Dictionary& getDictionary() const; |
340 | CV_WRAP void setDictionary(const Dictionary& dictionary); |
341 | |
342 | CV_WRAP const DetectorParameters& getDetectorParameters() const; |
343 | CV_WRAP void setDetectorParameters(const DetectorParameters& detectorParameters); |
344 | |
345 | CV_WRAP const RefineParameters& getRefineParameters() const; |
346 | CV_WRAP void setRefineParameters(const RefineParameters& refineParameters); |
347 | |
348 | /** @brief Stores algorithm parameters in a file storage |
349 | */ |
350 | virtual void write(FileStorage& fs) const override; |
351 | |
352 | /** @brief simplified API for language bindings |
353 | */ |
354 | CV_WRAP inline void write(FileStorage& fs, const String& name) { Algorithm::write(fs, name); } |
355 | |
356 | /** @brief Reads algorithm parameters from a file storage |
357 | */ |
358 | CV_WRAP virtual void read(const FileNode& fn) override; |
359 | protected: |
360 | struct ArucoDetectorImpl; |
361 | Ptr<ArucoDetectorImpl> arucoDetectorImpl; |
362 | }; |
363 | |
364 | /** @brief Draw detected markers in image |
365 | * |
366 | * @param image input/output image. It must have 1 or 3 channels. The number of channels is not altered. |
367 | * @param corners positions of marker corners on input image. |
368 | * (e.g std::vector<std::vector<cv::Point2f> > ). For N detected markers, the dimensions of |
369 | * this array should be Nx4. The order of the corners should be clockwise. |
370 | * @param ids vector of identifiers for markers in markersCorners . |
371 | * Optional, if not provided, ids are not painted. |
372 | * @param borderColor color of marker borders. Rest of colors (text color and first corner color) |
373 | * are calculated based on this one to improve visualization. |
374 | * |
375 | * Given an array of detected marker corners and its corresponding ids, this functions draws |
376 | * the markers in the image. The marker borders are painted and the markers identifiers if provided. |
377 | * Useful for debugging purposes. |
378 | */ |
379 | CV_EXPORTS_W void drawDetectedMarkers(InputOutputArray image, InputArrayOfArrays corners, |
380 | InputArray ids = noArray(), Scalar borderColor = Scalar(0, 255, 0)); |
381 | |
382 | /** @brief Generate a canonical marker image |
383 | * |
384 | * @param dictionary dictionary of markers indicating the type of markers |
385 | * @param id identifier of the marker that will be returned. It has to be a valid id in the specified dictionary. |
386 | * @param sidePixels size of the image in pixels |
387 | * @param img output image with the marker |
388 | * @param borderBits width of the marker border. |
389 | * |
390 | * This function returns a marker image in its canonical form (i.e. ready to be printed) |
391 | */ |
392 | CV_EXPORTS_W void generateImageMarker(const Dictionary &dictionary, int id, int sidePixels, OutputArray img, |
393 | int borderBits = 1); |
394 | |
395 | //! @} |
396 | |
397 | } |
398 | } |
399 | |
400 | #endif |
401 | |