はじめに
画像の縮小やグレースケールなどの画像の変換処理をC#で行う場合、System.Drawing名前空間のクラスを使うことが多かったと思います。
.NET6以降でこれらのメソッドを使う場合、Visual Studioでは下記のようなWindows環境以外では問題があるという警告が表示されます。
警告 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用のテンプレートを作り
{
"configProperties": {
"System.Drawing.EnableUnixSupport": true
}
}
プロジェクトファイルでビルド時に.runtimeconfig.jsonを生成するように構成する。
<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>
あとはお好みで警告を黙らせてあげれば実行できます。
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した後に画像を操作して保存してあげればよいです。
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が沢山出てきますね。
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ですかねぇ。