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-07-27 18:38:25 +03:00
151 changed files with 5199 additions and 1111 deletions
@@ -13,32 +13,39 @@ namespace aruco {
//! @{
/** @brief Dictionary/Set of markers, it contains the inner codification
/** @brief Dictionary is a set of unique ArUco markers of the same size
*
* BytesList contains the marker codewords where:
* `bytesList` storing as 2-dimensions Mat with 4-th channels (CV_8UC4 type was used) and contains the marker codewords where:
* - bytesList.rows is the dictionary size
* - each marker is encoded using `nbytes = ceil(markerSize*markerSize/8.)`
* - each marker is encoded using `nbytes = ceil(markerSize*markerSize/8.)` bytes
* - each row contains all 4 rotations of the marker, so its length is `4*nbytes`
*
* `bytesList.ptr(i)[k*nbytes + j]` is then the j-th byte of i-th marker, in its k-th rotation.
* - the byte order in the bytesList[i] row:
* `//bytes without rotation/bytes with rotation 1/bytes with rotation 2/bytes with rotation 3//`
* So `bytesList.ptr(i)[k*nbytes + j]` is the j-th byte of i-th marker, in its k-th rotation.
* @note Python bindings generate matrix with shape of bytesList `dictionary_size x nbytes x 4`,
* but it should be indexed like C++ version. Python example for j-th byte of i-th marker, in its k-th rotation:
* `aruco_dict.bytesList[id].ravel()[k*nbytes + j]`
*/
class CV_EXPORTS_W_SIMPLE Dictionary {
public:
CV_PROP_RW Mat bytesList; // marker code information
CV_PROP_RW int markerSize; // number of bits per dimension
CV_PROP_RW int maxCorrectionBits; // maximum number of bits that can be corrected
CV_PROP_RW Mat bytesList; ///< marker code information. See class description for more details
CV_PROP_RW int markerSize; ///< number of bits per dimension
CV_PROP_RW int maxCorrectionBits; ///< maximum number of bits that can be corrected
CV_WRAP Dictionary();
/** @brief Basic ArUco dictionary constructor
*
* @param bytesList bits for all ArUco markers in dictionary see memory layout in the class description
* @param _markerSize ArUco marker size in units
* @param maxcorr maximum number of bits that can be corrected
*/
CV_WRAP Dictionary(const Mat &bytesList, int _markerSize, int maxcorr = 0);
/** @brief Read a new dictionary from FileNode.
*
* Dictionary format:\n
* Dictionary example in YAML format:\n
* nmarkers: 35\n
* markersize: 6\n
* maxCorrectionBits: 5\n
@@ -54,13 +61,13 @@ class CV_EXPORTS_W_SIMPLE Dictionary {
/** @brief Given a matrix of bits. Returns whether if marker is identified or not.
*
* It returns by reference the correct id (if any) and the correct rotation
* Returns reference to the marker id in the dictionary (if any) and its rotation.
*/
CV_WRAP bool identify(const Mat &onlyBits, CV_OUT int &idx, CV_OUT int &rotation, double maxCorrectionRate) const;
/** @brief Returns the distance of the input bits to the specific id.
/** @brief Returns Hamming distance of the input bits to the specific id.
*
* If allRotations is true, the four posible bits rotation are considered
* If `allRotations` flag is set, the four posible marker rotations are considered
*/
CV_WRAP int getDistanceToId(InputArray bits, int id, bool allRotations = true) const;
@@ -70,7 +77,7 @@ class CV_EXPORTS_W_SIMPLE Dictionary {
CV_WRAP void generateImageMarker(int id, int sidePixels, OutputArray _img, int borderBits = 1) const;
/** @brief Transform matrix of bits to list of bytes in the 4 rotations
/** @brief Transform matrix of bits to list of bytes with 4 marker rotations
*/
CV_WRAP static Mat getByteListFromBits(const Mat &bits);
@@ -104,7 +104,7 @@ public:
*/
CV_WRAP void detectDiamonds(InputArray image, OutputArrayOfArrays diamondCorners, OutputArray diamondIds,
InputOutputArrayOfArrays markerCorners = noArray(),
InputOutputArrayOfArrays markerIds = noArray()) const;
InputOutputArray markerIds = noArray()) const;
protected:
struct CharucoDetectorImpl;
Ptr<CharucoDetectorImpl> charucoDetectorImpl;
@@ -238,6 +238,27 @@ class aruco_objdetect_test(NewOpenCVTests):
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]))
list_gold_corners = [(cell_size, cell_size), (2*cell_size, cell_size), (2*cell_size, 2*cell_size),
(cell_size, 2*cell_size)]
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)
@@ -23,6 +23,54 @@ struct CharucoDetector::CharucoDetectorImpl {
arucoDetector(_arucoDetector)
{}
bool checkBoard(InputArrayOfArrays markerCorners, InputArray markerIds, InputArray charucoCorners, InputArray charucoIds) {
vector<Mat> mCorners;
markerCorners.getMatVector(mCorners);
Mat mIds = markerIds.getMat();
Mat chCorners = charucoCorners.getMat();
Mat chIds = charucoIds.getMat();
vector<vector<int> > nearestMarkerIdx = board.getNearestMarkerIdx();
vector<Point2f> distance(board.getNearestMarkerIdx().size(), Point2f(0.f, std::numeric_limits<float>::max()));
// distance[i].x: max distance from the i-th charuco corner to charuco corner-forming markers.
// The two charuco corner-forming markers of i-th charuco corner are defined in getNearestMarkerIdx()[i]
// distance[i].y: min distance from the charuco corner to other markers.
for (size_t i = 0ull; i < chIds.total(); i++) {
int chId = chIds.ptr<int>(0)[i];
Point2f charucoCorner(chCorners.ptr<Point2f>(0)[i]);
for (size_t j = 0ull; j < mIds.total(); j++) {
int idMaker = mIds.ptr<int>(0)[j];
Point2f centerMarker((mCorners[j].ptr<Point2f>(0)[0] + mCorners[j].ptr<Point2f>(0)[1] +
mCorners[j].ptr<Point2f>(0)[2] + mCorners[j].ptr<Point2f>(0)[3]) / 4.f);
float dist = sqrt(normL2Sqr<float>(centerMarker - charucoCorner));
// check distance from the charuco corner to charuco corner-forming markers
if (nearestMarkerIdx[chId][0] == idMaker || nearestMarkerIdx[chId][1] == idMaker) {
int nearestCornerId = nearestMarkerIdx[chId][0] == idMaker ? board.getNearestMarkerCorners()[chId][0] : board.getNearestMarkerCorners()[chId][1];
Point2f nearestCorner = mCorners[j].ptr<Point2f>(0)[nearestCornerId];
float distToNearest = sqrt(normL2Sqr<float>(nearestCorner - charucoCorner));
distance[chId].x = max(distance[chId].x, distToNearest);
// check that nearestCorner is nearest point
{
Point2f mid1 = (mCorners[j].ptr<Point2f>(0)[(nearestCornerId + 1) % 4]+nearestCorner)*0.5f;
Point2f mid2 = (mCorners[j].ptr<Point2f>(0)[(nearestCornerId + 3) % 4]+nearestCorner)*0.5f;
float tmpDist = min(sqrt(normL2Sqr<float>(mid1 - charucoCorner)), sqrt(normL2Sqr<float>(mid2 - charucoCorner)));
if (tmpDist < distToNearest)
return false;
}
}
// check distance from the charuco corner to other markers
else
distance[chId].y = min(distance[chId].y, dist);
}
// if distance from the charuco corner to charuco corner-forming markers more then distance from the charuco corner to other markers,
// then a false board is found.
if (distance[chId].x > 0.f && distance[chId].y < std::numeric_limits<float>::max() && distance[chId].x > distance[chId].y)
return false;
}
return true;
}
/** Calculate the maximum window sizes for corner refinement for each charuco corner based on the distance
* to their closest markers */
vector<Size> getMaximumSubPixWindowSizes(InputArrayOfArrays markerCorners, InputArray markerIds,
@@ -246,6 +294,31 @@ struct CharucoDetector::CharucoDetectorImpl {
Mat(filteredCharucoIds).copyTo(_filteredCharucoIds);
return (int)_filteredCharucoIds.total();
}
void detectBoard(InputArray image, OutputArray charucoCorners, OutputArray charucoIds,
InputOutputArrayOfArrays markerCorners, InputOutputArray markerIds) {
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;
InputOutputArray _markerIds = markerIds.needed() ? markerIds : tmpMarkerIds;
if (markerCorners.empty() && markerIds.empty()) {
vector<vector<Point2f> > rejectedMarkers;
arucoDetector.detectMarkers(image, _markerCorners, _markerIds, rejectedMarkers);
if (charucoParameters.tryRefineMarkers)
arucoDetector.refineDetectedMarkers(image, board, _markerCorners, _markerIds, rejectedMarkers);
}
// if camera parameters are avaible, use approximated calibration
if(!charucoParameters.cameraMatrix.empty())
interpolateCornersCharucoApproxCalib(_markerCorners, _markerIds, image, charucoCorners, charucoIds);
// else use local homography
else
interpolateCornersCharucoLocalHom(_markerCorners, _markerIds, image, charucoCorners, charucoIds);
// to return a charuco corner, its closest aruco markers should have been detected
filterCornersWithoutMinMarkers(charucoCorners, charucoIds, _markerIds, charucoCorners, charucoIds);
}
};
CharucoDetector::CharucoDetector(const CharucoBoard &board, const CharucoParameters &charucoParams,
@@ -288,34 +361,15 @@ 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.total() == markerIds.total()));
vector<vector<Point2f>> tmpMarkerCorners;
vector<int> tmpMarkerIds;
InputOutputArrayOfArrays _markerCorners = markerCorners.needed() ? markerCorners : tmpMarkerCorners;
InputOutputArray _markerIds = markerIds.needed() ? markerIds : tmpMarkerIds;
if (markerCorners.empty() && markerIds.empty()) {
vector<vector<Point2f> > rejectedMarkers;
charucoDetectorImpl->arucoDetector.detectMarkers(image, _markerCorners, _markerIds, rejectedMarkers);
if (charucoDetectorImpl->charucoParameters.tryRefineMarkers)
charucoDetectorImpl->arucoDetector.refineDetectedMarkers(image, charucoDetectorImpl->board, _markerCorners,
_markerIds, rejectedMarkers);
charucoDetectorImpl->detectBoard(image, charucoCorners, charucoIds, markerCorners, markerIds);
if (charucoDetectorImpl->checkBoard(markerCorners, markerIds, charucoCorners, charucoIds) == false) {
charucoCorners.release();
charucoIds.release();
}
// if camera parameters are avaible, use approximated calibration
if(!charucoDetectorImpl->charucoParameters.cameraMatrix.empty())
charucoDetectorImpl->interpolateCornersCharucoApproxCalib(_markerCorners, _markerIds, image, charucoCorners,
charucoIds);
// else use local homography
else
charucoDetectorImpl->interpolateCornersCharucoLocalHom(_markerCorners, _markerIds, image, charucoCorners,
charucoIds);
// to return a charuco corner, its closest aruco markers should have been detected
charucoDetectorImpl->filterCornersWithoutMinMarkers(charucoCorners, charucoIds, _markerIds, charucoCorners,
charucoIds);
}
void CharucoDetector::detectDiamonds(InputArray image, OutputArrayOfArrays _diamondCorners, OutputArray _diamondIds,
InputOutputArrayOfArrays inMarkerCorners, InputOutputArrayOfArrays inMarkerIds) const {
InputOutputArrayOfArrays inMarkerCorners, InputOutputArray inMarkerIds) const {
CV_Assert(getBoard().getChessboardSize() == Size(3, 3));
CV_Assert((inMarkerCorners.empty() && inMarkerIds.empty() && !image.empty()) || (inMarkerCorners.total() == inMarkerIds.total()));
@@ -416,7 +470,7 @@ void CharucoDetector::detectDiamonds(InputArray image, OutputArrayOfArrays _diam
// interpolate the charuco corners of the diamond
vector<Point2f> currentMarkerCorners;
Mat aux;
detectBoard(grey, currentMarkerCorners, aux, currentMarker, currentMarkerId);
charucoDetectorImpl->detectBoard(grey, currentMarkerCorners, aux, currentMarker, currentMarkerId);
// if everything is ok, save the diamond
if(currentMarkerCorners.size() > 0ull) {
@@ -8,11 +8,14 @@
#include "../../precomp.hpp"
#include "super_scale.hpp"
#ifdef HAVE_OPENCV_DNN
#include "opencv2/core.hpp"
#include "opencv2/core/utils/logger.hpp"
namespace cv {
namespace barcode {
#ifdef HAVE_OPENCV_DNN
constexpr static float MAX_SCALE = 4.0f;
int SuperScale::init(const std::string &proto_path, const std::string &model_path)
@@ -71,7 +74,26 @@ int SuperScale::superResolutionScale(const Mat &src, Mat &dst)
}
return 0;
}
#else // HAVE_OPENCV_DNN
int SuperScale::init(const std::string &proto_path, const std::string &model_path)
{
CV_UNUSED(proto_path);
CV_UNUSED(model_path);
return 0;
}
void SuperScale::processImageScale(const Mat &src, Mat &dst, float scale, const bool & isEnabled, int sr_max_size)
{
CV_UNUSED(sr_max_size);
if (isEnabled)
{
CV_LOG_WARNING(NULL, "objdetect/barcode: SuperScaling disabled - OpenCV has been built without DNN support");
}
resize(src, dst, Size(), scale, scale, INTER_CUBIC);
}
#endif // HAVE_OPENCV_DNN
} // namespace barcode
} // namespace cv
#endif // HAVE_OPENCV_DNN
@@ -9,8 +9,8 @@
#define OPENCV_BARCODE_SUPER_SCALE_HPP
#ifdef HAVE_OPENCV_DNN
#include "opencv2/dnn.hpp"
# include "opencv2/dnn.hpp"
#endif
namespace cv {
namespace barcode {
@@ -26,44 +26,16 @@ public:
void processImageScale(const Mat &src, Mat &dst, float scale, const bool &use_sr, int sr_max_size = 160);
#ifdef HAVE_OPENCV_DNN
private:
dnn::Net srnet_;
bool net_loaded_ = false;
int superResolutionScale(const cv::Mat &src, cv::Mat &dst);
#endif
};
} // namespace barcode
} // namespace cv
#else // HAVE_OPENCV_DNN
#include "opencv2/core.hpp"
#include "opencv2/core/utils/logger.hpp"
namespace cv {
namespace barcode {
class SuperScale
{
public:
int init(const std::string &, const std::string &)
{
return 0;
}
void processImageScale(const Mat &src, Mat &dst, float scale, const bool & isEnabled, int)
{
if (isEnabled)
{
CV_LOG_WARNING(NULL, "objdetect/barcode: SuperScaling disabled - OpenCV has been built without DNN support");
}
resize(src, dst, Size(), scale, scale, INTER_CUBIC);
}
};
} // namespace barcode
} // namespace cv
#endif // !HAVE_OPENCV_DNN
} // namespace barcode
} // namespace cv
#endif // OPENCV_BARCODE_SUPER_SCALE_HPP
@@ -689,4 +689,32 @@ TEST(Charuco, testmatchImagePoints)
}
}
typedef testing::TestWithParam<cv::Size> CharucoBoard;
INSTANTIATE_TEST_CASE_P(/**/, CharucoBoard, testing::Values(Size(3, 2), Size(3, 2), Size(6, 2), Size(2, 6),
Size(3, 4), Size(4, 3), Size(7, 3), Size(3, 7)));
TEST_P(CharucoBoard, testWrongSizeDetection)
{
cv::Size boardSize = GetParam();
ASSERT_FALSE(boardSize.width == boardSize.height);
aruco::CharucoBoard board(boardSize, 1.f, 0.5f, aruco::getPredefinedDictionary(aruco::DICT_4X4_50));
vector<int> detectedCharucoIds, detectedArucoIds;
vector<Point2f> detectedCharucoCorners;
vector<vector<Point2f>> detectedArucoCorners;
Mat boardImage;
board.generateImage(boardSize*40, boardImage);
swap(boardSize.width, boardSize.height);
aruco::CharucoDetector detector(aruco::CharucoBoard(boardSize, 1.f, 0.5f, aruco::getPredefinedDictionary(aruco::DICT_4X4_50)));
// try detect board with wrong size
detector.detectBoard(boardImage, detectedCharucoCorners, detectedCharucoIds, detectedArucoCorners, detectedArucoIds);
// aruco markers must be found
ASSERT_EQ(detectedArucoIds.size(), board.getIds().size());
ASSERT_EQ(detectedArucoCorners.size(), board.getIds().size());
// charuco corners should not be found in board with wrong size
ASSERT_TRUE(detectedCharucoCorners.empty());
ASSERT_TRUE(detectedCharucoIds.empty());
}
}} // namespace
+3
View File
@@ -10,6 +10,9 @@ void check_qr(const string& root, const string& name_current_image, const string
const std::vector<Point>& corners,
const std::vector<string>& decoded_info, const int max_pixel_error,
bool isMulti = false) {
#ifndef HAVE_QUIRC
CV_UNUSED(decoded_info);
#endif
const std::string dataset_config = findDataFile(root + "dataset_config.json");
FileStorage file_config(dataset_config, FileStorage::READ);
ASSERT_TRUE(file_config.isOpened()) << "Can't read validation data: " << dataset_config;
+2 -2
View File
@@ -374,8 +374,8 @@ TEST_P(Objdetect_QRCode_Multi, regression)
qrcode = QRCodeDetectorAruco();
}
std::vector<Point> corners;
#ifdef HAVE_QUIRC
std::vector<cv::String> decoded_info;
#ifdef HAVE_QUIRC
std::vector<Mat> straight_barcode;
EXPECT_TRUE(qrcode.detectAndDecodeMulti(src, decoded_info, corners, straight_barcode));
ASSERT_FALSE(corners.empty());
@@ -538,7 +538,6 @@ TEST(Objdetect_QRCode_detect_flipped, regression_23249)
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);
@@ -551,6 +550,7 @@ TEST(Objdetect_QRCode_detect_flipped, regression_23249)
EXPECT_TRUE(!corners.empty());
std::string decoded_msg;
#ifdef HAVE_QUIRC
const std::string &expect_msg = flipped_image.second;
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);