Flutter自拍人像分割插件learning_selfie_segmentation的使用

Flutter自拍人像分割插件 learning_selfie_segmentation 的使用

ML Selfie Segmentation 插件为开发者提供了一种简单的方式来使用 ML Kit 在 Flutter 中进行自拍照的人像分割。通过该插件,可以轻松地将背景与用户分离,并专注于重要的部分,如添加酷炫的特效或插入有趣的背景环境。

功能简介

  • 输入图像:插件接受一个 InputImage 对象作为输入。
  • 输出掩码:根据输入图像生成一个掩码,每个像素的值范围在 [0.0, 1.0] 之间,数值越接近 1.0 表示该像素属于前景(即人)的概率越高。
  • 实时视频处理:支持静态图片和实时视频流场景,在视频流中会利用前几帧的结果来平滑分割效果。

开始使用

添加依赖

首先,在你的 pubspec.yaml 文件中添加对 learning_selfie_segmentation 的依赖:

dependencies:
  learning_selfie_segmentation: ^0.0.2

然后运行以下命令获取包:

flutter pub get

导入包

在 Dart 文件中导入所需的包:

import 'package:learning_selfie_segmentation/learning_selfie_segmentation.dart';
import 'package:learning_input_image/learning_input_image.dart';

示例 Demo

下面是一个完整的示例代码,展示了如何使用 learning_selfie_segmentation 插件进行自拍人像分割。

import 'dart:io';

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

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

class MyApp extends StatelessWidget {
  @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: (_) => SelfieSegmentationState(),
        child: SelfieSegmentationPage(),
      ),
    );
  }
}

class SelfieSegmentationPage extends StatefulWidget {
  @override
  _SelfieSegmentationPageState createState() => _SelfieSegmentationPageState();
}

class _SelfieSegmentationPageState extends State<SelfieSegmentationPage> {
  SelfieSegmentationState get state => Provider.of(context, listen: false);

  SelfieSegmenter _segmenter = SelfieSegmenter(
    isStream: true,
    enableRawSizeMask: false,
  );

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

  Future<void> _process(InputImage image) async {
    if (state.isNotProcessing) {
      state.startProcessing();
      state.isFromLive = image.type == 'bytes';

      if (!state.isFromLive) {
        ImageProperties properties =
            await FlutterNativeImage.getImageProperties(image.path!);

        double aspectRatio = properties.width! / properties.height!;
        int targetWidth = aspectRatio > 1.0 ? 360 : (360 * aspectRatio).round();
        int targetHeight =
            aspectRatio > 1.0 ? (360 / aspectRatio).round() : 360;

        File scaledImage = await FlutterNativeImage.compressImage(
          image.path!,
          quality: 90,
          targetWidth: aspectRatio > 1.0 ? 360 : (360 * aspectRatio).round(),
          targetHeight: aspectRatio > 1.0 ? (360 / aspectRatio).round() : 360,
        );

        image = InputImage.fromFile(
          scaledImage,
          metadata: InputImageData(
            size: Size(targetWidth.toDouble(), targetHeight.toDouble()),
            rotation: image.metadata?.rotation ?? InputImageRotation.ROTATION_0,
          ),
        );
      }

      state.image = image;
      SegmentationMask? mask = await _segmenter.process(image);
      state.data = mask;
      state.stopProcessing();
    }
  }

  @override
  Widget build(BuildContext context) {
    return InputCameraView(
      mode: InputCameraMode.gallery,
      cameraDefault: InputCameraType.rear,
      title: 'Selfie Segmentation',
      onImage: _process,
      overlay: Consumer<SelfieSegmentationState>(
        builder: (_, state, __) {
          if (state.isEmpty) {
            return Container();
          }

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

          return SegmentationOverlay(
            size: size,
            originalSize: originalSize,
            rotation: state.rotation,
            mask: state.data!,
          );
        },
      ),
    );
  }
}

class SelfieSegmentationState extends ChangeNotifier {
  InputImage? _image;
  SegmentationMask? _data;
  bool _isProcessing = false;
  bool _isFromLive = false;

  InputImage? get image => _image;
  SegmentationMask? 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 => _isFromLive;
  bool get notFromLive => !isFromLive;

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

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

