mirror of
https://github.com/opencv/opencv.git
synced 2026-07-29 15:23:05 +04:00
Merge pull request #24873 from AleksandrPanov:fix_charuco_board
Fix generateImage for charuco chessboard
This commit is contained in:
@@ -483,39 +483,44 @@ void CharucoBoardImpl::generateImage(Size outSize, OutputArray img, int marginSi
|
||||
Mat noMarginsImg =
|
||||
out.colRange(marginSize, out.cols - marginSize).rowRange(marginSize, out.rows - marginSize);
|
||||
|
||||
double totalLengthX, totalLengthY;
|
||||
totalLengthX = squareLength * size.width;
|
||||
totalLengthY = squareLength * size.height;
|
||||
|
||||
// proportional transformation
|
||||
double xReduction = totalLengthX / double(noMarginsImg.cols);
|
||||
double yReduction = totalLengthY / double(noMarginsImg.rows);
|
||||
// the size of the chessboard square depends on the location of the chessboard
|
||||
float pixInSquare = 0.f;
|
||||
// the size of the chessboard in pixels
|
||||
Size pixInChessboard(noMarginsImg.cols, noMarginsImg.rows);
|
||||
|
||||
// determine the zone where the chessboard is placed
|
||||
Mat chessboardZoneImg;
|
||||
if(xReduction > yReduction) {
|
||||
int nRows = int(totalLengthY / xReduction);
|
||||
int rowsMargins = (noMarginsImg.rows - nRows) / 2;
|
||||
chessboardZoneImg = noMarginsImg.rowRange(rowsMargins, noMarginsImg.rows - rowsMargins);
|
||||
} else {
|
||||
int nCols = int(totalLengthX / yReduction);
|
||||
int colsMargins = (noMarginsImg.cols - nCols) / 2;
|
||||
chessboardZoneImg = noMarginsImg.colRange(colsMargins, noMarginsImg.cols - colsMargins);
|
||||
float pixInSquareX = (float)noMarginsImg.cols / (float)size.width;
|
||||
float pixInSquareY = (float)noMarginsImg.rows / (float)size.height;
|
||||
Point startChessboard(0, 0);
|
||||
if (pixInSquareX <= pixInSquareY) {
|
||||
// the width of "noMarginsImg" image determines the dimensions of the chessboard
|
||||
pixInSquare = pixInSquareX;
|
||||
pixInChessboard.height = cvRound(pixInSquare*size.height);
|
||||
int rowsMargin = (noMarginsImg.rows - pixInChessboard.height) / 2;
|
||||
startChessboard.y = rowsMargin;
|
||||
}
|
||||
else {
|
||||
// the height of "noMarginsImg" image determines the dimensions of the chessboard
|
||||
pixInSquare = pixInSquareY;
|
||||
pixInChessboard.width = cvRound(pixInSquare*size.width);
|
||||
int colsMargin = (noMarginsImg.cols - pixInChessboard.width) / 2;
|
||||
startChessboard.x = colsMargin;
|
||||
}
|
||||
// determine the zone where the chessboard is located
|
||||
Mat chessboardZoneImg = noMarginsImg(Rect(startChessboard, pixInChessboard));
|
||||
|
||||
// determine the margins to draw only the markers
|
||||
// take the minimum just to be sure
|
||||
double squareSizePixels = min(double(chessboardZoneImg.cols) / double(size.width),
|
||||
double(chessboardZoneImg.rows) / double(size.height));
|
||||
// marker size in pixels
|
||||
const float pixInMarker = markerLength/squareLength*pixInSquare;
|
||||
// the size of the marker margin in pixels
|
||||
const float pixInMarginMarker = 0.5f*(pixInSquare - pixInMarker);
|
||||
|
||||
double diffSquareMarkerLength = (squareLength - markerLength) / 2;
|
||||
int diffSquareMarkerLengthPixels =
|
||||
int(diffSquareMarkerLength * squareSizePixels / squareLength);
|
||||
// determine the zone where the aruco markers are located
|
||||
int endArucoX = cvRound(pixInSquare*(size.width-1)+pixInMarginMarker+pixInMarker);
|
||||
int endArucoY = cvRound(pixInSquare*(size.height-1)+pixInMarginMarker+pixInMarker);
|
||||
Mat arucoZone = chessboardZoneImg(Range(cvRound(pixInMarginMarker), endArucoY), Range(cvRound(pixInMarginMarker), endArucoX));
|
||||
|
||||
// draw markers
|
||||
Mat markersImg;
|
||||
Board::Impl::generateImage(chessboardZoneImg.size(), markersImg, diffSquareMarkerLengthPixels, borderBits);
|
||||
markersImg.copyTo(chessboardZoneImg);
|
||||
Board::Impl::generateImage(arucoZone.size(), arucoZone, 0, borderBits);
|
||||
|
||||
// now draw black squares
|
||||
for(int y = 0; y < size.height; y++) {
|
||||
@@ -527,12 +532,11 @@ void CharucoBoardImpl::generateImage(Size outSize, OutputArray img, int marginSi
|
||||
if(y % 2 != x % 2) continue; // white corner, dont do anything
|
||||
}
|
||||
|
||||
double startX, startY;
|
||||
startX = squareSizePixels * double(x);
|
||||
startY = squareSizePixels * double(y);
|
||||
float startX = pixInSquare * float(x);
|
||||
float startY = pixInSquare * float(y);
|
||||
|
||||
Mat squareZone = chessboardZoneImg.rowRange(int(startY), int(startY + squareSizePixels))
|
||||
.colRange(int(startX), int(startX + squareSizePixels));
|
||||
Mat squareZone = chessboardZoneImg(Range(cvRound(startY), cvRound(startY + pixInSquare)),
|
||||
Range(cvRound(startX), cvRound(startX + pixInSquare)));
|
||||
|
||||
squareZone.setTo(0);
|
||||
}
|
||||
|
||||
@@ -769,6 +769,57 @@ TEST_P(CharucoBoard, testWrongSizeDetection)
|
||||
ASSERT_TRUE(detectedCharucoIds.empty());
|
||||
}
|
||||
|
||||
TEST(CharucoBoardGenerate, issue_24806)
|
||||
{
|
||||
aruco::Dictionary dict = aruco::getPredefinedDictionary(aruco::DICT_4X4_1000);
|
||||
const float squareLength = 13.f, markerLength = 10.f;
|
||||
const Size boardSize(7ull, 4ull);
|
||||
const aruco::CharucoBoard board(boardSize, squareLength, markerLength, dict);
|
||||
const int marginSize = 24;
|
||||
Mat boardImg;
|
||||
|
||||
// generate chessboard image
|
||||
board.generateImage(Size(400, 300), boardImg, marginSize);
|
||||
// This condition checks that the width of the image determines the dimensions of the chessboard in this test
|
||||
CV_Assert((float)(boardImg.cols) / (float)boardSize.width <=
|
||||
(float)(boardImg.rows) / (float)boardSize.height);
|
||||
|
||||
// prepare data for chessboard image test
|
||||
Mat noMarginsImg = boardImg(Range(marginSize, boardImg.rows - marginSize),
|
||||
Range(marginSize, boardImg.cols - marginSize));
|
||||
const float pixInSquare = (float)(noMarginsImg.cols) / (float)boardSize.width;
|
||||
|
||||
Size pixInChessboard(cvRound(pixInSquare*boardSize.width), cvRound(pixInSquare*boardSize.height));
|
||||
const Point startChessboard((noMarginsImg.cols - pixInChessboard.width) / 2,
|
||||
(noMarginsImg.rows - pixInChessboard.height) / 2);
|
||||
Mat chessboardZoneImg = noMarginsImg(Rect(startChessboard, pixInChessboard));
|
||||
|
||||
// B - black pixel, W - white pixel
|
||||
// chessboard corner 1:
|
||||
// B W
|
||||
// W B
|
||||
Mat goldCorner1 = (Mat_<uint8_t>(2, 2) <<
|
||||
0, 255,
|
||||
255, 0);
|
||||
// B - black pixel, W - white pixel
|
||||
// chessboard corner 2:
|
||||
// W B
|
||||
// B W
|
||||
Mat goldCorner2 = (Mat_<uint8_t>(2, 2) <<
|
||||
255, 0,
|
||||
0, 255);
|
||||
|
||||
// test chessboard corners in generated image
|
||||
for (const Point3f& p: board.getChessboardCorners()) {
|
||||
Point2f chessCorner(pixInSquare*(p.x/squareLength),
|
||||
pixInSquare*(p.y/squareLength));
|
||||
Mat winCorner = chessboardZoneImg(Rect(Point(cvRound(chessCorner.x) - 1, cvRound(chessCorner.y) - 1), Size(2, 2)));
|
||||
bool eq = (cv::countNonZero(goldCorner1 != winCorner) == 0) | (cv::countNonZero(goldCorner2 != winCorner) == 0);
|
||||
ASSERT_TRUE(eq);
|
||||
}
|
||||
// TODO: fix aruco generateImage and add test aruco corners for generated image
|
||||
}
|
||||
|
||||
TEST(Charuco, testSeveralBoardsWithCustomIds)
|
||||
{
|
||||
Size res{500, 500};
|
||||
|
||||
Reference in New Issue
Block a user