谷歌机器学习人脸检测
谷歌机器学习人脸检测
如今,我们必须了解机器学习对我们的应用程序功能要求高的原因。特别是,我们在银行应用程序中使用了这些功能。
请让我将主要事实解释为缩写以涵盖整篇文章。
在这篇文章中,我尽可能多地解释了
- 使用谷歌机器学习和张量流在圆圈框内拍摄自拍。
- 使用谷歌机器学习套件进行面部动作检测并录制该视频
请让我请求一件事。那就是我们对某些功能在颤振中正常工作有一些限制。所以,有时,我会使用一些不同的方式来工作并实现我们的目标。
好吧,我们走吧。我不想浪费你无价的时光。
我使用的库——
- tflite_flutter: ^0.9.0
- google_ml_kit:^0.12.0
首先解释一下第一个自拍功能
当我们的脸到达圆圈框时,我们必须自拍。
我们需要使用一些服务。这些是
- 人脸检测服务
- 相机服务
- 机器学习服务
对于 FaceDectorService,
这些类使用的主要目的是进行人脸检测过程。
导入“包:相机/camera.dart”;
导入“包:google_ml_kit/google_ml_kit.dart”;
导入“包:颤振/material.dart”;
导入'包:xxxx/core/global/services/face_service/camera.service.dart';
导入'包:xxx/injection/injector.dart';
类 FaceDetectorService {
最终的 CameraService _cameraService = 注入器<CameraService>();
后期 FaceDetector _faceDetector;
FaceDetector 获取 faceDetector => _faceDetector;
列表<Face>_faces = [];
列表<Face>获取面孔=> _faces;
bool get faceDetected => _faces.isNotEmpty;
无效初始化(){
_faceDetector = 人脸检测器(
选项:FaceDetectorOptions(
启用分类:真,
enableContours:真,
));
}
未来<void>detectFacesFromImage(CameraImage 图像)异步 {
InputImageData firebaseImageMetadata = InputImageData(
图像旋转:
_cameraService.cameraRotation ?? InputImageRotation.rotation0deg,
inputImageFormat: InputImageFormatValue.fromRawValue(image.format.raw) ??
InputImageFormat.nv21,
尺寸:尺寸(image.width.toDouble(),image.height.toDouble()),
平面数据:image.planes.map(
(平面平面){
返回输入图像平面元数据(
bytesPerRow:plane.bytesPerRow,
高度:平面.高度,
宽度:平面宽度,
);
},
).toList(),
);
InputImage firebaseVisionImage = InputImage.fromBytes(
字节:image.planes[0].bytes,
inputImageData:firebaseImageMetadata,
);
_faces = 等待 _faceDetector.processImage(firebaseVisionImage);
}
处置(){
_faceDetector.close();
}
}
对于 MLService
此类使用的主要目的是检测我们的面部尺寸点并转换所需的图像格式。我使用 tflitte 进行计算。
导入“飞镖:io”;
导入'dart:typed_data';
导入“包:相机/camera.dart”;
导入“包:google_ml_kit/google_ml_kit.dart”;
导入'包:xxx/core/global/services/face_service/image_converter.dart';
导入“包:tflite_flutter/tflite_flutter.dart”;
将“package:image/image.dart”导入为 imglib;
类 MLService {
翻译? _解释器;
双阈值 = 0.5;
列表_predictedData = [];
列表获取预测数据 => _predictedData;
未来初始化()异步{
迟到的代表;
尝试 {
如果(平台.isAndroid){
委托= GpuDelegateV2(
选项:GpuDelegateOptionsV2(
isPrecisionLossAllowed:假,
inferencePreference:TfLiteGpuInferenceUsage.fastSingleAnswer,
inferencePriority1:TfLiteGpuInferencePriority.minLatency,
inferencePriority2:TfLiteGpuInferencePriority.auto,
inferencePriority3:TfLiteGpuInferencePriority.auto,
),
);
} else if (Platform.isIOS) {
委托= GpuDelegate(
选项:GpuDelegateOptions(
allowPrecisionLoss:真,
waitType: TFLGpuDelegateWaitType.active),
);
}
var interpreterOptions = InterpreterOptions()..addDelegate(delegate);
this._interpreter = await Interpreter.fromAsset('mobilefacenet.tflite',
选项:解释器选项);
} 抓住 (e) {
print('加载模型失败');
打印(e);
}
}
void setCurrentPrediction(CameraImage cameraImage, Face? face) {
if (_interpreter == null) throw Exception('Interpreter is null');
if (face == null) throw Exception('Face is null');
列表输入 = _preProcess(cameraImage, face);
输入 = input.reshape([1, 112, 112, 3]);
列表输出 = List.generate(1, (index) => List.filled(192, 0));
this._interpreter?.run(输入,输出);
输出 = output.reshape([192]);
this._predictedData = List.from(输出);
}
List _preProcess(CameraImage image, Face faceDetected) {
imglib.Image croppedImage = _cropFace(image, faceDetected);
imglib.Image img = imglib.copyResizeCropSquare(croppedImage, 112);
Float32List imageAsList = imageToByteListFloat32(img);
返回图像列表;
}
imglib.Image _cropFace(CameraImage 图像,人脸检测){
imglib.Image convertImage = _convertCameraImage(image);
双 x = faceDetected.boundingBox.left - 10.0;
双 y = faceDetected.boundingBox.top - 10.0;
双w = faceDetected.boundingBox.width + 10.0;
双 h = faceDetected.boundingBox.height + 10.0;
返回 imglib.copyCrop(
转换图像,x.round(),y.round(),w.round(),h.round());
}
imglib.Image _convertCameraImage(CameraImage 图像) {
var img = convertToImage(图像);
var img1 = imglib.copyRotate(img, -90);
返回img1;
}
Float32List imageToByteListFloat32(imglib.Image image) {
var convertBytes = Float32List(1 * 112 * 112 * 3);
var buffer = Float32List.view(convertedBytes.buffer);
整数像素索引 = 0;
for (var i = 0; i < 112; i++) {
for (var j = 0; j < 112; j++) {
var 像素 = image.getPixel(j, i);
缓冲区[pixelIndex++] = (imglib.getRed(pixel) - 128) / 128;
缓冲区[pixelIndex++] = (imglib.getGreen(pixel) - 128) / 128;
缓冲区[pixelIndex++] = (imglib.getBlue(pixel) - 128) / 128;
}
}
返回已转换的Bytes.buffer.asFloat32List();
}
处置(){}
}
对于 CameraService,
主要目的是检测图像旋转
导入“飞镖:ui”;
导入“包:相机/camera.dart”;
导入“包:google_ml_kit/google_ml_kit.dart”;
类相机服务 {
相机控制器? _cameraController;
相机控制器?获取cameraController => _cameraController;
输入图像旋转? _cameraRotation;
输入图像旋转?获取 cameraRotation => _cameraRotation;
细绳? _imagePath;
细绳?获取 imagePath => _imagePath;
未来<void>初始化()异步{
如果(_cameraController!= null)返回;
CameraDescription 描述 = 等待 _getCameraDescription();
等待_setupCameraController(描述:描述);
_cameraRotation = rotationIntToImageRotation(
描述.传感器方向,
);
等待_cameraController?.lockCaptureOrientation();
}
未来<CameraDescription>_getCameraDescription() 异步 {
列表<CameraDescription>相机 = 等待可用相机();
return camera.firstWhere((CameraDescription camera) =>
camera.lensDirection == CameraLensDirection.front);
}
未来 _setupCameraController({
必需的 CameraDescription 描述,
}) 异步 {
_cameraController = 相机控制器(
描述,
分辨率Preset.high,
启用音频:假,
);
等待_cameraController?.initialize();
}
InputImageRotation rotationIntToImageRotation(int rotation) {
开关(旋转){
案例 90:
返回 InputImageRotation.rotation90deg;
案例 180:
返回 InputImageRotation.rotation180deg;
案例 270:
返回 InputImageRotation.rotation270deg;
默认:
返回 InputImageRotation.rotation0deg;
}
}
未来<XFile?>takePicture() 异步 {
assert(_cameraController != null, '相机控制器未初始化');
等待_cameraController?.stopImageStream();
X文件?文件 = 等待 _cameraController?.takePicture();
_imagePath = 文件?。路径;
返回文件;
}
尺寸 getImageSize() {
assert(_cameraController != null, '相机控制器未初始化');
断言(
_cameraController!.value.previewSize != null, '预览尺寸为空');
返回尺寸(
_cameraController!.value.previewSize!.height,
_cameraController!.value.previewSize!.width,
);
}
处置()异步{
等待_cameraController?.dispose();
_cameraController = null;
}
}
来吧,这个类是实现在自定义盒子里自拍的所有东西。
请不要忘记这些步骤。
-
初始化我们必须使用的服务
-
仔细计算角度。
细绳?图像路径;
脸?人脸检测;
尺寸?图片尺寸;bool _detectingFaces = false;
布尔图片拍摄=假;bool _initializing = false;
bool _captured = false;
布尔_保存=假;// 服务注入
最终 FaceDetectorService _faceDetectorService =
注射器();
最终的 CameraService _cameraService = 注入器();
最终 MLService _mlService = 注入器(); @覆盖
无效初始化状态(){
super.initState();
_开始();
}@覆盖
无效处置(){
_cameraService.dispose();
_cameraService.cameraController!.dispose();
super.dispose();
}_start() 异步 {
setState(() => _initializing = true);
等待_cameraService.initialize();
等待 _mlService.initialize();
_faceDetectorService.initialize();
setState(() => _initializing = false);_frameFaces();
}_frameFaces() {
imageSize = _cameraService.getImageSize();
_cameraService.cameraController?.startImageStream((image) async {
if (_cameraService.cameraController != null) {
如果(_detectingFaces)返回;_detectingFaces = true;
尝试 {
等待_faceDetectorService.detectFacesFromImage(图像);如果(_faceDetectorService.faces.isNotEmpty){
if (_faceDetectorService.faces.length >= 2) {
设置状态((){
_捕获=假;
});
}
faceDetected = _faceDetectorService.faces[0];
//boundingBox:宽度:317.w,高度:470.h,中心
// 左:screenWidth - 317.w / 2
最终检测框宽度 = 317.w * (image.width / context.screenWidth);
最终检测框高度 =
470.h * (image.height / context.screenHeight);最后的leftBorder = (image.width - detectBoxWidth) / 2;
最终 topBorder = (image.height - detectBoxHeight) / 2;
最终的 rightBorder = image.width / 2 + detectBoxWidth / 2;
最终的bottomBorder = image.height / 2 + detectBoxHeight / 2;
最终rotateX = faceDetected!.headEulerAngleX! <= 5 &&
faceDetected!.headEulerAngleX! >= -5;
最终rotateY = faceDetected!.headEulerAngleY! <= 5 &&
faceDetected!.headEulerAngleY! >= -5;
最终 rotateZ = faceDetected!.headEulerAngleZ! <= 5 &&
faceDetected!.headEulerAngleZ! >= -5;if (faceDetected!.boundingBox.left > leftBorder &&
faceDetected!.boundingBox.right < rightBorder &&
faceDetected!.boundingBox.bottom <bottomBorder &&
faceDetected!.boundingBox.top > topBorder &&
旋转X &&
旋转Y &&
旋转Z){
设置状态((){
_捕获=真;
});
} 别的 {
设置状态((){
_捕获=假;
});
}如果(_保存){
_mlService.setCurrentPrediction(image, faceDetected);
设置状态((){
_保存=假;
});
}
} 别的 {
设置状态((){
人脸检测 = null;
});
}_detectingFaces = false;
} 抓住 (e) {
_detectingFaces = false;
}
}
});
}@覆盖
小部件构建(BuildContext 上下文){
迟到的 Widget 正文;
如果(_初始化){
身体 = 常量中心(
孩子:CircularProgressIndicator(),
);
}if (!_initializing && !pictureTaken) {
身体=变换.规模(
规模:1.0,
孩子:纵横比(
纵横比:MediaQuery.of(context).size.aspectRatio,
孩子:溢出框(
对齐:对齐中心,
孩子:FittedBox(
适合:BoxFit.fitHeight,
孩子:大小框(
宽度:context.screenWidth,
高度:context.screenHeight,
孩子:堆栈(
对齐:对齐中心,
孩子们:[
变换.scale(
比例:_cameraService.cameraController!.value.aspectRatio,
孩子:CameraPreview(_cameraService.cameraController!),
),
定位(
顶部:context.screenHeight / 2 - 470.h / 2,
孩子:Image.asset(
AppAssets.imgFaceGuideline,
宽度:317.w,
高度:470.h,
),
),
_捕获 == 真
?定位(
底部:0,
孩子:墨水井(
onTap: () 异步 {
最终的 XFile 图像 = 等待 _cameraService
.cameraController!
。拍照片();
最终文件 =
等待 FunctionsHelper.rotateImage(image);
如果(已安装){
_cameraService.cameraController!
.stopImageStream();
等待 AutoRouter.of(context).push(
TakeSelfieResultRoute(
图像文件,
isSuccess:真,
onBackTryAgain:widget.onBackTryAgain,
tryAgain: widget.tryAgain),
);
AutoRouter.of(context).pop('success');
}
},
孩子:容器(
宽度:100.w,
高度:100.h,
利润:
const EdgeInsets.symmetric(垂直:20),
装饰:const BoxDecoration(
颜色:颜色。白色,
边界半径:边界半径.all(
半径.圆形(60))),
对齐:对齐中心,
孩子:常量图标(
Icons.camera_alt,
尺寸:50,
)),
),
)
:定位(
底部:0,
孩子:墨水井(
孩子:容器(
宽度:context.screenWidth,
高度:132.h,
颜色:ThemePalatte.darkThemePalatte.corePalatte
.backgroundBlueColor,
对齐:对齐中心,
孩子:文本(
'将手机保持在视线水平',
样式:AppTextStyles.whiteW400S20,
),
),
),
),
],
),
),
),
),
),
);
}返回脚手架(
extendBodyBehindAppBar:真,
appBar: 常量 AppBarWidget(
背景颜色:颜色。透明,
),
正文:堆栈(
孩子们: [
身体,
],
),
);
我希望你能对自己的项目有一些想法
好的,让我们进入视频主题
2. 活体人脸检测比在盒子里自拍更简单。
您可以在这些代码文件中看到示例计算公式。
_cameraService.cameraController?.startImageStream((image) async {
if (_cameraService.cameraController != null) {
如果(_detectingFaces)返回;
_detectingFaces = true;
尝试 {
等待_faceDetectorService.detectFacesFromImage(图像);
如果(_faceDetectorService.faces.isNotEmpty){
faceDetected = _faceDetectorService.faces[0];
如果(_checkIndex < 5){
开关(_myDetect[_checkIndex].action){
案例“请微笑”:
_smileCheck(人脸检测);
休息;
案例“左转”:
_leftCheck(faceDetected);
休息;
案例“右转”:
_rightCheck(人脸检测);
休息;
案例“眨眼”:
_eyeBlinkCheck(人脸检测);
休息;
案例“请中立”:
_neutralCheck(faceDetected);
休息;
默认:
_doneCheck();
}
} 别的 {
_cameraService.cameraController!.stopImageStream();
停止记录();
//AppToasts.showToast(context: context, title: "成功");
}
如果(_保存){
_mlService.setCurrentPrediction(image, faceDetected);
设置状态((){
_保存=假;
});
}
} 别的 {
设置状态((){
人脸检测 = null;
});
}
_detectingFaces = false;
} 抓住 (e) {
_detectingFaces = false;
}
}
});
bool _smileCheck(Face?faceDetected) {
布尔微笑=假;
if (faceDetected!.smilingProbability! > 0.80 && _checkIndex < 5) {
微笑=真;
}
微笑归来;
}
bool _rightCheck(Face?faceDetected) {
布尔图灵右 = 假;
if (faceDetected!.headEulerAngleY!> 15 && _checkIndex < 5) {
设置状态((){
// 你可以做你想做的
});
图灵右=真;
}
返回图灵权;
}
bool _eyeBlinkCheck(人脸?人脸检测){
bool eyeBlink = false;
if (faceDetected!.leftEyeOpenProbability! < 0.1 &&
faceDetected.rightEyeOpenProbability! < 0.1 &&
眨眼第一 == 真){
设置状态((){
// 你可以做你想做的
});
}
if (faceDetected.leftEyeOpenProbability! > 0.1 &&
faceDetected.rightEyeOpenProbability! > 0.1 &&
闭眼 == 真 &&
_checkIndex < 5) {
眨眼=真;
眨眼第一=真;
设置状态((){
// 你可以做你想做的
});
}
返回eyeBlink;
}
bool _leftCheck(Face?faceDetected) {
布尔转向左 = 假;
if (faceDetected!.headEulerAngleY! < -15 && _checkIndex < 5) {
设置状态((){
// 你可以做你想做的
});
向左转=真;
}
返回左转;
}
bool _neutralCheck(人脸?人脸检测){
布尔中性=假;
if (faceDetected!.smilingProbability! < 0.30 && _checkIndex < 5) {
设置状态((){
// 你可以做你想做的
});
中性=真;
}
回归中立;
}
对于视频录制,我们可以使用 ed_screen_recorder 库。
这个库可以导出mp4格式的视频
我真的很想解释更多细节。但是,我真的很喜欢使用方法而不是理论。我希望这篇文章可以帮助你度过挣扎的时光。
您可以通过个人聊天询问我的详细信息。
你也可以通过linkedIn给我发消息。
我的参考回购是
https://github.com/MCarlomagno/FaceRecognitionAuth.git
我的linkedIn 邮件是 [email protected]
感谢您花时间阅读我的文章。
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明