mirror of
https://github.com/opencv/opencv.git
synced 2026-07-29 23:33:05 +04:00
Merge branch 4.x
This commit is contained in:
@@ -113,7 +113,8 @@ public:
|
||||
};
|
||||
|
||||
enum ECIEncodings {
|
||||
ECI_UTF8 = 26
|
||||
ECI_SHIFT_JIS = 20,
|
||||
ECI_UTF8 = 26,
|
||||
};
|
||||
|
||||
/** @brief QR code encoder parameters. */
|
||||
@@ -194,6 +195,13 @@ public:
|
||||
*/
|
||||
CV_WRAP std::string detectAndDecodeCurved(InputArray img, OutputArray points=noArray(),
|
||||
OutputArray straight_qrcode = noArray());
|
||||
|
||||
/** @brief Returns a kind of encoding for the decoded info from the latest @ref decode or @ref detectAndDecode call
|
||||
@param codeIdx an index of the previously decoded QR code.
|
||||
When @ref decode or @ref detectAndDecode is used, valid value is zero.
|
||||
For @ref decodeMulti or @ref detectAndDecodeMulti use indices corresponding to the output order.
|
||||
*/
|
||||
CV_WRAP QRCodeEncoder::ECIEncodings getEncoding(int codeIdx = 0);
|
||||
};
|
||||
|
||||
/** @brief QR code detector based on Aruco markers detection code. */
|
||||
|
||||
@@ -16,6 +16,7 @@ struct CV_EXPORTS_W_SIMPLE CharucoParameters {
|
||||
CV_WRAP CharucoParameters() {
|
||||
minMarkers = 2;
|
||||
tryRefineMarkers = false;
|
||||
checkMarkers = true;
|
||||
}
|
||||
/// cameraMatrix optional 3x3 floating-point camera matrix
|
||||
CV_PROP_RW Mat cameraMatrix;
|
||||
@@ -28,6 +29,9 @@ struct CV_EXPORTS_W_SIMPLE CharucoParameters {
|
||||
|
||||
/// try to use refine board, default false
|
||||
CV_PROP_RW bool tryRefineMarkers;
|
||||
|
||||
/// run check to verify that markers belong to the same board, default true
|
||||
CV_PROP_RW bool checkMarkers;
|
||||
};
|
||||
|
||||
class CV_EXPORTS_W CharucoDetector : public Algorithm {
|
||||
|
||||
@@ -73,6 +73,17 @@ public:
|
||||
*/
|
||||
CV_WRAP bool detectAndDecodeMulti(InputArray img, CV_OUT std::vector<std::string>& decoded_info, OutputArray points = noArray(),
|
||||
OutputArrayOfArrays straight_code = noArray()) const;
|
||||
|
||||
#ifdef OPENCV_BINDINGS_PARSER
|
||||
CV_WRAP_AS(detectAndDecodeBytes) NativeByteArray detectAndDecode(InputArray img, OutputArray points = noArray(),
|
||||
OutputArray straight_code = noArray()) const;
|
||||
CV_WRAP_AS(decodeBytes) NativeByteArray decode(InputArray img, InputArray points, OutputArray straight_code = noArray()) const;
|
||||
CV_WRAP_AS(decodeBytesMulti) bool decodeMulti(InputArray img, InputArray points, CV_OUT std::vector<NativeByteArray>& decoded_info,
|
||||
OutputArrayOfArrays straight_code = noArray()) const;
|
||||
CV_WRAP_AS(detectAndDecodeBytesMulti) bool detectAndDecodeMulti(InputArray img, CV_OUT std::vector<NativeByteArray>& decoded_info, OutputArray points = noArray(),
|
||||
OutputArrayOfArrays straight_code = noArray()) const;
|
||||
#endif
|
||||
|
||||
struct Impl;
|
||||
protected:
|
||||
Ptr<Impl> p;
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
misc/java/src/cpp/objdetect_converters.hpp
|
||||
@@ -0,0 +1,69 @@
|
||||
{
|
||||
"ManualFuncs" : {
|
||||
"QRCodeEncoder" : {
|
||||
"QRCodeEncoder" : {
|
||||
"j_code" : [
|
||||
"\n",
|
||||
"/** Generates QR code from input string.",
|
||||
"@param encoded_info Input bytes to encode.",
|
||||
"@param qrcode Generated QR code.",
|
||||
"*/",
|
||||
"public void encode(byte[] encoded_info, Mat qrcode) {",
|
||||
" encode_1(nativeObj, encoded_info, qrcode.nativeObj);",
|
||||
"}",
|
||||
"\n"
|
||||
],
|
||||
"jn_code": [
|
||||
"\n",
|
||||
"private static native void encode_1(long nativeObj, byte[] encoded_info, long qrcode_nativeObj);",
|
||||
"\n"
|
||||
],
|
||||
"cpp_code": [
|
||||
"//",
|
||||
"// void cv::QRCodeEncoder::encode(String encoded_info, Mat& qrcode)",
|
||||
"//",
|
||||
"\n",
|
||||
"JNIEXPORT void JNICALL Java_org_opencv_objdetect_QRCodeEncoder_encode_11 (JNIEnv*, jclass, jlong, jbyteArray, jlong);",
|
||||
"\n",
|
||||
"JNIEXPORT void JNICALL Java_org_opencv_objdetect_QRCodeEncoder_encode_11",
|
||||
"(JNIEnv* env, jclass , jlong self, jbyteArray encoded_info, jlong qrcode_nativeObj)",
|
||||
"{",
|
||||
"",
|
||||
" static const char method_name[] = \"objdetect::encode_11()\";",
|
||||
" try {",
|
||||
" LOGD(\"%s\", method_name);",
|
||||
" Ptr<cv::QRCodeEncoder>* me = (Ptr<cv::QRCodeEncoder>*) self; //TODO: check for NULL",
|
||||
" const char* n_encoded_info = reinterpret_cast<char*>(env->GetByteArrayElements(encoded_info, NULL));",
|
||||
" const jsize n_encoded_info_size = env->GetArrayLength(encoded_info);",
|
||||
" Mat& qrcode = *((Mat*)qrcode_nativeObj);",
|
||||
" (*me)->encode( std::string(n_encoded_info, n_encoded_info_size), qrcode );",
|
||||
" } catch(const std::exception &e) {",
|
||||
" throwJavaException(env, &e, method_name);",
|
||||
" } catch (...) {",
|
||||
" throwJavaException(env, 0, method_name);",
|
||||
" }",
|
||||
"}",
|
||||
"\n"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"type_dict": {
|
||||
"NativeByteArray": {
|
||||
"j_type" : "byte[]",
|
||||
"jn_type": "byte[]",
|
||||
"jni_type": "jbyteArray",
|
||||
"jni_name": "n_%(n)s",
|
||||
"jni_var": "jbyteArray n_%(n)s = env->NewByteArray(static_cast<jsize>(%(n)s.size())); env->SetByteArrayRegion(n_%(n)s, 0, static_cast<jsize>(%(n)s.size()), reinterpret_cast<const jbyte*>(%(n)s.c_str()));",
|
||||
"cast_from": "std::string"
|
||||
},
|
||||
"vector_NativeByteArray": {
|
||||
"j_type": "List<byte[]>",
|
||||
"jn_type": "List<byte[]>",
|
||||
"jni_type": "jobject",
|
||||
"jni_var": "std::vector< std::string > %(n)s",
|
||||
"suffix": "Ljava_util_List",
|
||||
"v_type": "vector_NativeByteArray"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
#include "objdetect_converters.hpp"
|
||||
|
||||
#define LOG_TAG "org.opencv.objdetect"
|
||||
|
||||
void Copy_vector_NativeByteArray_to_List(JNIEnv* env, std::vector<std::string>& vs, jobject list)
|
||||
{
|
||||
static jclass juArrayList = ARRAYLIST(env);
|
||||
jmethodID m_clear = LIST_CLEAR(env, juArrayList);
|
||||
jmethodID m_add = LIST_ADD(env, juArrayList);
|
||||
|
||||
env->CallVoidMethod(list, m_clear);
|
||||
for (std::vector<std::string>::iterator it = vs.begin(); it != vs.end(); ++it)
|
||||
{
|
||||
jsize sz = static_cast<jsize>((*it).size());
|
||||
jbyteArray element = env->NewByteArray(sz);
|
||||
env->SetByteArrayRegion(element, 0, sz, reinterpret_cast<const jbyte*>((*it).c_str()));
|
||||
env->CallBooleanMethod(list, m_add, element);
|
||||
env->DeleteLocalRef(element);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
// 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
|
||||
|
||||
#ifndef OBJDETECT_CONVERTERS_HPP
|
||||
#define OBJDETECT_CONVERTERS_HPP
|
||||
|
||||
#include <jni.h>
|
||||
#include "opencv_java.hpp"
|
||||
#include "opencv2/core.hpp"
|
||||
|
||||
void Copy_vector_NativeByteArray_to_List(JNIEnv* env, std::vector<std::string>& vs, jobject list);
|
||||
|
||||
#endif /* OBJDETECT_CONVERTERS_HPP */
|
||||
@@ -2,13 +2,19 @@ package org.opencv.test.objdetect;
|
||||
|
||||
import java.util.List;
|
||||
import org.opencv.core.Mat;
|
||||
import org.opencv.core.Size;
|
||||
import org.opencv.objdetect.QRCodeDetector;
|
||||
import org.opencv.objdetect.QRCodeEncoder;
|
||||
import org.opencv.objdetect.QRCodeEncoder_Params;
|
||||
import org.opencv.imgcodecs.Imgcodecs;
|
||||
import org.opencv.imgproc.Imgproc;
|
||||
import org.opencv.test.OpenCVTestCase;
|
||||
import java.util.Arrays;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
public class QRCodeDetectorTest extends OpenCVTestCase {
|
||||
|
||||
@@ -50,4 +56,26 @@ public class QRCodeDetectorTest extends OpenCVTestCase {
|
||||
List < String > expectedResults = Arrays.asList("SKIP", "EXTRA", "TWO STEPS FORWARD", "STEP BACK", "QUESTION", "STEP FORWARD");
|
||||
assertEquals(new HashSet<String>(output), new HashSet<String>(expectedResults));
|
||||
}
|
||||
|
||||
public void testKanji() {
|
||||
byte[] inp = new byte[]{(byte)0x82, (byte)0xb1, (byte)0x82, (byte)0xf1, (byte)0x82, (byte)0xc9, (byte)0x82,
|
||||
(byte)0xbf, (byte)0x82, (byte)0xcd, (byte)0x90, (byte)0xa2, (byte)0x8a, (byte)0x45};
|
||||
QRCodeEncoder_Params params = new QRCodeEncoder_Params();
|
||||
params.set_mode(QRCodeEncoder.MODE_KANJI);
|
||||
QRCodeEncoder encoder = QRCodeEncoder.create(params);
|
||||
|
||||
Mat qrcode = new Mat();
|
||||
encoder.encode(inp, qrcode);
|
||||
Imgproc.resize(qrcode, qrcode, new Size(0, 0), 2, 2, Imgproc.INTER_NEAREST);
|
||||
|
||||
QRCodeDetector detector = new QRCodeDetector();
|
||||
byte[] output = detector.detectAndDecodeBytes(qrcode);
|
||||
assertEquals(detector.getEncoding(), QRCodeEncoder.ECI_SHIFT_JIS);
|
||||
assertArrayEquals(inp, output);
|
||||
|
||||
List < byte[] > outputs = new ArrayList< byte[] >();
|
||||
assertTrue(detector.detectAndDecodeBytesMulti(qrcode, outputs));
|
||||
assertEquals(detector.getEncoding(0), QRCodeEncoder.ECI_SHIFT_JIS);
|
||||
assertArrayEquals(inp, outputs.get(0));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,4 +8,31 @@ typedef std::vector<cv::Ptr<mcc::CChecker>> vector_Ptr_CChecker;
|
||||
typedef dnn::Net dnn_Net;
|
||||
#endif
|
||||
|
||||
class NativeByteArray
|
||||
{
|
||||
public:
|
||||
inline NativeByteArray& operator=(const std::string& from) {
|
||||
val = from;
|
||||
return *this;
|
||||
}
|
||||
std::string val;
|
||||
};
|
||||
|
||||
class vector_NativeByteArray : public std::vector<std::string> {};
|
||||
|
||||
template<>
|
||||
PyObject* pyopencv_from(const NativeByteArray& from)
|
||||
{
|
||||
return PyBytes_FromStringAndSize(from.val.c_str(), from.val.size());
|
||||
}
|
||||
|
||||
template<>
|
||||
PyObject* pyopencv_from(const vector_NativeByteArray& results)
|
||||
{
|
||||
PyObject* list = PyList_New(results.size());
|
||||
for(size_t i = 0; i < results.size(); ++i)
|
||||
PyList_SetItem(list, i, PyBytes_FromStringAndSize(results[i].c_str(), results[i].size()));
|
||||
return list;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#!/usr/bin/env python
|
||||
'''
|
||||
===============================================================================
|
||||
@@ -8,7 +9,7 @@ import os
|
||||
import numpy as np
|
||||
import cv2 as cv
|
||||
|
||||
from tests_common import NewOpenCVTests
|
||||
from tests_common import NewOpenCVTests, unittest
|
||||
|
||||
class qrcode_detector_test(NewOpenCVTests):
|
||||
|
||||
@@ -50,3 +51,36 @@ class qrcode_detector_test(NewOpenCVTests):
|
||||
self.assertTrue("STEP BACK" in decoded_data)
|
||||
self.assertTrue("QUESTION" in decoded_data)
|
||||
self.assertEqual(points.shape, (6, 4, 2))
|
||||
|
||||
def test_decode_non_ascii(self):
|
||||
import sys
|
||||
if sys.version_info[0] < 3:
|
||||
raise unittest.SkipTest('Python 2.x is not supported')
|
||||
|
||||
img = cv.imread(os.path.join(self.extraTestDataPath, 'cv/qrcode/umlaut.png'))
|
||||
self.assertFalse(img is None)
|
||||
detector = cv.QRCodeDetector()
|
||||
decoded_data, _, _ = detector.detectAndDecode(img)
|
||||
self.assertTrue(isinstance(decoded_data, str))
|
||||
self.assertTrue("Müllheimstrasse" in decoded_data)
|
||||
|
||||
def test_kanji(self):
|
||||
inp = "こんにちは世界"
|
||||
inp_bytes = inp.encode("shift-jis")
|
||||
|
||||
params = cv.QRCodeEncoder_Params()
|
||||
params.mode = cv.QRCodeEncoder_MODE_KANJI
|
||||
encoder = cv.QRCodeEncoder_create(params)
|
||||
qrcode = encoder.encode(inp_bytes)
|
||||
qrcode = cv.resize(qrcode, (0, 0), fx=2, fy=2, interpolation=cv.INTER_NEAREST)
|
||||
|
||||
detector = cv.QRCodeDetector()
|
||||
data, _, _ = detector.detectAndDecodeBytes(qrcode)
|
||||
self.assertEqual(data, inp_bytes)
|
||||
self.assertEqual(detector.getEncoding(), cv.QRCodeEncoder_ECI_SHIFT_JIS)
|
||||
self.assertEqual(data.decode("shift-jis"), inp)
|
||||
|
||||
_, data, _, _ = detector.detectAndDecodeBytesMulti(qrcode)
|
||||
self.assertEqual(data[0], inp_bytes)
|
||||
self.assertEqual(detector.getEncoding(0), cv.QRCodeEncoder_ECI_SHIFT_JIS)
|
||||
self.assertEqual(data[0].decode("shift-jis"), inp)
|
||||
|
||||
@@ -335,7 +335,7 @@ struct CharucoDetector::CharucoDetectorImpl {
|
||||
InputOutputArrayOfArrays _markerCorners = markerCorners.needed() ? markerCorners : tmpMarkerCorners;
|
||||
InputOutputArray _markerIds = markerIds.needed() ? markerIds : tmpMarkerIds;
|
||||
detectBoard(image, charucoCorners, charucoIds, _markerCorners, _markerIds);
|
||||
if (checkBoard(_markerCorners, _markerIds, charucoCorners, charucoIds) == false) {
|
||||
if (charucoParameters.checkMarkers && checkBoard(_markerCorners, _markerIds, charucoCorners, charucoIds) == false) {
|
||||
CV_LOG_DEBUG(NULL, "ChArUco board is built incorrectly");
|
||||
charucoCorners.release();
|
||||
charucoIds.release();
|
||||
|
||||
@@ -960,6 +960,7 @@ public:
|
||||
double epsX, epsY;
|
||||
mutable vector<vector<Point2f>> alignmentMarkers;
|
||||
mutable vector<Point2f> updateQrCorners;
|
||||
mutable vector<QRCodeEncoder::ECIEncodings> encodings;
|
||||
bool useAlignmentMarkers = true;
|
||||
|
||||
bool detect(InputArray in, OutputArray points) const override;
|
||||
@@ -975,6 +976,8 @@ public:
|
||||
String decodeCurved(InputArray in, InputArray points, OutputArray straight_qrcode);
|
||||
|
||||
std::string detectAndDecodeCurved(InputArray in, OutputArray points, OutputArray straight_qrcode);
|
||||
|
||||
QRCodeEncoder::ECIEncodings getEncoding(int codeIdx);
|
||||
};
|
||||
|
||||
QRCodeDetector::QRCodeDetector() {
|
||||
@@ -991,6 +994,13 @@ QRCodeDetector& QRCodeDetector::setEpsY(double epsY) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
QRCodeEncoder::ECIEncodings QRCodeDetector::getEncoding(int codeIdx) {
|
||||
auto& encodings = std::dynamic_pointer_cast<ImplContour>(p)->encodings;
|
||||
CV_Assert(codeIdx >= 0);
|
||||
CV_Assert(codeIdx < static_cast<int>(encodings.size()));
|
||||
return encodings[codeIdx];
|
||||
}
|
||||
|
||||
bool ImplContour::detect(InputArray in, OutputArray points) const
|
||||
{
|
||||
Mat inarr;
|
||||
@@ -1032,6 +1042,8 @@ public:
|
||||
uint8_t total_num = 1;
|
||||
} structure_info;
|
||||
|
||||
QRCodeEncoder::ECIEncodings eci;
|
||||
|
||||
protected:
|
||||
double getNumModules();
|
||||
Mat getHomography() {
|
||||
@@ -2799,7 +2811,6 @@ static std::string encodeUTF8_bytesarray(const uint8_t* str, const size_t size)
|
||||
|
||||
bool QRDecode::decodingProcess()
|
||||
{
|
||||
QRCodeEncoder::ECIEncodings eci;
|
||||
const uint8_t* payload;
|
||||
size_t payload_len;
|
||||
|
||||
@@ -2858,7 +2869,7 @@ bool QRDecode::decodingProcess()
|
||||
return true;
|
||||
case QRCodeEncoder::EncodeMode::MODE_KANJI:
|
||||
// FIXIT BUG: we must return UTF-8 compatible string
|
||||
CV_LOG_WARNING(NULL, "QR: Kanji is not supported properly");
|
||||
eci = QRCodeEncoder::ECIEncodings::ECI_SHIFT_JIS;
|
||||
result_info.assign((const char*)payload, payload_len);
|
||||
return true;
|
||||
case QRCodeEncoder::EncodeMode::MODE_ECI:
|
||||
@@ -2929,6 +2940,7 @@ std::string ImplContour::decode(InputArray in, InputArray points, OutputArray st
|
||||
alignmentMarkers = {qrdec.alignment_coords};
|
||||
updateQrCorners = qrdec.getOriginalPoints();
|
||||
}
|
||||
encodings.resize(1, qrdec.eci);
|
||||
return ok ? decoded_info : std::string();
|
||||
}
|
||||
|
||||
@@ -2962,6 +2974,7 @@ String ImplContour::decodeCurved(InputArray in, InputArray points, OutputArray s
|
||||
{
|
||||
qrdec.getStraightBarcode().convertTo(straight_qrcode, CV_8UC1);
|
||||
}
|
||||
encodings.resize(1, qrdec.eci);
|
||||
|
||||
return ok ? decoded_info : std::string();
|
||||
}
|
||||
@@ -4074,20 +4087,22 @@ bool ImplContour::decodeMulti(
|
||||
straight_qrcode.assign(tmp_straight_qrcodes);
|
||||
}
|
||||
|
||||
decoded_info.clear();
|
||||
decoded_info.resize(info.size());
|
||||
encodings.resize(info.size());
|
||||
for (size_t i = 0; i < info.size(); i++)
|
||||
{
|
||||
auto& decoder = qrdec[i];
|
||||
encodings[i] = decoder.eci;
|
||||
if (!decoder.isStructured())
|
||||
{
|
||||
decoded_info.push_back(info[i]);
|
||||
decoded_info[i] = info[i];
|
||||
continue;
|
||||
}
|
||||
|
||||
// Store final message corresponding to 0-th code in a sequence.
|
||||
if (decoder.structure_info.sequence_num != 0)
|
||||
{
|
||||
decoded_info.push_back("");
|
||||
decoded_info[i] = "";
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -4108,7 +4123,7 @@ bool ImplContour::decodeMulti(
|
||||
break;
|
||||
}
|
||||
}
|
||||
decoded_info.push_back(decoded);
|
||||
decoded_info[i] = decoded;
|
||||
}
|
||||
|
||||
alignmentMarkers.resize(src_points.size());
|
||||
|
||||
@@ -343,9 +343,11 @@ TEST(Objdetect_QRCode_Encode_Kanji, regression)
|
||||
}
|
||||
|
||||
Mat straight_barcode;
|
||||
std::string decoded_info = QRCodeDetector().decode(resized_src, corners, straight_barcode);
|
||||
QRCodeDetector detector;
|
||||
std::string decoded_info = detector.decode(resized_src, corners, straight_barcode);
|
||||
EXPECT_FALSE(decoded_info.empty()) << "The generated QRcode cannot be decoded.";
|
||||
EXPECT_EQ(input_info, decoded_info);
|
||||
EXPECT_EQ(detector.getEncoding(), QRCodeEncoder::ECIEncodings::ECI_SHIFT_JIS);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user