Skip to content

Commit 4a4a1aa

Browse files
authored
Merge pull request #91 from abdelaziz-mahdy/face-objdetect
Add FaceDetectorYN and FaceRecognizerSF
2 parents ee4f263 + 871c853 commit 4a4a1aa

File tree

9 files changed

+904
-23
lines changed

9 files changed

+904
-23
lines changed

.github/workflows/build_test_release.yaml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ jobs:
120120
build/publish/libopencv_dart-linux-x64.tar.gz
121121
- name: Run Test
122122
run: |
123-
export LD_LIBRARY_PATH="${{github.workspace}}/linux:$LD_LIBRARY_PATH"
123+
export OPENCV_DART_LIB_PATH="${{github.workspace}}/linux/libopencv_dart.so"
124124
dart pub get
125125
dart test -x skip-workflow,no-local-files
126126
@@ -160,7 +160,7 @@ jobs:
160160
build/publish/libopencv_dart-windows-x64.tar.gz
161161
- name: Run Test
162162
run: |
163-
$env:PATH = "${{github.workspace}}\windows;${env:PATH}"
163+
$env:OPENCV_DART_LIB_PATH = "${{github.workspace}}\windows\opencv_dart.dll"
164164
dart pub get
165165
dart test -x no-local-files
166166
build-macos:
@@ -201,7 +201,7 @@ jobs:
201201
- name: Run Test
202202
run: |
203203
ls -alh ${{github.workspace}}/macos
204-
export DYLD_LIBRARY_PATH="${{github.workspace}}/macos:$DYLD_LIBRARY_PATH"
204+
export OPENCV_DART_LIB_PATH="${{github.workspace}}/macos/libopencv_dart.dylib"
205205
dart pub get
206206
dart test -x skip-workflow,no-local-files
207207
build-macos-arm64:
@@ -243,7 +243,7 @@ jobs:
243243
- name: Run Test
244244
run: |
245245
ls -alh ${{github.workspace}}/macos
246-
export DYLD_LIBRARY_PATH="${{github.workspace}}/macos:$DYLD_LIBRARY_PATH"
246+
export OPENCV_DART_LIB_PATH="${{github.workspace}}/macos/libopencv_dart.dylib"
247247
dart pub get
248248
dart test -x skip-workflow,no-local-files
249249
build-ios:

lib/src/core/base.dart

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
// coverage:ignore-file
12
// ignore_for_file: constant_identifier_names, non_constant_identifier_names
23

34
library cv;
@@ -35,17 +36,15 @@ const double CV_F32_MAX = 3.4028234663852886e+38;
3536
const double CV_F64_MAX = 1.7976931348623157e+308;
3637

3738
ffi.DynamicLibrary loadNativeLibrary() {
38-
if (Platform.isWindows) {
39-
return ffi.DynamicLibrary.open("$_libraryName.dll");
40-
} else if (Platform.isAndroid || Platform.isFuchsia || Platform.isLinux) {
41-
return ffi.DynamicLibrary.open("lib$_libraryName.so");
42-
} else if (Platform.isMacOS) {
43-
return ffi.DynamicLibrary.open("lib$_libraryName.dylib");
44-
} else if (Platform.isIOS) {
45-
return ffi.DynamicLibrary.process();
46-
} else {
47-
throw UnsupportedError("Platform ${Platform.operatingSystem} not supported");
48-
}
39+
if (Platform.isIOS) return ffi.DynamicLibrary.process();
40+
final defaultLibPath = switch (Platform.operatingSystem) {
41+
"windows" => "$_libraryName.dll",
42+
"linux" || "android" || "fuchsia" => "lib$_libraryName.so",
43+
"macos" => "lib$_libraryName.dylib",
44+
_ => throw UnsupportedError("Platform ${Platform.operatingSystem} not supported")
45+
};
46+
final libPath = Platform.environment["OPENCV_DART_LIB_PATH"] ?? defaultLibPath;
47+
return ffi.DynamicLibrary.open(libPath);
4948
}
5049

5150
final CFFI = CvNative(loadNativeLibrary());
@@ -76,8 +75,7 @@ void cvRun(CvStatus Function() func) {
7675
}
7776
}
7877

