Dart イメージライブラリを使った画像処理

Flutter

Flutter で画像処理をおこなう場合、Dart の Image ライブラリは検討に値するかもしれません。この Dart 用に書かれた優れた画像処理ライブラリの基本的な使い方について解説します。

スポンサーリンク

Dart Image ライブラリとは?

さまざまなファイル形式で画像をロード、保存、操作する機能を提供する Dart ライブラリです。(ここまで公式からの機械翻訳)

CPU 処理なので GPU な画像処理ライブラリなどと比べると速度は劣りますが、基本的な画像処理は一通り揃っているため、大体の画像処理の要求には十分に応えてくれるでしょう。

どのような機能が搭載されているかの詳細は公式 API リファレンスを参照してみてください。

基本的な使い方

ライブラリのインポート

Image ライブラリを使う場合は、以下のライブラリをインポートします。

import 'package:image/image.dart' as imgLib;

// Uint8List などの型を扱う場合があるので以下もインポートすると良いです
import 'dart:typed_data';

Flutter で使う場合、Flutter の Image ウィジェットクラスと名前が重複するため、as imgLib などのように名前をつけてインポートするようにします。

新規作成

Image の新規作成は以下のようになります。

var width = 500;
var height = 500;

imgLib.Image image = imgLib.Image(width, height);

画像の読み込み

ファイルから画像を読み込む場合は、

imgLib.Image image = imgLib.decodeImage(File('ファイル名').readAsBytesSync());

プロジェクトのアセット画像を読み込む場合は、まず pubspec.yaml にアセット画像を定義して、

assets:
  - images/image.jpg

ソースコードで以下のように読み込みます。

import 'package:flutter/services.dart'; // rootBundle.を使うためインポート
import 'dart:typed_data'; // Uint8List型を使うためインポート

// アセット画像の読み込み
ByteData imageData = await rootBundle.load('images/image.jpg');
imgLib.Image image = imgLib.decodeImage(Uint8List.view(imageData.buffer));

クローンやコピー

Image ライブラリでは、関数に渡した画像データがそのまま上書き処理されるため、元画像のクローンやコピーを作成してから画像処理に渡すことになります。

// 画像のクローンを作成
imgLib.Image cloneImage = image.clone();

// リサイズされたコピーを作成
imgLib.Image resizeImage = imglib.copyResize(image, width: 500);

各種フィルター

Image ライブラリで使える各種フィルター(の一部)です。

// ビネット
imgLib.vignette(image);

// グレースケール
imgLib.grayscale(image);

// ガウスブラー
imgLib.gaussianBlur(image, 5);

// ピクセレート
imgLib.pixelate(image, 10);

// カラー補正
imgLib.adjustColor(image, 
  contrast: _contrast,
  saturation: _saturation,
  brightness: _brightness,
  exposure: _exposure
  gamma: _gamma,
  amount: _amount,
);

...(他にもありますが省略)

ドロー関連

ドロー関連も色々揃っています。

// 塗り潰し
imglib.fill(image, 0x00000000);

// テキストの描画
imgLib.drawString(image, imgLib.arial_48, 0, 0, 'my string');

// 画像の合成
imgLib.drawImage(image, image2);

...などなど

データ変換など

その他、Uint8List や List<int> を使う場合が多いので、変換などのメモです。

// Dart Image のバイトデータ
Uint8List bytes = image.getBytes()

// v = List<int> を Uint8List に変換
Uint8List bytes = Uint8List.fromList(v);

// v = List<int> を Dart Image に変換
imgLib.Image image = imageLib.Image.fromBytes(width, height, v);

Flutter ウィジェットでの画像表示

Flutter ウィジェットで画像を表示する場合は、Image.memory() にバイト配列を渡します。

// Dart Image を List<int> に変換
List<int> _imageBytes = imgLib.encodeJpg(image);

// Flutter の Image ウィジェットを以下のように書きます
Image.memory(_imageBytes),

サンプルコード

アセットの画像を読み込んで Dart Image のフィルターを実行するサンプルコードです。(アセットの画像を別途用意して画像のパスを変更した上でお試しください)

「フィルター」ボタンを押すと、ビネット/ガウスブラー/グレースケールの3つのフィルターを通した画像が表示されます。

import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; // rootBundle
import 'package:image/image.dart' as imgLib; // <- Dart Image Library
import 'dart:typed_data'; // Uint8List

void main() => runApp(MyApp());

// MyApp ウィジェットクラス
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData.light(),
      home: MyHomePage(title: 'Dart Image Library'),
    );
  }
}

// MyHomePage ウィジェットクラス
class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);
  final String title;
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

// MyHomePage ステートクラス
class _MyHomePageState extends State<MyHomePage> {
  imgLib.Image _image;
  List<int> _imageBytes;

  // アセットから画像を読み込む関数
  void loadAssetImage() async {
    ByteData imageData = await rootBundle.load('lib/len_std.jpg');
    _image = imgLib.decodeImage(Uint8List.view(imageData.buffer));
    _imageBytes = imgLib.encodeJpg(_image);
    setState((){});
  }

  // 初期化
  @override
  void initState() {
    // 初期ダミー画像作成
    _image = imgLib.Image(250, 250);
    _imageBytes = imgLib.encodeJpg(_image);

    // アセットから画像を読み込む
    loadAssetImage();

    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text('オリジナル画像'),
            SizedBox(width: 250, height: 250, child: Image.asset('lib/len_std.jpg')),

            SizedBox(height: 30),

            Text('フィルター画像'),
            SizedBox(width: 250, height: 250, child: Image.memory(_imageBytes)),

            RaisedButton(
              child: Text('フィルター'),
              onPressed: () {
                var image = _image.clone();
                // ----- フィルター処理テスト
                imgLib.vignette(image);
                imgLib.gaussianBlur(image, 5);
                imgLib.grayscale(image);
                // -----
                _imageBytes = imgLib.encodeJpg(image);
                setState((){});
              },
            ),
          ],
        ),
      ),
    );
  }
}

コメント

タイトルとURLをコピーしました