4
4
from __future__ import print_function
5
5
6
6
import os , tempfile , numpy as np
7
+ from math import pi
7
8
8
9
import cv2 as cv
9
10
10
11
from tests_common import NewOpenCVTests
11
12
13
+ def getSyntheticRT (yaw , pitch , distance ):
14
+ rvec = np .zeros ((3 , 1 ), np .float64 )
15
+ tvec = np .zeros ((3 , 1 ), np .float64 )
16
+
17
+ rotPitch = np .array ([[- pitch ], [0 ], [0 ]])
18
+ rotYaw = np .array ([[0 ], [yaw ], [0 ]])
19
+
20
+ rvec , tvec = cv .composeRT (rotPitch , np .zeros ((3 , 1 ), np .float64 ),
21
+ rotYaw , np .zeros ((3 , 1 ), np .float64 ))[:2 ]
22
+
23
+ tvec = np .array ([[0 ], [0 ], [distance ]])
24
+ return rvec , tvec
25
+
26
+ # see test_aruco_utils.cpp
27
+ def projectMarker (img , board , markerIndex , cameraMatrix , rvec , tvec , markerBorder ):
28
+ markerSizePixels = 100
29
+ markerImg = cv .aruco .generateImageMarker (board .getDictionary (), board .getIds ()[markerIndex ], markerSizePixels , borderBits = markerBorder )
30
+
31
+ distCoeffs = np .zeros ((5 , 1 ), np .float64 )
32
+ maxCoord = board .getRightBottomCorner ()
33
+ objPoints = board .getObjPoints ()[markerIndex ]
34
+ for i in range (len (objPoints )):
35
+ objPoints [i ][0 ] -= maxCoord [0 ] / 2
36
+ objPoints [i ][1 ] -= maxCoord [1 ] / 2
37
+ objPoints [i ][2 ] -= maxCoord [2 ] / 2
38
+
39
+ corners , _ = cv .projectPoints (objPoints , rvec , tvec , cameraMatrix , distCoeffs )
40
+
41
+ originalCorners = np .array ([
42
+ [0 , 0 ],
43
+ [markerSizePixels , 0 ],
44
+ [markerSizePixels , markerSizePixels ],
45
+ [0 , markerSizePixels ],
46
+ ], np .float32 )
47
+
48
+ transformation = cv .getPerspectiveTransform (originalCorners , corners )
49
+
50
+ borderValue = 127
51
+ aux = cv .warpPerspective (markerImg , transformation , img .shape , None , cv .INTER_NEAREST , cv .BORDER_CONSTANT , borderValue )
52
+
53
+ assert (img .shape == aux .shape )
54
+ mask = (aux == borderValue ).astype (np .uint8 )
55
+ img = img * mask + aux * (1 - mask )
56
+ return img
57
+
58
+ def projectChessboard (squaresX , squaresY , squareSize , imageSize , cameraMatrix , rvec , tvec ):
59
+ img = np .ones (imageSize , np .uint8 ) * 255
60
+ distCoeffs = np .zeros ((5 , 1 ), np .float64 )
61
+ for y in range (squaresY ):
62
+ startY = y * squareSize
63
+ for x in range (squaresX ):
64
+ if (y % 2 != x % 2 ):
65
+ continue
66
+ startX = x * squareSize
67
+
68
+ squareCorners = np .array ([[startX - squaresX * squareSize / 2 ,
69
+ startY - squaresY * squareSize / 2 ,
70
+ 0 ]], np .float32 )
71
+ squareCorners = np .stack ((squareCorners [0 ],
72
+ squareCorners [0 ] + [squareSize , 0 , 0 ],
73
+ squareCorners [0 ] + [squareSize , squareSize , 0 ],
74
+ squareCorners [0 ] + [0 , squareSize , 0 ]))
75
+
76
+ projectedCorners , _ = cv .projectPoints (squareCorners , rvec , tvec , cameraMatrix , distCoeffs )
77
+ projectedCorners = projectedCorners .astype (np .int64 )
78
+ projectedCorners = projectedCorners .reshape (1 , 4 , 2 )
79
+ img = cv .fillPoly (img , [projectedCorners ], 0 )
80
+
81
+ return img
82
+
83
+ def projectCharucoBoard (board , cameraMatrix , yaw , pitch , distance , imageSize , markerBorder ):
84
+ rvec , tvec = getSyntheticRT (yaw , pitch , distance )
85
+
86
+ img = np .ones (imageSize , np .uint8 ) * 255
87
+ for indexMarker in range (len (board .getIds ())):
88
+ img = projectMarker (img , board , indexMarker , cameraMatrix , rvec , tvec , markerBorder )
89
+
90
+ chessboard = projectChessboard (board .getChessboardSize ()[0 ], board .getChessboardSize ()[1 ],
91
+ board .getSquareLength (), imageSize , cameraMatrix , rvec , tvec )
92
+
93
+ chessboard = (chessboard != 0 ).astype (np .uint8 )
94
+ img = img * chessboard
95
+ return img , rvec , tvec
96
+
12
97
class aruco_objdetect_test (NewOpenCVTests ):
13
98
14
99
def test_board (self ):
@@ -153,5 +238,80 @@ def test_charuco_detector(self):
153
238
self .assertEqual (charucoIds [i ], i )
154
239
np .testing .assert_allclose (gold_corners , charucoCorners .reshape (- 1 , 2 ), 0.01 , 0.1 )
155
240
241
+ # check no segfault when cameraMatrix or distCoeffs are not initialized
242
+ def test_charuco_no_segfault_params (self ):
243
+ dictionary = cv .aruco .getPredefinedDictionary (cv .aruco .DICT_4X4_1000 )
244
+ board = cv .aruco .CharucoBoard ((10 , 10 ), 0.019 , 0.015 , dictionary )
245
+ charuco_parameters = cv .aruco .CharucoParameters ()
246
+ detector = cv .aruco .CharucoDetector (board )
247
+ detector .setCharucoParameters (charuco_parameters )
248
+
249
+ self .assertIsNone (detector .getCharucoParameters ().cameraMatrix )
250
+ self .assertIsNone (detector .getCharucoParameters ().distCoeffs )
251
+
252
+ def test_charuco_no_segfault_params_constructor (self ):
253
+ dictionary = cv .aruco .getPredefinedDictionary (cv .aruco .DICT_4X4_1000 )
254
+ board = cv .aruco .CharucoBoard ((10 , 10 ), 0.019 , 0.015 , dictionary )
255
+ charuco_parameters = cv .aruco .CharucoParameters ()
256
+ detector = cv .aruco .CharucoDetector (board , charucoParams = charuco_parameters )
257
+
258
+ self .assertIsNone (detector .getCharucoParameters ().cameraMatrix )
259
+ self .assertIsNone (detector .getCharucoParameters ().distCoeffs )
260
+
261
+ # similar to C++ test CV_CharucoDetection.accuracy
262
+ def test_charuco_detector_accuracy (self ):
263
+ iteration = 0
264
+ cameraMatrix = np .eye (3 , 3 , dtype = np .float64 )
265
+ imgSize = (500 , 500 )
266
+ params = cv .aruco .DetectorParameters ()
267
+ params .minDistanceToBorder = 3
268
+
269
+ board = cv .aruco .CharucoBoard ((4 , 4 ), 0.03 , 0.015 , cv .aruco .getPredefinedDictionary (cv .aruco .DICT_6X6_250 ))
270
+ detector = cv .aruco .CharucoDetector (board , detectorParams = params )
271
+
272
+ cameraMatrix [0 , 0 ] = cameraMatrix [1 , 1 ] = 600
273
+ cameraMatrix [0 , 2 ] = imgSize [0 ] / 2
274
+ cameraMatrix [1 , 2 ] = imgSize [1 ] / 2
275
+
276
+ # for different perspectives
277
+ distCoeffs = np .zeros ((5 , 1 ), dtype = np .float64 )
278
+ for distance in [0.2 , 0.4 ]:
279
+ for yaw in range (- 55 , 51 , 25 ):
280
+ for pitch in range (- 55 , 51 , 25 ):
281
+ markerBorder = iteration % 2 + 1
282
+ iteration += 1
283
+
284
+ # create synthetic image
285
+ img , rvec , tvec = projectCharucoBoard (board , cameraMatrix , yaw * pi / 180 , pitch * pi / 180 , distance , imgSize , markerBorder )
286
+
287
+ params .markerBorderBits = markerBorder
288
+ detector .setDetectorParameters (params )
289
+
290
+ if (iteration % 2 != 0 ):
291
+ charucoParameters = cv .aruco .CharucoParameters ()
292
+ charucoParameters .cameraMatrix = cameraMatrix
293
+ charucoParameters .distCoeffs = distCoeffs
294
+ detector .setCharucoParameters (charucoParameters )
295
+
296
+ charucoCorners , charucoIds , corners , ids = detector .detectBoard (img )
297
+
298
+ self .assertGreater (len (ids ), 0 )
299
+
300
+ copyChessboardCorners = board .getChessboardCorners ()
301
+ copyChessboardCorners -= np .array (board .getRightBottomCorner ()) / 2
302
+
303
+ projectedCharucoCorners , _ = cv .projectPoints (copyChessboardCorners , rvec , tvec , cameraMatrix , distCoeffs )
304
+
305
+ if charucoIds is None :
306
+ self .assertEqual (iteration , 46 )
307
+ continue
308
+
309
+ for i in range (len (charucoIds )):
310
+ currentId = charucoIds [i ]
311
+ self .assertLess (currentId , len (board .getChessboardCorners ()))
312
+
313
+ reprErr = cv .norm (charucoCorners [i ] - projectedCharucoCorners [currentId ])
314
+ self .assertLessEqual (reprErr , 5 )
315
+
156
316
if __name__ == '__main__' :
157
317
NewOpenCVTests .bootstrap ()
0 commit comments