chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,37 @@
|
||||
#ifdef HAVE_OPENCV_OBJDETECT
|
||||
|
||||
#include "opencv2/objdetect.hpp"
|
||||
|
||||
typedef QRCodeEncoder::Params QRCodeEncoder_Params;
|
||||
|
||||
typedef HOGDescriptor::HistogramNormType HOGDescriptor_HistogramNormType;
|
||||
typedef HOGDescriptor::DescriptorStorageFormat HOGDescriptor_DescriptorStorageFormat;
|
||||
|
||||
class NativeByteArray
|
||||
{
|
||||
public:
|
||||
inline NativeByteArray& operator=(const std::string& from) {
|
||||
val = from;
|
||||
return *this;
|
||||
}
|
||||
std::string val;
|
||||
};
|
||||
|
||||
class vector_NativeByteArray : public std::vector<std::string> {};
|
||||
|
||||
template<>
|
||||
PyObject* pyopencv_from(const NativeByteArray& from)
|
||||
{
|
||||
return PyBytes_FromStringAndSize(from.val.c_str(), from.val.size());
|
||||
}
|
||||
|
||||
template<>
|
||||
PyObject* pyopencv_from(const vector_NativeByteArray& results)
|
||||
{
|
||||
PyObject* list = PyList_New(results.size());
|
||||
for(size_t i = 0; i < results.size(); ++i)
|
||||
PyList_SetItem(list, i, PyBytes_FromStringAndSize(results[i].c_str(), results[i].size()));
|
||||
return list;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,33 @@
|
||||
#!/usr/bin/env python
|
||||
'''
|
||||
===============================================================================
|
||||
Barcode detect and decode pipeline.
|
||||
===============================================================================
|
||||
'''
|
||||
import os
|
||||
import numpy as np
|
||||
import cv2 as cv
|
||||
|
||||
from tests_common import NewOpenCVTests
|
||||
|
||||
class barcode_detector_test(NewOpenCVTests):
|
||||
|
||||
def test_detect(self):
|
||||
img = cv.imread(os.path.join(self.extraTestDataPath, 'cv/barcode/multiple/4_barcodes.jpg'))
|
||||
self.assertFalse(img is None)
|
||||
detector = cv.barcode_BarcodeDetector()
|
||||
retval, corners = detector.detect(img)
|
||||
self.assertTrue(retval)
|
||||
self.assertEqual(corners.shape, (4, 4, 2))
|
||||
|
||||
def test_detect_and_decode(self):
|
||||
img = cv.imread(os.path.join(self.extraTestDataPath, 'cv/barcode/single/book.jpg'))
|
||||
self.assertFalse(img is None)
|
||||
detector = cv.barcode_BarcodeDetector()
|
||||
retval, decoded_info, decoded_type, corners = detector.detectAndDecodeWithType(img)
|
||||
self.assertTrue(retval)
|
||||
self.assertTrue(len(decoded_info) > 0)
|
||||
self.assertTrue(len(decoded_type) > 0)
|
||||
self.assertEqual(decoded_info[0], "9787115279460")
|
||||
self.assertEqual(decoded_type[0], "EAN_13")
|
||||
self.assertEqual(corners.shape, (1, 4, 2))
|
||||
@@ -0,0 +1,92 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
'''
|
||||
face detection using haar cascades
|
||||
'''
|
||||
|
||||
# Python 2/3 compatibility
|
||||
from __future__ import print_function
|
||||
|
||||
import numpy as np
|
||||
import cv2 as cv
|
||||
|
||||
def detect(img, cascade):
|
||||
rects = cascade.detectMultiScale(img, scaleFactor=1.275, minNeighbors=4, minSize=(30, 30),
|
||||
flags=cv.CASCADE_SCALE_IMAGE)
|
||||
if len(rects) == 0:
|
||||
return []
|
||||
rects[:,2:] += rects[:,:2]
|
||||
return rects
|
||||
|
||||
from tests_common import NewOpenCVTests, intersectionRate
|
||||
|
||||
class facedetect_test(NewOpenCVTests):
|
||||
|
||||
def test_facedetect(self):
|
||||
cascade_fn = self.repoPath + '/data/haarcascades/haarcascade_frontalface_alt.xml'
|
||||
nested_fn = self.repoPath + '/data/haarcascades/haarcascade_eye.xml'
|
||||
|
||||
cascade = cv.CascadeClassifier(cascade_fn)
|
||||
nested = cv.CascadeClassifier(nested_fn)
|
||||
|
||||
samples = ['samples/data/lena.jpg', 'cv/cascadeandhog/images/mona-lisa.png']
|
||||
|
||||
faces = []
|
||||
eyes = []
|
||||
|
||||
testFaces = [
|
||||
#lena
|
||||
[[218, 200, 389, 371],
|
||||
[ 244, 240, 294, 290],
|
||||
[ 309, 246, 352, 289]],
|
||||
|
||||
#lisa
|
||||
[[167, 119, 307, 259],
|
||||
[188, 153, 229, 194],
|
||||
[236, 153, 277, 194]]
|
||||
]
|
||||
|
||||
for sample in samples:
|
||||
|
||||
img = self.get_sample( sample)
|
||||
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
|
||||
gray = cv.GaussianBlur(gray, (5, 5), 0)
|
||||
|
||||
rects = detect(gray, cascade)
|
||||
faces.append(rects)
|
||||
|
||||
if not nested.empty():
|
||||
for x1, y1, x2, y2 in rects:
|
||||
roi = gray[y1:y2, x1:x2]
|
||||
subrects = detect(roi.copy(), nested)
|
||||
|
||||
for rect in subrects:
|
||||
rect[0] += x1
|
||||
rect[2] += x1
|
||||
rect[1] += y1
|
||||
rect[3] += y1
|
||||
|
||||
eyes.append(subrects)
|
||||
|
||||
faces_matches = 0
|
||||
eyes_matches = 0
|
||||
|
||||
eps = 0.8
|
||||
|
||||
for i in range(len(faces)):
|
||||
for j in range(len(testFaces)):
|
||||
if intersectionRate(faces[i][0], testFaces[j][0]) > eps:
|
||||
faces_matches += 1
|
||||
#check eyes
|
||||
if len(eyes[i]) == 2:
|
||||
if intersectionRate(eyes[i][0], testFaces[j][1]) > eps and intersectionRate(eyes[i][1] , testFaces[j][2]) > eps:
|
||||
eyes_matches += 1
|
||||
elif intersectionRate(eyes[i][1], testFaces[j][1]) > eps and intersectionRate(eyes[i][0], testFaces[j][2]) > eps:
|
||||
eyes_matches += 1
|
||||
|
||||
self.assertEqual(faces_matches, 2)
|
||||
self.assertEqual(eyes_matches, 2)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
NewOpenCVTests.bootstrap()
|
||||
@@ -0,0 +1,520 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# Python 2/3 compatibility
|
||||
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):
|
||||
p1 = np.array([[0, 0, 0], [0, 1, 0], [1, 1, 0], [1, 0, 0]], dtype=np.float32)
|
||||
p2 = np.array([[1, 0, 0], [1, 1, 0], [2, 1, 0], [2, 0, 0]], dtype=np.float32)
|
||||
objPoints = np.array([p1, p2])
|
||||
dictionary = cv.aruco.getPredefinedDictionary(cv.aruco.DICT_4X4_50)
|
||||
ids = np.array([0, 1])
|
||||
|
||||
board = cv.aruco.Board(objPoints, dictionary, ids)
|
||||
np.testing.assert_array_equal(board.getIds().squeeze(), ids)
|
||||
np.testing.assert_array_equal(np.ravel(np.array(board.getObjPoints())), np.ravel(np.concatenate([p1, p2])))
|
||||
|
||||
def test_idsAccessibility(self):
|
||||
|
||||
ids = np.arange(17)
|
||||
rev_ids = ids[::-1]
|
||||
|
||||
aruco_dict = cv.aruco.getPredefinedDictionary(cv.aruco.DICT_5X5_250)
|
||||
board = cv.aruco.CharucoBoard((7, 5), 1, 0.5, aruco_dict)
|
||||
|
||||
np.testing.assert_array_equal(board.getIds().squeeze(), ids)
|
||||
|
||||
board = cv.aruco.CharucoBoard((7, 5), 1, 0.5, aruco_dict, rev_ids)
|
||||
np.testing.assert_array_equal(board.getIds().squeeze(), rev_ids)
|
||||
|
||||
board = cv.aruco.CharucoBoard((7, 5), 1, 0.5, aruco_dict, ids)
|
||||
np.testing.assert_array_equal(board.getIds().squeeze(), ids)
|
||||
|
||||
def test_identify(self):
|
||||
aruco_dict = cv.aruco.getPredefinedDictionary(cv.aruco.DICT_4X4_50)
|
||||
expected_idx = 9
|
||||
expected_rotation = 2
|
||||
bit_marker = np.array([[0, 1, 1, 0], [1, 0, 1, 0], [1, 1, 1, 1], [0, 0, 1, 1]], dtype=np.uint8)
|
||||
|
||||
check, idx, rotation = aruco_dict.identify(bit_marker, 0)
|
||||
|
||||
self.assertTrue(check, True)
|
||||
self.assertEqual(idx, expected_idx)
|
||||
self.assertEqual(rotation, expected_rotation)
|
||||
|
||||
def test_getDistanceToId(self):
|
||||
aruco_dict = cv.aruco.getPredefinedDictionary(cv.aruco.DICT_4X4_50)
|
||||
idx = 7
|
||||
rotation = 3
|
||||
bit_marker = np.array([[0, 1, 0, 1], [0, 1, 1, 1], [1, 1, 0, 0], [0, 1, 0, 0]], dtype=np.uint8)
|
||||
dist = aruco_dict.getDistanceToId(bit_marker, idx)
|
||||
|
||||
self.assertEqual(dist, 0)
|
||||
|
||||
def test_getDistanceToId_cell_pixel_ratio(self):
|
||||
aruco_dict = cv.aruco.getPredefinedDictionary(cv.aruco.DICT_4X4_50)
|
||||
idx = 7
|
||||
valid_bit_id_threshold = 0.49
|
||||
bit_marker = np.array([[0, 1, 0, 1], [0, 1, 1, 1], [1, 1, 0, 0], [0, 1, 0, 0]], dtype=np.uint8)
|
||||
ratio_marker = bit_marker.astype(np.float32)
|
||||
|
||||
# Same marker as test_getDistanceToId, but passed as float cell ratios.
|
||||
dist = aruco_dict.getDistanceToId(ratio_marker, idx, True, valid_bit_id_threshold)
|
||||
self.assertEqual(dist, 0)
|
||||
|
||||
# A small drift stays within the threshold.
|
||||
accepted_ratio = ratio_marker.copy()
|
||||
accepted_ratio[0, 0] = 0.4
|
||||
dist = aruco_dict.getDistanceToId(accepted_ratio, idx, True, valid_bit_id_threshold)
|
||||
self.assertEqual(dist, 0)
|
||||
|
||||
# A full flip crosses the threshold and counts as one bad cell.
|
||||
erroneous_ratio = ratio_marker.copy()
|
||||
erroneous_ratio[0, 0] = 1.0 - erroneous_ratio[0, 0]
|
||||
dist = aruco_dict.getDistanceToId(onlyCellPixelRatio=erroneous_ratio,
|
||||
id=idx,
|
||||
allRotations=True,
|
||||
validBitIdThreshold=valid_bit_id_threshold)
|
||||
self.assertEqual(dist, 1)
|
||||
|
||||
def test_aruco_detector(self):
|
||||
aruco_params = cv.aruco.DetectorParameters()
|
||||
aruco_dict = cv.aruco.getPredefinedDictionary(cv.aruco.DICT_4X4_250)
|
||||
aruco_detector = cv.aruco.ArucoDetector(aruco_dict, aruco_params)
|
||||
id = 2
|
||||
marker_size = 100
|
||||
offset = 10
|
||||
img_marker = cv.aruco.generateImageMarker(aruco_dict, id, marker_size, aruco_params.markerBorderBits)
|
||||
img_marker = np.pad(img_marker, pad_width=offset, mode='constant', constant_values=255)
|
||||
gold_corners = np.array([[offset, offset],[marker_size+offset-1.0,offset],
|
||||
[marker_size+offset-1.0,marker_size+offset-1.0],
|
||||
[offset, marker_size+offset-1.0]], dtype=np.float32)
|
||||
corners, ids, rejected = aruco_detector.detectMarkers(img_marker)
|
||||
|
||||
self.assertEqual(1, len(ids))
|
||||
self.assertEqual(id, ids[0])
|
||||
for i in range(0, len(corners)):
|
||||
np.testing.assert_array_equal(gold_corners, corners[i].reshape(4, 2))
|
||||
|
||||
def test_aruco_detector_refine(self):
|
||||
aruco_params = cv.aruco.DetectorParameters()
|
||||
aruco_dict = cv.aruco.getPredefinedDictionary(cv.aruco.DICT_4X4_250)
|
||||
aruco_detector = cv.aruco.ArucoDetector(aruco_dict, aruco_params)
|
||||
board_size = (3, 4)
|
||||
board = cv.aruco.GridBoard(board_size, 5.0, 1.0, aruco_dict)
|
||||
board_image = board.generateImage((board_size[0]*50, board_size[1]*50), marginSize=10)
|
||||
|
||||
corners, ids, rejected = aruco_detector.detectMarkers(board_image)
|
||||
self.assertEqual(board_size[0]*board_size[1], len(ids))
|
||||
|
||||
part_corners, part_ids, part_rejected = corners[:-1], ids[:-1], list(rejected)
|
||||
part_rejected.append(corners[-1])
|
||||
|
||||
refine_corners, refine_ids, refine_rejected, recovered_ids = aruco_detector.refineDetectedMarkers(board_image, board, part_corners, part_ids, part_rejected)
|
||||
|
||||
self.assertEqual(board_size[0] * board_size[1], len(refine_ids))
|
||||
self.assertEqual(1, len(recovered_ids))
|
||||
|
||||
self.assertEqual(ids[-1], refine_ids[-1])
|
||||
self.assertEqual((1, 4, 2), refine_corners[0].shape)
|
||||
np.testing.assert_array_equal(corners, refine_corners)
|
||||
|
||||
def test_charuco_refine(self):
|
||||
aruco_dict = cv.aruco.getPredefinedDictionary(cv.aruco.DICT_6X6_50)
|
||||
board_size = (3, 4)
|
||||
board = cv.aruco.CharucoBoard(board_size, 1., .7, aruco_dict)
|
||||
aruco_detector = cv.aruco.ArucoDetector(aruco_dict)
|
||||
charuco_detector = cv.aruco.CharucoDetector(board)
|
||||
cell_size = 100
|
||||
image = board.generateImage((cell_size*board_size[0], cell_size*board_size[1]))
|
||||
camera = np.array([[1, 0, 0.5],
|
||||
[0, 1, 0.5],
|
||||
[0, 0, 1]])
|
||||
dist = np.array([0, 0, 0, 0, 0], dtype=np.float32).reshape(1, -1)
|
||||
|
||||
# generate gold corners of the ArUco markers for the test
|
||||
gold_corners = np.array(board.getObjPoints())[:, :, 0:2]*cell_size
|
||||
|
||||
# detect corners
|
||||
markerCorners, markerIds, _ = aruco_detector.detectMarkers(image)
|
||||
|
||||
# test refine
|
||||
rejected = [markerCorners[-1]]
|
||||
markerCorners, markerIds = markerCorners[:-1], markerIds[:-1]
|
||||
markerCorners, markerIds, _, _ = aruco_detector.refineDetectedMarkers(image, board, markerCorners, markerIds,
|
||||
rejected, cameraMatrix=camera, distCoeffs=dist)
|
||||
|
||||
charucoCorners, charucoIds, _, _ = charuco_detector.detectBoard(image, markerCorners=markerCorners,
|
||||
markerIds=markerIds)
|
||||
self.assertEqual(len(charucoIds), 6)
|
||||
self.assertEqual(len(markerIds), 6)
|
||||
|
||||
for i, id in enumerate(markerIds.reshape(-1)):
|
||||
np.testing.assert_allclose(gold_corners[id], markerCorners[i].reshape(4, 2), 0.01, 1.)
|
||||
|
||||
def test_write_read_dictionary(self):
|
||||
try:
|
||||
aruco_dict = cv.aruco.getPredefinedDictionary(cv.aruco.DICT_5X5_50)
|
||||
markers_gold = aruco_dict.bytesList
|
||||
|
||||
# write aruco_dict
|
||||
fd, filename = tempfile.mkstemp(prefix="opencv_python_aruco_dict_", suffix=".yml")
|
||||
os.close(fd)
|
||||
|
||||
fs_write = cv.FileStorage(filename, cv.FileStorage_WRITE)
|
||||
aruco_dict.writeDictionary(fs_write)
|
||||
fs_write.release()
|
||||
|
||||
# reset aruco_dict
|
||||
aruco_dict = cv.aruco.getPredefinedDictionary(cv.aruco.DICT_6X6_250)
|
||||
|
||||
# read aruco_dict
|
||||
fs_read = cv.FileStorage(filename, cv.FileStorage_READ)
|
||||
aruco_dict.readDictionary(fs_read.root())
|
||||
fs_read.release()
|
||||
|
||||
# check equal
|
||||
self.assertEqual(aruco_dict.markerSize, 5)
|
||||
self.assertEqual(aruco_dict.maxCorrectionBits, 3)
|
||||
np.testing.assert_array_equal(aruco_dict.bytesList, markers_gold)
|
||||
|
||||
finally:
|
||||
if os.path.exists(filename):
|
||||
os.remove(filename)
|
||||
|
||||
def test_charuco_detector(self):
|
||||
aruco_dict = cv.aruco.getPredefinedDictionary(cv.aruco.DICT_4X4_250)
|
||||
board_size = (3, 3)
|
||||
board = cv.aruco.CharucoBoard(board_size, 1.0, .8, aruco_dict)
|
||||
charuco_detector = cv.aruco.CharucoDetector(board)
|
||||
cell_size = 100
|
||||
|
||||
image = board.generateImage((cell_size*board_size[0], cell_size*board_size[1]))
|
||||
|
||||
# Note: Expected values adjusted by -0.5px after fixing the systematic offset bug in charuco_detector.cpp
|
||||
# The fix removes the incorrect +0.5 offset that was added after cornerSubPix
|
||||
list_gold_corners = []
|
||||
for i in range(1, board_size[0]):
|
||||
for j in range(1, board_size[1]):
|
||||
list_gold_corners.append((j*cell_size - 0.5, i*cell_size - 0.5))
|
||||
gold_corners = np.array(list_gold_corners, dtype=np.float32)
|
||||
|
||||
charucoCorners, charucoIds, markerCorners, markerIds = charuco_detector.detectBoard(image)
|
||||
|
||||
self.assertEqual(len(charucoIds), 4)
|
||||
for i in range(0, 4):
|
||||
self.assertEqual(charucoIds[i], i)
|
||||
np.testing.assert_allclose(gold_corners, charucoCorners.reshape(-1, 2), 0.01, 0.1)
|
||||
|
||||
def test_detect_diamonds(self):
|
||||
aruco_dict = cv.aruco.getPredefinedDictionary(cv.aruco.DICT_6X6_250)
|
||||
board_size = (3, 3)
|
||||
board = cv.aruco.CharucoBoard(board_size, 1.0, .8, aruco_dict)
|
||||
charuco_detector = cv.aruco.CharucoDetector(board)
|
||||
cell_size = 120
|
||||
|
||||
image = board.generateImage((cell_size*board_size[0], cell_size*board_size[1]))
|
||||
|
||||
# Note: Expected values adjusted by -0.5px after fixing the systematic offset bug in charuco_detector.cpp
|
||||
# The fix removes the incorrect +0.5 offset that was added after cornerSubPix
|
||||
list_gold_corners = [(cell_size - 0.5, cell_size - 0.5), (2*cell_size - 0.5, cell_size - 0.5),
|
||||
(2*cell_size - 0.5, 2*cell_size - 0.5), (cell_size - 0.5, 2*cell_size - 0.5)]
|
||||
gold_corners = np.array(list_gold_corners, dtype=np.float32)
|
||||
|
||||
diamond_corners, diamond_ids, marker_corners, marker_ids = charuco_detector.detectDiamonds(image)
|
||||
|
||||
self.assertEqual(diamond_ids.size, 4)
|
||||
self.assertEqual(marker_ids.size, 4)
|
||||
for i in range(0, 4):
|
||||
self.assertEqual(diamond_ids[0][0][i], i)
|
||||
np.testing.assert_allclose(gold_corners, np.array(diamond_corners, dtype=np.float32).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
|
||||
params.validBitIdThreshold = 0.5
|
||||
|
||||
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:
|
||||
# Detection can fail at extreme viewing angles
|
||||
self.assertTrue(abs(yaw) >= 45 or abs(pitch) >= 45,
|
||||
f"Detection failed unexpectedly at yaw={yaw}, pitch={pitch}")
|
||||
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)
|
||||
|
||||
def test_aruco_match_image_points(self):
|
||||
aruco_dict = cv.aruco.getPredefinedDictionary(cv.aruco.DICT_4X4_50)
|
||||
board_size = (3, 4)
|
||||
board = cv.aruco.GridBoard(board_size, 5.0, 1.0, aruco_dict)
|
||||
aruco_corners = np.array(board.getObjPoints())[:, :, :2]
|
||||
aruco_ids = board.getIds()
|
||||
obj_points, img_points = board.matchImagePoints(aruco_corners, aruco_ids)
|
||||
aruco_corners = aruco_corners.reshape(-1, 2)
|
||||
|
||||
self.assertEqual(aruco_corners.shape[0], obj_points.shape[0])
|
||||
self.assertEqual(img_points.shape[0], obj_points.shape[0])
|
||||
self.assertEqual(2, img_points.shape[2])
|
||||
np.testing.assert_array_equal(aruco_corners, obj_points[:, :, :2].reshape(-1, 2))
|
||||
|
||||
def test_charuco_match_image_points(self):
|
||||
aruco_dict = cv.aruco.getPredefinedDictionary(cv.aruco.DICT_4X4_50)
|
||||
board_size = (3, 4)
|
||||
board = cv.aruco.CharucoBoard(board_size, 5.0, 1.0, aruco_dict)
|
||||
chessboard_corners = np.array(board.getChessboardCorners())[:, :2]
|
||||
chessboard_ids = board.getIds()
|
||||
obj_points, img_points = board.matchImagePoints(chessboard_corners, chessboard_ids)
|
||||
|
||||
self.assertEqual(chessboard_corners.shape[0], obj_points.shape[0])
|
||||
self.assertEqual(img_points.shape[0], obj_points.shape[0])
|
||||
self.assertEqual(2, img_points.shape[2])
|
||||
np.testing.assert_array_equal(chessboard_corners, obj_points[:, :, :2].reshape(-1, 2))
|
||||
|
||||
def test_draw_detected_markers(self):
|
||||
detected_points = [[[10, 10], [50, 10], [50, 50], [10, 50]]]
|
||||
img = np.zeros((60, 60), dtype=np.uint8)
|
||||
|
||||
# add extra dimension in Python to create Nx4 Mat with 2 channels
|
||||
points1 = np.array(detected_points).reshape(-1, 4, 1, 2)
|
||||
img = cv.aruco.drawDetectedMarkers(img, points1, borderColor=255)
|
||||
|
||||
# check that the marker borders are painted
|
||||
contours, _ = cv.findContours(img, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
|
||||
self.assertEqual(len(contours), 1)
|
||||
self.assertEqual(img[10, 10], 255)
|
||||
self.assertEqual(img[50, 10], 255)
|
||||
self.assertEqual(img[50, 50], 255)
|
||||
self.assertEqual(img[10, 50], 255)
|
||||
|
||||
# must throw Exception without extra dimension
|
||||
points2 = np.array(detected_points)
|
||||
with self.assertRaises(Exception):
|
||||
img = cv.aruco.drawDetectedMarkers(img, points2, borderColor=255)
|
||||
|
||||
def test_draw_detected_charuco(self):
|
||||
detected_points = [[[10, 10], [50, 10], [50, 50], [10, 50]]]
|
||||
img = np.zeros((60, 60), dtype=np.uint8)
|
||||
|
||||
# add extra dimension in Python to create Nx1 Mat with 2 channels
|
||||
points = np.array(detected_points).reshape(-1, 1, 2)
|
||||
img = cv.aruco.drawDetectedCornersCharuco(img, points, cornerColor=255)
|
||||
|
||||
# check that the 4 charuco corners are painted
|
||||
contours, _ = cv.findContours(img, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
|
||||
self.assertEqual(len(contours), 4)
|
||||
for contour in contours:
|
||||
center_x = round(np.average(contour[:, 0, 0]))
|
||||
center_y = round(np.average(contour[:, 0, 1]))
|
||||
center = [center_x, center_y]
|
||||
self.assertTrue(center in detected_points[0])
|
||||
|
||||
# must throw Exception without extra dimension
|
||||
points2 = np.array(detected_points)
|
||||
with self.assertRaises(Exception):
|
||||
img = cv.aruco.drawDetectedCornersCharuco(img, points2, borderColor=255)
|
||||
|
||||
def test_draw_detected_diamonds(self):
|
||||
detected_points = [[[10, 10], [50, 10], [50, 50], [10, 50]]]
|
||||
img = np.zeros((60, 60), dtype=np.uint8)
|
||||
|
||||
# add extra dimension in Python to create Nx4 Mat with 2 channels
|
||||
points = np.array(detected_points).reshape(-1, 4, 1, 2)
|
||||
img = cv.aruco.drawDetectedDiamonds(img, points, borderColor=255)
|
||||
|
||||
# check that the diamonds borders are painted
|
||||
contours, _ = cv.findContours(img, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
|
||||
self.assertEqual(len(contours), 1)
|
||||
self.assertEqual(img[10, 10], 255)
|
||||
self.assertEqual(img[50, 10], 255)
|
||||
self.assertEqual(img[50, 50], 255)
|
||||
self.assertEqual(img[10, 50], 255)
|
||||
|
||||
# must throw Exception without extra dimension
|
||||
points2 = np.array(detected_points)
|
||||
with self.assertRaises(Exception):
|
||||
img = cv.aruco.drawDetectedDiamonds(img, points2, borderColor=255)
|
||||
|
||||
def test_multi_dict_arucodetector(self):
|
||||
aruco_params = cv.aruco.DetectorParameters()
|
||||
aruco_dicts = [
|
||||
cv.aruco.getPredefinedDictionary(cv.aruco.DICT_4X4_250),
|
||||
cv.aruco.getPredefinedDictionary(cv.aruco.DICT_5X5_250)
|
||||
]
|
||||
aruco_detector = cv.aruco.ArucoDetector(aruco_dicts, aruco_params)
|
||||
id = 2
|
||||
marker_size = 100
|
||||
offset = 10
|
||||
img_marker1 = cv.aruco.generateImageMarker(aruco_dicts[0], id, marker_size, aruco_params.markerBorderBits)
|
||||
img_marker1 = np.pad(img_marker1, pad_width=offset, mode='constant', constant_values=255)
|
||||
img_marker2 = cv.aruco.generateImageMarker(aruco_dicts[1], id, marker_size, aruco_params.markerBorderBits)
|
||||
img_marker2 = np.pad(img_marker2, pad_width=offset, mode='constant', constant_values=255)
|
||||
img_markers = np.concatenate((img_marker1, img_marker2), axis=1)
|
||||
|
||||
corners, ids, rejected, dictIndices = aruco_detector.detectMarkersMultiDict(img_markers)
|
||||
|
||||
self.assertEqual(2, len(ids))
|
||||
self.assertEqual(id, ids[0])
|
||||
self.assertEqual(id, ids[1])
|
||||
self.assertEqual(2, len(dictIndices))
|
||||
self.assertEqual(0, dictIndices[0])
|
||||
self.assertEqual(1, dictIndices[1])
|
||||
|
||||
if __name__ == '__main__':
|
||||
NewOpenCVTests.bootstrap()
|
||||
@@ -0,0 +1,65 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
'''
|
||||
example to detect upright people in images using HOG features
|
||||
'''
|
||||
|
||||
# Python 2/3 compatibility
|
||||
from __future__ import print_function
|
||||
|
||||
import numpy as np
|
||||
import cv2 as cv
|
||||
|
||||
|
||||
def inside(r, q):
|
||||
rx, ry, rw, rh = r
|
||||
qx, qy, qw, qh = q
|
||||
return rx > qx and ry > qy and rx + rw < qx + qw and ry + rh < qy + qh
|
||||
|
||||
from tests_common import NewOpenCVTests, intersectionRate
|
||||
|
||||
class peopledetect_test(NewOpenCVTests):
|
||||
def test_peopledetect(self):
|
||||
|
||||
hog = cv.HOGDescriptor()
|
||||
hog.setSVMDetector( cv.HOGDescriptor_getDefaultPeopleDetector() )
|
||||
|
||||
dirPath = 'samples/data/'
|
||||
samples = ['basketball1.png', 'basketball2.png']
|
||||
|
||||
testPeople = [
|
||||
[[23, 76, 164, 477], [440, 22, 637, 478]],
|
||||
[[23, 76, 164, 477], [440, 22, 637, 478]]
|
||||
]
|
||||
|
||||
eps = 0.5
|
||||
|
||||
for sample in samples:
|
||||
|
||||
img = self.get_sample(dirPath + sample, 0)
|
||||
|
||||
found, _w = hog.detectMultiScale(img, winStride=(8,8), padding=(32,32), scale=1.05)
|
||||
found_filtered = []
|
||||
for ri, r in enumerate(found):
|
||||
for qi, q in enumerate(found):
|
||||
if ri != qi and inside(r, q):
|
||||
break
|
||||
else:
|
||||
found_filtered.append(r)
|
||||
|
||||
matches = 0
|
||||
|
||||
for i in range(len(found_filtered)):
|
||||
for j in range(len(testPeople)):
|
||||
|
||||
found_rect = (found_filtered[i][0], found_filtered[i][1],
|
||||
found_filtered[i][0] + found_filtered[i][2],
|
||||
found_filtered[i][1] + found_filtered[i][3])
|
||||
|
||||
if intersectionRate(found_rect, testPeople[j][0]) > eps or intersectionRate(found_rect, testPeople[j][1]) > eps:
|
||||
matches += 1
|
||||
|
||||
self.assertGreater(matches, 0)
|
||||
|
||||
if __name__ == '__main__':
|
||||
NewOpenCVTests.bootstrap()
|
||||
@@ -0,0 +1,86 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#!/usr/bin/env python
|
||||
'''
|
||||
===============================================================================
|
||||
QR code detect and decode pipeline.
|
||||
===============================================================================
|
||||
'''
|
||||
import os
|
||||
import numpy as np
|
||||
import cv2 as cv
|
||||
|
||||
from tests_common import NewOpenCVTests, unittest
|
||||
|
||||
class qrcode_detector_test(NewOpenCVTests):
|
||||
|
||||
def test_detect(self):
|
||||
img = cv.imread(os.path.join(self.extraTestDataPath, 'cv/qrcode/link_ocv.jpg'))
|
||||
self.assertFalse(img is None)
|
||||
detector = cv.QRCodeDetector()
|
||||
retval, points = detector.detect(img)
|
||||
self.assertTrue(retval)
|
||||
self.assertEqual(points.shape, (1, 4, 2))
|
||||
|
||||
def test_detect_and_decode(self):
|
||||
img = cv.imread(os.path.join(self.extraTestDataPath, 'cv/qrcode/link_ocv.jpg'))
|
||||
self.assertFalse(img is None)
|
||||
detector = cv.QRCodeDetector()
|
||||
retval, points, straight_qrcode = detector.detectAndDecode(img)
|
||||
self.assertEqual(retval, "https://opencv.org/")
|
||||
self.assertEqual(points.shape, (1, 4, 2))
|
||||
|
||||
def test_detect_multi(self):
|
||||
img = cv.imread(os.path.join(self.extraTestDataPath, 'cv/qrcode/multiple/6_qrcodes.png'))
|
||||
self.assertFalse(img is None)
|
||||
detector = cv.QRCodeDetector()
|
||||
retval, points = detector.detectMulti(img)
|
||||
self.assertTrue(retval)
|
||||
self.assertEqual(points.shape, (6, 4, 2))
|
||||
|
||||
def test_detect_and_decode_multi(self):
|
||||
img = cv.imread(os.path.join(self.extraTestDataPath, 'cv/qrcode/multiple/6_qrcodes.png'))
|
||||
self.assertFalse(img is None)
|
||||
detector = cv.QRCodeDetector()
|
||||
retval, decoded_data, points, straight_qrcode = detector.detectAndDecodeMulti(img)
|
||||
self.assertTrue(retval)
|
||||
self.assertEqual(len(decoded_data), 6)
|
||||
self.assertTrue("TWO STEPS FORWARD" in decoded_data)
|
||||
self.assertTrue("EXTRA" in decoded_data)
|
||||
self.assertTrue("SKIP" in decoded_data)
|
||||
self.assertTrue("STEP FORWARD" in decoded_data)
|
||||
self.assertTrue("STEP BACK" in decoded_data)
|
||||
self.assertTrue("QUESTION" in decoded_data)
|
||||
self.assertEqual(points.shape, (6, 4, 2))
|
||||
|
||||
def test_decode_non_ascii(self):
|
||||
import sys
|
||||
if sys.version_info[0] < 3:
|
||||
raise unittest.SkipTest('Python 2.x is not supported')
|
||||
|
||||
img = cv.imread(os.path.join(self.extraTestDataPath, 'cv/qrcode/umlaut.png'))
|
||||
self.assertFalse(img is None)
|
||||
detector = cv.QRCodeDetector()
|
||||
decoded_data, _, _ = detector.detectAndDecode(img)
|
||||
self.assertTrue(isinstance(decoded_data, str))
|
||||
self.assertTrue("Müllheimstrasse" in decoded_data)
|
||||
|
||||
def test_kanji(self):
|
||||
inp = "こんにちは世界"
|
||||
inp_bytes = inp.encode("shift-jis")
|
||||
|
||||
params = cv.QRCodeEncoder_Params()
|
||||
params.mode = cv.QRCodeEncoder_MODE_KANJI
|
||||
encoder = cv.QRCodeEncoder_create(params)
|
||||
qrcode = encoder.encode(inp_bytes)
|
||||
qrcode = cv.resize(qrcode, (0, 0), fx=2, fy=2, interpolation=cv.INTER_NEAREST)
|
||||
|
||||
detector = cv.QRCodeDetector()
|
||||
data, _, _ = detector.detectAndDecodeBytes(qrcode)
|
||||
self.assertEqual(data, inp_bytes)
|
||||
self.assertEqual(detector.getEncoding(), cv.QRCodeEncoder_ECI_SHIFT_JIS)
|
||||
self.assertEqual(data.decode("shift-jis"), inp)
|
||||
|
||||
_, data, _, _ = detector.detectAndDecodeBytesMulti(qrcode)
|
||||
self.assertEqual(data[0], inp_bytes)
|
||||
self.assertEqual(detector.getEncoding(0), cv.QRCodeEncoder_ECI_SHIFT_JIS)
|
||||
self.assertEqual(data[0].decode("shift-jis"), inp)
|
||||
Reference in New Issue
Block a user