init - 初始化项目

This commit is contained in:
Lee Nony
2022-05-06 02:00:27 +08:00
commit 4edb98bf8a
126 changed files with 15370 additions and 0 deletions

11
README.md Normal file
View File

@@ -0,0 +1,11 @@
# OpenCV Contrib
OpenCV的扩展包目前只包含了一个微信二维码的包
## 用途
1. 支持更好的二维码扫描
## 代码修改
原代码只反回了二维码矩形,未返回二维码方向信息,主要添加此内容
## 参考
原地址: https://github.com/WeChatCV/opencv_contrib

View File

@@ -0,0 +1,30 @@
set(the_description "WeChat QR code Detector")
ocv_define_module(wechat_qrcode opencv_core opencv_imgproc opencv_dnn WRAP java objc python js)
# need to change
set(wechat_qrcode_commit_hash "a8b69ccc738421293254aec5ddb38bd523503252")
set(hash_detect_caffemodel "238e2b2d6f3c18d6c3a30de0c31e23cf")
set(hash_detect_prototxt "6fb4976b32695f9f5c6305c19f12537d")
set(hash_sr_caffemodel "cbfcd60361a73beb8c583eea7e8e6664")
set(hash_sr_prototxt "69db99927a70df953b471daaba03fbef")
set(model_types caffemodel prototxt)
set(model_names detect sr)
foreach(model_name ${model_names})
foreach(model_type ${model_types})
ocv_download(FILENAME ${model_name}.${model_type}
HASH ${hash_${model_name}_${model_type}}
URL
"${OPENCV_WECHAT_QRCODE_URL}"
"$ENV{OPENCV_WECHAT_QRCODE_URL}"
"https://raw.githubusercontent.com/WeChatCV/opencv_3rdparty/${wechat_qrcode_commit_hash}/"
DESTINATION_DIR "${CMAKE_BINARY_DIR}/downloads/wechat_qrcode"
ID "wechat_qrcode"
RELATIVE_URL
STATUS res)
if(NOT res)
message(WARNING "WeChatQRCode: Can't get ${model_name} ${model_type} file for wechat qrcode.")
endif()
endforeach()
endforeach()

View File

@@ -0,0 +1,253 @@
Tencent is pleased to support the open source community by making WeChat QRCode available.
Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved.
The below software in this distribution may have been modified by THL A29 Limited ("Tencent Modifications").
All Tencent Modifications are Copyright (C) THL A29 Limited.
WeChat QRCode is licensed under the Apache License Version 2.0, except for the third-party components listed below.
Terms of the Apache License Version 2.0
--------------------------------------------------------------------
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
Other dependencies and licenses:
Open Source Software Licensed under the Apache License Version 2.0:
--------------------------------------------------------------------
1. zxing
Copyright (c) zxing authors and contributors
Please note this software may have been modified by Tencent.
Terms of the Apache License Version 2.0:
--------------------------------------------------------------------
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
You must give any other recipients of the Work or Derivative Works a copy of this License; and
You must cause any modified files to carry prominent notices stating that You changed the files; and
You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License.
You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS

View File

