2014年9月30日火曜日

C#を使って画像を高速でリサイズする方法

今日はC#を使って画像を高速でリサイズする方法をご紹介します。

みなさんも一度はC#を使って大量の画像を処理したいと考えたことがあると思います。しかし、実際にやってみると、例えばたった458枚をリサイズするのに15.28秒もかかってしまい、使い物になりません。

私はあきらめてしばらくそのまま使っていました。が、しかしネット上で偶然見つけたRalpha Image Resizerというソフトは同じ処理をたった5.15秒でやってのけるじゃありませんか!

調べてみると、このソフトはマルチコアで並列に処理し、高速化を図っているのだそう。

そこでC#で同じことができるように試しに書いてみました。Ralphaを真似てWIC (Windows Imaging Component)を使用してみたらさらなる高速化が期待できましたので、それを使用します。

以下は指定したディレクトリ内の画像ファイルをすべて縦800pxにリサイズし、保存するプログラムです。

//using System.IO; //using System.Text.RegularExpressions; //using System.Windows.Media; //using System.Windows.Media.Imaging; // リサイズしたい画像が含まれているディレクトリ var dir = @"C:\Images\"; // ファイルを列挙し、並列で処理 Parallel.ForEach(Directory.EnumerateFiles(dir), path => { // 画像ファイルじゃない場合はスキップ if (!Regex.IsMatch(path, "\\.(?:png|jpg|gif|bmp)$", RegexOptions.IgnoreCase)) return; // ファイルを開いて Stream オブジェクトを作成 using (var sourceStream = File.OpenRead(path)) { // 画像をデコードするための BitmapDecoder オブジェクトを作成する // (ファイルの種類に応じて適切なデコーダーが作成される) var decoder = BitmapDecoder.Create(sourceStream, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default); // 画像ファイル内の1フレーム目を取り出す (通常1フレームしかない) var bitmapSource = decoder.Frames[0]; var scale = 800 / bitmapSource.Height; // 拡大率 // 拡大・縮小されたビットマップを作成する var scaledBitmapSource = new TransformedBitmap(bitmapSource, new ScaleTransform(scale, scale)); // 元のファイルと同じ形式のエンコーダー (BitmapEncoder) を選択・作成する var extension = Path.GetExtension(path); var encoder = extension == ".png" ? new PngBitmapEncoder() : extension == ".jpg" ? new JpegBitmapEncoder() : extension == ".gif" ? new GifBitmapEncoder() : extension == ".bmp" ? new BmpBitmapEncoder() : (BitmapEncoder)(new PngBitmapEncoder()); // エンコーダーにフレームを追加する encoder.Frames.Add(BitmapFrame.Create(scaledBitmapSource)); var destDir = dir + "\\resize\\"; // 出力ディレクトリ // 出力ディレクトリが存在しない場合は、新しく作成する if (!Directory.Exists(destDir)) { Directory.CreateDirectory(destDir); } var dest = destDir + Path.GetFileNameWithoutExtension(path) + extension; // 出力ファイル // 出力ファイルのストリームを開く using (var destStream = File.OpenWrite(dest)) { encoder.Save(destStream); // 保存 } } }); Console.WriteLine("終了しました");

※実行するには WindowsBase, PresentationCore, System.Xaml の参照が必要です。

実行速度を計測するとこんな感じ