1
0
mirror of https://github.com/opencv/opencv.git synced 2026-07-29 23:33:05 +04:00

Merge branch 4.x

This commit is contained in:
Alexander Smorkalov
2023-06-01 09:37:38 +03:00
565 changed files with 84396 additions and 17589 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

@@ -58,14 +58,22 @@ public:
CV_WRAP const Point3f& getRightBottomCorner() const;
/** @brief Given a board configuration and a set of detected markers, returns the corresponding
* image points and object points to call solvePnP()
* image points and object points, can be used in solvePnP()
*
* @param detectedCorners List of detected marker corners of the board.
* For CharucoBoard class you can set list of charuco corners.
* @param detectedIds List of identifiers for each marker or list of charuco identifiers for each corner.
* For CharucoBoard class you can set list of charuco identifiers for each corner.
* @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.
* For cv::Board and cv::GridBoard the method expects std::vector<std::vector<Point2f>> or std::vector<Mat> with Aruco marker corners.
* For cv::CharucoBoard the method expects std::vector<Point2f> or Mat with ChAruco corners (chess board corners matched with Aruco markers).
*
* @param detectedIds List of identifiers for each marker or charuco corner.
* For any Board class the method expects std::vector<int> or Mat.
*
* @param objPoints Vector of marker points in the board coordinate space.
* For any Board class the method expects std::vector<cv::Point3f> objectPoints or cv::Mat
*
* @param imgPoints Vector of marker points in the image coordinate space.
* For any Board class the method expects std::vector<cv::Point2f> objectPoints or cv::Mat
*
* @sa solvePnP
*/
CV_WRAP void matchImagePoints(InputArrayOfArrays detectedCorners, InputArray detectedIds,
OutputArray objPoints, OutputArray imgPoints) const;
@@ -138,6 +146,18 @@ public:
CV_WRAP CharucoBoard(const Size& size, float squareLength, float markerLength,
const Dictionary &dictionary, InputArray ids = noArray());
/** @brief set legacy chessboard pattern.
*
* Legacy setting creates chessboard patterns starting with a white box in the upper left corner
* if there is an even row count of chessboard boxes, otherwise it starts with a black box.
* This setting ensures compatibility to patterns created with OpenCV versions prior OpenCV 4.6.0.
* See https://github.com/opencv/opencv/issues/23152.
*
* Default value: false.
*/
CV_WRAP void setLegacyPattern(bool legacyPattern);
CV_WRAP bool getLegacyPattern() const;
CV_WRAP Size getChessboardSize() const;
CV_WRAP float getSquareLength() const;
CV_WRAP float getMarkerLength() const;
@@ -269,13 +269,13 @@ public:
* 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
* input image with corresponding camera model, if camera parameters are known
* @sa undistort, estimatePoseSingleMarkers, estimatePoseBoard
*/
CV_WRAP void detectMarkers(InputArray image, OutputArrayOfArrays corners, OutputArray ids,
OutputArrayOfArrays rejectedImgPoints = noArray()) const;
/** @brief Refind not detected markers based on the already detected and the board layout
/** @brief Refine not detected markers based on the already detected and the board layout
*
* @param image input image
* @param board layout of markers in the board.
@@ -13,7 +13,7 @@ namespace aruco {
//! @{
struct CV_EXPORTS_W_SIMPLE CharucoParameters {
CharucoParameters() {
CV_WRAP CharucoParameters() {
minMarkers = 2;
tryRefineMarkers = false;
}
@@ -54,10 +54,20 @@ public:
CV_WRAP virtual int getTopK() = 0;
/** @brief A simple interface to detect face from given image
*
/** @brief Detects faces in the input image. Following is an example output.
* ![image](pics/lena-face-detection.jpg)
* @param image an image to detect
* @param faces detection results stored in a cv::Mat
* @param faces detection results stored in a 2D cv::Mat of shape [num_faces, 15]
* - 0-1: x, y of bbox top left corner
* - 2-3: width, height of bbox
* - 4-5: x, y of right eye (blue point in the example image)
* - 6-7: x, y of left eye (red point in the example image)
* - 8-9: x, y of nose tip (green point in the example image)
* - 10-11: x, y of right corner of mouth (pink point in the example image)
* - 12-13: x, y of left corner of mouth (yellow point in the example image)
* - 14: face score
*/
CV_WRAP virtual int detect(InputArray image, OutputArray faces) = 0;
@@ -4,13 +4,109 @@
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)
@@ -142,5 +238,107 @@ 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)
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))
if __name__ == '__main__':
NewOpenCVTests.bootstrap()
+137 -78
View File
@@ -27,17 +27,17 @@ struct Board::Impl {
Impl(const Impl&) = delete;
Impl& operator=(const Impl&) = delete;
virtual void matchImagePoints(InputArray detectedCorners, InputArray detectedIds, OutputArray _objPoints,
virtual void matchImagePoints(InputArrayOfArrays detectedCorners, InputArray detectedIds, OutputArray _objPoints,
OutputArray imgPoints) const;
virtual void generateImage(Size outSize, OutputArray img, int marginSize, int borderBits) const;
};
void Board::Impl::matchImagePoints(InputArray detectedCorners, InputArray detectedIds, OutputArray _objPoints,
OutputArray imgPoints) const {
CV_Assert(ids.size() == objPoints.size());
void Board::Impl::matchImagePoints(InputArrayOfArrays detectedCorners, InputArray detectedIds, OutputArray _objPoints,
OutputArray imgPoints) const {
CV_Assert(detectedIds.total() == detectedCorners.total());
CV_Assert(detectedIds.total() > 0ull);
CV_Assert(detectedCorners.depth() == CV_32F);
size_t nDetectedMarkers = detectedIds.total();
@@ -48,13 +48,19 @@ void Board::Impl::matchImagePoints(InputArray detectedCorners, InputArray detect
imgPnts.reserve(nDetectedMarkers);
// look for detected markers that belong to the board and get their information
Mat detectedIdsMat = detectedIds.getMat();
vector<Mat> detectedCornersVecMat;
detectedCorners.getMatVector(detectedCornersVecMat);
CV_Assert((int)detectedCornersVecMat.front().total()*detectedCornersVecMat.front().channels() == 8);
for(unsigned int i = 0; i < nDetectedMarkers; i++) {
int currentId = detectedIds.getMat().ptr< int >(0)[i];
int currentId = detectedIdsMat.at<int>(i);
for(unsigned int j = 0; j < ids.size(); j++) {
if(currentId == ids[j]) {
for(int p = 0; p < 4; p++) {
objPnts.push_back(objPoints[j][p]);
imgPnts.push_back(detectedCorners.getMat(i).ptr<Point2f>(0)[p]);
imgPnts.push_back(detectedCornersVecMat[i].ptr<Point2f>(0)[p]);
}
}
}
@@ -157,7 +163,6 @@ Board::Board():
Board::Board(InputArrayOfArrays objPoints, const Dictionary &dictionary, InputArray ids):
Board(new Board::Impl(dictionary)) {
CV_Assert(ids.size() == objPoints.size());
CV_Assert(objPoints.total() == ids.total());
CV_Assert(objPoints.type() == CV_32FC3 || objPoints.type() == CV_32FC1);
@@ -213,7 +218,7 @@ void Board::generateImage(Size outSize, OutputArray img, int marginSize, int bor
impl->generateImage(outSize, img, marginSize, borderBits);
}
void Board::matchImagePoints(InputArray detectedCorners, InputArray detectedIds, OutputArray objPoints,
void Board::matchImagePoints(InputArrayOfArrays detectedCorners, InputArray detectedIds, OutputArray objPoints,
OutputArray imgPoints) const {
CV_Assert(this->impl);
impl->matchImagePoints(detectedCorners, detectedIds, objPoints, imgPoints);
@@ -224,7 +229,8 @@ struct GridBoardImpl : public Board::Impl {
Board::Impl(_dictionary),
size(_size),
markerLength(_markerLength),
markerSeparation(_markerSeparation)
markerSeparation(_markerSeparation),
legacyPattern(false)
{
CV_Assert(size.width*size.height > 0 && markerLength > 0 && markerSeparation > 0);
}
@@ -235,6 +241,8 @@ struct GridBoardImpl : public Board::Impl {
float markerLength;
// separation between markers in the grid
float markerSeparation;
// set pre4.6.0 chessboard pattern behavior (even row count patterns have a white box in the upper left corner)
bool legacyPattern;
};
GridBoard::GridBoard() {}
@@ -292,7 +300,8 @@ struct CharucoBoardImpl : Board::Impl {
Board::Impl(_dictionary),
size(_size),
squareLength(_squareLength),
markerLength(_markerLength)
markerLength(_markerLength),
legacyPattern(false)
{}
// chessboard size
@@ -304,6 +313,9 @@ struct CharucoBoardImpl : Board::Impl {
// Physical marker side length (normally in meters)
float markerLength;
// set pre4.6.0 chessboard pattern behavior (even row count patterns have a white box in the upper left corner)
bool legacyPattern;
// vector of chessboard 3D corners precalculated
std::vector<Point3f> chessboardCorners;
@@ -311,16 +323,65 @@ struct CharucoBoardImpl : Board::Impl {
std::vector<std::vector<int> > nearestMarkerIdx;
std::vector<std::vector<int> > nearestMarkerCorners;
void createCharucoBoard();
void calcNearestMarkerCorners();
void matchImagePoints(InputArrayOfArrays detectedCorners, InputArray detectedIds,
void matchImagePoints(InputArrayOfArrays detectedCharuco, InputArray detectedIds,
OutputArray objPoints, OutputArray imgPoints) const override;
void generateImage(Size outSize, OutputArray img, int marginSize, int borderBits) const override;
};
void CharucoBoardImpl::createCharucoBoard() {
float diffSquareMarkerLength = (squareLength - markerLength) / 2;
int totalMarkers = (int)(ids.size());
// calculate Board objPoints
int nextId = 0;
objPoints.clear();
for(int y = 0; y < size.height; y++) {
for(int x = 0; x < size.width; x++) {
if(legacyPattern && (size.height % 2 == 0)) { // legacy behavior only for even row count patterns
if((y + 1) % 2 == x % 2) continue; // black corner, no marker here
} else {
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
if (totalMarkers == 0)
ids.push_back(nextId);
nextId++;
}
}
if (totalMarkers > 0 && nextId != totalMarkers)
CV_Error(cv::Error::StsBadSize, "Size of ids must be equal to the number of markers: "+std::to_string(nextId));
// now fill chessboardCorners
chessboardCorners.clear();
for(int y = 0; y < size.height - 1; y++) {
for(int x = 0; x < size.width - 1; x++) {
Point3f corner;
corner.x = (x + 1) * squareLength;
corner.y = (y + 1) * squareLength;
corner.z = 0;
chessboardCorners.push_back(corner);
}
}
rightBottomBorder = Point3f(size.width * squareLength, size.height * squareLength, 0.f);
calcNearestMarkerCorners();
}
/** Fill nearestMarkerIdx and nearestMarkerCorners arrays */
void CharucoBoardImpl::calcNearestMarkerCorners() {
nearestMarkerIdx.clear();
nearestMarkerCorners.clear();
nearestMarkerIdx.resize(chessboardCorners.size());
nearestMarkerCorners.resize(chessboardCorners.size());
unsigned int nMarkers = (unsigned int)objPoints.size();
@@ -368,25 +429,42 @@ void CharucoBoardImpl::calcNearestMarkerCorners() {
}
}
void CharucoBoardImpl::matchImagePoints(InputArrayOfArrays detectedCorners, InputArray detectedIds,
OutputArray _objPoints, OutputArray imgPoints) const {
if (detectedCorners.kind() == _InputArray::STD_VECTOR_VECTOR ||
detectedCorners.isMatVector() || detectedCorners.isUMatVector())
Board::Impl::matchImagePoints(detectedCorners, detectedIds, _objPoints, imgPoints);
else {
CV_Assert(detectedCorners.isMat() || detectedCorners.isVector());
size_t nDetected = detectedCorners.total();
vector<Point3f> objPnts(nDetected);
vector<Point2f> imgPnts(nDetected);
for(size_t i = 0ull; i < nDetected; i++) {
int pointId = detectedIds.getMat().at<int>((int)i);
CV_Assert(pointId >= 0 && pointId < (int)chessboardCorners.size());
objPnts[i] = chessboardCorners[pointId];
imgPnts[i] = detectedCorners.getMat().at<Point2f>((int)i);
}
Mat(objPnts).copyTo(_objPoints);
Mat(imgPnts).copyTo(imgPoints);
void CharucoBoardImpl::matchImagePoints(InputArrayOfArrays detectedCharuco, InputArray detectedIds,
OutputArray outObjPoints, OutputArray outImgPoints) const {
CV_CheckEQ(detectedIds.total(), detectedCharuco.total(), "Number of corners and ids must be equal");
CV_Assert(detectedIds.total() > 0ull);
CV_Assert(detectedCharuco.depth() == CV_32F);
// detectedCharuco includes charuco corners as vector<Point2f> or Mat.
// Python bindings could add extra dimension to detectedCharuco and therefore vector<Mat> case is additionally processed.
CV_Assert((detectedCharuco.isMat() || detectedCharuco.isVector() || detectedCharuco.isMatVector() || detectedCharuco.isUMatVector())
&& detectedCharuco.depth() == CV_32F);
size_t nDetected = detectedCharuco.total();
vector<Point3f> objPnts(nDetected);
vector<Point2f> imgPnts(nDetected);
Mat detectedCharucoMat, detectedIdsMat = detectedIds.getMat();
if (!detectedCharuco.isMatVector()) {
detectedCharucoMat = detectedCharuco.getMat();
CV_Assert(detectedCharucoMat.checkVector(2));
}
std::vector<Mat> detectedCharucoVecMat;
if (detectedCharuco.isMatVector()) {
detectedCharuco.getMatVector(detectedCharucoVecMat);
}
for(size_t i = 0ull; i < nDetected; i++) {
int pointId = detectedIdsMat.at<int>((int)i);
CV_Assert(pointId >= 0 && pointId < (int)chessboardCorners.size());
objPnts[i] = chessboardCorners[pointId];
if (detectedCharuco.isMatVector()) {
CV_Assert((int)detectedCharucoVecMat[i].total() * detectedCharucoVecMat[i].channels() == 2);
imgPnts[i] = detectedCharucoVecMat[i].ptr<Point2f>(0)[0];
}
else
imgPnts[i] = detectedCharucoMat.ptr<Point2f>(0)[i];
}
Mat(objPnts).copyTo(outObjPoints);
Mat(imgPnts).copyTo(outImgPoints);
}
void CharucoBoardImpl::generateImage(Size outSize, OutputArray img, int marginSize, int borderBits) const {
@@ -437,7 +515,11 @@ void CharucoBoardImpl::generateImage(Size outSize, OutputArray img, int marginSi
for(int y = 0; y < size.height; y++) {
for(int x = 0; x < size.width; x++) {
if(y % 2 != x % 2) continue; // white corner, dont do anything
if(legacyPattern && (size.height % 2 == 0)) { // legacy behavior only for even row count patterns
if((y + 1) % 2 != x % 2) continue; // white corner, dont do anything
} else {
if(y % 2 != x % 2) continue; // white corner, dont do anything
}
double startX, startY;
startX = squareSizePixels * double(x);
@@ -459,47 +541,9 @@ CharucoBoard::CharucoBoard(const Size& size, float squareLength, float markerLen
CV_Assert(size.width > 1 && size.height > 1 && markerLength > 0 && squareLength > markerLength);
vector<vector<Point3f> > objPoints;
float diffSquareMarkerLength = (squareLength - markerLength) / 2;
int totalMarkers = (int)(ids.total());
ids.copyTo(impl->ids);
// calculate Board objPoints
int nextId = 0;
for(int y = 0; y < size.height; y++) {
for(int x = 0; x < size.width; 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
if (totalMarkers == 0)
impl->ids.push_back(nextId);
nextId++;
}
}
if (totalMarkers > 0 && nextId != totalMarkers)
CV_Error(cv::Error::StsBadSize, "Size of ids must be equal to the number of markers: "+std::to_string(nextId));
impl->objPoints = objPoints;
// now fill chessboardCorners
std::vector<Point3f> & c = static_pointer_cast<CharucoBoardImpl>(impl)->chessboardCorners;
for(int y = 0; y < size.height - 1; y++) {
for(int x = 0; x < size.width - 1; x++) {
Point3f corner;
corner.x = (x + 1) * squareLength;
corner.y = (y + 1) * squareLength;
corner.z = 0;
c.push_back(corner);
}
}
impl->rightBottomBorder = Point3f(size.width * squareLength, size.height * squareLength, 0.f);
static_pointer_cast<CharucoBoardImpl>(impl)->calcNearestMarkerCorners();
static_pointer_cast<CharucoBoardImpl>(impl)->createCharucoBoard();
}
Size CharucoBoard::getChessboardSize() const {
@@ -517,22 +561,37 @@ float CharucoBoard::getMarkerLength() const {
return static_pointer_cast<CharucoBoardImpl>(impl)->markerLength;
}
void CharucoBoard::setLegacyPattern(bool legacyPattern) {
CV_Assert(impl);
if (static_pointer_cast<CharucoBoardImpl>(impl)->legacyPattern != legacyPattern)
{
static_pointer_cast<CharucoBoardImpl>(impl)->legacyPattern = legacyPattern;
static_pointer_cast<CharucoBoardImpl>(impl)->createCharucoBoard();
}
}
bool CharucoBoard::getLegacyPattern() const {
CV_Assert(impl);
return static_pointer_cast<CharucoBoardImpl>(impl)->legacyPattern;
}
bool CharucoBoard::checkCharucoCornersCollinear(InputArray charucoIds) const {
CV_Assert(impl);
Mat charucoIdsMat = charucoIds.getMat();
unsigned int nCharucoCorners = (unsigned int)charucoIds.getMat().total();
unsigned int nCharucoCorners = (unsigned int)charucoIdsMat.total();
if (nCharucoCorners <= 2)
return true;
// only test if there are 3 or more corners
auto board = static_pointer_cast<CharucoBoardImpl>(impl);
CV_Assert(board->chessboardCorners.size() >= charucoIds.getMat().total());
CV_Assert(board->chessboardCorners.size() >= charucoIdsMat.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> point0(board->chessboardCorners[charucoIdsMat.at<int>(0)].x,
board->chessboardCorners[charucoIdsMat.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);
Vec<double, 3> point1(board->chessboardCorners[charucoIdsMat.at<int>(1)].x,
board->chessboardCorners[charucoIdsMat.at<int>(1)].y, 1);
// create a line from the first two points.
Vec<double, 3> testLine = point0.cross(point1);
@@ -546,8 +605,8 @@ bool CharucoBoard::checkCharucoCornersCollinear(InputArray charucoIds) const {
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;
testPoint(0) = board->chessboardCorners[charucoIdsMat.at<int>(i)].x;
testPoint(1) = board->chessboardCorners[charucoIdsMat.at<int>(i)].y;
// if testPoint is on testLine, dotProduct will be zero (or very, very close)
dotProduct = testPoint.dot(testLine);
@@ -965,7 +965,7 @@ void ArucoDetector::detectMarkers(InputArray _image, OutputArrayOfArrays _corner
/// STEP 3, Optional : Corner refinement :: use contour container
if (detectorParams.cornerRefinementMethod == CORNER_REFINE_CONTOUR){
if (!_ids.empty()) {
if (!ids.empty()) {
// do corner refinement using the contours for each detected markers
parallel_for_(Range(0, (int)candidates.size()), [&](const Range& range) {
@@ -129,25 +129,21 @@ struct CharucoDetector::CharucoDetectorImpl {
// approximated pose estimation using marker corners
Mat approximatedRvec, approximatedTvec;
Mat objPoints, imgPoints; // object and image points for the solvePnP function
printf("before board.matchImagePoints(markerCorners, markerIds, objPoints, imgPoints);\n");
board.matchImagePoints(markerCorners, markerIds, objPoints, imgPoints);
printf("after board.matchImagePoints(markerCorners, markerIds, objPoints, imgPoints);\n");
Board simpleBoard(board.getObjPoints(), board.getDictionary(), board.getIds());
simpleBoard.matchImagePoints(markerCorners, markerIds, objPoints, imgPoints);
if (objPoints.total() < 4ull) // need, at least, 4 corners
return;
solvePnP(objPoints, imgPoints, charucoParameters.cameraMatrix, charucoParameters.distCoeffs, approximatedRvec, approximatedTvec);
printf("after solvePnP\n");
// project chessboard corners
vector<Point2f> allChessboardImgPoints;
projectPoints(board.getChessboardCorners(), approximatedRvec, approximatedTvec, charucoParameters.cameraMatrix,
charucoParameters.distCoeffs, allChessboardImgPoints);
printf("after projectPoints\n");
// calculate maximum window sizes for subpixel refinement. The size is limited by the distance
// to the closes marker corner to avoid erroneous displacements to marker corners
vector<Size> subPixWinSizes = getMaximumSubPixWindowSizes(markerCorners, markerIds, allChessboardImgPoints);
// filter corners outside the image and subpixel-refine charuco corners
printf("before selectAndRefineChessboardCorners\n");
selectAndRefineChessboardCorners(allChessboardImgPoints, image, charucoCorners, charucoIds, subPixWinSizes);
}
@@ -292,7 +288,7 @@ void CharucoDetector::setRefineParameters(const RefineParameters& refineParamete
void CharucoDetector::detectBoard(InputArray image, OutputArray charucoCorners, OutputArray charucoIds,
InputOutputArrayOfArrays markerCorners, InputOutputArray markerIds) const {
CV_Assert((markerCorners.empty() && markerIds.empty() && !image.empty()) || (markerCorners.size() == markerIds.size()));
CV_Assert((markerCorners.empty() && markerIds.empty() && !image.empty()) || (markerCorners.total() == markerIds.total()));
vector<vector<Point2f>> tmpMarkerCorners;
vector<int> tmpMarkerIds;
InputOutputArrayOfArrays _markerCorners = markerCorners.needed() ? markerCorners : tmpMarkerCorners;
@@ -321,7 +317,7 @@ void CharucoDetector::detectBoard(InputArray image, OutputArray charucoCorners,
void CharucoDetector::detectDiamonds(InputArray image, OutputArrayOfArrays _diamondCorners, OutputArray _diamondIds,
InputOutputArrayOfArrays inMarkerCorners, InputOutputArrayOfArrays inMarkerIds) const {
CV_Assert(getBoard().getChessboardSize() == Size(3, 3));
CV_Assert((inMarkerCorners.empty() && inMarkerIds.empty() && !image.empty()) || (inMarkerCorners.size() == inMarkerIds.size()));
CV_Assert((inMarkerCorners.empty() && inMarkerIds.empty() && !image.empty()) || (inMarkerCorners.total() == inMarkerIds.total()));
vector<vector<Point2f>> tmpMarkerCorners;
vector<int> tmpMarkerIds;
+86 -124
View File
@@ -6,6 +6,7 @@
#include "opencv2/imgproc.hpp"
#include "opencv2/core.hpp"
#ifdef HAVE_OPENCV_DNN
#include "opencv2/dnn.hpp"
#endif
@@ -27,6 +28,8 @@ public:
int top_k,
int backend_id,
int target_id)
:divisor(32),
strides({8, 16, 32})
{
net = dnn::readNet(model, config);
CV_Assert(!net.empty());
@@ -37,18 +40,20 @@ public:
inputW = input_size.width;
inputH = input_size.height;
padW = (int((inputW - 1) / divisor) + 1) * divisor;
padH = (int((inputH - 1) / divisor) + 1) * divisor;
scoreThreshold = score_threshold;
nmsThreshold = nms_threshold;
topK = top_k;
generatePriors();
}
void setInputSize(const Size& input_size) override
{
inputW = input_size.width;
inputH = input_size.height;
generatePriors();
padW = ((inputW - 1) / divisor + 1) * divisor;
padH = ((inputH - 1) / divisor + 1) * divisor;
}
Size getInputSize() override
@@ -97,12 +102,14 @@ public:
return 0;
}
CV_CheckEQ(input_image.size(), Size(inputW, inputH), "Size does not match. Call setInputSize(size) if input size does not match the preset size");
// Pad input_image with divisor 32
Mat pad_image = padWithDivisor(input_image);
// Build blob from input image
Mat input_blob = dnn::blobFromImage(input_image);
Mat input_blob = dnn::blobFromImage(pad_image);
// Forward
std::vector<String> output_names = { "loc", "conf", "iou" };
std::vector<String> output_names = { "cls_8", "cls_16", "cls_32", "obj_8", "obj_16", "obj_32", "bbox_8", "bbox_16", "bbox_32", "kps_8", "kps_16", "kps_32" };
std::vector<Mat> output_blobs;
net.setInput(input_blob);
net.forward(output_blobs, output_names);
@@ -113,126 +120,70 @@ public:
return 1;
}
private:
void generatePriors()
{
// Calculate shapes of different scales according to the shape of input image
Size feature_map_2nd = {
int(int((inputW+1)/2)/2), int(int((inputH+1)/2)/2)
};
Size feature_map_3rd = {
int(feature_map_2nd.width/2), int(feature_map_2nd.height/2)
};
Size feature_map_4th = {
int(feature_map_3rd.width/2), int(feature_map_3rd.height/2)
};
Size feature_map_5th = {
int(feature_map_4th.width/2), int(feature_map_4th.height/2)
};
Size feature_map_6th = {
int(feature_map_5th.width/2), int(feature_map_5th.height/2)
};
std::vector<Size> feature_map_sizes;
feature_map_sizes.push_back(feature_map_3rd);
feature_map_sizes.push_back(feature_map_4th);
feature_map_sizes.push_back(feature_map_5th);
feature_map_sizes.push_back(feature_map_6th);
// Fixed params for generating priors
const std::vector<std::vector<float>> min_sizes = {
{10.0f, 16.0f, 24.0f},
{32.0f, 48.0f},
{64.0f, 96.0f},
{128.0f, 192.0f, 256.0f}
};
CV_Assert(min_sizes.size() == feature_map_sizes.size()); // just to keep vectors in sync
const std::vector<int> steps = { 8, 16, 32, 64 };
// Generate priors
priors.clear();
for (size_t i = 0; i < feature_map_sizes.size(); ++i)
{
Size feature_map_size = feature_map_sizes[i];
std::vector<float> min_size = min_sizes[i];
for (int _h = 0; _h < feature_map_size.height; ++_h)
{
for (int _w = 0; _w < feature_map_size.width; ++_w)
{
for (size_t j = 0; j < min_size.size(); ++j)
{
float s_kx = min_size[j] / inputW;
float s_ky = min_size[j] / inputH;
float cx = (_w + 0.5f) * steps[i] / inputW;
float cy = (_h + 0.5f) * steps[i] / inputH;
Rect2f prior = { cx, cy, s_kx, s_ky };
priors.push_back(prior);
}
}
}
}
}
Mat postProcess(const std::vector<Mat>& output_blobs)
{
// Extract from output_blobs
Mat loc = output_blobs[0];
Mat conf = output_blobs[1];
Mat iou = output_blobs[2];
// Decode from deltas and priors
const std::vector<float> variance = {0.1f, 0.2f};
float* loc_v = (float*)(loc.data);
float* conf_v = (float*)(conf.data);
float* iou_v = (float*)(iou.data);
Mat faces;
// (tl_x, tl_y, w, h, re_x, re_y, le_x, le_y, nt_x, nt_y, rcm_x, rcm_y, lcm_x, lcm_y, score)
// 'tl': top left point of the bounding box
// 're': right eye, 'le': left eye
// 'nt': nose tip
// 'rcm': right corner of mouth, 'lcm': left corner of mouth
Mat face(1, 15, CV_32FC1);
for (size_t i = 0; i < priors.size(); ++i) {
// Get score
float clsScore = conf_v[i*2+1];
float iouScore = iou_v[i];
// Clamp
if (iouScore < 0.f) {
iouScore = 0.f;
for (size_t i = 0; i < strides.size(); ++i) {
int cols = int(padW / strides[i]);
int rows = int(padH / strides[i]);
// Extract from output_blobs
Mat cls = output_blobs[i];
Mat obj = output_blobs[i + strides.size() * 1];
Mat bbox = output_blobs[i + strides.size() * 2];
Mat kps = output_blobs[i + strides.size() * 3];
// Decode from predictions
float* cls_v = (float*)(cls.data);
float* obj_v = (float*)(obj.data);
float* bbox_v = (float*)(bbox.data);
float* kps_v = (float*)(kps.data);
// (tl_x, tl_y, w, h, re_x, re_y, le_x, le_y, nt_x, nt_y, rcm_x, rcm_y, lcm_x, lcm_y, score)
// 'tl': top left point of the bounding box
// 're': right eye, 'le': left eye
// 'nt': nose tip
// 'rcm': right corner of mouth, 'lcm': left corner of mouth
Mat face(1, 15, CV_32FC1);
for(int r = 0; r < rows; ++r) {
for(int c = 0; c < cols; ++c) {
size_t idx = r * cols + c;
// Get score
float cls_score = cls_v[idx];
float obj_score = obj_v[idx];
// Clamp
cls_score = MIN(cls_score, 1.f);
cls_score = MAX(cls_score, 0.f);
obj_score = MIN(obj_score, 1.f);
obj_score = MAX(obj_score, 0.f);
float score = std::sqrt(cls_score * obj_score);
face.at<float>(0, 14) = score;
// Get bounding box
float cx = ((c + bbox_v[idx * 4 + 0]) * strides[i]);
float cy = ((r + bbox_v[idx * 4 + 1]) * strides[i]);
float w = exp(bbox_v[idx * 4 + 2]) * strides[i];
float h = exp(bbox_v[idx * 4 + 3]) * strides[i];
float x1 = cx - w / 2.f;
float y1 = cy - h / 2.f;
face.at<float>(0, 0) = x1;
face.at<float>(0, 1) = y1;
face.at<float>(0, 2) = w;
face.at<float>(0, 3) = h;
// Get landmarks
for(int n = 0; n < 5; ++n) {
face.at<float>(0, 4 + 2 * n) = (kps_v[idx * 10 + 2 * n] + c) * strides[i];
face.at<float>(0, 4 + 2 * n + 1) = (kps_v[idx * 10 + 2 * n + 1]+ r) * strides[i];
}
faces.push_back(face);
}
}
else if (iouScore > 1.f) {
iouScore = 1.f;
}
float score = std::sqrt(clsScore * iouScore);
face.at<float>(0, 14) = score;
// Get bounding box
float cx = (priors[i].x + loc_v[i*14+0] * variance[0] * priors[i].width) * inputW;
float cy = (priors[i].y + loc_v[i*14+1] * variance[0] * priors[i].height) * inputH;
float w = priors[i].width * exp(loc_v[i*14+2] * variance[0]) * inputW;
float h = priors[i].height * exp(loc_v[i*14+3] * variance[1]) * inputH;
float x1 = cx - w / 2;
float y1 = cy - h / 2;
face.at<float>(0, 0) = x1;
face.at<float>(0, 1) = y1;
face.at<float>(0, 2) = w;
face.at<float>(0, 3) = h;
// Get landmarks
face.at<float>(0, 4) = (priors[i].x + loc_v[i*14+ 4] * variance[0] * priors[i].width) * inputW; // right eye, x
face.at<float>(0, 5) = (priors[i].y + loc_v[i*14+ 5] * variance[0] * priors[i].height) * inputH; // right eye, y
face.at<float>(0, 6) = (priors[i].x + loc_v[i*14+ 6] * variance[0] * priors[i].width) * inputW; // left eye, x
face.at<float>(0, 7) = (priors[i].y + loc_v[i*14+ 7] * variance[0] * priors[i].height) * inputH; // left eye, y
face.at<float>(0, 8) = (priors[i].x + loc_v[i*14+ 8] * variance[0] * priors[i].width) * inputW; // nose tip, x
face.at<float>(0, 9) = (priors[i].y + loc_v[i*14+ 9] * variance[0] * priors[i].height) * inputH; // nose tip, y
face.at<float>(0, 10) = (priors[i].x + loc_v[i*14+10] * variance[0] * priors[i].width) * inputW; // right corner of mouth, x
face.at<float>(0, 11) = (priors[i].y + loc_v[i*14+11] * variance[0] * priors[i].height) * inputH; // right corner of mouth, y
face.at<float>(0, 12) = (priors[i].x + loc_v[i*14+12] * variance[0] * priors[i].width) * inputW; // left corner of mouth, x
face.at<float>(0, 13) = (priors[i].y + loc_v[i*14+13] * variance[0] * priors[i].height) * inputH; // left corner of mouth, y
faces.push_back(face);
}
if (faces.rows > 1)
@@ -265,16 +216,27 @@ private:
return faces;
}
}
Mat padWithDivisor(InputArray& input_image)
{
int bottom = padH - inputH;
int right = padW - inputW;
Mat pad_image;
copyMakeBorder(input_image, pad_image, 0, bottom, 0, right, BORDER_CONSTANT, 0);
return pad_image;
}
private:
dnn::Net net;
int inputW;
int inputH;
int padW;
int padH;
const int divisor;
int topK;
float scoreThreshold;
float nmsThreshold;
int topK;
std::vector<Rect2f> priors;
const std::vector<int> strides;
};
#endif
+11 -4
View File
@@ -571,10 +571,11 @@ bool QRDetect::computeTransformationPoints()
{
Mat mask = Mat::zeros(bin_barcode.rows + 2, bin_barcode.cols + 2, CV_8UC1);
uint8_t next_pixel, future_pixel = 255;
int count_test_lines = 0, index = cvRound(localization_points[i].x);
for (; index < bin_barcode.cols - 1; index++)
int count_test_lines = 0, index_c = max(0, min(cvRound(localization_points[i].x), bin_barcode.cols - 1));
const int index_r = max(0, min(cvRound(localization_points[i].y), bin_barcode.rows - 1));
for (; index_c < bin_barcode.cols - 1; index_c++)
{
next_pixel = bin_barcode.ptr<uint8_t>(cvRound(localization_points[i].y))[index + 1];
next_pixel = bin_barcode.ptr<uint8_t>(index_r)[index_c + 1];
if (next_pixel == future_pixel)
{
future_pixel = static_cast<uint8_t>(~future_pixel);
@@ -582,7 +583,7 @@ bool QRDetect::computeTransformationPoints()
if (count_test_lines == 2)
{
floodFill(bin_barcode, mask,
Point(index + 1, cvRound(localization_points[i].y)), 255,
Point(index_c + 1, index_r), 255,
0, Scalar(), Scalar(), FLOODFILL_MASK_ONLY);
break;
}
@@ -2732,6 +2733,12 @@ bool QRDecode::decodingProcess()
quirc_data qr_code_data;
quirc_decode_error_t errorCode = quirc_decode(&qr_code, &qr_code_data);
if(errorCode == QUIRC_ERROR_DATA_ECC){
quirc_flip(&qr_code);
errorCode = quirc_decode(&qr_code, &qr_code_data);
}
if (errorCode != 0) { return false; }
for (int i = 0; i < qr_code_data.payload_len; i++)
+9 -6
View File
@@ -332,14 +332,16 @@ void QRCodeEncoderImpl::generateQR(const std::string &input)
}
total_num = (uint8_t) struct_num - 1;
}
int segment_len = (int) ceil((int) input.length() / struct_num);
for (int i = 0; i < struct_num; i++)
auto string_itr = input.begin();
for (int i = struct_num; i > 0; --i)
{
sequence_num = (uint8_t) i;
int segment_begin = i * segment_len;
int segemnt_end = min((i + 1) * segment_len, (int) input.length()) - 1;
std::string input_info = input.substr(segment_begin, segemnt_end - segment_begin + 1);
size_t segment_begin = string_itr - input.begin();
size_t segment_end = (input.end() - string_itr) / i;
std::string input_info = input.substr(segment_begin, segment_end);
string_itr += segment_end;
int detected_version = versionAuto(input_info);
CV_Assert(detected_version != -1);
if (version_level == 0)
@@ -349,7 +351,6 @@ void QRCodeEncoderImpl::generateQR(const std::string &input)
payload.clear();
payload.reserve(MAX_PAYLOAD_LEN);
final_qrcodes.clear();
format = vector<uint8_t> (15, 255);
version_reserved = vector<uint8_t> (18, 255);
version_size = (21 + (version_level - 1) * 4);
@@ -1234,6 +1235,7 @@ void QRCodeEncoderImpl::encode(const String& input, OutputArray output)
generateQR(input);
CV_Assert(!final_qrcodes.empty());
output.assign(final_qrcodes[0]);
final_qrcodes.clear();
}
void QRCodeEncoderImpl::encodeStructuredAppend(const String& input, OutputArrayOfArrays output)
@@ -1250,6 +1252,7 @@ void QRCodeEncoderImpl::encodeStructuredAppend(const String& input, OutputArrayO
{
output.getMatRef(i) = final_qrcodes[i];
}
final_qrcodes.clear();
}
Ptr<QRCodeEncoder> QRCodeEncoder::create(const QRCodeEncoder::Params& parameters)
+5 -5
View File
@@ -8,12 +8,12 @@
namespace opencv_test {
vector<Point2f> getAxis(InputArray _cameraMatrix, InputArray _distCoeffs, InputArray _rvec,
InputArray _tvec, float length, const float offset) {
InputArray _tvec, float length, const Point2f offset) {
vector<Point3f> axis;
axis.push_back(Point3f(offset, offset, 0.f));
axis.push_back(Point3f(length+offset, offset, 0.f));
axis.push_back(Point3f(offset, length+offset, 0.f));
axis.push_back(Point3f(offset, offset, length));
axis.push_back(Point3f(offset.x, offset.y, 0.f));
axis.push_back(Point3f(length+offset.x, offset.y, 0.f));
axis.push_back(Point3f(offset.x, length+offset.y, 0.f));
axis.push_back(Point3f(offset.x, offset.y, length));
vector<Point2f> axis_to_img;
projectPoints(axis, _rvec, _tvec, _cameraMatrix, _distCoeffs, axis_to_img);
return axis_to_img;
+1 -1
View File
@@ -10,7 +10,7 @@ namespace opencv_test {
static inline double deg2rad(double deg) { return deg * CV_PI / 180.; }
vector<Point2f> getAxis(InputArray _cameraMatrix, InputArray _distCoeffs, InputArray _rvec, InputArray _tvec,
float length, const float offset = 0.f);
float length, const Point2f offset = Point2f(0, 0));
vector<Point2f> getMarkerById(int id, const vector<vector<Point2f> >& corners, const vector<int>& ids);
@@ -247,7 +247,7 @@ void CV_ArucoDetectionPerspective::run(int) {
aruco::ArucoDetector detector(aruco::getPredefinedDictionary(aruco::DICT_6X6_250), params);
// detect from different positions
for(double distance = 0.1; distance < 0.7; distance += 0.2) {
for(double distance : {0.1, 0.3, 0.5, 0.7}) {
for(int pitch = 0; pitch < 360; pitch += (distance == 0.1? 60:180)) {
for(int yaw = 70; yaw <= 120; yaw += 40){
int currentId = iter % 250;
@@ -51,7 +51,7 @@ void CV_ArucoBoardPose::run(int) {
aruco::DetectorParameters detectorParameters = detector.getDetectorParameters();
// for different perspectives
for(double distance = 0.2; distance <= 0.4; distance += 0.15) {
for(double distance : {0.2, 0.35}) {
for(int yaw = -55; yaw <= 50; yaw += 25) {
for(int pitch = -55; pitch <= 50; pitch += 25) {
vector<int> tmpIds;
@@ -162,7 +162,7 @@ void CV_ArucoRefine::run(int) {
aruco::DetectorParameters detectorParameters = detector.getDetectorParameters();
// for different perspectives
for(double distance = 0.2; distance <= 0.4; distance += 0.2) {
for(double distance : {0.2, 0.4}) {
for(int yaw = -60; yaw < 60; yaw += 30) {
for(int pitch = -60; pitch <= 60; pitch += 30) {
aruco::GridBoard gridboard(Size(3, 3), 0.02f, 0.005f, detector.getDictionary());
@@ -12,7 +12,7 @@ namespace opencv_test { namespace {
* @brief Get a synthetic image of Chessboard in perspective
*/
static Mat projectChessboard(int squaresX, int squaresY, float squareSize, Size imageSize,
Mat cameraMatrix, Mat rvec, Mat tvec) {
Mat cameraMatrix, Mat rvec, Mat tvec, bool legacyPattern) {
Mat img(imageSize, CV_8UC1, Scalar::all(255));
Mat distCoeffs(5, 1, CV_64FC1, Scalar::all(0));
@@ -20,7 +20,11 @@ static Mat projectChessboard(int squaresX, int squaresY, float squareSize, Size
for(int y = 0; y < squaresY; y++) {
float startY = float(y) * squareSize;
for(int x = 0; x < squaresX; x++) {
if(y % 2 != x % 2) continue;
if(legacyPattern && (squaresY % 2 == 0)) {
if((y + 1) % 2 != x % 2) continue;
} else {
if(y % 2 != x % 2) continue;
}
float startX = float(x) * squareSize;
vector< Point3f > squareCorners;
@@ -66,7 +70,7 @@ static Mat projectCharucoBoard(aruco::CharucoBoard& board, Mat cameraMatrix, dou
// project chessboard
Mat chessboard =
projectChessboard(board.getChessboardSize().width, board.getChessboardSize().height,
board.getSquareLength(), imageSize, cameraMatrix, rvec, tvec);
board.getSquareLength(), imageSize, cameraMatrix, rvec, tvec, board.getLegacyPattern());
for(unsigned int i = 0; i < chessboard.total(); i++) {
if(chessboard.ptr< unsigned char >()[i] == 0) {
@@ -82,16 +86,15 @@ static Mat projectCharucoBoard(aruco::CharucoBoard& board, Mat cameraMatrix, dou
*/
class CV_CharucoDetection : public cvtest::BaseTest {
public:
CV_CharucoDetection();
CV_CharucoDetection(bool _legacyPattern) : legacyPattern(_legacyPattern) {}
protected:
void run(int);
bool legacyPattern;
};
CV_CharucoDetection::CV_CharucoDetection() {}
void CV_CharucoDetection::run(int) {
int iter = 0;
@@ -100,6 +103,7 @@ void CV_CharucoDetection::run(int) {
aruco::DetectorParameters params;
params.minDistanceToBorder = 3;
aruco::CharucoBoard board(Size(4, 4), 0.03f, 0.015f, aruco::getPredefinedDictionary(aruco::DICT_6X6_250));
board.setLegacyPattern(legacyPattern);
aruco::CharucoDetector detector(board, aruco::CharucoParameters(), params);
cameraMatrix.at<double>(0, 0) = cameraMatrix.at<double>(1, 1) = 600;
@@ -109,7 +113,7 @@ void CV_CharucoDetection::run(int) {
Mat distCoeffs(5, 1, CV_64FC1, Scalar::all(0));
// for different perspectives
for(double distance = 0.2; distance <= 0.4; distance += 0.2) {
for(double distance : {0.2, 0.4}) {
for(int yaw = -55; yaw <= 50; yaw += 25) {
for(int pitch = -55; pitch <= 50; pitch += 25) {
@@ -140,11 +144,7 @@ void CV_CharucoDetection::run(int) {
detector.detectBoard(img, charucoCorners, charucoIds, corners, ids);
}
if(ids.size() == 0) {
ts->printf(cvtest::TS::LOG, "Marker detection failed");
ts->set_failed_test_info(cvtest::TS::FAIL_MISMATCH);
return;
}
ASSERT_GT(ids.size(), std::vector< int >::size_type(0)) << "Marker detection failed";
// check results
vector< Point2f > projectedCharucoCorners;
@@ -161,20 +161,11 @@ void CV_CharucoDetection::run(int) {
int currentId = charucoIds[i];
if(currentId >= (int)board.getChessboardCorners().size()) {
ts->printf(cvtest::TS::LOG, "Invalid Charuco corner id");
ts->set_failed_test_info(cvtest::TS::FAIL_MISMATCH);
return;
}
ASSERT_LT(currentId, (int)board.getChessboardCorners().size()) << "Invalid Charuco corner id";
double repError = cv::norm(charucoCorners[i] - projectedCharucoCorners[currentId]); // TODO cvtest
if(repError > 5.) {
ts->printf(cvtest::TS::LOG, "Charuco corner reprojection error too high");
ts->set_failed_test_info(cvtest::TS::FAIL_MISMATCH);
return;
}
ASSERT_LE(repError, 5.) << "Charuco corner reprojection error too high";
}
}
}
@@ -188,32 +179,33 @@ void CV_CharucoDetection::run(int) {
*/
class CV_CharucoPoseEstimation : public cvtest::BaseTest {
public:
CV_CharucoPoseEstimation();
CV_CharucoPoseEstimation(bool _legacyPattern) : legacyPattern(_legacyPattern) {}
protected:
void run(int);
bool legacyPattern;
};
CV_CharucoPoseEstimation::CV_CharucoPoseEstimation() {}
void CV_CharucoPoseEstimation::run(int) {
int iter = 0;
Mat cameraMatrix = Mat::eye(3, 3, CV_64FC1);
Size imgSize(500, 500);
Size imgSize(750, 750);
aruco::DetectorParameters params;
params.minDistanceToBorder = 3;
aruco::CharucoBoard board(Size(4, 4), 0.03f, 0.015f, aruco::getPredefinedDictionary(aruco::DICT_6X6_250));
board.setLegacyPattern(legacyPattern);
aruco::CharucoDetector detector(board, aruco::CharucoParameters(), params);
cameraMatrix.at<double>(0, 0) = cameraMatrix.at< double >(1, 1) = 650;
cameraMatrix.at<double>(0, 0) = cameraMatrix.at< double >(1, 1) = 1000;
cameraMatrix.at<double>(0, 2) = imgSize.width / 2;
cameraMatrix.at<double>(1, 2) = imgSize.height / 2;
Mat distCoeffs(5, 1, CV_64FC1, Scalar::all(0));
// for different perspectives
for(double distance = 0.2; distance <= 0.3; distance += 0.1) {
for(double distance : {0.2, 0.25}) {
for(int yaw = -55; yaw <= 50; yaw += 25) {
for(int pitch = -55; pitch <= 50; pitch += 25) {
@@ -252,12 +244,21 @@ void CV_CharucoPoseEstimation::run(int) {
// check axes
const float offset = (board.getSquareLength() - board.getMarkerLength()) / 2.f;
const float aruco_offset = (board.getSquareLength() - board.getMarkerLength()) / 2.f;
Point2f offset;
vector<Point2f> topLeft, bottomLeft;
if(legacyPattern) { // white box in upper left corner for even row count chessboard patterns
offset = Point2f(aruco_offset + board.getSquareLength(), aruco_offset);
topLeft = getMarkerById(board.getIds()[1], corners, ids);
bottomLeft = getMarkerById(board.getIds()[2], corners, ids);
} else { // always a black box in the upper left corner
offset = Point2f(aruco_offset, aruco_offset);
topLeft = getMarkerById(board.getIds()[0], corners, ids);
bottomLeft = getMarkerById(board.getIds()[2], corners, ids);
}
vector<Point2f> axes = getAxis(cameraMatrix, distCoeffs, rvec, tvec, board.getSquareLength(), offset);
vector<Point2f> topLeft = getMarkerById(board.getIds()[0], corners, ids);
ASSERT_NEAR(topLeft[0].x, axes[1].x, 3.f);
ASSERT_NEAR(topLeft[0].y, axes[1].y, 3.f);
vector<Point2f> bottomLeft = getMarkerById(board.getIds()[2], corners, ids);
ASSERT_NEAR(bottomLeft[0].x, axes[2].x, 3.f);
ASSERT_NEAR(bottomLeft[0].y, axes[2].y, 3.f);
@@ -271,20 +272,11 @@ void CV_CharucoPoseEstimation::run(int) {
int currentId = charucoIds[i];
if(currentId >= (int)board.getChessboardCorners().size()) {
ts->printf(cvtest::TS::LOG, "Invalid Charuco corner id");
ts->set_failed_test_info(cvtest::TS::FAIL_MISMATCH);
return;
}
ASSERT_LT(currentId, (int)board.getChessboardCorners().size()) << "Invalid Charuco corner id";
double repError = cv::norm(charucoCorners[i] - projectedCharucoCorners[currentId]); // TODO cvtest
if(repError > 5.) {
ts->printf(cvtest::TS::LOG, "Charuco corner reprojection error too high");
ts->set_failed_test_info(cvtest::TS::FAIL_MISMATCH);
return;
}
ASSERT_LE(repError, 5.) << "Charuco corner reprojection error too high";
}
}
}
@@ -332,7 +324,7 @@ void CV_CharucoDiamondDetection::run(int) {
detector.setCharucoParameters(charucoParameters);
// for different perspectives
for(double distance = 0.2; distance <= 0.3; distance += 0.1) {
for(double distance : {0.2, 0.22}) {
for(int yaw = -50; yaw <= 50; yaw += 25) {
for(int pitch = -50; pitch <= 50; pitch += 25) {
@@ -490,12 +482,26 @@ void CV_CharucoBoardCreation::run(int)
TEST(CV_CharucoDetection, accuracy) {
CV_CharucoDetection test;
const bool legacyPattern = false;
CV_CharucoDetection test(legacyPattern);
test.safe_run();
}
TEST(CV_CharucoDetection, accuracy_legacyPattern) {
const bool legacyPattern = true;
CV_CharucoDetection test(legacyPattern);
test.safe_run();
}
TEST(CV_CharucoPoseEstimation, accuracy) {
CV_CharucoPoseEstimation test;
const bool legacyPattern = false;
CV_CharucoPoseEstimation test(legacyPattern);
test.safe_run();
}
TEST(CV_CharucoPoseEstimation, accuracy_legacyPattern) {
const bool legacyPattern = true;
CV_CharucoPoseEstimation test(legacyPattern);
test.safe_run();
}
@@ -656,4 +662,31 @@ TEST(Charuco, issue_14014)
EXPECT_EQ(Size(4, 1), rejectedPoints[0].size()); // check dimension of rejected corners after successfully refine
}
TEST(Charuco, testmatchImagePoints)
{
aruco::CharucoBoard board(Size(2, 3), 1.f, 0.5f, aruco::getPredefinedDictionary(aruco::DICT_4X4_50));
auto chessboardPoints = board.getChessboardCorners();
vector<int> detectedIds;
vector<Point2f> detectedCharucoCorners;
for (const Point3f& point : chessboardPoints) {
detectedIds.push_back((int)detectedCharucoCorners.size());
detectedCharucoCorners.push_back({2.f*point.x, 2.f*point.y});
}
vector<Point3f> objPoints;
vector<Point2f> imagePoints;
board.matchImagePoints(detectedCharucoCorners, detectedIds, objPoints, imagePoints);
ASSERT_EQ(detectedCharucoCorners.size(), objPoints.size());
ASSERT_EQ(detectedCharucoCorners.size(), imagePoints.size());
for (size_t i = 0ull; i < detectedCharucoCorners.size(); i++) {
EXPECT_EQ(detectedCharucoCorners[i], imagePoints[i]);
EXPECT_EQ(chessboardPoints[i].x, objPoints[i].x);
EXPECT_EQ(chessboardPoints[i].y, objPoints[i].y);
}
}
}} // namespace
+5 -8
View File
@@ -65,20 +65,16 @@ TEST(Objdetect_face_detection, regression)
{
// Pre-set params
float scoreThreshold = 0.7f;
float matchThreshold = 0.9f;
float l2disThreshold = 5.0f;
float matchThreshold = 0.7f;
float l2disThreshold = 15.0f;
int numLM = 5;
int numCoords = 4 + 2 * numLM;
// Load ground truth labels
std::map<std::string, Mat> gt = blobFromTXT(findDataFile("dnn_face/detection/cascades_labels.txt"), numCoords);
// for (auto item: gt)
// {
// std::cout << item.first << " " << item.second.size() << std::endl;
// }
// Initialize detector
std::string model = findDataFile("dnn/onnx/models/yunet-202202.onnx", false);
std::string model = findDataFile("dnn/onnx/models/yunet-202303.onnx", false);
Ptr<FaceDetectorYN> faceDetector = FaceDetectorYN::create(model, "", Size(300, 300));
faceDetector->setScoreThreshold(0.7f);
@@ -137,6 +133,7 @@ TEST(Objdetect_face_detection, regression)
lmMatched[lmIdx] = true;
}
}
break;
}
EXPECT_TRUE(boxMatched) << "In image " << item.first << ", cannot match resBox " << resBox << " with any ground truth.";
if (boxMatched)
@@ -178,7 +175,7 @@ TEST(Objdetect_face_recognition, regression)
}
// Initialize detector
std::string detect_model = findDataFile("dnn/onnx/models/yunet-202202.onnx", false);
std::string detect_model = findDataFile("dnn/onnx/models/yunet-202303.onnx", false);
Ptr<FaceDetectorYN> faceDetector = FaceDetectorYN::create(detect_model, "", Size(150, 150), score_thresh, nms_thresh);
std::string recog_model = findDataFile("dnn/onnx/models/face_recognizer_fast.onnx", false);
+32
View File
@@ -708,6 +708,38 @@ TEST(Objdetect_QRCode_detect, detect_regression_21287)
#endif
}
TEST(Objdetect_QRCode_detect_flipped, regression_23249)
{
const std::vector<std::pair<std::string, std::string>> flipped_images =
// image name , expected result
{{"flipped_1.png", "The key is /qrcod_OMevpf"},
{"flipped_2.png", "A26"}};
const std::string root = "qrcode/flipped/";
for(const auto &flipped_image : flipped_images){
const std::string &image_name = flipped_image.first;
const std::string &expect_msg = flipped_image.second;
std::string image_path = findDataFile(root + image_name);
Mat src = imread(image_path);
ASSERT_FALSE(src.empty()) << "Can't read image: " << image_path;
QRCodeDetector qrcode;
std::vector<Point> corners;
Mat straight_barcode;
cv::String decoded_info;
EXPECT_TRUE(qrcode.detect(src, corners));
EXPECT_TRUE(!corners.empty());
std::string decoded_msg;
#ifdef HAVE_QUIRC
EXPECT_NO_THROW(decoded_msg = qrcode.decode(src, corners, straight_barcode));
ASSERT_FALSE(straight_barcode.empty()) << "Can't decode qrimage.";
EXPECT_EQ(expect_msg, decoded_msg);
#endif
}
}
// @author Kumataro, https://github.com/Kumataro
TEST(Objdetect_QRCode_decode, decode_regression_21929)
{
@@ -450,6 +450,32 @@ TEST(Objdetect_QRCode_Encode_Decode_Structured_Append, DISABLED_regression)
#endif // UPDATE_QRCODE_TEST_DATA
CV_ENUM(EncodeModes, QRCodeEncoder::EncodeMode::MODE_NUMERIC,
QRCodeEncoder::EncodeMode::MODE_ALPHANUMERIC,
QRCodeEncoder::EncodeMode::MODE_BYTE)
typedef ::testing::TestWithParam<EncodeModes> Objdetect_QRCode_Encode_Decode_Structured_Append_Parameterized;
TEST_P(Objdetect_QRCode_Encode_Decode_Structured_Append_Parameterized, regression_22205)
{
const std::string input_data = "the quick brown fox jumps over the lazy dog";
std::vector<cv::Mat> result_qrcodes;
cv::QRCodeEncoder::Params params;
int encode_mode = GetParam();
params.mode = static_cast<cv::QRCodeEncoder::EncodeMode>(encode_mode);
for(size_t struct_num = 2; struct_num < 5; ++struct_num)
{
params.structure_number = static_cast<int>(struct_num);
cv::Ptr<cv::QRCodeEncoder> encoder = cv::QRCodeEncoder::create(params);
encoder->encodeStructuredAppend(input_data, result_qrcodes);
EXPECT_EQ(result_qrcodes.size(), struct_num) << "The number of QR Codes requested is not equal"<<
"to the one returned";
}
}
INSTANTIATE_TEST_CASE_P(/**/, Objdetect_QRCode_Encode_Decode_Structured_Append_Parameterized, EncodeModes::all());
TEST(Objdetect_QRCode_Encode_Decode, regression_issue22029)
{
const cv::String msg = "OpenCV";