@@ -0,0 +1,12 @@
WeChat QR code detector for detecting and parsing QR code.
================================================
WeChat QR code detector is a high-performance and lightweight QR code detect and decode library, which is contributed by WeChat Computer Vision Team (WeChatCV). It has been widely used in various Tencent applications, including WeChat, WeCom, QQ, QQ Browser, and so on. There are four primary features of WeChat QR code detector:
1. CNN-based QR code detector. Different from the traditional detector, we introduce a tiny CNN model for multiple code detection. The detector is based on SSD architecture with a MobileNetV2-like backbone, which is run on caffe inference framework.
2. CNN-based QR code enhancement. To improve the performance of tiny QR code, we design a lighten super-resolution CNN model for QR code, called QRSR. Depth-wise convolution, DenseNet concat and deconvolution are the core techniques in the QRSR model.
3. More robust finder pattern detection. Besides traditional horizontal line searching, we propose an area size based finder pattern detection method. we calculate the area size of black and white block to locate the finder pattern by the pre-computed connected cells.
4. Massive engineering optimization. Based on [zing-cpp](https://github.com/glassechidna/zxing-cpp), we conduct massive engineering optimization to boost the decoding success rate, such as trying more binarization methods, supporting N:1:3:1:1 finder pattern detection, finding more alignment pattern, clustering similar size finder pattern, and etc.

View File

@@ -0,0 +1,62 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
#ifndef __OPENCV_WECHAT_QRCODE_HPP__
#define __OPENCV_WECHAT_QRCODE_HPP__
#include "opencv2/core.hpp"
/** @defgroup wechat_qrcode WeChat QR code detector for detecting and parsing QR code.
*/
namespace cv {
namespace wechat_qrcode {
//! @addtogroup wechat_qrcode
//! @{
/**
* @brief WeChat QRCode includes two CNN-based models:
* A object detection model and a super resolution model.
* Object detection model is applied to detect QRCode with the bounding box.
* super resolution model is applied to zoom in QRCode when it is small.
*
*/
class CV_EXPORTS_W WeChatQRCode {
public:
/**
* @brief Initialize the WeChatQRCode.
* It includes two models, which are packaged with caffe format.
* Therefore, there are prototxt and caffe models (In total, four paramenters).
*
* @param detector_prototxt_path prototxt file path for the detector
* @param detector_caffe_model_path caffe model file path for the detector
* @param super_resolution_prototxt_path prototxt file path for the super resolution model
* @param super_resolution_caffe_model_path caffe file path for the super resolution model
*/
CV_WRAP WeChatQRCode(const String& detector_prototxt_path = "",
const String& detector_caffe_model_path = "",
const String& super_resolution_prototxt_path = "",
const String& super_resolution_caffe_model_path = "");
~WeChatQRCode(){};
/**
* @brief Both detects and decodes QR code.
* To simplify the usage, there is a only API: detectAndDecode
*
* @param img supports grayscale or color (BGR) image.
* @param points optional output array of vertices of the found QR code quadrangle. Will be
* empty if not found.
* @return list of decoded string.
*/
CV_WRAP std::vector<String> detectAndDecode(InputArray img,
OutputArrayOfArrays points = noArray());
protected:
class Impl;
Ptr<Impl> p;
};
//! @}
} // namespace wechat_qrcode
} // namespace cv
#endif // __OPENCV_WECHAT_QRCODE_HPP__

View File

@@ -0,0 +1,53 @@
import cv2
import sys
print(sys.argv[0])
print('A demo program of WeChat QRCode Detector:')
camIdx = -1
if len(sys.argv) > 1:
if sys.argv[1] == "-camera":
camIdx = int(sys.argv[2]) if len(sys.argv)>2 else 0
img = cv2.imread(sys.argv[1])
else:
print(" Usage: " + sys.argv[0] + " <input_image>")
exit(0)
# For python API generator, it follows the template: {module_name}_{class_name},
# so it is a little weird.
# The model is downloaded to ${CMAKE_BINARY_DIR}/downloads/wechat_qrcode if cmake runs without warnings,
# otherwise you can download them from https://github.com/WeChatCV/opencv_3rdparty/tree/wechat_qrcode.
try:
detector = cv2.wechat_qrcode_WeChatQRCode(
"detect.prototxt", "detect.caffemodel", "sr.prototxt", "sr.caffemodel")
except:
print("---------------------------------------------------------------")
print("Failed to initialize WeChatQRCode.")
print("Please, download 'detector.*' and 'sr.*' from")
print("https://github.com/WeChatCV/opencv_3rdparty/tree/wechat_qrcode")
print("and put them into the current directory.")
print("---------------------------------------------------------------")
exit(0)
prevstr = ""
if camIdx < 0:
res, points = detector.detectAndDecode(img)
print(res,points)
else:
cap = cv2.VideoCapture(camIdx)
while True:
res, img = cap.read()
if img.empty():
break
res, points = detector.detectAndDecode(img)
for t in res:
if t != prevstr:
print(t)
if res:
prevstr = res[-1]
cv2.imshow("image", img)
if cv2.waitKey(30) >= 0:
break
# When everything done, release the capture
cap.release()
cv2.destroyAllWindows()

View File

@@ -0,0 +1,70 @@
#include <iostream>
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
using namespace std;
using namespace cv;
#include <opencv2/wechat_qrcode.hpp>
int main(int argc, char* argv[]) {
cout << endl << argv[0] << endl << endl;
cout << "A demo program of WeChat QRCode Detector: " << endl;
Mat img;
int camIdx = -1;
if (argc > 1) {
bool live = strcmp(argv[1], "-camera") == 0;
if (live) {
camIdx = argc > 2 ? atoi(argv[2]) : 0;
} else {
img = imread(argv[1]);
}
} else {
cout << " Usage: " << argv[0] << " <input_image>" << endl;
return 0;
}
// The model is downloaded to ${CMAKE_BINARY_DIR}/downloads/wechat_qrcode if cmake runs without warnings,
// otherwise you can download them from https://github.com/WeChatCV/opencv_3rdparty/tree/wechat_qrcode.
Ptr<wechat_qrcode::WeChatQRCode> detector;
try {
detector = makePtr<wechat_qrcode::WeChatQRCode>("detect.prototxt", "detect.caffemodel",
"sr.prototxt", "sr.caffemodel");
} catch (const std::exception& e) {
cout <<
"\n---------------------------------------------------------------\n"
"Failed to initialize WeChatQRCode.\n"
"Please, download 'detector.*' and 'sr.*' from\n"
"https://github.com/WeChatCV/opencv_3rdparty/tree/wechat_qrcode\n"
"and put them into the current directory.\n"
"---------------------------------------------------------------\n";
cout << e.what() << endl;
return 0;
}
string prevstr = "";
vector<Mat> points;
if (camIdx < 0) {
auto res = detector->detectAndDecode(img, points);
for (const auto& t : res) cout << t << endl;
} else {
VideoCapture cap(camIdx);
for(;;) {
cap >> img;
if (img.empty())
break;
auto res = detector->detectAndDecode(img, points);
for (const auto& t : res) {
if (t != prevstr)
cout << t << endl;
}
if (!res.empty())
prevstr = res.back();
imshow("image", img);
if (waitKey(30) >= 0)
break;
}
}
return 0;
}

View File

@@ -0,0 +1,70 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
#include "precomp.hpp"
#include "binarizermgr.hpp"
#include "imgsource.hpp"
using zxing::Binarizer;
using zxing::LuminanceSource;
namespace cv {
namespace wechat_qrcode {
BinarizerMgr::BinarizerMgr() : m_iNowRotateIndex(0), m_iNextOnceBinarizer(-1) {
m_vecRotateBinarizer.push_back(Hybrid);
m_vecRotateBinarizer.push_back(FastWindow);
m_vecRotateBinarizer.push_back(SimpleAdaptive);
m_vecRotateBinarizer.push_back(AdaptiveThreshold);
}
BinarizerMgr::~BinarizerMgr() {}
zxing::Ref<Binarizer> BinarizerMgr::Binarize(zxing::Ref<LuminanceSource> source) {
BINARIZER binarizerIdx = m_vecRotateBinarizer[m_iNowRotateIndex];
if (m_iNextOnceBinarizer >= 0) {
binarizerIdx = (BINARIZER)m_iNextOnceBinarizer;
}
zxing::Ref<Binarizer> binarizer;
switch (binarizerIdx) {
case Hybrid:
binarizer = new zxing::HybridBinarizer(source);
break;
case FastWindow:
binarizer = new zxing::FastWindowBinarizer(source);
break;
case SimpleAdaptive:
binarizer = new zxing::SimpleAdaptiveBinarizer(source);
break;
case AdaptiveThreshold:
binarizer = new zxing::AdaptiveThresholdMeanBinarizer(source);
break;
default:
binarizer = new zxing::HybridBinarizer(source);
break;
}
return binarizer;
}
void BinarizerMgr::SwitchBinarizer() {
m_iNowRotateIndex = (m_iNowRotateIndex + 1) % m_vecRotateBinarizer.size();
}
int BinarizerMgr::GetCurBinarizer() {
if (m_iNextOnceBinarizer != -1) return m_iNextOnceBinarizer;
return m_vecRotateBinarizer[m_iNowRotateIndex];
}
void BinarizerMgr::SetNextOnceBinarizer(int iBinarizerIndex) {
m_iNextOnceBinarizer = iBinarizerIndex;
}
void BinarizerMgr::SetBinarizer(vector<BINARIZER> vecRotateBinarizer) {
m_vecRotateBinarizer = vecRotateBinarizer;
}
} // namespace wechat_qrcode
} // namespace cv

View File

@@ -0,0 +1,51 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
#ifndef __OPENCV_WECHAT_QRCODE_BINARIZERMGR_HPP__
#define __OPENCV_WECHAT_QRCODE_BINARIZERMGR_HPP__
#include "zxing/binarizer.hpp"
#include "zxing/common/binarizer/adaptive_threshold_mean_binarizer.hpp"
#include "zxing/common/counted.hpp"
#include "zxing/common/binarizer/fast_window_binarizer.hpp"
#include "zxing/common/binarizer/hybrid_binarizer.hpp"
#include "zxing/common/binarizer/simple_adaptive_binarizer.hpp"
#include "zxing/zxing.hpp"
namespace cv {
namespace wechat_qrcode {
class BinarizerMgr {
public:
enum BINARIZER {
Hybrid = 0,
FastWindow = 1,
SimpleAdaptive = 2,
AdaptiveThreshold = 3
};
public:
BinarizerMgr();
~BinarizerMgr();
zxing::Ref<zxing::Binarizer> Binarize(zxing::Ref<zxing::LuminanceSource> source);
void SwitchBinarizer();
int GetCurBinarizer();
void SetNextOnceBinarizer(int iBinarizerIndex);
void SetBinarizer(vector<BINARIZER> vecRotateBinarizer);
private:
int m_iNowRotateIndex;
int m_iNextOnceBinarizer;
vector<BINARIZER> m_vecRotateBinarizer;
};
} // namespace wechat_qrcode
} // namespace cv
#endif // __OPENCV_WECHAT_QRCODE_BINARIZERMGR_HPP__

View File

@@ -0,0 +1,103 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
#include "precomp.hpp"
#include "decodermgr.hpp"
using zxing::ArrayRef;
using zxing::BinaryBitmap;
using zxing::DecodeHints;
using zxing::ErrorHandler;
using zxing::LuminanceSource;
using zxing::Ref;
using zxing::Result;
using zxing::UnicomBlock;
namespace cv {
namespace wechat_qrcode {
int DecoderMgr::decodeImage(cv::Mat src, bool use_nn_detector, string& result, int pos[4][2]) {
int width = src.cols;
int height = src.rows;
if (width <= 20 || height <= 20)
return -1; // image data is not enough for providing reliable results
std::vector<uint8_t> scaled_img_data(src.data, src.data + width * height);
zxing::ArrayRef<uint8_t> scaled_img_zx =
zxing::ArrayRef<uint8_t>(new zxing::Array<uint8_t>(scaled_img_data));
zxing::Ref<zxing::Result> zx_result;
decode_hints_.setUseNNDetector(use_nn_detector);
Ref<ImgSource> source;
qbarUicomBlock_ = new UnicomBlock(width, height);
// Four Binarizers
int tryBinarizeTime = 4;
for (int tb = 0; tb < tryBinarizeTime; tb++) {
if (source == NULL || height * width > source->getMaxSize()) {
source = ImgSource::create(scaled_img_zx.data(), width, height);
} else {
source->reset(scaled_img_zx.data(), width, height);
}
int ret = TryDecode(source, zx_result);
if (!ret) {
result = zx_result->getText()->getText();
ArrayRef<Ref<zxing::ResultPoint>> pps = zx_result->getResultPoints();
for (int i = 0; pps && i < pps->size() && i < 4; i++) {
pos[i][0] = pps[i]->getX();
pos[i][1] = pps[i]->getY();
}
return ret;
}
// try different binarizers
binarizer_mgr_.SwitchBinarizer();
}
return -1;
}
int DecoderMgr::TryDecode(Ref<LuminanceSource> source, Ref<Result>& result) {
int res = -1;
string cell_result;
// get binarizer
zxing::Ref<zxing::Binarizer> binarizer = binarizer_mgr_.Binarize(source);
zxing::Ref<zxing::BinaryBitmap> binary_bitmap(new BinaryBitmap(binarizer));
binary_bitmap->m_poUnicomBlock = qbarUicomBlock_;
result = Decode(binary_bitmap, decode_hints_);
res = (result == NULL) ? 1 : 0;
//if (result) {
// ArrayRef<Ref<zxing::ResultPoint>> pps = result->getResultPoints();
// if (!pps) {
// //printf("Nony: %d\n", pps->size());*/
// printf("It is null................\n");
// }
// else {
// printf("It is not null................\n");
// printf("Nony: %d\n", pps->size());
// for (int i = 0; i < pps->size(); i++) {
// Ref<zxing::ResultPoint> rr = pps[i];
// printf("Nony: %f, %f\n", rr->getX(), rr->getY());
// }
// }
//}
if (res == 0) {
result->setBinaryMethod(int(binarizer_mgr_.GetCurBinarizer()));
}
return res;
}
Ref<Result> DecoderMgr::Decode(Ref<BinaryBitmap> image, DecodeHints hints) {
return reader_->decode(image, hints);
}
} // namespace wechat_qrcode
} // namespace cv

View File

@@ -0,0 +1,46 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
#ifndef __OPENCV_WECHAT_QRCODE_DECODERMGR_HPP__
#define __OPENCV_WECHAT_QRCODE_DECODERMGR_HPP__
// zxing
#include "zxing/binarizer.hpp"
#include "zxing/binarybitmap.hpp"
#include "zxing/decodehints.hpp"
#include "zxing/qrcode/qrcode_reader.hpp"
#include "zxing/result.hpp"
// qbar
#include "binarizermgr.hpp"
#include "imgsource.hpp"
namespace cv {
namespace wechat_qrcode {
class DecoderMgr {
public:
DecoderMgr() { reader_ = new zxing::qrcode::QRCodeReader(); };
~DecoderMgr(){};
int decodeImage(cv::Mat src, bool use_nn_detector, string& result, int pos[4][2]);
private:
zxing::Ref<zxing::UnicomBlock> qbarUicomBlock_;
zxing::DecodeHints decode_hints_;
zxing::Ref<zxing::qrcode::QRCodeReader> reader_;
BinarizerMgr binarizer_mgr_;
zxing::Ref<zxing::Result> Decode(zxing::Ref<zxing::BinaryBitmap> image,
zxing::DecodeHints hints);
int TryDecode(zxing::Ref<zxing::LuminanceSource> source, zxing::Ref<zxing::Result>& result);
};
} // namespace wechat_qrcode
} // namespace cv
#endif // __OPENCV_WECHAT_QRCODE_DECODERMGR_HPP__

View File

@@ -0,0 +1,66 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
#include "../precomp.hpp"
#include "align.hpp"
using std::max;
using std::min;
namespace cv {
namespace wechat_qrcode {
Align::Align() { rotate90_ = false; }
Mat Align::calcWarpMatrix(const Mat src, const Mat dst) {
M_ = getPerspectiveTransform(src, dst);
M_inv_ = M_.inv();
return M_;
}
vector<Point2f> Align::warpBack(const vector<Point2f> &dst_pts) {
vector<Point2f> src_pts;
for (size_t j = 0; j < dst_pts.size(); j++) {
auto src_x = (rotate90_ ? dst_pts[j].y : dst_pts[j].x) + crop_x_;
auto src_y = (rotate90_ ? dst_pts[j].x : dst_pts[j].y) + crop_y_;
src_pts.push_back(Point2f(src_x, src_y));
}
return src_pts;
}
Mat Align::crop(const Mat &inputImg, const int width, const int height) {
Mat warp_dst = Mat::zeros(height, width, inputImg.type());
warpPerspective(inputImg, warp_dst, M_, warp_dst.size(), INTER_LINEAR, BORDER_CONSTANT, 255);
return warp_dst;
}
Mat Align::crop(const Mat &inputImg, const Mat &srcPts, const float paddingW, const float paddingH,
const int minPadding) {
int x0 = srcPts.at<float>(0, 0);
int y0 = srcPts.at<float>(0, 1);
int x2 = srcPts.at<float>(2, 0);
int y2 = srcPts.at<float>(2, 1);
int width = x2 - x0 + 1;
int height = y2 - y0 + 1;
int padx = max(paddingW * width, static_cast<float>(minPadding));
int pady = max(paddingH * height, static_cast<float>(minPadding));
crop_x_ = max(x0 - padx, 0);
crop_y_ = max(y0 - pady, 0);
int end_x = min(x2 + padx, inputImg.cols - 1);
int end_y = min(y2 + pady, inputImg.rows - 1);
Rect crop_roi(crop_x_, crop_y_, end_x - crop_x_ + 1, end_y - crop_y_ + 1);
Mat dst = inputImg(crop_roi).clone();
if (rotate90_) dst = dst.t(); // transpose
return dst;
}
} // namespace wechat_qrcode
} // namespace cv

View File

@@ -0,0 +1,41 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
#ifndef __DETECTOR_ALIGN_HPP_
#define __DETECTOR_ALIGN_HPP_
#include <stdio.h>
#include <fstream>
#include "opencv2/core.hpp"
#include "opencv2/imgproc.hpp"
namespace cv {
namespace wechat_qrcode {
class Align {
public:
Align();
Mat calcWarpMatrix(const Mat src, const Mat dst);
std::vector<Point2f> warpBack(const std::vector<Point2f> &dst_pts);
Mat crop(const Mat &inputImg, const Mat &srcPts, const float paddingW, const float paddingH,
const int minPadding);
void setRotate90(bool v) { rotate90_ = v; }
private:
Mat crop(const Mat &inputImg, const int width, const int height);
Mat M_;
Mat M_inv_;
int crop_x_;
int crop_y_;
bool rotate90_;
};
} // namespace wechat_qrcode
} // namespace cv
#endif // __DETECTOR_ALIGN_HPP_

View File

@@ -0,0 +1,57 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
#include "../precomp.hpp"
#include "ssd_detector.hpp"
#define CLIP(x, x1, x2) max(x1, min(x, x2))
namespace cv {
namespace wechat_qrcode {
int SSDDetector::init(const string& proto_path, const string& model_path) {
net_ = dnn::readNetFromCaffe(proto_path, model_path);
return 0;
}
vector<Mat> SSDDetector::forward(Mat img, const int target_width, const int target_height) {
int img_w = img.cols;
int img_h = img.rows;
Mat input;
resize(img, input, Size(target_width, target_height), 0, 0, INTER_CUBIC);
dnn::blobFromImage(input, input, 1.0 / 255, Size(input.cols, input.rows), {0.0f, 0.0f, 0.0f},
false, false);
net_.setInput(input, "data");
auto prob = net_.forward("detection_output");
vector<Mat> point_list;
// the shape is (1,1,100,7)=>(batch,channel,count,dim)
for (int row = 0; row < prob.size[2]; row++) {
const float* prob_score = prob.ptr<float>(0, 0, row);
// prob_score[0] is not used.
// prob_score[1]==1 stands for qrcode
if (prob_score[1] == 1 && prob_score[2] > 1E-5) {
// add a safe score threshold due to https://github.com/opencv/opencv_contrib/issues/2877
// prob_score[2] is the probability of the qrcode, which is not used.
auto point = Mat(4, 2, CV_32FC1);
float x0 = CLIP(prob_score[3] * img_w, 0.0f, img_w - 1.0f);
float y0 = CLIP(prob_score[4] * img_h, 0.0f, img_h - 1.0f);
float x1 = CLIP(prob_score[5] * img_w, 0.0f, img_w - 1.0f);
float y1 = CLIP(prob_score[6] * img_h, 0.0f, img_h - 1.0f);
point.at<float>(0, 0) = x0;
point.at<float>(0, 1) = y0;
point.at<float>(1, 0) = x1;
point.at<float>(1, 1) = y0;
point.at<float>(2, 0) = x1;
point.at<float>(2, 1) = y1;
point.at<float>(3, 0) = x0;
point.at<float>(3, 1) = y1;
point_list.push_back(point);
}
}
return point_list;
}
} // namespace wechat_qrcode
} // namespace cv

View File

@@ -0,0 +1,31 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
#ifndef __DETECTOR_SSD_DETECTOR_HPP_
#define __DETECTOR_SSD_DETECTOR_HPP_
#include <stdio.h>
#include "opencv2/dnn.hpp"
#include "opencv2/imgproc.hpp"
namespace cv {
namespace wechat_qrcode {
class SSDDetector {
public:
SSDDetector(){};
~SSDDetector(){};
int init(const std::string& proto_path, const std::string& model_path);
std::vector<Mat> forward(Mat img, const int target_width, const int target_height);
private:
dnn::Net net_;
};
} // namespace wechat_qrcode
} // namespace cv
#endif // __DETECTOR_SSD_DETECTOR_HPP_

View File

@@ -0,0 +1,188 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
//
// Modified from ZXing. Copyright ZXing authors.
// Licensed under the Apache License, Version 2.0 (the "License").
#include "precomp.hpp"
#include "imgsource.hpp"
using zxing::ArrayRef;
using zxing::ByteMatrix;
using zxing::ErrorHandler;
using zxing::LuminanceSource;
using zxing::Ref;
namespace cv {
namespace wechat_qrcode {
// Initialize the ImgSource
ImgSource::ImgSource(unsigned char* pixels, int width, int height)
: Super(width, height) {
luminances = new unsigned char[width * height];
memset(luminances, 0, width * height);
rgbs = pixels;
dataWidth = width;
dataHeight = height;
left = 0;
top = 0;
// Make gray luminances first
makeGray();
}
// Added for crop function
ImgSource::ImgSource(unsigned char* pixels, int width, int height, int left_, int top_,
int cropWidth, int cropHeight,
ErrorHandler& err_handler)
: Super(cropWidth, cropHeight) {
rgbs = pixels;
dataWidth = width;
dataHeight = height;
left = left_;
top = top_;
// super(width, height);
if ((left_ + cropWidth) > dataWidth || (top_ + cropHeight) > dataHeight || top_ < 0 ||
left_ < 0) {
err_handler =
zxing::IllegalArgumentErrorHandler("Crop rectangle does not fit within image data.");
return;
}
luminances = new unsigned char[width * height];
// Make gray luminances first
makeGray();
}
ImgSource::~ImgSource() {
if (luminances != NULL) {
delete[] luminances;
}
}
Ref<ImgSource> ImgSource::create(unsigned char* pixels, int width, int height) {
return Ref<ImgSource>(new ImgSource(pixels, width, height));
}
Ref<ImgSource> ImgSource::create(unsigned char* pixels, int width, int height, int left, int top,
int cropWidth, int cropHeight,
zxing::ErrorHandler& err_handler) {
return Ref<ImgSource>(new ImgSource(pixels, width, height, left, top, cropWidth, cropHeight, err_handler));
}
void ImgSource::reset(unsigned char* pixels, int width, int height) {
rgbs = pixels;
left = 0;
top = 0;
setWidth(width);
setHeight(height);
dataWidth = width;
dataHeight = height;
makeGrayReset();
}
ArrayRef<char> ImgSource::getRow(int y, zxing::ArrayRef<char> row,
zxing::ErrorHandler& err_handler) const {
if (y < 0 || y >= getHeight()) {
err_handler = zxing::IllegalArgumentErrorHandler("Requested row is outside the image");
return ArrayRef<char>();
}
int width = getWidth();
if (row->data() == NULL || row->empty() || row->size() < width) {
row = zxing::ArrayRef<char>(width);
}
int offset = (y + top) * dataWidth + left;
char* rowPtr = &row[0];
arrayCopy(luminances, offset, rowPtr, 0, width);
return row;
}
/** This is a more efficient implementation. */
ArrayRef<char> ImgSource::getMatrix() const {
int width = getWidth();
int height = getHeight();
int area = width * height;
// If the caller asks for the entire underlying image, save the copy and
// give them the original data. The docs specifically warn that
// result.length must be ignored.
if (width == dataWidth && height == dataHeight) {
return _matrix;
}
zxing::ArrayRef<char> newMatrix = zxing::ArrayRef<char>(area);
int inputOffset = top * dataWidth + left;
// If the width matches the full width of the underlying data, perform a
// single copy.
if (width == dataWidth) {
arrayCopy(luminances, inputOffset, &newMatrix[0], 0, area);
return newMatrix;
}
// Otherwise copy one cropped row at a time.
for (int y = 0; y < height; y++) {
int outputOffset = y * width;
arrayCopy(luminances, inputOffset, &newMatrix[0], outputOffset, width);
inputOffset += dataWidth;
}
return newMatrix;
}
void ImgSource::makeGray() {
int area = dataWidth * dataHeight;
_matrix = zxing::ArrayRef<char>(area);
arrayCopy(rgbs, 0, &_matrix[0], 0, area);
}
void ImgSource::makeGrayReset() {
int area = dataWidth * dataHeight;
arrayCopy(rgbs, 0, &_matrix[0], 0, area);
}
void ImgSource::arrayCopy(unsigned char* src, int inputOffset, char* dst, int outputOffset,
int length) const {
const unsigned char* srcPtr = src + inputOffset;
char* dstPtr = dst + outputOffset;
memcpy(dstPtr, srcPtr, length * sizeof(unsigned char));
}
bool ImgSource::isCropSupported() const { return true; }
Ref<LuminanceSource> ImgSource::crop(int left_, int top_, int width, int height,
ErrorHandler& err_handler) const {
return ImgSource::create(rgbs, dataWidth, dataHeight, left + left_, top + top_, width, height, err_handler);
}
bool ImgSource::isRotateSupported() const { return false; }
Ref<LuminanceSource> ImgSource::rotateCounterClockwise(ErrorHandler& err_handler) const {
// Intentionally flip the left, top, width, and height arguments as
// needed. dataWidth and dataHeight are always kept unrotated.
int width = getWidth();
int height = getHeight();
return ImgSource::create(rgbs, dataWidth, dataHeight, top, left, height, width, err_handler);
}
Ref<ByteMatrix> ImgSource::getByteMatrix() const {
return Ref<ByteMatrix>(new ByteMatrix(getWidth(), getHeight(), getMatrix()));
}
} // namespace wechat_qrcode
} // namespace cv

View File

@@ -0,0 +1,63 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
//
// Modified from ZXing. Copyright ZXing authors.
// Licensed under the Apache License, Version 2.0 (the "License").
#ifndef __OPENCV_WECHAT_QRCODE_IMGSOURCE_HPP__
#define __OPENCV_WECHAT_QRCODE_IMGSOURCE_HPP__
#include "zxing/common/bytematrix.hpp"
#include "zxing/errorhandler.hpp"
#include "zxing/luminance_source.hpp"
namespace cv {
namespace wechat_qrcode {
class ImgSource : public zxing::LuminanceSource {
private:
typedef LuminanceSource Super;
zxing::ArrayRef<char> _matrix;
unsigned char* rgbs;
unsigned char* luminances;
int dataWidth;
int dataHeight;
int left;
int top;
void makeGray();
void makeGrayReset();
void arrayCopy(unsigned char* src, int inputOffset, char* dst, int outputOffset,
int length) const;
~ImgSource();
public:
ImgSource(unsigned char* pixels, int width, int height);
ImgSource(unsigned char* pixels, int width, int height, int left, int top, int cropWidth,
int cropHeight, zxing::ErrorHandler& err_handler);
static zxing::Ref<ImgSource> create(unsigned char* pixels, int width, int height);
static zxing::Ref<ImgSource> create(unsigned char* pixels, int width, int height, int left,
int top, int cropWidth, int cropHeight, zxing::ErrorHandler& err_handler);
void reset(unsigned char* pixels, int width, int height);
zxing::ArrayRef<char> getRow(int y, zxing::ArrayRef<char> row,
zxing::ErrorHandler& err_handler) const override;
zxing::ArrayRef<char> getMatrix() const override;
zxing::Ref<zxing::ByteMatrix> getByteMatrix() const override;
bool isCropSupported() const override;
zxing::Ref<LuminanceSource> crop(int left, int top, int width, int height,
zxing::ErrorHandler& err_handler) const override;
bool isRotateSupported() const override;
zxing::Ref<LuminanceSource> rotateCounterClockwise(
zxing::ErrorHandler& err_handler) const override;
int getMaxSize() { return dataHeight * dataWidth; }
};
} // namespace wechat_qrcode
} // namespace cv
#endif // __OPENCV_WECHAT_QRCODE_IMGSOURCE_HPP__

View File

@@ -0,0 +1,29 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
#ifndef __OPENCV_WECHAT_QRCODE_PRECOMP_HPP__
#define __OPENCV_WECHAT_QRCODE_PRECOMP_HPP__
#ifdef _MSC_VER
#pragma warning(disable: 4244)
#pragma warning(disable: 4267)
#endif
#include <stdint.h>
#include <stdio.h>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <memory>
#include <string>
#include <tuple>
#include <utility>
#include <vector>
#include <map>
#include "imgsource.hpp"
using std::ostringstream;
using std::string;
using std::vector;
#endif // __OPENCV_WECHAT_QRCODE_PRECOMP_HPP__

View File

@@ -0,0 +1,63 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
#include "../precomp.hpp"
#include "super_scale.hpp"
#define CLIP(x, x1, x2) max(x1, min(x, x2))
namespace cv {
namespace wechat_qrcode {
int SuperScale::init(const std::string &proto_path, const std::string &model_path) {
srnet_ = dnn::readNetFromCaffe(proto_path, model_path);
net_loaded_ = true;
return 0;
}
Mat SuperScale::processImageScale(const Mat &src, float scale, const bool &use_sr,
int sr_max_size) {
Mat dst = src;
if (scale == 1.0) { // src
return dst;
}
int width = src.cols;
int height = src.rows;
if (scale == 2.0) { // upsample
int SR_TH = sr_max_size;
if (use_sr && (int)sqrt(width * height * 1.0) < SR_TH && net_loaded_) {
int ret = superResoutionScale(src, dst);
if (ret == 0) return dst;
}
{ resize(src, dst, Size(), scale, scale, INTER_CUBIC); }
} else if (scale < 1.0) { // downsample
resize(src, dst, Size(), scale, scale, INTER_AREA);
}
return dst;
}
int SuperScale::superResoutionScale(const Mat &src, Mat &dst) {
Mat blob;
dnn::blobFromImage(src, blob, 1.0 / 255, Size(src.cols, src.rows), {0.0f}, false, false);
srnet_.setInput(blob);
auto prob = srnet_.forward();
dst = Mat(prob.size[2], prob.size[3], CV_8UC1);
for (int row = 0; row < prob.size[2]; row++) {
const float *prob_score = prob.ptr<float>(0, 0, row);
for (int col = 0; col < prob.size[3]; col++) {
float pixel = prob_score[col] * 255.0;
dst.at<uint8_t>(row, col) = static_cast<uint8_t>(CLIP(pixel, 0.0f, 255.0f));
}
}
return 0;
}
} // namespace wechat_qrcode
} // namespace cv

View File

@@ -0,0 +1,32 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
#ifndef __SCALE_SUPER_SCALE_HPP_
#define __SCALE_SUPER_SCALE_HPP_
#include <stdio.h>
#include "opencv2/dnn.hpp"
#include "opencv2/imgproc.hpp"
namespace cv {
namespace wechat_qrcode {
class SuperScale {
public:
SuperScale(){};
~SuperScale(){};
int init(const std::string &proto_path, const std::string &model_path);
Mat processImageScale(const Mat &src, float scale, const bool &use_sr, int sr_max_size = 160);
private:
dnn::Net srnet_;
bool net_loaded_ = false;
int superResoutionScale(const cv::Mat &src, cv::Mat &dst);
};
} // namespace wechat_qrcode
} // namespace cv
#endif // __SCALE_SUPER_SCALE_HPP_

View File

@@ -0,0 +1,221 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
#include "precomp.hpp"
#include "opencv2/wechat_qrcode.hpp"
#include "decodermgr.hpp"
#include "detector/align.hpp"
#include "detector/ssd_detector.hpp"
#include "opencv2/core.hpp"
#include "opencv2/core/utils/filesystem.hpp"
#include "scale/super_scale.hpp"
#include "zxing/result.hpp"
namespace cv {
namespace wechat_qrcode {
class WeChatQRCode::Impl {
public:
Impl() {}
~Impl() {}
/**
* @brief detect QR codes from the given image
*
* @param img supports grayscale or color (BGR) image.
* @return vector<Mat> detected QR code bounding boxes.
*/
std::vector<Mat> detect(const Mat& img);
/**
* @brief decode QR codes from detected points
*
* @param img supports grayscale or color (BGR) image.
* @param candidate_points detected points. we name it "candidate points" which means no
* all the qrcode can be decoded.
* @param points succussfully decoded qrcode with bounding box points.
* @return vector<string>
*/
std::vector<std::string> decode(const Mat& img, std::vector<Mat>& candidate_points,
std::vector<Mat>& points);
int applyDetector(const Mat& img, std::vector<Mat>& points);
Mat cropObj(const Mat& img, const Mat& point, Align& aligner);
std::vector<float> getScaleList(const int width, const int height);
std::shared_ptr<SSDDetector> detector_;
std::shared_ptr<SuperScale> super_resolution_model_;
bool use_nn_detector_, use_nn_sr_;
};
WeChatQRCode::WeChatQRCode(const String& detector_prototxt_path,
const String& detector_caffe_model_path,
const String& super_resolution_prototxt_path,
const String& super_resolution_caffe_model_path) {
p = makePtr<WeChatQRCode::Impl>();
if (!detector_caffe_model_path.empty() && !detector_prototxt_path.empty()) {
// initialize detector model (caffe)
p->use_nn_detector_ = true;
//CV_Assert(utils::fs::exists(detector_prototxt_path));
//CV_Assert(utils::fs::exists(detector_caffe_model_path));
p->detector_ = make_shared<SSDDetector>();
auto ret = p->detector_->init(detector_prototxt_path, detector_caffe_model_path);
CV_Assert(ret == 0);
} else {
p->use_nn_detector_ = false;
p->detector_ = NULL;
}
// initialize super_resolution_model
// it could also support non model weights by cubic resizing
// so, we initialize it first.
p->super_resolution_model_ = make_shared<SuperScale>();
if (!super_resolution_prototxt_path.empty() && !super_resolution_caffe_model_path.empty()) {
p->use_nn_sr_ = true;
// initialize dnn model (caffe format)
//CV_Assert(utils::fs::exists(super_resolution_prototxt_path));
//CV_Assert(utils::fs::exists(super_resolution_caffe_model_path));
auto ret = p->super_resolution_model_->init(super_resolution_prototxt_path,
super_resolution_caffe_model_path);
CV_Assert(ret == 0);
} else {
p->use_nn_sr_ = false;
}
}
std::vector<String> WeChatQRCode::detectAndDecode(InputArray img, OutputArrayOfArrays points) {
CV_Assert(!img.empty());
CV_CheckDepthEQ(img.depth(), CV_8U, "");
if (img.cols() <= 20 || img.rows() <= 20) {
return vector<string>(); // image data is not enough for providing reliable results
}
Mat input_img;
int incn = img.channels();
CV_Check(incn, incn == 1 || incn == 3 || incn == 4, "");
if (incn == 3 || incn == 4) {
cvtColor(img, input_img, COLOR_BGR2GRAY);
} else {
input_img = img.getMat();
}
auto candidate_points = p->detect(input_img);
auto res_points = vector<Mat>();
auto ret = p->decode(input_img, candidate_points, res_points);
// opencv type convert
vector<Mat> tmp_points;
if (points.needed()) {
for (size_t i = 0; i < res_points.size(); i++) {
Mat tmp_point;
tmp_points.push_back(tmp_point);
res_points[i].convertTo(((OutputArray)tmp_points[i]), CV_32FC2);
}
points.createSameSize(tmp_points, CV_32FC2);
points.assign(tmp_points);
}
return ret;
};
vector<string> WeChatQRCode::Impl::decode(const Mat& img, vector<Mat>& candidate_points,
vector<Mat>& points) {
if (candidate_points.size() == 0) {
return vector<string>();
}
vector<string> decode_results;
for (auto& point : candidate_points) {
Mat cropped_img;
if (use_nn_detector_) {
Align aligner;
cropped_img = cropObj(img, point, aligner);
} else {
cropped_img = img;
}
// scale_list contains different scale ratios
auto scale_list = getScaleList(cropped_img.cols, cropped_img.rows);
for (auto cur_scale : scale_list) {
Mat scaled_img =
super_resolution_model_->processImageScale(cropped_img, cur_scale, use_nn_sr_);
string result;
DecoderMgr decodemgr;
int pos[4][2] = { {0, 0}, {0, 0}, {0, 0}, {0, 0} };
auto ret = decodemgr.decodeImage(scaled_img, use_nn_detector_, result, pos);
if (ret == 0) {
decode_results.push_back(result);
points.push_back(point);
float* data = point.ptr<float>(0);
float x = *data;
float y = *(data + 1);
float oH = abs(point.at<float>(0, 1) - point.at<float>(3, 1));
float oW = abs(point.at<float>(0, 0) - point.at<float>(1, 0));
float wRatio = (float)oW / scaled_img.cols;
float hRatio = (float)oH / scaled_img.rows;
cv::Mat mat(4, 2, CV_32FC1);
for (int i = 0; i < 4; i++) {
float* dd = mat.ptr<float>(i);
*dd = pos[i][0] * wRatio + x;
*(dd + 1) = pos[i][1] * hRatio + y;
}
points.push_back(mat);
break;
}
}
}
return decode_results;
}
vector<Mat> WeChatQRCode::Impl::detect(const Mat& img) {
auto points = vector<Mat>();
if (use_nn_detector_) {
// use cnn detector
auto ret = applyDetector(img, points);
CV_Assert(ret == 0);
} else {
auto width = img.cols, height = img.rows;
// if there is no detector, use the full image as input
auto point = Mat(4, 2, CV_32FC1);
point.at<float>(0, 0) = 0;
point.at<float>(0, 1) = 0;
point.at<float>(1, 0) = width - 1;
point.at<float>(1, 1) = 0;
point.at<float>(2, 0) = width - 1;
point.at<float>(2, 1) = height - 1;
point.at<float>(3, 0) = 0;
point.at<float>(3, 1) = height - 1;
points.push_back(point);
}
return points;
}
int WeChatQRCode::Impl::applyDetector(const Mat& img, vector<Mat>& points) {
int img_w = img.cols;
int img_h = img.rows;
// hard code input size
int minInputSize = 400;
float resizeRatio = sqrt(img_w * img_h * 1.0 / (minInputSize * minInputSize));
int detect_width = img_w / resizeRatio;
int detect_height = img_h / resizeRatio;
points = detector_->forward(img, detect_width, detect_height);
return 0;
}
Mat WeChatQRCode::Impl::cropObj(const Mat& img, const Mat& point, Align& aligner) {
// make some padding to boost the qrcode details recall.
float padding_w = 0.1f, padding_h = 0.1f;
auto min_padding = 15;
auto cropped = aligner.crop(img, point, padding_w, padding_h, min_padding);
return cropped;
}
// empirical rules
vector<float> WeChatQRCode::Impl::getScaleList(const int width, const int height) {
if (width < 320 || height < 320) return {1.0, 2.0, 0.5};
if (width < 640 && height < 640) return {1.0, 0.5};
return {0.5, 1.0};
}
} // namespace wechat_qrcode
} // namespace cv

View File

@@ -0,0 +1,88 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
//
// Modified from ZXing. Copyright ZXing authors.
// Licensed under the Apache License, Version 2.0 (the "License").
#include "../precomp.hpp"
#include "binarizer.hpp"
namespace zxing {
Binarizer::Binarizer(Ref<LuminanceSource> source) : source_(source) {
dataWidth = source->getWidth();
dataHeight = source->getHeight();
width = dataWidth;
height = dataHeight;
matrix_ = NULL;
matrix0_ = NULL;
matrixInverted_ = NULL;
histogramBinarized = false;
usingHistogram = false;
}
Binarizer::~Binarizer() {}
Ref<LuminanceSource> Binarizer::getLuminanceSource() const { return source_; }
int Binarizer::getWidth() const {
return width;
}
int Binarizer::getHeight() const {
return height;
}
int Binarizer::rotateCounterClockwise() { return 0; }
int Binarizer::rotateCounterClockwise45() { return 0; }
Ref<BitMatrix> Binarizer::getInvertedMatrix(ErrorHandler& err_handler) {
if (!matrix_) {
return Ref<BitMatrix>();
}
if (matrixInverted_ == NULL) {
matrixInverted_ = new BitMatrix(matrix_->getWidth(), matrix_->getHeight(), err_handler);
matrixInverted_->copyOf(matrix_, err_handler);
matrixInverted_->flipAll();
}
return matrixInverted_;
}
// Return different black matrix according to cacheMode
Ref<BitMatrix> Binarizer::getBlackMatrix(ErrorHandler& err_handler) {
if (err_handler.ErrCode()) return Ref<BitMatrix>();
matrix_ = matrix0_;
return matrix_;
}
Ref<BitArray> Binarizer::getBlackRow(int y, Ref<BitArray> row, ErrorHandler& err_handler) {
if (!matrix_) {
matrix_ = getBlackMatrix(err_handler);
if (err_handler.ErrCode()) return Ref<BitArray>();
}
matrix_->getRow(y, row);
return row;
}
ArrayRef<BINARIZER_BLOCK> Binarizer::getBlockArray(int size) {
ArrayRef<BINARIZER_BLOCK> blocks(new Array<BINARIZER_BLOCK>(size));
for (int i = 0; i < blocks->size(); i++) {
blocks[i].sum = 0;
blocks[i].min = 0xFF;
blocks[i].max = 0;
}
return blocks;
}
} // namespace zxing

View File

@@ -0,0 +1,88 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
//
// Modified from ZXing. Copyright ZXing authors.
// Licensed under the Apache License, Version 2.0 (the "License").
#ifndef __ZXING_BINARIZER_HPP__
#define __ZXING_BINARIZER_HPP__
#include "common/bitarray.hpp"
#include "common/bitmatrix.hpp"
#include "common/counted.hpp"
#include "errorhandler.hpp"
#include "luminance_source.hpp"
#define ONED_ENABLE_LINE_BINARIZER
namespace zxing {
// typedef unsigned char uint8_t;
struct BINARIZER_BLOCK {
int sum;
int min;
int max;
int threshold;
// int average;
};
#ifdef ONED_ENABLE_LINE_BINARIZER
struct DecodeTipInfo {
int class_id;
};
#endif
class Binarizer : public Counted {
private:
Ref<LuminanceSource> source_;
bool histogramBinarized;
bool usingHistogram;
public:
explicit Binarizer(Ref<LuminanceSource> source);
virtual ~Binarizer();
// Added for store binarized result
int dataWidth;
int dataHeight;
int width;
int height;
// Store dynamicalli choice of which matrix is currently used
Ref<BitMatrix> matrix_;
// Restore 0 degree result
Ref<BitMatrix> matrix0_;
Ref<BitMatrix> matrixInverted_;
bool isRotateSupported() const { return false; }
// rotate counter clockwise 45 & 90 degree from binarized cache
int rotateCounterClockwise();
int rotateCounterClockwise45();
virtual Ref<BitMatrix> getBlackMatrix(ErrorHandler& err_handler);
virtual Ref<BitMatrix> getInvertedMatrix(ErrorHandler& err_handler);
virtual Ref<BitArray> getBlackRow(int y, Ref<BitArray> row, ErrorHandler& err_handler);
Ref<LuminanceSource> getLuminanceSource() const;
// virtual Ref<Binarizer> createBinarizer(Ref<LuminanceSource> source) = 0;
virtual Ref<Binarizer> createBinarizer(Ref<LuminanceSource> source) {
return Ref<Binarizer>(new Binarizer(source));
};
int getWidth() const;
int getHeight() const;
ArrayRef<BINARIZER_BLOCK> getBlockArray(int size);
};
} // namespace zxing
#endif // __ZXING_BINARIZER_HPP__

View File

@@ -0,0 +1,66 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
//
// Modified from ZXing. Copyright ZXing authors.
// Licensed under the Apache License, Version 2.0 (the "License").
#include "../precomp.hpp"
#include "binarybitmap.hpp"
using zxing::BinaryBitmap;
using zxing::BitArray;
using zxing::BitMatrix;
using zxing::ErrorHandler;
using zxing::LuminanceSource;
using zxing::Ref;
// VC++
using zxing::Binarizer;
BinaryBitmap::BinaryBitmap(Ref<Binarizer> binarizer) : binarizer_(binarizer) {}
BinaryBitmap::~BinaryBitmap() {}
Ref<BitArray> BinaryBitmap::getBlackRow(int y, Ref<BitArray> row, ErrorHandler& err_handler) {
Ref<BitArray> bitary = binarizer_->getBlackRow(y, row, err_handler);
if (err_handler.ErrCode()) return Ref<BitArray>();
return bitary;
}
Ref<BitMatrix> BinaryBitmap::getBlackMatrix(ErrorHandler& err_handler) {
Ref<BitMatrix> bitmtx = binarizer_->getBlackMatrix(err_handler);
if (err_handler.ErrCode()) return Ref<BitMatrix>();
return bitmtx;
}
Ref<BitMatrix> BinaryBitmap::getInvertedMatrix(ErrorHandler& err_handler) {
Ref<BitMatrix> bitmtx = binarizer_->getInvertedMatrix(err_handler);
if (err_handler.ErrCode()) return Ref<BitMatrix>();
return bitmtx;
}
int BinaryBitmap::getWidth() const { return binarizer_->getWidth(); }
int BinaryBitmap::getHeight() const { return binarizer_->getHeight(); }
Ref<LuminanceSource> BinaryBitmap::getLuminanceSource() const {
return binarizer_->getLuminanceSource();
}
bool BinaryBitmap::isCropSupported() const { return getLuminanceSource()->isCropSupported(); }
Ref<BinaryBitmap> BinaryBitmap::crop(int left, int top, int width, int height,
ErrorHandler& err_handler) {
return Ref<BinaryBitmap>(new BinaryBitmap(binarizer_->createBinarizer(
getLuminanceSource()->crop(left, top, width, height, err_handler))));
}
bool BinaryBitmap::isRotateSupported() const { return binarizer_->isRotateSupported(); }
Ref<BinaryBitmap> BinaryBitmap::rotateCounterClockwise() {
binarizer_->rotateCounterClockwise();
return Ref<BinaryBitmap>(new BinaryBitmap(binarizer_));
}

View File

@@ -0,0 +1,53 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
//
// Modified from ZXing. Copyright ZXing authors.
// Licensed under the Apache License, Version 2.0 (the "License").
#ifndef __ZXING_BINARYBITMAP_HPP__
#define __ZXING_BINARYBITMAP_HPP__
#include "binarizer.hpp"
#include "common/bitarray.hpp"
#include "common/bitmatrix.hpp"
#include "common/counted.hpp"
#include "common/unicomblock.hpp"
#include "errorhandler.hpp"
namespace zxing {
class BinaryBitmap : public Counted {
private:
Ref<Binarizer> binarizer_;
public:
explicit BinaryBitmap(Ref<Binarizer> binarizer);
virtual ~BinaryBitmap();
Ref<BitArray> getBlackRow(int y, Ref<BitArray> row, ErrorHandler& err_handler);
Ref<BitMatrix> getBlackMatrix(ErrorHandler& err_handler);
Ref<BitMatrix> getInvertedMatrix(ErrorHandler& err_handler);
Ref<LuminanceSource> getLuminanceSource() const;
Ref<UnicomBlock> m_poUnicomBlock;
int getWidth() const;
int getHeight() const;
bool isRotateSupported() const;
Ref<BinaryBitmap> rotateCounterClockwise();
bool isCropSupported() const;
Ref<BinaryBitmap> crop(int left, int top, int width, int height, ErrorHandler& err_handler);
bool isHistogramBinarized() const;
bool ifUseHistogramBinarize() const;
};
} // namespace zxing
#endif // __ZXING_BINARYBITMAP_HPP__

View File

@@ -0,0 +1,113 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
//
// Modified from ZXing. Copyright ZXing authors.
// Licensed under the Apache License, Version 2.0 (the "License").
#ifndef __ZXING_COMMON_ARRAY_HPP__
#define __ZXING_COMMON_ARRAY_HPP__
#include "counted.hpp"
namespace zxing {
template <typename T>
class Array : public Counted {
protected:
public:
std::vector<T> values_;
Array() {}
explicit Array(int n) : Counted(), values_(n, T()) {}
Array(T const *ts, int n) : Counted(), values_(ts, ts + n) {}
Array(T const *ts, T const *te) : Counted(), values_(ts, te) {}
Array(T v, int n) : Counted(), values_(n, v) {}
explicit Array(std::vector<T> &v) : Counted(), values_(v) {}
Array(Array<T> &other) : Counted(), values_(other.values_) {}
explicit Array(Array<T> *other) : Counted(), values_(other->values_) {}
virtual ~Array() {}
Array<T> &operator=(const Array<T> &other) {
values_ = other.values_;
return *this;
}
Array<T> &operator=(const std::vector<T> &array) {
values_ = array;
return *this;
}
T const &operator[](int i) const { return values_[i]; }
T &operator[](int i) { return values_[i]; }
int size() const { return values_.size(); }
bool empty() const { return values_.size() == 0; }
std::vector<T> const &values() const { return values_; }
std::vector<T> &values() { return values_; }
T *data() {
// return values_.data();
return &values_[0];
}
void append(T value) { values_.push_back(value); }
};
template <typename T>
class ArrayRef : public Counted {
private:
public:
Array<T> *array_;
ArrayRef() : array_(0) {}
explicit ArrayRef(int n) : array_(0) { reset(new Array<T>(n)); }
ArrayRef(T *ts, int n) : array_(0) { reset(new Array<T>(ts, n)); }
explicit ArrayRef(Array<T> *a) : array_(0) { reset(a); }
ArrayRef(const ArrayRef &other) : Counted(), array_(0) { reset(other.array_); }
~ArrayRef() {
if (array_) {
array_->release();
}
array_ = 0;
}
T const &operator[](int i) const { return (*array_)[i]; }
T &operator[](int i) { return (*array_)[i]; }
void reset(Array<T> *a) {
if (a) {
a->retain();
}
if (array_) {
array_->release();
}
array_ = a;
}
void reset(const ArrayRef<T> &other) { reset(other.array_); }
ArrayRef<T> &operator=(const ArrayRef<T> &other) {
reset(other);
return *this;
}
ArrayRef<T> &operator=(Array<T> *a) {
reset(a);
return *this;
}
Array<T> &operator*() const { return *array_; }
Array<T> *operator->() const { return array_; }
operator bool() const { return array_ != 0; }
bool operator!() const { return array_ == 0; }
T *data() { return array_->data(); }
void clear() {
T *ptr = array_->data();
memset(ptr, 0, array_->size());
}
void append(T value) { array_->append(value); }
};
} // namespace zxing
#endif // __ZXING_COMMON_ARRAY_HPP__

View File

@@ -0,0 +1,99 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
#include "../../../precomp.hpp"
#include "adaptive_threshold_mean_binarizer.hpp"
using zxing::AdaptiveThresholdMeanBinarizer;
namespace {
const int BLOCK_SIZE = 25;
const int Bias = 10;
} // namespace
AdaptiveThresholdMeanBinarizer::AdaptiveThresholdMeanBinarizer(Ref<LuminanceSource> source)
: GlobalHistogramBinarizer(source) {}
AdaptiveThresholdMeanBinarizer::~AdaptiveThresholdMeanBinarizer() {}
Ref<Binarizer> AdaptiveThresholdMeanBinarizer::createBinarizer(Ref<LuminanceSource> source) {
return Ref<Binarizer>(new AdaptiveThresholdMeanBinarizer(source));
}
Ref<BitArray> AdaptiveThresholdMeanBinarizer::getBlackRow(int y, Ref<BitArray> row,
ErrorHandler& err_handler) {
// First call binarize image in child class to get matrix0_ and binCache
if (!matrix0_) {
binarizeImage(err_handler);
if (err_handler.ErrCode()) return Ref<BitArray>();
}
// Call parent getBlackMatrix to get current matrix
return Binarizer::getBlackRow(y, row, err_handler);
}
Ref<BitMatrix> AdaptiveThresholdMeanBinarizer::getBlackMatrix(ErrorHandler& err_handler) {
// First call binarize image in child class to get matrix0_ and binCache
if (!matrix0_) {
binarizeImage(err_handler);
if (err_handler.ErrCode()) return Ref<BitMatrix>();
}
return Binarizer::getBlackMatrix(err_handler);
}
int AdaptiveThresholdMeanBinarizer::binarizeImage(ErrorHandler& err_handler) {
if (width >= BLOCK_SIZE && height >= BLOCK_SIZE) {
LuminanceSource& source = *getLuminanceSource();
Ref<BitMatrix> matrix(new BitMatrix(width, height, err_handler));
if (err_handler.ErrCode()) return -1;
auto src = (unsigned char*)source.getMatrix()->data();
auto dst = matrix->getPtr();
cv::Mat mDst;
mDst = cv::Mat::zeros(cv::Size(width, height), CV_8UC1);
TransBufferToMat(src, mDst, width, height);
cv::Mat result;
int bs = width / 10;
bs = bs + bs % 2 - 1;
if (!(bs % 2 == 1 && bs > 1)) return -1;
cv::adaptiveThreshold(mDst, result, 255, cv::ADAPTIVE_THRESH_GAUSSIAN_C, cv::THRESH_BINARY,
bs, Bias);
TransMatToBuffer(result, dst, width, height);
if (err_handler.ErrCode()) return -1;
matrix0_ = matrix;
} else {
matrix0_ = GlobalHistogramBinarizer::getBlackMatrix(err_handler);
if (err_handler.ErrCode()) return 1;
}
return 0;
}
int AdaptiveThresholdMeanBinarizer::TransBufferToMat(unsigned char* pBuffer, cv::Mat& mDst,
int nWidth, int nHeight) {
for (int j = 0; j < nHeight; ++j) {
unsigned char* data = mDst.ptr<unsigned char>(j);
unsigned char* pSubBuffer = pBuffer + (nHeight - 1 - j) * nWidth;
memcpy(data, pSubBuffer, nWidth);
}
return 0;
}
int AdaptiveThresholdMeanBinarizer::TransMatToBuffer(cv::Mat mSrc, unsigned char* ppBuffer,
int& nWidth, int& nHeight) {
nWidth = mSrc.cols;
// nWidth = ((nWidth + 3) / 4) * 4;
nHeight = mSrc.rows;
for (int j = 0; j < nHeight; ++j) {
unsigned char* pdi = ppBuffer + j * nWidth;
for (int z = 0; z < nWidth; ++z) {
int nj = nHeight - j - 1;
int value = *(uchar*)(mSrc.ptr<uchar>(nj) + z);
if (value > 120)
pdi[z] = 0;
else
pdi[z] = 1;
}
}
return 0;
}

View File

@@ -0,0 +1,38 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
#ifndef __ZXING_COMMON_ADAPTIVE_THRESHOLD_MEAN_BINARIZER_HPP__
#define __ZXING_COMMON_ADAPTIVE_THRESHOLD_MEAN_BINARIZER_HPP__
#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>
#include "../../binarizer.hpp"
#include "../../errorhandler.hpp"
#include "../bitarray.hpp"
#include "../bitmatrix.hpp"
#include "../bytematrix.hpp"
#include "global_histogram_binarizer.hpp"
namespace zxing {
class AdaptiveThresholdMeanBinarizer : public GlobalHistogramBinarizer {
public:
explicit AdaptiveThresholdMeanBinarizer(Ref<LuminanceSource> source);
virtual ~AdaptiveThresholdMeanBinarizer();
virtual Ref<BitMatrix> getBlackMatrix(ErrorHandler& err_handler) override;
virtual Ref<BitArray> getBlackRow(int y, Ref<BitArray> row, ErrorHandler& err_handler) override;
Ref<Binarizer> createBinarizer(Ref<LuminanceSource> source) override;
private:
int binarizeImage(ErrorHandler& err_handler);
int TransBufferToMat(unsigned char* pBuffer, cv::Mat& mDst, int nWidth, int nHeight);
int TransMatToBuffer(cv::Mat mSrc, unsigned char* ppBuffer, int& nWidth, int& nHeight);
};
} // namespace zxing
#endif // __ZXING_COMMON_ADAPTIVE_THRESHOLD_MEAN_BINARIZER_HPP__

View File

@@ -0,0 +1,285 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
//
// Modified from ZXing. Copyright ZXing authors.
// Licensed under the Apache License, Version 2.0 (the "License").
#include "../../../precomp.hpp"
#include "fast_window_binarizer.hpp"
using zxing::FastWindowBinarizer;
namespace {
const int BLOCK_SIZE = 6;
// const int BLOCK_SIZE = 8; // not as good as BLOCK_SIZE = 6
const float WINDOW_FRACTION = 0.13f;
static int min(int a, int b) { return a < b ? a : b; }
static int max(int a, int b) { return a > b ? a : b; }
} // namespace
FastWindowBinarizer::FastWindowBinarizer(Ref<LuminanceSource> source)
: GlobalHistogramBinarizer(source), matrix_(NULL), cached_row_(NULL) {
width = source->getWidth();
height = source->getHeight();
int aw = width / BLOCK_SIZE;
int ah = height / BLOCK_SIZE;
int ah2 = ah;
int ow2 = aw + 1;
_luminancesInt = new int[width * height];
_blockTotals = new int[ah * aw];
_totals = new int[(ah + 1) * (aw + 1)];
_rowTotals = new int[ah2 * ow2];
_internal = new unsigned int[(height + 1) * (width + 1)];
}
FastWindowBinarizer::~FastWindowBinarizer() {
delete[] _totals;
delete[] _blockTotals;
delete[] _luminancesInt;
delete[] _rowTotals;
delete[] _internal;
}
Ref<Binarizer> FastWindowBinarizer::createBinarizer(Ref<LuminanceSource> source) {
return Ref<Binarizer>(new FastWindowBinarizer(source));
}
/**
* Calculates the final BitMatrix once for all requests. This could be called
* once from the constructor instead, but there are some advantages to doing it
* lazily, such as making profiling easier, and not doing heavy lifting when
* callers don't expect it.
*/
Ref<BitMatrix> FastWindowBinarizer::getBlackMatrix(ErrorHandler& err_handler) {
if (!matrix0_) {
binarizeImage1(err_handler);
if (err_handler.ErrCode()) return Ref<BitMatrix>();
}
return Binarizer::getBlackMatrix(err_handler);
}
/**
* Calculate black row from BitMatrix
* If BitMatrix has been calculated then just get the row
* If BitMatrix has not been calculated then call getBlackMatrix first
*/
Ref<BitArray> FastWindowBinarizer::getBlackRow(int y, Ref<BitArray> row,
ErrorHandler& err_handler) {
if (!matrix0_) {
binarizeImage1(err_handler);
if (err_handler.ErrCode()) return Ref<BitArray>();
}
// Call parent getBlackMatrix to get current matrix
return Binarizer::getBlackRow(y, row, err_handler);
}
void FastWindowBinarizer::calcBlockTotals(int* luminancesInt, int* output, int aw, int ah) {
for (int by = 0; by < ah; by++) {
int ey = (by + 1) * BLOCK_SIZE;
for (int bx = 0; bx < aw; bx++) {
int t = 0;
for (int y = by * BLOCK_SIZE; y < ey; y++) {
int offset = y * width + bx * BLOCK_SIZE;
int ex = offset + BLOCK_SIZE;
for (; offset < ex; offset++) {
// int v = luminancesInt[offset] & 0xff;
t += luminancesInt[offset];
}
}
output[by * aw + bx] = t;
}
}
}
void FastWindowBinarizer::cumulative(int* data, int* output, int _width, int _height) {
int ah = _height;
int aw = _width;
int ow = _width + 1;
// int[][] totals = new int[ah + 1][aw + 1];
// int* rowTotals = new int[ah*ow];
for (int y = 0; y < ah; y++) {
int* row = _rowTotals + (y * ow);
int* rowdata = data + (y * aw);
int t = 0;
row[0] = t;
for (int x = 0; x < aw; x++) {
t += rowdata[x];
row[x + 1] = t;
}
}
for (int x = 0; x <= aw; x++) {
output[x] = 0; // First row
int t = 0;
for (int y = 0; y < ah; y++) {
t += _rowTotals[y * ow + x];
output[(y + 1) * ow + x] = t;
}
}
}
void FastWindowBinarizer::fastIntegral(const unsigned char* inputMatrix,
unsigned int* outputMatrix) {
// memset(outputMatrix,0,sizeof(int)*(height+1)*(width+1));
// unsigned int *columnSum = new unsigned int[width]; // sum of each column
// calculate integral of the first line
outputMatrix[0] = outputMatrix[width + 1] = 0;
for (int i = 0; i < width; i++) {
// columnSum[i]=inputMatrix[i];
outputMatrix[i + 1] = 0;
outputMatrix[width + 1 + i + 1] = outputMatrix[width + 1 + i] + inputMatrix[i];
}
for (int i = 1; i < height; i++) {
const unsigned char* psi = inputMatrix + i * width;
unsigned int* pdi = outputMatrix + (i + 1) * (width + 1);
// first column of each line
pdi[0] = 0;
pdi[1] = psi[0];
int row_sum = psi[0];
// other columns
for (int j = 1; j < width; j++) {
row_sum += psi[j];
pdi[j + 1] = pdi[j + 1 - width - 1] + row_sum;
}
}
return;
}
int FastWindowBinarizer::binarizeImage1(ErrorHandler& err_handler) {
LuminanceSource& source = *getLuminanceSource();
Ref<BitMatrix> matrix(new BitMatrix(width, height, err_handler));
if (err_handler.ErrCode()) return -1;
ArrayRef<char> localLuminances = source.getMatrix();
unsigned char* src = (unsigned char*)localLuminances->data();
unsigned char* dst = matrix->getPtr();
fastWindow(src, dst, err_handler);
if (err_handler.ErrCode()) return -1;
matrix0_ = matrix;
return 0;
}
void FastWindowBinarizer::fastWindow(const unsigned char* src, unsigned char* dst,
ErrorHandler& err_handler) {
int r = (int)(min(width, height) * WINDOW_FRACTION / BLOCK_SIZE / 2 + 1);
const int NEWH_BLOCK_SIZE = BLOCK_SIZE * r;
if (height < NEWH_BLOCK_SIZE || width < NEWH_BLOCK_SIZE) {
matrix_ = GlobalHistogramBinarizer::getBlackMatrix(err_handler);
return;
}
const unsigned char* _img = src;
fastIntegral(_img, _internal);
int aw = width / BLOCK_SIZE;
int ah = height / BLOCK_SIZE;
memset(dst, 0, sizeof(char) * height * width);
for (int ai = 0; ai < ah; ai++) {
int top = max(0, ((ai - r + 1) * BLOCK_SIZE));
int bottom = min(height, (ai + r) * BLOCK_SIZE);
unsigned int* pt = _internal + top * (width + 1);
unsigned int* pb = _internal + bottom * (width + 1);
for (int aj = 0; aj < aw; aj++) {
int left = max(0, (aj - r + 1) * BLOCK_SIZE);
int right = min(width, (aj + r) * BLOCK_SIZE);
unsigned int block = pb[right] + pt[left] - pt[right] - pb[left];
int pixels = (bottom - top) * (right - left);
int avg = (int)block / pixels;
for (int bi = ai * BLOCK_SIZE; bi < height && bi < (ai + 1) * BLOCK_SIZE; bi++) {
const unsigned char* psi = src + bi * width;
unsigned char* pdi = dst + bi * width;
for (int bj = aj * BLOCK_SIZE; bj < width && bj < (aj + 1) * BLOCK_SIZE; bj++) {
if ((int)psi[bj] < avg)
pdi[bj] = 1;
else
pdi[bj] = 0;
}
}
}
}
// delete [] _internal;
return;
}
int FastWindowBinarizer::binarizeImage0(ErrorHandler& err_handler) {
// if (matrix_) {
// return matrix_;
//}
LuminanceSource& source = *getLuminanceSource();
if (width >= BLOCK_SIZE && height >= BLOCK_SIZE) {
int r = (int)(min(width, height) * WINDOW_FRACTION / BLOCK_SIZE / 2 + 1);
int aw = width / BLOCK_SIZE;
int ah = height / BLOCK_SIZE;
int ow = aw + 1;
ArrayRef<char> _luminances = source.getMatrix();
// Get luminances for int value first
for (int i = 0; i < width * height; i++) {
_luminancesInt[i] = _luminances[i] & 0xff;
}
calcBlockTotals(_luminancesInt, _blockTotals, aw, ah);
cumulative(_blockTotals, _totals, aw, ah);
Ref<BitMatrix> newMatrix(new BitMatrix(width, height, err_handler));
if (err_handler.ErrCode()) return -1;
unsigned char* newimg = newMatrix->getPtr();
for (int by = 0; by < ah; by++) {
int top = max(0, by - r + 1);
int bottom = min(ah, by + r);
for (int bx = 0; bx < aw; bx++) {
int left = max(0, bx - r + 1);
int right = min(aw, bx + r);
int block = _totals[bottom * ow + right] + _totals[top * ow + left] -
_totals[top * ow + right] - _totals[bottom * ow + left];
int pixels = (bottom - top) * (right - left) * BLOCK_SIZE * BLOCK_SIZE;
int avg = block / pixels;
for (int y = by * BLOCK_SIZE; y < (by + 1) * BLOCK_SIZE; y++) {
// int offset = y*width;
int* plumint = _luminancesInt + y * width;
unsigned char* pn = newimg + y * width;
for (int x = bx * BLOCK_SIZE; x < (bx + 1) * BLOCK_SIZE; x++) {
// int pixel = luminances[y*width + x] & 0xff;
// if(plumint[x] < avg)
// newMatrix->set(x, y);
if (plumint[x] < avg)
pn[x] = 1;
else
pn[x] = 0;
}
}
}
}
// delete[] data;
matrix_ = newMatrix;
} else {
// If the image is too small, fall back to the global histogram
// approach.
matrix_ = GlobalHistogramBinarizer::getBlackMatrix(err_handler);
}
return 0;
}

View File

@@ -0,0 +1,56 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
//
// Modified from ZXing. Copyright ZXing authors.
// Licensed under the Apache License, Version 2.0 (the "License").
#ifndef __ZXING_COMMON_DETECTOR_RESULT_HPP__
#define __ZXING_COMMON_DETECTOR_RESULT_HPP__
#include "../../binarizer.hpp"
#include "../../errorhandler.hpp"
#include "../bitarray.hpp"
#include "../bitmatrix.hpp"
#include "global_histogram_binarizer.hpp"
#include <vector>
namespace zxing {
class FastWindowBinarizer : public GlobalHistogramBinarizer {
private:
Ref<BitMatrix> matrix_;
Ref<BitArray> cached_row_;
int* _luminancesInt;
int* _blockTotals;
int* _totals;
int* _rowTotals;
unsigned int* _internal;
public:
explicit FastWindowBinarizer(Ref<LuminanceSource> source);
virtual ~FastWindowBinarizer();
virtual Ref<BitMatrix> getBlackMatrix(ErrorHandler& err_handler) override;
virtual Ref<BitArray> getBlackRow(int y, Ref<BitArray> row, ErrorHandler& err_handler) override;
Ref<Binarizer> createBinarizer(Ref<LuminanceSource> source) override;
private:
void calcBlockTotals(int* luminancesInt, int* output, int aw, int ah);
void cumulative(int* data, int* output, int _width, int _height);
int binarizeImage0(ErrorHandler& err_handler);
void fastIntegral(const unsigned char* inputMatrix, unsigned int* outputMatrix);
int binarizeImage1(ErrorHandler& err_handler);
void fastWindow(const unsigned char* src, unsigned char* dst, ErrorHandler& err_handler);
};
} // namespace zxing
#endif // __ZXING_COMMON_DETECTOR_RESULT_HPP__

View File

@@ -0,0 +1,308 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
//
// Modified from ZXing. Copyright ZXing authors.
// Licensed under the Apache License, Version 2.0 (the "License").
#include "../../../precomp.hpp"
#include "global_histogram_binarizer.hpp"
using zxing::GlobalHistogramBinarizer;
namespace {
const int LUMINANCE_BITS = 5;
const int LUMINANCE_SHIFT = 8 - LUMINANCE_BITS;
const int LUMINANCE_BUCKETS = 1 << LUMINANCE_BITS;
const ArrayRef<char> EMPTY(0);
} // namespace
GlobalHistogramBinarizer::GlobalHistogramBinarizer(Ref<LuminanceSource> source)
: Binarizer(source), luminances(EMPTY), buckets(LUMINANCE_BUCKETS) {
filtered = false;
}
GlobalHistogramBinarizer::~GlobalHistogramBinarizer() {}
void GlobalHistogramBinarizer::initArrays(int luminanceSize) {
if (luminances->size() < luminanceSize) {
luminances = ArrayRef<char>(luminanceSize);
}
for (int x = 0; x < LUMINANCE_BUCKETS; x++) {
buckets[x] = 0;
}
}
// Applies simple sharpening to the row data to improve performance of the 1D
// readers.
Ref<BitArray> GlobalHistogramBinarizer::getBlackRow(int y, Ref<BitArray> row,
ErrorHandler& err_handler) {
// First call binarize image in child class to get matrix0_ and binCache
if (!matrix0_) {
binarizeImage0(err_handler);
if (err_handler.ErrCode()) return Ref<BitArray>();
}
// Call parent getBlackMatrix to get current matrix
return Binarizer::getBlackRow(y, row, err_handler);
}
// Does not sharpen the data, as this call is intended to only be used by 2D
// readers.
Ref<BitMatrix> GlobalHistogramBinarizer::getBlackMatrix(ErrorHandler& err_handler) {
binarizeImage0(err_handler);
if (err_handler.ErrCode()) return Ref<BitMatrix>();
// First call binarize image in child class to get matrix0_ and binCache
// Call parent getBlackMatrix to get current matrix
return Binarizer::getBlackMatrix(err_handler);
}
using namespace std;
int GlobalHistogramBinarizer::estimateBlackPoint(ArrayRef<int> const& _buckets,
ErrorHandler& err_handler) {
// Find tallest peak in histogram
int numBuckets = _buckets->size();
int maxBucketCount = 0;
int firstPeak = 0;
int firstPeakSize = 0;
for (int x = 0; x < numBuckets; x++) {
if (_buckets[x] > firstPeakSize) {
firstPeak = x;
firstPeakSize = _buckets[x];
}
if (_buckets[x] > maxBucketCount) {
maxBucketCount = _buckets[x];
}
}
// Find second-tallest peak -- well, another peak that is tall and not
// so close to the first one
int secondPeak = 0;
int secondPeakScore = 0;
for (int x = 0; x < numBuckets; x++) {
int distanceToBiggest = x - firstPeak;
// Encourage more distant second peaks by multiplying by square of
// distance
int score = _buckets[x] * distanceToBiggest * distanceToBiggest;
if (score > secondPeakScore) {
secondPeak = x;
secondPeakScore = score;
}
}
// Make sure firstPeak corresponds to the black peak.
if (firstPeak > secondPeak) {
int temp = firstPeak;
firstPeak = secondPeak;
secondPeak = temp;
}
// Kind of arbitrary; if the two peaks are very close, then we figure there
// is so little dynamic range in the image, that discriminating black and
// white is too error-prone. Decoding the image/line is either pointless, or
// may in some cases lead to a false positive for 1D formats, which are
// relatively lenient. We arbitrarily say "close" is "<= 1/16 of the total
// histogram buckets apart" std::cerr << "! " << secondPeak << " " <<
// firstPeak << " " << numBuckets << std::endl;
if (secondPeak - firstPeak <= numBuckets >> 4) {
err_handler = NotFoundErrorHandler("NotFound GlobalHistogramBinarizer");
return -1;
}
// Find a valley between them that is low and closer to the white peak
int bestValley = secondPeak - 1;
int bestValleyScore = -1;
for (int x = secondPeak - 1; x > firstPeak; x--) {
int fromFirst = x - firstPeak;
// Favor a "valley" that is not too close to either peak -- especially
// not the black peak -- and that has a low value of course
int score = fromFirst * fromFirst * (secondPeak - x) * (maxBucketCount - buckets[x]);
if (score > bestValleyScore) {
bestValley = x;
bestValleyScore = score;
}
}
// std::cerr << "bps " << (bestValley << LUMINANCE_SHIFT) << std::endl;
return bestValley << LUMINANCE_SHIFT;
}
// codes from sagazhou, only works well on one dataset
int GlobalHistogramBinarizer::estimateBlackPoint2(ArrayRef<int> const& _buckets) {
int midValue = LUMINANCE_BUCKETS / 2 + 1;
// Find tallest and lowest peaks in histogram
// const int numBuckets = buckets->size();
int maxPointArray[LUMINANCE_BUCKETS] = {0};
int maxCrusor = 0;
int maxValue = 0, maxIndex = 0;
int minPointArray[LUMINANCE_BUCKETS] = {0};
int minCrusor = 0;
for (int i = 2; i < LUMINANCE_BUCKETS - 3; i++) {
if (_buckets[i] < _buckets[i + 1] && _buckets[i] < _buckets[i + 2] &&
_buckets[i] < _buckets[i - 1] && _buckets[i] < _buckets[i - 2]) {
minPointArray[minCrusor++] = i;
} else if (_buckets[i] > _buckets[i + 1] && _buckets[i] > _buckets[i + 2] &&
_buckets[i] > _buckets[i - 1] && _buckets[i] > _buckets[i - 2]) {
maxPointArray[maxCrusor++] = i;
if (_buckets[i] > maxValue) {
maxValue = _buckets[i];
maxIndex = i;
}
}
}
bool bSlantBlack = true;
// most pixels are black
for (int i = 0; i < maxCrusor; ++i) {
if (maxPointArray[i] > midValue) {
bSlantBlack = false;
break;
}
}
bool bSlantWhite = true;
// most pixels are white
for (int i = 0; i < maxCrusor; ++i) {
if (maxPointArray[i] < midValue) {
bSlantWhite = false;
break;
}
}
if (bSlantBlack) {
int start = maxIndex + 30;
int end = midValue;
if (minCrusor == 0) // unimodal
{
return 255;
} else {
int mostLeftIndex = 0;
bool bFind = false;
for (int i = 0; i < minCrusor; ++i) // wave motion
{
if (minPointArray[i] > start && minPointArray[i] < end) {
mostLeftIndex = minPointArray[i];
bFind = true;
break;
}
}
if (bFind) {
return mostLeftIndex;
} else {
return 255;
}
}
}
if (bSlantWhite) {
int start = midValue;
int end = maxIndex - 30;
if (minCrusor == 0) // unimodal
{
return 0;
} else {
int mostRightIndex = 0;
bool bFind = false;
for (int i = 0; i < minCrusor; ++i) // wave motion
{
if (minPointArray[i] > start && minPointArray[i] < end) {
mostRightIndex = i;
bFind = true;
}
}
if (bFind) {
return mostRightIndex;
} else {
return 0;
}
}
}
// balanced distribution
if (maxIndex < midValue) {
// the minest min value
if (minCrusor == 0) {
return 255; // all black
} else {
int start = maxIndex + 30;
int end = 253;
for (int i = 0; i < minCrusor; ++i) // wave motion
{
if (minPointArray[i] > start && minPointArray[i] < end) {
return minPointArray[i];
}
}
}
} else {
// maxest min value
if (minCrusor == 0) {
return 0; // white
} else {
int start = 0;
int end = maxIndex - 30;
int mostRightIndex = 0;
for (int i = 0; i < minCrusor; ++i) // wave motion
{
if (minPointArray[i] > start && minPointArray[i] < end) {
mostRightIndex = minPointArray[i];
}
}
return mostRightIndex;
}
}
return 0;
}
int GlobalHistogramBinarizer::binarizeImage0(ErrorHandler& err_handler) {
LuminanceSource& source = *getLuminanceSource();
Ref<BitMatrix> matrix(new BitMatrix(width, height, err_handler));
if (err_handler.ErrCode()) return -1;
// Quickly calculates the histogram by sampling four rows from the image.
// This proved to be more robust on the blackbox tests than sampling a
// diagonal as we used to do.
initArrays(width);
ArrayRef<int> localBuckets = buckets;
for (int y = 1; y < 5; y++) {
int row = height * y / 5;
ArrayRef<char> localLuminances = source.getRow(row, luminances, err_handler);
if (err_handler.ErrCode()) return -1;
int right = (width << 2) / 5;
for (int x = width / 5; x < right; x++) {
int pixel = localLuminances[x] & 0xff;
localBuckets[pixel >> LUMINANCE_SHIFT]++;
}
}
int blackPoint = estimateBlackPoint(localBuckets, err_handler);
if (err_handler.ErrCode()) return -1;
ArrayRef<char> localLuminances = source.getMatrix();
for (int y = 0; y < height; y++) {
int offset = y * width;
for (int x = 0; x < width; x++) {
int pixel = localLuminances[offset + x] & 0xff;
if (pixel < blackPoint) {
matrix->set(x, y);
}
}
}
matrix0_ = matrix;
return 0;
}
Ref<Binarizer> GlobalHistogramBinarizer::createBinarizer(Ref<LuminanceSource> source) {
return Ref<Binarizer>(new GlobalHistogramBinarizer(source));
}

View File

@@ -0,0 +1,55 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
//
// Modified from ZXing. Copyright ZXing authors.
// Licensed under the Apache License, Version 2.0 (the "License").
#ifndef __ZXING_COMMON_GLOBAL_HISTOGRAM_BINARIZER_HPP__
#define __ZXING_COMMON_GLOBAL_HISTOGRAM_BINARIZER_HPP__
#include "../../binarizer.hpp"
#include "../../errorhandler.hpp"
#include "../array.hpp"
#include "../bitarray.hpp"
#include "../bitmatrix.hpp"
#include "../bytematrix.hpp"
using zxing::ArrayRef;
using zxing::Binarizer;
using zxing::BitArray;
using zxing::BitMatrix;
using zxing::ByteMatrix;
using zxing::ErrorHandler;
using zxing::LuminanceSource;
using zxing::Ref;
namespace zxing {
class GlobalHistogramBinarizer : public Binarizer {
protected:
ArrayRef<char> luminances;
ArrayRef<int> buckets;
public:
explicit GlobalHistogramBinarizer(Ref<LuminanceSource> source);
virtual ~GlobalHistogramBinarizer();
virtual Ref<BitArray> getBlackRow(int y, Ref<BitArray> row, ErrorHandler &err_handler) override;
virtual Ref<BitMatrix> getBlackMatrix(ErrorHandler &err_handler) override;
int estimateBlackPoint(ArrayRef<int> const &buckets, ErrorHandler &err_handler);
int estimateBlackPoint2(ArrayRef<int> const &buckets);
Ref<Binarizer> createBinarizer(Ref<LuminanceSource> source) override;
private:
int binarizeImage0(ErrorHandler &err_handler);
void initArrays(int luminanceSize);
bool filtered;
};
} // namespace zxing
#endif // __ZXING_COMMON_GLOBAL_HISTOGRAM_BINARIZER_HPP__

View File

@@ -0,0 +1,424 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
//
// Modified from ZXing. Copyright ZXing authors.
// Licensed under the Apache License, Version 2.0 (the "License").
#include "../../../precomp.hpp"
#include "hybrid_binarizer.hpp"
using zxing::HybridBinarizer;
using zxing::BINARIZER_BLOCK;
// This class uses 5*5 blocks to compute local luminance, where each block is
// 8*8 pixels So this is the smallest dimension in each axis we can accept.
namespace {
const int BLOCK_SIZE_POWER = 3;
const int BLOCK_SIZE = 1 << BLOCK_SIZE_POWER; // ...0100...00
const int BLOCK_SIZE_MASK = BLOCK_SIZE - 1; // ...0011...11
const int MINIMUM_DIMENSION = BLOCK_SIZE * 5;
#ifdef USE_SET_INT
const int BITS_PER_BYTE = 8;
const int BITS_PER_WORD = BitMatrix::bitsPerWord;
#endif
} // namespace
HybridBinarizer::HybridBinarizer(Ref<LuminanceSource> source) : GlobalHistogramBinarizer(source) {
int subWidth = width >> BLOCK_SIZE_POWER;
if ((width & BLOCK_SIZE_MASK) != 0) {
subWidth++;
}
int subHeight = height >> BLOCK_SIZE_POWER;
if ((height & BLOCK_SIZE_MASK) != 0) {
subHeight++;
}
grayByte_ = source->getByteMatrix();
blocks_ = getBlockArray(subWidth * subHeight);
subWidth_ = subWidth;
subHeight_ = subHeight;
initBlocks();
initBlockIntegral();
}
HybridBinarizer::~HybridBinarizer() {
}
Ref<Binarizer> HybridBinarizer::createBinarizer(Ref<LuminanceSource> source) {
return Ref<Binarizer>(new GlobalHistogramBinarizer(source));
}
int HybridBinarizer::initBlockIntegral() {
blockIntegral_ = new Array<int>(width * height);
int* integral = blockIntegral_->data();
// unsigned char* therow = grayByte_->getByteRow(0);
// first row only
int rs = 0;
for (int j = 0; j < width; j++) {
integral[j] = 0;
}
for (int i = 0; i < height; i++) {
integral[i * width] = 0;
}
for (int j = 0; j < subWidth_; j++) {
rs += blocks_[j].threshold;
integral[width + j + 1] = rs;
}
// remaining cells are sum above and to the left
int offset = width;
int offsetBlock = 0;
for (int i = 1; i < subHeight_; ++i) {
// therow = grayByte_->getByteRow(i);
offsetBlock = i * subWidth_;
rs = 0;
offset += width;
for (int j = 0; j < subWidth_; ++j) {
rs += blocks_[offsetBlock + j].threshold;
integral[offset + j + 1] = rs + integral[offset - width + j + 1];
}
}
return 1;
}
/**
* Calculates the final BitMatrix once for all requests. This could be called
* once from the constructor instead, but there are some advantages to doing it
* lazily, such as making profiling easier, and not doing heavy lifting when
* callers don't expect it.
*/
Ref<BitMatrix> HybridBinarizer::getBlackMatrix(ErrorHandler& err_handler) {
// First call binarize image in child class to get matrix0_ and binCache
if (!matrix0_) {
binarizeByBlock(err_handler);
if (err_handler.ErrCode()) return Ref<BitMatrix>();
}
// First call binarize image in child class to get matrix0_ and binCache
// Call parent getBlackMatrix to get current matrix
return Binarizer::getBlackMatrix(err_handler);
}
#if 1
/**
* Calculate black row from BitMatrix
* If BitMatrix has been calculated then just get the row
* If BitMatrix has not been calculated then call getBlackMatrix first
*/
Ref<BitArray> HybridBinarizer::getBlackRow(int y, Ref<BitArray> row, ErrorHandler& err_handler) {
// First call binarize image in child class to get matrix0_ and binCache
if (!matrix0_) {
binarizeByBlock(err_handler);
if (err_handler.ErrCode()) return Ref<BitArray>();
}
// Call parent getBlackMatrix to get current matrix
return Binarizer::getBlackRow(y, row, err_handler);
}
#endif
namespace {
inline int cap(int value, int min, int max) {
return value < min ? min : value > max ? max : value;
}
} // namespace
// For each block in the image, calculates the average black point using a 5*5
// grid of the blocks around it. Also handles the corner cases (fractional
// blocks are computed based on the last pixels in the row/column which are also
// used in the previous block.)
#define THRES_BLOCKSIZE 2
// No use of level now
ArrayRef<int> HybridBinarizer::getBlackPoints() {
int blackWidth, blackHeight;
blackWidth = subWidth_;
blackHeight = subHeight_;
ArrayRef<int> blackPoints(blackWidth * blackHeight);
int* blackArray = blackPoints->data();
int offset = 0;
for (int i = 0; i < blackHeight; i++) {
offset = i * blackWidth;
for (int j = 0; j < blackWidth; j++) {
blackArray[offset + j] = blocks_[offset + j].threshold;
}
}
return blackPoints;
}
// Original code 20140606
void HybridBinarizer::calculateThresholdForBlock(Ref<ByteMatrix>& _luminances, int subWidth,
int subHeight, int SIZE_POWER,
// ArrayRef<int> &blackPoints,
Ref<BitMatrix> const& matrix,
ErrorHandler& err_handler) {
int block_size = 1 << SIZE_POWER;
int maxYOffset = height - block_size;
int maxXOffset = width - block_size;
int* blockIntegral = blockIntegral_->data();
int blockArea = ((2 * THRES_BLOCKSIZE + 1) * (2 * THRES_BLOCKSIZE + 1));
for (int y = 0; y < subHeight; y++) {
int yoffset = y << SIZE_POWER;
if (yoffset > maxYOffset) {
yoffset = maxYOffset;
}
for (int x = 0; x < subWidth; x++) {
int xoffset = x << SIZE_POWER;
if (xoffset > maxXOffset) {
xoffset = maxXOffset;
}
int left = cap(x, THRES_BLOCKSIZE, subWidth - THRES_BLOCKSIZE - 1);
int top = cap(y, THRES_BLOCKSIZE, subHeight - THRES_BLOCKSIZE - 1);
int sum = 0;
// int sum2 = 0;
int offset1 = (top - THRES_BLOCKSIZE) * (subWidth + 1) + left - THRES_BLOCKSIZE;
int offset2 = (top + THRES_BLOCKSIZE + 1) * (subWidth + 1) + left - THRES_BLOCKSIZE;
int blocksize = THRES_BLOCKSIZE * 2 + 1;
sum = blockIntegral[offset1] - blockIntegral[offset1 + blocksize] -
blockIntegral[offset2] + blockIntegral[offset2 + blocksize];
int average = sum / blockArea;
thresholdBlock(_luminances, xoffset, yoffset, average, matrix, err_handler);
if (err_handler.ErrCode()) return;
}
}
}
#ifdef USE_SET_INT
void HybridBinarizer::thresholdFourBlocks(Ref<ByteMatrix>& luminances, int xoffset, int yoffset,
int* thresholds, int stride,
Ref<BitMatrix> const& matrix) {
int setIntCircle = BITS_PER_WORD / BITS_PER_BYTE;
for (int y = 0; y < BLOCK_SIZE; y++) {
unsigned char* pTemp = luminances->getByteRow(yoffset + y);
pTemp = pTemp + xoffset;
unsigned int valueInt = 0;
int bitPosition = 0;
for (int k = 0; k < setIntCircle; k++) {
for (int x = 0; x < BLOCK_SIZE; x++) {
int pixel = *pTemp++;
if (pixel <= thresholds[k]) {
// bitPosition=(3-k)*8+x;
valueInt |= (unsigned int)1 << bitPosition;
}
bitPosition++;
}
}
matrix->setIntOneTime(xoffset, yoffset + y, valueInt);
}
return;
}
#endif
// Applies a single threshold to a block of pixels
void HybridBinarizer::thresholdBlock(Ref<ByteMatrix>& _luminances, int xoffset, int yoffset,
int threshold, Ref<BitMatrix> const& matrix,
ErrorHandler& err_handler) {
int rowBitsSize = matrix->getRowBitsSize();
int rowSize = width;
int rowBitStep = rowBitsSize - BLOCK_SIZE;
int rowStep = rowSize - BLOCK_SIZE;
unsigned char* pTemp = _luminances->getByteRow(yoffset, err_handler);
if (err_handler.ErrCode()) return;
bool* bpTemp = matrix->getRowBoolPtr(yoffset);
pTemp += xoffset;
bpTemp += xoffset;
for (int y = 0; y < BLOCK_SIZE; y++) {
for (int x = 0; x < BLOCK_SIZE; x++) {
// comparison needs to be <= so that black == 0 pixels are black
// even if the threshold is 0.
*bpTemp++ = (*pTemp++ <= threshold) ? true : false;
}
pTemp += rowBitStep;
bpTemp += rowStep;
}
}
void HybridBinarizer::thresholdIrregularBlock(Ref<ByteMatrix>& _luminances, int xoffset,
int yoffset, int blockWidth, int blockHeight,
int threshold, Ref<BitMatrix> const& matrix,
ErrorHandler& err_handler) {
for (int y = 0; y < blockHeight; y++) {
unsigned char* pTemp = _luminances->getByteRow(yoffset + y, err_handler);
if (err_handler.ErrCode()) return;
pTemp = pTemp + xoffset;
for (int x = 0; x < blockWidth; x++) {
// comparison needs to be <= so that black == 0 pixels are black
// even if the threshold is 0.
int pixel = *pTemp++;
if (pixel <= threshold) {
matrix->set(xoffset + x, yoffset + y);
}
}
}
}
namespace {
inline int getBlackPointFromNeighbors(ArrayRef<BINARIZER_BLOCK> block, int subWidth, int x, int y) {
return (block[(y - 1) * subWidth + x].threshold + 2 * block[y * subWidth + x - 1].threshold +
block[(y - 1) * subWidth + x - 1].threshold) >>
2;
}
} // namespace
#define MIN_DYNAMIC_RANGE 24
// Calculates a single black point for each block of pixels and saves it away.
int HybridBinarizer::initBlocks() {
Ref<ByteMatrix>& _luminances = grayByte_;
int subWidth = subWidth_;
int subHeight = subHeight_;
unsigned char* bytes = _luminances->bytes;
const int minDynamicRange = 24;
for (int y = 0; y < subHeight; y++) {
int yoffset = y << BLOCK_SIZE_POWER;
int maxYOffset = height - BLOCK_SIZE;
if (yoffset > maxYOffset) yoffset = maxYOffset;
for (int x = 0; x < subWidth; x++) {
int xoffset = x << BLOCK_SIZE_POWER;
int maxXOffset = width - BLOCK_SIZE;
if (xoffset > maxXOffset) xoffset = maxXOffset;
int sum = 0;
int min = 0xFF;
int max = 0;
for (int yy = 0, offset = yoffset * width + xoffset; yy < BLOCK_SIZE;
yy++, offset += width) {
for (int xx = 0; xx < BLOCK_SIZE; xx++) {
// int pixel = luminances->bytes[offset + xx] & 0xFF;
int pixel = bytes[offset + xx];
sum += pixel;
// still looking for good contrast
if (pixel < min) {
min = pixel;
}
if (pixel > max) {
max = pixel;
}
}
// short-circuit min/max tests once dynamic range is met
if (max - min > minDynamicRange) {
// finish the rest of the rows quickly
for (yy++, offset += width; yy < BLOCK_SIZE; yy++, offset += width) {
for (int xx = 0; xx < BLOCK_SIZE; xx += 2) {
sum += bytes[offset + xx];
sum += bytes[offset + xx + 1];
}
}
}
}
blocks_[y * subWidth + x].min = min;
blocks_[y * subWidth + x].max = max;
blocks_[y * subWidth + x].sum = sum;
blocks_[y * subWidth + x].threshold =
getBlockThreshold(x, y, subWidth, sum, min, max, minDynamicRange, BLOCK_SIZE_POWER);
}
}
return 1;
}
int HybridBinarizer::getBlockThreshold(int x, int y, int subWidth, int sum, int min, int max,
int minDynamicRange, int SIZE_POWER) {
// See
// http://groups.google.com/group/zxing/browse_thread/thread/d06efa2c35a7ddc0
// The default estimate is the average of the values in the block.
int average = sum >> (SIZE_POWER * 2);
if (max - min <= minDynamicRange) {
// If variation within the block is low, assume this is a block withe
// only light or only dark pixels. In that case we do not want to use
// the average, as it would divide this low contrast area into black and
// white pixels, essentially creating data out of noise. The default
// assumption is that the block is light/background. Since no estimate
// for the level of dark pixels exists locally, use half the min for the
// block.
average = min >> 1;
if (y > 0 && x > 0) {
// Correct the "white background" assumption for blocks that have
// neighbors by comparing the pixels in this block to the previously
// calculated black points. This is based on the fact that dark
// barcode symbology is always surrounded by some amout of light
// background for which reasonable black point estimates were made.
// The bp estimated at the boundaries is used for the interior.
int bp = getBlackPointFromNeighbors(blocks_, subWidth, x, y);
// The (min<bp) is arbitrary but works better than other heuristics
// that were tried.
if (min < bp) {
average = bp;
}
}
}
// blocks_[y * subWidth + x].average = average;
// blocks_[y * subWidth + x].threshold = average;
return average;
}
int HybridBinarizer::binarizeByBlock(ErrorHandler& err_handler) {
if (width >= MINIMUM_DIMENSION && height >= MINIMUM_DIMENSION) {
Ref<BitMatrix> newMatrix(new BitMatrix(width, height, err_handler));
if (err_handler.ErrCode()) return -1;
calculateThresholdForBlock(grayByte_, subWidth_, subHeight_, BLOCK_SIZE_POWER, newMatrix,
err_handler);
if (err_handler.ErrCode()) return -1;
matrix0_ = newMatrix;
} else {
// If the image is too small, fall back to the global histogram
// approach.
matrix0_ = GlobalHistogramBinarizer::getBlackMatrix(err_handler);
if (err_handler.ErrCode()) return 1;
}
// return matrix0_;
return 1;
}

View File

@@ -0,0 +1,83 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
//
// Modified from ZXing. Copyright ZXing authors.
// Licensed under the Apache License, Version 2.0 (the "License").
#ifndef __ZXING_COMMON_HYBRID_BINARIZER_HPP__
#define __ZXING_COMMON_HYBRID_BINARIZER_HPP__
#include "../../binarizer.hpp"
#include "../../errorhandler.hpp"
#include "../bitarray.hpp"
#include "../bitmatrix.hpp"
#include "../bytematrix.hpp"
#include "global_histogram_binarizer.hpp"
#include <vector>
namespace zxing {
class HybridBinarizer : public GlobalHistogramBinarizer {
private:
Ref<ByteMatrix> grayByte_;
// ArrayRef<int> integral_;
ArrayRef<int> blockIntegral_;
ArrayRef<BINARIZER_BLOCK> blocks_;
ArrayRef<int> blackPoints_;
int level_;
int subWidth_;
int subHeight_;
public:
explicit HybridBinarizer(Ref<LuminanceSource> source);
virtual ~HybridBinarizer();
virtual Ref<BitMatrix> getBlackMatrix(ErrorHandler& err_handler) override;
virtual Ref<BitArray> getBlackRow(int y, Ref<BitArray> row, ErrorHandler& err_handler) override;
Ref<Binarizer> createBinarizer(Ref<LuminanceSource> source) override;
private:
int initIntegral();
int initBlockIntegral();
int initBlocks();
// int calculateBlackPoints();
ArrayRef<int> getBlackPoints();
int getBlockThreshold(int x, int y, int subWidth, int sum, int min, int max,
int minDynamicRange, int SIZE_POWER);
void calculateThresholdForBlock(Ref<ByteMatrix>& luminances, int subWidth, int subHeight,
int SIZE_POWER, Ref<BitMatrix> const& matrix,
ErrorHandler& err_handler);
void thresholdBlock(Ref<ByteMatrix>& luminances, int xoffset, int yoffset, int threshold,
Ref<BitMatrix> const& matrix, ErrorHandler& err_handler);
void thresholdIrregularBlock(Ref<ByteMatrix>& luminances, int xoffset, int yoffset,
int blockWidth, int blockHeight, int threshold,
Ref<BitMatrix> const& matrix, ErrorHandler& err_handler);
#ifdef USE_SET_INT
void thresholdFourBlocks(Ref<ByteMatrix>& luminances, int xoffset, int yoffset, int* thresholds,
int stride, Ref<BitMatrix> const& matrix);
#endif
// Add for binarize image when call getBlackMatrix
// By Skylook
int binarizeByBlock(ErrorHandler& err_handler);
};
} // namespace zxing
#endif // __ZXING_COMMON_HYBRID_BINARIZER_HPP__

View File

@@ -0,0 +1,155 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
//
// Modified from ZXing. Copyright ZXing authors.
// Licensed under the Apache License, Version 2.0 (the "License").
#include "../../../precomp.hpp"
#include "simple_adaptive_binarizer.hpp"
using zxing::SimpleAdaptiveBinarizer;
SimpleAdaptiveBinarizer::SimpleAdaptiveBinarizer(Ref<LuminanceSource> source)
: GlobalHistogramBinarizer(source) {
filtered = false;
}
SimpleAdaptiveBinarizer::~SimpleAdaptiveBinarizer() {}
// Applies simple sharpening to the row data to improve performance of the 1D
// readers.
Ref<BitArray> SimpleAdaptiveBinarizer::getBlackRow(int y, Ref<BitArray> row,
ErrorHandler &err_handler) {
// First call binarize image in child class to get matrix0_ and binCache
if (!matrix0_) {
binarizeImage0(err_handler);
if (err_handler.ErrCode()) return Ref<BitArray>();
}
// Call parent getBlackMatrix to get current matrix
return Binarizer::getBlackRow(y, row, err_handler);
}
// Does not sharpen the data, as this call is intended to only be used by 2D
// readers.
Ref<BitMatrix> SimpleAdaptiveBinarizer::getBlackMatrix(ErrorHandler &err_handler) {
// First call binarize image in child class to get matrix0_ and binCache
if (!matrix0_) {
binarizeImage0(err_handler);
if (err_handler.ErrCode()) return Ref<BitMatrix>();
}
// First call binarize image in child class to get matrix0_ and binCache
// Call parent getBlackMatrix to get current matrix
return Binarizer::getBlackMatrix(err_handler);
}
using namespace std;
int SimpleAdaptiveBinarizer::binarizeImage0(ErrorHandler &err_handler) {
LuminanceSource &source = *getLuminanceSource();
Ref<BitMatrix> matrix(new BitMatrix(width, height, err_handler));
if (err_handler.ErrCode()) return -1;
ArrayRef<char> localLuminances = source.getMatrix();
unsigned char *src = (unsigned char *)localLuminances->data();
unsigned char *dst = matrix->getPtr();
qrBinarize(src, dst);
matrix0_ = matrix;
return 0;
}
/*A simplified adaptive thresholder.
This compares the current pixel value to the mean value of a (large) window
surrounding it.*/
int SimpleAdaptiveBinarizer::qrBinarize(const unsigned char *src, unsigned char *dst) {
unsigned char *mask = dst;
if (width > 0 && height > 0) {
unsigned *col_sums;
int logwindw;
int logwindh;
int windw;
int windh;
int y0offs;
int y1offs;
unsigned g;
int x;
int y;
/*We keep the window size fairly large to ensure it doesn't fit
completely inside the center of a finder pattern of a version 1 QR
code at full resolution.*/
for (logwindw = 4; logwindw < 8 && (1 << logwindw) < ((width + 7) >> 3); logwindw++)
;
for (logwindh = 4; logwindh < 8 && (1 << logwindh) < ((height + 7) >> 3); logwindh++)
;
windw = 1 << logwindw;
windh = 1 << logwindh;
int logwinds = (logwindw + logwindh);
col_sums = (unsigned *)malloc(width * sizeof(*col_sums));
/*Initialize sums down each column.*/
for (x = 0; x < width; x++) {
g = src[x];
col_sums[x] = (g << (logwindh - 1)) + g;
}
for (y = 1; y < (windh >> 1); y++) {
y1offs = min(y, height - 1) * width;
for (x = 0; x < width; x++) {
g = src[y1offs + x];
col_sums[x] += g;
}
}
for (y = 0; y < height; y++) {
unsigned m;
int x0;
int x1;
/*Initialize the sum over the window.*/
m = (col_sums[0] << (logwindw - 1)) + col_sums[0];
for (x = 1; x < (windw >> 1); x++) {
x1 = min(x, width - 1);
m += col_sums[x1];
}
int offset = y * width;
for (x = 0; x < width; x++) {
/*Perform the test against the threshold T = (m/n)-D,
where n=windw*windh and D=3.*/
g = src[offset + x];
mask[offset + x] = ((g + 3) << (logwinds) < m);
/*Update the window sum.*/
if (x + 1 < width) {
x0 = max(0, x - (windw >> 1));
x1 = min(x + (windw >> 1), width - 1);
m += col_sums[x1] - col_sums[x0];
}
}
/*Update the column sums.*/
if (y + 1 < height) {
y0offs = max(0, y - (windh >> 1)) * width;
y1offs = min(y + (windh >> 1), height - 1) * width;
for (x = 0; x < width; x++) {
col_sums[x] -= src[y0offs + x];
col_sums[x] += src[y1offs + x];
}
}
}
free(col_sums);
}
return 1;
}
Ref<Binarizer> SimpleAdaptiveBinarizer::createBinarizer(Ref<LuminanceSource> source) {
return Ref<Binarizer>(new SimpleAdaptiveBinarizer(source));
}

View File

@@ -0,0 +1,40 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
//
// Modified from ZXing. Copyright ZXing authors.
// Licensed under the Apache License, Version 2.0 (the "License").
#ifndef __ZXING_COMMON_SIMPLEADAPTIVEBINARIZER_HPP__
#define __ZXING_COMMON_SIMPLEADAPTIVEBINARIZER_HPP__
#include "../../binarizer.hpp"
#include "../array.hpp"
#include "../bitarray.hpp"
#include "../bitmatrix.hpp"
#include "global_histogram_binarizer.hpp"
namespace zxing {
class SimpleAdaptiveBinarizer : public GlobalHistogramBinarizer {
public:
explicit SimpleAdaptiveBinarizer(Ref<LuminanceSource> source);
virtual ~SimpleAdaptiveBinarizer();
virtual Ref<BitArray> getBlackRow(int y, Ref<BitArray> row, ErrorHandler &err_handler) override;
virtual Ref<BitMatrix> getBlackMatrix(ErrorHandler &err_handler) override;
Ref<Binarizer> createBinarizer(Ref<LuminanceSource> source) override;
private:
int binarizeImage0(ErrorHandler &err_handler);
int qrBinarize(const unsigned char *src, unsigned char *dst);
bool filtered;
};
} // namespace zxing
#endif // __ZXING_COMMON_SIMPLEADAPTIVEBINARIZER_HPP__

View File

@@ -0,0 +1,239 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
//
// Modified from ZXing. Copyright ZXing authors.
// Licensed under the Apache License, Version 2.0 (the "License").
#include "../../precomp.hpp"
#include "bitarray.hpp"
using zxing::ArrayRef;
using zxing::BitArray;
using zxing::ErrorHandler;
using zxing::Ref;
#if __WORDSIZE == 64
// typedef long int int64_t;
#else
typedef long long int int64_t;
#endif
BitArray::BitArray(int size_) : size(size_), bits(size_), nextSets(size_), nextUnSets(size_) {}
void BitArray::setUnchar(int i, unsigned char newBits) { bits[i] = newBits; }
bool BitArray::isRange(int start, int end, bool value, ErrorHandler &err_handler) {
if (end < start) {
err_handler = IllegalArgumentErrorHandler("isRange");
return false;
}
if (start < 0 || end >= bits->size()) {
err_handler = IllegalArgumentErrorHandler("isRange");
return false;
}
if (end == start) {
return true; // empty range matches
}
bool startBool = bits[start] != (unsigned char)0;
int end2 = start;
if (startBool) {
end2 = getNextUnset(start);
} else {
end2 = getNextSet(start);
}
if (startBool == value) {
if (end2 < end) {
return false;
}
} else {
return false;
}
return true;
}
void BitArray::reverse() {
bool *rowBits = getRowBoolPtr();
bool tempBit;
for (int i = 0; i < size / 2; i++) {
tempBit = rowBits[i];
rowBits[i] = rowBits[size - i - 1];
rowBits[size - i - 1] = tempBit;
}
}
void BitArray::initAllNextSets() {
bool *rowBits = getRowBoolPtr();
int *nextSetArray = nextSets->data();
int *nextUnsetArray = nextUnSets->data();
// Init the last one
if (rowBits[size - 1]) {
nextSetArray[size - 1] = size - 1;
nextUnsetArray[size - 1] = size;
} else {
nextUnsetArray[size - 1] = size - 1;
nextSetArray[size - 1] = size;
}
// do inits
for (int i = size - 2; i >= 0; i--) {
if (rowBits[i]) {
nextSetArray[i] = i;
nextUnsetArray[i] = nextUnsetArray[i + 1];
} else {
nextUnsetArray[i] = i;
nextSetArray[i] = nextSetArray[i + 1];
}
}
}
void BitArray::initAllNextSetsFromCounters(std::vector<int> counters) {
bool *rowBits = getRowBoolPtr();
bool isWhite = rowBits[0];
int c = 0;
int offset = 0;
int count = 0;
int prevCount = 0;
int currCount = 0;
int _size = counters.size();
int *nextSetArray = nextSets->data();
int *nextUnsetArray = nextUnSets->data();
// int* countersArray = counters.data();
int *countersArray = &counters[0];
while (c < _size) {
currCount = countersArray[c];
count += currCount;
if (isWhite) {
for (int i = 0; i < currCount; i++) {
offset = prevCount + i;
nextSetArray[offset] = prevCount + i;
nextUnsetArray[offset] = count;
}
} else {
for (int i = 0; i < currCount; i++) {
offset = prevCount + i;
nextSetArray[offset] = count;
nextUnsetArray[offset] = prevCount + i;
}
}
isWhite = !isWhite;
prevCount += currCount;
c++;
}
}
int BitArray::getNextSet(int from) {
if (from >= size) {
return size;
}
return nextSets[from];
}
int BitArray::getNextUnset(int from) {
if (from >= size) {
return size;
}
return nextUnSets[from];
}
BitArray::~BitArray() {}
int BitArray::getSize() const { return size; }
void BitArray::clear() {
int max = bits->size();
for (int i = 0; i < max; i++) {
bits[i] = 0;
}
}
BitArray::Reverse::Reverse(Ref<BitArray> array_) : array(array_) { array->reverse(); }
BitArray::Reverse::~Reverse() { array->reverse(); }
void BitArray::appendBit(bool value) {
ArrayRef<unsigned char> newBits(size + 1);
for (int i = 0; i < size; i++) {
newBits[i] = bits[i];
}
bits = newBits;
if (value) {
set(size);
}
++size;
}
int BitArray::getSizeInBytes() const { return size; }
// Appends the least-significant bits, from value, in order from
// most-significant to least-significant. For example, appending 6 bits
// from 0x000001E will append the bits 0, 1, 1, 1, 1, 0 in that order.
void BitArray::appendBits(int value, int numBits, ErrorHandler &err_handler) {
if (numBits < 0 || numBits > 32) {
err_handler = IllegalArgumentErrorHandler("Number of bits must be between 0 and 32");
return;
}
ArrayRef<unsigned char> newBits(size + numBits);
for (int i = 0; i < size; i++) newBits[i] = bits[i];
bits = newBits;
for (int numBitsLeft = numBits; numBitsLeft > 0; numBitsLeft--) {
if (((value >> (numBitsLeft - 1)) & 0x01) == 1) {
set(size);
}
++size;
}
return;
}
void BitArray::appendBitArray(const BitArray &array) {
ArrayRef<unsigned char> newBits(size + array.getSize());
for (int i = 0; i < size; ++i) {
newBits[i] = bits[i];
}
bits = newBits;
for (int i = 0; i < array.getSize(); ++i) {
if (array.get(i)) {
set(size);
}
++size;
}
}
void BitArray::toBytes(int bitOffset, ArrayRef<int> &array, int offset, int numBytes) {
for (int i = 0; i < numBytes; i++) {
int theByte = 0;
if (get(bitOffset)) {
theByte = 1;
}
bitOffset++;
array[offset + i] = theByte;
}
}
void BitArray::bitXOR(const BitArray &other, ErrorHandler &err_handler) {
if (size != other.size) {
err_handler = IllegalArgumentErrorHandler("Sizes don't match");
return;
}
for (int i = 0; i < bits->size(); i++) {
bits[i] = bits[i] == other.bits[i] ? 0 : 1;
}
}

View File

@@ -0,0 +1,88 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
//
// Modified from ZXing. Copyright ZXing authors.
// Licensed under the Apache License, Version 2.0 (the "License").
#ifndef __ZXING_COMMON_BITARRAY_HPP__
#define __ZXING_COMMON_BITARRAY_HPP__
#include "../errorhandler.hpp"
#include "../zxing.hpp"
#include "array.hpp"
#include "counted.hpp"
#include <cstring>
namespace zxing {
class BitArray : public Counted {
private:
int size;
ArrayRef<unsigned char> bits;
ArrayRef<int> nextSets;
ArrayRef<int> nextUnSets;
// bool nextSetsInited;
public:
explicit BitArray(int size);
~BitArray();
int getSize() const;
bool get(int i) const { return bits[i] != 0; }
void set(int i) {
// bits[i] |= 0xFF;
bits[i] = true;
}
void setOneRow(unsigned char* rowBits, int length) {
unsigned char* dst = bits->data();
memcpy(dst, rowBits, length);
}
bool* getRowBoolPtr() {
// return (bool*)bits.data();
return (bool*)bits->data();
}
// Init for next sets and unsets to speed up
void initAllNextSets();
void initAllNextSetsFromCounters(std::vector<int> counters);
int getNextSet(int from);
int getNextUnset(int from);
void setUnchar(int i, unsigned char newBist);
void clear();
bool isRange(int start, int end, bool value, ErrorHandler& err_handler);
void reverse();
class Reverse {
private:
Ref<BitArray> array;
public:
explicit Reverse(Ref<BitArray> array);
~Reverse();
};
void appendBit(bool value);
int getSizeInBytes() const;
void appendBits(int value, int numberOfBits, ErrorHandler& err_handler);
void appendBitArray(const BitArray& array);
void toBytes(int bitOffset, ArrayRef<int>& array, int offset, int numBytes);
void bitXOR(const BitArray& other, ErrorHandler& err_handler);
#ifndef USE_BYTE_FOR_BIT
private:
static int makeArraySize(int size);
#endif
};
} // namespace zxing
#endif // __ZXING_COMMON_BITARRAY_HPP__

View File

@@ -0,0 +1,397 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
//
// Modified from ZXing. Copyright ZXing authors.
// Licensed under the Apache License, Version 2.0 (the "License").
#include "../../precomp.hpp"
#include "bitmatrix.hpp"
using zxing::ArrayRef;
using zxing::BitArray;
using zxing::BitMatrix;
using zxing::ErrorHandler;
using zxing::Ref;
void BitMatrix::init(int _width, int _height, ErrorHandler& err_handler) {
if (_width < 1 || _height < 1) {
err_handler = IllegalArgumentErrorHandler("Both dimensions must be greater than 0");
return;
}
width = _width;
height = _height;
this->rowBitsSize = width;
bits = ArrayRef<unsigned char>(width * height);
rowOffsets = ArrayRef<int>(height);
// offsetRowSize = new int[height];
rowOffsets[0] = 0;
for (int i = 1; i < height; i++) {
rowOffsets[i] = rowOffsets[i - 1] + width;
}
isInitRowCounters = false;
isInitColsCounters = false;
}
void BitMatrix::init(int _width, int _height, unsigned char* bitsPtr, ErrorHandler& err_handler) {
init(_width, _height, err_handler);
if (err_handler.ErrCode()) return;
memcpy(bits->data(), bitsPtr, width * height * sizeof(unsigned char));
}
void BitMatrix::initRowCounters() {
if (isInitRowCounters == true) {
return;
}
row_counters = vector<COUNTER_TYPE>(width * height, 0);
row_counters_offset = vector<COUNTER_TYPE>(width * height, 0);
row_point_offset = vector<COUNTER_TYPE>(width * height, 0);
row_counter_offset_end = vector<COUNTER_TYPE>(height, 0);
row_counters_recorded = vector<bool>(height, false);
isInitRowCounters = true;
}
void BitMatrix::initColsCounters() {
if (isInitColsCounters == true) {
return;
}
cols_counters = vector<COUNTER_TYPE>(width * height, 0);
cols_counters_offset = vector<COUNTER_TYPE>(width * height, 0);
cols_point_offset = vector<COUNTER_TYPE>(width * height, 0);
cols_counter_offset_end = vector<COUNTER_TYPE>(width, 0);
cols_counters_recorded = vector<bool>(width, false);
isInitColsCounters = true;
}
BitMatrix::BitMatrix(int dimension, ErrorHandler& err_handler) {
init(dimension, dimension, err_handler);
}
BitMatrix::BitMatrix(int _width, int _height, ErrorHandler& err_handler) {
init(_width, _height, err_handler);
}
BitMatrix::BitMatrix(int _width, int _height, unsigned char* bitsPtr, ErrorHandler& err_handler) {
init(_width, _height, bitsPtr, err_handler);
}
// Copy bitMatrix
void BitMatrix::copyOf(Ref<BitMatrix> _bits, ErrorHandler& err_handler) {
int _width = _bits->getWidth();
int _height = _bits->getHeight();
init(_width, _height, err_handler);
for (int y = 0; y < height; y++) {
bool* rowPtr = _bits->getRowBoolPtr(y);
setRowBool(y, rowPtr);
}
}
void BitMatrix::xxor(Ref<BitMatrix> _bits) {
if (width != _bits->getWidth() || height != _bits->getHeight()) {
return;
}
for (int y = 0; y < height && y < _bits->getHeight(); ++y) {
bool* rowPtrA = _bits->getRowBoolPtr(y);
bool* rowPtrB = getRowBoolPtr(y);
for (int x = 0; x < width && x < _bits->getWidth(); ++x) {
rowPtrB[x] = rowPtrB[x] ^ rowPtrA[x];
}
setRowBool(y, rowPtrB);
}
}
BitMatrix::~BitMatrix() {}
void BitMatrix::flip(int x, int y) {
bits[rowOffsets[y] + x] = (bits[rowOffsets[y] + x] == (unsigned char)0);
}
void BitMatrix::flipAll() {
bool* matrixBits = (bool*)bits->data();
for (int i = 0; i < bits->size(); i++) {
matrixBits[i] = !matrixBits[i];
}
}
void BitMatrix::flipRegion(int left, int top, int _width, int _height, ErrorHandler& err_handler) {
if (top < 0 || left < 0) {
err_handler = IllegalArgumentErrorHandler("Left and top must be nonnegative");
return;
}
if (_height < 1 || _width < 1) {
err_handler = IllegalArgumentErrorHandler("Height and width must be at least 1");
return;
}
int right = left + _width;
int bottom = top + _height;
if (bottom > this->height || right > this->width) {
err_handler = IllegalArgumentErrorHandler("The region must fit inside the matrix");
return;
}
for (int y = top; y < bottom; y++) {
for (int x = left; x < right; x++) {
bits[rowOffsets[y] + x] ^= 1;
}
}
}
void BitMatrix::setRegion(int left, int top, int _width, int _height, ErrorHandler& err_handler) {
if (top < 0 || left < 0) {
err_handler = IllegalArgumentErrorHandler("Left and top must be nonnegative");
return;
}
if (_height < 1 || _width < 1) {
err_handler = IllegalArgumentErrorHandler("Height and width must be at least 1");
return;
}
int right = left + _width;
int bottom = top + _height;
if (bottom > this->height || right > this->width) {
err_handler = IllegalArgumentErrorHandler("The region must fit inside the matrix");
return;
}
for (int y = top; y < bottom; y++) {
for (int x = left; x < right; x++) {
bits[rowOffsets[y] + x] = true;
// bits[rowOffsets[y]+x] |= 0xFF;
}
}
}
Ref<BitArray> BitMatrix::getRow(int y, Ref<BitArray> row) {
if (row.empty() || row->getSize() < width) {
row = new BitArray(width);
}
// row->
unsigned char* src = bits.data() + rowOffsets[y];
row->setOneRow(src, width);
return row;
}
ArrayRef<int> BitMatrix::getTopLeftOnBit() const {
int bitsOffset = 0;
while (bitsOffset < bits->size() && bits[bitsOffset] == 0) {
bitsOffset++;
}
if (bitsOffset == bits->size()) {
return ArrayRef<int>();
}
int y = bitsOffset / width;
int x = bitsOffset % width;
ArrayRef<int> res(2);
res[0] = x;
res[1] = y;
return res;
}
ArrayRef<int> BitMatrix::getBottomRightOnBit() const {
int bitsOffset = bits->size() - 1;
while (bitsOffset >= 0 && bits[bitsOffset] == 0) {
bitsOffset--;
}
if (bitsOffset < 0) {
return ArrayRef<int>();
}
int y = bitsOffset / width;
int x = bitsOffset % width;
ArrayRef<int> res(2);
res[0] = x;
res[1] = y;
return res;
}
void BitMatrix::getRowBool(int y, bool* getrow) {
int offset = rowOffsets[y];
unsigned char* src = bits.data() + offset;
memcpy(getrow, src, rowBitsSize * sizeof(bool));
}
void BitMatrix::setRowBool(int y, bool* row) {
int offset = rowOffsets[y];
unsigned char* dst = bits.data() + offset;
memcpy(dst, row, rowBitsSize * sizeof(bool));
return;
}
bool* BitMatrix::getRowBoolPtr(int y) {
int offset = y * rowBitsSize;
unsigned char* src = bits.data() + offset;
return (bool*)src;
}
void BitMatrix::clear() {
int size = bits->size();
unsigned char* dst = bits->data();
memset(dst, 0, size * sizeof(unsigned char));
}
int BitMatrix::getWidth() const { return width; }
int BitMatrix::getHeight() const { return height; }
COUNTER_TYPE* BitMatrix::getRowPointInRecords(int y) {
if (!row_point_offset[y]) {
setRowRecords(y);
}
int offset = y * width;
COUNTER_TYPE* counters = &row_point_offset[0] + offset;
return (COUNTER_TYPE*)counters;
}
COUNTER_TYPE* BitMatrix::getRowRecords(int y) {
if (!row_counters_recorded[y]) {
setRowRecords(y);
}
int offset = y * width;
COUNTER_TYPE* counters = &row_counters[0] + offset;
return (COUNTER_TYPE*)counters;
}
COUNTER_TYPE* BitMatrix::getRowRecordsOffset(int y) {
if (!row_counters_recorded[y]) {
setRowRecords(y);
}
int offset = y * width;
COUNTER_TYPE* counters = &row_counters_offset[0] + offset;
return (COUNTER_TYPE*)counters;
}
bool BitMatrix::getRowFirstIsWhite(int y) {
bool is_white = !get(0, y);
return is_white;
}
bool BitMatrix::getRowLastIsWhite(int y) {
bool last_is_white = !get(width - 1, y);
return last_is_white;
}
COUNTER_TYPE BitMatrix::getRowCounterOffsetEnd(int y) {
if (!row_counters_recorded[y]) {
setRowRecords(y);
}
return row_counter_offset_end[y];
}
void BitMatrix::setRowRecords(int y) {
COUNTER_TYPE* cur_row_counters = &row_counters[0] + y * width;
COUNTER_TYPE* cur_row_counters_offset = &row_counters_offset[0] + y * width;
COUNTER_TYPE* cur_row_point_in_counters = &row_point_offset[0] + y * width;
int end = width;
bool* rowBit = getRowBoolPtr(y);
bool isWhite = !rowBit[0];
int counterPosition = 0;
int i = 0;
cur_row_counters_offset[0] = 0;
while (i < end) {
if (rowBit[i] ^ isWhite) { // that is, exactly one is true
cur_row_counters[counterPosition]++;
} else {
counterPosition++;
if (counterPosition == end) {
break;
} else {
cur_row_counters[counterPosition] = 1;
isWhite = !isWhite;
cur_row_counters_offset[counterPosition] = i;
}
}
cur_row_point_in_counters[i] = counterPosition;
i++;
}
// use the last row__onedReaderData->counter_size to record
// _onedReaderData->counter_size
row_counter_offset_end[y] = counterPosition < end ? (counterPosition + 1) : end;
row_counters_recorded[y] = true;
return;
}
COUNTER_TYPE* BitMatrix::getColsPointInRecords(int x) {
if (!cols_point_offset[x]) {
setColsRecords(x);
}
int offset = x * height;
COUNTER_TYPE* counters = &cols_point_offset[0] + offset;
return (COUNTER_TYPE*)counters;
}
COUNTER_TYPE* BitMatrix::getColsRecords(int x) {
if (!cols_counters_recorded[x]) {
setColsRecords(x);
}
int offset = x * height;
COUNTER_TYPE* counters = &cols_counters[0] + offset;
return (COUNTER_TYPE*)counters;
}
COUNTER_TYPE* BitMatrix::getColsRecordsOffset(int x) {
if (!cols_counters_recorded[x]) {
setColsRecords(x);
}
int offset = x * height;
COUNTER_TYPE* counters = &cols_counters_offset[0] + offset;
return (COUNTER_TYPE*)counters;
}
COUNTER_TYPE BitMatrix::getColsCounterOffsetEnd(int x) {
if (!cols_counters_recorded[x]) {
setColsRecords(x);
}
return cols_counter_offset_end[x];
}
void BitMatrix::setColsRecords(int x) {
COUNTER_TYPE* cur_cols_counters = &cols_counters[0] + x * height;
COUNTER_TYPE* cur_cols_counters_offset = &cols_counters_offset[0] + x * height;
COUNTER_TYPE* cur_cols_point_in_counters = &cols_point_offset[0] + x * height;
int end = height;
bool* rowBit = getRowBoolPtr(0);
bool isWhite = !rowBit[0];
int counterPosition = 0;
int i = 0;
cur_cols_counters_offset[0] = 0;
while (i < end) {
if (rowBit[i] ^ isWhite) { // that is, exactly one is true
cur_cols_counters[counterPosition]++;
} else {
counterPosition++;
if (counterPosition == end) {
break;
} else {
cur_cols_counters[counterPosition] = 1;
isWhite = !isWhite;
cur_cols_counters_offset[counterPosition] = i;
}
}
cur_cols_point_in_counters[i] = counterPosition;
i++;
rowBit += width;
}
cols_counter_offset_end[x] = counterPosition < end ? (counterPosition + 1) : end;
cols_counters_recorded[x] = true;
return;
};

View File

@@ -0,0 +1,115 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
//
// Modified from ZXing. Copyright ZXing authors.
// Licensed under the Apache License, Version 2.0 (the "License").
#ifndef __ZXING_COMMON_BITMATRIX_HPP__
#define __ZXING_COMMON_BITMATRIX_HPP__
#include "../errorhandler.hpp"
#include "array.hpp"
#include "bitarray.hpp"
#include "counted.hpp"
using namespace std;
namespace zxing {
class BitMatrix : public Counted {
public:
static const int bitsPerWord = std::numeric_limits<unsigned int>::digits;
private:
int width;
int height;
int rowBitsSize;
vector<COUNTER_TYPE> row_counters;
vector<COUNTER_TYPE> row_counters_offset;
vector<bool> row_counters_recorded;
vector<COUNTER_TYPE> row_counter_offset_end;
vector<COUNTER_TYPE> row_point_offset;
vector<COUNTER_TYPE> cols_counters;
vector<COUNTER_TYPE> cols_counters_offset;
vector<bool> cols_counters_recorded;
vector<COUNTER_TYPE> cols_counter_offset_end;
vector<COUNTER_TYPE> cols_point_offset;
ArrayRef<unsigned char> bits;
ArrayRef<int> rowOffsets;
public:
BitMatrix(int _width, int _height, unsigned char* bitsPtr, ErrorHandler& err_handler);
BitMatrix(int dimension, ErrorHandler& err_handler);
BitMatrix(int _width, int _height, ErrorHandler& err_handler);
void copyOf(Ref<BitMatrix> _bits, ErrorHandler& err_handler);
void xxor(Ref<BitMatrix> _bits);
~BitMatrix();
unsigned char get(int x, int y) const { return bits[width * y + x]; }
void set(int x, int y) { bits[rowOffsets[y] + x] = (unsigned char)1; }
void set(int x, int y, unsigned char value) { bits[rowOffsets[y] + x] = value; }
void swap(int srcX, int srcY, int dstX, int dstY) {
auto temp = bits[width * srcY + srcX];
bits[width * srcY + srcX] = bits[width * dstY + dstX];
bits[width * dstY + dstX] = temp;
}
void getRowBool(int y, bool* row);
bool* getRowBoolPtr(int y);
void setRowBool(int y, bool* row);
int getRowBitsSize() { return rowBitsSize; }
unsigned char* getPtr() { return bits->data(); }
void flip(int x, int y);
void flipAll();
void clear();
void setRegion(int left, int top, int _width, int _height, ErrorHandler& err_handler);
void flipRegion(int left, int top, int _width, int _height, ErrorHandler& err_handler);
Ref<BitArray> getRow(int y, Ref<BitArray> row);
int getWidth() const;
int getHeight() const;
ArrayRef<int> getTopLeftOnBit() const;
ArrayRef<int> getBottomRightOnBit() const;
bool isInitRowCounters;
void initRowCounters();
COUNTER_TYPE* getRowRecords(int y);
COUNTER_TYPE* getRowRecordsOffset(int y);
bool getRowFirstIsWhite(int y);
COUNTER_TYPE getRowCounterOffsetEnd(int y);
bool getRowLastIsWhite(int y);
COUNTER_TYPE* getRowPointInRecords(int y);
bool isInitColsCounters;
void initColsCounters();
COUNTER_TYPE* getColsRecords(int x);
COUNTER_TYPE* getColsRecordsOffset(int x);
COUNTER_TYPE* getColsPointInRecords(int x);
COUNTER_TYPE getColsCounterOffsetEnd(int x);
private:
inline void init(int, int, ErrorHandler& err_handler);
inline void init(int _width, int _height, unsigned char* bitsPtr, ErrorHandler& err_handler);
void setRowRecords(int y);
void setColsRecords(int x);
BitMatrix(const BitMatrix&, ErrorHandler& err_handler);
};
} // namespace zxing
#endif // __ZXING_COMMON_BITMATRIX_HPP__

View File

@@ -0,0 +1,62 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
//
// Modified from ZXing. Copyright ZXing authors.
// Licensed under the Apache License, Version 2.0 (the "License").
#include "../../precomp.hpp"
#include "bitsource.hpp"
#include <sstream>
namespace zxing {
int BitSource::readBits(int numBits, ErrorHandler& err_handler) {
if (numBits < 0 || numBits > 32 || numBits > available()) {
std::ostringstream oss;
oss << numBits;
err_handler = IllegalArgumentErrorHandler(oss.str().c_str());
return -1;
}
int result = 0;
// First, read remainder from current byte
if (bitOffset_ > 0) {
int bitsLeft = 8 - bitOffset_;
int toRead = numBits < bitsLeft ? numBits : bitsLeft;
int bitsToNotRead = bitsLeft - toRead;
int mask = (0xFF >> (8 - toRead)) << bitsToNotRead;
result = (bytes_[byteOffset_] & mask) >> bitsToNotRead;
numBits -= toRead;
bitOffset_ += toRead;
if (bitOffset_ == 8) {
bitOffset_ = 0;
byteOffset_++;
}
}
// Next read whole bytes
if (numBits > 0) {
while (numBits >= 8) {
result = (result << 8) | (bytes_[byteOffset_] & 0xFF);
byteOffset_++;
numBits -= 8;
}
// Finally read a partial byte
if (numBits > 0) {
int bitsToNotRead = 8 - numBits;
int mask = (0xFF >> bitsToNotRead) << bitsToNotRead;
result = (result << numBits) | ((bytes_[byteOffset_] & mask) >> bitsToNotRead);
bitOffset_ += numBits;
}
}
return result;
}
int BitSource::available() { return 8 * (bytes_->size() - byteOffset_) - bitOffset_; }
} // namespace zxing

View File

@@ -0,0 +1,57 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
//
// Modified from ZXing. Copyright ZXing authors.
// Licensed under the Apache License, Version 2.0 (the "License").
#ifndef __ZXING_COMMON_BITSOURCE_HPP__
#define __ZXING_COMMON_BITSOURCE_HPP__
#include "../errorhandler.hpp"
#include "array.hpp"
namespace zxing {
/**
* <p>This provides an easy abstraction to read bits at a time from a sequence
* of bytes, where the number of bits read is not often a multiple of 8.</p>
*
* <p>This class is not thread-safe.</p>
*
* @author srowen@google.com (Sean Owen)
* @author christian.brunschen@gmail.com (Christian Brunschen)
*/
class BitSource : public Counted {
typedef char byte;
private:
ArrayRef<byte> bytes_;
int byteOffset_;
int bitOffset_;
public:
/**
* @param bytes bytes from which this will read bits. Bits will be read from
* the first byte first. Bits are read within a byte from most-significant
* to least-significant bit.
*/
explicit BitSource(ArrayRef<byte> &bytes) : bytes_(bytes), byteOffset_(0), bitOffset_(0) {}
int getBitOffset() { return bitOffset_; }
int getByteOffset() { return byteOffset_; }
int readBits(int numBits, ErrorHandler &err_handler);
/**
* @return number of bits that can be read successfully
*/
int available();
};
} // namespace zxing
#endif // __ZXING_COMMON_BITSOURCE_HPP__

View File

@@ -0,0 +1,50 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
#include "../../precomp.hpp"
#include "bytematrix.hpp"
using zxing::ArrayRef;
using zxing::ByteMatrix;
using zxing::ErrorHandler;
using zxing::Ref;
void ByteMatrix::init(int _width, int _height) {
if (_width < 1 || _height < 1) {
return;
}
this->width = _width;
this->height = _height;
bytes = new unsigned char[width * height];
row_offsets = new int[height];
row_offsets[0] = 0;
for (int i = 1; i < height; i++) {
row_offsets[i] = row_offsets[i - 1] + width;
}
}
ByteMatrix::ByteMatrix(int dimension) { init(dimension, dimension); }
ByteMatrix::ByteMatrix(int _width, int _height) { init(_width, _height); }
ByteMatrix::ByteMatrix(int _width, int _height, ArrayRef<char> source) {
init(_width, _height);
int size = _width * _height;
memcpy(&bytes[0], &source[0], size);
}
ByteMatrix::~ByteMatrix() {
if (bytes) delete[] bytes;
if (row_offsets) delete[] row_offsets;
}
unsigned char* ByteMatrix::getByteRow(int y, ErrorHandler& err_handler) {
if (y < 0 || y >= getHeight()) {
err_handler = IllegalArgumentErrorHandler("Requested row is outside the image.");
return NULL;
}
return &bytes[row_offsets[y]];
}

View File

@@ -0,0 +1,58 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
#ifndef __ZXING_COMMON_BYTEMATRIX_HPP__
#define __ZXING_COMMON_BYTEMATRIX_HPP__
#include "../errorhandler.hpp"
#include "array.hpp"
#include "bitarray.hpp"
#include "counted.hpp"
namespace zxing {
class ByteMatrix : public Counted {
public:
explicit ByteMatrix(int dimension);
ByteMatrix(int _width, int _height);
ByteMatrix(int _width, int _height, ArrayRef<char> source);
~ByteMatrix();
char get(int x, int y) const {
int offset = row_offsets[y] + x;
return bytes[offset];
}
void set(int x, int y, char char_value) {
int offset = row_offsets[y] + x;
bytes[offset] = char_value & 0XFF;
}
unsigned char* getByteRow(int y, ErrorHandler& err_handler);
int getWidth() const { return width; }
int getHeight() const { return height; }
unsigned char* bytes;
private:
int width;
int height;
// ArrayRef<char> bytes;
// ArrayRef<int> row_offsets;
int* row_offsets;
private:
inline void init(int, int);
ByteMatrix(const ByteMatrix&);
ByteMatrix& operator=(const ByteMatrix&);
};
} // namespace zxing
#endif // __ZXING_COMMON_BYTEMATRIX_HPP__

View File

@@ -0,0 +1,111 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
//
// Modified from ZXing. Copyright ZXing authors.
// Licensed under the Apache License, Version 2.0 (the "License").
#include "../../precomp.hpp"
#include "characterseteci.hpp"
using zxing::common::CharacterSetECI;
// Fix memory leak
// https://github.com/ukeller/zxing-cpp/commit/c632ffe47ca7342f894ae533263be249cbdfd37e
// std::map<int, CharacterSetECI*> CharacterSetECI::VALUE_TO_ECI;
// std::map<std::string, CharacterSetECI*> CharacterSetECI::NAME_TO_ECI;
std::map<int, zxing::Ref<CharacterSetECI> > CharacterSetECI::VALUE_TO_ECI;
std::map<std::string, zxing::Ref<CharacterSetECI> > CharacterSetECI::NAME_TO_ECI;
const bool CharacterSetECI::inited = CharacterSetECI::init_tables();
#define ADD_CHARACTER_SET(VALUES, STRINGS) \
{ \
static int values[] = {VALUES, -1}; \
static char const* strings[] = {STRINGS, 0}; \
addCharacterSet(values, strings); \
}
#define XC ,
bool CharacterSetECI::init_tables() {
ADD_CHARACTER_SET(0 XC 2, "Cp437");
ADD_CHARACTER_SET(1 XC 3, "ISO8859_1" XC "ISO-8859-1");
ADD_CHARACTER_SET(4, "ISO8859_2" XC "ISO-8859-2");
ADD_CHARACTER_SET(5, "ISO8859_3" XC "ISO-8859-3");
ADD_CHARACTER_SET(6, "ISO8859_4" XC "ISO-8859-4");
ADD_CHARACTER_SET(7, "ISO8859_5" XC "ISO-8859-5");
ADD_CHARACTER_SET(8, "ISO8859_6" XC "ISO-8859-6");
ADD_CHARACTER_SET(9, "ISO8859_7" XC "ISO-8859-7");
ADD_CHARACTER_SET(10, "ISO8859_8" XC "ISO-8859-8");
ADD_CHARACTER_SET(11, "ISO8859_9" XC "ISO-8859-9");
ADD_CHARACTER_SET(12, "ISO8859_10" XC "ISO-8859-10");
ADD_CHARACTER_SET(13, "ISO8859_11" XC "ISO-8859-11");
ADD_CHARACTER_SET(15, "ISO8859_13" XC "ISO-8859-13");
ADD_CHARACTER_SET(16, "ISO8859_14" XC "ISO-8859-14");
ADD_CHARACTER_SET(17, "ISO8859_15" XC "ISO-8859-15");
ADD_CHARACTER_SET(18, "ISO8859_16" XC "ISO-8859-16");
ADD_CHARACTER_SET(20, "SJIS" XC "Shift_JIS");
ADD_CHARACTER_SET(21, "Cp1250" XC "windows-1250");
ADD_CHARACTER_SET(22, "Cp1251" XC "windows-1251");
ADD_CHARACTER_SET(23, "Cp1252" XC "windows-1252");
ADD_CHARACTER_SET(24, "Cp1256" XC "windows-1256");
ADD_CHARACTER_SET(25, "UnicodeBigUnmarked" XC "UTF-16BE" XC "UnicodeBig");
ADD_CHARACTER_SET(26, "UTF8" XC "UTF-8");
ADD_CHARACTER_SET(27 XC 170, "ASCII" XC "US-ASCII");
ADD_CHARACTER_SET(28, "Big5");
ADD_CHARACTER_SET(29, "GB18030" XC "GB2312" XC "EUC_CN" XC "GBK");
ADD_CHARACTER_SET(30, "EUC_KR" XC "EUC-KR");
return true;
}
#undef XC
CharacterSetECI::CharacterSetECI(int const* values, char const* const* names)
: values_(values), names_(names) {
zxing::Ref<CharacterSetECI> this_ref(this);
for (int const* p_values = values_; *p_values != -1; p_values++) {
// VALUE_TO_ECI[*values] = this;
VALUE_TO_ECI[*p_values] = this_ref;
}
for (char const* const* p_names = names_; *p_names; p_names++) {
// NAME_TO_ECI[string(*names)] = this;
NAME_TO_ECI[string(*p_names)] = this_ref;
}
}
char const* CharacterSetECI::name() const { return names_[0]; }
int CharacterSetECI::getValue() const { return values_[0]; }
void CharacterSetECI::addCharacterSet(int const* values, char const* const* names) {
new CharacterSetECI(values, names);
}
CharacterSetECI* CharacterSetECI::getCharacterSetECIByValueFind(int value) {
if (value < 0 || value >= 900) {
return zxing::Ref<CharacterSetECI>(0);
}
std::map<int, zxing::Ref<CharacterSetECI> >::iterator iter;
iter = VALUE_TO_ECI.find(value);
if (iter != VALUE_TO_ECI.end()) {
return iter->second;
} else {
return zxing::Ref<CharacterSetECI>(0);
}
}
CharacterSetECI* CharacterSetECI::getCharacterSetECIByName(string const& name) {
std::map<std::string, zxing::Ref<CharacterSetECI> >::iterator iter;
iter = NAME_TO_ECI.find(name);
if (iter != NAME_TO_ECI.end()) {
return iter->second;
} else {
return zxing::Ref<CharacterSetECI>(0);
}
}

View File

@@ -0,0 +1,46 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
//
// Modified from ZXing. Copyright ZXing authors.
// Licensed under the Apache License, Version 2.0 (the "License").
#ifndef __ZXING_COMMON_CHARACTERSETECI_HPP__
#define __ZXING_COMMON_CHARACTERSETECI_HPP__
#include <map>
#include "../decodehints.hpp"
#include "counted.hpp"
namespace zxing {
namespace common {
class CharacterSetECI : public Counted {
private:
static std::map<int, zxing::Ref<CharacterSetECI> > VALUE_TO_ECI;
static std::map<std::string, zxing::Ref<CharacterSetECI> > NAME_TO_ECI;
static const bool inited;
static bool init_tables();
int const* const values_;
char const* const* const names_;
CharacterSetECI(int const* values, char const* const* names);
static void addCharacterSet(int const* value, char const* const* encodingNames);
public:
char const* name() const;
int getValue() const;
static CharacterSetECI* getCharacterSetECIByValueFind(int value);
static CharacterSetECI* getCharacterSetECIByName(std::string const& name);
};
} // namespace common
} // namespace zxing
#endif // __ZXING_COMMON_CHARACTERSETECI_HPP__

View File

@@ -0,0 +1,110 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
//
// Modified from ZXing. Copyright ZXing authors.
// Licensed under the Apache License, Version 2.0 (the "License").
#ifndef __ZXING_COMMON_COUNTED_HPP__
#define __ZXING_COMMON_COUNTED_HPP__
#include <cstddef>
#include <algorithm>
namespace zxing {
/* base class for reference-counted objects */
class Counted {
private:
unsigned int count_;
public:
Counted() : count_(0) {}
virtual ~Counted() {}
Counted* retain() {
count_++;
return this;
}
void release() {
count_--;
if (count_ == 0) {
count_ = 0xDEADF001;
delete this;
}
}
/* return the current count for denugging purposes or similar */
int count() const { return count_; }
};
/* counting reference to reference-counted objects */
template <typename T>
class Ref {
private:
public:
T* object_;
explicit Ref(T* o = 0) : object_(0) { reset(o); }
Ref(const Ref& other) : object_(0) { reset(other.object_); }
template <class Y>
Ref(const Ref<Y>& other) : object_(0) {
reset(other.object_);
}
~Ref() {
if (object_) {
object_->release();
}
}
void reset(T* o) {
if (o) {
o->retain();
}
if (object_ != 0) {
object_->release();
}
object_ = o;
}
Ref& operator=(const Ref& other) {
reset(other.object_);
return *this;
}
template <class Y>
Ref& operator=(const Ref<Y>& other) {
reset(other.object_);
return *this;
}
Ref& operator=(T* o) {
reset(o);
return *this;
}
template <class Y>
Ref& operator=(Y* o) {
reset(o);
return *this;
}
T& operator*() { return *object_; }
T* operator->() const { return object_; }
operator T*() const { return object_; }
bool operator==(const T* that) { return object_ == that; }
bool operator==(const Ref& other) const {
return object_ == other.object_ || *object_ == *(other.object_);
}
template <class Y>
bool operator==(const Ref<Y>& other) const {
return object_ == other.object_ || *object_ == *(other.object_);
}
bool operator!=(const T* that) { return !(*this == that); }
bool empty() const { return object_ == 0; }
};
} // namespace zxing
#endif // __ZXING_COMMON_COUNTED_HPP__

View File

@@ -0,0 +1,65 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
//
// Modified from ZXing. Copyright ZXing authors.
// Licensed under the Apache License, Version 2.0 (the "License").
#include "../../precomp.hpp"
#include "decoder_result.hpp"
using zxing::DecoderResult;
using zxing::Ref;
using zxing::ArrayRef;
using zxing::String;
DecoderResult::DecoderResult(ArrayRef<char> rawBytes, Ref<String> text,
ArrayRef<ArrayRef<char> >& byteSegments, string const& ecLevel)
: rawBytes_(rawBytes), text_(text), byteSegments_(byteSegments), ecLevel_(ecLevel) {
outputCharset_ = "UTF-8";
otherClassName = "";
qrcodeVersion_ = -1;
}
DecoderResult::DecoderResult(ArrayRef<char> rawBytes, Ref<String> text,
ArrayRef<ArrayRef<char> >& byteSegments, string const& ecLevel,
string outputCharset)
: rawBytes_(rawBytes),
text_(text),
byteSegments_(byteSegments),
ecLevel_(ecLevel),
outputCharset_(outputCharset) {
otherClassName = "";
qrcodeVersion_ = -1;
}
DecoderResult::DecoderResult(ArrayRef<char> rawBytes, Ref<String> text,
ArrayRef<ArrayRef<char> >& byteSegments, string const& ecLevel,
string outputCharset, int qrcodeVersion, string& charsetMode)
: rawBytes_(rawBytes),
text_(text),
byteSegments_(byteSegments),
ecLevel_(ecLevel),
outputCharset_(outputCharset),
qrcodeVersion_(qrcodeVersion),
charsetMode_(charsetMode) {
otherClassName = "";
}
DecoderResult::DecoderResult(ArrayRef<char> rawBytes, Ref<String> text)
: rawBytes_(rawBytes), text_(text) {
outputCharset_ = "UTF-8";
otherClassName = "";
}
DecoderResult::DecoderResult(ArrayRef<char> rawBytes, Ref<String> text, std::string outputCharset)
: rawBytes_(rawBytes), text_(text), outputCharset_(outputCharset) {
otherClassName = "";
}
ArrayRef<char> DecoderResult::getRawBytes() { return rawBytes_; }
Ref<String> DecoderResult::getText() { return text_; }
string DecoderResult::getCharset() { return outputCharset_; }

View File

@@ -0,0 +1,77 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
//
// Modified from ZXing. Copyright ZXing authors.
// Licensed under the Apache License, Version 2.0 (the "License").
#ifndef __ZXING_COMMON_DECODER_RESULT_HPP__
#define __ZXING_COMMON_DECODER_RESULT_HPP__
#include "../qrcode/decoder/qrcode_decoder_metadata.hpp"
#include "array.hpp"
#include "counted.hpp"
#include "str.hpp"
namespace zxing {
class DecoderResult : public Counted {
private:
ArrayRef<char> rawBytes_;
Ref<String> text_;
ArrayRef<ArrayRef<char> > byteSegments_;
std::string ecLevel_;
std::string outputCharset_;
int qrcodeVersion_;
std::string charsetMode_;
Ref<qrcode::QRCodeDecoderMetaData> other_;
string otherClassName;
public:
DecoderResult(ArrayRef<char> rawBytes, Ref<String> text,
ArrayRef<ArrayRef<char> >& byteSegments, std::string const& ecLevel);
DecoderResult(ArrayRef<char> rawBytes, Ref<String> text,
ArrayRef<ArrayRef<char> >& byteSegments, std::string const& ecLevel,
std::string outputCharset);
DecoderResult(ArrayRef<char> rawBytes, Ref<String> text,
ArrayRef<ArrayRef<char> >& byteSegments, std::string const& ecLevel,
std::string outputCharset, int qrcodeVersion, std::string& charsetMode);
DecoderResult(ArrayRef<char> rawBytes, Ref<String> text);
DecoderResult(ArrayRef<char> rawBytes, Ref<String> text, std::string outputCharset);
ArrayRef<char> getRawBytes();
Ref<String> getText();
std::string getCharset();
void setOther(Ref<qrcode::QRCodeDecoderMetaData> other) {
other_ = other;
otherClassName = "QRCodeDecoderMetaData";
};
Ref<qrcode::QRCodeDecoderMetaData> getOther() {
// className = otherClassName;
return other_;
};
string getOtherClassName() { return otherClassName; };
int getQRCodeVersion() const { return qrcodeVersion_; };
void setText(Ref<String> text) { text_ = text; };
string getEcLevel() { return ecLevel_; }
string getCharsetMode() { return charsetMode_; }
};
} // namespace zxing
#endif // __ZXING_COMMON_DECODER_RESULT_HPP__

View File

@@ -0,0 +1,27 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
//
// Modified from ZXing. Copyright ZXing authors.
// Licensed under the Apache License, Version 2.0 (the "License").
#include "../../precomp.hpp"
#include "detector_result.hpp"
namespace zxing {
DetectorResult::DetectorResult(Ref<BitMatrix> bits, ArrayRef<Ref<ResultPoint> > points,
int dimension, float modulesize)
: bits_(bits), points_(points), dimension_(dimension), modulesize_(modulesize) {}
void DetectorResult::SetGray(Ref<ByteMatrix> gray) { gray_ = gray; }
Ref<BitMatrix> DetectorResult::getBits() { return bits_; }
Ref<ByteMatrix> DetectorResult::getGray() { return gray_; }
ArrayRef<Ref<ResultPoint> > DetectorResult::getPoints() { return points_; }
} // namespace zxing

View File

@@ -0,0 +1,42 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
//
// Modified from ZXing. Copyright ZXing authors.
// Licensed under the Apache License, Version 2.0 (the "License").
#ifndef __ZXING_COMMON_DETECTOR_RESULT_HPP__
#define __ZXING_COMMON_DETECTOR_RESULT_HPP__
#include "../resultpoint.hpp"
#include "array.hpp"
#include "bitmatrix.hpp"
#include "bytematrix.hpp"
#include "counted.hpp"
namespace zxing {
class DetectorResult : public Counted {
private:
Ref<BitMatrix> bits_;
Ref<ByteMatrix> gray_;
ArrayRef<Ref<ResultPoint> > points_;
public:
DetectorResult(Ref<BitMatrix> bits, ArrayRef<Ref<ResultPoint> > points, int dimension = 0,
float modulesize = 0);
DetectorResult(Ref<ByteMatrix> gray, ArrayRef<Ref<ResultPoint> > points, int dimension = 0,
float modulesize = 0);
Ref<BitMatrix> getBits();
Ref<ByteMatrix> getGray();
void SetGray(Ref<ByteMatrix> gray);
ArrayRef<Ref<ResultPoint> > getPoints();
int dimension_;
float modulesize_;
};
} // namespace zxing
#endif // __ZXING_COMMON_DETECTOR_RESULT_HPP__

View File

@@ -0,0 +1,77 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
//
// Modified from ZXing. Copyright ZXing authors.
// Licensed under the Apache License, Version 2.0 (the "License").
#include "../../precomp.hpp"
#include "greyscale_luminance_source.hpp"
#include "bytematrix.hpp"
#include "greyscale_rotated_luminance_source.hpp"
using zxing::ArrayRef;
using zxing::ByteMatrix;
using zxing::ErrorHandler;
using zxing::GreyscaleLuminanceSource;
using zxing::LuminanceSource;
using zxing::Ref;
GreyscaleLuminanceSource::GreyscaleLuminanceSource(ArrayRef<char> greyData, int dataWidth,
int dataHeight, int left, int top, int width,
int height, ErrorHandler& err_handler)
: Super(width, height),
greyData_(greyData),
dataWidth_(dataWidth),
dataHeight_(dataHeight),
left_(left),
top_(top) {
if (left + width > dataWidth || top + height > dataHeight || top < 0 || left < 0) {
err_handler = IllegalArgumentErrorHandler("Crop rectangle does not fit within image data.");
}
}
ArrayRef<char> GreyscaleLuminanceSource::getRow(int y, ArrayRef<char> row,
ErrorHandler& err_handler) const {
if (y < 0 || y >= this->getHeight()) {
err_handler = IllegalArgumentErrorHandler("Requested row is outside the image.");
return ArrayRef<char>();
}
int width = getWidth();
if (!row || row->size() < width) {
ArrayRef<char> temp(width);
row = temp;
}
int offset = (y + top_) * dataWidth_ + left_;
memcpy(&row[0], &greyData_[offset], width);
return row;
}
ArrayRef<char> GreyscaleLuminanceSource::getMatrix() const {
int size = getWidth() * getHeight();
ArrayRef<char> result(size);
if (left_ == 0 && top_ == 0 && dataWidth_ == getWidth() && dataHeight_ == getHeight()) {
memcpy(&result[0], &greyData_[0], size);
} else {
for (int row = 0; row < getHeight(); row++) {
memcpy(&result[row * getWidth()], &greyData_[(top_ + row) * dataWidth_ + left_],
getWidth());
}
}
return result;
}
Ref<LuminanceSource> GreyscaleLuminanceSource::rotateCounterClockwise(
ErrorHandler& err_handler) const {
// Intentionally flip the left, top, width, and height arguments as
// needed. dataWidth and dataHeight are always kept unrotated.
Ref<LuminanceSource> result(new GreyscaleRotatedLuminanceSource(
greyData_, dataWidth_, dataHeight_, top_, left_, getHeight(), getWidth(), err_handler));
if (err_handler.ErrCode()) return Ref<LuminanceSource>();
return result;
}
Ref<ByteMatrix> GreyscaleLuminanceSource::getByteMatrix() const {
return Ref<ByteMatrix>(new ByteMatrix(getWidth(), getHeight(), getMatrix()));
}

View File

@@ -0,0 +1,44 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
//
// Modified from ZXing. Copyright ZXing authors.
// Licensed under the Apache License, Version 2.0 (the "License").
#ifndef __ZXING_COMMON_GREYSCALE_LUMINANCE_SOURCE_HPP__
#define __ZXING_COMMON_GREYSCALE_LUMINANCE_SOURCE_HPP__
#include "../errorhandler.hpp"
#include "../luminance_source.hpp"
#include "bytematrix.hpp"
namespace zxing {
class GreyscaleLuminanceSource : public LuminanceSource {
private:
typedef LuminanceSource Super;
ArrayRef<char> greyData_;
const int dataWidth_;
const int dataHeight_;
const int left_;
const int top_;
public:
GreyscaleLuminanceSource(ArrayRef<char> greyData, int dataWidth, int dataHeight, int left,
int top, int width, int height, ErrorHandler& err_handler);
ArrayRef<char> getRow(int y, ArrayRef<char> row, ErrorHandler& err_handler) const override;
ArrayRef<char> getMatrix() const override;
Ref<ByteMatrix> getByteMatrix() const override;
bool isRotateSupported() const override { return true; }
Ref<LuminanceSource> rotateCounterClockwise(ErrorHandler& err_handler) const override;
};
} // namespace zxing
#endif // __ZXING_COMMON_GREYSCALE_LUMINANCE_SOURCE_HPP__

View File

@@ -0,0 +1,67 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
//
// Modified from ZXing. Copyright ZXing authors.
// Licensed under the Apache License, Version 2.0 (the "License").
#include "../../precomp.hpp"
#include "greyscale_rotated_luminance_source.hpp"
#include "bytematrix.hpp"
using zxing::ArrayRef;
using zxing::ByteMatrix;
using zxing::ErrorHandler;
using zxing::GreyscaleRotatedLuminanceSource;
using zxing::Ref;
// Note that dataWidth and dataHeight are not reversed, as we need to
// be able to traverse the greyData correctly, which does not get
// rotated.
GreyscaleRotatedLuminanceSource::GreyscaleRotatedLuminanceSource(ArrayRef<char> greyData,
int dataWidth, int dataHeight,
int left, int top, int _width,
int _height,
ErrorHandler& err_handler)
: Super(_width, _height), greyData_(greyData), dataWidth_(dataWidth), left_(left), top_(top) {
// Intentionally comparing to the opposite dimension since we're rotated.
if (left + _width > dataHeight || top + _height > dataWidth) {
err_handler = IllegalArgumentErrorHandler("Crop rectangle does not fit within image data.");
}
}
// The API asks for rows, but we're rotated, so we return columns.
ArrayRef<char> GreyscaleRotatedLuminanceSource::getRow(int y, ArrayRef<char> row,
ErrorHandler& err_handler) const {
if (y < 0 || y >= getHeight()) {
err_handler = IllegalArgumentErrorHandler("Requested row is outside the image.");
return ArrayRef<char>();
}
if (!row || row->size() < getWidth()) {
row = ArrayRef<char>(getWidth());
}
int offset = (left_ * dataWidth_) + (dataWidth_ - 1 - (y + top_));
for (int x = 0; x < getWidth(); x++) {
row[x] = greyData_[offset];
offset += dataWidth_;
}
return row;
}
ArrayRef<char> GreyscaleRotatedLuminanceSource::getMatrix() const {
ArrayRef<char> result(getWidth() * getHeight());
for (int y = 0; y < getHeight(); y++) {
char* row = &result[y * getWidth()];
int offset = (left_ * dataWidth_) + (dataWidth_ - 1 - (y + top_));
for (int x = 0; x < getWidth(); x++) {
row[x] = greyData_[offset];
offset += dataWidth_;
}
}
return result;
}
Ref<ByteMatrix> GreyscaleRotatedLuminanceSource::getByteMatrix() const {
return Ref<ByteMatrix>(new ByteMatrix(getWidth(), getHeight(), getMatrix()));
}

View File

@@ -0,0 +1,39 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
//
// Modified from ZXing. Copyright ZXing authors.
// Licensed under the Apache License, Version 2.0 (the "License").
#ifndef __ZXING_COMMON_GREYSCALE_ROTATED_LUMINANCE_SOURCE_HPP__
#define __ZXING_COMMON_GREYSCALE_ROTATED_LUMINANCE_SOURCE_HPP__
#include "../errorhandler.hpp"
#include "../luminance_source.hpp"
#include "bytematrix.hpp"
namespace zxing {
class GreyscaleRotatedLuminanceSource : public LuminanceSource {
private:
typedef LuminanceSource Super;
ArrayRef<char> greyData_;
const int dataWidth_;
const int left_;
const int top_;
public:
GreyscaleRotatedLuminanceSource(ArrayRef<char> greyData, int dataWidth, int dataHeight,
int left, int top, int _width, int _height,
ErrorHandler& err_handler);
ArrayRef<char> getRow(int y, ArrayRef<char> row, ErrorHandler& err_handler) const override;
ArrayRef<char> getMatrix() const override;
Ref<ByteMatrix> getByteMatrix() const override;
};
} // namespace zxing
#endif // __ZXING_COMMON_GREYSCALE_ROTATED_LUMINANCE_SOURCE_HPP__

View File

@@ -0,0 +1,119 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
//
// Modified from ZXing. Copyright ZXing authors.
// Licensed under the Apache License, Version 2.0 (the "License").
#include "../../precomp.hpp"
#include "grid_sampler.hpp"
#include "perspective_transform.hpp"
#include <sstream>
namespace zxing {
GridSampler GridSampler::gridSampler;
GridSampler::GridSampler() {}
// Samples an image for a rectangular matrix of bits of the given dimension.
Ref<BitMatrix> GridSampler::sampleGrid(Ref<BitMatrix> image, int dimension,
Ref<PerspectiveTransform> transform,
ErrorHandler &err_handler) {
Ref<BitMatrix> bits(new BitMatrix(dimension, err_handler));
if (err_handler.ErrCode()) return Ref<BitMatrix>();
vector<float> points(dimension << 1, 0.0f);
int outlier = 0;
int maxOutlier = dimension * dimension * 3 / 10 - 1;
for (int y = 0; y < dimension; y++) {
int max = points.size();
float yValue = (float)y + 0.5f;
for (int x = 0; x < max; x += 2) {
points[x] = (float)(x >> 1) + 0.5f;
points[x + 1] = yValue;
}
transform->transformPoints(points);
// Quick check to see if points transformed to something inside the
// image; sufficient to check the endpoings
outlier += checkAndNudgePoints(image->getWidth(), image->getHeight(), points, err_handler);
if (err_handler.ErrCode()) return Ref<BitMatrix>();
if (outlier >= maxOutlier) {
ostringstream s;
s << "Over 30% points out of bounds.";
err_handler = ReaderErrorHandler(s.str().c_str());
return Ref<BitMatrix>();
}
for (int x = 0; x < max; x += 2) {
if (image->get((int)points[x], (int)points[x + 1])) {
// Black (-ish) pixel
bits->set(x >> 1, y);
}
}
}
return bits;
}
int GridSampler::checkAndNudgePoints(int width, int height, vector<float> &points,
ErrorHandler &err_handler) {
// Modified to support stlport
float *pts = NULL;
if (points.size() > 0) {
pts = &points[0];
} else {
err_handler = ReaderErrorHandler("checkAndNudgePoints:: no points!");
return -1;
}
int size = (int)points.size() / 2;
// The Java code assumes that if the start and end points are in bounds, the
// rest will also be. However, in some unusual cases points in the middle
// may also be out of bounds. Since we can't rely on an
// ArrayIndexOutOfBoundsException like Java, we check every point.
int outCount = 0;
// int maxError = (int)(size/2/3 - 1);
float maxborder = width / size * 3;
for (size_t offset = 0; offset < points.size(); offset += 2) {
int x = (int)pts[offset];
int y = (int)pts[offset + 1];
// if((int)offset==0)
// cout<<"checkAndNudgePoints "<<(int)offset<<": ("<<x<<",
//"<<y<<")"<<endl;
if (x < -1 || x > width || y < -1 || y > height) {
outCount++;
if (x > width + maxborder || y > height + maxborder || x < -maxborder ||
y < -maxborder) {
err_handler = ReaderErrorHandler("checkAndNudgePoints::Out of bounds!");
return -1;
}
}
if (x <= -1) {
points[offset] = 0.0f;
} else if (x >= width) {
points[offset] = float(width - 1);
}
if (y <= -1) {
points[offset + 1] = 0.0f;
} else if (y >= height) {
points[offset + 1] = float(height - 1);
}
}
return outCount;
}
GridSampler &GridSampler::getInstance() { return gridSampler; }
} // namespace zxing

View File

@@ -0,0 +1,34 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
//
// Modified from ZXing. Copyright ZXing authors.
// Licensed under the Apache License, Version 2.0 (the "License").
#ifndef __ZXING_COMMON_GRID_SAMPLER_HPP__
#define __ZXING_COMMON_GRID_SAMPLER_HPP__
#include "bitmatrix.hpp"
#include "bytematrix.hpp"
#include "counted.hpp"
#include "perspective_transform.hpp"
namespace zxing {
class GridSampler {
private:
static GridSampler gridSampler;
GridSampler();
public:
Ref<BitMatrix> sampleGrid(Ref<BitMatrix> image, int dimension,
Ref<PerspectiveTransform> transform, ErrorHandler &err_handler);
static int checkAndNudgePoints(int width, int height, vector<float> &points,
ErrorHandler &err_handler);
static GridSampler &getInstance();
};
} // namespace zxing
#endif // __ZXING_COMMON_GRID_SAMPLER_HPP__

View File

@@ -0,0 +1,66 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
#include "../../precomp.hpp"
#include "imagecut.hpp"
namespace zxing {
ImageCut::ImageCut() {}
ImageCut::~ImageCut() {}
int ImageCut::Cut(uint8_t* poImageData, int iWidth, int iHeight, int iTopLeftX, int iTopLeftY,
int iBottomRightX, int iBottomRightY, ImageCutResult& result) {
if (iTopLeftX < 0 || iTopLeftX > iBottomRightX || iBottomRightX >= iWidth) return -1;
if (iTopLeftY < 0 || iTopLeftY > iBottomRightY || iBottomRightY >= iHeight) return -1;
int iNewWidth = iBottomRightX - iTopLeftX + 1;
int iNewHeight = iBottomRightY - iTopLeftY + 1;
result.arrImage = new Array<uint8_t>(iNewWidth * iNewHeight);
result.iHeight = iNewHeight;
result.iWidth = iNewWidth;
int idx = 0;
for (int y = 0; y < iHeight; ++y) {
if (y < iTopLeftY || y > iBottomRightY) continue;
for (int x = 0; x < iWidth; ++x) {
if (x < iTopLeftX || x > iBottomRightX) continue;
result.arrImage[idx++] = poImageData[y * iWidth + x];
}
}
return 0;
}
int ImageCut::Cut(Ref<ByteMatrix> matrix, float fRatio, ImageCutResult& result) {
int iWidth = matrix->getWidth();
int iHeight = matrix->getHeight();
int iMinX = iWidth * (1 - fRatio) / 2;
int iMinY = iHeight * (1 - fRatio) / 2;
int iMaxX = iWidth * (1 + fRatio) / 2 - 1;
int iMaxY = iHeight * (1 + fRatio) / 2 - 1;
if (iMinY < 0 || iMinY > iMaxX || iMaxX >= iWidth) return -1;
if (iMinX < 0 || iMinX > iMaxX || iMaxX >= iWidth) return -1;
int iNewHeight = iMaxY - iMinY + 1;
int iNewWidth = iMaxX - iMinX + 1;
result.arrImage = new Array<uint8_t>(iNewWidth * iNewHeight);
result.iWidth = iNewWidth;
result.iHeight = iNewHeight;
int idx = 0;
for (int y = 0; y < iNewHeight; ++y) {
for (int x = 0; x < iNewWidth; ++x) {
result.arrImage[idx++] = matrix->get(x + iMinX, y + iMinY);
}
}
return 0;
}
} // namespace zxing

View File

@@ -0,0 +1,32 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
#ifndef __ZXING_COMMON_IMAGECUT_HPP__
#define __ZXING_COMMON_IMAGECUT_HPP__
#include "bytematrix.hpp"
#include "counted.hpp"
namespace zxing {
typedef struct _ImageCutResult {
ArrayRef<uint8_t> arrImage;
int iWidth;
int iHeight;
} ImageCutResult;
class ImageCut {
public:
ImageCut();
~ImageCut();
static int Cut(uint8_t* poImageData, int iWidth, int iHeight, int iTopLeftX, int iTopLeftY,
int iBottomRightX, int iBottomRightY, ImageCutResult& result);
static int Cut(Ref<ByteMatrix> matrix, float fRatio, ImageCutResult& result);
};
} // namespace zxing
#endif // __ZXING_COMMON_IMAGECUT_HPP__

View File

@@ -0,0 +1,89 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
#include "../../precomp.hpp"
#include "kmeans.hpp"
typedef unsigned int uint;
namespace zxing {
double cal_distance(vector<double> a, vector<double> b) {
const float KMEANS_COUNT_FACTOR = 0;
const float KMEANS_MS_FACTOR = 1;
uint da = a.size();
double val = 0.0;
for (uint i = 0; i < da; i++) {
if (i == 1)
val += KMEANS_MS_FACTOR * pow((a[i] - b[i]), 2);
else if (i == 0)
val += KMEANS_COUNT_FACTOR * pow((a[i] - b[i]), 2);
else
val += pow((a[i] - b[i]), 2);
}
return pow(val, 0.5);
}
/*
* maxepoches max iteration epochs
* minchanged min central change times
*/
vector<Cluster> k_means(vector<vector<double> > trainX, uint k, uint maxepoches, uint minchanged) {
const uint row_num = trainX.size();
const uint col_num = trainX[0].size();
// initialize the cluster central
vector<Cluster> clusters(k);
int step = trainX.size() / k;
for (uint i = 0; i < k; i++) {
clusters[i].centroid = trainX[i * step];
}
// try max epochs times iteration untill convergence
for (uint it = 0; it < maxepoches; it++) {
for (uint i = 0; i < k; i++) {
clusters[i].samples.clear();
}
for (uint j = 0; j < row_num; j++) {
uint c = 0;
double min_distance = cal_distance(trainX[j], clusters[c].centroid);
for (uint i = 1; i < k; i++) {
double distance = cal_distance(trainX[j], clusters[i].centroid);
if (distance < min_distance) {
min_distance = distance;
c = i;
}
}
clusters[c].samples.push_back(j);
}
uint changed = 0;
// update cluster central
for (uint i = 0; i < k; i++) {
vector<double> val(col_num, 0.0);
for (uint j = 0; j < clusters[i].samples.size(); j++) {
uint sample = clusters[i].samples[j];
for (uint d = 0; d < col_num; d++) {
val[d] += trainX[sample][d];
if (j == clusters[i].samples.size() - 1) {
double value = val[d] / clusters[i].samples.size();
if (clusters[i].centroid[d] != value) {
clusters[i].centroid[d] = value;
changed++;
}
}
}
}
}
if (changed <= minchanged) return clusters;
}
return clusters;
}
} // namespace zxing

View File

@@ -0,0 +1,26 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
#ifndef __ZXING_COMMON_KMEANS_HPP__
#define __ZXING_COMMON_KMEANS_HPP__
#include <vector>
namespace zxing {
using namespace std;
typedef unsigned int uint;
struct Cluster {
vector<double> centroid;
vector<uint> samples;
};
double cal_distance(vector<double> a, vector<double> b);
vector<Cluster> k_means(vector<vector<double> > trainX, uint k, uint maxepoches, uint minchanged);
} // namespace zxing
#endif // __ZXING_COMMON_KMEANS_HPP__

View File

@@ -0,0 +1,95 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
//
// Modified from ZXing. Copyright ZXing authors.
// Licensed under the Apache License, Version 2.0 (the "License").
#ifndef __ZXING_COMMON_MATHUTILS_HPP__
#define __ZXING_COMMON_MATHUTILS_HPP__
#include <cmath>
#if (defined __GNUC__ && defined __x86_64__ && defined __SSE2__ && !defined __APPLE__ && \
!defined __GXX_WEAK__)
#include <ammintrin.h>
#elif defined _MSC_VER && (defined _M_X64 || defined _M_IX86)
#include <emmintrin.h>
#endif
#include <algorithm>
#include <numeric>
#include <vector>
namespace zxing {
namespace common {
class MathUtils {
private:
MathUtils();
~MathUtils();
public:
static inline float distance(float aX, float aY, float bX, float bY) {
float xDiff = aX - bX;
float yDiff = aY - bY;
return sqrt(float(xDiff * xDiff + yDiff * yDiff));
}
static inline float distance_4_int(int aX, int aY, int bX, int bY) {
return sqrt(float((aX - bX) * (aX - bX) + (aY - bY) * (aY - bY)));
}
static inline void getRangeValues(int& minValue, int& maxValue, int min, int max) {
int finalMinValue, finalMaxValue;
if (minValue < maxValue) {
finalMinValue = minValue;
finalMaxValue = maxValue;
} else {
finalMinValue = maxValue;
finalMaxValue = minValue;
}
finalMinValue = finalMinValue > min ? finalMinValue : min;
finalMaxValue = finalMaxValue < max ? finalMaxValue : max;
minValue = finalMinValue;
maxValue = finalMaxValue;
}
static inline bool isInRange(float x, float y, float width, float height) {
if ((x >= 0.0 && x <= (width - 1.0)) && (y >= 0.0 && y <= (height - 1.0))) {
return true;
} else {
return false;
}
}
static inline float distance(int aX, int aY, int bX, int bY) {
int xDiff = aX - bX;
int yDiff = aY - bY;
return sqrt(float(xDiff * xDiff + yDiff * yDiff));
}
static inline float VecCross(float* v1, float* v2) { return v1[0] * v2[1] - v1[1] * v2[0]; }
static inline void Stddev(std::vector<float>& resultSet, float& avg, float& stddev) {
double sum = std::accumulate(resultSet.begin(), resultSet.end(), 0.0);
avg = sum / resultSet.size();
double accum = 0.0;
for (std::size_t i = 0; i < resultSet.size(); i++) {
accum += (resultSet[i] - avg) * (resultSet[i] - avg);
}
stddev = sqrt(accum / (resultSet.size()));
}
};
} // namespace common
} // namespace zxing
#endif // __ZXING_COMMON_MATHUTILS_HPP__

