Flutter手势识别插件apple_vision_hand的使用

Flutter手势识别插件apple_vision_hand的使用

apple_vision_hand 是一个用于Flutter应用的手势识别插件,它利用了苹果公司的Vision框架来检测手部姿势。以下是如何使用该插件的详细步骤。

插件信息

apple_vision_hand 是一个基于苹果Vision框架的手势识别插件,用于在Flutter应用中实现手部姿势检测。

注意: 该插件并非由苹果公司赞助或维护。作者是一些希望为macOS开发类似Google ML Kit功能的开发者。

要求

MacOS

  • 最低osx部署目标版本: 12.0
  • Xcode 13 或更新版本
  • Swift 5
  • ML Kit仅支持64位架构(x86_64 和 arm64)

iOS

  • 最低ios部署目标版本: 14.0
  • Xcode 13 或更新版本
  • Swift 5
  • ML Kit仅支持64位架构(x86_64 和 arm64)

开始使用

首先,在你的项目中导入插件:

import 'package:apple_vision_hand/apple_vision_hand.dart';

然后,初始化插件并设置相机:

final GlobalKey cameraKey = GlobalKey(debugLabel: "cameraKey");
late AppleVisionHandController visionController;
InsertCamera camera = InsertCamera();
Size imageSize = const Size(640, 640 * 9 / 16);
String? deviceId;
bool loading = true;

List<HandData>? handData;
late double deviceWidth;
late double deviceHeight;

[@override](/user/override)
void initState() {
  camera.setupCameras().then((value) {
    setState(() {
      loading = false;
    });
    camera.startLiveFeed((InputImage i) {
      if (i.metadata?.size != null) {
        imageSize = i.metadata!.size;
      }
      if (mounted) {
        Uint8List? image = i.bytes;
        visionController.processImage(image!, imageSize).then((data) {
          handData = data;
          setState(() {});
        });
      }
    });
  });
  super.initState();
}

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

[@override](/user/override)
Widget build(BuildContext context) {
  deviceWidth = MediaQuery.of(context).size.width;
  deviceHeight = MediaQuery.of(context).size.height;
  return Stack(
    children: [
      SizedBox(
        width: imageSize.width,
        height: imageSize.height,
        child: loading ? loadingWidget() : CameraSetup(camera: camera, size: imageSize),
      ),
    ] + showPoints()
  );
}

List<Widget> showPoints() {
  if (handData == null || handData!.isEmpty) return [];
  List<Widget> widgets = [];
  Map<FingerJoint, Color> colors = {
    FingerJoint.thumbCMC: Colors.amber,
    FingerJoint.thumbIP: Colors.amber,
    FingerJoint.thumbMP: Colors.amber,
    FingerJoint.thumbTip: Colors.amber,

    FingerJoint.indexDIP: Colors.green,
    FingerJoint.indexMCP: Colors.green,
    FingerJoint.indexPIP: Colors.green,
    FingerJoint.indexTip: Colors.green,

    FingerJoint.middleDIP: Colors.purple,
    FingerJoint.middleMCP: Colors.purple,
    FingerJoint.middlePIP: Colors.purple,
    FingerJoint.middleTip: Colors.purple,

    FingerJoint.ringDIP: Colors.pink,
    FingerJoint.ringMCP: Colors.pink,
    FingerJoint.ringPIP: Colors.pink,
    FingerJoint.ringTip: Colors.pink,

    FingerJoint.littleDIP: Colors.cyanAccent,
    FingerJoint.littleMCP: Colors.cyanAccent,
    FingerJoint.littlePIP: Colors.cyanAccent,
    FingerJoint.littleTip: Colors.cyanAccent
  };
  for (int j = 0; j < handData!.length; j++) {
    for (int i = 0; i < handData![j].poses.length; i++) {
      if (handData![j].poses[i].confidence > 0.5) {
        widgets.add(
          Positioned(
            top: handData![j].poses[i].location.y,
            left: handData![j].poses[i].location.x,
            child: Container(
              width: 10,
              height: 10,
              decoration: BoxDecoration(
                color: colors[handData![j].poses[i].joint],
                borderRadius: BorderRadius.circular(5)
              ),
            )
          )
        );
      }
    }
  }
  return widgets;
}

Widget loadingWidget() {
  return Container(
    width: deviceWidth,
    height: deviceHeight,
    color: Theme.of(context).canvasColor,
    alignment: Alignment.center,
    child: const CircularProgressIndicator(color: Colors.blue)
  );
}

