Java bindings for QR code with bytes encoding/decoding

This commit is contained in:
Dmitry Kurtaev 2025-06-02 11:23:48 +03:00
parent 8801c6f75e
commit cb8d6c59a7
4 changed files with 119 additions and 4 deletions

View File

@ -810,12 +810,12 @@ public:
CV_WRAP std::string detectAndDecodeCurved(InputArray img, OutputArray points=noArray(),
OutputArray straight_qrcode = noArray());
/** @brief Returns a kid of encoding for the decoded info from the latest QR codes
/** @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(size_t codeIdx = 0);
CV_WRAP QRCodeEncoder::ECIEncodings getEncoding(int codeIdx = 0);
};
class CV_EXPORTS_W_SIMPLE QRCodeDetectorAruco : public GraphicalCodeDetector {

View File

@ -0,0 +1,92 @@
{
"ManualFuncs" : {
"QRCodeEncoder" : {
"QRCodeEncoder" : {
"j_code" : [
"\n",
"/**",
" * Constructor of streaming callback object with abstract 'read' and 'seek' methods that should be implemented in Java code.<br>",
" * <b>NOTE</b>: Implemented callbacks should be called from the creation thread to avoid JNI performance degradation",
"*/",
"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));",
" Mat& qrcode = *((Mat*)qrcode_nativeObj);",
" (*me)->encode( n_encoded_info, qrcode );",
" } catch(const std::exception &e) {",
" throwJavaException(env, &e, method_name);",
" } catch (...) {",
" throwJavaException(env, 0, method_name);",
" }",
"}",
"\n"
]
}
},
"GraphicalCodeDetector" : {
"GraphicalCodeDetector" : {
"j_code" : [
"\n",
"public byte[] detectAndDecodeBytes(Mat img) {",
" return detectAndDecodeBytes_0(nativeObj, img.nativeObj);",
"}",
"\n"
],
"jn_code": [
"\n",
"private static native byte[] detectAndDecodeBytes_0(long nativeObj, long img_nativeObj);",
"\n"
],
"cpp_code": [
"JNIEXPORT jbyteArray JNICALL Java_org_opencv_objdetect_GraphicalCodeDetector_detectAndDecodeBytes_10 (JNIEnv*, jclass, jlong, jlong);",
"\n",
"JNIEXPORT jbyteArray JNICALL Java_org_opencv_objdetect_GraphicalCodeDetector_detectAndDecodeBytes_10",
" (JNIEnv* env, jclass , jlong self, jlong img_nativeObj)",
"{",
" ",
" static const char method_name[] = \"objdetect::detectAndDecodeBytes_10()\";",
" try {",
" LOGD(\"%s\", method_name);",
" cv::GraphicalCodeDetector* me = (cv::GraphicalCodeDetector*) self; //TODO: check for NULL",
" Mat& img = *((Mat*)img_nativeObj);",
" std::string result = me->detectAndDecode( img );",
" jsize sz = result.size();",
" jbyteArray _retval_ = env->NewByteArray(static_cast<jsize>(sz));",
" env->SetByteArrayRegion(_retval_, 0, sz, reinterpret_cast<jbyte*>(&result[0]));",
" return _retval_;",
" } catch(const std::exception &e) {",
" throwJavaException(env, &e, method_name);",
" } catch (...) {",
" throwJavaException(env, 0, method_name);",
" }",
" return 0;",
"}",
"\n"
]
}
}
}
}

View File

@ -2,13 +2,18 @@ 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;
public class QRCodeDetectorTest extends OpenCVTestCase {
@ -50,4 +55,21 @@ 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() throws UnsupportedEncodingException {
String inp = new String("こんにちは世界");
QRCodeEncoder_Params params = new QRCodeEncoder_Params();
params.set_mode(QRCodeEncoder.MODE_KANJI);
QRCodeEncoder encoder = QRCodeEncoder.create(params);
Mat qrcode = new Mat();
encoder.encode(inp.getBytes("Shift_JIS"), 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);
assertEquals(inp, new String(output, "Shift_JIS"));
}
}

View File

@ -980,7 +980,7 @@ public:
std::string detectAndDecodeCurved(InputArray in, OutputArray points, OutputArray straight_qrcode);
QRCodeEncoder::ECIEncodings getEncoding(size_t codeIdx);
QRCodeEncoder::ECIEncodings getEncoding(int codeIdx);
};
QRCodeDetector::QRCodeDetector() {
@ -997,8 +997,9 @@ QRCodeDetector& QRCodeDetector::setEpsY(double epsY) {
return *this;
}
QRCodeEncoder::ECIEncodings QRCodeDetector::getEncoding(size_t codeIdx) {
QRCodeEncoder::ECIEncodings QRCodeDetector::getEncoding(int codeIdx) {
auto& encodings = std::dynamic_pointer_cast<ImplContour>(p)->encodings;
CV_Assert(codeIdx >= 0);
CV_Assert(codeIdx < encodings.size());
return encodings[codeIdx];
}