.NET TIPS

より高速にサムネイル画像(縮小画像)を作成するには?[2.0のみ、C#、VB]

デジタルアドバンテージ 遠藤 孝信
2007/06/14

 「TIPS:サムネイル画像(縮小画像)を作成するには?」では、Imageクラス(System.Drawing名前空間)のGetThumbnailImageメソッドによりサムネイル画像を作成する方法について紹介した。そのTIPSのサンプル・プログラムでは、元の画像を読み込むために(画像オブジェクトを作成するために)ImageクラスのFromFileメソッドを使用している。

 しかし、「TIPS:画像ファイルを高速に読み込むには?」で紹介しているように、.NET Framework 2.0ではImageクラスのFromStreamメソッドにより、FromFileメソッドよりも高速に画像オブジェクト(Imageオブジェクト)を作成することができる。

 そこで本稿では、より高速にサムネイル画像を作成するための、FromStreamメソッドとGetThumbnailImageメソッドを組み合わせた場合のコードを紹介し、FromFileメソッドを使用した場合と速度を比較してみる。また、さらに高速にサムネイル画像を作成するための方法として、Exif情報から直接サムネイル画像を取得する方法についても触れる。

FromFileメソッド+GetThumbnailImageメソッド

 まず冒頭のTIPSと同様の方法である、ImageクラスのFromFileメソッドとGetThumbnailImageメソッドの組み合わせによりサムネイル画像を作成するサンプル・プログラムを以下に示す。

 このプログラムでは、C:\jpgsディレクトリにあるJPEGファイルを順に読み込んでImageオブジェクトを作成し、それを基に160ピクセルX120ピクセル*のサムネイル画像を作成してファイルに保存する。また、すべての処理にかかる時間をStopwatchクラス(System.Diagnostics名前空間)により計測する。

* 最近のデジタル・カメラで撮影したJPEG画像には、たいていこのサイズのサムネイル画像がそのExif情報部分に格納されているようである。

// normalthumbnail.cs
// Image.FromFileメソッド+GetThumbnailImageメソッドによる作成

using System;
using System.IO;
using System.Drawing;
using System.Diagnostics;

class NormalThumbnail {
  static void Main() {

    string dir = @"C:\jpgs"; // 画像のあるディレクトリ
    string[] jpgFiles = Directory.GetFiles(dir, "*.jpg");

    Stopwatch sw = Stopwatch.StartNew();

    foreach (string jpg in jpgFiles) {
      Console.WriteLine(jpg);

      // 画像オブジェクトの作成
      Image orig = Image.FromFile(jpg);

      // サムネイルの作成
      Image thumbnail = orig.GetThumbnailImage(
        160, 120, delegate { return false; }, IntPtr.Zero);

      // サムネイルの保存
      thumbnail.Save("tn_" + Path.GetFileName(jpg),
        System.Drawing.Imaging.ImageFormat.Jpeg);

      orig.Dispose();
      thumbnail.Dispose();
    }
    sw.Stop();
    Console.WriteLine(sw.ElapsedMilliseconds + "ミリ秒");
    // 出力例:53109ミリ秒
  }
}

// コンパイル方法:csc normalthumbnail.cs
C#のサンプル・プログラム(normalthumbnail.cs)

' normalthumbnail.vb
' Image.FromFileメソッド+GetThumbnailImageメソッドによる作成

Imports System
Imports System.IO
Imports System.Drawing
Imports System.Diagnostics

Class CreateThumbnailTest

  Shared Function dummy() As Boolean
    Return False
  End Function

  Shared Sub Main()

    Dim dir As String = "C:\jpgs" ' 画像のあるディレクトリ
    Dim jpgFiles As String() = Directory.GetFiles(dir, "*.jpg")

    Dim sw As Stopwatch = Stopwatch.StartNew()

    For Each jpg As String In jpgFiles
      Console.WriteLine(jpg)

      ' 画像オブジェクトの作成
      Dim orig As Image = Image.FromFile(jpg)

      ' サムネイルの作成
      Dim thumbnail As Image = orig.GetThumbnailImage(160, 120, _
        New Image.GetThumbnailImageAbort(AddressOf dummy), _
        IntPtr.Zero)

      ' サムネイルの保存
      thumbnail.Save("tn_" + Path.GetFileName(jpg), _
        System.Drawing.Imaging.ImageFormat.Jpeg)

      orig.Dispose()
      thumbnail.Dispose()
    Next
    sw.Stop()
    Console.WriteLine(sw.ElapsedMilliseconds & "ミリ秒")
    ' 出力例:53109ミリ秒

  End Sub
End Class

' コンパイル方法:vbc normalthumbnail.vb
VBのサンプル・プログラム(normalthumbnail.vb)

 コメントで出力例として記述している処理時間(53109ミリ秒)は、筆者のPC(CPU:Intel Core 2 Duo 2.4GHz、メモリ:2Gbytes)で実行したときのものだ。C:\jpgsディレクトリには200個のデジカメ画像(JPEGファイル)が保存されている(ファイルの平均サイズは約1.9Mbytes)。

FromStreamメソッド+GetThumbnailImageメソッド

 次のサンプル・プログラムは、ImageクラスのFromStreamメソッドにより画像オブジェクトを作成し、GetThumbnailImageメソッドによりサムネイル画像を作成する。

// fastthumbnail.cs
// Image.FromStreamメソッド+GetThumbnailImageメソッドによる作成

using System;
using System.IO;
using System.Drawing;
using System.Diagnostics;

class FastThumbnail {
  static void Main() {

    string dir = @"C:\jpgs"; // 画像のあるディレクトリ
    string[] jpgFiles = Directory.GetFiles(dir, "*.jpg");

    Stopwatch sw = Stopwatch.StartNew();

    foreach (string jpg in jpgFiles) {
      Console.WriteLine(jpg);

      using (FileStream fs = File.OpenRead(jpg)) {

        // 画像の読み込み
        Image orig = Image.FromStream(fs, false, false);

        // サムネイルの作成
        Image thumbnail = orig.GetThumbnailImage(
          160, 120, delegate { return false; }, IntPtr.Zero);

        // サムネイルの保存
        thumbnail.Save("tn_" + Path.GetFileName(jpg),
          System.Drawing.Imaging.ImageFormat.Jpeg);

        orig.Dispose();
        thumbnail.Dispose();
      }
    }
    sw.Stop();
    Console.WriteLine(sw.ElapsedMilliseconds + "ミリ秒");
    // 出力例:9492ミリ秒
  }
}

// コンパイル方法:csc fastthumbnail.cs
C#のサンプル・プログラム(fastthumbnail.cs)

' fastthumbnail.vb
' Image.FromStreamメソッド+GetThumbnailImageメソッドによる作成

Imports System
Imports System.IO
Imports System.Drawing
Imports System.Diagnostics

Class FastThumbnail

  Shared Function dummy() As Boolean
    Return False
  End Function

  Shared Sub Main()

    Dim dir As String = "C:\jpgs" ' 画像のあるディレクトリ
    Dim jpgFiles As String() = Directory.GetFiles(dir, "*.jpg")

    Dim sw As Stopwatch = Stopwatch.StartNew()

    For Each jpg As String In jpgFiles
      Console.WriteLine(jpg)

      Using fs As FileStream = File.OpenRead(jpg)

        ' 画像の読み込み
        Dim orig As Image = Image.FromStream(fs, False, False)

        ' サムネイルの作成
        Dim thumbnail As Image = orig.GetThumbnailImage(160, 120, _
          New Image.GetThumbnailImageAbort(AddressOf dummy), _
          IntPtr.Zero)

        ' サムネイルの保存
        thumbnail.Save("tn_" + Path.GetFileName(jpg), _
          System.Drawing.Imaging.ImageFormat.Jpeg)

        orig.Dispose()
        thumbnail.Dispose()
      End Using
    Next
    sw.Stop()
    Console.WriteLine(sw.ElapsedMilliseconds & "ミリ秒")
    ' 出力例:9492ミリ秒
  End Sub
End Class

' コンパイル方法:vbc fastthumbnail.vb
VBのサンプル・プログラム(fastthumbnail.vb)

 FromStreamメソッドはファイル全体を一度に読み込まず、かつ、通常はサムネイル画像がJPEGファイルの先頭部分に格納されているため、筆者の環境では、処理速度はFromFileメソッドで画像オブジェクトを作成した先ほどのサンプル・プログラムよりも約6倍速くなっている。

Exif情報内のサムネイル画像の読み込み

 次のサンプル・プログラムでは、JPEGファイルのExif情報に格納されているサムネイル画像のデータ(タグ番号は0x501b)をバイト配列のデータとして取り出し、そこからサムネイル画像の画像オブジェクトを作成する。

 なお、Exif情報内のデータの取得に関しては「TIPS:デジカメ画像のExif情報を取得するには?」を、バイト配列のデータから画像オブジェクトを作成する方法については「TIPS:バイト配列→画像オブジェクト/画像オブジェクト→バイト配列の変換を行うには?」を参照していただきたい。

// fasterthumbnail.cs
// FromStreamメソッドを使ったExifデータの読み込み

using System;
using System.IO;
using System.Drawing;
using System.Drawing.Imaging;
using System.Diagnostics;

class FasterThumbnail {
  static void Main() {

    string dir = @"C:\jpgs"; // 画像のあるディレクトリ
    string[] jpgFiles = Directory.GetFiles(dir, "*.jpg");

    ImageConverter imgconv = new ImageConverter();

    Stopwatch sw = Stopwatch.StartNew();

    foreach (string jpg in jpgFiles) {
      Console.WriteLine(jpg);

      using (FileStream fs = File.OpenRead(jpg)) {

        // 画像オブジェクトの作成
        Image orig = Image.FromStream(fs, false, false);

        int[] pils = orig.PropertyIdList;
        int index = Array.IndexOf(pils, 0x501b); // サムネイル・データ

        if (index == -1) {
          Console.WriteLine("画像にサムネイルが含まれていません。");
        } else {
          // サムネイル・データの取得
          PropertyItem pi = orig.PropertyItems[index];
          byte[] jpgBytes = pi.Value;

          // サムネイルの作成
          Image thumbnail = (Image)imgconv.ConvertFrom(jpgBytes);

          // サムネイルの保存
          thumbnail.Save("tn_" + Path.GetFileName(jpg),
            System.Drawing.Imaging.ImageFormat.Jpeg);
          thumbnail.Dispose();
        }
        orig.Dispose();
      }
    }
    sw.Stop();
    Console.WriteLine(sw.ElapsedMilliseconds + "ミリ秒");
    // 出力例:619ミリ秒
  }
}

// コンパイル方法:csc fasterthumbnail.cs
C#のサンプル・プログラム(fasterthumbnail.cs)

' fasterthumbnail.vb
' FromStreamメソッドを使ったExifデータの読み込み

Imports System
Imports System.IO
Imports System.Drawing
Imports System.Drawing.Imaging
Imports System.Diagnostics

Class FasterThumbnail
  Shared Sub Main()

    Dim dir As String = "C:\jpgs" ' 画像のあるディレクトリ
    Dim jpgFiles As String() = Directory.GetFiles(dir, "*.jpg")

    Dim imgconv As New ImageConverter()

    Dim sw As Stopwatch = Stopwatch.StartNew()

    For Each jpg As String In jpgFiles
      Console.WriteLine(jpg)

      Using fs As FileStream = File.OpenRead(jpg)

        ' 画像オブジェクトの作成
        Dim orig As Image = Image.FromStream(fs, False, False)

        Dim pils As Integer() = orig.PropertyIdList
        Dim index As Integer = Array.IndexOf(pils, &H501B) ' サムネイル・データ

        If index = -1 Then
          Console.WriteLine("画像にサムネイルが含まれていません。")
        Else
          ' サムネイル・データの取得
          Dim pi As PropertyItem = orig.PropertyItems(index)
          Dim jpgBytes As Byte() = pi.Value

          ' サムネイルの作成
          Dim thumbnail As Image _
              = CType(imgconv.ConvertFrom(jpgBytes), Image)

          ' サムネイルの保存
          thumbnail.Save("tn_" + Path.GetFileName(jpg), _
            System.Drawing.Imaging.ImageFormat.Jpeg)
          thumbnail.Dispose()
        End If
        orig.Dispose()
      End Using
    Next
    sw.Stop()
    Console.WriteLine(sw.ElapsedMilliseconds & "ミリ秒")
    ' 出力例:619ミリ秒
  End Sub
End Class

' コンパイル方法:vbc fasterthumbnail.vb
VBのサンプル・プログラム(fasterthumbnail.vb)

 筆者の環境では、このプログラムではさらに約15倍(最初のプログラムに比べて約86倍)高速にサムネイル画像を作成できた(もちろんこのような速度比は、対象となるJPEGファイルのサイズや構造、プログラムを実行した環境による)。

 当然ながら、この最後のプログラムでは、Exif情報内にサムネイル画像が含まれていないJPEGファイルに対しては、サムネイル画像を作成できない。また、サムネイル画像を取り出すだけであるため、そのサイズを指定することもできない。

 これに対して、最初の2つのプログラムではExif情報内にサムネイル画像が存在しない場合にも、それを作成することができる。これはGetThumbnailImageメソッドが本体画像からサムネイル画像を作成するためだ。また、GetThumbnailImageメソッドではサムネイル画像のサイズも指定できる。End of Article

利用可能バージョン:.NET Framework 2.0のみ
カテゴリ:クラス・ライブラリ 処理対象:ビットマップ
使用ライブラリ:Imageクラス(System.Drawing名前空間)
使用ライブラリ:Stopwatchクラス(System.Diagnostics名前空間)
関連TIPS:サムネイル画像(縮小画像)を作成するには?
関連TIPS:画像ファイルを高速に読み込むには?
関連TIPS:デジカメ画像のExif情報を取得するには?

この記事と関連性の高い別の.NET TIPS
サムネイル画像(縮小画像)を作成するには?
画像ファイルを高速に読み込むには?
バイト配列→画像オブジェクト/画像オブジェクト→バイト配列の変換を行うには?
PictureBoxコントロールにWeb画像を表示するには?
デジカメ画像のExif情報を取得するには?
このリストは、(株)デジタルアドバンテージが開発した
自動関連記事探索システム Jigsaw(ジグソー) により自動抽出したものです。
generated by

「.NET TIPS」


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Insider.NET 記事ランキング

本日 月間