View File

@@ -0,0 +1,120 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
//
// Modified from ZXing. Copyright ZXing authors.
// Licensed under the Apache License, Version 2.0 (the "License").
#include "../../precomp.hpp"
#include "perspective_transform.hpp"
namespace zxing {
// This class implements a perspective transform in two dimensions. Given four
// source and four destination points, it will compute the transformation
// implied between them. The code is based directly upon section 3.4.2 of George
// Wolberg's "Digital Image Warping"; see pages 54-56
PerspectiveTransform::PerspectiveTransform(float inA11, float inA21, float inA31, float inA12,
float inA22, float inA32, float inA13, float inA23,
float inA33)
: a11(inA11),
a12(inA12),
a13(inA13),
a21(inA21),
a22(inA22),
a23(inA23),
a31(inA31),
a32(inA32),
a33(inA33) {}
Ref<PerspectiveTransform> PerspectiveTransform::quadrilateralToQuadrilateral(
float x0, float y0, float x1, float y1, float x2, float y2, float x3, float y3, float x0p,
float y0p, float x1p, float y1p, float x2p, float y2p, float x3p, float y3p) {
Ref<PerspectiveTransform> qToS =
PerspectiveTransform::quadrilateralToSquare(x0, y0, x1, y1, x2, y2, x3, y3);
Ref<PerspectiveTransform> sToQ =
PerspectiveTransform::squareToQuadrilateral(x0p, y0p, x1p, y1p, x2p, y2p, x3p, y3p);
return sToQ->times(qToS);
}
Ref<PerspectiveTransform> PerspectiveTransform::squareToQuadrilateral(float x0, float y0, float x1,
float y1, float x2, float y2,
float x3, float y3) {
float dx3 = x0 - x1 + x2 - x3;
float dy3 = y0 - y1 + y2 - y3;
if (fabs(dx3) <= 1e-6 && fabs(dy3) <= 1e-6) {
Ref<PerspectiveTransform> result(
new PerspectiveTransform(x1 - x0, x2 - x1, x0, y1 - y0, y2 - y1, y0, 0.0f, 0.0f, 1.0f));
return result;
} else {
float dx1 = x1 - x2;
float dx2 = x3 - x2;
float dy1 = y1 - y2;
float dy2 = y3 - y2;
float denominator = dx1 * dy2 - dx2 * dy1;
float a13 = (dx3 * dy2 - dx2 * dy3) / denominator;
float a23 = (dx1 * dy3 - dx3 * dy1) / denominator;
Ref<PerspectiveTransform> result(
new PerspectiveTransform(x1 - x0 + a13 * x1, x3 - x0 + a23 * x3, x0, y1 - y0 + a13 * y1,
y3 - y0 + a23 * y3, y0, a13, a23, 1.0f));
return result;
}
}
Ref<PerspectiveTransform> PerspectiveTransform::quadrilateralToSquare(float x0, float y0, float x1,
float y1, float x2, float y2,
float x3, float y3) {
// Here, the adjoint serves as the inverse:
return squareToQuadrilateral(x0, y0, x1, y1, x2, y2, x3, y3)->buildAdjoint();
}
Ref<PerspectiveTransform> PerspectiveTransform::buildAdjoint() {
// Adjoint is the transpose of the cofactor matrix:
Ref<PerspectiveTransform> result(new PerspectiveTransform(
a22 * a33 - a23 * a32, a23 * a31 - a21 * a33, a21 * a32 - a22 * a31, a13 * a32 - a12 * a33,
a11 * a33 - a13 * a31, a12 * a31 - a11 * a32, a12 * a23 - a13 * a22, a13 * a21 - a11 * a23,
a11 * a22 - a12 * a21));
return result;
}
Ref<PerspectiveTransform> PerspectiveTransform::times(Ref<PerspectiveTransform> other) {
Ref<PerspectiveTransform> result(
new PerspectiveTransform(a11 * other->a11 + a21 * other->a12 + a31 * other->a13,
a11 * other->a21 + a21 * other->a22 + a31 * other->a23,
a11 * other->a31 + a21 * other->a32 + a31 * other->a33,
a12 * other->a11 + a22 * other->a12 + a32 * other->a13,
a12 * other->a21 + a22 * other->a22 + a32 * other->a23,
a12 * other->a31 + a22 * other->a32 + a32 * other->a33,
a13 * other->a11 + a23 * other->a12 + a33 * other->a13,
a13 * other->a21 + a23 * other->a22 + a33 * other->a23,
a13 * other->a31 + a23 * other->a32 + a33 * other->a33));
return result;
}
void PerspectiveTransform::transformPoints(vector<float>& points) {
int max = points.size();
// Modified to support stlport
float* pts = NULL;
if (points.size() > 0) {
pts = &points[0];
}
for (int i = 0; i < max; i += 2) {
float x = pts[i];
float y = pts[i + 1];
float denominator = a13 * x + a23 * y + a33;
float w = 1.0f / denominator;
pts[i] = (a11 * x + a21 * y + a31) * w;
pts[i + 1] = (a12 * x + a22 * y + a32) * w;
}
}
} // namespace zxing

