Merge pull request #23436 from siilats:patch-2

Fix python bindings for setCharucoParameters #23436

setCharucoParameters fails in python
Fixes: https://github.com/opencv/opencv/issues/23440

### Pull Request Readiness Checklist

See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request

- [x] I agree to contribute to the project under Apache 2 License.
- [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV
- [x] The PR is proposed to the proper branch
- [x] There is a reference to the original bug report and related work
- [x] There is accuracy test, performance test and test data in opencv_extra repository, if applicable
      Patch to opencv_extra has the same branch name.
- [x] The feature is well documented and sample code can be built with the project CMake
pull/23504/head
keith siilats 2 years ago committed by GitHub
parent aa17f881b1
commit 8512deb3cc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      modules/objdetect/include/opencv2/objdetect/charuco_detector.hpp
  2. 160
      modules/objdetect/misc/python/test/test_objdetect_aruco.py

@ -13,7 +13,7 @@ namespace aruco {
//! @{
struct CV_EXPORTS_W_SIMPLE CharucoParameters {
CharucoParameters() {
CV_WRAP CharucoParameters() {
minMarkers = 2;
tryRefineMarkers = false;
}

@ -4,11 +4,96 @@
from __future__ import print_function
import os, tempfile, numpy as np
from math import pi
import cv2 as cv
from tests_common import NewOpenCVTests
def getSyntheticRT(yaw, pitch, distance):
rvec = np.zeros((3, 1), np.float64)
tvec = np.zeros((3, 1), np.float64)
rotPitch = np.array([[-pitch], [0], [0]])
rotYaw = np.array([[0], [yaw], [0]])
rvec, tvec = cv.composeRT(rotPitch, np.zeros((3, 1), np.float64),
rotYaw, np.zeros((3, 1), np.float64))[:2]
tvec = np.array([[0], [0], [distance]])
return rvec, tvec
# see test_aruco_utils.cpp
def projectMarker(img, board, markerIndex, cameraMatrix, rvec, tvec, markerBorder):
markerSizePixels = 100
markerImg = cv.aruco.generateImageMarker(board.getDictionary(), board.getIds()[markerIndex], markerSizePixels, borderBits=markerBorder)
distCoeffs = np.zeros((5, 1), np.float64)
maxCoord = board.getRightBottomCorner()
objPoints = board.getObjPoints()[markerIndex]
for i in range(len(objPoints)):
objPoints[i][0] -= maxCoord[0] / 2
objPoints[i][1] -= maxCoord[1] / 2
objPoints[i][2] -= maxCoord[2] / 2
corners, _ = cv.projectPoints(objPoints, rvec, tvec, cameraMatrix, distCoeffs)
originalCorners = np.array([
[0, 0],
[markerSizePixels, 0],
[markerSizePixels, markerSizePixels],
[0, markerSizePixels],
], np.float32)
transformation = cv.getPerspectiveTransform(originalCorners, corners)
borderValue = 127
aux = cv.warpPerspective(markerImg, transformation, img.shape, None, cv.INTER_NEAREST, cv.BORDER_CONSTANT, borderValue)
assert(img.shape == aux.shape)
mask = (aux == borderValue).astype(np.uint8)
img = img * mask + aux * (1 - mask)
return img
def projectChessboard(squaresX, squaresY, squareSize, imageSize, cameraMatrix, rvec, tvec):
img = np.ones(imageSize, np.uint8) * 255
distCoeffs = np.zeros((5, 1), np.float64)
for y in range(squaresY):
startY = y * squareSize
for x in range(squaresX):
if (y % 2 != x % 2):
continue
startX = x * squareSize
squareCorners = np.array([[startX - squaresX*squareSize/2,
startY - squaresY*squareSize/2,
0]], np.float32)
squareCorners = np.stack((squareCorners[0],
squareCorners[0] + [squareSize, 0, 0],
squareCorners[0] + [squareSize, squareSize, 0],
squareCorners[0] + [0, squareSize, 0]))
projectedCorners, _ = cv.projectPoints(squareCorners, rvec, tvec, cameraMatrix, distCoeffs)
projectedCorners = projectedCorners.astype(np.int64)
projectedCorners = projectedCorners.reshape(1, 4, 2)
img = cv.fillPoly(img, [projectedCorners], 0)
return img
def projectCharucoBoard(board, cameraMatrix, yaw, pitch, distance, imageSize, markerBorder):
rvec, tvec = getSyntheticRT(yaw, pitch, distance)
img = np.ones(imageSize, np.uint8) * 255
for indexMarker in range(len(board.getIds())):
img = projectMarker(img, board, indexMarker, cameraMatrix, rvec, tvec, markerBorder)
chessboard = projectChessboard(board.getChessboardSize()[0], board.getChessboardSize()[1],
board.getSquareLength(), imageSize, cameraMatrix, rvec, tvec)
chessboard = (chessboard != 0).astype(np.uint8)
img = img * chessboard
return img, rvec, tvec
class aruco_objdetect_test(NewOpenCVTests):
def test_board(self):
@ -153,5 +238,80 @@ class aruco_objdetect_test(NewOpenCVTests):
self.assertEqual(charucoIds[i], i)
np.testing.assert_allclose(gold_corners, charucoCorners.reshape(-1, 2), 0.01, 0.1)
# check no segfault when cameraMatrix or distCoeffs are not initialized
def test_charuco_no_segfault_params(self):
dictionary = cv.aruco.getPredefinedDictionary(cv.aruco.DICT_4X4_1000)
board = cv.aruco.CharucoBoard((10, 10), 0.019, 0.015, dictionary)
charuco_parameters = cv.aruco.CharucoParameters()
detector = cv.aruco.CharucoDetector(board)
detector.setCharucoParameters(charuco_parameters)
self.assertIsNone(detector.getCharucoParameters().cameraMatrix)
self.assertIsNone(detector.getCharucoParameters().distCoeffs)
def test_charuco_no_segfault_params_constructor(self):
dictionary = cv.aruco.getPredefinedDictionary(cv.aruco.DICT_4X4_1000)
board = cv.aruco.CharucoBoard((10, 10), 0.019, 0.015, dictionary)
charuco_parameters = cv.aruco.CharucoParameters()
detector = cv.aruco.CharucoDetector(board, charucoParams=charuco_parameters)
self.assertIsNone(detector.getCharucoParameters().cameraMatrix)
self.assertIsNone(detector.getCharucoParameters().distCoeffs)
# similar to C++ test CV_CharucoDetection.accuracy
def test_charuco_detector_accuracy(self):
iteration = 0
cameraMatrix = np.eye(3, 3, dtype=np.float64)
imgSize = (500, 500)
params = cv.aruco.DetectorParameters()
params.minDistanceToBorder = 3
board = cv.aruco.CharucoBoard((4, 4), 0.03, 0.015, cv.aruco.getPredefinedDictionary(cv.aruco.DICT_6X6_250))
detector = cv.aruco.CharucoDetector(board, detectorParams=params)
cameraMatrix[0, 0] = cameraMatrix[1, 1] = 600
cameraMatrix[0, 2] = imgSize[0] / 2
cameraMatrix[1, 2] = imgSize[1] / 2
# for different perspectives
distCoeffs = np.zeros((5, 1), dtype=np.float64)
for distance in [0.2, 0.4]:
for yaw in range(-55, 51, 25):
for pitch in range(-55, 51, 25):
markerBorder = iteration % 2 + 1
iteration += 1
# create synthetic image
img, rvec, tvec = projectCharucoBoard(board, cameraMatrix, yaw * pi / 180, pitch * pi / 180, distance, imgSize, markerBorder)
params.markerBorderBits = markerBorder
detector.setDetectorParameters(params)
if (iteration % 2 != 0):
charucoParameters = cv.aruco.CharucoParameters()
charucoParameters.cameraMatrix = cameraMatrix
charucoParameters.distCoeffs = distCoeffs
detector.setCharucoParameters(charucoParameters)
charucoCorners, charucoIds, corners, ids = detector.detectBoard(img)
self.assertGreater(len(ids), 0)
copyChessboardCorners = board.getChessboardCorners()
copyChessboardCorners -= np.array(board.getRightBottomCorner()) / 2
projectedCharucoCorners, _ = cv.projectPoints(copyChessboardCorners, rvec, tvec, cameraMatrix, distCoeffs)
if charucoIds is None:
self.assertEqual(iteration, 46)
continue
for i in range(len(charucoIds)):
currentId = charucoIds[i]
self.assertLess(currentId, len(board.getChessboardCorners()))
reprErr = cv.norm(charucoCorners[i] - projectedCharucoCorners[currentId])
self.assertLessEqual(reprErr, 5)
if __name__ == '__main__':
NewOpenCVTests.bootstrap()

Loading…
Cancel
Save