C#でMagick.NETを使って画像ファイルの幅と高さを取得する。

C# コンピュータ
C#

画像ファイルの幅と高さを取得する方法として、Sysmte.Drawing.Bitmapのオブジェクトを生成し、プロパティのWidthやHeightを参照すれば良いのですが、ファイルサイズが大きな画像ファイルが多量にある場合、毎回Bitmapオブジェクトを生成していると、処理時間が大変なことに成ります。

高速に取得する方法として、画像ファイルの幅と高さが記録している部分をピンポイントで読みだす方法があります。
C#でJpegとPNGファイルから幅と高さを取得
多数の画像ファイルから幅と高さを取得する必要があり、なるべく短い時間で取得できるように試作してみました。 C#で画像の高さや幅を取得する方法として、画像ファイルからSystem.Drawing.Bitmapなどのオブジェクトを生成し、オブジ...

C#向きの処理では無いですが、がんばれば実用レベルのプログラムを自前で作ることが出来るかもしれません。ただ色々な画像ファイルのフォーマットに対応させようと思うと、あまり自分では作りたくないのが本音です。

Magick.NET(ImageMagick)の機能を眺めていたいたら画像ファイルの情報を取得する機能があるようなので試してみたいと思います。

プロジェクトの作成

dotnet new console --name プロジェクト名
cd プロジェクト名
dotnet add package System.Drawing.Common
dotnet add package Magick.NET.Core
dotnet add package Magick.NET.SystemDrawing
dotnet add package Magick.NET-Q8-AnyCPU

ソースコード

ファイル名:Programe.cs

namespace ImageMagickGetSize;

using ImageMagick;
class Program
{
    static string GetImageSizeString(string imageFile)
    {
        var info = new MagickImageInfo(imageFile);

        return string.Format("{0}x{1}", info.Width, info.Height);
    }
    static string GetImageSizeString_DrawingVer(string imageFile)
    {
        using var fs = new FileStream(imageFile, FileMode.Open, FileAccess.Read);
        using var bmp = System.Drawing.Bitmap.FromStream(fs);

        return string.Format("{0}x{1}", bmp.Width, bmp.Height);
    }
    static void Main()
    {
        string dirPath = @"C:\Users\karet\Pictures";
        var files = System.IO.Directory.EnumerateFiles(
            dirPath, "*.PNG", System.IO.SearchOption.AllDirectories);

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

        sw.Start();

        foreach(var f in files) {
            string sizeStr = GetImageSizeString(f);
            string name = System.IO.Path.GetFileName(f);
            Console.WriteLine($"{sizeStr}: {name}");
        }

        sw.Stop();
        var resultA = sw.ElapsedMilliseconds;

        sw.Restart();

        foreach(var f in files) {
            string sizeStr = GetImageSizeString_DrawingVer(f);
            string name = System.IO.Path.GetFileName(f);
            Console.WriteLine($"{sizeStr}: {name}");
        }

        sw.Stop();
        Console.WriteLine("Magic.NETを使った場合");
        Console.WriteLine($"{resultA}ミリ秒");
        Console.WriteLine("System.Drawingを使った場合");
        Console.WriteLine($"{sw.ElapsedMilliseconds}ミリ秒");
    }
}

実行

dotnet run

結果

Magic.NETを使った場合
1655ミリ秒
System.Drawingを使った場合
46272ミリ秒

サンプルプログラムでは複数の画像ファイルが存在するフォルダから画像ファイルの一覧を取得し、各画像の幅と高さを取得しています。処理速度を比較するためにMagick.NETとSystem.Drawing.Bitmapのプロパティを参照する2つを用意してみました。
ファイルキャッシュ的に後から実行するSystem.Drawingが有利なはずですが、Magic.NETの方が速い結果となりました。
(本当であればこのようなテストだけではなくMagic.NET(ImageMagick)のソースコードを読んで、実際どのように作られているか確認すれば良いのですが…)

また、Magic.NETは色々なファイルフォーマットに対応している点も魅力的です。

ファイルサイズが小さな画像で実験

namespace ImageMagickGetSize;

using ImageMagick;
class Program
{
    static string GetImageSizeString(Stream s)
    {
        var info = new MagickImageInfo(s);

        return string.Format("{0}x{1}", info.Width, info.Height);
    }
    static string GetImageSizeString_DrawingVer(Stream s)
    {
        using var bmp = System.Drawing.Bitmap.FromStream(s);

        return string.Format("{0}x{1}", bmp.Width, bmp.Height);
    }
    static void Main()
    {
        string dirPath = @"H:\Pictures";
        var files = System.IO.Directory.EnumerateFiles(
            dirPath, "*.PNG", System.IO.SearchOption.AllDirectories);

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

        sw.Start();

        foreach(var f in files) 
        {
            using (var fs = new FileStream(f, FileMode.Open, FileAccess.Read))
            {
                string sizeStr = GetImageSizeString(fs);
            }
        }

        sw.Stop();
        var resultA = sw.ElapsedMilliseconds;

        sw.Restart();

        foreach(var f in files)
        {
            using(var fs = new FileStream(f, FileMode.Open, FileAccess.Read))
            {
                string sizeStr = GetImageSizeString_DrawingVer(fs);
            }
        }

        sw.Stop();
        Console.WriteLine("Magic.NETを使った場合");
        Console.WriteLine($"{resultA}ミリ秒");
        Console.WriteLine("System.Drawingを使った場合");
        Console.WriteLine($"{sw.ElapsedMilliseconds}ミリ秒");
    }
}

ファイル名ではなくファイルストリームを引数に取るプログラムに変更してみました。
前項目とは別の比較的画像サイズが小さめのファイルの一覧で試した所以下のような結果となりました。

Magic.NETを使った場合
217ミリ秒
System.Drawingを使った場合
166ミリ秒

予想ですが、基本的にMagick.netの方が動作が重いが、画像ファイルの読み込む量を減らすことで速度を稼いでいたところ、そもそものファイルサイズが小さくなったため、優位性が失われたと思われます。(まるで違っていたらすみません。)

コメント