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

Merge branch 4.x

This commit is contained in:
Alexander Smorkalov
2026-02-11 13:54:39 +03:00
committed by Alexander Smorkalov
468 changed files with 16647 additions and 11284 deletions
@@ -18,6 +18,7 @@
#include "../../precomp.hpp"
#include "apriltag_quad_thresh.hpp"
#include "unionfind.hpp"
//#define APRIL_DEBUG
#ifdef APRIL_DEBUG
@@ -93,7 +94,7 @@ void ptsort_(struct pt *pts, int sz)
// a merge sort with temp storage.
// Use stack storage if it's not too big.
cv::AutoBuffer<struct pt, 1024> _tmp_stack(sz);
AutoBuffer<struct pt, 1024> _tmp_stack(sz);
memcpy(_tmp_stack.data(), pts, sizeof(struct pt) * sz);
int asz = sz/2;
@@ -571,31 +572,6 @@ int quad_segment_agg(int sz, struct line_fit_pt *lfps, int indices[4]){
return 1;
}
#define DO_UNIONFIND(dx, dy) if (im.data[y*s + dy*s + x + dx] == v) unionfind_connect(uf, y*w + x, y*w + dy*w + x + dx);
static void do_unionfind_line(unionfind_t *uf, Mat &im, int w, int s, int y){
CV_Assert(y+1 < im.rows);
CV_Assert(!im.empty());
for (int x = 1; x < w - 1; x++) {
uint8_t v = im.data[y*s + x];
if (v == 127)
continue;
// (dx,dy) pairs for 8 connectivity:
// (REFERENCE) (1, 0)
// (-1, 1) (0, 1) (1, 1)
//
DO_UNIONFIND(1, 0);
DO_UNIONFIND(0, 1);
if (v == 255) {
DO_UNIONFIND(-1, 1);
DO_UNIONFIND(1, 1);
}
}
}
#undef DO_UNIONFIND
/**
* return 1 if the quad looks okay, 0 if it should be discarded
* quad
@@ -1309,11 +1285,11 @@ zarray_t *apriltag_quad_thresh(const DetectorParameters &parameters, const Mat &
////////////////////////////////////////////////////////
// step 2. find connected components.
unionfind_t *uf = unionfind_create(w * h);
UnionFind uf(w * h);
// TODO PARALLELIZE
for (int y = 0; y < h - 1; y++) {
do_unionfind_line(uf, thold, w, ts, y);
uf.do_line(thold, w, ts, y);
}
// XXX sizing??
@@ -1329,7 +1305,7 @@ zarray_t *apriltag_quad_thresh(const DetectorParameters &parameters, const Mat &
continue;
// XXX don't query this until we know we need it?
uint64_t rep0 = unionfind_get_representative(uf, y*w + x);
uint64_t rep0 = uf.get_representative(y*w + x);
// whenever we find two adjacent pixels such that one is
// white and the other black, we add the point half-way
@@ -1356,7 +1332,7 @@ zarray_t *apriltag_quad_thresh(const DetectorParameters &parameters, const Mat &
uint8_t v1 = thold.data[y*ts + dy*ts + x + dx]; \
\
if (v0 + v1 == 255) { \
uint64_t rep1 = unionfind_get_representative(uf, y*w + dy*w + x + dx); \
uint64_t rep1 = uf.get_representative(y*w + dy*w + x + dx); \
uint64_t clusterid; \
if (rep0 < rep1) \
clusterid = (rep1 << 32) + rep0; \
@@ -1405,9 +1381,9 @@ uint32_t *colors = (uint32_t*) calloc(w*h, sizeof(*colors));
for (int y = 0; y < h; y++) {
for (int x = 0; x < w; x++) {
uint32_t v = unionfind_get_representative(uf, y*w+x);
uint32_t v = uf.get_representative(y*w+x);
if (unionfind_get_set_size(uf, v) < parameters->aprilTagMinClusterPixels)
if (uf.get_set_size(v) < parameters->aprilTagMinClusterPixels)
continue;
uint32_t color = colors[v];
@@ -1533,8 +1509,6 @@ for (int i = 0; i < _zarray_size(quads); i++) {
imwrite("2.5 debug_lines.pnm", out);
#endif
unionfind_destroy(uf);
for (int i = 0; i < _zarray_size(clusters); i++) {
zarray_t *cluster;
_zarray_get(clusters, i, &cluster);
@@ -19,7 +19,6 @@
#ifndef _OPENCV_APRIL_QUAD_THRESH_HPP_
#define _OPENCV_APRIL_QUAD_THRESH_HPP_
#include "unionfind.hpp"
#include "zmaxheap.hpp"
#include "zarray.hpp"
+107 -107
View File
@@ -17,115 +17,115 @@
namespace cv {
namespace aruco {
typedef struct unionfind unionfind_t;
struct unionfind{
struct UnionFind {
UnionFind(uint32_t _maxid)
{
maxid = _maxid;
data.resize(maxid+1);
for (unsigned int i = 0; i <= maxid; i++) {
data[i].size = 1;
data[i].parent = i;
}
};
inline uint32_t get_representative(uint32_t id) {
uint32_t root = id;
// chase down the root
while (data[root].parent != root) {
root = data[root].parent;
}
// go back and collapse the tree.
//
// XXX: on some of our workloads that have very shallow trees
// (e.g. image segmentation), we are actually faster not doing
// this...
while (data[id].parent != root) {
uint32_t tmp = data[id].parent;
data[id].parent = root;
id = tmp;
}
return root;
}
inline uint32_t get_set_size(uint32_t id) {
uint32_t repid = get_representative(id);
return data[repid].size;
}
inline uint32_t connect(uint32_t aid, uint32_t bid) {
uint32_t aroot = get_representative(aid);
uint32_t broot = get_representative(bid);
if (aroot == broot)
return aroot;
// we don't perform "union by rank", but we perform a similar
// operation (but probably without the same asymptotic guarantee):
// We join trees based on the number of *elements* (as opposed to
// rank) contained within each tree. I.e., we use size as a proxy
// for rank. In my testing, it's often *faster* to use size than
// rank, perhaps because the rank of the tree isn't that critical
// if there are very few nodes in it.
uint32_t asize = data[aroot].size;
uint32_t bsize = data[broot].size;
// optimization idea: We could shortcut some or all of the tree
// that is grafted onto the other tree. Pro: those nodes were just
// read and so are probably in cache. Con: it might end up being
// wasted effort -- the tree might be grafted onto another tree in
// a moment!
if (asize > bsize) {
data[broot].parent = aroot;
data[aroot].size += bsize;
return aroot;
} else {
data[aroot].parent = broot;
data[broot].size += asize;
return broot;
}
}
#define DO_UNIONFIND(dx, dy) if (im.data[y*s + dy*s + x + dx] == v) connect(y*w + x, y*w + dy*w + x + dx);
void do_line(Mat &im, int w, int s, int y) {
CV_Assert(y+1 < im.rows);
CV_Assert(!im.empty());
for (int x = 1; x < w - 1; x++) {
uint8_t v = im.data[y*s + x];
if (v == 127)
continue;
// (dx,dy) pairs for 8 connectivity:
// (REFERENCE) (1, 0)
// (-1, 1) (0, 1) (1, 1)
//
DO_UNIONFIND(1, 0);
DO_UNIONFIND(0, 1);
if (v == 255) {
DO_UNIONFIND(-1, 1);
DO_UNIONFIND(1, 1);
}
}
}
#undef DO_UNIONFIND
struct ufrec {
// the parent of this node. If a node's parent is its own index,
// then it is a root.
uint32_t parent;
// for the root of a connected component, the number of components
// connected to it. For intermediate values, it's not meaningful.
uint32_t size;
};
uint32_t maxid;
struct ufrec *data;
std::vector<ufrec> data;
};
struct ufrec{
// the parent of this node. If a node's parent is its own index,
// then it is a root.
uint32_t parent;
// for the root of a connected component, the number of components
// connected to it. For intermediate values, it's not meaningful.
uint32_t size;
};
static inline unionfind_t *unionfind_create(uint32_t maxid){
unionfind_t *uf = (unionfind_t*) calloc(1, sizeof(unionfind_t));
uf->maxid = maxid;
uf->data = (struct ufrec*) malloc((maxid+1) * sizeof(struct ufrec));
for (unsigned int i = 0; i <= maxid; i++) {
uf->data[i].size = 1;
uf->data[i].parent = i;
}
return uf;
}
static inline void unionfind_destroy(unionfind_t *uf){
free(uf->data);
free(uf);
}
/*
static inline uint32_t unionfind_get_representative(unionfind_t *uf, uint32_t id)
{
// base case: a node is its own parent
if (uf->data[id].parent == id)
return id;
// otherwise, recurse
uint32_t root = unionfind_get_representative(uf, uf->data[id].parent);
// short circuit the path. [XXX This write prevents tail recursion]
uf->data[id].parent = root;
return root;
}
*/
// this one seems to be every-so-slightly faster than the recursive
// version above.
static inline uint32_t unionfind_get_representative(unionfind_t *uf, uint32_t id){
uint32_t root = id;
// chase down the root
while (uf->data[root].parent != root) {
root = uf->data[root].parent;
}
// go back and collapse the tree.
//
// XXX: on some of our workloads that have very shallow trees
// (e.g. image segmentation), we are actually faster not doing
// this...
while (uf->data[id].parent != root) {
uint32_t tmp = uf->data[id].parent;
uf->data[id].parent = root;
id = tmp;
}
return root;
}
static inline uint32_t unionfind_get_set_size(unionfind_t *uf, uint32_t id){
uint32_t repid = unionfind_get_representative(uf, id);
return uf->data[repid].size;
}
static inline uint32_t unionfind_connect(unionfind_t *uf, uint32_t aid, uint32_t bid){
uint32_t aroot = unionfind_get_representative(uf, aid);
uint32_t broot = unionfind_get_representative(uf, bid);
if (aroot == broot)
return aroot;
// we don't perform "union by rank", but we perform a similar
// operation (but probably without the same asymptotic guarantee):
// We join trees based on the number of *elements* (as opposed to
// rank) contained within each tree. I.e., we use size as a proxy
// for rank. In my testing, it's often *faster* to use size than
// rank, perhaps because the rank of the tree isn't that critical
// if there are very few nodes in it.
uint32_t asize = uf->data[aroot].size;
uint32_t bsize = uf->data[broot].size;
// optimization idea: We could shortcut some or all of the tree
// that is grafted onto the other tree. Pro: those nodes were just
// read and so are probably in cache. Con: it might end up being
// wasted effort -- the tree might be grafted onto another tree in
// a moment!
if (asize > bsize) {
uf->data[broot].parent = aroot;
uf->data[aroot].size += bsize;
return aroot;
} else {
uf->data[aroot].parent = broot;
uf->data[broot].size += asize;
return broot;
}
}
}}
#endif
@@ -84,7 +84,6 @@ void zmaxheap_destroy(zmaxheap_t *heap)
{
free(heap->values);
free(heap->data);
memset(heap, 0, sizeof(zmaxheap_t));
free(heap);
}
+106 -19
View File
@@ -9,6 +9,7 @@
#include "opencv2/objdetect/aruco_board.hpp"
#include "apriltag/apriltag_quad_thresh.hpp"
#include "aruco_utils.hpp"
#include <algorithm>
#include <cmath>
#include <map>
@@ -124,7 +125,7 @@ static void _threshold(InputArray _in, OutputArray _out, int winSize, double con
/**
* @brief Given a tresholded image, find the contours, calculate their polygonal approximation
* @brief Given a thresholded image, find the contours, calculate their polygonal approximation
* and take those that accomplish some conditions
*/
static void _findMarkerContours(const Mat &in, vector<vector<Point2f> > &candidates,
@@ -313,10 +314,11 @@ static void _detectInitialCandidates(const Mat &grey, vector<vector<Point2f> > &
* the border bits
*/
static Mat _extractBits(InputArray _image, const vector<Point2f>& corners, int markerSize,
int markerBorderBits, int cellSize, double cellMarginRate, double minStdDevOtsu) {
int markerBorderBits, int cellSize, double cellMarginRate, double minStdDevOtsu,
OutputArray _cellPixelRatio = noArray()) {
CV_Assert(_image.getMat().channels() == 1);
CV_Assert(corners.size() == 4ull);
CV_Assert(markerBorderBits > 0 && cellSize > 0 && cellMarginRate >= 0 && cellMarginRate <= 1);
CV_Assert(markerBorderBits > 0 && cellSize > 0 && cellMarginRate >= 0 && cellMarginRate <= 0.5);
CV_Assert(minStdDevOtsu >= 0);
// number of bits in the marker
@@ -353,9 +355,16 @@ static Mat _extractBits(InputArray _image, const vector<Point2f>& corners, int m
bits.setTo(1);
else
bits.setTo(0);
if(_cellPixelRatio.needed()) bits.convertTo(_cellPixelRatio, CV_32F);
return bits;
}
Mat cellPixelRatio;
if (_cellPixelRatio.needed()) {
_cellPixelRatio.create(markerSizeWithBorders, markerSizeWithBorders, CV_32FC1);
cellPixelRatio = _cellPixelRatio.getMatRef();
}
// now extract code, first threshold using Otsu
threshold(resultImg, resultImg, 125, 255, THRESH_BINARY | THRESH_OTSU);
@@ -369,6 +378,9 @@ static Mat _extractBits(InputArray _image, const vector<Point2f>& corners, int m
// count white pixels on each cell to assign its value
size_t nZ = (size_t) countNonZero(square);
if(nZ > square.total() / 2) bits.at<unsigned char>(y, x) = 1;
// define the cell pixel ratio as the ratio of the white pixels. For inverted markers, the ratio will be inverted.
if(_cellPixelRatio.needed()) cellPixelRatio.at<float>(y, x) = (nZ / (float)square.total());
}
}
@@ -403,6 +415,52 @@ static int _getBorderErrors(const Mat &bits, int markerSize, int borderSize) {
}
/** @brief Given a matrix containing the percentage of white pixels in each marker cell, returns the normalized marker confidence [0;1].
* The confidence is defined as 1 - normalized uncertainty, where 1 describes a pixel perfect detection.
* The rotation is set to 0,1,2,3 for [0, 90, 180, 270] deg CCW rotations.
*/
static float _getMarkerConfidence(const Mat& groundTruthbits, const Mat &cellPixelRatio, const int markerSize, const int borderSize) {
CV_Assert(markerSize == groundTruthbits.cols && markerSize == groundTruthbits.rows);
const int sizeWithBorders = markerSize + 2 * borderSize;
CV_Assert(markerSize > 0 && cellPixelRatio.cols == sizeWithBorders && cellPixelRatio.rows == sizeWithBorders);
// Get border uncertainty. cellPixelRatio has the opposite color as the borders --> it is the uncertainty.
float tempBorderUnc = 0.f;
for(int y = 0; y < sizeWithBorders; y++) {
for(int k = 0; k < borderSize; k++) {
// Left and right vertical sides
tempBorderUnc += cellPixelRatio.ptr<float>(y)[k];
tempBorderUnc += cellPixelRatio.ptr<float>(y)[sizeWithBorders - 1 - k];
}
}
for(int x = borderSize; x < sizeWithBorders - borderSize; x++) {
for(int k = 0; k < borderSize; k++) {
// Top and bottom horizontal sides
tempBorderUnc += cellPixelRatio.ptr<float>(k)[x];
tempBorderUnc += cellPixelRatio.ptr<float>(sizeWithBorders - 1 - k)[x];
}
}
// Get the inner marker uncertainty. For a white or black cell, the uncertainty is the ratio of black or white pixels respectively.
float tempInnerUnc = 0.f;
for(int y = borderSize; y < markerSize + borderSize; y++) {
for(int x = borderSize; x < markerSize + borderSize; x++) {
tempInnerUnc += std::abs(groundTruthbits.ptr<float>(y - borderSize)[x - borderSize] - cellPixelRatio.ptr<float>(y)[x]);
}
}
// Compute the overall normalized marker uncertainty and convert it to confidence
const float area = static_cast<float>(sizeWithBorders) * sizeWithBorders;
const float normalizedMarkerUnc = (tempInnerUnc + tempBorderUnc) / area;
const float normalizedMarkerConfidence = 1.f - normalizedMarkerUnc;
return std::max(0.f, std::min(1.f, normalizedMarkerConfidence));
}
/**
* @brief Tries to identify one candidate given the dictionary
* @return candidate typ. zero if the candidate is not valid,
@@ -412,6 +470,7 @@ static int _getBorderErrors(const Mat &bits, int markerSize, int borderSize) {
static uint8_t _identifyOneCandidate(const Dictionary& dictionary, const Mat& _image,
const vector<Point2f>& _corners, int& idx,
const DetectorParameters& params, int& rotation,
float &markerConfidence, bool confidenceNeeded,
const float scale = 1.f) {
CV_DbgAssert(params.markerBorderBits > 0);
uint8_t typ=1;
@@ -423,10 +482,12 @@ static uint8_t _identifyOneCandidate(const Dictionary& dictionary, const Mat& _i
scaled_corners[i].y = _corners[i].y * scale;
}
Mat cellPixelRatio;
Mat candidateBits =
_extractBits(_image, scaled_corners, dictionary.markerSize, params.markerBorderBits,
params.perspectiveRemovePixelPerCell,
params.perspectiveRemoveIgnoredMarginPerCell, params.minOtsuStdDev);
params.perspectiveRemoveIgnoredMarginPerCell, params.minOtsuStdDev,
cellPixelRatio);
// analyze border bits
int maximumErrorsInBorder =
@@ -441,6 +502,7 @@ static uint8_t _identifyOneCandidate(const Dictionary& dictionary, const Mat& _i
int invBError = _getBorderErrors(invertedImg, dictionary.markerSize, params.markerBorderBits);
// white marker
if(invBError<borderErrors){
cellPixelRatio = 1.f - cellPixelRatio;
borderErrors = invBError;
invertedImg.copyTo(candidateBits);
typ=2;
@@ -458,6 +520,14 @@ static uint8_t _identifyOneCandidate(const Dictionary& dictionary, const Mat& _i
if(!dictionary.identify(onlyBits, idx, rotation, params.errorCorrectionRate))
return 0;
// compute the candidate's confidence
if(confidenceNeeded) {
Mat groundTruthbits;
Mat bitsUints = dictionary.getBitsFromByteList(dictionary.bytesList.rowRange(idx, idx + 1), dictionary.markerSize, rotation);
bitsUints.convertTo(groundTruthbits, CV_32F);
markerConfidence = _getMarkerConfidence(groundTruthbits, cellPixelRatio, dictionary.markerSize, params.markerBorderBits);
}
return typ;
}
@@ -657,7 +727,7 @@ struct ArucoDetector::ArucoDetectorImpl {
* @brief Detect markers either using multiple or just first dictionary
*/
void detectMarkers(InputArray _image, OutputArrayOfArrays _corners, OutputArray _ids,
OutputArrayOfArrays _rejectedImgPoints, OutputArray _dictIndices, DictionaryMode dictMode) {
OutputArrayOfArrays _rejectedImgPoints, OutputArray _dictIndices, OutputArray _markersConfidence, DictionaryMode dictMode) {
CV_Assert(!_image.empty());
CV_Assert(detectorParams.markerBorderBits > 0);
@@ -717,6 +787,7 @@ struct ArucoDetector::ArucoDetectorImpl {
vector<vector<Point2f> > candidates;
vector<vector<Point> > contours;
vector<int> ids;
vector<float> markersConfidence;
/// STEP 2.a Detect marker candidates :: using AprilTag
if(detectorParams.cornerRefinementMethod == (int)CORNER_REFINE_APRILTAG){
@@ -738,7 +809,7 @@ struct ArucoDetector::ArucoDetectorImpl {
/// STEP 2: Check candidate codification (identify markers)
identifyCandidates(grey, grey_pyramid, selectedCandidates, candidates, contours,
ids, dictionary, rejectedImgPoints);
ids, dictionary, rejectedImgPoints, markersConfidence, _markersConfidence.needed());
/// STEP 3: Corner refinement :: use corner subpix
if (detectorParams.cornerRefinementMethod == (int)CORNER_REFINE_SUBPIX) {
@@ -766,7 +837,7 @@ struct ArucoDetector::ArucoDetectorImpl {
// temporary variable to store the current candidates
vector<vector<Point2f>> currentCandidates;
identifyCandidates(grey, grey_pyramid, candidatesPerDictionarySize.at(currentDictionary.markerSize), currentCandidates, contours,
ids, currentDictionary, rejectedImgPoints);
ids, currentDictionary, rejectedImgPoints, markersConfidence, _markersConfidence.needed());
if (_dictIndices.needed()) {
dictIndices.insert(dictIndices.end(), currentCandidates.size(), dictIndex);
}
@@ -849,6 +920,9 @@ struct ArucoDetector::ArucoDetectorImpl {
if (_dictIndices.needed()) {
Mat(dictIndices).copyTo(_dictIndices);
}
if (_markersConfidence.needed()) {
Mat(markersConfidence).copyTo(_markersConfidence);
}
}
/**
@@ -982,9 +1056,10 @@ struct ArucoDetector::ArucoDetectorImpl {
*/
void identifyCandidates(const Mat& grey, const vector<Mat>& image_pyr, vector<MarkerCandidateTree>& selectedContours,
vector<vector<Point2f> >& accepted, vector<vector<Point> >& contours,
vector<int>& ids, const Dictionary& currentDictionary, vector<vector<Point2f>>& rejected) const {
vector<int>& ids, const Dictionary& currentDictionary, vector<vector<Point2f>>& rejected, vector<float>& markersConfidence, bool confidenceNeeded) const {
size_t ncandidates = selectedContours.size();
vector<float> markersConfidenceTmp(ncandidates, 0.f);
vector<int> idsTmp(ncandidates, -1);
vector<int> rotated(ncandidates, 0);
vector<uint8_t> validCandidates(ncandidates, 0);
@@ -1018,11 +1093,11 @@ struct ArucoDetector::ArucoDetectorImpl {
}
const float scale = detectorParams.useAruco3Detection ? img.cols / static_cast<float>(grey.cols) : 1.f;
validCandidates[v] = _identifyOneCandidate(currentDictionary, img, selectedContours[v].corners, idsTmp[v], detectorParams, rotated[v], scale);
validCandidates[v] = _identifyOneCandidate(currentDictionary, img, selectedContours[v].corners, idsTmp[v], detectorParams, rotated[v], markersConfidenceTmp[v], confidenceNeeded, scale);
if (validCandidates[v] == 0 && checkCloseContours) {
for (const MarkerCandidate& closeMarkerCandidate: selectedContours[v].closeContours) {
validCandidates[v] = _identifyOneCandidate(currentDictionary, img, closeMarkerCandidate.corners, idsTmp[v], detectorParams, rotated[v], scale);
validCandidates[v] = _identifyOneCandidate(currentDictionary, img, closeMarkerCandidate.corners, idsTmp[v], detectorParams, rotated[v], markersConfidenceTmp[v], confidenceNeeded, scale);
if (validCandidates[v] > 0) {
selectedContours[v].corners = closeMarkerCandidate.corners;
selectedContours[v].contour = closeMarkerCandidate.contour;
@@ -1052,17 +1127,24 @@ struct ArucoDetector::ArucoDetectorImpl {
for (size_t i = 0ull; i < selectedContours.size(); i++) {
if (validCandidates[i] > 0) {
// shift corner positions to the correct rotation
correctCornerPosition(selectedContours[i].corners, rotated[i]);
// shift corner positions to the correct rotation
correctCornerPosition(selectedContours[i].corners, rotated[i]);
accepted.push_back(selectedContours[i].corners);
contours.push_back(selectedContours[i].contour);
ids.push_back(idsTmp[i]);
}
else {
accepted.push_back(selectedContours[i].corners);
contours.push_back(selectedContours[i].contour);
ids.push_back(idsTmp[i]);
} else {
rejected.push_back(selectedContours[i].corners);
}
}
if(confidenceNeeded) {
for (size_t i = 0ull; i < selectedContours.size(); i++) {
if (validCandidates[i] > 0) {
markersConfidence.push_back(markersConfidenceTmp[i]);
}
}
}
}
void performCornerSubpixRefinement(const Mat& grey, const vector<Mat>& grey_pyramid, int closest_pyr_image_idx, const vector<vector<Point2f>>& candidates, const Dictionary& dictionary) const {
@@ -1103,14 +1185,19 @@ ArucoDetector::ArucoDetector(const vector<Dictionary> &_dictionaries,
arucoDetectorImpl = makePtr<ArucoDetectorImpl>(_dictionaries, _detectorParams, _refineParams);
}
void ArucoDetector::detectMarkersWithConfidence(InputArray _image, OutputArrayOfArrays _corners, OutputArray _ids, OutputArray _markersConfidence,
OutputArrayOfArrays _rejectedImgPoints) const {
arucoDetectorImpl->detectMarkers(_image, _corners, _ids, _rejectedImgPoints, noArray(), _markersConfidence, DictionaryMode::Single);
}
void ArucoDetector::detectMarkers(InputArray _image, OutputArrayOfArrays _corners, OutputArray _ids,
OutputArrayOfArrays _rejectedImgPoints) const {
arucoDetectorImpl->detectMarkers(_image, _corners, _ids, _rejectedImgPoints, noArray(), DictionaryMode::Single);
arucoDetectorImpl->detectMarkers(_image, _corners, _ids, _rejectedImgPoints, noArray(), noArray(), DictionaryMode::Single);
}
void ArucoDetector::detectMarkersMultiDict(InputArray _image, OutputArrayOfArrays _corners, OutputArray _ids,
OutputArrayOfArrays _rejectedImgPoints, OutputArray _dictIndices) const {
arucoDetectorImpl->detectMarkers(_image, _corners, _ids, _rejectedImgPoints, _dictIndices, DictionaryMode::Multi);
arucoDetectorImpl->detectMarkers(_image, _corners, _ids, _rejectedImgPoints, _dictIndices, noArray(), DictionaryMode::Multi);
}
/**
@@ -194,17 +194,24 @@ Mat Dictionary::getByteListFromBits(const Mat &bits) {
}
Mat Dictionary::getBitsFromByteList(const Mat &byteList, int markerSize) {
Mat Dictionary::getBitsFromByteList(const Mat &byteList, int markerSize, int rotationId) {
CV_Assert(byteList.total() > 0 &&
byteList.total() >= (unsigned int)markerSize * markerSize / 8 &&
byteList.total() <= (unsigned int)markerSize * markerSize / 8 + 1);
CV_Assert(rotationId >=0 && rotationId < 4);
Mat bits(markerSize, markerSize, CV_8UC1, Scalar::all(0));
unsigned char base2List[] = { 128, 64, 32, 16, 8, 4, 2, 1 };
// Use a base offset for the selected rotation
int nbytes = (bits.cols * bits.rows + 8 - 1) / 8; // integer ceil
int base = rotationId * nbytes;
int currentByteIdx = 0;
// we only need the bytes in normal rotation
unsigned char currentByte = byteList.ptr()[0];
unsigned char currentByte = byteList.ptr()[base + currentByteIdx];
int currentBit = 0;
for(int row = 0; row < bits.rows; row++) {
for(int col = 0; col < bits.cols; col++) {
if(currentByte >= base2List[currentBit]) {
@@ -214,7 +221,7 @@ Mat Dictionary::getBitsFromByteList(const Mat &byteList, int markerSize) {
currentBit++;
if(currentBit == 8) {
currentByteIdx++;
currentByte = byteList.ptr()[currentByteIdx];
currentByte = byteList.ptr()[base + currentByteIdx];
// if not enough bits for one more byte, we are in the end
// update bit position accordingly
if(8 * (currentByteIdx + 1) > (int)bits.total())
@@ -316,8 +316,8 @@ struct CharucoDetector::CharucoDetectorImpl {
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;
InputOutputArrayOfArrays _markerCorners = markerCorners.needed() ? markerCorners : _InputOutputArray(tmpMarkerCorners);
InputOutputArray _markerIds = markerIds.needed() ? markerIds : _InputOutputArray(tmpMarkerIds);
if (markerCorners.empty() && markerIds.empty()) {
vector<vector<Point2f> > rejectedMarkers;
@@ -341,8 +341,8 @@ struct CharucoDetector::CharucoDetectorImpl {
InputOutputArrayOfArrays markerCorners, InputOutputArray markerIds) {
vector<vector<Point2f>> tmpMarkerCorners;
vector<int> tmpMarkerIds;
InputOutputArrayOfArrays _markerCorners = markerCorners.needed() ? markerCorners : tmpMarkerCorners;
InputOutputArray _markerIds = markerIds.needed() ? markerIds : tmpMarkerIds;
InputOutputArrayOfArrays _markerCorners = markerCorners.needed() ? markerCorners : _InputOutputArray(tmpMarkerCorners);
InputOutputArray _markerIds = markerIds.needed() ? markerIds : _InputOutputArray(tmpMarkerIds);
detectBoard(image, charucoCorners, charucoIds, _markerCorners, _markerIds);
if (charucoParameters.checkMarkers && checkBoard(_markerCorners, _markerIds, charucoCorners, charucoIds) == false) {
CV_LOG_DEBUG(NULL, "ChArUco board is built incorrectly");
@@ -402,8 +402,8 @@ void CharucoDetector::detectDiamonds(InputArray image, OutputArrayOfArrays _diam
vector<vector<Point2f>> tmpMarkerCorners;
vector<int> tmpMarkerIds;
InputOutputArrayOfArrays _markerCorners = inMarkerCorners.needed() ? inMarkerCorners : tmpMarkerCorners;
InputOutputArray _markerIds = inMarkerIds.needed() ? inMarkerIds : tmpMarkerIds;
InputOutputArrayOfArrays _markerCorners = inMarkerCorners.needed() ? inMarkerCorners : _InputOutputArray(tmpMarkerCorners);
InputOutputArray _markerIds = inMarkerIds.needed() ? inMarkerIds : _InputOutputArray(tmpMarkerIds);
if (_markerCorners.empty() && _markerIds.empty()) {
charucoDetectorImpl->arucoDetector.detectMarkers(image, _markerCorners, _markerIds);
}
+1
View File
@@ -43,6 +43,7 @@
#ifndef __OPENCV_PRECOMP_H__
#define __OPENCV_PRECOMP_H__
#include "opencv2/core.hpp"
#include "opencv2/objdetect.hpp"
#include "opencv2/objdetect/barcode.hpp"
#include "opencv2/imgproc.hpp"
+30 -20
View File
@@ -80,8 +80,10 @@ static Point2f intersectionLines(Point2f a1, Point2f a2, Point2f b1, Point2f b2)
// / |
// a/ | c
static inline double getCosVectors(Point2f a, Point2f b, Point2f c)
static inline double getCosVectors(Point a, Point b, Point c)
{
CV_DbgCheckNE(a, b, "Angle between vector and point is undetermined");
CV_DbgCheckNE(b, c, "Angle between vector and point is undetermined");
return ((a - b).x * (c - b).x + (a - b).y * (c - b).y) / (norm(a - b) * norm(c - b));
}
@@ -407,9 +409,9 @@ void QRDetect::fixationPoints(vector<Point2f> &local_point)
list_area_pnt.push_back(current_point);
vector<LineIterator> list_line_iter;
list_line_iter.push_back(LineIterator(bin_barcode, current_point, left_point));
list_line_iter.push_back(LineIterator(bin_barcode, current_point, central_point));
list_line_iter.push_back(LineIterator(bin_barcode, current_point, right_point));
list_line_iter.emplace_back(bin_barcode, current_point, left_point);
list_line_iter.emplace_back(bin_barcode, current_point, central_point);
list_line_iter.emplace_back(bin_barcode, current_point, right_point);
for (size_t k = 0; k < list_line_iter.size(); k++)
{
@@ -752,7 +754,7 @@ vector<Point2f> QRDetect::getQuadrilateral(vector<Point2f> angle_list)
{
int x = cvRound(angle_list[i].x);
int y = cvRound(angle_list[i].y);
locations.push_back(Point(x, y));
locations.emplace_back(x,y);
}
vector<Point> integer_hull;
@@ -822,7 +824,11 @@ vector<Point2f> QRDetect::getQuadrilateral(vector<Point2f> angle_list)
Point intrsc_line_hull =
intersectionLines(hull[index_hull], hull[next_index_hull],
angle_list[1], angle_list[2]);
double temp_norm = getCosVectors(hull[index_hull], intrsc_line_hull, angle_closest_pnt);
double temp_norm = min_norm;
if (intrsc_line_hull != angle_closest_pnt)
{
temp_norm = getCosVectors(hull[index_hull], intrsc_line_hull, angle_closest_pnt);
}
if (min_norm > temp_norm &&
norm(hull[index_hull] - hull[next_index_hull]) >
norm(angle_list[1] - angle_list[2]) * 0.1)
@@ -860,7 +866,11 @@ vector<Point2f> QRDetect::getQuadrilateral(vector<Point2f> angle_list)
Point intrsc_line_hull =
intersectionLines(hull[index_hull], hull[next_index_hull],
angle_list[0], angle_list[1]);
double temp_norm = getCosVectors(hull[index_hull], intrsc_line_hull, angle_closest_pnt);
double temp_norm = min_norm;
if (intrsc_line_hull != angle_closest_pnt)
{
temp_norm = getCosVectors(hull[index_hull], intrsc_line_hull, angle_closest_pnt);
}
if (min_norm > temp_norm &&
norm(hull[index_hull] - hull[next_index_hull]) >
norm(angle_list[0] - angle_list[1]) * 0.05)
@@ -2204,13 +2214,13 @@ bool QRDecode::straightenQRCodeInParts()
vector<Point2f> perspective_points;
perspective_points.push_back(Point2f(0.0, start_cut));
perspective_points.push_back(Point2f(perspective_curved_size, start_cut));
perspective_points.emplace_back(0.0f, start_cut);
perspective_points.emplace_back(perspective_curved_size, start_cut);
perspective_points.push_back(Point2f(perspective_curved_size, start_cut + dist));
perspective_points.push_back(Point2f(0.0, start_cut+dist));
perspective_points.emplace_back(perspective_curved_size, start_cut + dist);
perspective_points.emplace_back(0.0f, start_cut+dist);
perspective_points.push_back(Point2f(perspective_curved_size * 0.5f, start_cut + dist * 0.5f));
perspective_points.emplace_back(perspective_curved_size * 0.5f, start_cut + dist * 0.5f);
if (i == 1)
{
@@ -2281,13 +2291,13 @@ bool QRDecode::straightenQRCodeInParts()
}
vector<Point2f> perspective_straight_points;
perspective_straight_points.push_back(Point2f(0.f, 0.f));
perspective_straight_points.push_back(Point2f(perspective_curved_size, 0.f));
perspective_straight_points.emplace_back(0.f, 0.f);
perspective_straight_points.emplace_back(perspective_curved_size, 0.f);
perspective_straight_points.push_back(Point2f(perspective_curved_size, perspective_curved_size));
perspective_straight_points.push_back(Point2f(0.f, perspective_curved_size));
perspective_straight_points.emplace_back(perspective_curved_size, perspective_curved_size);
perspective_straight_points.emplace_back(0.f, perspective_curved_size);
perspective_straight_points.push_back(Point2f(perspective_curved_size * 0.5f, perspective_curved_size * 0.5f));
perspective_straight_points.emplace_back(perspective_curved_size * 0.5f, perspective_curved_size * 0.5f);
Mat H = findHomography(original_curved_points, perspective_straight_points);
if (H.empty())
@@ -3272,9 +3282,9 @@ void QRDetectMulti::fixationPoints(vector<Point2f> &local_point)
list_area_pnt.push_back(current_point);
vector<LineIterator> list_line_iter;
list_line_iter.push_back(LineIterator(bin_barcode_temp, current_point, left_point));
list_line_iter.push_back(LineIterator(bin_barcode_temp, current_point, central_point));
list_line_iter.push_back(LineIterator(bin_barcode_temp, current_point, right_point));
list_line_iter.emplace_back(bin_barcode_temp, current_point, left_point);
list_line_iter.emplace_back(bin_barcode_temp, current_point, central_point);
list_line_iter.emplace_back(bin_barcode_temp, current_point, right_point);
for (size_t k = 0; k < list_line_iter.size(); k++)
{