Flutter图像处理插件processing_camera_image的使用

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

Flutter图像处理插件processing_camera_image的使用

processing_camera_image是一个用于在Flutter应用中处理来自相机插件(由Google提供)的YUV格式图像的插件。它支持Android和iOS平台,并提供了将图像转换为8位灰度像素的功能,适用于人脸检测等场景。

开始使用

前提条件

  • 该插件仅处理YUV类型的相机图像,请确保您使用的相机插件设置了正确的图像格式类型。
  • 插件支持导出每像素8位灰度图像,以便进行面部检测处理。

iOS注意事项

  • 在iOS上,除了processCameraImageToGray8Bit方法外,其他功能都可以正常使用。

示例代码

下面是一个完整的示例demo,展示了如何使用processing_camera_image插件来处理从相机捕获到的图像。

import 'dart:async' show Future;
import 'dart:typed_data';
import 'package:camera/camera.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:image/image.dart' as imglib;
import 'package:processing_camera_image/processing_camera_image.dart';
import 'package:rxdart/rxdart.dart';

final ProcessingCameraImage _processingCameraImage = ProcessingCameraImage();

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);
  
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key, required this.title}) : super(key: key);

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  late final CameraController _cameraController;
  late Future<void> _instanceInit;
  final pipe = BehaviorSubject<CameraImage?>.seeded(null);
  imglib.Image? currentImage;
  final stopwatch = Stopwatch();

  void _processinngImage(CameraImage? value) async {
    if (value != null) {
      stopwatch.start();
      currentImage = await compute(processImage, value);
      stopwatch.stop();
      print(stopwatch.elapsedMilliseconds); // Likely > 0.
      stopwatch.reset();
    }
  }

  @override
  void initState() {
    pipe.listen(_processinngImage);
    _instanceInit = initCamera();
    super.initState();
  }

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

  Future<void> initCamera() async {
    final cameras = await availableCameras();
    _cameraController = CameraController(cameras[0], ResolutionPreset.medium,
        imageFormatGroup: ImageFormatGroup.yuv420);
    await _cameraController.initialize();
    await _cameraController.startImageStream((image) {
      pipe.sink.add(image);
    });
  }

  static imglib.Image? processImage(CameraImage savedImage) {
    return _processingCameraImage.processCameraImageToRGB(
      bytesPerPixelPlan1: savedImage.planes[1].bytesPerPixel,
      bytesPerRowPlane0: savedImage.planes[0].bytesPerRow,
      bytesPerRowPlane1: savedImage.planes[1].bytesPerRow,
      height: savedImage.height,
      plane0: savedImage.planes[0].bytes,
      plane1: savedImage.planes[1].bytes,
      plane2: savedImage.planes[2].bytes,
      width: savedImage.width,
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      floatingActionButton: InkWell(
        child: Container(
          color: Colors.white.withOpacity(0.0),
          height: 36,
          width: 36,
          child: const Icon(Icons.photo_camera),
        ),
        onTap: () {
          Navigator.push(
              context,
              MaterialPageRoute(
                  builder: (context) => Scaffold(
                        body: Center(
                          child: currentImage != null
                              ? Image.memory(Uint8List.fromList(
                                  imglib.encodeJpg(currentImage!)))
                              : Container(),
                        ),
                      )));
        },
      ),
      body: Center(
        child: FutureBuilder<void>(
          future: _instanceInit,
          builder: (context, snapshot) {
            if (snapshot.connectionState == ConnectionState.done) {
              return AspectRatio(
                aspectRatio: 1 /
                    (_cameraController.value.previewSize?.aspectRatio ?? 4 / 3),
                child: CameraPreview(_cameraController),
              );
            }
            return const Center(
              child: CircularProgressIndicator(),
            );
          },
        ),
      ),
    );
  }
}

这个示例展示了如何初始化相机、设置图像流并处理接收到的YUV格式图像。通过点击浮动按钮,可以查看处理后的图像。请根据您的具体需求调整代码。


更多关于Flutter图像处理插件processing_camera_image的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter图像处理插件processing_camera_image的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,关于Flutter图像处理插件processing_camera_image的使用,这里提供一个简单的代码案例来展示其基本功能。这个插件通常用于从相机捕获图像并进行处理。请注意,实际使用中可能需要根据具体需求进行调整和扩展。

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

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

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

接下来是一个简单的Flutter应用示例,展示如何使用processing_camera_image插件从相机捕获图像并在屏幕上显示:

import 'package:flutter/material.dart';
import 'package:camera/camera.dart';
import 'package:processing_camera_image/processing_camera_image.dart';

List<CameraDescription> cameras;

Future<void> main() async {
  // 获取可用相机列表
  cameras = await availableCameras();
  runApp(MyApp());
}

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

class CameraApp extends StatefulWidget {
  @override
  _CameraAppState createState() => _CameraAppState();
}

class _CameraAppState extends State<CameraApp> {
  CameraController? _controller;
  XFile? _image;

  @override
  void initState() {
    super.initState();
    // 使用第一个相机
    _controller = CameraController(cameras[0], ResolutionPreset.medium);
    _controller!.initialize().then((_) {
      if (!mounted) {
        return;
      }
      setState(() {});
    });
  }

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

  Future<void> _takePicture() async {
    try {
      final XFile image = await _controller!.takePicture();
      setState(() {
        _image = image;
      });

      // 使用processing_camera_image插件处理图像
      processImage(_image!.path);
    } catch (e) {
      print(e);
    }
  }

  void processImage(String imagePath) {
    // 这里是一个简单的图像处理示例,实际中你可能会使用更复杂的处理逻辑
    ProcessingCameraImage.processImage(
      imagePath: imagePath,
      outputPath: '${(imagePath.split('/')).sublist(0, (imagePath.split('/')).length - 1).join('/')}/processed_image.jpg',
      options: ProcessingOptions(
        grayscale: true, // 转换为灰度图像
        // 可以添加更多处理选项,如旋转、缩放等
      ),
    ).then((result) {
      print('Image processing result: $result');
      // 这里可以显示处理后的图像或者进行其他操作
    }).catchError((error) {
      print('Image processing error: $error');
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Camera App'),
      ),
      body: _controller!.value.isInitialized
          ? Column(
              crossAxisAlignment: CrossAxisAlignment.center,
              children: <Widget>[
                Expanded(
                  child: CameraPreview(_controller!),
                ),
                SizedBox(height: 20),
                Center(
                  child: _image == null
                      ? Text('No image selected.')
                      : Image.file(File(_image!.path)),
                ),
                SizedBox(height: 20),
                FloatingActionButton(
                  onPressed: _takePicture,
                  tooltip: 'Capture Image',
                  child: Icon(Icons.camera_alt),
                ),
              ],
            )
          : Center(
              child: CircularProgressIndicator(),
            ),
    );
  }
}

在这个示例中,我们做了以下几件事:

  1. 使用camera插件从相机捕获图像。
  2. 将捕获的图像保存到本地存储。
  3. 使用processing_camera_image插件对捕获的图像进行处理(在这个例子中,我们简单地将图像转换为灰度)。
  4. 显示原始图像和处理后的图像(如果处理成功并返回了路径)。

请注意,processing_camera_image插件的具体API和处理功能可能会根据版本有所不同,因此请参考该插件的官方文档以获取最新和最准确的信息。此外,处理后的图像路径和显示逻辑可能需要根据实际需求进行调整。

回到顶部