午後わてんのブログ

ベランダ菜園とWindows用アプリ作成(WPFとC#)

WPF、Tiff画像の圧縮形式とファイルサイズ、TiffBitmapEncoderのCompress

 
 
今までは何も指定しないで作成していたけど圧縮(エンコード)方式をいくつか選べるみたいなので試してみた
TiffBitmapEncoder.Compression プロパティに
TiffCompressOption列挙型から選んで指定する

 
TiffCompressOption 列挙型 (System.Windows.Media.Imaging)
から引用
Ccitt3
CCITT3 圧縮スキーマが使用されます。

Ccitt4
CCITT4 圧縮スキーマが使用されます。

Default
TiffBitmapEncoder エンコーダーは、実行できる最適な圧縮スキーマで、ビットマップを保存しようとしています。

Lzw
LZW 圧縮スキーマが使用されます。

None
Tagged Image File Format (TIFF) イメージは圧縮されません。

Rle
RLE 圧縮スキーマが使用されます。

Zip
Zip 圧縮スキーマが使用されます。
引用ここまで
 
何も指定しないとDefaultになる
 
圧縮する画像は
イメージ 2
イメージ 3
イメージ 1
256x192ピクセルの24bitのbmp形式の
画像A
ファイルサイズは144KB(147510バイト)
256*192*24/8=147456
圧縮方式による見た目の違い
イメージ 4
Tiff可逆圧縮なので見た目は元のBitmap画像とまったく同じはず
Ccitt3と4はファクシミリに使われている方式だから白黒2値になるみたい
 
 
ファイルサイズ変化
画像A
KB
元画像 144
Ccitt3 14.2
Ccitt4 14.4
Default 128
Lzw 170
None 144
Rle 145
Zip 132
圧縮なのにLzwとRleは元画像よりファイルサイズが増えている
一番効率がいいのはDefaultなので何も指定しないのがいいみたい

Defaultの説明では最適な圧縮方法を採りますってあるけどなんだろうって気になる
エクスプローラーのファイルのプロパティから見ると
イメージ 5
LZWってある
でもLzwを指定してできたファイルとはサイズが違いすぎる
 
ファイルのプロパティから見た圧縮方式
イメージ 6
指定した単語とは微妙に違ったり表示がなかったり
でもDefaultとLzwはどちらもLZWだねえ
 
 
TIFFファイルの圧縮フォーマット判別法
http://robaq.info/tiffcompression.xhtml
こちらを参考にして
バイナリエディタで確認してみた
イメージ 7
先頭2バイトが49 49なら
検索で03 01 03 00 01 00
イメージ 8
これで見つかった場所から12バイト分の後の4バイトの値が、5なら圧縮方式はLZWってことらしい
イメージ 9
DefaultもLzwも5
だけどLzwを指定したものは170KBってぜんぜん違うサイズだからなあ
LZWにも種類があるってことかしら
これ以上は難しそうなのでパス
 
 
別の画像を圧縮
イメージ 10
png画像が得意そうな画像B

プロパティでみると
イメージ 11
527x497ピクセルの32bitのbmp形式の画像B
ファイルサイズは990KB(1047730バイト)
527*497*32/8=1047676
 
        ファイルサイズ変化
		画像A	画像B
		KB		KB
元画像	144		990
Ccitt3	  14		  31
Ccitt4	  14		  41
Default	128		107
Lzw		170		  93
None		144		769
Rle		145		575
Zip		132		  46
おお、スゴイ縮んだ
Defaultでも10分の1に圧縮できているし、一番効率が良かったZip形式では20分の1
可逆圧縮でこの圧縮率はスゴイ
png画像も同じくらい縮むから好きなんだけどTiff画像もいいねえ
 
 
 
 
 
今回参照したところ
TiffCompressOption 列挙型 (System.Windows.Media.Imaging)
https://msdn.microsoft.com/ja-jp/library/system.windows.media.imaging.tiffcompressoption(v=vs.110).aspx

TIFFファイルの圧縮フォーマット判別法
http://robaq.info/tiffcompression.xhtml

CGファイル概説 第5章 第2節 その2
http://www.snap-tck.com/room03/c02/cg/cg05_03.html

TIFFのフォーマット(その1) | JProgramer
http://jprogramer.com/libtiffcate/3188

TIFF Tag Reference, Baseline TIFF Tags
https://www.awaresystems.be/imaging/tiff/tifftags/baseline.html

TIFF - Wikipedia
https://en.wikipedia.org/wiki/TIFF
ありがとうございます
 
 
using System.Windows;
using System.Windows.Media.Imaging;
using System.IO;

//TiffBitmapEncoder クラス(System.Windows.Media.Imaging)
//https://msdn.microsoft.com/ja-jp/library/system.windows.media.imaging.tiffbitmapencoder(v=vs.110).aspx

namespace _20180115_TiffEncoder
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            this.Title = this.ToString();
            string filePath;
            //filePath = @"D:\ブログ用\テスト用画像\NEC_8041_2017_05_09_午後わてん_96dpi_Bgr32.bmp";
            //filePath = @"D:\ブログ用\テスト用画像\NEC_8041_2017_05_09_午後わてん_96dpi_BlackWhite.png";
            //filePath = @"D:\ブログ用\テスト用画像\NEC_8041_2017_05_09_午後わてん_96dpi_Gray2.png";
            //filePath = @"D:\ブログ用\テスト用画像\NEC_8041_2017_05_09_午後わてん_72dpi.jpg";
            //filePath = @"D:\ブログ用\チェック用2\NEC_1259_2018_01_14_午後わてん.jpg";
            //filePath = @"D:\ブログ用\テスト用画像\NEC_8041_2017_05_09_午後わてん_96dpi_Prgba64.png";
            //filePath = @"D:\ブログ用\テスト用画像\NEC_8041_2017_05_09_午後わてん_96dpi_Bgr24.bmp";
            filePath = @"D:\ブログ用\WPF\20180113_画像読み込み07_02_.bmp";

            BitmapSource source = GetBitmapSource10(filePath, true, 96, 96);

            TiffTest2(source, TiffCompressOption.Ccitt3);
            TiffTest2(source, TiffCompressOption.Ccitt4);
            TiffTest2(source, TiffCompressOption.Default);
            TiffTest2(source, TiffCompressOption.Lzw);
            TiffTest2(source, TiffCompressOption.None);
            TiffTest2(source, TiffCompressOption.Rle);
            TiffTest2(source, TiffCompressOption.Zip);

        }
        private void TiffTest2(BitmapSource source, TiffCompressOption compressOption)
        {
            TiffBitmapEncoder encoder = new TiffBitmapEncoder();
            BitmapFrame frame = BitmapFrame.Create(source);
            encoder.Frames.Add(frame);
            encoder.Compression = compressOption;
            string fileName = frame.Format.ToString() + "_" + encoder.Compression.ToString() + ".tiff";
            using (FileStream fs = new FileStream(fileName, FileMode.Create, FileAccess.Write))
            {
                encoder.Save(fs);
            }
        }
        /// <summary>
        /// ファイルパスとPixelFormatを正確にするか最適にするかを指定してBitmapSourceを取得、
        /// dpiの変更は任意
        /// </summary>
        /// <param name="filePath">画像ファイルのフルパス</param>
        /// <param name="accuratePixelFormat">Trueなら画像ファイルと同じ正確なPixelFormat、
        /// Falseは今の環境で最適なものに変更される</param>
        /// <param name="dpiX">無指定なら画像ファイルで指定されているdpiになる</param>
        /// <param name="dpiY">無指定なら画像ファイルで指定されているdpiになる</param>
        /// <returns></returns>
        private BitmapSource GetBitmapSource10(
            string filePath, bool accuratePixelFormat, double dpiX = 0, double dpiY = 0)
        {
            BitmapSource source;
            using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read))
            {
                BitmapFrame bitmapFrame;
                //PixelFormatを元の画像と同じにするかパソコンの環境に合わせるか
                if (accuratePixelFormat)
                {//画像と同じ
                    bitmapFrame = BitmapFrame.Create(
                        fs,
                        BitmapCreateOptions.PreservePixelFormat,
                        BitmapCacheOption.Default);
                }
                else
                {//環境に最適なものに変更
                    bitmapFrame = BitmapFrame.Create(
                        fs,
                        BitmapCreateOptions.None,
                        BitmapCacheOption.Default);
                }

                int w = bitmapFrame.PixelWidth;
                int h = bitmapFrame.PixelHeight;
                int stride = (w * bitmapFrame.Format.BitsPerPixel + 7) / 8;
                byte[] pixels = new byte[h * stride];
                bitmapFrame.CopyPixels(pixels, stride, 0);
                //dpi指定がなければ元の画像と同じdpiにする
                if (dpiX == 0) { dpiX = bitmapFrame.DpiX; }
                if (dpiY == 0) { dpiY = bitmapFrame.DpiY; }
                //dpiを指定してBitmapSource作成
                source = BitmapSource.Create(
                    w, h, dpiX, dpiY,
                    bitmapFrame.Format,
                    bitmapFrame.Palette, pixels, stride);
            };

            return source;
        }
    }
}
 
 画像ファイルパスからBitmapSourceを作成
 