  set isFromLive(bool isFromLive) {
    _isFromLive = isFromLive;
    notifyListeners();
  }

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

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

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

关键点说明

输入图像

输入图像需要转换为 InputImage 格式。你可以使用 learning_input_image 包中的 InputCameraView 小部件来从相机或存储中获取图像并将其转换为 InputImage

自拍分割

创建 SelfieSegmenter 实例后,可以通过调用 process 方法处理输入图像:

SelfieSegmenter segmenter = SelfieSegmenter(isStream: true, enableRawSizeMask: false);
SegmentationMask? mask = await segmenter.process(image);

输出结果

处理后的输出是一个 SegmentationMask 对象,包含以下数据:

  • width: 分割掩码的宽度
  • height: 分割掩码的高度
  • confidences: 每个像素属于前景的置信度列表

分割掩码绘制

为了方便绘制分割掩码,提供了 SegmentationOverlay 小部件,可以直接传递给 InputCameraViewoverlay 参数。

资源清理

记得在不再使用时释放资源:

segmenter.dispose();

更多关于Flutter自拍人像分割插件learning_selfie_segmentation的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter自拍人像分割插件learning_selfie_segmentation的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是如何在Flutter项目中使用learning_selfie_segmentation插件来进行自拍人像分割的示例代码。这个插件通常用于实现人像与背景的分离,以便进行背景替换或其他图像处理任务。

首先,你需要在你的pubspec.yaml文件中添加依赖项:

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

然后运行flutter pub get来获取依赖项。

接下来,你可以在你的Flutter项目中编写代码来使用这个插件。以下是一个基本的示例,展示了如何拍摄照片、应用人像分割,并显示分割后的结果。

import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:learning_selfie_segmentation/learning_selfie_segmentation.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: SelfieSegmentationScreen(),
    );
  }
}

class SelfieSegmentationScreen extends StatefulWidget {
  @override
  _SelfieSegmentationScreenState createState() => _SelfieSegmentationScreenState();
}

class _SelfieSegmentationScreenState extends State<SelfieSegmentationScreen> {
  final ImagePicker _picker = ImagePicker();
  File? _imageFile;
  File? _segmentedImageFile;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Selfie Segmentation Demo'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          children: [
            _imageFile == null
                ? Text('No image selected.')
                : Image.file(_imageFile!),
            SizedBox(height: 20),
            _segmentedImageFile == null
                ? Text('Segmentation result will be shown here.')
                : Image.file(_segmentedImageFile!),
            SizedBox(height: 20),
            ElevatedButton(
              onPressed: _pickImage,
              child: Text('Pick Image'),
            ),
            SizedBox(height: 20),
            ElevatedButton(
              onPressed: _segmentImage,
              child: Text('Segment Image'),
              isEnabled: _imageFile != null,
            ),
          ],
        ),
      ),
    );
  }

  Future<void> _pickImage() async {
    final pickedFile = await _picker.pickImage(source: ImageSource.camera);

    if (pickedFile != null) {
      setState(() {
        _imageFile = File(pickedFile.path);
        _segmentedImageFile = null;
      });
    }
  }

  Future<void> _segmentImage() async {
    if (_imageFile == null) return;

    final segmentor = SelfieSegmentation();
    final result = await segmentor.segmentImage(_imageFile!.path);

    if (result != null) {
      setState(() {
        _segmentedImageFile = File(result.path);
      });
    } else {
      // Handle error
      print('Segmentation failed.');
    }
  }
}

在这个示例中,我们使用了image_picker插件来拍摄或选择照片。请注意,learning_selfie_segmentation插件的API可能会根据版本有所变化,因此请参考其官方文档以获取最新的API信息。

请注意,由于learning_selfie_segmentation插件可能需要访问设备摄像头和存储,因此你需要在AndroidManifest.xmlInfo.plist文件中添加相应的权限。

对于Android,你需要在AndroidManifest.xml中添加:

<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

对于iOS,你需要在Info.plist中添加:

<key>NSCameraUsageDescription</key>
<string>App needs access to the camera to take photos.</string>
<key>NSPhotoLibraryAddUsageDescription</key>
<string>App needs access to the photo library to save photos.</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>App needs access to the photo library to pick photos.</string>

请确保在实际项目中遵循最新的权限管理和隐私政策要求。

回到顶部