Flutter姿态检测插件learning_pose_detection的使用

Flutter姿态检测插件learning_pose_detection的使用

ML姿态检测

ML姿态检测是一个轻量且多功能的解决方案,用于在Flutter应用中实时从连续视频或静态图像中检测用户的身体姿势。

一个姿态描述了某一时刻身体的位置,包括一系列骨骼关键点。这些关键点对应于不同的身体部位,如肩膀和臀部。关键点之间的相对位置可以用来区分不同的姿态。它会产生一个包含33个点的全身骨骼匹配,其中包括面部关键点(耳朵、眼睛、嘴巴和鼻子)以及手和脚上的点。

ML姿态检测不需要专门的设备或机器学习专业知识就可以获得很好的效果。通过这项技术,开发者只需几行代码就可以为用户提供独一无二的体验。

为了检测姿态,用户的面部必须出现在画面中。姿态检测在主体整个身体都可见时效果最佳,但也可以检测到部分身体的姿态。在这种情况下,未识别的关键点会被分配到图像外的坐标。

开始使用

在你的Flutter项目中添加依赖:

$ flutter pub add learning_pose_detection

或者在pubspec.yaml文件中添加以下内容:

dependencies:
  learning_pose_detection: ^0.0.1

然后运行 flutter pub get

使用方法

首先导入learning_pose_detection库:

import 'package:learning_pose_detection/learning_pose_detection.dart';

输入图像

与其他ML视觉插件一样,输入图像需要作为InputImage实例。InputImage属于learning_input_image包的一部分。

你可以使用来自learning_input_image包的InputCameraView小部件作为默认实现,将相机或存储中的图像(或图像流)处理为InputImage格式。但是,如果你想创建自己的自定义实现,可以深入了解InputCameraView代码。

以下是使用InputCameraView获取InputImage进行姿态检测的示例:

import 'package:learning_input_image/learning_input_image.dart';

InputCameraView(
  title: 'Pose Detection',
  onImage: (InputImage image) {
    // 现在我们可以将输入图像传递给姿态检测器
  },
)

姿态检测

在获取到InputImage之后,我们可以通过调用PoseDetector实例的detect方法来开始检测用户的姿态。

PoseDetector detector = PoseDetector();
Pose? pose = await detector.detect(image);

输出

姿态检测过程的输出是一个Pose对象,该对象包含以下信息:

Map<PoseLandmarkType, PoseLandmark> landmarks // 表示关键点列表的Map对象

每个PoseLandmark包含以下数据:

PoseLandmarkType type // 检测到的关键点类型
Offset position // 检测到关键点的x, y位置
double inFrameLikelihood // 检测到的关键点位于图像内的概率

以下是PoseLandmarkType值的列表:

LEFT_EYE
LEFT_EYE_INNER
LEFT_EYE_OUTER
RIGHT_EYE
RIGHT_EYE_INNER
RIGHT_EYE_OUTER
LEFT_EAR
RIGHT_EAR
NOSE
LEFT_MOUTH
RIGHT_MOUTH
LEFT_SHOULDER
RIGHT_SHOULDER
LEFT_ELBOW
RIGHT_ELBOW
LEFT_WRIST
RIGHT_WRIST
LEFT_THUMB
RIGHT_THUMB
LEFT_INDEX
RIGHT_INDEX
LEFT_PINKY
RIGHT_PINKY
LEFT_HIP
RIGHT_HIP
LEFT_KNEE
RIGHT_KNEE
LEFT_ANKLE
RIGHT_ANKLE
LEFT_HEEL
RIGHT_HEEL
LEFT_FOOT_INDEX
RIGHT_FOOT_INDEX

姿态绘制

为了方便地从Pose对象绘制到屏幕,我们提供了一个PoseOverlay,你可以将其传递给InputCameraViewoverlay参数。更多关于如何使用这种绘制的信息,你可以查看这里的工作示例代码

PoseOverlay(
  size: size,
  originalSize: originalSize,
  rotation: rotation,
  pose: pose,
)

释放资源

确保在不再使用PoseDetector时释放资源:

detector.dispose();

示例项目

你可以在这里查看示例项目。

示例代码

以下是完整的示例代码:

