search
LoginSignup
3

posted at

.NETCoreでマルチプラットフォームを対象にした画像処理

はじめに

画像の縮小やグレースケールなどの画像の変換処理をC#で行う場合、System.Drawing名前空間のクラスを使うことが多かったと思います。

.NET6以降でこれらのメソッドを使う場合、Visual Studioでは下記のようなWindows環境以外では問題があるという警告が表示されます。
image.png

警告	CA1416	この呼び出しサイトはすべてのプラットフォームで到達可能です。'Bitmap' は 'windows' でのみサポートされています。
警告	CA1416	この呼び出しサイトはすべてのプラットフォームで到達可能です。'Image.FromStream(Stream)' は 'windows' でのみサポートされています。

これは、System.Drawing.CommonがWindows以外で依存しているlibgdiplusというネイティブモジュールの品質が問題視され、.NET 6.0以降ではサポートされなくなったためです。

今回は、System.Drawing.Commonと、↑のページで代替えとして紹介されている下記2つのライブラリを使って画像の拡大処理の例を見ていきます(Microsoft.Maui.Graphicsはうまく動かせなかったので今回はスキップ)。

  • System.Drawing.Common
  • ImageSharp
  • SkiaSharp

System.Drawing.Common

非推奨だけれど大人の事情などでどうしても使いたいという場合は、次の手順で利用できる。

System.Drawing.Commonとlibgdiplusをインストールし、

$ dotnet add package System.Drawing.Common
$ sudo apt install libgdiplus

.runtimeconfig.json用のテンプレートを作り

runtimeconfig.template.json
{
    "configProperties": {
       "System.Drawing.EnableUnixSupport": true
    }
}

プロジェクトファイルでビルド時に.runtimeconfig.jsonを生成するように構成する。

csproj
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net6.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
+    <GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="System.Drawing.Common" Version="6.0.0" />
  </ItemGroup>
</Project>

あとはお好みで警告を黙らせてあげれば実行できます。

Program.cs
using System.Drawing;

public static void Resize(string input, string output)
{
    var imageData = File.OpenRead(input);
#pragma warning disable CA1416
    var resized = new Bitmap(Image.FromStream(imageData), new Size(1280, 720));
    resized.Save(output);
#pragma warning restore
}

ImageSharp

.NET ネイティブな画像処理ライブラリです。

追加のネイティブモジュールを利用しないので扱いやすいし、コードも直感的でわかりやすいのですが、ライセンス形態が独自になっていて、100万ドル以上の売り上げがある場合は有償ライセンスが必要になるので注意してください。

利用する場合はNuGetで SixLabors.ImageSharp を追加し

dotnet add package SixLabors.ImageSharp

Image.LoadAsyncした後に画像を操作して保存してあげればよいです。

Program.cs
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Processing;

public static async Task ResizeAsync(string input, string output)
{
    using var image = await Image.LoadAsync(input);

    image.Mutate(x => x
        .Resize(1280, 720));
    await image.SaveAsync(output);
}

SkiaSharp

SkiaSharpはMonoプロジェクトが管理する画像ライブラリで、Googleが提供するネイティブモジュールであるSkiaのラッパーとして提供されています。

Linuxで動作させる場合は、SkiaSharpのほかにSkiaSharp.NativeAssets.Linuxもインストールするようにしてください。

dotnet add package SkiaSharp
dotnet add package SkiaSharp.NativeAssets.Linux

ネイティブモジュールのラッパーということでusingが沢山出てきますね。

Program.cs
using SkiaSharp;

public static async Task ResizeAsync(string input, string output)
{
    await using var imageData = File.OpenRead(input);
    using var bitmap = SKBitmap.Decode(imageData);
    using var resizedBitmap = bitmap.Resize(new SKSizeI(1280, 720), SKFilterQuality.Medium);
    using var data = resizedBitmap.Encode(SKEncodedImageFormat.Png, 10);

    await File.WriteAllBytesAsync(output, data.ToArray());
}

ベンチマーク

BenchmarkDotNetでベンチマークしたんだけれど、Linux環境でSystem.Drawing.Commonがどうしても失敗するのでN/Aにしてます。
単品で動かす分には動くんだけれどな。

Windows環境

Method Mean Error StdDev Median
RunDrawingCommon 146.9 ms 5.78 ms 17.05 ms 143.3 ms
RunImageSharp 648.9 ms 29.18 ms 86.05 ms 608.0 ms
RunSkiaSharp 510.0 ms 10.14 ms 16.66 ms 507.7 ms

Linux環境

Method Mean Error StdDev Median
RunDrawingCommon NA NA NA NA
RunImageSharp 585.7 ms 10.47 ms 8.74 ms 590.8 ms
RunSkiaSharp 539.3 ms 12.10 ms 34.52 ms 526.8 ms

System.Drwaing が圧倒的に早い、さすがWindows環境ではMSのサポートがあるライブラリ、、、

おわりに

ImageSharpが一番好みではあるのですが、ライセンス問題があるのでまず考えるのはSkiaSharpですかねぇ。

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
What you can do with signing up
3