View File

@@ -0,0 +1,39 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
//
// Modified from ZXing. Copyright ZXing authors.
// Licensed under the Apache License, Version 2.0 (the "License").
#ifndef __ZXING_COMMON_PERSPECTIVETRANSFORM_HPP__
#define __ZXING_COMMON_PERSPECTIVETRANSFORM_HPP__
#include "counted.hpp"
#include <vector>
namespace zxing {
class PerspectiveTransform : public Counted {
private:
float a11, a12, a13, a21, a22, a23, a31, a32, a33;
PerspectiveTransform(float a11, float a21, float a31, float a12, float a22, float a32,
float a13, float a23, float a33);
public:
static Ref<PerspectiveTransform> quadrilateralToQuadrilateral(
float x0, float y0, float x1, float y1, float x2, float y2, float x3, float y3, float x0p,
float y0p, float x1p, float y1p, float x2p, float y2p, float x3p, float y3p);
static Ref<PerspectiveTransform> squareToQuadrilateral(float x0, float y0, float x1, float y1,
float x2, float y2, float x3, float y3);
static Ref<PerspectiveTransform> quadrilateralToSquare(float x0, float y0, float x1, float y1,
float x2, float y2, float x3, float y3);
Ref<PerspectiveTransform> buildAdjoint();
Ref<PerspectiveTransform> times(Ref<PerspectiveTransform> other);
void transformPoints(std::vector<float>& points);
};
} // namespace zxing
#endif // __ZXING_COMMON_PERSPECTIVETRANSFORM_HPP__