示例代码

以下是完整的示例代码,演示如何使用apple_vision_hand插件进行手势识别。

import 'package:apple_vision_hand/apple_vision_hand.dart';
import 'package:flutter/material.dart';
import '../camera/camera_insert.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'camera/input_image.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const VisionHand(),
    );
  }
}

class VisionHand extends StatefulWidget {
  const VisionHand({
    Key? key,
    this.onScanned
  }) : super(key: key);

  final Function(dynamic data)? onScanned;

  [@override](/user/override)
  _VisionHand createState() => _VisionHand();
}

class _VisionHand extends State<VisionHand> {
  final GlobalKey cameraKey = GlobalKey(debugLabel: "cameraKey");
  AppleVisionHandController visionController = AppleVisionHandController();
  InsertCamera camera = InsertCamera();
  Size imageSize = const Size(640, 640 * 9 / 16);
  String? deviceId;
  bool loading = true;

  List<HandData>? handData;
  late double deviceWidth;
  late double deviceHeight;

  [@override](/user/override)
  void initState() {
    camera.setupCameras().then((value) {
      setState(() {
        loading = false;
      });
      camera.startLiveFeed((InputImage i) {
        if (i.metadata?.size != null) {
          imageSize = i.metadata!.size;
        }
        if (mounted) {
          Uint8List? image = i.bytes;
          visionController.processImage(image!, imageSize).then((data) {
            handData = data;
            setState(() {});
          });
        }
      });
    });
    super.initState();
  }

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

  [@override](/user/override)
  Widget build(BuildContext context) {
    deviceWidth = MediaQuery.of(context).size.width;
    deviceHeight = MediaQuery.of(context).size.height;
    return Stack(
      children: [
        SizedBox(
          width: imageSize.width,
          height: imageSize.height,
          child: loading ? loadingWidget() : CameraSetup(camera: camera, size: imageSize),
        ),
      ] + showPoints()
    );
  }

  List<Widget> showPoints() {
    if (handData == null || handData!.isEmpty) return [];
    List<Widget> widgets = [];
    Map<FingerJoint, Color> colors = {
      FingerJoint.thumbCMC: Colors.amber,
      FingerJoint.thumbIP: Colors.amber,
      FingerJoint.thumbMP: Colors.amber,
      FingerJoint.thumbTip: Colors.amber,

      FingerJoint.indexDIP: Colors.green,
      FingerJoint.indexMCP: Colors.green,
      FingerJoint.indexPIP: Colors.green,
      FingerJoint.indexTip: Colors.green,

      FingerJoint.middleDIP: Colors.purple,
      FingerJoint.middleMCP: Colors.purple,
      FingerJoint.middlePIP: Colors.purple,
      FingerJoint.middleTip: Colors.purple,

      FingerJoint.ringDIP: Colors.pink,
      FingerJoint.ringMCP: Colors.pink,
      FingerJoint.ringPIP: Colors.pink,
      FingerJoint.ringTip: Colors.pink,

      FingerJoint.littleDIP: Colors.cyanAccent,
      FingerJoint.littleMCP: Colors.cyanAccent,
      FingerJoint.littlePIP: Colors.cyanAccent,
      FingerJoint.littleTip: Colors.cyanAccent
    };
    for (int j = 0; j < handData!.length; j++) {
      for (int i = 0; i < handData![j].poses.length; i++) {
        if (handData![j].poses[i].confidence > 0.5) {
          widgets.add(
            Positioned(
              top: handData![j].poses[i].location.y,
              left: handData![j].poses[i].location.x,
              child: Container(
                width: 10,
                height: 10,
                decoration: BoxDecoration(
                  color: colors[handData![j].poses[i].joint],
                  borderRadius: BorderRadius.circular(5)
                ),
              )
            )
          );
        }
      }
    }
    return widgets;
  }

  Widget loadingWidget() {
    return Container(
      width: deviceWidth,
      height: deviceHeight,
      color: Theme.of(context).canvasColor,
      alignment: Alignment.center,
      child: const CircularProgressIndicator(color: Colors.blue)
    );
  }
}

更多关于Flutter手势识别插件apple_vision_hand的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter手势识别插件apple_vision_hand的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,关于使用Flutter手势识别插件apple_vision_hand,这里是一个简单的代码案例,展示如何在Flutter应用中集成和使用这个插件来识别手势。

