mirror of
https://github.com/opencv/opencv.git
synced 2026-07-30 07:43:03 +04:00
Merge branch 4.x
This commit is contained in:
@@ -721,19 +721,21 @@ public:
|
||||
ECI_UTF8 = 26
|
||||
};
|
||||
|
||||
/** @brief QR code encoder parameters.
|
||||
@param version The optional version of QR code (by default - maximum possible depending on
|
||||
the length of the string).
|
||||
@param correction_level The optional level of error correction (by default - the lowest).
|
||||
@param mode The optional encoding mode - Numeric, Alphanumeric, Byte, Kanji, ECI or Structured Append.
|
||||
@param structure_number The optional number of QR codes to generate in Structured Append mode.
|
||||
*/
|
||||
/** @brief QR code encoder parameters. */
|
||||
struct CV_EXPORTS_W_SIMPLE Params
|
||||
{
|
||||
CV_WRAP Params();
|
||||
|
||||
//! The optional version of QR code (by default - maximum possible depending on the length of the string).
|
||||
CV_PROP_RW int version;
|
||||
|
||||
//! The optional level of error correction (by default - the lowest).
|
||||
CV_PROP_RW CorrectionLevel correction_level;
|
||||
|
||||
//! The optional encoding mode - Numeric, Alphanumeric, Byte, Kanji, ECI or Structured Append.
|
||||
CV_PROP_RW EncodeMode mode;
|
||||
|
||||
//! The optional number of QR codes to generate in Structured Append mode.
|
||||
CV_PROP_RW int structure_number;
|
||||
};
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@ struct CV_EXPORTS_W_SIMPLE DetectorParameters {
|
||||
minMarkerDistanceRate = 0.05;
|
||||
cornerRefinementMethod = (int)CORNER_REFINE_NONE;
|
||||
cornerRefinementWinSize = 5;
|
||||
relativeCornerRefinmentWinSize = 0.3f;
|
||||
cornerRefinementMaxIterations = 30;
|
||||
cornerRefinementMinAccuracy = 0.1;
|
||||
markerBorderBits = 1;
|
||||
@@ -56,7 +57,7 @@ struct CV_EXPORTS_W_SIMPLE DetectorParameters {
|
||||
useAruco3Detection = false;
|
||||
minSideLengthCanonicalImg = 32;
|
||||
minMarkerLengthRatioOriginalImg = 0.0;
|
||||
};
|
||||
}
|
||||
|
||||
/** @brief Read a new set of DetectorParameters from FileNode (use FileStorage.root()).
|
||||
*/
|
||||
@@ -108,9 +109,27 @@ struct CV_EXPORTS_W_SIMPLE DetectorParameters {
|
||||
/** @brief default value CORNER_REFINE_NONE */
|
||||
CV_PROP_RW int cornerRefinementMethod;
|
||||
|
||||
/// window size for the corner refinement process (in pixels) (default 5).
|
||||
/** @brief maximum window size for the corner refinement process (in pixels) (default 5).
|
||||
*
|
||||
* The window size may decrease if the ArUco marker is too small, check relativeCornerRefinmentWinSize.
|
||||
* The final window size is calculated as:
|
||||
* min(cornerRefinementWinSize, averageArucoModuleSize*relativeCornerRefinmentWinSize),
|
||||
* where averageArucoModuleSize is average module size of ArUco marker in pixels.
|
||||
* (ArUco marker is composed of black and white modules)
|
||||
*/
|
||||
CV_PROP_RW int cornerRefinementWinSize;
|
||||
|
||||
/** @brief Dynamic window size for corner refinement relative to Aruco module size (default 0.3).
|
||||
*
|
||||
* The final window size is calculated as:
|
||||
* min(cornerRefinementWinSize, averageArucoModuleSize*relativeCornerRefinmentWinSize),
|
||||
* where averageArucoModuleSize is average module size of ArUco marker in pixels.
|
||||
* (ArUco marker is composed of black and white modules)
|
||||
* In the case of markers located far from each other, it may be useful to increase the value of the parameter to 0.4-0.5.
|
||||
* In the case of markers located close to each other, it may be useful to decrease the parameter value to 0.1-0.2.
|
||||
*/
|
||||
CV_PROP_RW float relativeCornerRefinmentWinSize;
|
||||
|
||||
/// maximum number of iterations for stop criteria of the corner refinement process (default 30).
|
||||
CV_PROP_RW int cornerRefinementMaxIterations;
|
||||
|
||||
|
||||
@@ -192,7 +192,7 @@ class CV_EXPORTS DetectionBasedTracker
|
||||
{
|
||||
lastPositions.push_back(rect);
|
||||
id=getNextId();
|
||||
};
|
||||
}
|
||||
|
||||
static int getNextId()
|
||||
{
|
||||
|
||||
@@ -20,7 +20,7 @@ model download link: https://github.com/opencv/opencv_zoo/tree/master/models/fac
|
||||
class CV_EXPORTS_W FaceDetectorYN
|
||||
{
|
||||
public:
|
||||
virtual ~FaceDetectorYN() {};
|
||||
virtual ~FaceDetectorYN() {}
|
||||
|
||||
/** @brief Set the size for the network input, which overwrites the input size of creating model. Call this method when the size of input image does not match the input size when creating model
|
||||
*
|
||||
@@ -99,7 +99,7 @@ model download link: https://github.com/opencv/opencv_zoo/tree/master/models/fac
|
||||
class CV_EXPORTS_W FaceRecognizerSF
|
||||
{
|
||||
public:
|
||||
virtual ~FaceRecognizerSF() {};
|
||||
virtual ~FaceRecognizerSF() {}
|
||||
|
||||
/** @brief Definition of distance used for calculating the distance between two face features
|
||||
*/
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// of this distribution and at http://opencv.org/license.html
|
||||
|
||||
#include "../precomp.hpp"
|
||||
#include <opencv2/core/utils/logger.hpp>
|
||||
#include "opencv2/objdetect/aruco_board.hpp"
|
||||
|
||||
#include <opencv2/objdetect/aruco_dictionary.hpp>
|
||||
@@ -250,7 +251,11 @@ GridBoard::GridBoard() {}
|
||||
GridBoard::GridBoard(const Size& size, float markerLength, float markerSeparation,
|
||||
const Dictionary &dictionary, InputArray ids):
|
||||
Board(new GridBoardImpl(dictionary, size, markerLength, markerSeparation)) {
|
||||
|
||||
float onePin = markerLength / ((float)(dictionary.markerSize+2));
|
||||
if (markerSeparation < onePin*.7f) {
|
||||
CV_LOG_WARNING(NULL, "Marker border " << markerSeparation << " is less than 70% of ArUco pin size "
|
||||
<< onePin << ". Please increase markerSeparation or decrease markerLength for stable board detection");
|
||||
}
|
||||
size_t totalMarkers = (size_t) size.width*size.height;
|
||||
CV_Assert(ids.empty() || totalMarkers == ids.total());
|
||||
vector<vector<Point3f> > objPoints;
|
||||
@@ -541,7 +546,12 @@ CharucoBoard::CharucoBoard(const Size& size, float squareLength, float markerLen
|
||||
Board(new CharucoBoardImpl(dictionary, size, squareLength, markerLength)) {
|
||||
|
||||
CV_Assert(size.width > 1 && size.height > 1 && markerLength > 0 && squareLength > markerLength);
|
||||
|
||||
float onePin = markerLength / ((float)(dictionary.markerSize+2));
|
||||
float markerSeparation = (squareLength - markerLength)/2.f;
|
||||
if (markerSeparation < onePin*.7f) {
|
||||
CV_LOG_WARNING(NULL, "Marker border " << markerSeparation << " is less than 70% of ArUco pin size "
|
||||
<< onePin <<". Please increase markerSeparation or decrease markerLength for stable board detection");
|
||||
}
|
||||
ids.copyTo(impl->ids);
|
||||
|
||||
static_pointer_cast<CharucoBoardImpl>(impl)->createCharucoBoard();
|
||||
|
||||
@@ -35,6 +35,8 @@ static inline bool readWrite(DetectorParameters ¶ms, const FileNode* readNod
|
||||
check |= readWriteParameter("minMarkerDistanceRate", params.minMarkerDistanceRate, readNode, writeStorage);
|
||||
check |= readWriteParameter("cornerRefinementMethod", params.cornerRefinementMethod, readNode, writeStorage);
|
||||
check |= readWriteParameter("cornerRefinementWinSize", params.cornerRefinementWinSize, readNode, writeStorage);
|
||||
check |= readWriteParameter("relativeCornerRefinmentWinSize", params.relativeCornerRefinmentWinSize, readNode,
|
||||
writeStorage);
|
||||
check |= readWriteParameter("cornerRefinementMaxIterations", params.cornerRefinementMaxIterations,
|
||||
readNode, writeStorage);
|
||||
check |= readWriteParameter("cornerRefinementMinAccuracy", params.cornerRefinementMinAccuracy,
|
||||
@@ -692,7 +694,7 @@ static void _identifyCandidates(InputArray grey,
|
||||
|
||||
/**
|
||||
* Line fitting A * B = C :: Called from function refineCandidateLines
|
||||
* @param nContours, contour-container
|
||||
* @param nContours contour-container
|
||||
*/
|
||||
static Point3f _interpolate2Dline(const vector<Point2f>& nContours){
|
||||
CV_Assert(nContours.size() >= 2);
|
||||
@@ -748,10 +750,8 @@ static Point2f _getCrossPoint(Point3f nLine1, Point3f nLine2){
|
||||
|
||||
/**
|
||||
* Refine Corners using the contour vector :: Called from function detectMarkers
|
||||
* @param nContours, contour-container
|
||||
* @param nCorners, candidate Corners
|
||||
* @param camMatrix, cameraMatrix input 3x3 floating-point camera matrix
|
||||
* @param distCoeff, distCoeffs vector of distortion coefficient
|
||||
* @param nContours contour-container
|
||||
* @param nCorners candidate Corners
|
||||
*/
|
||||
static void _refineCandidateLines(vector<Point>& nContours, vector<Point2f>& nCorners){
|
||||
vector<Point2f> contour2f(nContours.begin(), nContours.end());
|
||||
@@ -847,6 +847,16 @@ struct ArucoDetector::ArucoDetectorImpl {
|
||||
const RefineParameters& _refineParams): dictionary(_dictionary),
|
||||
detectorParams(_detectorParams), refineParams(_refineParams) {}
|
||||
|
||||
float getAverageArucoPinSize(vector<Point2f> markerCorners) {
|
||||
float averageArucoModuleSize = 0.f;
|
||||
int numPins = dictionary.markerSize + detectorParams.markerBorderBits * 2;
|
||||
for (size_t i = 0ull; i < markerCorners.size(); i++) {
|
||||
averageArucoModuleSize += sqrt(normL2Sqr<float>(Point2f(markerCorners[i] - markerCorners[(i+1ull)%markerCorners.size()])));
|
||||
}
|
||||
averageArucoModuleSize /= ((float)markerCorners.size()*numPins);
|
||||
return averageArucoModuleSize;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
ArucoDetector::ArucoDetector(const Dictionary &_dictionary,
|
||||
@@ -951,13 +961,15 @@ void ArucoDetector::detectMarkers(InputArray _image, OutputArrayOfArrays _corner
|
||||
const float scale_init = (float) grey_pyramid[closest_pyr_image_idx].cols / grey.cols;
|
||||
findCornerInPyrImage(scale_init, closest_pyr_image_idx, grey_pyramid, Mat(candidates[i]), detectorParams);
|
||||
}
|
||||
else
|
||||
cornerSubPix(grey, Mat(candidates[i]),
|
||||
Size(detectorParams.cornerRefinementWinSize, detectorParams.cornerRefinementWinSize),
|
||||
Size(-1, -1),
|
||||
TermCriteria(TermCriteria::MAX_ITER | TermCriteria::EPS,
|
||||
detectorParams.cornerRefinementMaxIterations,
|
||||
detectorParams.cornerRefinementMinAccuracy));
|
||||
else {
|
||||
int cornerRefinementWinSize = std::max(1, cvRound(detectorParams.relativeCornerRefinmentWinSize*
|
||||
arucoDetectorImpl->getAverageArucoPinSize(candidates[i])));
|
||||
cornerRefinementWinSize = min(cornerRefinementWinSize, detectorParams.cornerRefinementWinSize);
|
||||
cornerSubPix(grey, Mat(candidates[i]), Size(cornerRefinementWinSize, cornerRefinementWinSize), Size(-1, -1),
|
||||
TermCriteria(TermCriteria::MAX_ITER | TermCriteria::EPS,
|
||||
detectorParams.cornerRefinementMaxIterations,
|
||||
detectorParams.cornerRefinementMinAccuracy));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1223,8 +1235,13 @@ void ArucoDetector::refineDetectedMarkers(InputArray _image, const Board& _board
|
||||
CV_Assert(detectorParams.cornerRefinementWinSize > 0 &&
|
||||
detectorParams.cornerRefinementMaxIterations > 0 &&
|
||||
detectorParams.cornerRefinementMinAccuracy > 0);
|
||||
|
||||
std::vector<Point2f> marker(closestRotatedMarker.begin<Point2f>(), closestRotatedMarker.end<Point2f>());
|
||||
int cornerRefinementWinSize = std::max(1, cvRound(detectorParams.relativeCornerRefinmentWinSize*
|
||||
arucoDetectorImpl->getAverageArucoPinSize(marker)));
|
||||
cornerRefinementWinSize = min(cornerRefinementWinSize, detectorParams.cornerRefinementWinSize);
|
||||
cornerSubPix(grey, closestRotatedMarker,
|
||||
Size(detectorParams.cornerRefinementWinSize, detectorParams.cornerRefinementWinSize),
|
||||
Size(cornerRefinementWinSize, cornerRefinementWinSize),
|
||||
Size(-1, -1), TermCriteria(TermCriteria::MAX_ITER | TermCriteria::EPS,
|
||||
detectorParams.cornerRefinementMaxIterations,
|
||||
detectorParams.cornerRefinementMinAccuracy));
|
||||
|
||||
@@ -27,13 +27,12 @@ struct CharucoDetector::CharucoDetectorImpl {
|
||||
bool checkBoard(InputArrayOfArrays markerCorners, InputArray markerIds, InputArray charucoCorners, InputArray charucoIds) {
|
||||
vector<Mat> mCorners;
|
||||
markerCorners.getMatVector(mCorners);
|
||||
const Mat& mIds = markerIds.getMat();
|
||||
|
||||
const Mat& chCorners = charucoCorners.getMat();
|
||||
const Mat& chIds = charucoIds.getMat();
|
||||
const Mat mIds = markerIds.getMat();
|
||||
const Mat chCorners = charucoCorners.getMat();
|
||||
const Mat chIds = charucoIds.getMat();
|
||||
const vector<int>& boardIds = board.getIds();
|
||||
|
||||
const vector<vector<int> >& nearestMarkerIdx = board.getNearestMarkerIdx();
|
||||
const 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]
|
||||
|
||||
@@ -25,7 +25,7 @@ public:
|
||||
|
||||
net.setPreferableBackend(backend_id);
|
||||
net.setPreferableTarget(target_id);
|
||||
};
|
||||
}
|
||||
void alignCrop(InputArray _src_img, InputArray _face_mat, OutputArray _aligned_img) const override
|
||||
{
|
||||
Mat face_mat = _face_mat.getMat();
|
||||
@@ -39,13 +39,13 @@ public:
|
||||
}
|
||||
Mat warp_mat = getSimilarityTransformMatrix(src_point);
|
||||
warpAffine(_src_img, _aligned_img, warp_mat, Size(112, 112), INTER_LINEAR);
|
||||
};
|
||||
}
|
||||
void feature(InputArray _aligned_img, OutputArray _face_feature) override
|
||||
{
|
||||
Mat inputBolb = dnn::blobFromImage(_aligned_img, 1, Size(112, 112), Scalar(0, 0, 0), true, false);
|
||||
net.setInput(inputBolb);
|
||||
net.forward(_face_feature);
|
||||
};
|
||||
}
|
||||
double match(InputArray _face_feature1, InputArray _face_feature2, int dis_type) const override
|
||||
{
|
||||
Mat face_feature1 = _face_feature1.getMat(), face_feature2 = _face_feature2.getMat();
|
||||
@@ -60,7 +60,7 @@ public:
|
||||
throw std::invalid_argument("invalid parameter " + std::to_string(dis_type));
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
private:
|
||||
Mat getSimilarityTransformMatrix(float src[5][2]) const {
|
||||
|
||||
@@ -2728,6 +2728,58 @@ bool QRDecode::samplingForVersion()
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static bool checkASCIIcompatible(const uint8_t* str, const size_t size) {
|
||||
for (size_t i = 0; i < size; ++i) {
|
||||
uint8_t byte = str[i];
|
||||
if (byte >= 0x80)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool checkUTF8(const uint8_t* str, const size_t size) {
|
||||
for (size_t i = 0; i < size; ++i) {
|
||||
uint8_t byte = str[i];
|
||||
if (byte >= 0x80) {
|
||||
// Check that symbol is encoded correctly.
|
||||
|
||||
// Count number of bytes per symbol as a number of leading non-zero bits
|
||||
uint8_t numBytesPerSymbol;
|
||||
if ((byte & 0xe0) == 0xc0)
|
||||
numBytesPerSymbol = 2;
|
||||
else if ((byte & 0xf0) == 0xe0)
|
||||
numBytesPerSymbol = 3;
|
||||
else if ((byte & 0xf8) == 0xf0)
|
||||
numBytesPerSymbol = 4;
|
||||
else
|
||||
return false;
|
||||
|
||||
for (size_t j = 1; j < numBytesPerSymbol; ++j) {
|
||||
if (i + j >= size || (str[i + j] & 0xc0) != 0x80) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
i += numBytesPerSymbol - 1;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static std::string encodeUTF8_bytesarray(const uint8_t* str, const size_t size) {
|
||||
std::ostringstream res;
|
||||
for (size_t i = 0; i < size; ++i) {
|
||||
uint8_t byte = str[i];
|
||||
if (byte >= 0x80) {
|
||||
res << (char)(0xc0 | (byte >> 6));
|
||||
res << (char)(0x80 | (byte & 0x3f));
|
||||
} else {
|
||||
res << (char)byte;
|
||||
}
|
||||
}
|
||||
return res.str();
|
||||
}
|
||||
|
||||
bool QRDecode::decodingProcess()
|
||||
{
|
||||
#ifdef HAVE_QUIRC
|
||||
@@ -2757,11 +2809,58 @@ bool QRDecode::decodingProcess()
|
||||
|
||||
if (errorCode != 0) { return false; }
|
||||
|
||||
for (int i = 0; i < qr_code_data.payload_len; i++)
|
||||
CV_LOG_INFO(NULL, "QR: decoded with .version=" << qr_code_data.version << " .data_type=" << qr_code_data.data_type << " .eci=" << qr_code_data.eci << " .payload_len=" << qr_code_data.payload_len)
|
||||
|
||||
switch (qr_code_data.data_type)
|
||||
{
|
||||
result_info += qr_code_data.payload[i];
|
||||
case QUIRC_DATA_TYPE_NUMERIC:
|
||||
if (!checkASCIIcompatible(qr_code_data.payload, qr_code_data.payload_len)) {
|
||||
CV_LOG_INFO(NULL, "QR: DATA_TYPE_NUMERIC payload must be ACSII compatible string");
|
||||
return false;
|
||||
}
|
||||
result_info.assign((const char*)qr_code_data.payload, qr_code_data.payload_len);
|
||||
return true;
|
||||
case QUIRC_DATA_TYPE_ALPHA:
|
||||
if (!checkASCIIcompatible(qr_code_data.payload, qr_code_data.payload_len)) {
|
||||
CV_LOG_INFO(NULL, "QR: DATA_TYPE_ALPHA payload must be ASCII compatible string");
|
||||
return false;
|
||||
}
|
||||
result_info.assign((const char*)qr_code_data.payload, qr_code_data.payload_len);
|
||||
return true;
|
||||
case QUIRC_DATA_TYPE_BYTE:
|
||||
// https://en.wikipedia.org/wiki/Extended_Channel_Interpretation
|
||||
if (qr_code_data.eci == QUIRC_ECI_UTF_8) {
|
||||
CV_LOG_INFO(NULL, "QR: payload ECI is UTF-8");
|
||||
if (!checkUTF8(qr_code_data.payload, qr_code_data.payload_len)) {
|
||||
CV_LOG_INFO(NULL, "QUIRC_DATA_TYPE_BYTE with UTF-8 ECI must be UTF-8 compatible string");
|
||||
return false;
|
||||
}
|
||||
result_info.assign((const char*)qr_code_data.payload, qr_code_data.payload_len);
|
||||
} else if (qr_code_data.eci == 25/*ECI_UTF_16BE*/) {
|
||||
CV_LOG_INFO(NULL, "QR: UTF-16BE ECI is not supported");
|
||||
return false;
|
||||
} else if (checkASCIIcompatible(qr_code_data.payload, qr_code_data.payload_len)) {
|
||||
CV_LOG_INFO(NULL, "QR: payload is ASCII compatible (special handling for symbols encoding is not needed)");
|
||||
result_info.assign((const char*)qr_code_data.payload, qr_code_data.payload_len);
|
||||
} else {
|
||||
if (checkUTF8(qr_code_data.payload, qr_code_data.payload_len)) {
|
||||
CV_LOG_INFO(NULL, "QR: payload QUIRC_DATA_TYPE_BYTE is UTF-8 compatible, return as-is");
|
||||
result_info.assign((const char*)qr_code_data.payload, qr_code_data.payload_len);
|
||||
} else {
|
||||
CV_LOG_INFO(NULL, "QR: assume 1-byte per symbol encoding");
|
||||
result_info = encodeUTF8_bytesarray(qr_code_data.payload, qr_code_data.payload_len);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
case QUIRC_DATA_TYPE_KANJI:
|
||||
// FIXIT BUG: we must return UTF-8 compatible string
|
||||
CV_LOG_WARNING(NULL, "QR: Kanji is not supported properly");
|
||||
result_info.assign((const char*)qr_code_data.payload, qr_code_data.payload_len);
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
|
||||
CV_LOG_WARNING(NULL, "QR: unsupported QR data type");
|
||||
return false;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
|
||||
@@ -703,7 +703,7 @@ bool QRCodeEncoderImpl::stringToBits(const std::string& input_info)
|
||||
default:
|
||||
return encodeAuto(input_info, payload);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
void QRCodeEncoderImpl::eccGenerate(vector<vector<uint8_t> > &data_blocks, vector<vector<uint8_t> > &ecc_blocks)
|
||||
{
|
||||
|
||||
@@ -637,4 +637,25 @@ TEST_P(Objdetect_QRCode_detectAndDecodeMulti, decode_9_qrcodes_version7)
|
||||
|
||||
#endif // UPDATE_QRCODE_TEST_DATA
|
||||
|
||||
TEST(Objdetect_QRCode_detectAndDecode, utf8_output)
|
||||
{
|
||||
#ifndef HAVE_QUIRC
|
||||
throw SkipTestException("Quirc is required for decoding");
|
||||
#else
|
||||
const std::string name_current_image = "umlaut.png";
|
||||
const std::string root = "qrcode/";
|
||||
|
||||
std::string image_path = findDataFile(root + name_current_image);
|
||||
Mat src = imread(image_path);
|
||||
ASSERT_FALSE(src.empty()) << "Can't read image: " << image_path;
|
||||
|
||||
QRCodeDetector qrcode;
|
||||
std::vector<Point> corners;
|
||||
Mat straight;
|
||||
std::string decoded_info = qrcode.detectAndDecode(src, corners, straight);
|
||||
EXPECT_FALSE(decoded_info.empty());
|
||||
EXPECT_NE(decoded_info.find("M\xc3\xbcllheimstrasse"), std::string::npos);
|
||||
#endif // HAVE_QUIRC
|
||||
}
|
||||
|
||||
}} // namespace
|
||||
|
||||
Reference in New Issue
Block a user