mirror of
https://github.com/opencv/opencv.git
synced 2025-06-07 17:44:04 +08:00
Merge pull request #24364 from bagelbytes61:bugfix/qrcode-version-estimator
Bugfix/qrcode version estimator #24364 Fixes https://github.com/opencv/opencv/issues/24366 ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [x] The PR is proposed to the proper branch - [x] There is a reference to the original bug report and related work - [x] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [x] The feature is well documented and sample code can be built with the project CMake
This commit is contained in:
parent
8c10545d3c
commit
fad0dbb9ac
@ -196,17 +196,18 @@ protected:
|
||||
uint8_t total_num;
|
||||
vector<Mat> final_qrcodes;
|
||||
|
||||
Ptr<VersionInfo> version_info;
|
||||
Ptr<BlockParams> cur_ecc_params;
|
||||
const VersionInfo* version_info;
|
||||
const BlockParams* cur_ecc_params;
|
||||
|
||||
bool isNumeric(const std::string& input);
|
||||
bool isAlphaNumeric(const std::string& input);
|
||||
bool isNumeric(const std::string& input) const;
|
||||
bool isAlphaNumeric(const std::string& input) const;
|
||||
EncodeMode autoEncodeMode(const std::string &input) const ;
|
||||
bool encodeByte(const std::string& input, vector<uint8_t> &output);
|
||||
bool encodeAlpha(const std::string& input, vector<uint8_t> &output);
|
||||
bool encodeNumeric(const std::string& input, vector<uint8_t> &output);
|
||||
bool encodeECI(const std::string& input, vector<uint8_t> &output);
|
||||
bool encodeKanji(const std::string& input, vector<uint8_t> &output);
|
||||
bool encodeAuto(const std::string& input, vector<uint8_t> &output);
|
||||
bool encodeAuto(const std::string& input, vector<uint8_t> &output, EncodeMode *mode = nullptr);
|
||||
bool encodeStructure(const std::string& input, vector<uint8_t> &output);
|
||||
int eccLevelToCode(CorrectionLevel level);
|
||||
void padBitStream();
|
||||
@ -222,9 +223,9 @@ protected:
|
||||
void fillReserved(const vector<uint8_t> &format_array, Mat &masked);
|
||||
void maskData(const int mask_type_num, Mat &masked);
|
||||
void findAutoMaskType();
|
||||
bool estimateVersion(const int input_length, vector<int> &possible_version);
|
||||
bool estimateVersion(const int input_length, EncodeMode mode, vector<int> &possible_version);
|
||||
int versionAuto(const std::string &input_str);
|
||||
int findVersionCapacity(const int input_length, const int ecc, const int version_begin, const int version_end);
|
||||
int findVersionCapacity(const int input_length, const int ecc, const std::vector<int>& possible_versions);
|
||||
void generatingProcess(const std::string& input, Mat &qrcode);
|
||||
void generateQR(const std::string& input);
|
||||
};
|
||||
@ -247,17 +248,17 @@ int QRCodeEncoderImpl::eccLevelToCode(CorrectionLevel level)
|
||||
"CORRECT_LEVEL_L, CORRECT_LEVEL_M, CORRECT_LEVEL_Q, CORRECT_LEVEL_H." );
|
||||
}
|
||||
|
||||
int QRCodeEncoderImpl::findVersionCapacity(const int input_length, const int ecc, const int version_begin, const int version_end)
|
||||
int QRCodeEncoderImpl::findVersionCapacity(const int input_length, const int ecc, const std::vector<int>& possible_versions)
|
||||
{
|
||||
int data_codewords, version_index = -1;
|
||||
const int byte_len = 8;
|
||||
version_index = -1;
|
||||
|
||||
for (int i = version_begin; i < version_end; i++)
|
||||
for (int i : possible_versions)
|
||||
{
|
||||
Ptr<BlockParams> tmp_ecc_params = makePtr<BlockParams>(version_info_database[i].ecc[ecc]);
|
||||
data_codewords = tmp_ecc_params->data_codewords_in_G1 * tmp_ecc_params->num_blocks_in_G1 +
|
||||
tmp_ecc_params->data_codewords_in_G2 * tmp_ecc_params->num_blocks_in_G2;
|
||||
auto& tmp_ecc_params = version_info_database[i].ecc[ecc];
|
||||
data_codewords = tmp_ecc_params.data_codewords_in_G1 * tmp_ecc_params.num_blocks_in_G1 +
|
||||
tmp_ecc_params.data_codewords_in_G2 * tmp_ecc_params.num_blocks_in_G2;
|
||||
|
||||
if (data_codewords * byte_len >= input_length)
|
||||
{
|
||||
@ -268,53 +269,70 @@ int QRCodeEncoderImpl::findVersionCapacity(const int input_length, const int ecc
|
||||
return version_index;
|
||||
}
|
||||
|
||||
bool QRCodeEncoderImpl::estimateVersion(const int input_length, vector<int>& possible_version)
|
||||
static inline int getCapacity(int version, QRCodeEncoder::CorrectionLevel ecc_level, QRCodeEncoder::EncodeMode mode) {
|
||||
const int* capacity = version_capacity_database[version].ec_level[ecc_level].encoding_modes;
|
||||
switch (mode) {
|
||||
case QRCodeEncoder::EncodeMode::MODE_NUMERIC:
|
||||
return capacity[0];
|
||||
case QRCodeEncoder::EncodeMode::MODE_ALPHANUMERIC:
|
||||
return capacity[1];
|
||||
case QRCodeEncoder::EncodeMode::MODE_BYTE:
|
||||
return capacity[2];
|
||||
case QRCodeEncoder::EncodeMode::MODE_KANJI:
|
||||
return capacity[3];
|
||||
default:
|
||||
CV_Error(Error::StsNotImplemented, format("Unexpected mode %d", mode));
|
||||
}
|
||||
}
|
||||
|
||||
bool QRCodeEncoderImpl::estimateVersion(const int input_length, EncodeMode mode, vector<int>& possible_version)
|
||||
{
|
||||
possible_version.clear();
|
||||
if (input_length > version_capacity_database[40].ec_level[ecc_level].encoding_modes[1])
|
||||
|
||||
CV_Assert(mode != EncodeMode::MODE_AUTO);
|
||||
|
||||
if (input_length > getCapacity(MAX_VERSION, ecc_level, mode))
|
||||
{
|
||||
return false;
|
||||
if (input_length <= version_capacity_database[9].ec_level[ecc_level].encoding_modes[3])
|
||||
{
|
||||
possible_version.push_back(1);
|
||||
}
|
||||
else if (input_length <= version_capacity_database[9].ec_level[ecc_level].encoding_modes[1])
|
||||
|
||||
int version = MAX_VERSION;
|
||||
|
||||
for (; version > 0; --version)
|
||||
{
|
||||
possible_version.push_back(1);
|
||||
possible_version.push_back(2);
|
||||
if (input_length > getCapacity(version, ecc_level, mode)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (input_length <= version_capacity_database[26].ec_level[ecc_level].encoding_modes[3])
|
||||
|
||||
if (version < MAX_VERSION)
|
||||
{
|
||||
possible_version.push_back(2);
|
||||
version += 1;
|
||||
}
|
||||
else if (input_length <= version_capacity_database[26].ec_level[ecc_level].encoding_modes[1])
|
||||
|
||||
possible_version.push_back(version);
|
||||
|
||||
if (version < MAX_VERSION)
|
||||
{
|
||||
possible_version.push_back(2);
|
||||
possible_version.push_back(3);
|
||||
}
|
||||
else
|
||||
{
|
||||
possible_version.push_back(3);
|
||||
possible_version.push_back(version + 1);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int QRCodeEncoderImpl::versionAuto(const std::string& input_str)
|
||||
{
|
||||
vector<int> possible_version;
|
||||
estimateVersion((int)input_str.length(), possible_version);
|
||||
int tmp_version = 0;
|
||||
vector<uint8_t> payload_tmp;
|
||||
int version_range[5] = {0, 1, 10, 27, 41};
|
||||
for(size_t i = 0; i < possible_version.size(); i++)
|
||||
{
|
||||
int version_range_index = possible_version[i];
|
||||
EncodeMode mode;
|
||||
encodeAuto(input_str, payload_tmp, &mode);
|
||||
|
||||
encodeAuto(input_str, payload_tmp);
|
||||
tmp_version = findVersionCapacity((int)payload_tmp.size(), ecc_level,
|
||||
version_range[version_range_index], version_range[version_range_index + 1]);
|
||||
if(tmp_version != -1)
|
||||
break;
|
||||
vector<int> possible_version;
|
||||
if (!estimateVersion((int)input_str.length(), mode, possible_version)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
const auto tmp_version = findVersionCapacity((int)payload_tmp.size(), ecc_level, possible_version);
|
||||
|
||||
return tmp_version;
|
||||
}
|
||||
|
||||
@ -342,20 +360,23 @@ void QRCodeEncoderImpl::generateQR(const std::string &input)
|
||||
|
||||
std::string input_info = input.substr(segment_begin, segment_end);
|
||||
string_itr += segment_end;
|
||||
|
||||
int detected_version = versionAuto(input_info);
|
||||
CV_Assert(detected_version != -1);
|
||||
if (version_level == 0)
|
||||
version_level = detected_version;
|
||||
else if (version_level < detected_version)
|
||||
int tmp_version_level = version_level;
|
||||
if (detected_version == -1)
|
||||
CV_Error(Error::StsBadArg, "The given input exceeds the maximum capacity of a QR code with the selected encoding mode and error correction level " );
|
||||
else if (tmp_version_level == 0)
|
||||
tmp_version_level = detected_version;
|
||||
else if (tmp_version_level < detected_version)
|
||||
CV_Error(Error::StsBadArg, "The given version is not suitable for the given input string length ");
|
||||
|
||||
payload.clear();
|
||||
payload.reserve(MAX_PAYLOAD_LEN);
|
||||
format = vector<uint8_t> (15, 255);
|
||||
version_reserved = vector<uint8_t> (18, 255);
|
||||
version_size = (21 + (version_level - 1) * 4);
|
||||
version_info = makePtr<VersionInfo>(version_info_database[version_level]);
|
||||
cur_ecc_params = makePtr<BlockParams>(version_info->ecc[ecc_level]);
|
||||
version_size = (21 + (tmp_version_level - 1) * 4);
|
||||
version_info = &version_info_database[tmp_version_level];
|
||||
cur_ecc_params = &version_info->ecc[ecc_level];
|
||||
original = Mat(Size(version_size, version_size), CV_8UC1, Scalar(255));
|
||||
masked_data = original.clone();
|
||||
Mat qrcode = masked_data.clone();
|
||||
@ -613,7 +634,7 @@ bool QRCodeEncoderImpl::encodeStructure(const std::string& input, vector<uint8_t
|
||||
return encodeAuto(input, output);
|
||||
}
|
||||
|
||||
bool QRCodeEncoderImpl::isNumeric(const std::string& input)
|
||||
bool QRCodeEncoderImpl::isNumeric(const std::string& input) const
|
||||
{
|
||||
for (size_t i = 0; i < input.length(); i++)
|
||||
{
|
||||
@ -623,7 +644,7 @@ bool QRCodeEncoderImpl::isNumeric(const std::string& input)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool QRCodeEncoderImpl::isAlphaNumeric(const std::string& input)
|
||||
bool QRCodeEncoderImpl::isAlphaNumeric(const std::string& input) const
|
||||
{
|
||||
for (size_t i = 0; i < input.length(); i++)
|
||||
{
|
||||
@ -633,14 +654,56 @@ bool QRCodeEncoderImpl::isAlphaNumeric(const std::string& input)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool QRCodeEncoderImpl::encodeAuto(const std::string& input, vector<uint8_t>& output)
|
||||
QRCodeEncoder::EncodeMode QRCodeEncoderImpl::autoEncodeMode(const std::string &input) const
|
||||
{
|
||||
if (isNumeric(input))
|
||||
encodeNumeric(input, output);
|
||||
else if (isAlphaNumeric(input))
|
||||
encodeAlpha(input, output);
|
||||
else
|
||||
encodeByte(input, output);
|
||||
{
|
||||
return EncodeMode::MODE_NUMERIC;
|
||||
}
|
||||
|
||||
if (isAlphaNumeric(input))
|
||||
{
|
||||
return EncodeMode::MODE_ALPHANUMERIC;
|
||||
}
|
||||
|
||||
return EncodeMode::MODE_BYTE;
|
||||
}
|
||||
|
||||
bool QRCodeEncoderImpl::encodeAuto(const std::string& input, vector<uint8_t>& output, EncodeMode *mode)
|
||||
{
|
||||
const auto selected_mode = autoEncodeMode(input);
|
||||
|
||||
CV_Assert(selected_mode != EncodeMode::MODE_AUTO);
|
||||
|
||||
switch (selected_mode)
|
||||
{
|
||||
case EncodeMode::MODE_NUMERIC:
|
||||
encodeNumeric(input, output);
|
||||
break;
|
||||
case EncodeMode::MODE_ALPHANUMERIC:
|
||||
encodeAlpha(input, output);
|
||||
break;
|
||||
case EncodeMode::MODE_STRUCTURED_APPEND:
|
||||
encodeByte(input, output);
|
||||
break;
|
||||
case EncodeMode::MODE_BYTE:
|
||||
encodeByte(input, output);
|
||||
break;
|
||||
case EncodeMode::MODE_KANJI:
|
||||
encodeKanji(input, output);
|
||||
break;
|
||||
case EncodeMode::MODE_ECI:
|
||||
encodeECI(input, output);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (mode != nullptr)
|
||||
{
|
||||
*mode = selected_mode;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -548,4 +548,22 @@ TEST(Objdetect_QRCode_Encode_Decode, regression_issue22029)
|
||||
}
|
||||
}
|
||||
|
||||
// This test reproduces issue https://github.com/opencv/opencv/issues/24366 only in a loop
|
||||
TEST(Objdetect_QRCode_Encode_Decode, auto_version_pick)
|
||||
{
|
||||
cv::QRCodeEncoder::Params params;
|
||||
params.correction_level = cv::QRCodeEncoder::CORRECT_LEVEL_L;
|
||||
params.mode = cv::QRCodeEncoder::EncodeMode::MODE_AUTO;
|
||||
|
||||
cv::Ptr<cv::QRCodeEncoder> encoder = cv::QRCodeEncoder::create(params);
|
||||
|
||||
for (int len = 1; len < 19; len++) {
|
||||
std::string input;
|
||||
input.resize(len);
|
||||
cv::randu(Mat(1, len, CV_8U, &input[0]), 'a', 'z' + 1);
|
||||
cv::Mat qrcode;
|
||||
encoder->encode(input, qrcode);
|
||||
}
|
||||
}
|
||||
|
||||
}} // namespace
|
||||
|
Loading…
Reference in New Issue
Block a user