import 'package:flutter/material.dart';
import 'package:learning_pose_detection/learning_pose_detection.dart';
import 'package:learning_input_image/learning_input_image.dart';
import 'package:provider/provider.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primarySwatch: Colors.lightBlue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
        primaryTextTheme: TextTheme(headline6: TextStyle(color: Colors.white)),
      ),
      home: ChangeNotifierProvider(
        create: (_) => PoseDetectionState(),
        child: PoseDetectionPage(),
      ),
    );
  }
}

class PoseDetectionPage extends StatefulWidget {
  [@override](/user/override)
  _PoseDetectionPageState createState() => _PoseDetectionPageState();
}

class _PoseDetectionPageState extends State<PoseDetectionPage> {
  PoseDetectionState get state => Provider.of(context, listen: false);
  PoseDetector _detector = PoseDetector(isStream: false);

  [@override](/user/override)
  void dispose() {
    _detector.dispose();
    super.dispose();
  }

  Future<void> _detectPose(InputImage image) async {
    if (state.isNotProcessing) {
      state.startProcessing();
      state.image = image;
      state.data = await _detector.detect(image);
      state.stopProcessing();
    }
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return InputCameraView(
      cameraDefault: InputCameraType.rear,
      title: 'Pose Detection',
      onImage: _detectPose,
      overlay: Consumer<PoseDetectionState>(
        builder: (_, state, __) {
          if (state.isEmpty) {
            return Container();
          }

          Size originalSize = state.size!;
          Size size = MediaQuery.of(context).size;

          // 如果图像源来自图库
          // 图像显示大小被缩放为360x360,并保留宽高比
          if (state.notFromLive) {
            if (originalSize.aspectRatio > 1) {
              size = Size(360.0, 360.0 / originalSize.aspectRatio);
            } else {
              size = Size(360.0 * originalSize.aspectRatio, 360.0);
            }
          }

          return PoseOverlay(
            size: size,
            originalSize: originalSize,
            rotation: state.rotation,
            pose: state.data!,
          );
        },
      ),
    );
  }
}

class PoseDetectionState extends ChangeNotifier {
  InputImage? _image;
  Pose? _data;
  bool _isProcessing = false;

  InputImage? get image => _image;
  Pose? get data => _data;

  String? get type => _image?.type;
  InputImageRotation? get rotation => _image?.metadata?.rotation;
  Size? get size => _image?.metadata?.size;

  bool get isNotProcessing => !_isProcessing;
  bool get isEmpty => _data == null;
  bool get isFromLive => type == 'bytes';
  bool get notFromLive => !isFromLive;

  void startProcessing() {
    _isProcessing = true;
    notifyListeners();
  }

  void stopProcessing() {
    _isProcessing = false;
    notifyListeners();
  }

  set image(InputImage? image) {
    _image = image;

    if (notFromLive) {
      _data = null;
    }
    notifyListeners();
  }

  set data(Pose? data) {
    _data = data;
    notifyListeners();
  }
}

更多关于Flutter姿态检测插件learning_pose_detection的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter姿态检测插件learning_pose_detection的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是如何在Flutter项目中使用learning_pose_detection插件进行姿态检测的示例代码。请注意,learning_pose_detection插件的具体API和功能可能会随时间变化,因此请参考最新的官方文档以确保准确性。

首先,确保你已经在pubspec.yaml文件中添加了learning_pose_detection依赖:

dependencies:
  flutter:
    sdk: flutter
  learning_pose_detection: ^最新版本号  # 替换为实际的最新版本号

然后,运行flutter pub get以获取依赖。

接下来,在你的Flutter项目中,你可以按照以下步骤实现姿态检测:

  1. 导入必要的包
import 'package:flutter/material.dart';
import 'package:learning_pose_detection/learning_pose_detection.dart';
  1. 初始化姿态检测插件

在你的主页面或需要检测姿态的页面中,初始化姿态检测插件。通常,这需要在State类中完成。

class PoseDetectionPage extends StatefulWidget {
  @override
  _PoseDetectionPageState createState() => _PoseDetectionPageState();
}

class _PoseDetectionPageState extends State<PoseDetectionPage> {
  late PoseDetector poseDetector;

  @override
  void initState() {
    super.initState();
    // 初始化PoseDetector,可能需要传入一些配置参数,具体参考插件文档
    poseDetector = PoseDetector();
    // 加载模型(如果插件需要)
    poseDetector.loadModel().then((value) {
      print("Model loaded successfully");
    }).catchError((error) {
      print("Failed to load model: $error");
    });
  }

  @override
  void dispose() {
    poseDetector.dispose();  // 释放资源
    super.dispose();
  }

