|   | 
 
|  
 .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メソッドではサムネイル画像のサイズも指定できる。
 
|  
 | 
 
generated by  
 | 
 
 
 | 
 
 
	
		Insider.NET 記事ランキング
		
		
			本日
			月間