79-
R cvRunArena<R>(R Function(Arena arena) computation,
80-
[Allocator wrappedAllocator = calloc, bool keep = false]) {
78+
R cvRunArena<R>(R Function(Arena arena) computation, [Allocator wrappedAllocator = calloc, bool keep = false]) {
8179
final arena = Arena(wrappedAllocator);
8280
bool isAsync = false;
8381
try {
@@ -109,7 +107,7 @@ typedef I32 = ffi.Int;
109107
typedef F32 = ffi.Float;
110108
typedef F64 = ffi.Double;
111109

112-
extension PointerCharExtension on ffi.Pointer<ffi.Char>{
110+
extension PointerCharExtension on ffi.Pointer<ffi.Char> {
113111
String toDartString() => cast<Utf8>().toDartString();
114112
}
115113

lib/src/objdetect/objdetect.dart

Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1+
// ignore_for_file: constant_identifier_names
2+
13
library cv;
24

35
import 'dart:ffi' as ffi;
6+
import 'dart:typed_data';
47

58
import 'package:ffi/ffi.dart';
69

@@ -598,3 +601,213 @@ class QRCodeDetector extends CvStruct<cvg.QRCodeDetector> {
598601
@override
599602
List<int> get props => [ptr.address];
600603
}
604+
605+
/// DNN-based face detector.
606+
///
607+
/// model download link: https://github.com/opencv/opencv_zoo/tree/master/models/face_detection_yunet
608+
class FaceDetectorYN extends CvStruct<cvg.FaceDetectorYN> {
609+
FaceDetectorYN._(cvg.FaceDetectorYNPtr ptr, [bool attach = true]) : super.fromPointer(ptr) {
610+
if (attach) {
611+
finalizer.attach(this, ptr.cast(), detach: this);
612+
}
613+
}
614+
615+
factory FaceDetectorYN.fromFile(
616+
String model,
617+
String config,
618+
Size inputSize, {
619+
double scoreThreshold = 0.9,
620+
double nmsThreshold = 0.3,
621+
int topK = 5000,
622+
int backendId = 0,
623+
int targetId = 0,
624+
}) {
625+
final p = calloc<cvg.FaceDetectorYN>();
626+
return using<FaceDetectorYN>((arena) {
627+
final cModel = model.toNativeUtf8().cast<ffi.Char>();
628+
final cConfig = config.toNativeUtf8().cast<ffi.Char>();
629+
cvRun(
630+
() => CFFI.FaceDetectorYN_New(
631+
cModel,
632+
cConfig,
633+
inputSize.toSize(arena).ref,
634+
scoreThreshold,
635+
nmsThreshold,
636+
topK,
637+
backendId,
638+
targetId,
639+
p,
640+
),
641+
);
642+
calloc.free(cModel);
643+
calloc.free(cConfig);
644+
return FaceDetectorYN._(p);
645+
});
646+
}
647+
648+
factory FaceDetectorYN.fromBuffer(
649+
String framework,
650+
Uint8List bufferModel,
651+
Uint8List bufferConfig,
652+
Size inputSize, {
653+
double scoreThreshold = 0.9,
654+
double nmsThreshold = 0.3,
655+
int topK = 5000,
656+
int backendId = 0,
657+
int targetId = 0,
658+
}) {
659+
final p = calloc<cvg.FaceDetectorYN>();
660+
return using<FaceDetectorYN>((arena) {
661+
final cFramework = framework.toNativeUtf8().cast<ffi.Char>();
662+
cvRun(
663+
() => CFFI.FaceDetectorYN_NewFromBuffer(
664+
cFramework,
665+
VecUChar.fromList(bufferModel).ref,
666+
VecUChar.fromList(bufferConfig).ref,
667+
inputSize.toSize(arena).ref,
668+
scoreThreshold,
669+
nmsThreshold,
670+
topK,
671+
backendId,
672+
targetId,
673+
p,
674+
),
675+
);
676+
calloc.free(cFramework);
677+
return FaceDetectorYN._(p);
678+
});
679+
}
680+
681+
(int, int) getInputSize() {
682+
final p = calloc<cvg.Size>();
683+
cvRun(() => CFFI.FaceDetectorYN_GetInputSize(ref, p));
684+
final ret = (p.ref.width, p.ref.height);
685+
calloc.free(p);
686+
return ret;
687+
}
688+
689+
double getScoreThreshold() {
690+
return using<double>((arena) {
691+
final p = arena<ffi.Float>();
692+
cvRun(() => CFFI.FaceDetectorYN_GetScoreThreshold(ref, p));
693+
return p.value;
694+
});
695+
}
696+
697+
double getNmsThreshold() {
698+
return using<double>((arena) {
699+
final p = arena<ffi.Float>();
700+
cvRun(() => CFFI.FaceDetectorYN_GetNMSThreshold(ref, p));
701+
return p.value;
702+
});
703+
}
704+
705+
int getTopK() {
706+
return using<int>((arena) {
707+
final p = arena<ffi.Int>();
708+
cvRun(() => CFFI.FaceDetectorYN_GetTopK(ref, p));
709+
return p.value;
710+
});
711+
}
712+
713+
Mat detect(Mat img) {
714+
final p = calloc<cvg.Mat>();
715+
cvRun(() => CFFI.FaceDetectorYN_Detect(ref, img.ref, p));
716+
return Mat.fromPointer(p);
717+
}
718+
719+
void setInputSize(Size inputSize) {
720+
using<void>((arena) {
721+
cvRun(() => CFFI.FaceDetectorYN_SetInputSize(ref, inputSize.toSize(arena).ref));
722+
});
723+
}
724+
725+
void setScoreThreshold(double scoreThreshold) {
726+
cvRun(() => CFFI.FaceDetectorYN_SetScoreThreshold(ref, scoreThreshold));
727+
}
728+
729+
void setNMSThreshold(double nmsThreshold) {
730+
cvRun(() => CFFI.FaceDetectorYN_SetNMSThreshold(ref, nmsThreshold));
731+
}
732+
733+
void setTopK(int topK) {
734+
cvRun(() => CFFI.FaceDetectorYN_SetTopK(ref, topK));
735+
}
736+
737+
@override
738+
cvg.FaceDetectorYN get ref => ptr.ref;
739+
740+
static final finalizer = OcvFinalizer<cvg.FaceDetectorYNPtr>(CFFI.addresses.FaceDetectorYN_Close);
741+
742+
void dispose() {
743+
finalizer.detach(this);
744+
CFFI.FaceDetectorYN_Close(ptr);
745+
}
746+
747+
@override
748+
List<int> get props => [ptr.address];
749+
}
750+
751+
/// DNN-based face recognizer.
752+
///
753+
/// model download link: https://github.com/opencv/opencv_zoo/tree/master/models/face_recognition_sface
754+
class FaceRecognizerSF extends CvStruct<cvg.FaceRecognizerSF> {
755+
FaceRecognizerSF._(cvg.FaceRecognizerSFPtr ptr, [bool attach = true]) : super.fromPointer(ptr) {
756+
if (attach) {
757+
finalizer.attach(this, ptr.cast(), detach: this);
758+
}
759+
}
760+
761+
factory FaceRecognizerSF.newRecognizer(
762+
String model,
763+
String config,
764+
int backendId,
765+
int targetId,
766+
) {
767+
final p = calloc<cvg.FaceRecognizerSF>();
768+
final cModel = model.toNativeUtf8().cast<ffi.Char>();
769+
final cConfig = config.toNativeUtf8().cast<ffi.Char>();
770+
cvRun(() => CFFI.FaceRecognizerSF_New(cModel, cConfig, backendId, targetId, p));
771+
calloc.free(cModel);
772+
calloc.free(cConfig);
773+
return FaceRecognizerSF._(p);
774+
}
775+
776+
Mat alignCrop(Mat srcImg, Mat faceBox) {
777+
final p = calloc<cvg.Mat>();
778+
cvRun(() => CFFI.FaceRecognizerSF_AlignCrop(ref, srcImg.ref, faceBox.ref, p));
779+
return Mat.fromPointer(p);
780+
}
781+
782+
Mat feature(Mat alignedImg) {
783+
final p = calloc<cvg.Mat>();
784+
cvRun(() => CFFI.FaceRecognizerSF_Feature(ref, alignedImg.ref, p));
785+
return Mat.fromPointer(p);
786+
}
787+
788+
double match(Mat faceFeature1, Mat faceFeature2, int disType) {
789+
return using<double>((arena) {
790+
final distance = arena<ffi.Double>();
791+
cvRun(() =>
792+
CFFI.FaceRecognizerSF_Match(ref, faceFeature1.ref, faceFeature2.ref, disType, distance));
793+
return distance.value;
794+
});
795+
}
796+
797+
@override
798+
cvg.FaceRecognizerSF get ref => ptr.ref;
799+
800+
static final finalizer =
801+
OcvFinalizer<cvg.FaceRecognizerSFPtr>(CFFI.addresses.FaceRecognizerSF_Close);
802+
803+
void dispose() {
804+
finalizer.detach(this);
805+
CFFI.FaceRecognizerSF_Close(ptr);
806+
}
807+
808+
@override
809+
List<int> get props => [ptr.address];
810+
811+
static const int DIS_TYPR_FR_COSINE = 0;
812+
static const int DIS_TYPE_FR_NORM_L2 = 1;
813+
}

0 commit comments

Comments
 (0)