View File

@@ -0,0 +1,99 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
//
// Modified from ZXing. Copyright ZXing authors.
// Licensed under the Apache License, Version 2.0 (the "License").
#include "../../../precomp.hpp"
#include "genericgf.hpp"
#include "genericgfpoly.hpp"
using zxing::ErrorHandler;
using zxing::GenericGF;
using zxing::GenericGFPoly;
using zxing::Ref;
GenericGF::GenericGF(int primitive_, int size_, int b, ErrorHandler &err_handler)
: size(size_), primitive(primitive_), generatorBase(b) {
expTable.resize(size);
logTable.resize(size);
int x = 1;
for (int i = 0; i < size; i++) {
expTable[i] = x;
x <<= 1; // x = x * 2; we're assuming the generator alpha is 2
if (x >= size) {
x ^= primitive;
x &= size - 1;
}
}
for (int i = 0; i < size - 1; i++) {
logTable[expTable[i]] = i;
}
// logTable[0] == 0 but this should never be used
zero =
Ref<GenericGFPoly>(new GenericGFPoly(*this, ArrayRef<int>(new Array<int>(1)), err_handler));
zero->getCoefficients()[0] = 0;
one =
Ref<GenericGFPoly>(new GenericGFPoly(*this, ArrayRef<int>(new Array<int>(1)), err_handler));
one->getCoefficients()[0] = 1;
if (err_handler.ErrCode()) return;
// initialized = true;
}
Ref<GenericGFPoly> GenericGF::getZero() { return zero; }
Ref<GenericGFPoly> GenericGF::getOne() { return one; }
Ref<GenericGFPoly> GenericGF::buildMonomial(int degree, int coefficient,
ErrorHandler &err_handler) {
if (degree < 0) {
err_handler = IllegalArgumentErrorHandler("Degree must be non-negative");
return Ref<GenericGFPoly>();
}
if (coefficient == 0) {
return zero;
}
ArrayRef<int> coefficients(new Array<int>(degree + 1));
coefficients[0] = coefficient;
Ref<GenericGFPoly> gfpoly(new GenericGFPoly(*this, coefficients, err_handler));
if (err_handler.ErrCode()) return Ref<GenericGFPoly>();
return gfpoly;
}
int GenericGF::addOrSubtract(int a, int b) { return a ^ b; }
int GenericGF::exp(int a) { return expTable[a]; }
int GenericGF::log(int a, ErrorHandler &err_handler) {
if (a == 0) {
err_handler = IllegalArgumentErrorHandler("cannot give log(0)");
return -1;
}
return logTable[a];
}
int GenericGF::inverse(int a, ErrorHandler &err_handler) {
if (a == 0) {
err_handler = IllegalArgumentErrorHandler("Cannot calculate the inverse of 0");
return -1;
}
return expTable[size - logTable[a] - 1];
}
int GenericGF::multiply(int a, int b) {
if (a == 0 || b == 0) {
return 0;
}
return expTable[(logTable[a] + logTable[b]) % (size - 1)];
}
int GenericGF::getSize() { return size; }
int GenericGF::getGeneratorBase() { return generatorBase; }

View File

@@ -0,0 +1,58 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
//
// Modified from ZXing. Copyright ZXing authors.
// Licensed under the Apache License, Version 2.0 (the "License").
#ifndef __ZXING_COMMON_REEDSOLOMON_GENERICGF_HPP__
#define __ZXING_COMMON_REEDSOLOMON_GENERICGF_HPP__
#include "../../errorhandler.hpp"
#include "../counted.hpp"
namespace zxing {
class GenericGFPoly;
static zxing::ErrorHandler gf_err_handler_;
#define GF_AZTEC_DATA_12 (new GenericGF(0x1069, 4096, 1, gf_err_handler_))
#define GF_AZTEC_DATA_10 (new GenericGF(0x409, 1024, 1, gf_err_handler_))
#define GF_AZTEC_DATA_6 (new GenericGF(0x43, 64, 1, gf_err_handler_))
#define GF_AZTEC_PARAM (new GenericGF(0x13, 16, 1, gf_err_handler_))
#define GF_QR_CODE_FIELD_256 (new GenericGF(0x011D, 256, 0, gf_err_handler_))
#define GF_DATA_MATRIX_FIELD_256 (new GenericGF(0x012D, 256, 1, gf_err_handler_))
#define GF_AZTEC_DATA_8 (new GenericGF(0x012D, 256, 1, gf_err_handler_))
#define GF_MAXICODE_FIELD_64 (new GenericGF(0x43, 64, 1, gf_err_handler_))
// #define GF_WXCODE (new GenericGF(0x011D, 256, 0, gf_err_handler_))
class GenericGF : public Counted {
private:
std::vector<int> expTable;
std::vector<int> logTable;
Ref<GenericGFPoly> zero;
Ref<GenericGFPoly> one;
int size;
int primitive;
int generatorBase;
public:
GenericGF(int primitive, int size, int b, ErrorHandler &err_handler);
Ref<GenericGFPoly> getZero();
Ref<GenericGFPoly> getOne();
int getSize();
int getGeneratorBase();
Ref<GenericGFPoly> buildMonomial(int degree, int coefficient, ErrorHandler &err_handler);
static int addOrSubtract(int a, int b);
int exp(int a);
int log(int a, ErrorHandler &err_handler);
int inverse(int a, ErrorHandler &err_handler);
int multiply(int a, int b);
};
} // namespace zxing
#endif // __ZXING_COMMON_REEDSOLOMON_GENERICGF_HPP__

View File

@@ -0,0 +1,231 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
//
// Modified from ZXing. Copyright ZXing authors.
// Licensed under the Apache License, Version 2.0 (the "License").
#include "../../../precomp.hpp"
#include "genericgfpoly.hpp"
#include "genericgf.hpp"
using zxing::ArrayRef;
using zxing::ErrorHandler;
using zxing::GenericGFPoly;
using zxing::Ref;
// VC++
using zxing::GenericGF;
GenericGFPoly::GenericGFPoly(GenericGF &field, ArrayRef<int> coefficients,
ErrorHandler &err_handler)
: field_(field) {
if (coefficients->size() == 0) {
err_handler = IllegalArgumentErrorHandler("need coefficients");
return;
}
int coefficientsLength = coefficients->size();
if (coefficientsLength > 1 && coefficients[0] == 0) {
// Leading term must be non-zero for anything except the constant
// polynomial "0"
int firstNonZero = 1;
while (firstNonZero < coefficientsLength && coefficients[firstNonZero] == 0) {
firstNonZero++;
}
if (firstNonZero == coefficientsLength) {
coefficients_ = field.getZero()->getCoefficients();
} else {
coefficients_ = ArrayRef<int>(new Array<int>(coefficientsLength - firstNonZero));
for (int i = 0; i < (int)coefficients_->size(); i++) {
coefficients_[i] = coefficients[i + firstNonZero];
}
}
} else {
coefficients_ = coefficients;
}
}
ArrayRef<int> GenericGFPoly::getCoefficients() { return coefficients_; }
int GenericGFPoly::getDegree() { return coefficients_->size() - 1; }
bool GenericGFPoly::isZero() { return coefficients_[0] == 0; }
int GenericGFPoly::getCoefficient(int degree) {
return coefficients_[coefficients_->size() - 1 - degree];
}
int GenericGFPoly::evaluateAt(int a) {
if (a == 0) {
// Just return the x^0 coefficient
return getCoefficient(0);
}
int size = coefficients_->size();
if (a == 1) {
// Just the sum of the coefficients
int result = 0;
for (int i = 0; i < size; i++) {
result = GenericGF::addOrSubtract(result, coefficients_[i]);
}
return result;
}
int result = coefficients_[0];
for (int i = 1; i < size; i++) {
result = GenericGF::addOrSubtract(field_.multiply(a, result), coefficients_[i]);
}
return result;
}
Ref<GenericGFPoly> GenericGFPoly::addOrSubtract(Ref<zxing::GenericGFPoly> other,
ErrorHandler &err_handler) {
if (!(&field_ == &other->field_)) {
err_handler =
IllegalArgumentErrorHandler("GenericGFPolys do not have same GenericGF field");
return Ref<GenericGFPoly>();
}
if (isZero()) {
return other;
}
if (other->isZero()) {
return Ref<GenericGFPoly>(this);
}
ArrayRef<int> smallerCoefficients = coefficients_;
ArrayRef<int> largerCoefficients = other->getCoefficients();
if (smallerCoefficients->size() > largerCoefficients->size()) {
ArrayRef<int> temp = smallerCoefficients;
smallerCoefficients = largerCoefficients;
largerCoefficients = temp;
}
ArrayRef<int> sumDiff(new Array<int>(largerCoefficients->size()));
int lengthDiff = largerCoefficients->size() - smallerCoefficients->size();
// Copy high-order terms only found in higher-degree polynomial's
// coefficients
for (int i = 0; i < lengthDiff; i++) {
sumDiff[i] = largerCoefficients[i];
}
for (int i = lengthDiff; i < (int)largerCoefficients->size(); i++) {
sumDiff[i] =
GenericGF::addOrSubtract(smallerCoefficients[i - lengthDiff], largerCoefficients[i]);
}
// return Ref<GenericGFPoly>(new GenericGFPoly(field_, sumDiff));
Ref<GenericGFPoly> gfpoly(new GenericGFPoly(field_, sumDiff, err_handler));
if (err_handler.ErrCode()) return Ref<GenericGFPoly>();
return gfpoly;
}
Ref<GenericGFPoly> GenericGFPoly::multiply(Ref<zxing::GenericGFPoly> other,
ErrorHandler &err_handler) {
if (!(&field_ == &other->field_)) {
err_handler =
IllegalArgumentErrorHandler("GenericGFPolys do not have same GenericGF field");
return Ref<GenericGFPoly>();
}
if (isZero() || other->isZero()) {
return field_.getZero();
}
ArrayRef<int> aCoefficients = coefficients_;
int aLength = aCoefficients->size();
ArrayRef<int> bCoefficients = other->getCoefficients();
int bLength = bCoefficients->size();
ArrayRef<int> product(new Array<int>(aLength + bLength - 1));
for (int i = 0; i < aLength; i++) {
int aCoeff = aCoefficients[i];
for (int j = 0; j < bLength; j++) {
product[i + j] =
GenericGF::addOrSubtract(product[i + j], field_.multiply(aCoeff, bCoefficients[j]));
}
}
// return Ref<GenericGFPoly>(new GenericGFPoly(field_, product));
Ref<GenericGFPoly> gfpoly(new GenericGFPoly(field_, product, err_handler));
if (err_handler.ErrCode()) return Ref<GenericGFPoly>();
return gfpoly;
}
Ref<GenericGFPoly> GenericGFPoly::multiply(int scalar, ErrorHandler &err_handler) {
if (scalar == 0) {
return field_.getZero();
}
if (scalar == 1) {
return Ref<GenericGFPoly>(this);
}
int size = coefficients_->size();
ArrayRef<int> product(new Array<int>(size));
for (int i = 0; i < size; i++) {
product[i] = field_.multiply(coefficients_[i], scalar);
}
// return Ref<GenericGFPoly>(new GenericGFPoly(field_, product));
Ref<GenericGFPoly> gfpoly(new GenericGFPoly(field_, product, err_handler));
if (err_handler.ErrCode()) return Ref<GenericGFPoly>();
return gfpoly;
}
Ref<GenericGFPoly> GenericGFPoly::multiplyByMonomial(int degree, int coefficient,
ErrorHandler &err_handler) {
if (degree < 0) {
err_handler = IllegalArgumentErrorHandler("degree must not be less then 0");
return Ref<GenericGFPoly>();
}
if (coefficient == 0) {
return field_.getZero();
}
int size = coefficients_->size();
ArrayRef<int> product(new Array<int>(size + degree));
for (int i = 0; i < size; i++) {
product[i] = field_.multiply(coefficients_[i], coefficient);
}
// return Ref<GenericGFPoly>(new GenericGFPoly(field_, product));
Ref<GenericGFPoly> gfpoly(new GenericGFPoly(field_, product, err_handler));
if (err_handler.ErrCode()) return Ref<GenericGFPoly>();
return gfpoly;
}
std::vector<Ref<GenericGFPoly>> GenericGFPoly::divide(Ref<GenericGFPoly> other,
ErrorHandler &err_handler) {
if (!(&field_ == &other->field_)) {
err_handler =
IllegalArgumentErrorHandler("GenericGFPolys do not have same GenericGF field");
return std::vector<Ref<GenericGFPoly>>();
}
if (other->isZero()) {
err_handler = IllegalArgumentErrorHandler("divide by 0");
return std::vector<Ref<GenericGFPoly>>();
}
Ref<GenericGFPoly> quotient = field_.getZero();
Ref<GenericGFPoly> remainder = Ref<GenericGFPoly>(this);
int denominatorLeadingTerm = other->getCoefficient(other->getDegree());
int inverseDenominatorLeadingTerm = field_.inverse(denominatorLeadingTerm, err_handler);
if (err_handler.ErrCode()) return std::vector<Ref<GenericGFPoly>>();
while (remainder->getDegree() >= other->getDegree() && !remainder->isZero()) {
int degreeDifference = remainder->getDegree() - other->getDegree();
int scale = field_.multiply(remainder->getCoefficient(remainder->getDegree()),
inverseDenominatorLeadingTerm);
Ref<GenericGFPoly> term = other->multiplyByMonomial(degreeDifference, scale, err_handler);
if (err_handler.ErrCode()) return std::vector<Ref<GenericGFPoly>>();
Ref<GenericGFPoly> iterationQuotiont =
field_.buildMonomial(degreeDifference, scale, err_handler);
if (err_handler.ErrCode()) return std::vector<Ref<GenericGFPoly>>();
quotient = quotient->addOrSubtract(iterationQuotiont, err_handler);
remainder = remainder->addOrSubtract(term, err_handler);
if (err_handler.ErrCode()) return std::vector<Ref<GenericGFPoly>>();
}
std::vector<Ref<GenericGFPoly>> returnValue(2);
returnValue[0] = quotient;
returnValue[1] = remainder;
return returnValue;
}

View File

@@ -0,0 +1,43 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
//
// Modified from ZXing. Copyright ZXing authors.
// Licensed under the Apache License, Version 2.0 (the "License").
#ifndef __ZXING_COMMON_REEDSOLOMON_GENERICGFPOLY_HPP__
#define __ZXING_COMMON_REEDSOLOMON_GENERICGFPOLY_HPP__
#include "../../errorhandler.hpp"
#include "../array.hpp"
#include "../counted.hpp"
namespace zxing {
class GenericGF;
class GenericGFPoly : public Counted {
private:
GenericGF &field_;
ArrayRef<int> coefficients_;
public:
GenericGFPoly(GenericGF &field, ArrayRef<int> coefficients, ErrorHandler &err_handler);
ArrayRef<int> getCoefficients();
int getDegree();
bool isZero();
int getCoefficient(int degree);
int evaluateAt(int a);
Ref<GenericGFPoly> addOrSubtract(Ref<GenericGFPoly> other, ErrorHandler &err_handler);
Ref<GenericGFPoly> multiply(Ref<GenericGFPoly> other, ErrorHandler &err_handler);
Ref<GenericGFPoly> multiply(int scalar, ErrorHandler &err_handler);
Ref<GenericGFPoly> multiplyByMonomial(int degree, int coefficient, ErrorHandler &err_handler);
std::vector<Ref<GenericGFPoly>> divide(Ref<GenericGFPoly> other, ErrorHandler &err_handler);
};
} // namespace zxing
#endif // __ZXING_COMMON_REEDSOLOMON_GENERICGFPOLY_HPP__

View File

