Flutter图片流转换插件convert_native_img_stream的使用

发布于 1周前 作者 h691938207 来自 Flutter

Flutter图片流转换插件convert_native_img_stream的使用

简介

convert_native_img_stream 是一个用于将 ImageFormatGroup.nv21ImageFormatGroup.bgra8888 转换为 JPEG 文件的插件。这个插件主要适用于使用 ML Kit 或其他处理单平面原生格式(如 NV12 或 BGRA)帧的情况,并且你希望从 CameraImage 对象中保存处理后的帧。该插件可以帮助你将帧转换为内存或文件中的 JPEG 格式。

示例用法

示例代码

以下是一个完整的示例代码,展示了如何使用 convert_native_img_stream 插件从相机流中捕获帧并将其转换为 JPEG 图像。

import 'dart:io';
import 'package:camera/camera.dart';
import 'package:flutter/material.dart';
import 'package:convert_native_img_stream/convert_native_img_stream.dart';

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

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

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  final _convertNativeImgStreamPlugin = ConvertNativeImgStream();

  static List<CameraDescription> _cameras = [];
  CameraController? _controller;
  int _cameraIndex = -1;
  bool capture = false, converting = false;
  Uint8List? imageBytes;

  @override
  void initState() {
    super.initState();
    initializeCamera();
  }

  @override
  void dispose() async {
    super.dispose();
    await _controller?.stopImageStream();
    await _controller?.dispose();
    _controller = null;
  }

  void initializeCamera() async {
    if (_cameras.isEmpty) {
      _cameras = await availableCameras();
    }
    for (var i = 0; i < _cameras.length; i++) {
      if (_cameras[i].lensDirection == CameraLensDirection.back) {
        _cameraIndex = i;
        break;
      }
    }
    if (_cameraIndex != -1) {
      final camera = _cameras[_cameraIndex];
      _controller = CameraController(
        camera,
        // 主要解决的问题,将 NV12 流转换为 JPEG 格式
        ResolutionPreset.high,
        enableAudio: false,
        imageFormatGroup: Platform.isAndroid
            ? ImageFormatGroup.nv21
            : ImageFormatGroup.bgra8888,
      );
      _controller?.initialize().then((_) {
        _controller?.startImageStream((image) {
          if (capture) {
            setState(() {
              converting = true;
            });
            _controller?.stopImageStream();
            _controller?.pausePreview();
            _convertNativeImgStreamPlugin
                .convertImgToBytes(
                  image.planes.first.bytes,
                  image.width,
                  image.height,
                )
                .then((value) {
                  imageBytes = value;
                  converting = false;
                  setState(() {});
                });
          }
        });
        if (!mounted) {
          return;
        }
        setState(() {});
      });
    }
  }

  Widget _body() {
    if (_cameras.isEmpty) return Container();
    if (_controller == null) return Container();
    if (_controller?.value.isInitialized == false) return Container();
    if (converting) {
      return const Center(
        child: CircularProgressIndicator(),
      );
    }
    return Container(
      color: Colors.black,
      child: Stack(
        fit: StackFit.expand,
        children: <Widget>[
          if (imageBytes == null)
            Center(
              child: CameraPreview(
                _controller!,
              ),
            )
          else
            Container(
              color: Colors.white,
              child: Column(
                mainAxisAlignment: MainAxisAlignment.spaceAround,
                mainAxisSize: MainAxisSize.max,
                children: [
                  const Expanded(
                    flex: 10,
                    child: Center(
                      child: Text(
                        "Converted image",
                        style: TextStyle(fontWeight: FontWeight.bold, fontSize: 22),
                      ),
                    ),
                  ),
                  Expanded(flex: 90, child: Image.memory(imageBytes!, fit: BoxFit.cover)),
                ],
              ),
            ),
          if (imageBytes == null)
            Column(
              mainAxisAlignment: MainAxisAlignment.end,
              children: [
                ElevatedButton(
                  onPressed: () {
                    capture = true;
                  },
                  child: const Text("Pause & Convert Frame"),
                ),
              ],
            ),
        ],
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Plugin example app'),
        ),
        body: _body(),
      ),
    );
  }
}

