DOBON.NET プログラミング道: .NET Framework, VB.NET, C#, Visual Basic, Visual Studio, インストーラ, ...

マルチTIFFを作成する
圧縮方法を指定して画像をTIFFで保存する

マルチTIFFを作成する方法は、「Generating multi-page TIFF files」や「Save images into a multi-page TIFF file or add images to an existing TIFF file - CodeProject」で紹介されています。

これらを参考にさせていただいて、以下のようなコードを書いてみました。このサンプルのメソッドでは3番目のパラメータに圧縮方法としてEncoderValue列挙体の値を指定できます。指定できる値は、CompressionCCITT3(CCITT3、Group 3 Fax Encoding)、CompressionCCITT4(CCITT4、Group 4 Fax Encoding)、CompressionLZW(LZW)、CompressionNone(圧縮なし)、CompressionRle(PackBits)のいずれかです。ただし、圧縮方法にCompressionCCITT3、CompressionCCITT4、CompressionRleを指定すると、「使用されたパラメータが有効ではありません。」という例外ArgumentExceptionがスローされることがあります(私が試した限りでは、Windows XP SP3では例外がスローされましたが、Windows 7ではされませんでした)。そのような場合は、TIFFに追加する画像をあらかじめ白黒2値の画像に変換しておきます。その方法は、「2値化して、1bppの白黒画像を作成する」で説明しています。

補足:圧縮方法を指定しないで(EncoderParametersにEncoder.SaveFlagのみを追加して)作成することも出来ますが、その場合はLZWで圧縮されるようです。ただ、EncoderValue.CompressionLZWを指定した時とは同じになりません(私が試した限りでは、何も指定しない方がサイズが小さくなりました)。
VB.NET
コードを隠すコードを選択
'Imports System.Drawing
'Imports System.Drawing.Imaging

''' <summary>
''' 複数の画像をマルチTIFFで保存する
''' </summary>
''' <param name="fileName">保存先のTIFFファイルのパス</param>
''' <param name="baseImages">TIFFに追加する画像の配列</param>
''' <param name="compressionScheme">圧縮方法</param>
Public Shared Sub SaveMultiTiff(fileName As String, _
                                baseImages As Bitmap(), _
                                compressionScheme As EncoderValue)
    If fileName Is Nothing OrElse fileName.Length = 0 Then
        Throw New ArgumentException("fileName")
    End If
    If baseImages Is Nothing OrElse baseImages.Length = 0 Then
        Throw New ArgumentException("baseImages")
    End If
    If compressionScheme <> EncoderValue.CompressionCCITT3 AndAlso _
        compressionScheme <> EncoderValue.CompressionCCITT4 AndAlso _
        compressionScheme <> EncoderValue.CompressionLZW AndAlso _
        compressionScheme <> EncoderValue.CompressionNone AndAlso _
        compressionScheme <> EncoderValue.CompressionRle Then
        Throw New ArgumentException("compressionScheme")
    End If

    'TIFFのImageCodecInfoを取得する
    Dim ici As ImageCodecInfo = GetEncoderInfo("image/tiff")
    If ici Is Nothing Then
        Return
    End If

    Dim imagesCount As Integer = baseImages.Length
    Dim tiffImage As Bitmap = baseImages(0)
    Dim ep As EncoderParameters = Nothing

    If imagesCount = 1 Then
        'マルチTIFFではなく、1枚だけ保存する
        '圧縮方法を指定する
        ep = New EncoderParameters(1)
        ep.Param(0) = New EncoderParameter( _
            Encoder.Compression, CLng(compressionScheme))
        tiffImage.Save(fileName, ici, ep)
        ep.Dispose()
        Return
    End If

    Dim i As Integer
    For i = 0 To imagesCount - 1
        If i = 0 Then
            ep = New EncoderParameters(2)
            'はじめのフレームはMultiFrameで保存する
            ep.Param(0) = New EncoderParameter( _
                Encoder.SaveFlag, CLng(EncoderValue.MultiFrame))
            '圧縮方法を指定する
            ep.Param(1) = New EncoderParameter( _
                Encoder.Compression, CLng(compressionScheme))
            tiffImage.Save(fileName, ici, ep)
            ep.Dispose()
        Else
            ep = New EncoderParameters(2)
            '2枚目からはFrameDimensionPageで追加する
            ep.Param(0) = New EncoderParameter( _
                Encoder.SaveFlag, CLng(EncoderValue.FrameDimensionPage))
            '圧縮方法を指定する
            ep.Param(1) = New EncoderParameter( _
                Encoder.Compression, CLng(compressionScheme))
            tiffImage.SaveAdd(baseImages(i), ep)
            ep.Dispose()
        End If

        If i = imagesCount - 1 Then
            ep = New EncoderParameters(1)
            '最後にFlushで閉じる
            ep.Param(0) = New EncoderParameter( _
                Encoder.SaveFlag, CLng(EncoderValue.Flush))
            tiffImage.SaveAdd(ep)
            ep.Dispose()
        End If
    Next
End Sub

