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 Alekhin
2023-01-09 11:08:02 +00:00
880 changed files with 83958 additions and 9368 deletions
+365 -41
View File
@@ -8,6 +8,7 @@
#include "precomp.hpp"
#include "opencv2/objdetect.hpp"
#include "opencv2/3d.hpp"
#include <opencv2/core/utils/logger.hpp>
#ifdef HAVE_QUIRC
#include "quirc.h"
@@ -15,7 +16,6 @@
#include <limits>
#include <cmath>
#include <iostream>
#include <queue>
#include <limits>
#include <map>
@@ -23,6 +23,7 @@
namespace cv
{
using std::vector;
using std::pair;
static bool checkQRInputImage(InputArray img, Mat& gray)
{
@@ -136,7 +137,7 @@ void QRDetect::init(const Mat& src, double eps_vertical_, double eps_horizontal_
const int width = cvRound(src.size().width * coeff_expansion);
const int height = cvRound(src.size().height * coeff_expansion);
Size new_size(width, height);
resize(src, barcode, new_size, 0, 0, INTER_LINEAR);
resize(src, barcode, new_size, 0, 0, INTER_LINEAR_EXACT);
}
else if (min_side > 512.0)
{
@@ -524,7 +525,7 @@ bool QRDetect::localization()
const int height = cvRound(bin_barcode.size().height * coeff_expansion);
Size new_size(width, height);
Mat intermediate;
resize(bin_barcode, intermediate, new_size, 0, 0, INTER_LINEAR);
resize(bin_barcode, intermediate, new_size, 0, 0, INTER_LINEAR_EXACT);
bin_barcode = intermediate.clone();
for (size_t i = 0; i < localization_points.size(); i++)
{
@@ -537,7 +538,7 @@ bool QRDetect::localization()
const int height = cvRound(bin_barcode.size().height / coeff_expansion);
Size new_size(width, height);
Mat intermediate;
resize(bin_barcode, intermediate, new_size, 0, 0, INTER_LINEAR);
resize(bin_barcode, intermediate, new_size, 0, 0, INTER_LINEAR_EXACT);
bin_barcode = intermediate.clone();
for (size_t i = 0; i < localization_points.size(); i++)
{
@@ -948,6 +949,7 @@ vector<Point2f> QRDetect::getQuadrilateral(vector<Point2f> angle_list)
return result_angle_list;
}
struct QRCodeDetector::Impl
{
public:
@@ -955,9 +957,13 @@ public:
~Impl() {}
double epsX, epsY;
vector<vector<Point2f>> alignmentMarkers;
vector<Point2f> updateQrCorners;
bool useAlignmentMarkers = true;
};
QRCodeDetector::QRCodeDetector() : p(new Impl) {}
QRCodeDetector::~QRCodeDetector() {}
void QRCodeDetector::setEpsX(double epsX) { p->epsX = epsX; }
@@ -981,6 +987,7 @@ bool QRCodeDetector::detect(InputArray in, OutputArray points) const
class QRDecode
{
public:
QRDecode(bool useAlignmentMarkers);
void init(const Mat &src, const vector<Point2f> &points);
Mat getIntermediateBarcode() { return intermediate; }
Mat getStraightBarcode() { return straight; }
@@ -988,9 +995,23 @@ public:
std::string getDecodeInformation() { return result_info; }
bool straightDecodingProcess();
bool curvedDecodingProcess();
vector<Point2f> alignment_coords;
float coeff_expansion = 1.f;
vector<Point2f> getOriginalPoints() {return original_points;}
bool useAlignmentMarkers;
protected:
bool updatePerspective();
double getNumModules();
Mat getHomography() {
CV_TRACE_FUNCTION();
vector<Point2f> perspective_points = {{0.f, 0.f}, {test_perspective_size, 0.f},
{test_perspective_size, test_perspective_size},
{0.f, test_perspective_size}};
vector<Point2f> pts = original_points;
return findHomography(pts, perspective_points);
}
bool updatePerspective(const Mat& H);
bool versionDefinition();
void detectAlignment();
bool samplingForVersion();
bool decodingProcess();
inline double pointPosition(Point2f a, Point2f b , Point2f c);
@@ -1019,6 +1040,7 @@ protected:
const static int NUM_SIDES = 2;
Mat original, bin_barcode, no_border_intermediate, intermediate, straight, curved_to_straight, test_image;
vector<Point2f> original_points;
Mat homography;
vector<Point2f> original_curved_points;
vector<Point> qrcode_locations;
vector<std::pair<size_t, Point> > closest_points;
@@ -1070,6 +1092,7 @@ float static getMinSideLen(const vector<Point2f> &points) {
return static_cast<float>(res);
}
void QRDecode::init(const Mat &src, const vector<Point2f> &points)
{
CV_TRACE_FUNCTION();
@@ -2180,6 +2203,8 @@ bool QRDecode::straightenQRCodeInParts()
pts.push_back(center_point);
Mat H = findHomography(pts, perspective_points);
if (H.empty())
return false;
Mat temp_intermediate(temporary_size, CV_8UC1);
warpPerspective(test_mask, temp_intermediate, H, temporary_size, INTER_NEAREST);
perspective_result += temp_intermediate;
@@ -2212,6 +2237,8 @@ bool QRDecode::straightenQRCodeInParts()
perspective_straight_points.push_back(Point2f(perspective_curved_size * 0.5f, perspective_curved_size * 0.5f));
Mat H = findHomography(original_curved_points, perspective_straight_points);
if (H.empty())
return false;
warpPerspective(inversion, temp_result, H, temporary_size, INTER_NEAREST, BORDER_REPLICATE);
no_border_intermediate = temp_result(Range(1, temp_result.rows), Range(1, temp_result.cols));
@@ -2250,33 +2277,124 @@ bool QRDecode::preparingCurvedQRCodes()
return true;
}
bool QRDecode::updatePerspective()
{
CV_TRACE_FUNCTION();
const Point2f centerPt = intersectionLines(original_points[0], original_points[2],
original_points[1], original_points[3]);
if (cvIsNaN(centerPt.x) || cvIsNaN(centerPt.y))
/**
* @param finderPattern 4 points of finder pattern markers, calculated by findPatternsVerticesPoints()
* @return true if the pattern has the correct side lengths
*/
static inline bool checkFinderPatternByAspect(const vector<Point> &finderPattern) {
if (finderPattern.size() != 4ull)
return false;
float sidesLen[4];
for (size_t i = 0; i < finderPattern.size(); i++) {
sidesLen[i] = (sqrt(normL2Sqr<float>(Point2f(finderPattern[i] - finderPattern[(i+1ull)%finderPattern.size()]))));
}
const float maxSide = max(max(sidesLen[0], sidesLen[1]), max(sidesLen[2], sidesLen[3]));
const float minSide = min(min(sidesLen[0], sidesLen[1]), min(sidesLen[2], sidesLen[3]));
const Size temporary_size(cvRound(test_perspective_size), cvRound(test_perspective_size));
const float patternMaxRelativeLen = .3f;
if (1.f - minSide / maxSide > patternMaxRelativeLen)
return false;
return true;
}
vector<Point2f> perspective_points;
perspective_points.push_back(Point2f(0.f, 0.f));
perspective_points.push_back(Point2f(test_perspective_size, 0.f));
/**
* @param finderPattern - 4 points of finder pattern markers, calculated by findPatternsVerticesPoints()
* @param cornerPointsQR - 4 corner points of QR code
* @return pair<int, int> first - the index in points of finderPattern closest to the corner of the QR code,
* second - the index in points of cornerPointsQR closest to the corner of finderPattern
*
* This function matches finder patterns to the corners of the QR code. Points of finder pattern calculated by
* findPatternsVerticesPoints() may be erroneous, so they are checked.
*/
static inline std::pair<int, int> matchPatternPoints(const vector<Point> &finderPattern,
const vector<Point2f>& cornerPointsQR) {
if (!checkFinderPatternByAspect(finderPattern))
return std::make_pair(-1, -1);
perspective_points.push_back(Point2f(test_perspective_size, test_perspective_size));
perspective_points.push_back(Point2f(0.f, test_perspective_size));
float distanceToOrig = normL2Sqr<float>(Point2f(finderPattern[0]) - cornerPointsQR[0]);
int closestFinderPatternV = 0;
int closetOriginalV = 0;
perspective_points.push_back(Point2f(test_perspective_size * 0.5f, test_perspective_size * 0.5f));
for (size_t i = 0ull; i < finderPattern.size(); i++) {
for (size_t j = 0ull; j < cornerPointsQR.size(); j++) {
const float tmp = normL2Sqr<float>(Point2f(finderPattern[i]) - cornerPointsQR[j]);
if (tmp < distanceToOrig) {
distanceToOrig = tmp;
closestFinderPatternV = (int)i;
closetOriginalV = (int)j;
}
}
}
distanceToOrig = sqrt(distanceToOrig);
vector<Point2f> pts = original_points;
pts.push_back(centerPt);
// check that the distance from the QR pattern to the corners of the QR code is small
const float originalQrSide = sqrt(normL2Sqr<float>(cornerPointsQR[0] - cornerPointsQR[1]))*0.5f +
sqrt(normL2Sqr<float>(cornerPointsQR[0] - cornerPointsQR[3]))*0.5f;
const float maxRelativeDistance = .1f;
Mat H = findHomography(pts, perspective_points);
Mat bin_original;
adaptiveThreshold(original, bin_original, 255, ADAPTIVE_THRESH_GAUSSIAN_C, THRESH_BINARY, 83, 2);
if (distanceToOrig/originalQrSide > maxRelativeDistance)
return std::make_pair(-1, -1);
return std::make_pair(closestFinderPatternV, closetOriginalV);
}
double QRDecode::getNumModules() {
vector<vector<Point>> finderPatterns;
double numModulesX = 0., numModulesY = 0.;
if (findPatternsVerticesPoints(finderPatterns)) {
double pattern_distance[4] = {0.,0.,0.,0.};
for (auto& pattern : finderPatterns) {
auto indexes = matchPatternPoints(pattern, original_points);
if (indexes == std::make_pair(-1, -1))
return 0.;
Point2f vf[4] = {pattern[indexes.first % 4], pattern[(1+indexes.first) % 4],
pattern[(2+indexes.first) % 4], pattern[(3+indexes.first) % 4]};
for (int i = 1; i < 4; i++) {
pattern_distance[indexes.second] += (norm(vf[i] - vf[i-1]));
}
pattern_distance[indexes.second] += norm(vf[3] - vf[0]);
pattern_distance[indexes.second] /= 4.;
}
const double moduleSizeX = (pattern_distance[0] + pattern_distance[1])/(2.*7.);
const double moduleSizeY = (pattern_distance[0] + pattern_distance[3])/(2.*7.);
numModulesX = norm(original_points[1] - original_points[0])/moduleSizeX;
numModulesY = norm(original_points[3] - original_points[0])/moduleSizeY;
}
return (numModulesX + numModulesY)/2.;
}
// use code from https://stackoverflow.com/questions/13238704/calculating-the-position-of-qr-code-alignment-patterns
static inline vector<pair<int, int>> getAlignmentCoordinates(int version) {
if (version <= 1) return {};
int intervals = (version / 7) + 1; // Number of gaps between alignment patterns
int distance = 4 * version + 4; // Distance between first and last alignment pattern
int step = cvRound((double)distance / (double)intervals); // Round equal spacing to nearest integer
step += step & 0b1; // Round step to next even number
vector<int> coordinates((size_t)intervals + 1ull);
coordinates[0] = 6; // First coordinate is always 6 (can't be calculated with step)
for (int i = 1; i <= intervals; i++) {
coordinates[i] = (6 + distance - step * (intervals - i)); // Start right/bottom and go left/up by step*k
}
if (version >= 7) {
return {std::make_pair(coordinates.back(), coordinates.back()),
std::make_pair(coordinates.back(), coordinates[coordinates.size()-2]),
std::make_pair(coordinates[coordinates.size()-2], coordinates.back()),
std::make_pair(coordinates[coordinates.size()-2], coordinates[coordinates.size()-2]),
std::make_pair(coordinates[0], coordinates[1]),
std::make_pair(coordinates[1], coordinates[0]),
};
}
return {std::make_pair(coordinates.back(), coordinates.back())};
}
bool QRDecode::updatePerspective(const Mat& H)
{
if (H.empty())
return false;
homography = H;
Mat temp_intermediate;
warpPerspective(bin_original, temp_intermediate, H, temporary_size, INTER_NEAREST);
const Size temporary_size(cvRound(test_perspective_size), cvRound(test_perspective_size));
warpPerspective(bin_barcode, temp_intermediate, H, temporary_size, INTER_NEAREST);
no_border_intermediate = temp_intermediate(Range(1, temp_intermediate.rows), Range(1, temp_intermediate.cols));
const int border = cvRound(0.1 * test_perspective_size);
@@ -2285,7 +2403,7 @@ bool QRDecode::updatePerspective()
return true;
}
inline Point computeOffset(const vector<Point>& v)
static inline Point computeOffset(const vector<Point>& v)
{
// compute the width/height of convex hull
Rect areaBox = boundingRect(v);
@@ -2299,9 +2417,80 @@ inline Point computeOffset(const vector<Point>& v)
return offset;
}
// QR code with version 7 or higher has a special 18 bit version number code.
// @return std::pair<double, int> first - distance to estimatedVersion, second - version
/**
* @param numModules - estimated numModules
* @param estimatedVersion
* @return pair<double, int>, first - Hamming distance to 18 bit code, second - closest version
*
* QR code with version 7 or higher has a special 18 bit version number code:
* https://www.thonky.com/qr-code-tutorial/format-version-information
*/
static inline std::pair<double, int> getVersionByCode(double numModules, Mat qr, int estimatedVersion) {
const double moduleSize = qr.rows / numModules;
Point2d startVersionInfo1 = Point2d((numModules-8.-3.)*moduleSize, 0.);
Point2d endVersionInfo1 = Point2d((numModules-8.)*moduleSize, moduleSize*6.);
Point2d startVersionInfo2 = Point2d(0., (numModules-8.-3.)*moduleSize);
Point2d endVersionInfo2 = Point2d(moduleSize*6., (numModules-8.)*moduleSize);
Mat v1(qr, Rect2d(startVersionInfo1, endVersionInfo1));
Mat v2(qr, Rect2d(startVersionInfo2, endVersionInfo2));
const double thresh = 127.;
resize(v1, v1, Size(3, 6), 0., 0., INTER_AREA);
threshold(v1, v1, thresh, 255, THRESH_BINARY);
resize(v2, v2, Size(6, 3), 0., 0., INTER_AREA);
threshold(v2, v2, thresh, 255, THRESH_BINARY);
Mat version1, version2;
// convert version1 (top right version information block) and
// version2 (bottom left version information block) to version table format
// https://www.thonky.com/qr-code-tutorial/format-version-tables
rotate((255-v1)/255, version1, ROTATE_180), rotate(((255-v2)/255).t(), version2, ROTATE_180);
static uint8_t versionCodes[][18] = {{0,0,0,1,1,1,1,1,0,0,1,0,0,1,0,1,0,0},{0,0,1,0,0,0,0,1,0,1,1,0,1,1,1,1,0,0},
{0,0,1,0,0,1,1,0,1,0,1,0,0,1,1,0,0,1},{0,0,1,0,1,0,0,1,0,0,1,1,0,1,0,0,1,1},
{0,0,1,0,1,1,1,0,1,1,1,1,1,1,0,1,1,0},{0,0,1,1,0,0,0,1,1,1,0,1,1,0,0,0,1,0},
{0,0,1,1,0,1,1,0,0,0,0,1,0,0,0,1,1,1},{0,0,1,1,1,0,0,1,1,0,0,0,0,0,1,1,0,1},
{0,0,1,1,1,1,1,0,0,1,0,0,1,0,1,0,0,0},{0,1,0,0,0,0,1,0,1,1,0,1,1,1,1,0,0,0},
{0,1,0,0,0,1,0,1,0,0,0,1,0,1,1,1,0,1},{0,1,0,0,1,0,1,0,1,0,0,0,0,1,0,1,1,1},
{0,1,0,0,1,1,0,1,0,1,0,0,1,1,0,0,1,0},{0,1,0,1,0,0,1,0,0,1,1,0,1,0,0,1,1,0},
{0,1,0,1,0,1,0,1,1,0,1,0,0,0,0,0,1,1},{0,1,0,1,1,0,1,0,0,0,1,1,0,0,1,0,0,1},
{0,1,0,1,1,1,0,1,1,1,1,1,1,0,1,1,0,0},{0,1,1,0,0,0,1,1,1,0,1,1,0,0,0,1,0,0},
{0,1,1,0,0,1,0,0,0,1,1,1,1,0,0,0,0,1},{0,1,1,0,1,0,1,1,1,1,1,0,1,0,1,0,1,1},
{0,1,1,0,1,1,0,0,0,0,1,0,0,0,1,1,1,0},{0,1,1,1,0,0,1,1,0,0,0,0,0,1,1,0,1,0},
{0,1,1,1,0,1,0,0,1,1,0,0,1,1,1,1,1,1},{0,1,1,1,1,0,1,1,0,1,0,1,1,1,0,1,0,1},
{0,1,1,1,1,1,0,0,1,0,0,1,0,1,0,0,0,0},{1,0,0,0,0,0,1,0,0,1,1,1,0,1,0,1,0,1},
{1,0,0,0,0,1,0,1,1,0,1,1,1,1,0,0,0,0},{1,0,0,0,1,0,1,0,0,0,1,0,1,1,1,0,1,0},
{1,0,0,0,1,1,0,1,1,1,1,0,0,1,1,1,1,1},{1,0,0,1,0,0,1,0,1,1,0,0,0,0,1,0,1,1},
{1,0,0,1,0,1,0,1,0,0,0,0,1,0,1,1,1,0},{1,0,0,1,1,0,1,0,1,0,0,1,1,0,0,1,0,0},
{1,0,0,1,1,1,0,1,0,1,0,1,0,0,0,0,0,1},{1,0,1,0,0,0,1,1,0,0,0,1,1,0,1,0,0,1}
};
double minDist = 19.;
int bestVersion = -1;
const double penaltyFactor = 0.8;
for (int i = 0; i < (int)(sizeof(versionCodes)/sizeof(versionCodes[0])); i++) {
Mat currVers(Size(3, 6), CV_8UC1, versionCodes[i]);
// minimum hamming distance between version = 8
double tmp = norm(currVers, version1, NORM_HAMMING) + penaltyFactor*abs(estimatedVersion-i-7);
if (tmp < minDist) {
bestVersion = i+7;
minDist = tmp;
}
tmp = norm(currVers, version2, NORM_HAMMING) + penaltyFactor*abs(estimatedVersion-i-7);
if (tmp < minDist) {
bestVersion = i+7;
minDist = tmp;
}
}
return std::make_pair(minDist, bestVersion);
}
bool QRDecode::versionDefinition()
{
CV_TRACE_FUNCTION();
CV_LOG_DEBUG(NULL, "QR corners: " << original_points[0] << " " << original_points[1] << " " << original_points[2] <<
" " << original_points[3]);
LineIterator line_iter(intermediate, Point2f(0, 0), Point2f(test_perspective_size, test_perspective_size));
Point black_point = Point(0, 0);
for(int j = 0; j < line_iter.count; j++, ++line_iter)
@@ -2359,12 +2548,120 @@ bool QRDecode::versionDefinition()
transition_y++;
}
}
version = saturate_cast<uint8_t>((std::min(transition_x, transition_y) - 1) * 0.25 - 1);
if ( !( 0 < version && version <= 40 ) ) { return false; }
const int versionByTransition = saturate_cast<uint8_t>((std::min(transition_x, transition_y) - 1) * 0.25 - 1);
const int numModulesByTransition = 21 + (versionByTransition - 1) * 4;
const double numModulesByFinderPattern = getNumModules();
const double versionByFinderPattern = (numModulesByFinderPattern - 21.) * .25 + 1.;
bool useFinderPattern = false;
const double thresholdFinderPattern = 0.2;
const double roundingError = abs(numModulesByFinderPattern - cvRound(numModulesByFinderPattern));
if (cvRound(versionByFinderPattern) >= 1 && versionByFinderPattern <= 6. &&
transition_x != transition_y && roundingError < thresholdFinderPattern) {
useFinderPattern = true;
}
bool useCode = false;
int versionByCode = 7;
if (cvRound(versionByFinderPattern) >= 7 || versionByTransition >= 7) {
vector<std::pair<double, int>> versionAndDistances;
if (cvRound(versionByFinderPattern) >= 7) {
versionAndDistances.push_back(getVersionByCode(numModulesByFinderPattern, no_border_intermediate,
cvRound(versionByFinderPattern)));
}
if (versionByTransition >= 7) {
versionAndDistances.push_back(getVersionByCode(numModulesByTransition, no_border_intermediate,
versionByTransition));
}
const auto& bestVersion = min(versionAndDistances.front(), versionAndDistances.back());
double distanceByCode = bestVersion.first;
versionByCode = bestVersion.second;
if (distanceByCode < 5.) {
useCode = true;
}
}
if (useCode) {
CV_LOG_DEBUG(NULL, "Version type: useCode");
version = (uint8_t)versionByCode;
}
else if (useFinderPattern ) {
CV_LOG_DEBUG(NULL, "Version type: useFinderPattern");
version = (uint8_t)cvRound(versionByFinderPattern);
}
else {
CV_LOG_DEBUG(NULL, "Version type: useTransition");
version = (uint8_t)versionByTransition;
}
version_size = 21 + (version - 1) * 4;
if ( !(0 < version && version <= 40) ) { return false; }
CV_LOG_DEBUG(NULL, "QR version: " << (int)version);
return true;
}
void QRDecode::detectAlignment() {
vector<pair<int, int>> alignmentPositions = getAlignmentCoordinates(version);
if (alignmentPositions.size() > 0) {
vector<Point2f> perspective_points = {{0.f, 0.f}, {test_perspective_size, 0.f}, {0.f, test_perspective_size}};
vector<Point2f> object_points = {original_points[0], original_points[1], original_points[3]};
// create alignment image
static uint8_t alignmentMarker[25] = {
0, 0, 0, 0, 0,
0, 255, 255, 255, 0,
0, 255, 0, 255, 0,
0, 255, 255, 255, 0,
0, 0, 0, 0, 0
};
Mat alignmentMarkerMat(5, 5, CV_8UC1, alignmentMarker);
const float module_size = test_perspective_size / version_size;
Mat resizedAlignmentMarker;
resize(alignmentMarkerMat, resizedAlignmentMarker,
Size(cvRound(module_size * 5.f), cvRound(module_size * 5.f)), 0, 0, INTER_AREA);
const float module_offset = 1.9f;
const float offset = (module_size * (5 + module_offset * 2)); // 5 modules in alignment marker, 2 x module_offset modules in offset
for (const pair<int, int>& alignmentPos : alignmentPositions) {
const float left_top_x = (module_size * (alignmentPos.first - 2.f - module_offset)); // add offset
const float left_top_y = (module_size * (alignmentPos.second - 2.f - module_offset)); // add offset
Mat subImage(no_border_intermediate, Rect(cvRound(left_top_x), cvRound(left_top_y), cvRound(offset), cvRound(offset)));
Mat resTemplate;
matchTemplate(subImage, resizedAlignmentMarker, resTemplate, TM_CCOEFF_NORMED);
double minVal = 0., maxVal = 0.;
Point minLoc, maxLoc, matchLoc;
minMaxLoc(resTemplate, &minVal, &maxVal, &minLoc, &maxLoc);
CV_LOG_DEBUG(NULL, "Alignment maxVal: " << maxVal);
if (maxVal > 0.65) {
const float templateOffset = static_cast<float>(resizedAlignmentMarker.size().width) / 2.f;
Point2f alignmentCoord(Point2f(maxLoc.x + left_top_x + templateOffset, maxLoc.y + left_top_y + templateOffset));
alignment_coords.push_back(alignmentCoord);
perspectiveTransform(alignment_coords, alignment_coords, homography.inv());
CV_LOG_DEBUG(NULL, "Alignment coords: " << alignment_coords);
const float relativePosX = (alignmentPos.first + 0.5f) / version_size;
const float relativePosY = (alignmentPos.second + 0.5f) / version_size;
perspective_points.push_back({relativePosX * test_perspective_size, relativePosY * test_perspective_size});
object_points.push_back(alignment_coords.back());
}
}
if (object_points.size() > 3ull) {
double ransacReprojThreshold = 10.;
if (version == 2) { // in low version original_points[2] may be calculated more accurately using intersections method
object_points.push_back(original_points[2]);
ransacReprojThreshold = 5.; // set more strict ransacReprojThreshold
perspective_points.push_back({test_perspective_size, test_perspective_size});
}
Mat H = findHomography(object_points, perspective_points, RANSAC, ransacReprojThreshold);
if (H.empty())
return;
updatePerspective(H);
vector<Point2f> newCorner2 = {{test_perspective_size, test_perspective_size}};
perspectiveTransform(newCorner2, newCorner2, H.inv());
original_points[2] = newCorner2.front();
}
}
}
bool QRDecode::samplingForVersion()
{
CV_TRACE_FUNCTION();
@@ -2451,8 +2748,10 @@ bool QRDecode::decodingProcess()
bool QRDecode::straightDecodingProcess()
{
#ifdef HAVE_QUIRC
if (!updatePerspective()) { return false; }
if (!updatePerspective(getHomography())) { return false; }
if (!versionDefinition()) { return false; }
if (useAlignmentMarkers)
detectAlignment();
if (!samplingForVersion()) { return false; }
if (!decodingProcess()) { return false; }
return true;
@@ -2476,6 +2775,13 @@ bool QRDecode::curvedDecodingProcess()
#endif
}
QRDecode::QRDecode(bool _useAlignmentMarkers):
useAlignmentMarkers(_useAlignmentMarkers),
version(0),
version_size(0),
test_perspective_size(0.f)
{}
std::string QRCodeDetector::decode(InputArray in, InputArray points,
OutputArray straight_qrcode)
{
@@ -2488,7 +2794,7 @@ std::string QRCodeDetector::decode(InputArray in, InputArray points,
CV_Assert(src_points.size() == 4);
CV_CheckGT(contourArea(src_points), 0.0, "Invalid QR code source points");
QRDecode qrdec;
QRDecode qrdec(p->useAlignmentMarkers);
qrdec.init(inarr, src_points);
bool ok = qrdec.straightDecodingProcess();
@@ -2501,7 +2807,10 @@ std::string QRCodeDetector::decode(InputArray in, InputArray points,
{
qrdec.getStraightBarcode().convertTo(straight_qrcode, CV_8UC1);
}
if (ok && !decoded_info.empty()) {
p->alignmentMarkers = {qrdec.alignment_coords};
p->updateQrCorners = qrdec.getOriginalPoints();
}
return ok ? decoded_info : std::string();
}
@@ -2517,7 +2826,7 @@ cv::String QRCodeDetector::decodeCurved(InputArray in, InputArray points,
CV_Assert(src_points.size() == 4);
CV_CheckGT(contourArea(src_points), 0.0, "Invalid QR code source points");
QRDecode qrdec;
QRDecode qrdec(p->useAlignmentMarkers);
qrdec.init(inarr, src_points);
bool ok = qrdec.curvedDecodingProcess();
@@ -2742,7 +3051,7 @@ void QRDetectMulti::init(const Mat& src, double eps_vertical_, double eps_horizo
const int width = cvRound(src.size().width * coeff_expansion);
const int height = cvRound(src.size().height * coeff_expansion);
Size new_size(width, height);
resize(src, barcode, new_size, 0, 0, INTER_LINEAR);
resize(src, barcode, new_size, 0, 0, INTER_LINEAR_EXACT);
}
else if (min_side > 512.0)
{
@@ -3099,7 +3408,7 @@ int QRDetectMulti::findNumberLocalizationPoints(vector<Point2f>& tmp_localizatio
const int height = cvRound(bin_barcode.size().height * coeff_expansion);
Size new_size(width, height);
Mat intermediate;
resize(bin_barcode, intermediate, new_size, 0, 0, INTER_LINEAR);
resize(bin_barcode, intermediate, new_size, 0, 0, INTER_LINEAR_EXACT);
bin_barcode = intermediate.clone();
}
else if (purpose == ZOOMING)
@@ -3108,7 +3417,7 @@ int QRDetectMulti::findNumberLocalizationPoints(vector<Point2f>& tmp_localizatio
const int height = cvRound(bin_barcode.size().height / coeff_expansion);
Size new_size(width, height);
Mat intermediate;
resize(bin_barcode, intermediate, new_size, 0, 0, INTER_LINEAR);
resize(bin_barcode, intermediate, new_size, 0, 0, INTER_LINEAR_EXACT);
bin_barcode = intermediate.clone();
}
else
@@ -3126,7 +3435,7 @@ void QRDetectMulti::findQRCodeContours(vector<Point2f>& tmp_localization_points,
const int width = cvRound(bin_barcode.size().width);
const int height = cvRound(bin_barcode.size().height);
Size new_size(width, height);
resize(bar, bar, new_size, 0, 0, INTER_LINEAR);
resize(bar, bar, new_size, 0, 0, INTER_LINEAR_EXACT);
blur(bar, blur_image, Size(3, 3));
threshold(blur_image, threshold_output, 50, 255, THRESH_BINARY);
@@ -3552,15 +3861,15 @@ public:
else if (std::min(inarr.size().width, inarr.size().height) > 512)
{
const int min_side = std::min(inarr.size().width, inarr.size().height);
double coeff_expansion = min_side / 512;
const int width = cvRound(inarr.size().width / coeff_expansion);
const int height = cvRound(inarr.size().height / coeff_expansion);
qrdec[i].coeff_expansion = min_side / 512.f;
const int width = cvRound(inarr.size().width / qrdec[i].coeff_expansion);
const int height = cvRound(inarr.size().height / qrdec[i].coeff_expansion);
Size new_size(width, height);
Mat inarr2;
resize(inarr, inarr2, new_size, 0, 0, INTER_AREA);
for (size_t j = 0; j < 4; j++)
for (size_t j = 0ull; j < 4ull; j++)
{
src_points[i][j] /= static_cast<float>(coeff_expansion);
src_points[i][j] /= qrdec[i].coeff_expansion;
}
qrdec[i].init(inarr2, src_points[i]);
ok = qrdec[i].straightDecodingProcess();
@@ -3568,6 +3877,8 @@ public:
{
decoded_info[i] = qrdec[i].getDecodeInformation();
straight_barcode[i] = qrdec[i].getStraightBarcode();
for (size_t j = 0ull; j < qrdec[i].alignment_coords.size(); j++)
qrdec[i].alignment_coords[j] *= qrdec[i].coeff_expansion;
}
}
if (decoded_info[i].empty())
@@ -3608,7 +3919,7 @@ bool QRCodeDetector::decodeMulti(
}
}
CV_Assert(src_points.size() > 0);
vector<QRDecode> qrdec(src_points.size());
vector<QRDecode> qrdec(src_points.size(), p->useAlignmentMarkers);
vector<Mat> straight_barcode(src_points.size());
vector<std::string> info(src_points.size());
ParallelDecodeProcess parallelDecodeProcess(inarr, qrdec, info, straight_barcode, src_points);
@@ -3639,6 +3950,13 @@ bool QRCodeDetector::decodeMulti(
{
decoded_info.push_back(info[i]);
}
p->alignmentMarkers.resize(src_points.size());
p->updateQrCorners.resize(src_points.size()*4ull);
for (size_t i = 0ull; i < src_points.size(); i++) {
p->alignmentMarkers[i] = qrdec[i].alignment_coords;
for (size_t j = 0ull; j < 4ull; j++)
p->updateQrCorners[i*4ull+j] = qrdec[i].getOriginalPoints()[j] * qrdec[i].coeff_expansion;
}
if (!decoded_info.empty())
return true;
else
@@ -3669,7 +3987,13 @@ bool QRCodeDetector::detectAndDecodeMulti(
updatePointsResult(points_, points);
decoded_info.clear();
ok = decodeMulti(inarr, points, decoded_info, straight_qrcode);
updatePointsResult(points_, p->updateQrCorners);
return ok;
}
void QRCodeDetector::setUseAlignmentMarkers(bool useAlignmentMarkers) {
p->useAlignmentMarkers = useAlignmentMarkers;
}
} // namespace