TiffBitmapEncoderのFrameにそれを入れて
Compressに圧縮形式を指定してからファイルに保存しているだけ
今回はXAML必要なかった、こういう時にコンソールアプリってのがあるんだろうなあ
 
画像ファイルパスからBitmapSource作成の
GetBitmapSource10は前回の記事

gogowaten.hatenablog.com

からコピペ

 

TiffBitmapEncoderにFrame追加は
encoder.Frames.Add(取得したBitmapSource);
これだとエラーになったので
取得したBitmapSourceからBitmapFrame.Createで作成したものを追加で
BitmapFrame frame = BitmapFrame.Create(取得したBitmapSource);
encoder.Frames.Add(frame);
こうした
 

2018/01/17修正
int stride = (w * pixelFormat.BitsPerPixel) / 8;
↑を↓に書き直した
int stride = (w * pixelFormat.BitsPerPixel + 7) / 8;
これで8bpp以下で縦横ともに8の倍数じゃない画像でもエラーにならない

2018/01/21
byte pixels = new byte[w * stride];
↑を↓に書き直した
byte pixels = new byte[h * stride];

 
 
前回の記事は1日前
WPF、画像ファイルを開く方法まとめ ( ソフトウェア ) - 午後わてんのブログ - Yahoo!ブログ
https://blogs.yahoo.co.jp/gogowaten/15325331.html
 
次回は1日後
WPF、wdp画像ファイル作成時の画質設定とファイルサイズ、WmpBitmapEncoder ( ソフトウェア ) - 午後わてんのブログ - Yahoo!ブログ
https://blogs.yahoo.co.jp/gogowaten/15328411.html