'MimeTypeで指定されたImageCodecInfoを探して返す
Private Shared Function GetEncoderInfo(mineType As String) _
        As System.Drawing.Imaging.ImageCodecInfo
    'GDI+ に組み込まれたイメージ エンコーダに関する情報をすべて取得
    Dim encs As System.Drawing.Imaging.ImageCodecInfo() = _
        System.Drawing.Imaging.ImageCodecInfo.GetImageEncoders()
    '指定されたMimeTypeを探して見つかれば返す
    Dim enc As System.Drawing.Imaging.ImageCodecInfo
    For Each enc In encs
        If enc.MimeType = mineType Then
            Return enc
        End If
    Next
    Return Nothing
End Function

'ImageFormatで指定されたImageCodecInfoを探して返す
Private Shared Function GetEncoderInfo(f As System.Drawing.Imaging.ImageFormat) _
        As System.Drawing.Imaging.ImageCodecInfo
    Dim encs As System.Drawing.Imaging.ImageCodecInfo() = _
        System.Drawing.Imaging.ImageCodecInfo.GetImageEncoders()
    Dim enc As System.Drawing.Imaging.ImageCodecInfo
    For Each enc In encs
        If enc.FormatID = f.Guid Then
            Return enc
        End If
    Next
    Return Nothing
End Function
C#
コードを隠すコードを選択
//using System.Drawing;
//using System.Drawing.Imaging;

/// <summary>
/// 複数の画像をマルチTIFFで保存する
/// </summary>
/// <param name="fileName">保存先のTIFFファイルのパス</param>
/// <param name="baseImages">TIFFに追加する画像の配列</param>
/// <param name="compressionScheme">圧縮方法</param>
public static void SaveMultiTiff(string fileName,
        Bitmap[] baseImages, EncoderValue compressionScheme)
{
    if (fileName == null || fileName.Length == 0)
    {
        throw new ArgumentException("fileName");
    }
    if (baseImages == null || baseImages.Length == 0)
    {
        throw new ArgumentException("baseImages");
    }
    if (compressionScheme != EncoderValue.CompressionCCITT3 &&
        compressionScheme != EncoderValue.CompressionCCITT4 &&
        compressionScheme != EncoderValue.CompressionLZW &&
        compressionScheme != EncoderValue.CompressionNone &&
        compressionScheme != EncoderValue.CompressionRle)
    {
        throw new ArgumentException("compressionScheme");
    }

    //TIFFのImageCodecInfoを取得する
    ImageCodecInfo ici = GetEncoderInfo("image/tiff");
    if (ici == null)
        return;

    int imagesCount = baseImages.Length;
    Bitmap tiffImage = baseImages[0];
    EncoderParameters ep = null;

    if (imagesCount == 1)
    {
        //マルチTIFFではなく、1枚だけ保存する
        //圧縮方法を指定する
        ep = new EncoderParameters(1);
        ep.Param[0] = new EncoderParameter(
            Encoder.Compression, (long)compressionScheme);
        tiffImage.Save(fileName, ici, ep);
        ep.Dispose();
        return;
    }

    for (int i = 0; i < imagesCount; i++)
    {
        if (i == 0)
        {
            ep = new EncoderParameters(2);
            //はじめのフレームはMultiFrameで保存する
            ep.Param[0] = new EncoderParameter(
                Encoder.SaveFlag, (long)EncoderValue.MultiFrame);
            //圧縮方法を指定する
            ep.Param[1] = new EncoderParameter(
                Encoder.Compression, (long)compressionScheme);
            tiffImage.Save(fileName, ici, ep);
            ep.Dispose();
        }
        else
        {
            ep = new EncoderParameters(2);
            //2枚目からはFrameDimensionPageで追加する
            ep.Param[0] = new EncoderParameter(
                Encoder.SaveFlag, (long)EncoderValue.FrameDimensionPage);
            //圧縮方法を指定する
            ep.Param[1] = new EncoderParameter(
                Encoder.Compression, (long)compressionScheme);
            tiffImage.SaveAdd(baseImages[i], ep);
            ep.Dispose();
        }

        if (i == imagesCount - 1)
        {
            ep = new EncoderParameters(1);
            //最後にFlushで閉じる
            ep.Param[0] = new EncoderParameter(
                Encoder.SaveFlag, (long)EncoderValue.Flush);
            tiffImage.SaveAdd(ep);
            ep.Dispose();
        }
    }
}

//MimeTypeで指定されたImageCodecInfoを探して返す
private static System.Drawing.Imaging.ImageCodecInfo
    GetEncoderInfo(string mineType)
{
    //GDI+ に組み込まれたイメージ エンコーダに関する情報をすべて取得
    System.Drawing.Imaging.ImageCodecInfo[] encs =
        System.Drawing.Imaging.ImageCodecInfo.GetImageEncoders();
    //指定されたMimeTypeを探して見つかれば返す
    foreach (System.Drawing.Imaging.ImageCodecInfo enc in encs)
    {
        if (enc.MimeType == mineType)
        {
            return enc;
        }
    }
    return null;
}