@@ -0,0 +1,185 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
//
// Modified from ZXing. Copyright ZXing authors.
// Licensed under the Apache License, Version 2.0 (the "License").
#include "../../../precomp.hpp"
#include "reed_solomon_decoder.hpp"
using zxing::ArrayRef;
using zxing::ErrorHandler;
using zxing::GenericGFPoly;
using zxing::ReedSolomonDecoder;
using zxing::Ref;
using zxing::GenericGF;
ReedSolomonDecoder::ReedSolomonDecoder(Ref<GenericGF> field_) : field(field_) {}
ReedSolomonDecoder::~ReedSolomonDecoder() {}
void ReedSolomonDecoder::decode(ArrayRef<int> received, int twoS, ErrorHandler &err_handler) {
Ref<GenericGFPoly> poly(new GenericGFPoly(*field, received, err_handler));
if (err_handler.ErrCode()) return;
ArrayRef<int> syndromeCoefficients(twoS);
bool noError = true;
for (int i = 0; i < twoS; i++) {
int eval = poly->evaluateAt(field->exp(i + field->getGeneratorBase()));
syndromeCoefficients[syndromeCoefficients->size() - 1 - i] = eval;
if (eval != 0) {
noError = false;
}
}
if (noError) {
return;
}
Ref<GenericGFPoly> syndrome(new GenericGFPoly(*field, syndromeCoefficients, err_handler));
Ref<GenericGFPoly> monomial = field->buildMonomial(twoS, 1, err_handler);
if (!monomial || err_handler.ErrCode()) {
err_handler = ErrorHandler("buildMonomial was zero");
return;
}
vector<Ref<GenericGFPoly>> sigmaOmega =
runEuclideanAlgorithm(monomial, syndrome, twoS, err_handler);
if (err_handler.ErrCode()) return;
Ref<GenericGFPoly> sigma = sigmaOmega[0];
Ref<GenericGFPoly> omega = sigmaOmega[1];
ArrayRef<int> errorLocations = findErrorLocations(sigma, err_handler);
if (err_handler.ErrCode()) return;
ArrayRef<int> errorMagitudes = findErrorMagnitudes(omega, errorLocations, err_handler);
if (err_handler.ErrCode()) return;
for (int i = 0; i < errorLocations->size(); i++) {
int position = received->size() - 1 - field->log(errorLocations[i], err_handler);
if (position < 0 || err_handler.ErrCode()) {
err_handler = ErrorHandler("Bad error location");
return;
}
received[position] = GenericGF::addOrSubtract(received[position], errorMagitudes[i]);
}
}
vector<Ref<GenericGFPoly>> ReedSolomonDecoder::runEuclideanAlgorithm(Ref<GenericGFPoly> a,
Ref<GenericGFPoly> b, int R,
ErrorHandler &err_handler) {
vector<Ref<GenericGFPoly>> result(2);
// Assume a's degree is >= b's
if (a->getDegree() < b->getDegree()) {
Ref<GenericGFPoly> tmp = a;
a = b;
b = tmp;
}
Ref<GenericGFPoly> rLast(a);
Ref<GenericGFPoly> r(b);
Ref<GenericGFPoly> tLast(field->getZero());
Ref<GenericGFPoly> t(field->getOne());
// Run Euclidean algorithm until r's degree is less than R/2
while (r->getDegree() >= R / 2) {
Ref<GenericGFPoly> rLastLast(rLast);
Ref<GenericGFPoly> tLastLast(tLast);
rLast = r;
tLast = t;
// Divide rLastLast by rLast, with quotient q and remainder r
if (rLast->isZero()) {
// Oops, Euclidean algorithm already terminated?
err_handler = ErrorHandler("r_{i-1} was zero");
return vector<Ref<GenericGFPoly>>();
}
r = rLastLast;
Ref<GenericGFPoly> q = field->getZero();
int denominatorLeadingTerm = rLast->getCoefficient(rLast->getDegree());
int dltInverse = field->inverse(denominatorLeadingTerm, err_handler);
if (err_handler.ErrCode()) return vector<Ref<GenericGFPoly>>();
while (r->getDegree() >= rLast->getDegree() && !r->isZero()) {
int degreeDiff = r->getDegree() - rLast->getDegree();
int scale = field->multiply(r->getCoefficient(r->getDegree()), dltInverse);
q = q->addOrSubtract(field->buildMonomial(degreeDiff, scale, err_handler), err_handler);
r = r->addOrSubtract(rLast->multiplyByMonomial(degreeDiff, scale, err_handler),
err_handler);
if (err_handler.ErrCode()) return vector<Ref<GenericGFPoly>>();
}
Ref<GenericGFPoly> tmp = q->multiply(tLast, err_handler);
if (err_handler.ErrCode()) return vector<Ref<GenericGFPoly>>();
t = tmp->addOrSubtract(tLastLast, err_handler);
if (err_handler.ErrCode()) return vector<Ref<GenericGFPoly>>();
if (r->getDegree() >= rLast->getDegree()) {
err_handler = ErrorHandler("Division algorithm failed to reduce polynomial?");
return vector<Ref<GenericGFPoly>>();
}
}
int sigmaTildeAtZero = t->getCoefficient(0);
if (sigmaTildeAtZero == 0) {
err_handler = ErrorHandler("sigmaTilde(0) was zero");
return vector<Ref<GenericGFPoly>>();
}
int inverse = field->inverse(sigmaTildeAtZero, err_handler);
Ref<GenericGFPoly> sigma(t->multiply(inverse, err_handler));
Ref<GenericGFPoly> omega(r->multiply(inverse, err_handler));
if (err_handler.ErrCode()) return vector<Ref<GenericGFPoly>>();
result[0] = sigma;
result[1] = omega;
return result;
}
ArrayRef<int> ReedSolomonDecoder::findErrorLocations(Ref<GenericGFPoly> errorLocator,
ErrorHandler &err_handler) {
// This is a direct application of Chien's search
int numErrors = errorLocator->getDegree();
if (numErrors == 1) { // shortcut
ArrayRef<int> result(new Array<int>(1));
result[0] = errorLocator->getCoefficient(1);
return result;
}
ArrayRef<int> result(new Array<int>(numErrors));
int e = 0;
for (int i = 1; i < field->getSize() && e < numErrors; i++) {
if (errorLocator->evaluateAt(i) == 0) {
result[e] = field->inverse(i, err_handler);
e++;
}
}
if (e != numErrors || err_handler.ErrCode()) {
err_handler = ErrorHandler("Error locator degree does not match number of root");
return ArrayRef<int>();
}
return result;
}
ArrayRef<int> ReedSolomonDecoder::findErrorMagnitudes(Ref<GenericGFPoly> errorEvaluator,
ArrayRef<int> errorLocations,
ErrorHandler &err_handler) {
// This is directly applying Forney's Formula
int s = errorLocations->size();
ArrayRef<int> result(new Array<int>(s));
for (int i = 0; i < s; i++) {
int xiInverse = field->inverse(errorLocations[i], err_handler);
int denominator = 1;
for (int j = 0; j < s; j++) {
if (i != j) {
int term = field->multiply(errorLocations[j], xiInverse);
int termPlus1 = (term & 0x1) == 0 ? term | 1 : term & ~1;
denominator = field->multiply(denominator, termPlus1);
}
}
result[i] = field->multiply(errorEvaluator->evaluateAt(xiInverse),
field->inverse(denominator, err_handler));
if (field->getGeneratorBase() != 0) {
result[i] = field->multiply(result[i], xiInverse);
}
}
if (err_handler.ErrCode()) return ArrayRef<int>();
return result;
}

View File

@@ -0,0 +1,43 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
//
// Modified from ZXing. Copyright ZXing authors.
// Licensed under the Apache License, Version 2.0 (the "License").
#ifndef __ZXING_COMMON_REEDSOLOMON_REEDSOLOMONDECODER_HPP__
#define __ZXING_COMMON_REEDSOLOMON_REEDSOLOMONDECODER_HPP__
#include "../../errorhandler.hpp"
#include "../array.hpp"
#include "../counted.hpp"
#include "genericgf.hpp"
#include "genericgfpoly.hpp"
namespace zxing {
class GenericGFPoly;
class GenericGF;
class ReedSolomonDecoder {
private:
Ref<GenericGF> field;
public:
explicit ReedSolomonDecoder(Ref<GenericGF> fld);
~ReedSolomonDecoder();
void decode(ArrayRef<int> received, int twoS, ErrorHandler &err_handler);
std::vector<Ref<GenericGFPoly>> runEuclideanAlgorithm(Ref<GenericGFPoly> a,
Ref<GenericGFPoly> b, int R,
ErrorHandler &err_handler);
private:
ArrayRef<int> findErrorLocations(Ref<GenericGFPoly> errorLocator, ErrorHandler &err_handler);
ArrayRef<int> findErrorMagnitudes(Ref<GenericGFPoly> errorEvaluator,
ArrayRef<int> errorLocations, ErrorHandler &err_handler);
};
} // namespace zxing
#endif // __ZXING_COMMON_REEDSOLOMON_REEDSOLOMONDECODER_HPP__

View File

@@ -0,0 +1,96 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
//
// Modified from ZXing. Copyright ZXing authors.
// Licensed under the Apache License, Version 2.0 (the "License").
#include "../../precomp.hpp"
#include "str.hpp"
using zxing::Ref;
using zxing::String;
using zxing::StrUtil;
String::String(const std::string& text) : text_(text) {}
String::String(int capacity) { text_.reserve(capacity); }
const std::string& String::getText() const { return text_; }
char String::charAt(int i) const { return text_[i]; }
int String::size() const { return text_.size(); }
int String::length() const { return text_.size(); }
Ref<String> String::substring(int i) const { return Ref<String>(new String(text_.substr(i))); }
Ref<String> String::substring(int start, int end) const {
return Ref<String>(new String(text_.substr(start, (end - start))));
}
void String::append(const std::string& tail) { text_.append(tail); }
void String::append(char c) { text_.append(1, c); }
void String::append(int d) {
string str = StrUtil::numberToString(d);
text_.append(str);
}
void String::append(Ref<String> str) { append(str->getText()); }
string StrUtil::COMBINE_STRING(string str1, string str2) {
string str = str1;
str += str2;
return str;
}
string StrUtil::COMBINE_STRING(string str1, char c) {
string str = str1;
str += c;
return str;
}
string StrUtil::COMBINE_STRING(string str1, int d) {
string str = str1;
str += numberToString(d);
return str;
}
Ref<String> StrUtil::COMBINE_STRING(char c1, Ref<String> content, char c2) {
Ref<String> str(new String(0));
str->append(c1);
str->append(content);
str->append(c2);
return str;
}
template <typename T>
string StrUtil::numberToString(T Number) {
ostringstream ss;
ss << Number;
return ss.str();
}
template <typename T>
T StrUtil::stringToNumber(const string& Text) {
std::istringstream ss(Text);
T result;
return ss >> result ? result : 0;
}
int StrUtil::indexOf(const char* str, char c) {
int len = strlen(str);
for (int i = 0; i < len; i++) {
if (str[i] == c) {
return i;
}
}
return -1;
}

View File

@@ -0,0 +1,58 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
//
// Modified from ZXing. Copyright ZXing authors.
// Licensed under the Apache License, Version 2.0 (the "License").
#ifndef __ZXING_COMMON_STR_HPP__
#define __ZXING_COMMON_STR_HPP__
#include "counted.hpp"
#include <sstream>
#include <string>
using std::string;
namespace zxing {
class String : public Counted {
private:
std::string text_;
public:
explicit String(const std::string& text);
explicit String(int);
char charAt(int) const;
Ref<String> substring(int) const;
Ref<String> substring(int, int) const;
const std::string& getText() const;
int size() const;
void append(std::string const& tail);
void append(char c);
void append(int d);
void append(Ref<String> str);
int length() const;
};
class StrUtil {
public:
static string COMBINE_STRING(string str1, string str2);
static string COMBINE_STRING(string str1, char c);
static string COMBINE_STRING(string str1, int d);
static Ref<String> COMBINE_STRING(char c1, Ref<String> content, char c2);
template <typename T>
static string numberToString(T Number);
template <typename T>
static T stringToNumber(const string& Text);
static int indexOf(const char* str, char c);
};
} // namespace zxing
#endif // __ZXING_COMMON_STR_HPP__

View File

@@ -0,0 +1,683 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
//
// Modified from ZXing. Copyright ZXing authors.
// Licensed under the Apache License, Version 2.0 (the "License").
#include "../../precomp.hpp"
#include "../common/stringutils.hpp"
#include "../decodehints.hpp"
using namespace zxing::common;
// N.B.: these are the iconv strings for at least some versions of iconv
char const* const StringUtils::PLATFORM_DEFAULT_ENCODING = "ANY";
char const* const StringUtils::ASCII = "ASCII";
char const* const StringUtils::SHIFT_JIS = "SHIFT-JIS";
char const* const StringUtils::GBK = "GBK";
char const* const StringUtils::EUC_JP = "EUC-JP";
char const* const StringUtils::UTF8 = "UTF-8";
char const* const StringUtils::ISO88591 = "ISO8859-1";
char const* const StringUtils::GB2312 = "GB2312";
char const* const StringUtils::BIG5 = "BIG5";
char const* const StringUtils::GB18030 = "GB18030";
const bool StringUtils::ASSUME_SHIFT_JIS = false;
#ifdef USE_UCHARDET
#include "uchardet/uchardet.h"
#endif
// Added convertString
#ifndef NO_ICONV
#include <iconv.h>
// Required for compatibility. TODO: test on Symbian
//#ifdef ZXING_ICONV_CONST
#undef ICONV_CONST
#define ICONV_CONST const
//#endif
#ifndef ICONV_CONST
#define ICONV_CONST /**/
#endif
// Add this to fix both Mac and Windows compilers
// by Skylook
template <class T>
class sloppy {};
// convert between T** and const T**
template <class T>
class sloppy<T**> {
T** t;
public:
sloppy(T** mt) : t(mt) {}
sloppy(const T** mt) : t(const_cast<T**>(mt)) {}
operator T* *() const { return t; }
operator const T* *() const { return const_cast<const T**>(t); }
};
#endif
string StringUtils::convertString(const char* rawData, int length, const char* fromCharset,
const char* toCharset) {
string result;
const char* bufIn = rawData;
int nIn = length;
// If from and to charset are the same, return
int ret = strcmp(fromCharset, toCharset);
if (ret == 0) {
result.append((const char*)bufIn, nIn);
return result;
}
#ifndef NO_ICONV
if (nIn == 0) {
return "";
}
iconv_t cd;
// cout<<src<<endl;
cd = iconv_open(toCharset, fromCharset);
// iconv_t cd = iconv_open(StringUtils::GBK, src);
if (cd == (iconv_t)-1) {
// result.append((const char *)bufIn, nIn);
result = "";
return result;
}
const int maxOut = 4 * nIn + 1;
char* bufOut = new char[maxOut];
ICONV_CONST char* fromPtr = (ICONV_CONST char*)bufIn;
size_t nFrom = nIn;
char* toPtr = (char*)bufOut;
size_t nTo = maxOut;
size_t oneway = -1;
if (nFrom > 0) {
// size_t oneway = iconv(cd, &fromPtr, &nFrom, &toPtr, &nTo);
oneway = iconv(cd, sloppy<char**>(&fromPtr), &nFrom, sloppy<char**>(&toPtr), &nTo);
}
iconv_close(cd);
int nResult = maxOut - nTo;
bufOut[nResult] = '\0';
result.append((const char*)bufOut);
delete[] bufOut;
// Cannot convert string
if (oneway == (size_t)(-1)) {
// result.append((const char *)bufIn, nIn);
result = "";
}
#else
result.append((const char*)bufIn, nIn);
#endif
return result;
}
string StringUtils::guessEncoding(char* bytes, int length) {
#ifdef USE_UCHARDET
if (length < 10) {
return guessEncodingZXing(bytes, length);
} else {
return guessEncodingUCharDet(bytes, length);
}
#else
return guessEncodingZXing(bytes, length);
#endif
}
#ifdef USE_UCHARDET
string StringUtils::guessEncodingUCharDet(char* bytes, int length) {
uchardet_t handle = uchardet_new();
int retval = uchardet_handle_data(handle, bytes, length);
if (retval != 0) {
fprintf(stderr, "Handle data error.\n");
exit(0);
}
uchardet_data_end(handle);
const char* charset = uchardet_get_charset(handle);
string charsetStr(charset);
uchardet_delete(handle);
if (charsetStr.size() != 0) {
return charsetStr;
} else {
return guessEncodingZXing(bytes, length);
}
// Otherwise, we take a wild guess with platform encoding
// return PLATFORM_DEFAULT_ENCODING;
}
#endif
string StringUtils::guessEncodingZXing(char* bytes, int length) {
//
// typedef bool boolean;
// For now, merely tries to distinguish ISO-8859-1, UTF-8 and Shift_JIS,
// which should be by far the most common encodings.
bool canBeISO88591 = true;
bool canBeShiftJIS = true;
bool canBeUTF8 = true;
bool canBeGB2312 = true;
bool canBeGBK = true;
bool canBeBIG5 = true;
bool canBeASCII = true;
int utf8BytesLeft = 0;
int utf2BytesChars = 0;
int utf3BytesChars = 0;
int utf4BytesChars = 0;
int sjisBytesLeft = 0;
int sjisKatakanaChars = 0;
int sjisCurKatakanaWordLength = 0;
int sjisCurDoubleBytesWordLength = 0;
int sjisMaxKatakanaWordLength = 0;
int sjisMaxDoubleBytesWordLength = 0;
int isoHighOther = 0;
int gb2312SCByteChars = 0;
int big5TWBytesChars = 0;
bool utf8bom = length > 3 && (unsigned char)bytes[0] == 0xEF &&
(unsigned char)bytes[1] == 0xBB && (unsigned char)bytes[2] == 0xBF;
for (int i = 0; i < length && (canBeISO88591 || canBeShiftJIS || canBeUTF8 || canBeGBK); i++) {
int value = bytes[i] & 0xFF;
// UTF-8 stuff
if (canBeUTF8) {
if (utf8BytesLeft > 0) {
if ((value & 0x80) == 0) {
canBeUTF8 = false;
} else {
utf8BytesLeft--;
}
} else if ((value & 0x80) != 0) {
if ((value & 0x40) == 0) {
canBeUTF8 = false;
} else {
utf8BytesLeft++;
if ((value & 0x20) == 0) {
utf2BytesChars++;
} else {
utf8BytesLeft++;
if ((value & 0x10) == 0) {
utf3BytesChars++;
} else {
utf8BytesLeft++;
if ((value & 0x08) == 0) {
utf4BytesChars++;
} else {
canBeUTF8 = false;
}
}
}
}
}
}
// Shift_JIS stuff
if (canBeShiftJIS) {
if (sjisBytesLeft > 0) {
if (value < 0x40 || value == 0x7F || value > 0xFC) {
canBeShiftJIS = false;
} else {
sjisBytesLeft--;
}
} else if (value == 0x80 || value == 0xA0 || value > 0xEF) {
canBeShiftJIS = false;
} else if (value > 0xA0 && value < 0xE0) {
sjisKatakanaChars++;
sjisCurDoubleBytesWordLength = 0;
sjisCurKatakanaWordLength++;
if (sjisCurKatakanaWordLength > sjisMaxKatakanaWordLength) {
sjisMaxKatakanaWordLength = sjisCurKatakanaWordLength;
}
} else if (value > 0x7F) {
sjisBytesLeft++;
// sjisDoubleBytesChars++;
sjisCurKatakanaWordLength = 0;
sjisCurDoubleBytesWordLength++;
if (sjisCurDoubleBytesWordLength > sjisMaxDoubleBytesWordLength) {
sjisMaxDoubleBytesWordLength = sjisCurDoubleBytesWordLength;
}
} else {
// sjisLowChars++;
sjisCurKatakanaWordLength = 0;
sjisCurDoubleBytesWordLength = 0;
}
}
// ISO-8859-1 stuff
if (canBeISO88591) {
if (value > 0x7F && value < 0xA0) {
canBeISO88591 = false;
} else if (value > 0x9F) {
if (value < 0xC0 || value == 0xD7 || value == 0xF7) {
isoHighOther++;
}
}
}
}
// Get how many chinese sc & tw words
gb2312SCByteChars = is_gb2312_code(bytes, length);
big5TWBytesChars = is_big5_code(bytes, length);
if (gb2312SCByteChars <= 0) {
canBeGB2312 = false;
}
if (big5TWBytesChars <= 0) {
canBeBIG5 = false;
}
if (!is_gbk_code(bytes, length)) {
canBeGBK = false;
}
if (canBeUTF8 && utf8BytesLeft > 0) {
canBeUTF8 = false;
}
if (canBeShiftJIS && sjisBytesLeft > 0) {
canBeShiftJIS = false;
}
if (is_ascii_code(bytes, length) <= 0) {
canBeASCII = false;
}
// Easy -- if there is BOM or at least 1 valid not-single byte character
// (and no evidence it can't be UTF-8), done
if (canBeUTF8 && (utf8bom || utf2BytesChars + utf3BytesChars + utf4BytesChars > 0)) {
return UTF8;
}
// if ( canBeBIG5 == false && canBeGB2312 == false )
int chineseWordLen =
gb2312SCByteChars > big5TWBytesChars ? gb2312SCByteChars : big5TWBytesChars;
int chineseByteLen = chineseWordLen * 2;
int japaneseByteLen = sjisMaxKatakanaWordLength + sjisMaxDoubleBytesWordLength * 2;
// if ( chineseByteLen < japaneseByteLen || (japaneseByteLen == 0 &&
// chineseByteLen == 0) ) if ( (gb2312SCByteChars < sjisKatakanaChars) &&
// (big5TWBytesChars < sjisKatakanaChars) )
//{
// Easy -- if assuming Shift_JIS or at least 3 valid consecutive not-ascii
// characters (and no evidence it can't be), done
if (canBeShiftJIS &&
(ASSUME_SHIFT_JIS || sjisMaxKatakanaWordLength >= 3 || sjisMaxDoubleBytesWordLength >= 3)) {
// return SHIFT_JIS;
if (chineseByteLen <= japaneseByteLen) {
if (chineseByteLen == japaneseByteLen) {
if (chineseWordLen < sjisKatakanaChars) {
return SHIFT_JIS;
}
} else {
return SHIFT_JIS;
}
}
}
// Distinguishing Shift_JIS and ISO-8859-1 can be a little tough for short
// words. The crude heuristic is:
// - If we saw
// - only two consecutive katakana chars in the whole text, or
// - at least 10% of bytes that could be "upper" not-alphanumeric Latin1,
// - then we conclude Shift_JIS, else ISO-8859-1
if (canBeISO88591 && canBeShiftJIS) {
if ((sjisMaxKatakanaWordLength == 2 && sjisKatakanaChars == 2) ||
isoHighOther * 10 >= length) {
/*
if ( chineseByteLen < japaneseByteLen )
{
return SHIFT_JIS;
}
*/
if (chineseByteLen <= japaneseByteLen) {
if (chineseByteLen == japaneseByteLen) {
if (chineseWordLen < sjisKatakanaChars) {
return SHIFT_JIS;
}
} else {
return SHIFT_JIS;
}
}
} else {
if (chineseByteLen <= 0 && !canBeGB2312 && !canBeBIG5) {
return ISO88591;
}
}
}
//}
// Otherwise, try in order ISO-8859-1, Shift JIS, UTF-8 and fall back to
// default platform encoding
if (canBeGB2312) {
return GB2312;
}
if (canBeBIG5) {
return BIG5;
}
if (canBeShiftJIS) {
return SHIFT_JIS;
}
if (canBeGBK) {
return GBK;
}
if (canBeISO88591) {
return ISO88591;
}
if (canBeUTF8) {
return UTF8;
}
if (canBeASCII) {
return ASCII;
}
// Otherwise, we take a wild guess with platform encoding
return PLATFORM_DEFAULT_ENCODING;
}
// judge the byte whether begin with binary 10
int StringUtils::is_utf8_special_byte(unsigned char c) {
unsigned char special_byte = 0X02; // binary 00000010
if (c >> 6 == special_byte) {
return 1;
} else {
return 0;
}
}
int StringUtils::is_utf8_code(char* str, int length) {
unsigned char one_byte = 0X00; // binary 00000000
unsigned char two_byte = 0X06; // binary 00000110
unsigned char three_byte = 0X0E; // binary 00001110
unsigned char four_byte = 0X1E; // binary 00011110
unsigned char five_byte = 0X3E; // binary 00111110
unsigned char six_byte = 0X7E; // binary 01111110
int utf8_yes = 0;
int utf8_no = 0;
unsigned char k = 0;
unsigned char m = 0;
unsigned char n = 0;
unsigned char p = 0;
unsigned char q = 0;
unsigned char c = 0;
for (int i = 0; i < length;) {
c = (unsigned char)str[i];
if (c >> 7 == one_byte) {
i++;
continue;
} else if (c >> 5 == two_byte) {
k = (unsigned char)str[i + 1];
if (is_utf8_special_byte(k)) {
utf8_yes++;
i += 2;
continue;
}
} else if (c >> 4 == three_byte) {
m = (unsigned char)str[i + 1];
n = (unsigned char)str[i + 2];
if (is_utf8_special_byte(m) && is_utf8_special_byte(n)) {
utf8_yes++;
i += 3;
continue;
}
} else if (c >> 3 == four_byte) {
k = (unsigned char)str[i + 1];
m = (unsigned char)str[i + 2];
n = (unsigned char)str[i + 3];
if (is_utf8_special_byte(k) && is_utf8_special_byte(m) && is_utf8_special_byte(n)) {
utf8_yes++;
i += 4;
continue;
}
} else if (c >> 2 == five_byte) {
k = (unsigned char)str[i + 1];
m = (unsigned char)str[i + 2];
n = (unsigned char)str[i + 3];
p = (unsigned char)str[i + 4];
if (is_utf8_special_byte(k) && is_utf8_special_byte(m) && is_utf8_special_byte(n) &&
is_utf8_special_byte(p)) {
utf8_yes++;
i += 5;
continue;
}
} else if (c >> 1 == six_byte) {
k = (unsigned char)str[i + 1];
m = (unsigned char)str[i + 2];
n = (unsigned char)str[i + 3];
p = (unsigned char)str[i + 4];
q = (unsigned char)str[i + 5];
if (is_utf8_special_byte(k) && is_utf8_special_byte(m) && is_utf8_special_byte(n) &&
is_utf8_special_byte(p) && is_utf8_special_byte(q)) {
utf8_yes++;
i += 6;
continue;
}
}
utf8_no++;
i++;
}
// printf("uft8_yes: %d utf8_no:%d\n", utf8_yes, utf8_no);
if ((utf8_yes + utf8_no) != 0) {
int ret = (100 * utf8_yes) / (utf8_yes + utf8_no);
if (ret > 90) {
return 1;
} else {
return 0;
}
}
return 0;
}
int StringUtils::is_gb2312_code(char* str, int length) {
unsigned char one_byte = 0X00; // binary 00000000
int gb2312_yes = 0;
int gb2312_no = 0;
unsigned char k = 0;
unsigned char c = 0;
for (int i = 0; i < length;) {
c = (unsigned char)str[i];
if (c >> 7 == one_byte) {
i++;
continue;
} else if (c >= 0XA1 && c <= 0XF7) {
k = (unsigned char)str[i + 1];
if (k >= 0XA1 && k <= 0XFE) {
gb2312_yes++;
i += 2;
continue;
}
}
gb2312_no++;
i += 2;
}
// printf("gb2312_yes: %d gb2312_no:%d\n", gb2312_yes, gb2312_no);
if ((gb2312_yes + gb2312_no) > 0) {
int ret = (100 * gb2312_yes) / (gb2312_yes + gb2312_no);
if (ret == 100) {
// if (ret > 90) {
// gb2312SCByteChars = gb2312_yes;
return gb2312_yes;
} else {
return 0;
}
}
return 0;
}
int StringUtils::is_big5_code(char* str, int length) {
unsigned char one_byte = 0X00; // binary 00000000
int big5_yes = 0;
int big5_no = 0;
unsigned char k = 0;
unsigned char c = 0;
for (int i = 0; i < length;) {
c = (unsigned char)str[i];
if (c >> 7 == one_byte) {
i++;
continue;
} else if (c >= 0XA1 && c <= 0XF9) {
k = (unsigned char)str[i + 1];
if ((k >= 0X40 && k <= 0X7E) || (k >= 0XA1 && k <= 0XFE)) {
big5_yes++;
i += 2;
continue;
}
}
big5_no++;
i += 2;
}
// printf("%d %d\n", big5_yes, big5_no);
if ((big5_yes + big5_no) > 0) {
int ret = (100 * big5_yes) / (big5_yes + big5_no);
if (ret == 100) {
// if (ret > 90) {
// big5TWBytesChars = big5_yes;
return big5_yes;
} else {
return 0;
}
}
return 0;
}
int StringUtils::is_gbk_code(char* str, int length) {
unsigned char one_byte = 0X00; // binary 00000000
int gbk_yes = 0;
int gbk_no = 0;
unsigned char k = 0;
unsigned char c = 0;
for (int i = 0; i < length;) {
c = (unsigned char)str[i];
if (c >> 7 == one_byte) {
i++;
continue;
} else if (c >= 0X81 && c <= 0XFE) {
k = (unsigned char)str[i + 1];
if (k >= 0X40 && k <= 0XFE) {
gbk_yes++;
i += 2;
continue;
}
}
gbk_no++;
i += 2;
}
// printf("gbk_yes: %d gbk_no:%d\n", gbk_yes, gbk_no);
if ((gbk_yes + gbk_no) > 0) {
int ret = (100 * gbk_yes) / (gbk_yes + gbk_no);
if (ret == 100) {
// if (ret > 90) {
return 1;
} else {
return 0;
}
}
return 0;
}
int StringUtils::is_ascii_code(char* str, int length) {
unsigned char c = 0;
bool isASCII = true;
for (int i = 0; i < length; i++) {
c = (unsigned char)str[i];
if ((c > 127)) {
isASCII = false;
}
}
return (isASCII ? 1 : -1);
}
//#define DEBUG
int StringUtils::shift_jis_to_jis(const unsigned char* may_be_shift_jis, int* jis_first_ptr,
int* jis_second_ptr) {
int status = 0;
unsigned char first = may_be_shift_jis[0];
unsigned char second = may_be_shift_jis[1];
int jis_first = 0;
int jis_second = 0;
/* Check first byte is valid shift JIS. */
if ((first >= 0x81 && first <= 0x84) || (first >= 0x87 && first <= 0x9f)) {
jis_first = 2 * (first - 0x70) - 1;
if (second >= 0x40 && second <= 0x9e) {
jis_second = second - 31;
if (jis_second > 95) {
jis_second -= 1;
}
status = 1;
} else if (second >= 0x9f && second <= 0xfc) {
jis_second = second - 126;
jis_first += 1;
status = 1;
} else {
}
} else if (first >= 0xe0 && first <= 0xef) {
jis_first = 2 * (first - 0xb0) - 1;
if (second >= 0x40 && second <= 0x9e) {
jis_second = second - 31;
if (jis_second > 95) {
jis_second -= 1;
}
status = 1;
} else if (second >= 0x9f && second <= 0xfc) {
jis_second = second - 126;
jis_first += 1;
status = 1;
}
} else {
}
*jis_first_ptr = jis_first;
*jis_second_ptr = jis_second;
return status;
}

View File

@@ -0,0 +1,65 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
//
// Modified from ZXing. Copyright ZXing authors.
// Licensed under the Apache License, Version 2.0 (the "License").
#ifndef __ZXING_COMMON_STRINGUTILS_HPP__
#define __ZXING_COMMON_STRINGUTILS_HPP__
#include "../decodehints.hpp"
#include "../zxing.hpp"
#include <map>
#include <string>
namespace zxing {
namespace common {
class StringUtils;
}
} // namespace zxing
using namespace std;
class zxing::common::StringUtils {
private:
static char const* const PLATFORM_DEFAULT_ENCODING;
public:
static char const* const ASCII;
static char const* const SHIFT_JIS;
static char const* const GB2312;
static char const* const EUC_JP;
static char const* const UTF8;
static char const* const ISO88591;
static char const* const GBK;
static char const* const GB18030;
static char const* const BIG5;
static const bool ASSUME_SHIFT_JIS;
static std::string guessEncoding(char* bytes, int length);
static std::string guessEncodingZXing(char* bytes, int length);
#ifdef USE_UCHARDET
static std::string guessEncodingUCharDet(char* bytes, int length);
#endif
static int is_utf8_special_byte(unsigned char c);
// static int is_utf8_code(const string& str);
static int is_utf8_code(char* str, int length);
static int is_gb2312_code(char* str, int length);
static int is_big5_code(char* str, int length);
static int is_gbk_code(char* str, int length);
static int is_ascii_code(char* str, int length);
static int shift_jis_to_jis(const unsigned char* may_be_shift_jis, int* jis_first_ptr,
int* jis_second_ptr);
static std::string convertString(const char* rawData, int length, const char* fromCharset,
const char* toCharset);
};
#endif // __ZXING_COMMON_STRINGUTILS_HPP__

View File