  // ... 其他方法
}
  1. 实现实时姿态检测

你可以使用CameraController从相机获取实时图像,并将其传递给PoseDetector进行处理。

class _PoseDetectionPageState extends State<PoseDetectionPage> {
  late PoseDetector poseDetector;
  late CameraController cameraController;
  final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();

  @override
  void initState() {
    super.initState();
    poseDetector = PoseDetector();
    poseDetector.loadModel().then((value) {
      print("Model loaded successfully");
      _initCamera();
    }).catchError((error) {
      print("Failed to load model: $error");
      _scaffoldKey.currentState?.showSnackBar(SnackBar(content: Text("Failed to load model")));
    });
  }

  Future<void> _initCamera() async {
    cameraController = CameraController(
      // 选择后置或前置摄像头,这里选择后置
      cameras[0],
      ResolutionPreset.high,
    );

    cameraController.initialize().then((_) {
      if (!mounted) return;
      setState(() {});
      cameraController.startImageStream((CameraImage image) async {
        // 将CameraImage转换为Uint8List
        List<int>? imgBytes = await _convertCameraImageToUint8List(image);
        if (imgBytes != null) {
          // 进行姿态检测
          PoseResult? result = await poseDetector.detectPose(imgBytes);
          // 处理检测结果
          setState(() {
            // 例如,可以在这里更新UI以显示检测到的关键点
          });
        }
      });
    }).catchError((error) {
      print("Error initializing camera: $error");
      _scaffoldKey.currentState?.showSnackBar(SnackBar(content: Text("Error initializing camera")));
    });
  }

  Future<List<int>>? _convertCameraImageToUint8List(CameraImage image) async {
    // 这里需要实现将CameraImage转换为Uint8List的逻辑
    // 通常涉及将YUV420_888格式的图像数据转换为RGB或其他格式
    // 由于这部分代码可能比较复杂,这里省略具体实现,请参考相关文档或示例
    // 返回Uint8List格式的图像数据
    return null;  // 替换为实际的转换结果
  }

  @override
  void dispose() {
    cameraController.dispose();
    poseDetector.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      key: _scaffoldKey,
      appBar: AppBar(title: Text("Pose Detection")),
      body: cameraController.value.isInitialized
          ? AspectRatio(
              aspectRatio: cameraController.value.aspectRatio,
              child: CameraPreview(cameraController),
            )
          : Container(),
      // ... 其他UI组件,例如显示检测结果的组件
    );
  }
}
  1. 显示检测结果

setState回调中,你可以根据PoseResult更新UI以显示检测到的姿态关键点。例如,你可以使用CustomPaintCanvas在相机预览上绘制关键点。

// 假设PoseResult包含关键点的列表keypoints
List<Offset> keyPointsOnScreen = [];

// 在setState回调中
keyPointsOnScreen = result.keypoints.map((keypoint) {
  // 将关键点从图像坐标系转换为屏幕坐标系
  // 这通常涉及缩放和平移变换,具体取决于相机预览的尺寸和位置
  return Offset(/* 计算后的x, y值 */);
}).toList();

// 在CameraPreview上绘制关键点
@override
Widget build(BuildContext context) {
  return Scaffold(
    // ...
    body: Stack(
      children: [
        AspectRatio(
          // ...
          child: CameraPreview(cameraController),
        ),
        if (keyPointsOnScreen.isNotEmpty)
          CustomPaint(
            painter: PosePainter(keyPointsOnScreen),
          ),
      ],
    ),
    // ...
  );
}

class PosePainter extends CustomPainter {
  final List<Offset> keyPoints;

  PosePainter(this.keyPoints);

  @override
  void paint(Canvas canvas, Size size) {
    final paint = Paint()
      ..color = Colors.red
      ..style = PaintingStyle.fill
      ..strokeWidth = 2.0;

    for (final keyPoint in keyPoints) {
      canvas.drawCircle(keyPoint, 5.0, paint);
    }
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    return oldDelegate != this;
  }
}

请注意,上述代码是一个简化的示例,省略了一些细节,如将CameraImage转换为Uint8List的具体实现,以及将关键点从图像坐标系转换为屏幕坐标系的逻辑。这些部分需要根据你的具体需求和相机预览的配置来实现。

此外,learning_pose_detection插件的具体API和配置可能有所不同,请参考插件的官方文档和示例代码以获取最新和最准确的信息。

回到顶部