6
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Flutterでシャッター音なしで写真を撮る方法

Posted at

Flutterでアプリを作っています。裏で定期的に写真を撮ります。
iOSだと定期的に「パシャ」「パシャ」と音が鳴りかなりかっこ悪いです。
その解決方法を紹介します。

経緯

  • CameraController.takePicture() を使う。
    → Androidは無音ですがiOSで音が出るのでNG。
  • Swift の captureOutput() を使う。
    → 実際Swiftで使ってます。Flutter向けにパッケージ化するのが面倒なのでNG。
  • CameraController.startVideoRecording() で1秒録画してサムネイルを作る。
    → 結構試しました。10回に1回くらい失敗するのでNG。
  • CameraController.startImageStream()を使う方法。
    → 今回の方法です。

startImageStream() を使う

CameraController.startImageStream() を呼び出すと最新のカメラ画像を常に取得します。

  CameraImage? _cImage;

  cameraController.startImageStream((CameraImage cameraImage) async {
    _cImage = cameraImage; // ←最新のカメラ画像を取得し続ける
  });

問題点と改善点

  1. 処理が重いです。カメラサイズを240pや480pにすると改善します。
  2. 使うときだけ起動します。起動に400ミリ秒ほどかかります。

どちらの方法も長所と短所があります。
私の目的では使うときだけ起動する方法がよいです。

ソースコード

import 'package:camera/camera.dart';
import 'package:image/image.dart' as imglib;
import 'dart:async';

  // 実行例
  func() async {
    cameraController.startImageStream((CameraImage cameraImage) async {
      controller.stopImageStream(); // 1回で停止
      imglib.Image? img = await _toImage(controller, cameraImage); // 変換
    });
  }

  // CameraImageからImageに変換
  static Future<imglib.Image?> _toImage(CameraController? controller, CameraImage? cameraImage) async {
    if(controller==null || cameraImage==null) {
      return null;
    }
    imglib.Image? img;
    if (cameraImage.format.group == ImageFormatGroup.yuv420) {
      img = await _fromYuv(cameraImage);
      if(img!=null){
        int angle = controller.description.sensorOrientation;
        if(angle>0)
          img = imglib.copyRotate(img, angle); // 回転
      }
    } else if (cameraImage.format.group == ImageFormatGroup.bgra8888) {
      img = _fromRgb(cameraImage);
    }
    return img;
  }

  // YUVからRGBに変換
  static Future<imglib.Image?> _fromYuv(CameraImage? image) async {
    if(image==null)
      return null;
    final int width = image.width;
    final int height = image.height;

    if(image.planes.length<3) {
      print('err _fromYuv() planes.length=${image.planes.length}');
      return null;
    } else if(image.planes[1].bytesPerPixel==null) {
      print('err _fromYuv() planes[1].bytesPerPixel=null');
      return null;
    } else if(image.planes[0].bytes.length<width*height) {
      print('err _fromYuv() planes[0].bytes.length=${image.planes[0].bytes.length}');
      return null;
    }

    try {
      imglib.Image img = imglib.Image(width, height);
      final int perRow = image.planes[1].bytesPerRow;
      final int perPixel = image.planes[1].bytesPerPixel!;

      for(int ay=0; ay < height; ay++) {
        for(int ax=0; ax < width; ax++) {
          final int index = ax + (ay * width);
          final int uvIndex = perPixel * (ax / 2).floor() + perRow * (ay / 2).floor();
          final y = image.planes[0].bytes[index];
          final u = image.planes[1].bytes[uvIndex];
          final v = image.planes[2].bytes[uvIndex];
          int r = (y + v * 1436 / 1024 - 179).round().clamp(0, 255);
          int g = (y - u * 46549 / 131072 + 44 - v * 93604 / 131072 + 91).round().clamp(0, 255);
          int b = (y + u * 1814 / 1024 - 227).round().clamp(0, 255);
          img.data[index] = (0xFF << 24) | (b << 16) | (g << 8) | r;
        }
      }
      return img;
    } catch (e) {
      print("err _fromYuv() " + e.toString());
    }
    return null;
  }

  static imglib.Image _fromRgb(CameraImage image) {
    return imglib.Image.fromBytes(
      image.width, image.height,
      image.planes[0].bytes,
      format:imglib.Format.bgra,
    );
  }

解説

  • YUVをRGBに変換するのがポイントです。
  • 1回だけ実行して終了します。
  • 回転も考慮します。
  • 失敗するときはCameraControllerのimageFormatGroupをyuv420をbgra8888にして試してください。

まとめ

シャッター音が出ない方法を記事にしました。ネットで shutter sound で検索すると色々と出てきます。みんなも困ってるみたいです。
FlutterではAndroidでOKでもiOSでNGって場合があります。それでもSwiftとKotlinでやるよりもはるかに便利です。Flutterが流行るようにこれからも記事を書こう思います。

私はカメラと機械学習を組み合わせたアプリを作ろとしています。カメラアプリは今までなかったビジネス領域ですし、かなりの可能性を感じています。同じ目的を持っている人がいたら、是非一緒にやりましょう。

6
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?