@@ -0,0 +1,127 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
#include "../../precomp.hpp"
#include "unicomblock.hpp"
namespace zxing {
short UnicomBlock::SEARCH_POS[4][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
UnicomBlock::UnicomBlock(int iMaxHeight, int iMaxWidth)
: m_iHeight(iMaxHeight), m_iWidth(iMaxWidth), m_bInit(false) {}
UnicomBlock::~UnicomBlock() {}
void UnicomBlock::Init() {
if (m_bInit) return;
m_vcIndex = std::vector<unsigned short>(m_iHeight * m_iWidth, 0);
m_vcCount = std::vector<unsigned short>(m_iHeight * m_iWidth, 0);
m_vcMinPnt = std::vector<int>(m_iHeight * m_iWidth, 0);
m_vcMaxPnt = std::vector<int>(m_iHeight * m_iWidth, 0);
m_vcQueue = std::vector<int>(m_iHeight * m_iWidth, 0);
m_bInit = true;
}
void UnicomBlock::Reset(Ref<BitMatrix> poImage) {
m_poImage = poImage;
memset(&m_vcIndex[0], 0, m_vcIndex.size() * sizeof(short));
m_iNowIdx = 0;
}
unsigned short UnicomBlock::GetUnicomBlockIndex(int y, int x) {
if (y >= m_iHeight || x >= m_iWidth) return 0;
if (m_vcIndex[y * m_iWidth + x]) return m_vcIndex[y * m_iWidth + x];
Bfs(y, x);
return m_vcIndex[y * m_iWidth + x];
}
int UnicomBlock::GetUnicomBlockSize(int y, int x) {
if (y >= m_iHeight || x >= m_iWidth) return 0;
if (m_vcIndex[y * m_iWidth + x]) return m_vcCount[y * m_iWidth + x];
Bfs(y, x);
return m_vcCount[y * m_iWidth + x];
}
int UnicomBlock::GetMinPoint(int y, int x, int &iMinY, int &iMinX) {
if (y >= m_iHeight || x >= m_iWidth) return -1;
if (m_vcIndex[y * m_iWidth + x]) {
iMinY = m_vcMinPnt[y * m_iWidth + x] >> 16;
iMinX = m_vcMinPnt[y * m_iWidth + x] & (0xFFFF);
return 0;
}
Bfs(y, x);
iMinY = m_vcMinPnt[y * m_iWidth + x] >> 16;
iMinX = m_vcMinPnt[y * m_iWidth + x] & (0xFFFF);
return 0;
}
int UnicomBlock::GetMaxPoint(int y, int x, int &iMaxY, int &iMaxX) {
if (y >= m_iHeight || x >= m_iWidth) return -1;
if (m_vcIndex[y * m_iWidth + x]) {
iMaxY = m_vcMaxPnt[y * m_iWidth + x] >> 16;
iMaxX = m_vcMaxPnt[y * m_iWidth + x] & (0xFFFF);
return 0;
}
Bfs(y, x);
iMaxY = m_vcMaxPnt[y * m_iWidth + x] >> 16;
iMaxX = m_vcMaxPnt[y * m_iWidth + x] & (0xFFFF);
return 0;
}
void UnicomBlock::Bfs(int y, int x) {
m_iNowIdx++;
int iFront = 0;
int iTail = 0;
int iCount = 1;
int iMaxX = x, iMaxY = y;
int iMinX = x, iMinY = y;
const bool bValue = (m_poImage->get(x, y) != (unsigned char)0);
m_vcIndex[y * m_iWidth + x] = m_iNowIdx;
m_vcQueue[iTail++] = y << 16 | x;
while (iFront < iTail) {
int iNode = m_vcQueue[iFront++];
int iX = iNode & (0xFFFF);
int iY = iNode >> 16;
iMaxX = max(iX, iMaxX);
iMaxY = max(iY, iMaxY);
iMinX = min(iX, iMinX);
iMinY = min(iY, iMinY);
iCount++;
for (int i = 0; i < 4; ++i) {
const int iNextX = iX + SEARCH_POS[i][0], iNextY = iY + SEARCH_POS[i][1];
const int iPosition = iNextY * m_iWidth + iNextX;
if (iPosition >= 0 && iPosition < int(m_vcIndex.size()) && 0 == m_vcIndex[iPosition]) {
if (iNextX < 0 || iNextX >= m_poImage->getWidth() || iNextY < 0 ||
iNextY >= m_poImage->getHeight() ||
bValue != (m_poImage->get(iNextX, iNextY) != (unsigned char)0))
continue;
m_vcIndex[iPosition] = m_iNowIdx;
m_vcQueue[iTail++] = iNextY << 16 | iNextX;
}
}
}
if (iCount >= (1 << 16) - 1) iCount = 0xFFFF;
const int iMinCombine = iMinY << 16 | iMinX;
const int iMaxCombine = iMaxY << 16 | iMaxX;
for (int i = 0; i < iTail; ++i) {
const int iPosition = (m_vcQueue[i] >> 16) * m_iWidth + (m_vcQueue[i] & (0xFFFF));
m_vcCount[iPosition] = iCount;
m_vcMinPnt[iPosition] = iMinCombine;
m_vcMaxPnt[iPosition] = iMaxCombine;
}
}
} // namespace zxing

View File

@@ -0,0 +1,47 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
#ifndef __ZXING_COMMON_UNICOMBLOCK_HPP__
#define __ZXING_COMMON_UNICOMBLOCK_HPP__
#include "bitmatrix.hpp"
#include "counted.hpp"
namespace zxing {
class UnicomBlock : public Counted {
public:
UnicomBlock(int iMaxHeight, int iMaxWidth);
~UnicomBlock();
void Init();
void Reset(Ref<BitMatrix> poImage);
unsigned short GetUnicomBlockIndex(int y, int x);
int GetUnicomBlockSize(int y, int x);
int GetMinPoint(int y, int x, int &iMinY, int &iMinX);
int GetMaxPoint(int y, int x, int &iMaxY, int &iMaxX);
private:
void Bfs(int y, int x);
int m_iHeight;
int m_iWidth;
unsigned short m_iNowIdx;
bool m_bInit;
std::vector<unsigned short> m_vcIndex;
std::vector<unsigned short> m_vcCount;
std::vector<int> m_vcMinPnt;
std::vector<int> m_vcMaxPnt;
std::vector<int> m_vcQueue;
static short SEARCH_POS[4][2];
Ref<BitMatrix> m_poImage;
};
} // namespace zxing
#endif // __ZXING_COMMON_UNICOMBLOCK_HPP__

View File

@@ -0,0 +1,30 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
//
// Modified from ZXing. Copyright ZXing authors.
// Licensed under the Apache License, Version 2.0 (the "License").
#ifndef __ZXING_DECODEHINTS_HPP__
#define __ZXING_DECODEHINTS_HPP__
#include "errorhandler.hpp"
namespace zxing {
class DecodeHints {
private:
bool use_nn_detector_;
public:
explicit DecodeHints(bool use_nn_detector = false) : use_nn_detector_(use_nn_detector){};
bool getUseNNDetector() const { return use_nn_detector_; }
void setUseNNDetector(bool use_nn_detector) { use_nn_detector_ = use_nn_detector; }
};
} // namespace zxing
#endif // __ZXING_DECODEHINTS_HPP__

View File

@@ -0,0 +1,49 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
#include "../precomp.hpp"
#include "errorhandler.hpp"
namespace zxing {
ErrorHandler::ErrorHandler() : err_code_(0), err_msg_("") { Init(); }
ErrorHandler::ErrorHandler(const char* err_msg) : err_code_(-1), err_msg_(err_msg) { Init(); }
ErrorHandler::ErrorHandler(std::string& err_msg) : err_code_(-1), err_msg_(err_msg) { Init(); }
ErrorHandler::ErrorHandler(int err_code) : err_code_(err_code), err_msg_("error") { Init(); }
ErrorHandler::ErrorHandler(int err_code, const char* err_msg)
: err_code_(err_code), err_msg_(err_msg) {
Init();
}
ErrorHandler::ErrorHandler(const ErrorHandler& other) {
err_code_ = other.ErrCode();
err_msg_.assign(other.ErrMsg());
Init();
}
ErrorHandler& ErrorHandler::operator=(const ErrorHandler& other) {
err_code_ = other.ErrCode();
err_msg_.assign(other.ErrMsg());
Init();
return *this;
}
void ErrorHandler::Init() { handler_type_ = KErrorHandler; }
void ErrorHandler::Reset() {
err_code_ = 0;
err_msg_.assign("");
}
void ErrorHandler::PrintInfo() {
printf("handler_tpye %d, error code %d, errmsg %s\n", handler_type_, err_code_,
err_msg_.c_str());
}
} // namespace zxing

View File

@@ -0,0 +1,89 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
#ifndef __ZXING_ERRORHANDLER_HPP__
#define __ZXING_ERRORHANDLER_HPP__
#include <string>
namespace zxing {
enum {
KErrorHandler = 0,
KErrorHandler_NotFound = 1,
KErrorHandler_CheckSum = 2,
KErrorHandler_Reader = 3,
KErrorHandler_IllegalArgument = 4,
KErrorHandler_ReedSolomon = 5,
KErrorHandler_Format = 6,
KErrorHandler_Detector = 7,
KErrorHandler_IllegalState = 8,
};
class ErrorHandler {
public:
ErrorHandler();
explicit ErrorHandler(std::string& err_msg);
explicit ErrorHandler(const char* err_msg);
explicit ErrorHandler(int err_code);
ErrorHandler(int err_code, std::string& err_msg);
ErrorHandler(int err_code, const char* err_msg);
virtual ~ErrorHandler(){};
virtual inline int ErrCode() const { return err_code_; }
virtual inline const std::string& ErrMsg() const { return err_msg_; }
virtual inline int HandlerType() const { return handler_type_; }
virtual void Init();
ErrorHandler(const ErrorHandler& other);
ErrorHandler& operator=(const ErrorHandler& other);
virtual void PrintInfo();
virtual void Reset();
protected:
int handler_type_;
private:
int err_code_;
std::string err_msg_;
};
#define DECLARE_ERROR_HANDLER(__HANDLER__) \
class __HANDLER__##ErrorHandler : public ErrorHandler { \
public: \
__HANDLER__##ErrorHandler() : ErrorHandler() { Init(); }; \
__HANDLER__##ErrorHandler(std::string& err_msg) : ErrorHandler(err_msg) { Init(); }; \
__HANDLER__##ErrorHandler(const char* err_msg) : ErrorHandler(err_msg) { Init(); }; \
__HANDLER__##ErrorHandler(int err_code) : ErrorHandler(err_code) { Init(); }; \
__HANDLER__##ErrorHandler(int err_code, std::string& err_msg) \
: ErrorHandler(err_code, err_msg) { \
Init(); \
}; \
__HANDLER__##ErrorHandler(int err_code, const char* err_msg) \
: ErrorHandler(err_code, err_msg) { \
Init(); \
}; \
__HANDLER__##ErrorHandler(const ErrorHandler& other) : ErrorHandler(other) { Init(); }; \
void Init() override { handler_type_ = KErrorHandler_##__HANDLER__; } \
};
DECLARE_ERROR_HANDLER(Reader)
DECLARE_ERROR_HANDLER(IllegalArgument)
DECLARE_ERROR_HANDLER(ReedSolomon)
DECLARE_ERROR_HANDLER(Format)
DECLARE_ERROR_HANDLER(Detector)
DECLARE_ERROR_HANDLER(NotFound)
DECLARE_ERROR_HANDLER(CheckSum)
DECLARE_ERROR_HANDLER(IllegalState)
#undef DECLARE_ERROR_HANDLER
} // namespace zxing
#endif // __ZXING_ERRORHANDLER_HPP__

View File

@@ -0,0 +1,59 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
//
// Modified from ZXing. Copyright ZXing authors.
// Licensed under the Apache License, Version 2.0 (the "License").
#include "../precomp.hpp"
#include "luminance_source.hpp"
#include <sstream>
using zxing::LuminanceSource;
using zxing::Ref;
LuminanceSource::LuminanceSource(int width, int height)
: width_(width), height_(height) {}
LuminanceSource::~LuminanceSource() {}
bool LuminanceSource::isCropSupported() const { return false; }
Ref<LuminanceSource> LuminanceSource::crop(int, int, int, int, zxing::ErrorHandler&) const {
return Ref<LuminanceSource>();
}
bool LuminanceSource::isRotateSupported() const { return false; }
Ref<LuminanceSource> LuminanceSource::rotateCounterClockwise(zxing::ErrorHandler&) const {
return Ref<LuminanceSource>();
}
LuminanceSource::operator std::string() const {
ArrayRef<char> row;
std::ostringstream oss;
zxing::ErrorHandler err_handler;
for (int y = 0; y < getHeight(); y++) {
err_handler.Reset();
row = getRow(y, row, err_handler);
if (err_handler.ErrCode()) continue;
for (int x = 0; x < getWidth(); x++) {
int luminance = row[x] & 0xFF;
char c;
if (luminance < 0x40) {
c = '#';
} else if (luminance < 0x80) {
c = '+';
} else if (luminance < 0xC0) {
c = '.';
} else {
c = ' ';
}
oss << c;
}
oss << '\n';
}
return oss.str();
}

View File

@@ -0,0 +1,57 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
//
// Modified from ZXing. Copyright ZXing authors.
// Licensed under the Apache License, Version 2.0 (the "License").
#ifndef __ZXING_LUMINANCE_SOURCE_HPP__
#define __ZXING_LUMINANCE_SOURCE_HPP__
#include <string.h>
#include "common/array.hpp"
#include "common/bytematrix.hpp"
#include "common/counted.hpp"
#include "errorhandler.hpp"
namespace zxing {
class LuminanceSource : public Counted {
protected:
int width_;
int height_;
public:
LuminanceSource(int width, int height);
virtual ~LuminanceSource();
int getWidth() const { return width_; }
int getHeight() const { return height_; }
void setWidth(int w) { width_ = w; }
void setHeight(int h) { height_ = h; }
void filter();
// Callers take ownership of the returned memory and must call delete [] on
// it themselves.
virtual ArrayRef<char> getRow(int y, ArrayRef<char> row,
zxing::ErrorHandler& err_handler) const = 0;
virtual ArrayRef<char> getMatrix() const = 0;
virtual Ref<ByteMatrix> getByteMatrix() const = 0;
virtual bool isCropSupported() const;
virtual Ref<LuminanceSource> crop(int left, int top, int width, int height,
zxing::ErrorHandler& err_handler) const;
virtual bool isRotateSupported() const;
virtual Ref<LuminanceSource> rotateCounterClockwise(zxing::ErrorHandler& err_handler) const;
operator std::string() const;
};
} // namespace zxing
#endif // __ZXING_LUMINANCE_SOURCE_HPP__

View File

@@ -0,0 +1,240 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
//
// Modified from ZXing. Copyright ZXing authors.
// Licensed under the Apache License, Version 2.0 (the "License").
#include "../../../precomp.hpp"
#include "bitmatrixparser.hpp"
#include "datamask.hpp"
using zxing::ErrorHandler;
namespace zxing {
namespace qrcode {
int BitMatrixParser::copyBit(size_t x, size_t y, int versionBits) {
bool bit = ((mirror_ ? bitMatrix_->get(y, x) : bitMatrix_->get(x, y)) != (unsigned char)0);
return bit ? (versionBits << 1) | 0x1 : versionBits << 1;
}
BitMatrixParser::BitMatrixParser(Ref<BitMatrix> bitMatrix, ErrorHandler &err_handler)
: bitMatrix_(bitMatrix), parsedVersion_(0), parsedFormatInfo_() {
mirror_ = false;
size_t dimension = bitMatrix->getHeight();
if ((dimension < 21) || (dimension & 0x03) != 1) {
err_handler = zxing::ReaderErrorHandler("Dimension must be 1 mod 4 and >= 21");
return;
}
}
Ref<FormatInformation> BitMatrixParser::readFormatInformation(ErrorHandler &err_handler) {
if (parsedFormatInfo_ != 0) {
return parsedFormatInfo_;
}
// Read top-left format info bits
int formatInfoBits1 = 0;
for (int i = 0; i < 6; i++) {
formatInfoBits1 = copyBit(i, 8, formatInfoBits1);
}
// .. and skip a bit in the timing pattern ...
formatInfoBits1 = copyBit(7, 8, formatInfoBits1);
formatInfoBits1 = copyBit(8, 8, formatInfoBits1);
formatInfoBits1 = copyBit(8, 7, formatInfoBits1);
// .. and skip a bit in the timing pattern ...
for (int j = 5; j >= 0; j--) {
formatInfoBits1 = copyBit(8, j, formatInfoBits1);
}
// Read the top-right/bottom-left pattern
int dimension = bitMatrix_->getHeight();
int formatInfoBits2 = 0;
int jMin = dimension - 7;
for (int j = dimension - 1; j >= jMin; j--) {
formatInfoBits2 = copyBit(8, j, formatInfoBits2);
}
for (int i = dimension - 8; i < dimension; i++) {
formatInfoBits2 = copyBit(i, 8, formatInfoBits2);
}
parsedFormatInfo_ =
FormatInformation::decodeFormatInformation(formatInfoBits1, formatInfoBits2);
if (parsedFormatInfo_ != 0) {
return parsedFormatInfo_;
}
err_handler = zxing::ReaderErrorHandler("Could not decode format information");
return Ref<FormatInformation>();
}
Version *BitMatrixParser::readVersion(ErrorHandler &err_handler) {
if (parsedVersion_ != 0) {
return parsedVersion_;
}
int dimension = bitMatrix_->getHeight();
int provisionalVersion = (dimension - 17) >> 2;
if (provisionalVersion <= 6) {
Version *version = Version::getVersionForNumber(provisionalVersion, err_handler);
if (err_handler.ErrCode()) return NULL;
return version;
}
// Read top-right version info: 3 wide by 6 tall
int versionBits = 0;
for (int y = 5; y >= 0; y--) {
int xMin = dimension - 11;
for (int x = dimension - 9; x >= xMin; x--) {
versionBits = copyBit(x, y, versionBits);
}
}
parsedVersion_ = Version::decodeVersionInformation(versionBits);
if (parsedVersion_ != 0 && parsedVersion_->getDimensionForVersion(err_handler) == dimension) {
return parsedVersion_;
}
// Hmm, failed. Try bottom left: 6 wide by 3 tall
versionBits = 0;
for (int x = 5; x >= 0; x--) {
int yMin = dimension - 11;
for (int y = dimension - 9; y >= yMin; y--) {
versionBits = copyBit(x, y, versionBits);
}
}
parsedVersion_ = Version::decodeVersionInformation(versionBits);
if (parsedVersion_ == NULL) {
err_handler = zxing::ReaderErrorHandler("Could not decode version");
return NULL;
}
if (parsedVersion_ != 0 && parsedVersion_->getDimensionForVersion(err_handler) == dimension) {
return parsedVersion_;
}
err_handler = zxing::ReaderErrorHandler("Could not decode version");
return NULL;
}
/**
* <p>Reads the bits in the {@link BitMatrix} representing the finder pattern in
* the correct order in order to reconstruct the codewords bytes contained
* within the QR Code.</p>
*
* @return bytes encoded within the QR Code
*/
ArrayRef<char> BitMatrixParser::readCodewords(ErrorHandler &err_handler) {
Ref<FormatInformation> formatInfo = readFormatInformation(err_handler);
if (err_handler.ErrCode()) return ArrayRef<char>();
Version *version = readVersion(err_handler);
if (err_handler.ErrCode()) return ArrayRef<char>();
DataMask &dataMask = DataMask::forReference((int)formatInfo->getDataMask(), err_handler);
if (err_handler.ErrCode()) return ArrayRef<char>();
// cout << (int)formatInfo->getDataMask() << endl;
int dimension = bitMatrix_->getHeight();
dataMask.unmaskBitMatrix(*bitMatrix_, dimension);
// cerr << *bitMatrix_ << endl;
// cerr << version->getTotalCodewords() << endl;
Ref<BitMatrix> functionPattern = version->buildFunctionPattern(err_handler);
if (err_handler.ErrCode()) return ArrayRef<char>();
// cout << *functionPattern << endl;
bool readingUp = true;
ArrayRef<char> result(version->getTotalCodewords());
int resultOffset = 0;
int currentByte = 0;
int bitsRead = 0;
// Read columns in pairs, from right to left
for (int x = dimension - 1; x > 0; x -= 2) {
if (x == 6) {
// Skip whole column with vertical alignment pattern;
// saves time and makes the other code proceed more cleanly
x--;
}
// Read alternatingly from bottom to top then top to bottom
for (int counter = 0; counter < dimension; counter++) {
int y = readingUp ? dimension - 1 - counter : counter;
for (int col = 0; col < 2; col++) {
// Ignore bits covered by the function pattern
if (!functionPattern->get(x - col, y)) {
// Read a bit
bitsRead++;
currentByte <<= 1;
if (bitMatrix_->get(x - col, y)) {
currentByte |= 1;
}
// If we've made a whole byte, save it off
if (bitsRead == 8) {
result[resultOffset++] = (char)currentByte;
bitsRead = 0;
currentByte = 0;
}
}
}
}
readingUp = !readingUp; // switch directions
}
if (resultOffset != version->getTotalCodewords()) {
err_handler = zxing::ReaderErrorHandler("Did not read all codewords");
return ArrayRef<char>();
}
return result;
}
/**
* Revert the mask removal done while reading the code words. The bit matrix
* should revert to its original state.
*/
void BitMatrixParser::remask() {
if (parsedFormatInfo_ == NULL) {
return; // We have no format information, and have no data mask
}
ErrorHandler err_handler;
DataMask &dataMask = DataMask::forReference(parsedFormatInfo_->getDataMask(), err_handler);
if (err_handler.ErrCode()) return;
int dimension = bitMatrix_->getHeight();
dataMask.unmaskBitMatrix(*bitMatrix_, dimension);
}
/**
* Prepare the parser for a mirrored operation.
* This flag has effect only on the {@link #readFormatInformation()} and the
* {@link #readVersion()}. Before proceeding with {@link #readCodewords()} the
* {@link #mirror()} method should be called.
*
* @param mirror Whether to read version and format information mirrored.
*/
void BitMatrixParser::setMirror(bool mirror) {
parsedVersion_ = NULL;
parsedFormatInfo_ = NULL;
mirror_ = mirror;
}
/** Mirror the bit matrix in order to attempt a second reading. */
void BitMatrixParser::mirror() {
for (int x = 0; x < bitMatrix_->getWidth(); x++) {
for (int y = x + 1; y < bitMatrix_->getHeight(); y++) {
if (bitMatrix_->get(x, y) != bitMatrix_->get(y, x)) {
bitMatrix_->flip(y, x);
bitMatrix_->flip(x, y);
}
}
}
}
} // namespace qrcode
} // namespace zxing

View File

@@ -0,0 +1,53 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
//
// Modified from ZXing. Copyright ZXing authors.
// Licensed under the Apache License, Version 2.0 (the "License").
#ifndef __ZXING_QRCODE_DECODER_BITMATRIXPARSER_HPP__
#define __ZXING_QRCODE_DECODER_BITMATRIXPARSER_HPP__
#include "../../common/array.hpp"
#include "../../common/bitmatrix.hpp"
#include "../../common/counted.hpp"
#include "../../errorhandler.hpp"
#include "../format_information.hpp"
#include "../version.hpp"
namespace zxing {
namespace qrcode {
class BitMatrixParser : public Counted {
private:
Ref<BitMatrix> bitMatrix_;
Version *parsedVersion_;
Ref<FormatInformation> parsedFormatInfo_;
bool mirror_;
int copyBit(size_t x, size_t y, int versionBits);
public:
BitMatrixParser(Ref<BitMatrix> bitMatrix, ErrorHandler &err_handler);
Ref<FormatInformation> readFormatInformation(ErrorHandler &err_handler);
Version *readVersion(ErrorHandler &err_handler);
ArrayRef<char> readCodewords(ErrorHandler &err_handler);
public:
void remask();
void setMirror(bool mirror);
void mirror();
void mirrorH();
private:
BitMatrixParser(const BitMatrixParser &);
BitMatrixParser &operator=(const BitMatrixParser &);
};
} // namespace qrcode
} // namespace zxing
#endif // __ZXING_QRCODE_DECODER_BITMATRIXPARSER_HPP__

View File

@@ -0,0 +1,103 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
//
// Modified from ZXing. Copyright ZXing authors.
// Licensed under the Apache License, Version 2.0 (the "License").
#include "../../../precomp.hpp"
#include "datablock.hpp"
namespace zxing {
namespace qrcode {
using zxing::ErrorHandler;
DataBlock::DataBlock(int numDataCodewords, ArrayRef<char> codewords)
: numDataCodewords_(numDataCodewords), codewords_(codewords) {}
int DataBlock::getNumDataCodewords() { return numDataCodewords_; }
ArrayRef<char> DataBlock::getCodewords() { return codewords_; }
std::vector<Ref<DataBlock> > DataBlock::getDataBlocks(ArrayRef<char> rawCodewords, Version *version,
ErrorCorrectionLevel &ecLevel,
ErrorHandler &err_handler) {
// Figure out the number and size of data blocks used by this version and
// error correction level
ECBlocks &ecBlocks = version->getECBlocksForLevel(ecLevel);
// First count the total number of data blocks
int totalBlocks = 0;
vector<ECB *> ecBlockArray = ecBlocks.getECBlocks();
for (size_t i = 0; i < ecBlockArray.size(); i++) {
totalBlocks += ecBlockArray[i]->getCount();
}
// Now establish DataBlocks of the appropriate size and number of data
// codewords
std::vector<Ref<DataBlock> > result(totalBlocks);
int numResultBlocks = 0;
for (size_t j = 0; j < ecBlockArray.size(); j++) {
ECB *ecBlock = ecBlockArray[j];
for (int i = 0; i < ecBlock->getCount(); i++) {
int numDataCodewords = ecBlock->getDataCodewords();
int numBlockCodewords = ecBlocks.getECCodewords() + numDataCodewords;
ArrayRef<char> buffer(numBlockCodewords);
Ref<DataBlock> blockRef(new DataBlock(numDataCodewords, buffer));
result[numResultBlocks++] = blockRef;
}
}
// All blocks have the same amount of data, except that the last n
// (where n may be 0) have 1 more byte. Figure out where these start.
int shorterBlocksTotalCodewords = result[0]->codewords_->size();
int longerBlocksStartAt = result.size() - 1;
while (longerBlocksStartAt >= 0) {
int numCodewords = result[longerBlocksStartAt]->codewords_->size();
if (numCodewords == shorterBlocksTotalCodewords) {
break;
}
if (numCodewords != shorterBlocksTotalCodewords + 1) {
err_handler =
zxing::IllegalArgumentErrorHandler("Data block sizes differ by more than 1");
return std::vector<Ref<DataBlock> >();
}
longerBlocksStartAt--;
}
longerBlocksStartAt++;
int shorterBlocksNumDataCodewords = shorterBlocksTotalCodewords - ecBlocks.getECCodewords();
// The last elements of result may be 1 element longer;
// first fill out as many elements as all of them have
int rawCodewordsOffset = 0;
for (int i = 0; i < shorterBlocksNumDataCodewords; i++) {
for (int j = 0; j < numResultBlocks; j++) {
result[j]->codewords_[i] = rawCodewords[rawCodewordsOffset++];
}
}
// Fill out the last data block in the longer ones
for (int j = longerBlocksStartAt; j < numResultBlocks; j++) {
result[j]->codewords_[shorterBlocksNumDataCodewords] = rawCodewords[rawCodewordsOffset++];
}
// Now add in error correction blocks
int max = result[0]->codewords_->size();
for (int i = shorterBlocksNumDataCodewords; i < max; i++) {
for (int j = 0; j < numResultBlocks; j++) {
int iOffset = j < longerBlocksStartAt ? i : i + 1;
result[j]->codewords_[iOffset] = rawCodewords[rawCodewordsOffset++];
}
}
if (rawCodewordsOffset != rawCodewords->size()) {
err_handler =
zxing::IllegalArgumentErrorHandler("rawCodewordsOffset != rawCodewords.length");
return std::vector<Ref<DataBlock> >();
}
return result;
}
} // namespace qrcode
} // namespace zxing

View File

@@ -0,0 +1,43 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
//
// Modified from ZXing. Copyright ZXing authors.
// Licensed under the Apache License, Version 2.0 (the "License").
#ifndef __ZXING_QRCODE_DECODER_DATABLOCK_HPP__
#define __ZXING_QRCODE_DECODER_DATABLOCK_HPP__
#include "../../common/array.hpp"
#include "../../common/counted.hpp"
#include "../../errorhandler.hpp"
#include "../error_correction_level.hpp"
#include "../version.hpp"
namespace zxing {
namespace qrcode {
class DataBlock : public Counted {
private:
int numDataCodewords_;
ArrayRef<char> codewords_;
DataBlock(int numDataCodewords, ArrayRef<char> codewords);
public:
static std::vector<Ref<DataBlock> > getDataBlocks(ArrayRef<char> rawCodewords, Version *version,
ErrorCorrectionLevel &ecLevel,
ErrorHandler &err_handler);
int getNumDataCodewords();
ArrayRef<char> getCodewords();
};
} // namespace qrcode
} // namespace zxing
#endif // __ZXING_QRCODE_DECODER_DATABLOCK_HPP__

View File

@@ -0,0 +1,120 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
//
// Modified from ZXing. Copyright ZXing authors.
// Licensed under the Apache License, Version 2.0 (the "License").
#include "../../../precomp.hpp"
#include "datamask.hpp"
namespace zxing {
namespace qrcode {
using zxing::ErrorHandler;
DataMask::DataMask() {}
DataMask::~DataMask() {}
DataMask& DataMask::forReference(int reference, ErrorHandler& err_handler) {
if (reference < 0 || reference > 7) {
err_handler = zxing::IllegalArgumentErrorHandler("reference must be between 0 and 7");
return *DATA_MASKS[0];
}
return *DATA_MASKS[reference];
}
void DataMask::unmaskBitMatrix(BitMatrix& bits, size_t dimension) {
for (size_t y = 0; y < dimension; y++) {
for (size_t x = 0; x < dimension; x++) {
// TODO: check why the coordinates have to be swapped
if (isMasked(y, x)) {
bits.flip(x, y);
}
}
}
}
/**
* 000: mask bits for which (x + y) mod 2 == 0
*/
class DataMask000 : public DataMask {
public:
bool isMasked(size_t x, size_t y) override { return ((x + y) % 2) == 0; }
};
/**
* 001: mask bits for which x mod 2 == 0
*/
class DataMask001 : public DataMask {
public:
bool isMasked(size_t x, size_t) override { return (x % 2) == 0; }
};
/**
* 010: mask bits for which y mod 3 == 0
*/
class DataMask010 : public DataMask {
public:
bool isMasked(size_t, size_t y) override { return y % 3 == 0; }
};
/**
* 011: mask bits for which (x + y) mod 3 == 0
*/
class DataMask011 : public DataMask {
public:
bool isMasked(size_t x, size_t y) override { return (x + y) % 3 == 0; }
};
/**
* 100: mask bits for which (x/2 + y/3) mod 2 == 0
*/
class DataMask100 : public DataMask {
public:
bool isMasked(size_t x, size_t y) override { return (((x >> 1) + (y / 3)) % 2) == 0; }
};
/**
* 101: mask bits for which xy mod 2 + xy mod 3 == 0
*/
class DataMask101 : public DataMask {
public:
bool isMasked(size_t x, size_t y) override {
size_t temp = x * y;
return (temp % 2) + (temp % 3) == 0;
}
};
/**
* 110: mask bits for which (xy mod 2 + xy mod 3) mod 2 == 0
*/
class DataMask110 : public DataMask {
public:
bool isMasked(size_t x, size_t y) override {
size_t temp = x * y;
return (((temp % 2) + (temp % 3)) % 2) == 0;
}
};
/**
* 111: mask bits for which ((x+y)mod 2 + xy mod 3) mod 2 == 0
*/
class DataMask111 : public DataMask {
public:
bool isMasked(size_t x, size_t y) override {
return ((((x + y) % 2) + ((x * y) % 3)) % 2) == 0;
}
};
vector<Ref<DataMask> > DataMask::DATA_MASKS = {
Ref<DataMask>(new DataMask000()), Ref<DataMask>(new DataMask001()),
Ref<DataMask>(new DataMask010()), Ref<DataMask>(new DataMask011()),
Ref<DataMask>(new DataMask100()), Ref<DataMask>(new DataMask101()),
Ref<DataMask>(new DataMask110()), Ref<DataMask>(new DataMask111()),
};
} // namespace qrcode
} // namespace zxing

View File

@@ -0,0 +1,37 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
//
// Modified from ZXing. Copyright ZXing authors.
// Licensed under the Apache License, Version 2.0 (the "License").
#ifndef __ZXING_QRCODE_DECODER_DATAMASK_HPP__
#define __ZXING_QRCODE_DECODER_DATAMASK_HPP__
#include "../../common/array.hpp"
#include "../../common/bitmatrix.hpp"
#include "../../common/counted.hpp"
#include "../../errorhandler.hpp"
namespace zxing {
namespace qrcode {
class DataMask : public Counted {
private:
static std::vector<Ref<DataMask> > DATA_MASKS;
protected:
public:
DataMask();
virtual ~DataMask();
void unmaskBitMatrix(BitMatrix& matrix, size_t dimension);
virtual bool isMasked(size_t x, size_t y) = 0;
static DataMask& forReference(int reference, ErrorHandler& err_handler);
};
} // namespace qrcode
} // namespace zxing
#endif // __ZXING_QRCODE_DECODER_DATAMASK_HPP__

View File

@@ -0,0 +1,490 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
//
// Modified from ZXing. Copyright ZXing authors.
// Licensed under the Apache License, Version 2.0 (the "License").
#include "../../../precomp.hpp"
#include "decoded_bit_stream_parser.hpp"
#include "../../common/stringutils.hpp"
#include "../../zxing.hpp"
#ifndef NO_ICONV_INSIDE
#include <iconv.h>
#endif
#include <iomanip>
#undef ICONV_CONST
#define ICONV_CONST const
#ifndef ICONV_CONST
#define ICONV_CONST /**/
#endif
using zxing::ErrorHandler;
// Add this to fix both Mac and Windows compilers
template <class T>
class sloppy {};
// convert between T** and const T**
template <class T>
class sloppy<T**> {
T** t;
public:
explicit sloppy(T** mt) : t(mt) {}
explicit sloppy(const T** mt) : t(const_cast<T**>(mt)) {}
operator T* *() const { return t; }
operator const T* *() const { return const_cast<const T**>(t); }
};
using namespace std;
using namespace zxing;
using namespace zxing::qrcode;
using namespace zxing::common;
const char DecodedBitStreamParser::ALPHANUMERIC_CHARS[] = {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E',
'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
'U', 'V', 'W', 'X', 'Y', 'Z', ' ', '$', '%', '*', '+', '-', '.', '/', ':'};
// string DecodedBitStreamParser::outputCharset = "UTF-8";
namespace {
int GB2312_SUBSET = 1;
}
void DecodedBitStreamParser::append(std::string& result, string const& in,
ErrorHandler& err_handler) {
append(result, (char const*)in.c_str(), in.length(), err_handler);
}
void DecodedBitStreamParser::append(std::string& result, const char* bufIn, size_t nIn,
ErrorHandler& err_handler) {
if (err_handler.ErrCode()) return;
#ifndef NO_ICONV_INSIDE
if (nIn == 0) {
return;
}
iconv_t cd;
// cout<<src<<endl;
cd = iconv_open(StringUtils::UTF8, src);
// iconv_t cd = iconv_open(StringUtils::GBK, src);
if (cd == (iconv_t)-1) {
result.append((const char*)bufIn, nIn);
return;
}
const int maxOut = 4 * nIn + 1;
char* bufOut = new char[maxOut];
ICONV_CONST char* fromPtr = (ICONV_CONST char*)bufIn;
size_t nFrom = nIn;
char* toPtr = (char*)bufOut;
size_t nTo = maxOut;
while (nFrom > 0) {
size_t oneway = iconv(cd, sloppy<char**>(&fromPtr), &nFrom, sloppy<char**>(&toPtr), &nTo);
if (oneway == (size_t)(-1)) {
iconv_close(cd);
delete[] bufOut;
err_handler = zxing::ReaderErrorHandler("error converting characters");
return;
}
}
iconv_close(cd);
int nResult = maxOut - nTo;
bufOut[nResult] = '\0';
result.append((const char*)bufOut);
delete[] bufOut;
#else
result.append((const char*)bufIn, nIn);
#endif
}
void DecodedBitStreamParser::decodeHanziSegment(Ref<BitSource> bits_, string& result, int count,
ErrorHandler& err_handler) {
BitSource& bits(*bits_);
// Don't crash trying to read more bits than we have available.
if (count * 13 > bits.available()) {
err_handler = zxing::FormatErrorHandler("decodeKanjiSegment");
return;
}
// Each character will require 2 bytes. Read the characters as 2-byte pairs
// and decode as GB2312 afterwards
size_t nBytes = 2 * count;
char* buffer = new char[nBytes];
int offset = 0;
while (count > 0) {
// Each 13 bits encodes a 2-byte character
int twoBytes = bits.readBits(13, err_handler);
if (err_handler.ErrCode()) return;
int assembledTwoBytes = ((twoBytes / 0x060) << 8) | (twoBytes % 0x060);
if (assembledTwoBytes < 0x003BF) {
// In the 0xA1A1 to 0xAAFE range
assembledTwoBytes += 0x0A1A1;
} else {
// In the 0xB0A1 to 0xFAFE range
assembledTwoBytes += 0x0A6A1;
}
buffer[offset] = (char)((assembledTwoBytes >> 8) & 0xFF);
buffer[offset + 1] = (char)(assembledTwoBytes & 0xFF);
offset += 2;
count--;
}
// for(int i=0;i<nBytes;i++)
// cout<<buffer[i]<<endl;
append(result, buffer, nBytes, err_handler);
if (err_handler.ErrCode()) {
delete[] buffer;
return;
}
delete[] buffer;
}
void DecodedBitStreamParser::decodeKanjiSegment(Ref<BitSource> bits, std::string& result, int count,
ErrorHandler& err_handler) {
// Each character will require 2 bytes. Read the characters as 2-byte pairs
// and decode as Shift_JIS afterwards
size_t nBytes = 2 * count;
char* buffer = new char[nBytes];
int offset = 0;
while (count > 0) {
// Each 13 bits encodes a 2-byte character
int twoBytes = bits->readBits(13, err_handler);
if (err_handler.ErrCode()) return;
int assembledTwoBytes = ((twoBytes / 0x0C0) << 8) | (twoBytes % 0x0C0);
if (assembledTwoBytes < 0x01F00) {
// In the 0x8140 to 0x9FFC range
assembledTwoBytes += 0x08140;
} else {
// In the 0xE040 to 0xEBBF range
assembledTwoBytes += 0x0C140;
}
buffer[offset] = (char)(assembledTwoBytes >> 8);
buffer[offset + 1] = (char)assembledTwoBytes;
offset += 2;
count--;
}
append(result, buffer, nBytes, err_handler);
if (err_handler.ErrCode()) {
delete[] buffer;
return;
}
// cout<<buffer<<endl;
delete[] buffer;
}
void DecodedBitStreamParser::decodeByteSegment(Ref<BitSource> bits_, string& result, int count,
CharacterSetECI* currentCharacterSetECI,
ArrayRef<ArrayRef<char> >& byteSegments,
ErrorHandler& err_handler) {
int nBytes = count;
BitSource& bits(*bits_);
// Don't crash trying to read more bits than we have available.
int available = bits.available();
// try to repair count data if count data is invalid
if (count * 8 > available) {
count = (available + 7 / 8);
}
ArrayRef<char> bytes_(count);
char* readBytes = &(*bytes_)[0];
for (int i = 0; i < count; i++) {
// readBytes[i] = (char) bits.readBits(8);
int readBits = available < 8 ? available : 8;
readBytes[i] = (char)bits.readBits(readBits, err_handler);
}
if (err_handler.ErrCode()) return;
// vector<string> encoding;
string encoding;
if (currentCharacterSetECI == 0) {
// The spec isn't clear on this mode; see
// section 6.4.5: t does not say which encoding to assuming
// upon decoding. I have seen ISO-8859-1 used as well as
// Shift_JIS -- without anything like an ECI designator to
// give a hint.
encoding = outputCharset;
} else {
// encoding .push_back(currentCharacterSetECI->name());
encoding = currentCharacterSetECI->name();
}
// cout<<"encoding: "<<encoding<<endl;
append(result, readBytes, nBytes, err_handler);
if (err_handler.ErrCode()) return;
byteSegments->values().push_back(bytes_);
}
void DecodedBitStreamParser::decodeNumericSegment(Ref<BitSource> bits, std::string& result,
int count, ErrorHandler& err_handler) {
int nBytes = count;
// char* bytes = new char[nBytes];
ArrayRef<char> bytes = ArrayRef<char>(new Array<char>(nBytes));
int i = 0;
// Read three digits at a time
while (count >= 3) {
// Each 10 bits encodes three digits
if (bits->available() < 10) {
err_handler = zxing::ReaderErrorHandler("format exception");
return;
}
int threeDigitsBits = bits->readBits(10, err_handler);
if (err_handler.ErrCode()) return;
if (threeDigitsBits >= 1000) {
ostringstream s;
s << "Illegal value for 3-digit unit: " << threeDigitsBits;
err_handler = zxing::ReaderErrorHandler(s.str().c_str());
return;
}
bytes[i++] = ALPHANUMERIC_CHARS[threeDigitsBits / 100];
bytes[i++] = ALPHANUMERIC_CHARS[(threeDigitsBits / 10) % 10];
bytes[i++] = ALPHANUMERIC_CHARS[threeDigitsBits % 10];
count -= 3;
}
if (count == 2) {
if (bits->available() < 7) {
err_handler = zxing::ReaderErrorHandler("format exception");
return;
}
// Two digits left over to read, encoded in 7 bits
int twoDigitsBits = bits->readBits(7, err_handler);
if (err_handler.ErrCode()) return;
if (twoDigitsBits >= 100) {
ostringstream s;
s << "Illegal value for 2-digit unit: " << twoDigitsBits;
err_handler = zxing::ReaderErrorHandler(s.str().c_str());
return;
}
bytes[i++] = ALPHANUMERIC_CHARS[twoDigitsBits / 10];
bytes[i++] = ALPHANUMERIC_CHARS[twoDigitsBits % 10];
} else if (count == 1) {
if (bits->available() < 4) {
err_handler = zxing::ReaderErrorHandler("format exception");
return;
}
// One digit left over to read
int digitBits = bits->readBits(4, err_handler);
if (err_handler.ErrCode()) return;
if (digitBits >= 10) {
ostringstream s;
s << "Illegal value for digit unit: " << digitBits;
err_handler = zxing::ReaderErrorHandler(s.str().c_str());
return;
}
bytes[i++] = ALPHANUMERIC_CHARS[digitBits];
}
append(result, bytes->data(), nBytes, err_handler);
if (err_handler.ErrCode()) return;
}
char DecodedBitStreamParser::toAlphaNumericChar(size_t value, ErrorHandler& err_handler) {
if (value >= sizeof(DecodedBitStreamParser::ALPHANUMERIC_CHARS)) {
err_handler = zxing::FormatErrorHandler("toAlphaNumericChar");
return 0;
}
return ALPHANUMERIC_CHARS[value];
}
void DecodedBitStreamParser::decodeAlphanumericSegment(Ref<BitSource> bits_, string& result,
int count, bool fc1InEffect,
ErrorHandler& err_handler) {
BitSource& bits(*bits_);
ostringstream bytes;
// Read two characters at a time
while (count > 1) {
if (bits.available() < 11) {
err_handler = zxing::FormatErrorHandler("decodeAlphanumericSegment");
return;
}
int nextTwoCharsBits = bits.readBits(11, err_handler);
bytes << toAlphaNumericChar(nextTwoCharsBits / 45, err_handler);
bytes << toAlphaNumericChar(nextTwoCharsBits % 45, err_handler);
if (err_handler.ErrCode()) return;
count -= 2;
}
if (count == 1) {
// special case: one character left
if (bits.available() < 6) {
err_handler = zxing::FormatErrorHandler("decodeAlphanumericSegment");
return;
}
bytes << toAlphaNumericChar(bits.readBits(6, err_handler), err_handler);
if (err_handler.ErrCode()) return;
}
// See section 6.4.8.1, 6.4.8.2
string s = bytes.str();
if (fc1InEffect) {
// We need to massage the result a bit if in an FNC1 mode:
ostringstream r;
for (size_t i = 0; i < s.length(); i++) {
if (s[i] != '%') {
r << s[i];
} else {
if (i < s.length() - 1 && s[i + 1] == '%') {
// %% is rendered as %
r << s[i++];
} else {
// In alpha mode, % should be converted to FNC1 separator
// 0x1D
r << (char)0x1D;
}
}
}
s = r.str();
}
append(result, s, err_handler);
if (err_handler.ErrCode()) return;
}
namespace {
int parseECIValue(BitSource& bits, ErrorHandler& err_handler) {
int firstByte = bits.readBits(8, err_handler);
if (err_handler.ErrCode()) return 0;
if ((firstByte & 0x80) == 0) {
// just one byte
return firstByte & 0x7F;
}
if ((firstByte & 0xC0) == 0x80) {
// two bytes
int secondByte = bits.readBits(8, err_handler);
if (err_handler.ErrCode()) return 0;
return ((firstByte & 0x3F) << 8) | secondByte;
}
if ((firstByte & 0xE0) == 0xC0) {
// three bytes
int secondThirdBytes = bits.readBits(16, err_handler);
if (err_handler.ErrCode()) return 0;
return ((firstByte & 0x1F) << 16) | secondThirdBytes;
}
err_handler = zxing::FormatErrorHandler("parseECIValue");
return 0;
}
} // namespace
Ref<DecoderResult> DecodedBitStreamParser::decode(ArrayRef<char> bytes, Version* version,
ErrorCorrectionLevel const& ecLevel,
ErrorHandler& err_handler, int iVersion) {
Ref<BitSource> bits_(new BitSource(bytes));
BitSource& bits(*bits_);
string result;
result.reserve(50);
Mode* mode = 0;
string modeName;
ArrayRef<ArrayRef<char> > byteSegments(0);
CharacterSetECI* currentCharacterSetECI = 0;
bool fc1InEffect = false;
outputCharset = "UTF-8";
do {
// While still another segment to read...
if (bits.available() < 4) {
// OK, assume we're done. Really, a TERMINATOR mode should have been
// recorded here
mode = &Mode::TERMINATOR;
} else {
mode = &Mode::forBits(bits.readBits(4, err_handler),
err_handler); // mode is encoded by 4 bits
if (err_handler.ErrCode()) return Ref<DecoderResult>();
}
if (mode != &Mode::TERMINATOR) {
if ((mode == &Mode::FNC1_FIRST_POSITION) || (mode == &Mode::FNC1_SECOND_POSITION)) {
// We do little with FNC1 except alter the parsed result a bit
// according to the spec
fc1InEffect = true;
} else if (mode == &Mode::STRUCTURED_APPEND) {
if (bits.available() < 16) {
err_handler = zxing::FormatErrorHandler("decode");
return Ref<DecoderResult>();
}
// not really supported; all we do is ignore it
// Read next 8 bits (symbol sequence #) and 8 bits (parity
// data), then continue
bits.readBits(16, err_handler);
if (err_handler.ErrCode()) return Ref<DecoderResult>();
} else if (mode == &Mode::ECI) {
// Count doesn't apply to ECI
int value = parseECIValue(bits, err_handler);
if (err_handler.ErrCode()) Ref<DecoderResult>();
currentCharacterSetECI = CharacterSetECI::getCharacterSetECIByValueFind(value);
if (currentCharacterSetECI == 0) {
err_handler = zxing::FormatErrorHandler("decode");
return Ref<DecoderResult>();
}
} else {
// First handle Hanzi mode which does not start with character
// count
if (mode == &Mode::HANZI) {
// chinese mode contains a sub set indicator right after
// mode indicator
int subset = bits.readBits(4, err_handler);
int countHanzi =
bits.readBits(mode->getCharacterCountBits(version), err_handler);
if (err_handler.ErrCode()) return Ref<DecoderResult>();
if (subset == GB2312_SUBSET) {
decodeHanziSegment(bits_, result, countHanzi, err_handler);
if (err_handler.ErrCode()) Ref<DecoderResult>();
outputCharset = "GB2312";
modeName = mode->getName();
}
} else {
// "Normal" QR code modes:
// How many characters will follow, encoded in this mode?
int count = bits.readBits(mode->getCharacterCountBits(version), err_handler);
if (err_handler.ErrCode()) return Ref<DecoderResult>();
if (mode == &Mode::NUMERIC) {
decodeNumericSegment(bits_, result, count, err_handler);
if (err_handler.ErrCode()) {
err_handler = zxing::FormatErrorHandler("decode");
return Ref<DecoderResult>();
}
modeName = mode->getName();
} else if (mode == &Mode::ALPHANUMERIC) {
decodeAlphanumericSegment(bits_, result, count, fc1InEffect, err_handler);
if (err_handler.ErrCode()) Ref<DecoderResult>();
modeName = mode->getName();
} else if (mode == &Mode::BYTE) {
decodeByteSegment(bits_, result, count, currentCharacterSetECI,
byteSegments, err_handler);
if (err_handler.ErrCode()) {
err_handler = zxing::FormatErrorHandler("decode");
return Ref<DecoderResult>();
}
modeName = mode->getName();
// outputCharset = getResultCharset();
} else if (mode == &Mode::KANJI) {
// int countKanji =
// bits.readBits(mode->getCharacterCountBits(version));
// cout<<"countKanji: "<<countKanji<<endl;
// decodeKanjiSegment(bits_, result, countKanji);
decodeKanjiSegment(bits_, result, count, err_handler);
if (err_handler.ErrCode()) Ref<DecoderResult>();
modeName = mode->getName();
} else {
err_handler = zxing::FormatErrorHandler("decode");
return Ref<DecoderResult>();
}
}
}
}
} while (mode != &Mode::TERMINATOR);
return Ref<DecoderResult>(new DecoderResult(bytes, Ref<String>(new String(result)),
byteSegments, (string)ecLevel,
(string)outputCharset, iVersion, modeName));
}

View File

@@ -0,0 +1,66 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
//
// Modified from ZXing. Copyright ZXing authors.
// Licensed under the Apache License, Version 2.0 (the "License").
#ifndef __ZXING_QRCODE_DECODER_DECODEDBITSTREAMPARSER_HPP__
#define __ZXING_QRCODE_DECODER_DECODEDBITSTREAMPARSER_HPP__
#include "../../common/array.hpp"
#include "../../common/bitsource.hpp"
#include "../../common/characterseteci.hpp"
#include "../../common/counted.hpp"
#include "../../common/decoder_result.hpp"
#include "../../decodehints.hpp"
#include "../../errorhandler.hpp"
#include "mode.hpp"
namespace zxing {
namespace qrcode {
class DecodedBitStreamParser {
public:
DecodedBitStreamParser() : outputCharset("UTF-8") {}
private:
static char const ALPHANUMERIC_CHARS[];
string outputCharset;
// string outputCharset;
char toAlphaNumericChar(size_t value, ErrorHandler& err_handler);
void decodeHanziSegment(Ref<BitSource> bits, std::string& result, int count,
ErrorHandler& err_handler);
void decodeKanjiSegment(Ref<BitSource> bits, std::string& result, int count,
ErrorHandler& err_handler);
void decodeByteSegment(Ref<BitSource> bits, std::string& result, int count);
void decodeByteSegment(Ref<BitSource> bits_, std::string& result, int count,
zxing::common::CharacterSetECI* currentCharacterSetECI,
ArrayRef<ArrayRef<char> >& byteSegments, ErrorHandler& err_handler);
void decodeAlphanumericSegment(Ref<BitSource> bits, std::string& result, int count,
bool fc1InEffect, ErrorHandler& err_handler);
void decodeNumericSegment(Ref<BitSource> bits, std::string& result, int count,
ErrorHandler& err_handler);
void append(std::string& ost, const char* bufIn, size_t nIn, ErrorHandler& err_handler);
void append(std::string& ost, std::string const& in, ErrorHandler& err_handler);
public:
Ref<DecoderResult> decode(ArrayRef<char> bytes, Version* version,
ErrorCorrectionLevel const& ecLevel, ErrorHandler& err_handler,
int iVersion = -1);
// string getResultCharset();
};
} // namespace qrcode
} // namespace zxing
#endif // __ZXING_QRCODE_DECODER_DECODEDBITSTREAMPARSER_HPP__

View File

@@ -0,0 +1,223 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
//
// Modified from ZXing. Copyright ZXing authors.
// Licensed under the Apache License, Version 2.0 (the "License").
#include "../../../precomp.hpp"
#include "decoder.hpp"
#include "../error_correction_level.hpp"
#include "../version.hpp"
#include "datablock.hpp"
#include "decoded_bit_stream_parser.hpp"
#include "qrcode_decoder_metadata.hpp"
using zxing::DecoderResult;
using zxing::Ref;
using zxing::qrcode::Decoder;
// VC++
// The main class which implements QR Code decoding -- as opposed to locating
// and extracting the QR Code from an image.
using zxing::ArrayRef;
using zxing::BitMatrix;
using zxing::DetectorResult;
using zxing::ErrorHandler;
Decoder::Decoder() : rsDecoder_(Ref<GenericGF>(GF_QR_CODE_FIELD_256)) {
possibleVersion_ = 0;
possibleFix_ = 0;
decoderState_ = NOTSTART;
}
// Convenience method that can decode a QR Code represented as a 2D array of
// booleans. "true" is taken to mean a black module.
Ref<DecoderResult> Decoder::decode(Ref<BitMatrix> bits, ErrorHandler &err_handler) {
string errMsg = "";
// Used for mirrored qrcode
int width = bits->getWidth();
int height = bits->getHeight();
Ref<BitMatrix> bits2(new BitMatrix(width, height, bits->getPtr(), err_handler));
if (err_handler.ErrCode()) return Ref<DecoderResult>();
Ref<DecoderResult> rst = decode(bits, false, err_handler);
if (err_handler.ErrCode() || rst == NULL) {
errMsg = err_handler.ErrMsg();
} else {
return rst;
}
err_handler.Reset();
Ref<DecoderResult> result = decode(bits2, true, err_handler);
if (err_handler.ErrCode()) {
return Ref<DecoderResult>();
} else {
// Success! Notify the caller that the code was mirrored.
result->setOther(Ref<QRCodeDecoderMetaData>(new QRCodeDecoderMetaData(true)));
return result;
}
};
Ref<DecoderResult> Decoder::decode(Ref<BitMatrix> bits, bool isMirror, ErrorHandler &err_handler) {
// Ref<DecoderResult> Decoder::decode(BitMatrixParser& parser) {
// Construct a parser and read version, error-correction level
BitMatrixParser parser(bits, err_handler);
if (err_handler.ErrCode()) return Ref<DecoderResult>();
if (isMirror == true) {
// Revert the bit matrix
parser.remask();
// Will be attempting a mirrored reading of the version and format info.
parser.setMirror(true);
// Preemptively read the version.
parser.readVersion(err_handler);
if (err_handler.ErrCode()) {
err_handler = zxing::ReaderErrorHandler("Decoder::decode mirror & no mirror");
return Ref<DecoderResult>();
}
// Preemptively read the format information.
parser.readFormatInformation(err_handler);
if (err_handler.ErrCode()) return Ref<DecoderResult>();
/*
* Since we're here, this means we have successfully detected some kind
* of version and format information when mirrored. This is a good sign,
* that the QR code may be mirrored, and we should try once more with a
* mirrored content.
*/
// Prepare for a mirrored reading.
parser.mirror();
}
decoderState_ = START;
possibleFix_ = 0;
Version *version = parser.readVersion(err_handler);
if (err_handler.ErrCode() || version == NULL) {
err_handler = ReaderErrorHandler("Decoder::decode mirror & no mirror");
return Ref<DecoderResult>();
}
decoderState_ = READVERSION;
float fixedPatternScore = estimateFixedPattern(bits, version, err_handler);
if (err_handler.ErrCode()) return Ref<DecoderResult>();
Ref<FormatInformation> formatInfo = parser.readFormatInformation(err_handler);
if (err_handler.ErrCode()) return Ref<DecoderResult>();
ErrorCorrectionLevel &ecLevel = formatInfo->getErrorCorrectionLevel();
decoderState_ = READERRORCORRECTIONLEVEL;
// Read codewords
ArrayRef<char> codewords(parser.readCodewords(err_handler));
if (err_handler.ErrCode()) {
err_handler = zxing::ReaderErrorHandler("Decoder::decode mirror & no mirror");
return Ref<DecoderResult>();
}
decoderState_ = READCODEWORDSORRECTIONLEVEL;
possibleFix_ = fixedPatternScore;
// Separate into data blocks
std::vector<Ref<DataBlock> > dataBlocks(
DataBlock::getDataBlocks(codewords, version, ecLevel, err_handler));
if (err_handler.ErrCode()) return Ref<DecoderResult>();
// Count total number of data bytes
int totalBytes = 0;
for (size_t i = 0; i < dataBlocks.size(); i++) {
totalBytes += dataBlocks[i]->getNumDataCodewords();
}
ArrayRef<char> resultBytes(totalBytes);
int resultOffset = 0;
// Error-correct and copy data blocks together into a stream of bytes
for (size_t j = 0; j < dataBlocks.size(); j++) {
err_handler.Reset();
Ref<DataBlock> dataBlock(dataBlocks[j]);
ArrayRef<char> codewordBytes = dataBlock->getCodewords();
int numDataCodewords = dataBlock->getNumDataCodewords();
correctErrors(codewordBytes, numDataCodewords, err_handler);
if (err_handler.ErrCode()) return Ref<DecoderResult>();
for (int i = 0; i < numDataCodewords; i++) {
resultBytes[resultOffset++] = codewordBytes[i];
}
}
decoderState_ = FINISH;
// return DecodedBitStreamParser::decode(resultBytes,
DecodedBitStreamParser dbs_parser;
Ref<DecoderResult> rst =
dbs_parser.decode(resultBytes, version, ecLevel, err_handler, version->getVersionNumber());
if (err_handler.ErrCode()) return Ref<DecoderResult>();
return rst;
}
// Given data and error-correction codewords received, possibly corrupted by
// errors, attempts to correct the errors in-place using Reed-Solomon error
// correction.</p> codewordBytes: data and error correction codewords
// numDataCodewords: number of codewords that are data bytes
void Decoder::correctErrors(ArrayRef<char> codewordBytes, int numDataCodewords,
ErrorHandler &err_handler) {
// First read into an arrya of ints
int numCodewords = codewordBytes->size();
ArrayRef<int> codewordInts(numCodewords);
for (int i = 0; i < numCodewords; i++) {
codewordInts[i] = codewordBytes[i] & 0xff;
}
int numECCodewords = numCodewords - numDataCodewords;
bool correctErrorsFinishished = false;
rsDecoder_.decode(codewordInts, numECCodewords, err_handler);
if (err_handler.ErrCode()) {
return;
}
correctErrorsFinishished = true;
// Copy back into array of bytes -- only need to worry about the bytes that
// were data We don't care about errors in the error-correction codewords
if (correctErrorsFinishished) {
for (int i = 0; i < numDataCodewords; i++) {
codewordBytes[i] = (char)codewordInts[i];
}
}
}
unsigned int Decoder::getPossibleVersion() { return possibleVersion_; }
float Decoder::estimateFixedPattern(Ref<BitMatrix> bits, zxing::qrcode::Version *version,
ErrorHandler &err_handler) {
Ref<BitMatrix> fixedPatternValue = version->buildFixedPatternValue(err_handler);
if (err_handler.ErrCode()) {
err_handler = zxing::ReaderErrorHandler("Decoder::decode mirror & no mirror");
return -1.0;
}
Ref<BitMatrix> fixedPatternTemplate = version->buildFixedPatternTemplate(err_handler);
if (err_handler.ErrCode()) {
err_handler = zxing::ReaderErrorHandler("Decoder::decode mirror & no mirror");
return -1.0;
}
int iSum = 0;
int iCount = 0;
for (int i = 0; i < bits->getHeight(); ++i) {
for (int j = 0; j < bits->getWidth(); ++j) {
if (fixedPatternTemplate->get(i, j)) {
iSum++;
if (bits->get(i, j) == fixedPatternValue->get(i, j)) iCount++;
}
}
}
float possbielFix = 2.0 * iCount / iSum - 1;
return possbielFix > 0 ? possbielFix : 0;
}

View File

@@ -0,0 +1,65 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
//
// Modified from ZXing. Copyright ZXing authors.
// Licensed under the Apache License, Version 2.0 (the "License").
#ifndef __ZXING_QRCODE_DECODER_DECODER_HPP__
#define __ZXING_QRCODE_DECODER_DECODER_HPP__
#include "../../common/array.hpp"
#include "../../common/bitmatrix.hpp"
#include "../../common/counted.hpp"
#include "../../common/decoder_result.hpp"
#include "../../common/detector_result.hpp"
#include "../../common/reedsolomon/reed_solomon_decoder.hpp"
#include "../../errorhandler.hpp"
#include "../version.hpp"
#include "bitmatrixparser.hpp"
namespace zxing {
namespace qrcode {
class Decoder {
public:
enum DecoderState {
NOTSTART = 19,
START = 20,
READVERSION = 21,
READERRORCORRECTIONLEVEL = 22,
READCODEWORDSORRECTIONLEVEL = 23,
FINISH = 24
};
private:
DecoderState decoderState_;
float possibleFix_;
ReedSolomonDecoder rsDecoder_;
void correctErrors(ArrayRef<char> bytes, int numDataCodewords, ErrorHandler& err_handler);
public:
Decoder();
Ref<DecoderResult> decode(Ref<BitMatrix> bits, ErrorHandler& err_handler);
private:
Ref<DecoderResult> decode(Ref<BitMatrix> bits, bool isMirror, ErrorHandler& err_handler);
float estimateFixedPattern(Ref<BitMatrix> bits, Version* version, ErrorHandler& err_handler);
private:
unsigned int possibleVersion_;
public:
unsigned int getPossibleVersion();
DecoderState getState() { return decoderState_; }
float getPossibleFix() { return possibleFix_; }
};
} // namespace qrcode
} // namespace zxing
#endif // __ZXING_QRCODE_DECODER_DECODER_HPP__

View File

@@ -0,0 +1,91 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
//
// Modified from ZXing. Copyright ZXing authors.
// Licensed under the Apache License, Version 2.0 (the "License").
#include "../../../precomp.hpp"
#include "mode.hpp"
#include "../../common/counted.hpp"
#include "../../zxing.hpp"
#include "../version.hpp"
#include <sstream>
using std::ostringstream;
using zxing::qrcode::Mode;
// VC++
using zxing::ErrorHandler;
using zxing::qrcode::Version;
Mode Mode::TERMINATOR(0, 0, 0, 0x00, "TERMINATOR");
Mode Mode::NUMERIC(10, 12, 14, 0x01, "NUMERIC");
Mode Mode::ALPHANUMERIC(9, 11, 13, 0x02, "ALPHANUMERIC");
Mode Mode::STRUCTURED_APPEND(0, 0, 0, 0x03, "STRUCTURED_APPEND");
Mode Mode::BYTE(8, 16, 16, 0x04, "BYTE");
Mode Mode::ECI(0, 0, 0, 0x07, "ECI");
Mode Mode::KANJI(8, 10, 12, 0x08, "KANJI");
Mode Mode::FNC1_FIRST_POSITION(0, 0, 0, 0x05, "FNC1_FIRST_POSITION");
Mode Mode::FNC1_SECOND_POSITION(0, 0, 0, 0x09, "FNC1_SECOND_POSITION");
Mode Mode::HANZI(8, 10, 12, 0x0D, "HANZI");
// Mode::Mode(int cbv0_9, int cbv10_26, int cbv27, int /* bits */, char const*
// name) :
Mode::Mode(int cbv0_9, int cbv10_26, int cbv27, int bits, char const* name)
: characterCountBitsForVersions0To9_(cbv0_9),
characterCountBitsForVersions10To26_(cbv10_26),
// characterCountBitsForVersions27AndHigher_(cbv27), name_(name) {
characterCountBitsForVersions27AndHigher_(cbv27),
bits_(bits),
name_(name) {}
Mode& Mode::forBits(int bits, ErrorHandler& err_handler) {
switch (bits) {
case 0x0:
return TERMINATOR;
case 0x1:
return NUMERIC;
case 0x2:
return ALPHANUMERIC;
case 0x3:
return STRUCTURED_APPEND;
case 0x4:
return BYTE;
case 0x5:
return FNC1_FIRST_POSITION;
case 0x7:
return ECI;
case 0x8:
return KANJI;
case 0x9:
return FNC1_SECOND_POSITION;
case 0xD:
// 0xD is defined in GBT 18284-2000, may not be supported in foreign
// country
return HANZI;
default:
ostringstream s;
s << "Illegal mode bits: " << bits;
err_handler = zxing::ReaderErrorHandler(s.str().c_str());
return TERMINATOR;
}
}
int Mode::getCharacterCountBits(Version* version) const {
int number = version->getVersionNumber();
if (number <= 9) {
return characterCountBitsForVersions0To9_;
} else if (number <= 26) {
return characterCountBitsForVersions10To26_;
} else {
return characterCountBitsForVersions27AndHigher_;
}
}
int Mode::getBits() const { return bits_; }
string zxing::qrcode::Mode::getName() const { return name_; }

View File

@@ -0,0 +1,52 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
//
// Modified from ZXing. Copyright ZXing authors.
// Licensed under the Apache License, Version 2.0 (the "License").
#ifndef __ZXING_QRCODE_DECODER_MODE_HPP__
#define __ZXING_QRCODE_DECODER_MODE_HPP__
#include "../../common/counted.hpp"
#include "../../errorhandler.hpp"
#include "../version.hpp"
namespace zxing {
namespace qrcode {
class Mode {
private:
int characterCountBitsForVersions0To9_;
int characterCountBitsForVersions10To26_;
int characterCountBitsForVersions27AndHigher_;
int bits_;
std::string name_;
Mode(int cbv0_9, int cbv10_26, int cbv27, int bits, char const* name);
public:
static Mode TERMINATOR;
static Mode NUMERIC;
static Mode ALPHANUMERIC;
static Mode STRUCTURED_APPEND;
static Mode BYTE;
static Mode ECI;
static Mode KANJI;
static Mode FNC1_FIRST_POSITION;
static Mode FNC1_SECOND_POSITION;
static Mode HANZI;
static Mode& forBits(int bits, ErrorHandler& err_handler);
// int getCharacterCountBits(Version *version);
int getCharacterCountBits(Version* version) const;
int getBits() const;
string getName() const;
};
} // namespace qrcode
} // namespace zxing
#endif // __ZXING_QRCODE_DECODER_MODE_HPP__

View File

@@ -0,0 +1,64 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
//
// Modified from ZXing. Copyright ZXing authors.
// Licensed under the Apache License, Version 2.0 (the "License").
#ifndef __ZXING_QRCODE_DECODER_QRCODEDECODERMETADATA_HPP__
#define __ZXING_QRCODE_DECODER_QRCODEDECODERMETADATA_HPP__
#include "../../common/array.hpp"
#include "../../common/counted.hpp"
#include "../../resultpoint.hpp"
// VC++
// The main class which implements QR Code decoding -- as opposed to locating
// and extracting the QR Code from an image.
namespace zxing {
namespace qrcode {
/**
* Meta-data container for QR Code decoding. Instances of this class may be used
* to convey information back to the decoding caller. Callers are expected to
* process this.
*
* @see com.google.zxing.common.DecoderResult#getOther()
*/
class QRCodeDecoderMetaData : public Counted {
private:
bool mirrored_;
public:
explicit QRCodeDecoderMetaData(bool mirrored) : mirrored_(mirrored) {}
public:
/**
* @return true if the QR Code was mirrored.
*/
bool isMirrored() { return mirrored_; };
/**
* Apply the result points' order correction due to mirroring.
*
* @param points Array of points to apply mirror correction to.
*/
void applyMirroredCorrection(ArrayRef<Ref<ResultPoint> >& points) {
if (!mirrored_ || points->size() < 3) {
return;
}
Ref<ResultPoint> bottomLeft = points[0];
points[0] = points[2];
points[2] = bottomLeft;
// No need to 'fix' top-left and alignment pattern.
};
};
} // namespace qrcode
} // namespace zxing
#endif // __ZXING_QRCODE_DECODER_QRCODEDECODERMETADATA_HPP__

View File

@@ -0,0 +1,42 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
//
// Modified from ZXing. Copyright ZXing authors.
// Licensed under the Apache License, Version 2.0 (the "License").
#include "../../../precomp.hpp"
#include "alignment_pattern.hpp"
using zxing::Ref;
using zxing::qrcode::AlignmentPattern;
namespace zxing {
namespace qrcode {
AlignmentPattern::AlignmentPattern(float posX, float posY, float estimatedModuleSize)
: ResultPoint(posX, posY), estimatedModuleSize_(estimatedModuleSize) {}
// Determines if this alignment pattern "about equals" an alignment pattern at
// the stated position and size -- meaning, it is at nearly the same center with
// nearly the same size.
bool AlignmentPattern::aboutEquals(float moduleSize, float i, float j) const {
if (abs(i - getY()) <= moduleSize && abs(j - getX()) <= moduleSize) {
float moduleSizeDiff = abs(moduleSize - estimatedModuleSize_);
return moduleSizeDiff <= 1.0f || moduleSizeDiff <= estimatedModuleSize_;
}
return false;
}
// Combines this object's current estimate of a finder pattern position and
// module size with a new estimate. It returns a new {@code FinderPattern}
// containing an average of the two.
Ref<AlignmentPattern> AlignmentPattern::combineEstimate(float i, float j,
float newModuleSize) const {
float combinedX = (getX() + j) / 2.0f;
float combinedY = (getY() + i) / 2.0f;
float combinedModuleSize = (estimatedModuleSize_ + newModuleSize) / 2.0f;
Ref<AlignmentPattern> result(new AlignmentPattern(combinedX, combinedY, combinedModuleSize));
return result;
}
} // namespace qrcode
} // namespace zxing

View File

@@ -0,0 +1,34 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
//
// Modified from ZXing. Copyright ZXing authors.
// Licensed under the Apache License, Version 2.0 (the "License").
#ifndef __ZXING_QRCODE_DETECTOR_ALIGNMENT_PATTERN_HPP_
#define __ZXING_QRCODE_DETECTOR_ALIGNMENT_PATTERN_HPP_
#include "../../common/bitmatrix.hpp"
#include "../../resultpoint.hpp"
namespace zxing {
namespace qrcode {
class AlignmentPattern : public ResultPoint {
private:
float estimatedModuleSize_;
public:
AlignmentPattern(float posX, float posY, float estimatedModuleSize);
bool aboutEquals(float moduleSize, float i, float j) const;
float getModuleSize() { return estimatedModuleSize_; };
Ref<AlignmentPattern> combineEstimate(float i, float j, float newModuleSize) const;
};
} // namespace qrcode
} // namespace zxing
#endif // __ZXING_QRCODE_DETECTOR_ALIGNMENT_PATTERN_HPP_

View File

@@ -0,0 +1,231 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
//
// Modified from ZXing. Copyright ZXing authors.
// Licensed under the Apache License, Version 2.0 (the "License").
#include "../../../precomp.hpp"
#include "alignment_pattern_finder.hpp"
using zxing::ErrorHandler;
using zxing::ReaderErrorHandler;
using zxing::Ref;
using zxing::qrcode::AlignmentPattern;
using zxing::qrcode::AlignmentPatternFinder;
using zxing::qrcode::FinderPattern;
// VC++
// This class attempts to find alignment patterns in a QR Code. Alignment
// patterns look like finder patterns but are smaller and appear at regular
// intervals throughout the image. At the moment this only looks for the
// bottom-right alignment pattern. This is mostly a simplified copy of {@link
// FinderPatternFinder}. It is copied, pasted and stripped down here for maximum
// performance but does unfortunately duplicat some code. This class is
// thread-safe but not reentrant. Each thread must allocate its own object.
using zxing::BitMatrix;
// Creates a finder that will look in a portion of the whole image.
AlignmentPatternFinder::AlignmentPatternFinder(Ref<BitMatrix> image, int startX, int startY,
int width, int height, float moduleSize)
: image_(image),
possibleCenters_(new vector<AlignmentPattern *>()),
startX_(startX),
startY_(startY),
width_(width),
height_(height),
moduleSize_(moduleSize) {}
AlignmentPatternFinder::AlignmentPatternFinder(Ref<BitMatrix> image, float moduleSize)
: image_(image),
moduleSize_(moduleSize) {}
// This method attempts to find the bottom-right alignment pattern in the image.
// It is a bit messy since it's pretty performance-critical and so is written to
// be fast foremost.
Ref<AlignmentPattern> AlignmentPatternFinder::find(ErrorHandler &err_handler) {
int maxJ = startX_ + width_;
int middleI = startY_ + (height_ >> 1);
// We are looking for black/white/black modules in 1:1:1 ratio;
// this tracks the number of black/white/black modules seen so far
vector<int> stateCount(3, 0);
for (int iGen = 0; iGen < height_; iGen++) {
// Search from middle outwards
int i = middleI + ((iGen & 0x01) == 0 ? ((iGen + 1) >> 1) : -((iGen + 1) >> 1));
stateCount[0] = 0;
stateCount[1] = 0;
stateCount[2] = 0;
int j = startX_;
// Burn off leading white pixels before anything else; if we start in
// the middle of a white run, it doesn't make sense to count its length,
// since we don't know if the white run continued to the left of the
// start point
while (j < maxJ && !image_->get(j, i)) {
j++;
}
int currentState = 0;
while (j < maxJ) {
if (image_->get(j, i)) {
// Black pixel
if (currentState == 1) { // Counting black pixels
stateCount[currentState]++;
} else { // Counting white pixels
if (currentState == 2) { // A winner?
if (foundPatternCross(stateCount)) { // Yes
Ref<AlignmentPattern> confirmed(handlePossibleCenter(stateCount, i, j));
if (confirmed != 0) {
return confirmed;
}
}
stateCount[0] = stateCount[2];
stateCount[1] = 1;
stateCount[2] = 0;
currentState = 1;
} else {
stateCount[++currentState]++;
}
}
} else { // White pixel
if (currentState == 1) { // Counting black pixels
currentState++;
}
stateCount[currentState]++;
}
j++;
}
if (foundPatternCross(stateCount)) {
Ref<AlignmentPattern> confirmed(handlePossibleCenter(stateCount, i, maxJ));
if (confirmed != 0) {
return confirmed;
}
}
}
// Nothing we saw was observed and confirmed twice. If we had any guess at
// all, return it.
if (possibleCenters_->size() > 0) {
Ref<AlignmentPattern> center((*possibleCenters_)[0]);
return center;
}
err_handler = ReaderErrorHandler("Could not find alignment pattern");
return Ref<AlignmentPattern>();
}
// Given a count of black/white/black pixels just seen and an end position,
// figures the location of the center of this black/white/black run.
float AlignmentPatternFinder::centerFromEnd(vector<int> &stateCount, int end) {
return (float)(end - stateCount[2]) - stateCount[1] / 2.0f;
}
bool AlignmentPatternFinder::foundPatternCross(vector<int> &stateCount) {
float maxVariance = moduleSize_ / 2.0f;
for (int i = 0; i < 3; i++) {
if (abs(moduleSize_ - stateCount[i]) >= maxVariance) {
return false;
}
}
return true;
}
// After a horizontal scan finds a potential alignment pattern, this method
// "cross-checks" by scanning down vertically through the center of the possible
// alignment pattern to see if the same proportion is detected. return vertical
// center of alignment pattern, or nan() if not found startI: row where an
// alignment pattern was detected centerJ: center of the section that appears to
// cross an alignment pattern
// maxCount: maximum reasonable number of modules that should be observed in any
// reading state,
// based on the results of the horizontal scan
float AlignmentPatternFinder::crossCheckVertical(int startI, int centerJ, int maxCount,
int originalStateCountTotal) {
// This is slightly faster than using the Ref. Efficiency is important here
BitMatrix &matrix = *image_;
int maxI = matrix.getHeight();
vector<int> stateCount(3, 0);
// Start counting up from center
int i = startI;
while (i >= 0 && matrix.get(centerJ, i) && stateCount[1] <= maxCount) {
stateCount[1]++;
i--;
}
// If already too many modules in this state or ran off the edge:
if (i < 0 || stateCount[1] > maxCount) {
return nan();
}
while (i >= 0 && !matrix.get(centerJ, i) && stateCount[0] <= maxCount) {
stateCount[0]++;
i--;
}
if (stateCount[0] > maxCount) {
return nan();
}
// Now also count down from center
i = startI + 1;
while (i < maxI && matrix.get(centerJ, i) && stateCount[1] <= maxCount) {
stateCount[1]++;
i++;
}
if (i == maxI || stateCount[1] > maxCount) {
return nan();
}
while (i < maxI && !matrix.get(centerJ, i) && stateCount[2] <= maxCount) {
stateCount[2]++;
i++;
}
if (stateCount[2] > maxCount) {
return nan();
}
int stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2];
if (5 * abs(stateCountTotal - originalStateCountTotal) >= 2 * originalStateCountTotal) {
return nan();
}
return foundPatternCross(stateCount) ? centerFromEnd(stateCount, i) : nan();
}
// This is called when a horizontal scan finds a possible alignment pattern. It
// will cross check with a vertical scan, and if successful, will see if this
// pattern had been found on a previous horizontal scan. If so, we consider it
// confirmed and conclude we have found the alignment pattern. return {@link
// AlignmentPattern} if we have found the same pattern twice, or null if not i:
// row where alignment pattern may be found j: end of possible alignment pattern
// in row
Ref<AlignmentPattern> AlignmentPatternFinder::handlePossibleCenter(vector<int> &stateCount, int i,
int j) {
int stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2];
float centerJ = centerFromEnd(stateCount, j);
float centerI = crossCheckVertical(i, (int)centerJ, 2 * stateCount[1], stateCountTotal);
if (!isnan(centerI)) {
float estimatedModuleSize = (float)(stateCount[0] + stateCount[1] + stateCount[2]) / 3.0f;
int max = possibleCenters_->size();
for (int index = 0; index < max; index++) {
Ref<AlignmentPattern> center((*possibleCenters_)[index]);
// Look for about the same center and module size:
if (center->aboutEquals(estimatedModuleSize, centerI, centerJ)) {
return center->combineEstimate(centerI, centerJ, estimatedModuleSize);
}
}
// Hadn't found this before; save it
AlignmentPattern *tmp = new AlignmentPattern(centerJ, centerI, estimatedModuleSize);
tmp->retain();
possibleCenters_->push_back(tmp);
}
Ref<AlignmentPattern> result;
return result;
}
AlignmentPatternFinder::~AlignmentPatternFinder() {
for (int i = 0; i < int(possibleCenters_->size()); i++) {
(*possibleCenters_)[i]->release();
(*possibleCenters_)[i] = 0;
}
delete possibleCenters_;
}

