.NET MAUI Preview 10を試してみる その2 画像処理速度をFlutterと比較してみる

スポンサーリンク

概要

.NET MAUI Preview 10を試してみる その1の続きとして、今回は.NET MAUI(.NET 6)とFlutter(Dart)で画像処理にかかる時間を比較してみます。
理由は、現在公開中のFlutter製Android向けアプリReflowable Readerの動作が遅いため、改善方法を探しているためです。
どうもFlutterのimage PackageのcopyIntoが遅いような気がしているのですが…
C#(WPF)でMulti Backgroundsを作ってみて、個人的にはFlutterよりもC#の方が分かりやすい気がしたので、Reflowable ReaderもC#(.NET MAUI)で作れたらいいなぁと思っています。

画像処理内容と比較条件(環境)

Reflowable Readerの動作をイメージして、新画像イメージに元画像ファイルの一部を合成(コピー)する処理を行います。
画像は富岳三六景の有名な絵をWikipediaより借用し、元画像から少しずらして合成を繰り返す処理を行い、処理後は下の画像のようになります。
ソースコードは投稿の一番最後に記載します。

処理後の画像イメージ(本画像ファイルはサイズの都合上リサイズ済み)

.NET MAUIはSkiaSharpを、Flutterはimage Packageを利用して画像処理を行います。
SkiaSharpはプレリリース版で.NET MAUIでも使えるようになってきているようです。
時間は 1.画像ファイルの読み込み、2.新画像イメージ(白紙)の生成、3.合成、4.画像ファイルの保存 の時間を測定します。
どちらもWindows用にReleaseビルドして速度を計測し、3回実行して平均をとることにします。
(本来ならばReflowable ReaderのターゲットであるAndroid実機で計測するべきですが、まだ.NET MAUIでAndroid実機で動作するアプリを作成するところまで行けていないので…)

結果

結果はこのようになりました。

.NET MAUIFlutter
1.画像ファイルの読み込み126 ms827 ms
2.新画像イメージ(白紙)の生成0 ms16 ms
3.合成44 ms66 ms
4.画像ファイルの保存※3112 ms5243 ms

※画像ファイルの保存は同じPNG形式、同じ解像度でも圧縮率が違うのかサイズが異なるファイルとなったため参考
Flutterの方が遅いですが、気になっていた合成についてはそこまで大差がない感じでしょうか。(それでも.NET MAUIの1.5倍ですが)
copyIntoも思っていたより遅くはない感じですが…次はAndroid環境で試してみます。

プログラム(.NET MAUI/C#/.NET 6/SkiaSharp)

string stopWatch = "";
const int matrix = 50;

System.Diagnostics.Stopwatch sw = new();
sw.Start();

SKBitmap resourceBitmap;
SKBitmap resultBitmap;

using (System.IO.MemoryStream msImage = new(System.IO.File.ReadAllBytes(@"C:\画像.jpg")))
{
    resourceBitmap = SKBitmap.Decode(msImage);
}

stopWatch+="デコード完了:"+ sw.ElapsedMilliseconds.ToString()+ System.Environment.NewLine;

resultBitmap = new SKBitmap(resourceBitmap.Width + (int)Math.Ceiling((float)resourceBitmap.Width / matrix), resourceBitmap.Height + (int)Math.Ceiling((float)resourceBitmap.Height / matrix));

stopWatch += "resultBitmap作成完了:" + sw.ElapsedMilliseconds.ToString() + System.Environment.NewLine;

SKCanvas canvas = new(resultBitmap);
SKImage resourceImage = SKImage.FromBitmap(resourceBitmap);

//100x100ごとに分割し、10pxあけて貼り付けなおす
for (int x = 0; x < resourceBitmap.Width / matrix - 1; x++)
{
    for (int y = 0; y < resourceBitmap.Height / matrix - 1; y++)
    {
    canvas.DrawImage(resourceImage, new SKRect(matrix * x, matrix * y, matrix * x + matrix, matrix * y + matrix), new SKRect((matrix+10) * x, (matrix + 10) * y, (matrix + 10) * x + matrix, (matrix + 10) * y + matrix), null);
    }
}

stopWatch += "貼り付け完了:" + sw.ElapsedMilliseconds.ToString() + System.Environment.NewLine;

//保存
SKImage resultImage = SKImage.FromBitmap(resultBitmap);
using (var stream = File.Create(@"C:\保存画像.png"))
{
    var data = resultImage.Encode(SKEncodedImageFormat.Png, 100);
    data.SaveTo(stream);
}

stopWatch += "保存完了:" + sw.ElapsedMilliseconds.ToString() + System.Environment.NewLine;
CounterLabel.Text = stopWatch;

プログラム(Flutter/Dart/image package)

String stopWatchText = "";
const int matrix = 50;
Stopwatch stopWatch = Stopwatch();
stopWatch.start();

imgLib.Image? _sourceImage;
_sourceImage = imgLib.decodeImage(
    File("C:\\画像.jpg").readAsBytesSync());

stopWatchText += "デコート完了" + stopWatch.elapsedMilliseconds.toString() + "\n";

if (_sourceImage != null) {
    imgLib.Image? _resultImage = imgLib.Image(_sourceImage.width + (_sourceImage.width / matrix).ceil(),_sourceImage.height + (_sourceImage.height / matrix).ceil());

    stopWatchText += "_resultImage作成完了" + stopWatch.elapsedMilliseconds.toString() + "\n";

    for (int x = 0; x < _sourceImage.width / matrix - 1; x++) {
        for (int y = 0; y < _sourceImage.height / matrix - 1; y++) {
            imgLib.copyInto(_resultImage, _sourceImage,
                dstX: (matrix + 10) * x,
                dstY: (matrix + 10) * y,
                srcX: matrix * x,
                srcY: matrix * y,
                srcW: matrix,
                srcH: matrix);
        }
    }

    stopWatchText += "貼り付け完了" + stopWatch.elapsedMilliseconds.toString() + "\n";

    String imagePath = 'C:\\保存画像.png';
    File imageFile = File(imagePath);
    imageFile.writeAsBytesSync(imgLib.encodePng(_resultImage), flush: true);

    stopWatchText += "保存完了" + stopWatch.elapsedMilliseconds.toString() + "\n";
}
setState(() {_counter = stopWatchText;});

コメント

  1. […] 前回の続きとして、実際のAndroid環境で.NET MAUIとFlutterの画像処理速度を比較してみます。.NET MAUI Preview 10を試すのが中心ではなくなっていますが、そこは気にしない方向で… […]

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