Magick.NETを使って、.NETがサポートしていない形式の画像を読み込む †
.NET Frameworkは標準で、BMP、JPEG、GIF、PNGなど様々な形式の画像を扱うことができますが、それ以外の画像は表示することすら難しいです。このような.NET Frameworkでサポートされていない形式の画像を扱う方法の1つとして、「ImageMagick」を使う方法が考えられます。ImageMagickがサポートしている画像形式の数は100以上にのぼり、PDFやRAW画像にも対応しています(対応しているフォーマット一覧は、「ImageMagick: Formats」)。
ImageMagickのWindows版には、コマンドラインツール(EXE)とDLLの2種類があります。.NET FrameworkでImageMagickの機能を使う場合は、これらを直接使用してももちろんよいのですが、専用のライブラリが幾つか公開されていますので、それらを使わせていただくとかなり楽ができます。
ImageMagickの.NET Framework用のライブラリには、「Magick.NET」や「ImageMagick Application Wrapper」、「ImageMagick.NET」などがあります。ここでは、この中で一番メジャーと思われる「Magick.NET」を使ってみることにします。
Magick.NETを使う準備をする †
残念ながらMagick.NETは今のところAlpha版しかありません。ライセンスは、Apache License 2.0のようです(詳しくは、「Magick.NET - License」)。
Magick.NETは、基本的には、ImageMagickを必要としません。しかし、Visual C++ 再頒布可能パッケージが必要です(.NET 4.0 は for Visual Studio 2012、.NET 2.0 は for Visual Studio 2008)。それ以外にも、扱う画像のフォーマットによっては、何らかのアプリケーションが必要な場合があります(別途アプリケーションが必要なフォーマットについては、「ImageMagick: Formats」に記述があります)。
Magick.NETは、「Magick.NET - Download」からダウンロードすることができます。または、NuGetを使ってインストールすることもできます。
Magick.NETには様々なパッケージが用意されています。.NET 2.0と.NET 4.0がありますし、x64、x86、AnyCPUでも分かれています。さらに、「Q8」と「Q16」というものがあります。Q16は「16 bits-per-pixel(bpp)」の意味で、16bitの画像を劣化させることなく扱うことができるが、Q8よりもリソースは2倍必要になるということです(詳しくは、「ImageMagick: Install from Binary Distribution」)。
私は、「Magick.NET-6.8.8.1001-Q16-x86-net40-client」を使ってみました。
NuGetを使わずにZIPをダウンロードした時は、アーカイブを展開すると、「Magick.NET-x86.dll」と「Magick.NET-x86.xml」(x64の場合は、「Magick.NET-x64.dll」と「Magick.NET-x64.xml」)の2つのファイルができます。このDLLをプロジェクトの参照設定に追加すれば、そのプロジェクトで使用できるようになります(詳しくは、「「○○○.dllを参照に追加します」の意味は?」)。
ある形式の画像ファイルを別形式の画像ファイルにコンバートする †
Magick.NETでは画像形式のコンバートが簡単にできますので、この機能を使用すれば、.NET Frameworkがサポートしていない形式の画像ファイルをサポートしている形式の画像ファイルに変換して、.NET Frameworkで扱うということができます。
例えば以下の例では、PSD形式の画像ファイル"C:\test\1.psd"を読み込んで、BMP形式の画像ファイル"C:\test\1.bmp"として保存してから、PictureBoxに表示しています。ちなみに"C:\test\1.bmp"がすでに存在している場合は、上書きされます。
1
2
3
4
5
6
7
8
9
| | Dim img As New ImageMagick.MagickImage("C:\test\1.psd")
img.Write("C:\test\1.bmp")
img.Dispose()
PictureBox1.ImageLocation = "C:\test\1.bmp"
|
1
2
3
4
5
6
7
8
9
10
| | ImageMagick.MagickImage img =
new ImageMagick.MagickImage(@"C:\test\1.psd");
img.Write(@"C:\test\1.bmp");
img.Dispose();
PictureBox1.ImageLocation = @"C:\test\1.bmp";
|
ものすごく簡単なコードですが、これだけでうまく行きました。
このコードでは保存する画像形式を指定していませんが、ファイル名で自動的に判断されるようです。もしファイルの拡張子をどの画像形式のものでもない文字列にした場合は、読み込んだ時の画像形式で保存されるようです。
もし保存する時の画像形式を明確にするなら、MagickImageのFormatプロパティに画像形式を設定すればよいようです。
1
2
3
4
5
6
7
| | Dim img As New ImageMagick.MagickImage("C:\test\1.psd")
img.Format = ImageMagick.MagickFormat.Bmp
img.Write("C:\test\1.img")
img.Dispose()
|
1
2
3
4
5
6
7
8
| | ImageMagick.MagickImage img =
new ImageMagick.MagickImage(@"C:\test\1.psd");
img.Format = ImageMagick.MagickFormat.Bmp;
img.Write(@"C:\test\1.img");
img.Dispose();
|
読み込んだ画像をBitmapオブジェクトに変換する †
MagickImageクラスのToBitmapメソッドを使えば、読み込んだ画像からSystem.Drawing.Bitmapオブジェクトを作成することもできます。
以下の例では、JPEG-2000形式の画像ファイル"C:\test\1.j2k"を読み込んでBitmapオブジェクトを作成し、PictureBoxに表示しています。
1
2
3
4
5
6
7
8
9
| | Dim img As New ImageMagick.MagickImage("C:\test\1.j2k")
Dim bmp As System.Drawing.Bitmap = img.ToBitmap()
img.Dispose()
PictureBox1.Image = bmp
|
1
2
3
4
5
6
7
8
9
10
| | ImageMagick.MagickImage img =
new ImageMagick.MagickImage(@"C:\test\1.j2k");
System.Drawing.Bitmap bmp = img.ToBitmap();
img.Dispose();
PictureBox1.Image = bmp;
|
このコードのようにToBitmapメソッドの引数に何も指定しないと、BMP形式のBitmapオブジェクトになります。もし別の形式にしたいのであれば、ToBitmapメソッドの引数に適当なSystem.Drawing.Imaging.ImageFormatオブジェクトを指定します。
以下の例では、PNG形式のBitmapオブジェクトを作成しています。
1
2
3
4
5
6
7
| | Dim img As New ImageMagick.MagickImage("C:\test\1.j2k")
Dim bmp As System.Drawing.Bitmap = _
img.ToBitmap(System.Drawing.Imaging.ImageFormat.Png)
img.Dispose()
|
1
2
3
4
5
6
7
8
| | ImageMagick.MagickImage img =
new ImageMagick.MagickImage(@"C:\test\1.j2k");
System.Drawing.Bitmap bmp =
img.ToBitmap(System.Drawing.Imaging.ImageFormat.Png);
img.Dispose();
|
なおMagickImage.ToBitmapSourceメソッドを使えば、System.Windows.Media.Imaging.BitmapSourceオブジェクトを作成することもできます。
RAW画像を読み込む †
Magick.NETは、.RC2などのRAW画像を読み込むこともできます。ただしその場合は、「dcraw.exe」をMagick.NETのDLLと同じフォルダに入れる必要があります(「dcraw.exe」がないのにRAW画像を読み込もうとすると、ImageMagick.MagickBlobErrorExceptionがスローされます)。「dcraw.exe」は、「Windows Binary Release」からダウンロードできるZIPファイルの中にあります(または、ImageMagickをインストールしても入手できるかもしれません)。
以下に例を示しますが、前の例と全く同じです。
1
2
3
4
5
6
7
8
9
| | Dim img As New ImageMagick.MagickImage("C:\test\1.cr2")
Dim bmp As System.Drawing.Bitmap = img.ToBitmap()
img.Dispose()
PictureBox1.Image = bmp
|
1
2
3
4
5
6
7
8
9
10
| | ImageMagick.MagickImage img =
new ImageMagick.MagickImage(@"C:\test\1.cr2");
System.Drawing.Bitmap bmp = img.ToBitmap();
img.Dispose();
PictureBox1.Image = bmp;
|
私が試した限りでは、RAW画像を読み込むのには結構な時間がかかりました。
PDFファイルを読み込む †
Magick.NETは、PDFやPostscript形式のファイルを読み込むこともできます。ただしこの場合は、Ghostscriptがインストールされている必要があります(Ghostscriptがインストールされていないのに読み込もうとすると、ImageMagick.MagickDelegateErrorExceptionがスローされます)。
Magick.NETはPDFファイルを作成することもできますが、この場合はGhostscriptが必要ありません。
Ghostscriptをインストールしたところで、今までと全く同じ方法でPDFファイルをPNGファイルに変換させてみました。
1
2
3
4
5
6
| | Dim img As New ImageMagick.MagickImage("C:\test\1.pdf")
img.Write("C:\test\1.png")
img.Dispose()
|
1
2
3
4
5
6
7
| | ImageMagick.MagickImage img =
new ImageMagick.MagickImage(@"C:\test\1.pdf");
img.Write(@"C:\test\1.png");
img.Dispose();
|
これは見事に成功し、PNGファイルができました。しかしPNGファイルの画像はPDFの1ページ目のみで、しかも字や絵がスカスカで、まともに読める状態ではありませんでした。
解像度を指定する †
デフォルトでは解像度が72dpiとなるようで、かなり低く、そのためにスカスカになってしまったようです。MagickReadSettings.Densityプロパティを使って解像度を上げてあげれば、きれいになります。ただし、解像度を高くすると画像のサイズが大きくなり、読み込み時間も長くなります。
なおImageMagickの-densityオプションについては、「ImageMagick: Command-line Options」に詳しい説明があります。
1
2
3
4
5
6
7
8
9
10
| | Dim settings As New ImageMagick.MagickReadSettings()
settings.Density = New ImageMagick.MagickGeometry(300, 300)
Dim img As New ImageMagick.MagickImage("C:\test\1.pdf", settings)
img.Write("C:\test\1.png")
img.Dispose()
|
1
2
3
4
5
6
7
8
9
10
11
12
| | ImageMagick.MagickReadSettings settings =
new ImageMagick.MagickReadSettings();
settings.Density = new ImageMagick.MagickGeometry(300, 300);
ImageMagick.MagickImage img =
new ImageMagick.MagickImage(@"C:\test\1.pdf", settings);
img.Write(@"C:\test\1.png");
img.Dispose();
|
すべてのページを読み込む †
PDFファイルのすべてのページを読み込むには、MagickImageの代わりにMagickImageCollectionを使ってPDFファイルを読み込みます。MagickImageCollectionはその名の通りMagickImageのコレクションで、PDFのすべてのページがコレクションに追加されます。
以下の例では、PDFファイル"C:\test\1.pdf"を読み込んで、すべてのページを1枚ずつPNGファイルとして保存しています。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| | Dim settings As New ImageMagick.MagickReadSettings()
settings.Density = New ImageMagick.MagickGeometry(300, 300)
Dim imgs As New ImageMagick.MagickImageCollection("C:\test\1.pdf", settings)
For i As Integer = 0 To imgs.Count - 1
Dim img As ImageMagick.MagickImage = imgs(i)
img.Write(String.Format("C:\test\page-{0}.png", i + 1))
Next
imgs.Dispose()
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| | ImageMagick.MagickReadSettings settings =
new ImageMagick.MagickReadSettings();
settings.Density = new ImageMagick.MagickGeometry(300, 300);
ImageMagick.MagickImageCollection imgs =
new ImageMagick.MagickImageCollection(@"C:\test\1.pdf", settings);
for (int i = 0; i < imgs.Count; i++)
{
ImageMagick.MagickImage img = imgs[i];
img.Write(string.Format(@"C:\test\page-{0}.png", i + 1));
}
imgs.Dispose();
|
もっと簡単にすべてのページを保存する †
実はMagickImageCollection.Writeメソッドを使うと、一発ですべてのページを別々の画像ファイルに保存できます。
例えば以下の例のようにMagickImageCollection.Writeメソッドに"C:\test\1.png"という文字列を渡すと、"C:\test\1-0.png"、"C:\test\1-1.png"...のような名前のファイルがページ数分作成されます。
1
2
3
4
5
6
7
8
9
10
11
| | Dim settings As New ImageMagick.MagickReadSettings()
settings.Density = New ImageMagick.MagickGeometry(300, 300)
Dim imgs As New ImageMagick.MagickImageCollection("C:\test\1.pdf", settings)
imgs.Write("C:\test\1.png")
imgs.Dispose()
|
1
2
3
4
5
6
7
8
9
10
11
12
13
| | ImageMagick.MagickReadSettings settings =
new ImageMagick.MagickReadSettings();
settings.Density = new ImageMagick.MagickGeometry(300, 300);
ImageMagick.MagickImageCollection imgs =
new ImageMagick.MagickImageCollection(@"C:\test\1.pdf", settings);
imgs.Write(@"C:\test\1.png");
imgs.Dispose();
|
すべてのページを1枚の画像に結合させる †
MagickImageCollection.AppendVerticallyメソッドを使うと、すべてのページを縦に連結して1つの画像を作成できます。また、AppendHorizontallyメソッドを使うと、横に結合します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| | Dim imgs As New ImageMagick.MagickImageCollection("C:\test\1.pdf")
Dim vImg As ImageMagick.MagickImage = imgs.AppendVertically()
vImg.Write("C:\test\v.png")
Dim hImg As ImageMagick.MagickImage = imgs.AppendHorizontally()
hImg.Write("C:\test\h.png")
vImg.Dispose()
hImg.Dispose()
imgs.Dispose()
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| | ImageMagick.MagickImageCollection imgs =
new ImageMagick.MagickImageCollection(@"C:\test\1.pdf");
ImageMagick.MagickImage vImg = imgs.AppendVertically();
vImg.Write(@"C:\test\v.png");
ImageMagick.MagickImage hImg = imgs.AppendHorizontally();
hImg.Write(@"C:\test\h.png");
vImg.Dispose();
hImg.Dispose();
imgs.Dispose();
|
読み込むページと枚数を指定する †
MagickReadSettingsのFrameIndexとFrameCountプロパティを使うと、読み込むページと枚数を指定できます。FrameIndexプロパティには、読み込むページのインデックスを指定します。1ページ目から読み込む場合は、0です。FrameCountプロパティには、読み込む枚数を指定します。
以下の例では、PDFファイルの3ページ目だけを読み込んで、PictureBoxに表示しています。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| | Dim settings As New ImageMagick.MagickReadSettings()
settings.Density = New ImageMagick.MagickGeometry(300, 300)
settings.FrameIndex = 2
settings.FrameCount = 1
Dim imgs As New ImageMagick.MagickImageCollection("C:\test\1.pdf", settings)
PictureBox1.Image = imgs(0).ToBitmap()
imgs.Dispose()
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| | ImageMagick.MagickReadSettings settings =
new ImageMagick.MagickReadSettings();
settings.Density = new ImageMagick.MagickGeometry(300, 300);
settings.FrameIndex = 2;
settings.FrameCount = 1;
ImageMagick.MagickImageCollection imgs =
new ImageMagick.MagickImageCollection(@"C:\test\1.pdf", settings);
PictureBox1.Image = imgs[0].ToBitmap();
imgs.Dispose();
|
コメント †