View File

@@ -0,0 +1,60 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
//
// Modified from ZXing. Copyright ZXing authors.
// Licensed under the Apache License, Version 2.0 (the "License").
#ifndef __ZXING_QRCODE_DETECTOR_ALIGNMENT_PATTERN_FINDER_HPP_
#define __ZXING_QRCODE_DETECTOR_ALIGNMENT_PATTERN_FINDER_HPP_
#include "../../common/bitmatrix.hpp"
#include "../../common/counted.hpp"
#include "../../errorhandler.hpp"
#include "alignment_pattern.hpp"
#include "finder_pattern.hpp"
namespace zxing {
namespace qrcode {
class AlignmentPatternFinder : public Counted {
private:
static int CENTER_QUORUM;
static int MIN_SKIP;
static int MAX_MODULES;
Ref<BitMatrix> image_;
std::vector<AlignmentPattern *> *possibleCenters_;
int startX_;
int startY_;
int width_;
int height_;
float moduleSize_;
static float centerFromEnd(std::vector<int> &stateCount, int end);
float crossCheckVertical(int startI, int centerJ, int maxCount, int originalStateCountTotal);
public:
AlignmentPatternFinder(Ref<BitMatrix> image, int startX, int startY, int width, int height,
float moduleSize);
AlignmentPatternFinder(Ref<BitMatrix> image, float moduleSize);
~AlignmentPatternFinder();
Ref<AlignmentPattern> find(ErrorHandler &err_handler);
bool foundPatternCross(std::vector<int> &stateCount);
Ref<AlignmentPattern> handlePossibleCenter(std::vector<int> &stateCount, int i, int j);
private:
AlignmentPatternFinder(const AlignmentPatternFinder &);
AlignmentPatternFinder &operator=(const AlignmentPatternFinder &);
};
} // namespace qrcode
} // namespace zxing
#endif // __ZXING_QRCODE_DETECTOR_ALIGNMENT_PATTERN_FINDER_HPP_

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,145 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Tencent is pleased to support the open source community by making WeChat QRCode available.
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
//
// Modified from ZXing. Copyright ZXing authors.
// Licensed under the Apache License, Version 2.0 (the "License").
#ifndef __ZXING_QRCODE_DETECTOR_DETECTOR_HPP_
#define __ZXING_QRCODE_DETECTOR_DETECTOR_HPP_
#include "../../common/bitmatrix.hpp"
#include "../../common/counted.hpp"
#include "../../common/detector_result.hpp"
#include "../../common/perspective_transform.hpp"
#include "../../common/unicomblock.hpp"
#include "../../errorhandler.hpp"
#include "alignment_pattern.hpp"
#include "finder_pattern.hpp"
#include "finder_pattern_info.hpp"
#include "pattern_result.hpp"
namespace zxing {
class DecodeHints;
namespace qrcode {
// Possible Detect Result
class Detector : public Counted {
public:
enum DetectorState {
START = 10,
FINDFINDERPATTERN = 11,
FINDALIGNPATTERN = 12,
};
// Fix module size error when LEFT_SPILL or RIGHT_SPILL
enum FinderPatternMode {
NORMAL = 0,
LEFT_SPILL = 1,
RIGHT_SPILL = 2,
UP_SPILL = 3,
DOWN_SPILL = 4,
};
typedef struct Rect_ {
int x;
int y;
int width;
int height;
} Rect;
private:
Ref<BitMatrix> image_;
Ref<UnicomBlock> block_;
vector<Ref<PatternResult> > possiblePatternResults_;
DetectorState detectorState_;
protected:
Ref<BitMatrix> getImage() const;
static int computeDimension(Ref<ResultPoint> topLeft, Ref<ResultPoint> topRight,
Ref<ResultPoint> bottomLeft, float moduleSizeX, float moduleSizeY);
float calculateModuleSize(Ref<ResultPoint> topLeft, Ref<ResultPoint> topRight,
Ref<ResultPoint> bottomLeft);
float calculateModuleSizeOneWay(Ref<ResultPoint> pattern, Ref<ResultPoint> otherPattern,
int patternState, int otherPatternState);
float sizeOfBlackWhiteBlackRunBothWays(int fromX, int fromY, int toX, int toY, int patternState,
bool isReverse);
float sizeOfBlackWhiteBlackRunBothWays(int fromX, int fromY, int toX, int toY);
float sizeOfBlackWhiteBlackRun(int fromX, int fromY, int toX, int toY);
Ref<AlignmentPattern> findAlignmentInRegion(float overallEstModuleSize, int estAlignmentX,
int estAlignmentY, float allowanceFactor,
ErrorHandler &err_handler);
Ref<AlignmentPattern> findAlignmentWithFitLine(Ref<ResultPoint> topLeft,
Ref<ResultPoint> topRight,
Ref<ResultPoint> bottomLeft, float moduleSize,
ErrorHandler &err_handler);
int fitLine(vector<Ref<ResultPoint> > &oldPoints, float &k, float &b, int &a);
bool checkTolerance(Ref<ResultPoint> &topLeft, Ref<ResultPoint> &topRight, Rect &topRightRect,
double modelSize, Ref<ResultPoint> &p, int flag);
void findPointsForLine(Ref<ResultPoint> &topLeft, Ref<ResultPoint> &topRight,
Ref<ResultPoint> &bottomLeft, Rect topRightRect, Rect bottomLeftRect,
vector<Ref<ResultPoint> > &topRightPoints,
vector<Ref<ResultPoint> > &bottomLeftPoints, float modelSize);
bool checkConvexQuadrilateral(Ref<ResultPoint> topLeft, Ref<ResultPoint> topRight,
Ref<ResultPoint> bottomLeft, Ref<ResultPoint> bottomRight);
public:
virtual Ref<PerspectiveTransform> createTransform(Ref<ResultPoint> topLeft,
Ref<ResultPoint> topRight,
Ref<ResultPoint> bottomLeft,
Ref<ResultPoint> alignmentPattern,
int dimension);
Ref<PerspectiveTransform> createTransform(Ref<FinderPatternInfo> finderPatternInfo,
Ref<ResultPoint> alignmentPattern, int dimension);
static Ref<BitMatrix> sampleGrid(Ref<BitMatrix> image, int dimension, Ref<PerspectiveTransform>,
ErrorHandler &err_handler);
Detector(Ref<BitMatrix> image, Ref<UnicomBlock> block);
void detect(DecodeHints const &hints, ErrorHandler &err_handler);
Ref<DetectorResult> getResultViaAlignment(int patternIdx, int alignmentIndex,
int possibleDimension, ErrorHandler &err_handler);
int getPossiblePatternCount() { return possiblePatternResults_.size(); }
int getPossibleAlignmentCount(int idx);
Ref<AlignmentPattern> getNearestAlignmentPattern(int tryFindRange, float moduleSize,
int estAlignmentX, int estAlignmentY);
bool hasSameResult(vector<Ref<AlignmentPattern> > possibleAlignmentPatterns,
Ref<AlignmentPattern> alignmentPattern);
void fixAlignmentPattern(float &alignmentX, float &alignmentY, float moduleSize);
Ref<PatternResult> processFinderPatternInfo(Ref<FinderPatternInfo> info,
ErrorHandler &err_handler);
public:
Ref<FinderPatternInfo> getFinderPatternInfo(int idx) {
return possiblePatternResults_[idx]->finderPatternInfo;
}
Ref<AlignmentPattern> getAlignmentPattern(int patternIdx, int alignmentIdx) {
return possiblePatternResults_[patternIdx]->possibleAlignmentPatterns[alignmentIdx];
}
DetectorState getState() { return detectorState_; }
unsigned int getPossibleVersion(int idx) {
return possiblePatternResults_[idx]->possibleVersion;
}
float getPossibleFix(int idx) { return possiblePatternResults_[idx]->possibleFix; }
float getPossibleModuleSize(int idx) {
return possiblePatternResults_[idx]->possibleModuleSize;
}
int getDimension(int idx) { return possiblePatternResults_[idx]->possibleDimension; }
};
} // namespace qrcode
} // namespace zxing
#endif // __ZXING_QRCODE_DETECTOR_DETECTOR_HPP_

Some files were not shown because too many files have changed in this diff Show More