//ImageFormatで指定されたImageCodecInfoを探して返す
private static System.Drawing.Imaging.ImageCodecInfo
    GetEncoderInfo(System.Drawing.Imaging.ImageFormat f)
{
    System.Drawing.Imaging.ImageCodecInfo[] encs =
        System.Drawing.Imaging.ImageCodecInfo.GetImageEncoders();
    foreach (System.Drawing.Imaging.ImageCodecInfo enc in encs)
    {
        if (enc.FormatID == f.Guid)
        {
            return enc;
        }
    }
    return null;
}

.NET Framework 3.0以降で、TiffBitmapEncoderを使う

.NET Framework 3.0以降では、WPFの機能であるTiffBitmapEncoderクラスを使って作成することもできます。この方法は「GIFアニメーションを作成する」で紹介している「GifBitmapEncoderを使う方法」とほぼ同じですが、TiffBitmapEncoderはCompressionプロパティで圧縮方法を指定できます。Compressionプロパティに指定できるTiffCompressOption列挙体の値には以下のようなものがあります(MSDNからの引用です)。

メンバ名 説明
Default TiffBitmapEncoder エンコーダーは、利用可能な最適な圧縮スキーマでビットマップの保存を試みます。
None Tagged Image File Format (TIFF) イメージは圧縮されません。
Ccitt3 CCITT3 圧縮スキーマが使用されます。
Ccitt4 CCITT4 圧縮スキーマが使用されます。
Lzw LZW 圧縮スキーマが使用されます。
Rle RLE 圧縮スキーマが使用されます。
Zip Zip 圧縮スキーマが使用されます。

MSDNによると、Ccitt3、Ccitt4、Rleでは画像のFormatがBlackWhiteでないと(つまり白黒2値でないと)CompressionプロパティがDefaultにリセットされるということです。ただ、Windows XP SP3と7で私が試した所では、そのようなことはありませんでした。

以下に例を示します。PresentationCoreとWindowsBase、さらにVB.NETの場合はSystem.Xamlを参照設定に追加する必要があります。

VB.NET
コードを隠すコードを選択
'Imports System.Windows.Media.Imaging
'Imports System.IO

''' <summary>
''' 複数の画像をマルチTIFFで保存する
''' </summary>
''' <param name="savePath">保存先のTIFFファイルのパス</param>
''' <param name="imageFiles">TIFFに追加する画像の配列</param>
Public Shared Sub CreateMultiTiff(ByVal savePath As String, _
                                  ByVal imageFiles As String(), _
                                  ByVal compressOption As TiffCompressOption)
    'TiffBitmapEncoderを作成する
    Dim encoder As New TiffBitmapEncoder()
    '圧縮方法を変更する
    encoder.Compression = compressOption

    For Each f As String In imageFiles
        '画像ファイルからBitmapFrameを作成する
        Dim bmpFrame As BitmapFrame = _
            BitmapFrame.Create(New Uri(f, UriKind.RelativeOrAbsolute))
        'フレームに追加する
        encoder.Frames.Add(bmpFrame)
    Next

    '書き込むファイルを開く
    Dim outputFileStrm As New FileStream(savePath, _
        FileMode.Create, FileAccess.Write, FileShare.None)
    '保存する
    encoder.Save(outputFileStrm)
    '閉じる
    outputFileStrm.Close()
End Sub
C#
コードを隠すコードを選択
//using System.Windows.Media.Imaging;
//using System.IO;

/// <summary>
/// 複数の画像をマルチTIFFで保存する
/// </summary>
/// <param name="savePath">保存先のTIFFファイルのパス</param>
/// <param name="imageFiles">TIFFに追加する画像の配列</param>
public static void CreateMultiTiff(string savePath,
    string[] imageFiles, TiffCompressOption compressOption)
{
    //TiffBitmapEncoderを作成する
    TiffBitmapEncoder encoder = new TiffBitmapEncoder();
    //圧縮方法を変更する
    encoder.Compression = compressOption;

    foreach (string f in imageFiles)
    {
        //画像ファイルからBitmapFrameを作成する
        BitmapFrame bmpFrame =
            BitmapFrame.Create(new Uri(f, UriKind.RelativeOrAbsolute));
        //フレームに追加する
        encoder.Frames.Add(bmpFrame);
    }

    //書き込むファイルを開く
    FileStream outputFileStrm = new FileStream(savePath,
        FileMode.Create, FileAccess.Write, FileShare.None);
    //保存する
    encoder.Save(outputFileStrm);
    //閉じる
    outputFileStrm.Close();
}
  • 履歴:
  • 2013/7/4 EncoderParametersのDisposeを呼び出すようにした。

注意:この記事では、基本的な事柄の説明が省略されているかもしれません。初心者の方は、特に以下の点にご注意ください。

  • このサイトで紹介されているコードの多くは、例外処理が省略されています。例外処理については、こちらをご覧ください。
  • コードの先頭に記述されている「Imports ??? がソースファイルの一番上に書かれているものとする」(C#では、「using ???; がソースファイルの一番上に書かれているものとする」)の意味が分からないという方は、こちらをご覧ください。
  • 「???を参照に追加します」の意味が分からないという方は、こちらをご覧ください。