From 7668d0ad8d01ef5dd12e801833464f8660fcbc18 Mon Sep 17 00:00:00 2001 From: rainyl Date: Wed, 17 Apr 2024 17:05:13 +0800 Subject: [PATCH] add contrib:wechat_qrcode --- CMakeLists.txt | 2 +- ffigen.yaml | 2 + lib/src/contrib/wechat_qrcode.dart | 87 +++++++++++++++++ lib/src/opencv.dart | 1 + lib/src/opencv.g.dart | 135 ++++++++++++++++++++++++++ src/extra/wechat_qrcode.cpp | 56 +++++++++++ src/extra/wechat_qrcode.h | 41 ++++++++ test/{ => contrib}/aruco_test.dart | 0 test/{ => contrib}/img_hash_test.dart | 0 test/contrib/wechat_qrcode_test.dart | 34 +++++++ 10 files changed, 357 insertions(+), 1 deletion(-) create mode 100644 lib/src/contrib/wechat_qrcode.dart create mode 100644 src/extra/wechat_qrcode.cpp create mode 100644 src/extra/wechat_qrcode.h rename test/{ => contrib}/aruco_test.dart (100%) rename test/{ => contrib}/img_hash_test.dart (100%) create mode 100644 test/contrib/wechat_qrcode_test.dart diff --git a/CMakeLists.txt b/CMakeLists.txt index cdf10704..aaa72f0e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,7 +16,7 @@ set(OpenCV_LIBS opencv_aruco opencv_core opencv_calib3d opencv_dnn opencv_highgui opencv_features2d opencv_gapi opencv_photo opencv_imgproc opencv_objdetect opencv_video opencv_videoio opencv_stitching - opencv_img_hash + opencv_img_hash opencv_wechat_qrcode ) if(ANDROID) diff --git a/ffigen.yaml b/ffigen.yaml index 2e6e497c..565fa215 100644 --- a/ffigen.yaml +++ b/ffigen.yaml @@ -24,6 +24,7 @@ headers: - src/dnn/dnn.h - src/extra/aruco.h - src/extra/img_hash.h + - src/extra/wechat_qrcode.h - src/features2d/features2d.h - src/highgui/highgui.h - src/imgcodecs/imgcodecs.h @@ -44,6 +45,7 @@ headers: - src/dnn/dnn.h - src/extra/aruco.h - src/extra/img_hash.h + - src/extra/wechat_qrcode.h - src/features2d/features2d.h - src/highgui/highgui.h - src/imgcodecs/imgcodecs.h diff --git a/lib/src/contrib/wechat_qrcode.dart b/lib/src/contrib/wechat_qrcode.dart new file mode 100644 index 00000000..36053dae --- /dev/null +++ b/lib/src/contrib/wechat_qrcode.dart @@ -0,0 +1,87 @@ +library cv; + +import 'dart:ffi' as ffi; + +import 'package:ffi/ffi.dart'; + +import '../core/base.dart'; +import '../core/mat.dart'; +import '../core/vec.dart'; +import '../opencv.g.dart' as cvg; + +class WeChatQRCode extends CvStruct { + WeChatQRCode._(super.ptr) : super.fromPointer() { + finalizer.attach(this, ptr.cast()); + } + + factory WeChatQRCode.empty() { + final p = calloc(); + cvRun(() => CFFI.WeChatQRCode_New(p)); + return WeChatQRCode._(p); + } + + /// Initialize the WeChatQRCode. + /// It includes two models, which are packaged with caffe format. + /// Therefore, there are prototxt and caffe models (In total, four paramenters). + /// + /// https://docs.opencv.org/4.x/d5/d04/classcv_1_1wechat__qrcode_1_1WeChatQRCode.html#a9c0dc4c37646a1a051340d6b0916f388 + factory WeChatQRCode([ + String detectorPrototxtPath = "", + String detectorCaffeModelPath = "", + String superResolutionPrototxtPath = "", + String superResolutionCaffeModelPath = "", + ]) { + return cvRunArena((arena) { + final p = calloc(); + final dp = detectorPrototxtPath.toNativeUtf8(allocator: arena).cast(); + final dm = detectorCaffeModelPath.toNativeUtf8(allocator: arena).cast(); + final srp = superResolutionPrototxtPath.toNativeUtf8(allocator: arena).cast(); + final srm = superResolutionCaffeModelPath.toNativeUtf8(allocator: arena).cast(); + cvRun(() => CFFI.WeChatQRCode_NewWithParams(dp, dm, srp, srm, p)); + return WeChatQRCode._(p); + }); + } + + /// Both detects and decodes QR code. To simplify the usage, there is a only API: detectAndDecode. + /// https://docs.opencv.org/4.x/d5/d04/classcv_1_1wechat__qrcode_1_1WeChatQRCode.html#a27c167d2d58e5ee4418fd3a9ed5876cc + (List, VecMat points) detectAndDecode(InputArray img, [VecMat? points]) { + final p = calloc(); + final rval = calloc(); + cvRun(() => CFFI.WeChatQRCode_DetectAndDecode(ptr, img.ref, p, rval)); + final vec = VecVecChar.fromPointer(rval); + final points = VecMat.fromPointer(p); + return (vec.asStringList(), points); + } + + /// https://docs.opencv.org/4.x/d5/d04/classcv_1_1wechat__qrcode_1_1WeChatQRCode.html#abf807138abc2626c159abd3e9a80e791 + double get scaleFactor { + return cvRunArena((arena) { + final p = calloc(); + cvRun(() => CFFI.WeChatQRCode_GetScaleFactor(ptr, p)); + return p.value; + }); + } + + /// set scale factor QR code detector use neural network to detect QR. + /// Before running the neural network, the input image is pre-processed by scaling. + /// By default, the input image is scaled to an image with an area of 160000 pixels. + /// The scale factor allows to use custom scale the input image: + /// width = scaleFactor*width height = scaleFactor*width + /// + /// scaleFactor valuse must be > 0 and <= 1, + /// otherwise the scaleFactor value is set to -1 and + /// use default scaled to an image with an area of 160000 pixels. + /// + /// https://docs.opencv.org/4.x/d5/d04/classcv_1_1wechat__qrcode_1_1WeChatQRCode.html#a084f9aa8693fa0a62c43dd10d2533ab8 + set scaleFactor(double scaleFactor) { + cvRun(() => CFFI.WeChatQRCode_SetScaleFactor(ptr, scaleFactor)); + } + + static final finalizer = OcvFinalizer(CFFI.addresses.WeChatQRCode_Close); + + @override + List get props => [ptr.address]; + + @override + cvg.WeChatQRCode get ref => ptr.ref; +} diff --git a/lib/src/opencv.dart b/lib/src/opencv.dart index f34d2ef5..cef281ad 100644 --- a/lib/src/opencv.dart +++ b/lib/src/opencv.dart @@ -5,6 +5,7 @@ export 'constants.g.dart'; export 'contrib/aruco.dart'; export 'contrib/aruco_dict.dart'; export 'contrib/img_hash.dart'; +export 'contrib/wechat_qrcode.dart'; export 'core/array.dart'; export 'core/asyncarray.dart'; diff --git a/lib/src/opencv.g.dart b/lib/src/opencv.g.dart index 424b7469..d155fdb9 100644 --- a/lib/src/opencv.g.dart +++ b/lib/src/opencv.g.dart @@ -15867,6 +15867,126 @@ class CvNative { late final _Watershed = _WatershedPtr.asFunction(); + void WeChatQRCode_Close( + ffi.Pointer self, + ) { + return _WeChatQRCode_Close( + self, + ); + } + + late final _WeChatQRCode_ClosePtr = + _lookup)>>( + 'WeChatQRCode_Close'); + late final _WeChatQRCode_Close = _WeChatQRCode_ClosePtr.asFunction< + void Function(ffi.Pointer)>(); + + CvStatus WeChatQRCode_DetectAndDecode( + ffi.Pointer self, + Mat img, + ffi.Pointer points, + ffi.Pointer rval, + ) { + return _WeChatQRCode_DetectAndDecode( + self, + img, + points, + rval, + ); + } + + late final _WeChatQRCode_DetectAndDecodePtr = _lookup< + ffi.NativeFunction< + CvStatus Function(ffi.Pointer, Mat, ffi.Pointer, + ffi.Pointer)>>('WeChatQRCode_DetectAndDecode'); + late final _WeChatQRCode_DetectAndDecode = + _WeChatQRCode_DetectAndDecodePtr.asFunction< + CvStatus Function(ffi.Pointer, Mat, ffi.Pointer, + ffi.Pointer)>(); + + CvStatus WeChatQRCode_GetScaleFactor( + ffi.Pointer self, + ffi.Pointer rval, + ) { + return _WeChatQRCode_GetScaleFactor( + self, + rval, + ); + } + + late final _WeChatQRCode_GetScaleFactorPtr = _lookup< + ffi.NativeFunction< + CvStatus Function(ffi.Pointer, + ffi.Pointer)>>('WeChatQRCode_GetScaleFactor'); + late final _WeChatQRCode_GetScaleFactor = + _WeChatQRCode_GetScaleFactorPtr.asFunction< + CvStatus Function( + ffi.Pointer, ffi.Pointer)>(); + + CvStatus WeChatQRCode_New( + ffi.Pointer qrcode, + ) { + return _WeChatQRCode_New( + qrcode, + ); + } + + late final _WeChatQRCode_NewPtr = + _lookup)>>( + 'WeChatQRCode_New'); + late final _WeChatQRCode_New = _WeChatQRCode_NewPtr.asFunction< + CvStatus Function(ffi.Pointer)>(); + + CvStatus WeChatQRCode_NewWithParams( + ffi.Pointer detector_prototxt_path, + ffi.Pointer detector_caffe_model_path, + ffi.Pointer super_resolution_prototxt_path, + ffi.Pointer super_resolution_caffe_model_path, + ffi.Pointer qrcode, + ) { + return _WeChatQRCode_NewWithParams( + detector_prototxt_path, + detector_caffe_model_path, + super_resolution_prototxt_path, + super_resolution_caffe_model_path, + qrcode, + ); + } + + late final _WeChatQRCode_NewWithParamsPtr = _lookup< + ffi.NativeFunction< + CvStatus Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ffi.Pointer)>>('WeChatQRCode_NewWithParams'); + late final _WeChatQRCode_NewWithParams = + _WeChatQRCode_NewWithParamsPtr.asFunction< + CvStatus Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ffi.Pointer)>(); + + CvStatus WeChatQRCode_SetScaleFactor( + ffi.Pointer self, + double scale_factor, + ) { + return _WeChatQRCode_SetScaleFactor( + self, + scale_factor, + ); + } + + late final _WeChatQRCode_SetScaleFactorPtr = _lookup< + ffi.NativeFunction< + CvStatus Function(ffi.Pointer, + ffi.Float)>>('WeChatQRCode_SetScaleFactor'); + late final _WeChatQRCode_SetScaleFactor = _WeChatQRCode_SetScaleFactorPtr + .asFunction, double)>(); + void Window_Close( ffi.Pointer winname, ) { @@ -16513,6 +16633,8 @@ class _SymbolAddresses { get VideoCapture_Close => _library._VideoCapture_ClosePtr; ffi.Pointer)>> get VideoWriter_Close => _library._VideoWriter_ClosePtr; + ffi.Pointer)>> + get WeChatQRCode_Close => _library._WeChatQRCode_ClosePtr; ffi.Pointer)>> get Window_Close => _library._Window_ClosePtr; } @@ -17205,6 +17327,13 @@ final class NO_USE_VideoWriterPtr extends ffi.Struct { external ffi.Pointer p; } +/// \ +/// Dart ffigen will not generate typedefs if not referred \ +/// so here we confirm they are included \ +final class NO_USE_WeChatQRCodePtr extends ffi.Struct { + external ffi.Pointer p; +} + final class Net extends ffi.Struct { external ffi.Pointer ptr; } @@ -17836,6 +17965,12 @@ final class VideoWriter extends ffi.Struct { } typedef VideoWriterPtr = ffi.Pointer; + +final class WeChatQRCode extends ffi.Struct { + external ffi.Pointer ptr; +} + +typedef WeChatQRCodePtr = ffi.Pointer; typedef double_t = ffi.Double; typedef Dartdouble_t = double; typedef float_t = ffi.Float; diff --git a/src/extra/wechat_qrcode.cpp b/src/extra/wechat_qrcode.cpp new file mode 100644 index 00000000..c7beb396 --- /dev/null +++ b/src/extra/wechat_qrcode.cpp @@ -0,0 +1,56 @@ +#include "wechat_qrcode.h" +#include + +CvStatus WeChatQRCode_New(WeChatQRCode *qrcode) +{ + BEGIN_WRAP + *qrcode = {new cv::wechat_qrcode::WeChatQRCode()}; + END_WRAP +} +CvStatus WeChatQRCode_NewWithParams(const char *detector_prototxt_path, const char *detector_caffe_model_path, + const char *super_resolution_prototxt_path, + const char *super_resolution_caffe_model_path, WeChatQRCode *qrcode) +{ + BEGIN_WRAP + *qrcode = {new cv::wechat_qrcode::WeChatQRCode(detector_prototxt_path, detector_caffe_model_path, + super_resolution_prototxt_path, + super_resolution_caffe_model_path)}; + END_WRAP +} +void WeChatQRCode_Close(WeChatQRCode *self) +{ + delete self->ptr; + self->ptr = nullptr; + delete self; + self = nullptr; +} +CvStatus WeChatQRCode_DetectAndDecode(WeChatQRCode *self, Mat img, VecMat *points, VecVecChar *rval) +{ + BEGIN_WRAP + std::vector pts; + + auto strings = self->ptr->detectAndDecode(*img.ptr, pts); + *points = {new std::vector(pts)}; + + auto cstrings = new std::vector>(); + cstrings->reserve(strings.size()); + for (int i = 0; i < strings.size(); i++) { + auto s = strings.at(i); + cstrings->push_back(std::vector(s.begin(), s.end())); + } + *rval = {cstrings}; + + END_WRAP +} +CvStatus WeChatQRCode_GetScaleFactor(WeChatQRCode *self, float *rval) +{ + BEGIN_WRAP + *rval = self->ptr->getScaleFactor(); + END_WRAP +} +CvStatus WeChatQRCode_SetScaleFactor(WeChatQRCode *self, float scale_factor) +{ + BEGIN_WRAP + self->ptr->setScaleFactor(scale_factor); + END_WRAP +} diff --git a/src/extra/wechat_qrcode.h b/src/extra/wechat_qrcode.h new file mode 100644 index 00000000..9da3147a --- /dev/null +++ b/src/extra/wechat_qrcode.h @@ -0,0 +1,41 @@ +/* + Created by Rainyl. + Licensed: Apache 2.0 license. Copyright (c) 2024 Rainyl. +*/ +#pragma once +#ifndef WECHAT_QRCODE_H +#define WECHAT_QRCODE_H + +#include "core/core.h" + +#ifdef __cplusplus +#include +#include +extern "C" { +#endif + +// Main Content Start +#ifdef __cplusplus +CVD_TYPEDEF(cv::wechat_qrcode::WeChatQRCode, WeChatQRCode) +#else +CVD_TYPEDEF(void, WeChatQRCode) +#endif + +CVD_TYPEDEF_PTR(WeChatQRCode) + +CvStatus WeChatQRCode_New(WeChatQRCode *qrcode); +CvStatus WeChatQRCode_NewWithParams(const char *detector_prototxt_path, const char *detector_caffe_model_path, + const char *super_resolution_prototxt_path, + const char *super_resolution_caffe_model_path, WeChatQRCode *qrcode); +void WeChatQRCode_Close(WeChatQRCode *self); +CvStatus WeChatQRCode_DetectAndDecode(WeChatQRCode *self, Mat img, VecMat *points, VecVecChar *rval); +CvStatus WeChatQRCode_GetScaleFactor(WeChatQRCode *self, float *rval); +CvStatus WeChatQRCode_SetScaleFactor(WeChatQRCode *self, float scale_factor); + +// Main Content End + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/test/aruco_test.dart b/test/contrib/aruco_test.dart similarity index 100% rename from test/aruco_test.dart rename to test/contrib/aruco_test.dart diff --git a/test/img_hash_test.dart b/test/contrib/img_hash_test.dart similarity index 100% rename from test/img_hash_test.dart rename to test/contrib/img_hash_test.dart diff --git a/test/contrib/wechat_qrcode_test.dart b/test/contrib/wechat_qrcode_test.dart new file mode 100644 index 00000000..00b613a6 --- /dev/null +++ b/test/contrib/wechat_qrcode_test.dart @@ -0,0 +1,34 @@ +import 'package:test/test.dart'; + +import 'package:opencv_dart/opencv_dart.dart' as cv; + +void main() { + test('cv.WeChatQRCode.empty', () { + final qr = cv.WeChatQRCode.empty(); + expect(qr.ptr, isNotNull); + final (res, points) = qr.detectAndDecode(cv.imread("test/images/qrcode.png")); + expect(res.length, 1); + expect(res.first, "Hello World!"); + expect(points.length, 1); + }); + + test('cv.WeChatQRCode', () { + final qr = cv.WeChatQRCode( + "test/models/detect.prototxt", + "test/models/detect.caffemodel", + "test/models/sr.prototxt", + "test/models/sr.caffemodel", + ); + expect(qr.ptr, isNotNull); + final (res, points) = qr.detectAndDecode(cv.imread("test/images/multi_qrcodes.png")); + expect(res.length, 2); + expect(points.length, 2); + expect(res, ["bar", "foo"]); + + expect(qr.scaleFactor, closeTo(-1.0, 1e-3)); + qr.scaleFactor = 0.5; + expect(qr.scaleFactor, closeTo(0.5, 1e-3)); + qr.scaleFactor = 1.5; + expect(qr.scaleFactor, closeTo(-1.0, 1e-3)); + }); +}