首先,你需要确保你的Flutter环境已经设置好,并且你的pubspec.yaml文件中已经添加了apple_vision_hand依赖。请注意,这个插件目前只支持iOS平台。

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

然后,运行flutter pub get来安装依赖。

接下来是一个简单的Flutter应用示例,展示如何使用apple_vision_hand插件:

import 'package:flutter/material.dart';
import 'package:apple_vision_hand/apple_vision_hand.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Hand Gesture Recognition',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: HandGestureRecognitionPage(),
    );
  }
}

class HandGestureRecognitionPage extends StatefulWidget {
  @override
  _HandGestureRecognitionPageState createState() => _HandGestureRecognitionPageState();
}

class _HandGestureRecognitionPageState extends State<HandGestureRecognitionPage> {
  late AppleVisionHandController _controller;
  late CameraImage? _currentImage;

  @override
  void initState() {
    super.initState();
    _controller = AppleVisionHandController();
    // 监听手势识别结果
    _controller.handLandmarksStream.listen((handLandmarks) {
      // 在这里处理识别到的手势,例如更新UI显示手势关键点
      print('Hand landmarks: $handLandmarks');
    });
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Hand Gesture Recognition'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'Capture your hand gesture to see the recognition result.',
            ),
            SizedBox(height: 20),
            // 这里你可以添加一个相机预览组件,用于捕获实时图像
            // 由于篇幅限制,这里简单用一个容器代替
            Container(
              height: 300,
              width: 300,
              color: Colors.grey[200],
              child: _currentImage == null
                  ? Center(child: Text('No image captured yet'))
                  : CustomPaint(
                      painter: HandGesturePainter(_controller.latestHandLandmarks),
                    ),
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () async {
          // 这里模拟捕获一张图像(在实际应用中,应从相机获取图像)
          // 由于apple_vision_hand插件目前不支持直接从相机获取图像进行处理,
          // 因此这里仅展示如何处理假设已经捕获的图像数据。
          // 你需要实现自己的图像捕获逻辑,并将CameraImage传递给_processImage方法。
          // _currentImage = await _captureImageFromCamera();
          // _processImage(_currentImage!);
        },
        tooltip: 'Capture Image',
        child: Icon(Icons.camera_alt),
      ),
    );
  }

  // 假设的图像捕获方法(实际需自行实现)
  // Future<CameraImage?> _captureImageFromCamera() async {
  //   // 实现图像捕获逻辑
  //   return null;
  // }

  // 图像处理方法(假设已经捕获到图像)
  // void _processImage(CameraImage image) async {
  //   try {
  //     // 将CameraImage转换为CVPixelBufferRef,然后传递给apple_vision_hand进行处理
  //     // 注意:这里需要实现CameraImage到CVPixelBufferRef的转换逻辑,
  //     // 由于apple_vision_hand插件未提供直接转换的方法,因此这里仅作为示例说明。
  //     // CVPixelBufferRef pixelBuffer = ...; // 转换得到的CVPixelBufferRef
  //     // _controller.processImage(pixelBuffer);
  //   } catch (e) {
  //     print('Error processing image: $e');
  //   }
  // }
}

// 自定义Painter用于在图像上绘制手势关键点
class HandGesturePainter extends CustomPainter {
  final List<List<Offset>>? handLandmarks;

  HandGesturePainter(this.handLandmarks);

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

    if (handLandmarks != null) {
      for (var landmark in handLandmarks!) {
        for (var point in landmark) {
          canvas.drawCircle(point, 5.0, paint);
        }
      }
    }
  }

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

注意

  1. 由于apple_vision_hand插件目前不支持直接从Flutter的camera插件获取的图像进行处理,上面的代码示例中图像捕获和处理部分被注释掉了。你需要实现自己的图像捕获逻辑,并将CameraImage转换为CVPixelBufferRef,然后传递给apple_vision_hand进行处理。
  2. HandGesturePainter类是一个简单的自定义Painter,用于在捕获的图像上绘制手势关键点。你需要根据实际的handLandmarks数据结构进行调整。
  3. 由于apple_vision_hand插件的API和使用方法可能会随着版本更新而变化,请参考插件的官方文档和示例代码以获取最新和最准确的信息。

希望这个示例能帮助你开始在Flutter应用中使用apple_vision_hand插件进行手势识别。

回到顶部