代码说明

  1. 初始化相机

    • 使用 availableCameras() 获取可用的摄像头列表。
    • 选择后置摄像头(如果有)。
    • 初始化 CameraController 并设置图像格式组为 ImageFormatGroup.nv21(Android)或 ImageFormatGroup.bgra8888(iOS)。
  2. 开始图像流

    • 使用 startImageStream 方法开始从相机获取图像帧。
    • startImageStream 的回调中,检查是否需要捕获当前帧(通过 capture 变量控制)。
  3. 捕获和转换帧

    • 如果需要捕获当前帧,停止图像流并暂停预览。
    • 使用 convertNativeImgStreamPlugin.convertImgToBytes 方法将帧数据转换为 JPEG 格式的字节流。
    • 将转换后的字节流存储在 imageBytes 中,并更新 UI。
  4. 显示图像

    • 如果 imageBytes 不为空,显示转换后的图像。
    • 否则,显示相机预览。
  5. 按钮操作

    • 提供一个按钮,点击后设置 capturetrue,触发帧捕获和转换。

通过以上步骤,你可以轻松地从相机流中捕获帧并将其转换为 JPEG 图像。希望这个示例对你有所帮助!


更多关于Flutter图片流转换插件convert_native_img_stream的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter图片流转换插件convert_native_img_stream的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是如何在Flutter项目中使用convert_native_img_stream插件来处理图片流的示例代码。convert_native_img_stream插件允许你将原生平台(如Android或iOS)的图片流数据转换为Flutter可以处理的图像格式。

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

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

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

接下来,我们将编写一个示例,展示如何从原生平台接收图片流并将其显示在Flutter应用中。由于convert_native_img_stream插件的具体实现细节可能依赖于具体的平台(Android和iOS),这里我们假设你已经有了相应的原生代码来提供图片流。

Flutter代码部分

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

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Image Stream Example',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: ImageStreamPage(),
    );
  }
}

class ImageStreamPage extends StatefulWidget {
  @override
  _ImageStreamPageState createState() => _ImageStreamPageState();
}

class _ImageStreamPageState extends State<ImageStreamPage> {
  final NativeImageStreamConverter _converter = NativeImageStreamConverter();
  Uint8List? _imageBytes;

  @override
  void initState() {
    super.initState();
    // 假设你有一个方法从原生平台接收图片流
    _startReceivingImageStream();
  }

  void _startReceivingImageStream() {
    // 这里模拟从原生平台接收图片流
    // 在实际使用中,这部分代码将由原生平台代码触发
    Timer.periodic(Duration(seconds: 1), (timer) {
      // 模拟接收到的图片数据(在实际应用中,这里将是原生平台提供的图片数据)
      Uint8List imageData = Uint8List.fromList(List.generate(10000, (index) => index % 256));

      // 使用convert_native_img_stream插件转换图片数据
      _converter.convertNativeImageToUint8List(imageData).then((result) {
        setState(() {
          _imageBytes = result;
        });
      }).catchError((error) {
        print('Error converting image: $error');
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Image Stream Example'),
      ),
      body: Center(
        child: _imageBytes != null
            ? Image.memory(_imageBytes!)
            : CircularProgressIndicator(),
      ),
    );
  }
}

原生平台代码(示例)

由于convert_native_img_stream的具体原生实现细节可能会依赖于插件的版本和平台,这里仅提供伪代码思路。你需要根据插件的文档和示例来编写实际的原生代码。

Android部分(伪代码)

// 在你的Activity或Service中
public void sendImageStreamToFlutter() {
    // 获取图片数据(例如从相机预览回调中)
    byte[] imageData = getImageDataFromCamera();

    // 将图片数据发送到Flutter
    MethodChannel methodChannel = new MethodChannel(getFlutterEngine().getDartExecutor().getBinaryMessenger(), "your_channel_name");
    methodChannel.invokeMethod("receiveImageData", imageData);
}

iOS部分(伪代码)

// 在你的Swift或Objective-C代码中
func sendImageStreamToFlutter() {
    // 获取图片数据(例如从AVCaptureVideoDataOutputSampleBufferDelegate回调中)
    guard let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else { return }
    let imageData = convertPixelBufferToData(pixelBuffer)  // 自定义方法将CVPixelBuffer转换为Data

    // 将图片数据发送到Flutter
    let flutterEngine = (UIApplication.shared.delegate as! AppDelegate).flutterEngine
    let methodChannel = FlutterMethodChannel(name: "your_channel_name", binaryMessenger: flutterEngine.binaryMessenger)
    methodChannel.invokeMethod("receiveImageData", imageData)
}

在Flutter端,你需要监听从原生平台发送过来的图片数据,并调用convert_native_img_stream插件来处理这些数据。上面的Flutter代码示例中,我们使用了Timer.periodic来模拟接收图片流数据,但在实际应用中,你应该监听从原生平台发送过来的实际数据。

请注意,上述代码仅作为示例,并未包含完整的错误处理和资源管理逻辑。在实际开发中,请确保正确处理各种边缘情况和资源释放。

回到顶部