2017年5月17日水曜日

C# Magick.NETを使ってみる。その1

C# Magick.NETを使ってみる
目次→http://1studying.blogspot.jp/2017/07/c-magicknetmokuzi.html#kuw01

「Magick.NET」は「ImageMagick」のC#版です。

100種類以上の画像フォーマットに対応。
RGBだけで無く、CMYK、グレー、2値画像等が扱えます。
色々な事が出来る分メモリを贅沢に使う為、過度な処理速度は期待しない方がよいでしょう。
RGBメインな画像加工の場合は「OpenCvSharp」の使用も検討してみて下さい。

「Magick.NET」の使い方は
https://github.com/dlemstra/Magick.NET/tree/master/Documentation
に載っています。

バージョン7.0.0.0102以降は「.NETCore」もサポートされていますが、
今のところWindowsのみ対応となっています。

「Magick.NET」は現在、Macからは使用できないようです。
試しにMacで使用してみたのですが、
「DllNotFoundException」のエラーメッセージで蹴られてしまいました。
Macから呼ばれるDLL関数に対応していないようです。
「GitHub」を覗くと、
Macへの将来的な対応をほのめかしているので今後に期待したいです。
https://github.com/dlemstra/Magick.NET/issues


一方、Windowsでは「VisualStudio」から「NuGet」ですぐに使う事ができます。
バージョン6迄と7以降で変更点が多いようです。
不透明度の代わりにアルファを使用するようになっており、
構文の記述に違いがあります。
var6→7への「ImageMagick」の変更点は
https://www.imagemagick.org/script/porting.php
に記載されています。

個人的に嬉しいのは、バージョン7以降でグレースケール画像を使用する際、
今までは「赤、緑、青、アルファ」の4チャンネル消費していたのが、
「グレースケール」1チャンネルの使用に変更されたところです。

私は使った事が無いのですが、
「Magick.NET.Web」というDLLも存在します。
画像処理をXML的な記述でスクリプト化できるようです。
https://github.com/dlemstra/Magick.NET/blob/master/Documentation/Magick.NET.Web.md
に記載されています。

では、「Magick.NET」を使用してみます。


準備


「ファイル→新規作成→プロジェクト」
「テンプレート→VisualC#→Windowsクラシックデスクトップ→Windowsフォームアプリケーション」
を作成します。


私はプロジェクトの名前を「testMagick」としました。

次に、
「ソリューション」を右クリックして「プロパティ」を表示させます。


「構成プロパティ→構成」の「プラットフォーム」が「AnyCPU」である事を
確認しておきます。
(x86やx64を明示しておかなくても対応したMagick.NETのDLLが自動判別されます。)
「NuGet」で「.NET Core」バージョンの「Magick.NET.Core-Q8」等を使う場合、
確認の必要はありません。

今回は「NuGet」で「Magick.NET-Q8-AnyCPU」をインストールします。
(「Q8」は8bit階調0〜255、「Q16」は16bit階調0〜65535が扱えます)



「Form1」のデザイナへ
「button1」、「label1」、「pictureBox1」を配置します。


配置したら、「button1」をダブルクリックして、
「button1」のクリックイベントのコードを表示させておきます。
        private void button1_Click(object sender, EventArgs e)
        {

        }


テスト用の元画像が必要であれば「神奈川工科大学」のサイトから、
カラーの「Lenna.bmp」画像のオリジナルを取得しておきましょう。
http://www.ess.ic.kanagawa-it.ac.jp/app_images_j.html
レナさんは超有名な標準画像ですね。
ここでのオリジナル「横256×縦256pixel、8bit、RGB画像」を使用します。
(本来のオリジナルで「横512×縦480pixel」版が存在しますが、今回は使用しません)

ダウンロードで取得した「Lenna.bmp」画像を、
プロジェクトフォルダの「/bin/Debug」フォルダ内へ入れておいて下さい。


「Magick.NET」を使ってみる


とりあえず使ってみます。
「button1」のクリックイベントのコードを以下の形にします。
        private void button1_Click(object sender, EventArgs e)
        {
            //ピクチャーボックス表示用
            System.Drawing.Bitmap myBitmap;
            //キャンバス画像を作成 100*50pixel
            var myMagick = new ImageMagick.MagickImage( new ImageMagick.MagickColor("#00ff00"), 100, 50 );
            //ピクチャーボックスへ表示する為、Bitmapへ変換
            myBitmap = myMagick.ToBitmap();
            //リソース解放
            myMagick.Dispose();
            //pictureBox1へ表示
            pictureBox1.Image = myBitmap;
        }
「実行」して「ボタン」をクリック


「100×50Pixel」の緑の画像を生成し、
ピクチャーボックスへ表示させる為にBitmapへ変換して表示を行いました。

同じ内容を、
usingを使って書く事も出来ます。(IDisposeインターフェースを継承している)
        private void button1_Click(object sender, EventArgs e)
        {
            //ピクチャーボックス表示用
            System.Drawing.Bitmap myBitmap;
            //キャンバス画像を作成 100*50pixel
            using (var myMagick = new ImageMagick.MagickImage(new ImageMagick.MagickColor("#00ff00"), 100, 50))
            {
                //ピクチャーボックスへ表示する為、Bitmapへ変換
                myBitmap = myMagick.ToBitmap();
            }
            //pictureBox1へ表示
            pictureBox1.Image = myBitmap;
        }
当然、処理結果は先程と同じとなります。
言わずもがなですが、
リソース解放や例外処理の事を考えるとusingを使った方がコードが楽に書けます。




描画系


「ImageMagick」の「magick(旧convert)」コマンドの「-draw」系オプションにあたるのが
「new ImageMagick.Drawables()」メソッドです。
メソッドチェーンでの記述が可能です。

楕円と線の描画
「button1」クリックイベントのコードを以下の形に書き換えて下さい。
        private void button1_Click(object sender, EventArgs e)
        {
            System.Drawing.Bitmap myBitmap;
            //キャンバス画像を作成 150*150pixel
            using (var myMagick = new ImageMagick.MagickImage(new ImageMagick.MagickColor("#00ff00"), 150, 150))
            {
                var myDraw=new ImageMagick.Drawables();
                myDraw.StrokeColor(ImageMagick.MagickColors.Black);
                myDraw.StrokeWidth(10);
                myDraw.FillColor(new ImageMagick.MagickColor("yellow"));
                myDraw.Ellipse(75, 75, 75, 50, 0, 360); //楕円を描く
                //メソッドチェーンで線を描く
                myDraw.StrokeColor(ImageMagick.MagickColors.Blue)
                      .StrokeWidth(10)
                      .Line(50, 50, 100, 100);
                myDraw.Draw(myMagick); //実描画
                myBitmap = myMagick.ToBitmap(); //Bitmapへ変換
            }
            pictureBox1.Image = myBitmap; //pictureBox1へ表示
        }
↓「実行」して「ボタン」をクリック

このように表示されます。

描画手順も「ImageMagick」で使う「magick(旧convert)」コマンドに近いイメージです。
  「-stroke」(線色)オプション→「StrokeColor()」メソッド
  「-strokewidth」(中心からの線幅)オプション→「StrokeWidth()」メソッド
  「-fill」(塗り色)オプション→「FillColor()」メソッド
をそれぞれ使用して「線色、線幅、塗り色」を指定します。

色を何色にするかの指定には以下の記述方法があります。
  「new ImageMagick.MagickColor("#00ff00")」
  「new ImageMagick.MagickColor(0, ImageMagick.Quantum.Max, 0)」
  (「ImageMagick.Quantum.Max」は扱っている階調の最大値が取得できます。
  使用しているDLLが「Q8」だと「255」、「Q16」だと「65535」となります。
  ここでは、「new ImageMagick.MagickColor(0, 255, 0)」と同じになります。)
  「new ImageMagick.MagickColor("green")」
  「ImageMagick.MagickColors.Black」
  「ImageMagick.MagickColors.None」(塗らない)
使用の「色空間」が「RGB」であればこれらの記述方法で記述を行うと良いでしょう。


角丸四角と文字列の描画
「button1」クリックイベントのコードを以下の形に書き換えて下さい。
        private void button1_Click(object sender, EventArgs e)
        {
            System.Drawing.Bitmap myBitmap;
            //キャンバス画像を作成 150*150pixel
            using (var myMagick = new ImageMagick.MagickImage(new ImageMagick.MagickColor("#00ff00"), 150, 150))
            {
                //角丸四角と文字列を描く
                new ImageMagick.Drawables()
                    //角丸四角
                    .StrokeColor(ImageMagick.MagickColors.Black)
                    .StrokeWidth(3)
                    .FillColor(ImageMagick.MagickColors.None) //塗りを行わない
                    .RoundRectangle(10,10,140,140,40,40) //角丸四角描画
                    //文字列
                    .FillColor(ImageMagick.MagickColors.Blue) //塗り青
                    .FontPointSize(60) //文字列の大きさ
                    .Font("Comic Sans") //文字列の書体
                    .TextAlignment(ImageMagick.TextAlignment.Center) //文字列の揃え
                    .Text(75, 75, "Text") //文字列描画
                    .Draw(myMagick); //実描画
                myBitmap = myMagick.ToBitmap(); //Bitmapへ変換
            }
            pictureBox1.Image = myBitmap; //pictureBox1へ表示
        }
↓「実行」して「ボタン」をクリック

文字列を描画する際、手前で設定していた「線色と線幅」がまだ生きている為、
文字列に黒縁が付いた表示となります。



画像を開いて保存


既に準備してある「/bin/Debug」フォルダ内の「Lenna.bmp」を使用します。

「bmp画像を開く」→「50%縮小」→「グレースケール化」→「tif形式で保存」
「button1」クリックイベントのコードを以下の形に書き換えて下さい。
        private void button1_Click(object sender, EventArgs e)
        {
            System.Drawing.Bitmap myBitmap;
            //画像読み込み
            using (var myMagick = new ImageMagick.MagickImage("./Lenna.bmp"))
            {
                myMagick.Resize(new ImageMagick.MagickGeometry("50%")); //縮小
                myMagick.Grayscale(new ImageMagick.PixelIntensityMethod()); //グレー変換
                //出力形式をtifで書き出し
                myMagick.Write("./Lenna.tif");
                myBitmap = myMagick.ToBitmap(); //Bitmapへ変換
                label1.Text=myMagick.Format.ToString(); //フォーマット表示
            }
            pictureBox1.Image = myBitmap; //pictureBox1へ表示
        }
↓「実行」して「ボタン」をクリック

「Lenna.bmp」の同階層に加工された「Lenna.tif」ファイルが書き出されます。
確認してみて下さい。
レナさんが50%縮小され、グレー化された物がTiff画像で書き出されているはずです。

開いた画像形式(Bmp)とは違う画像形式(Tif)で保存を行っても、
所持している画像フォーマットの形式はファイルを開いた時のまま変わりません。
この例でも「ラベル」が「Bmp3」と表示され、ビットマップのままとなっています。
(「Bmp3」はBitmapのバージョン3という意味です)
「 myMagick.Format = ImageMagick.MagickFormat.Png; 」
とすれば所持フォーマットは任意に変更できます。


「Magick画像」と他バイナリ形式との相互変換


ここでは、「Magick画像」(「Magick.NET」の画像)と、
「ストリーム」や「バイト配列」等の相互変換の方法を説明します。

「ストリーム」や「byte[]」を「Magick画像」として開く
「ストリーム」や「byte[]」の取り扱いについてはリンク先で説明しました。
「C# バッキングストアとストリームについて」…
http://1studying.blogspot.jp/2017/05/c.html

では実際に「Magcik.NET」で画像を開いてみましょう。
「button1」クリックイベントのコードを以下の形に書き換えて下さい。
        private async void button1_Click(object sender, EventArgs e)
        {
            var myBitmap = new Bitmap(150, 150); //「Bitmap画像」を作成
            using (Graphics g = Graphics.FromImage(myBitmap))
            {
                g.FillRectangle(Brushes.LawnGreen, g.VisibleClipBounds); //塗りつぶし
                g.DrawLine(new Pen(Color.Black, 5), 20, 20, 130, 130); //線を引く
            }
            //「Bitmap画像」→「byte[]」へ変換
            byte[] byteArray = (byte[])new ImageConverter().ConvertTo(myBitmap, typeof(byte[]));

            //「byte[]」を「Magick.NET」で開く
            using (var myMagick = new ImageMagick.MagickImage(byteArray))
            {
                new ImageMagick.Drawables()
                    .StrokeColor(ImageMagick.MagickColors.Black)
                    .StrokeWidth(5)
                    .Line(20, 75, 130, 75)
                    .Draw(myMagick); //実描画
                pictureBox1.Image = myMagick.ToBitmap(); //pictureBox1へ表示
            }

            await Task.Delay(1000); //非同期で1秒待機

            //「byte[]」→「ストリーム」を生成
            using (var ms = new System.IO.MemoryStream(byteArray))
            {
                //ストリームを「Magick.NET」で開く
                using (var myMagick = new ImageMagick.MagickImage(ms))
                {
                    new ImageMagick.Drawables()
                        .StrokeColor(ImageMagick.MagickColors.Black)
                        .StrokeWidth(5)
                        .Line(20, 130, 130, 20)
                        .Draw(myMagick); //実描画
                    pictureBox1.Image = myMagick.ToBitmap(); //pictureBox1へ表示
                }
            }
↓「実行」して「ボタン」をクリック

↓1秒後

と表示されます。
最初の画像が「byte[]」を「Magick.NET」で開いた物で、
次の画像が「ストリーム」を「Magick.NET」で開いた物です。

「 ImageMagick.MagickImage」クラスは、
コンストラクタ時の引数で「ファイル」、「Bitmap」、「ストリーム」、「byte[]」等、
一通り開けるようです。とても便利。
書き出しは以下のようにします。

「ファイル」や「Bitmap」や「ストリーム」や「byte[]」へ書き出す
「Magick画像」を他の形態へ書き出すには以下の方法があります。
using(var myMagick = new ImageMagick.MagickImage(ファイル読み込み)){
    //「ファイル」へ書き出し
    myMagick.Write(@"./test1.tif");
    //「Bitmap画像」へ書き出し
    Bitmap myBitmap = myMagick.ToBitmap();
    //「ストリーム」へ書き出し
    using (var ms = new System.IO.MemoryStream())
    {
        //(Bmp3はBitmapのバージョン3という意味です)
        myMagick.Write(ms, ImageMagick.MagickFormat.Bmp3);
    }
    //「byte[]」へ書き出し(出力形式をBmp3にして書き出し)
    myMagick.Format = ImageMagick.MagickFormat.Bmp3;
    byte[] byteArray = myMagick.ToByteArray();
}




その2へ続きます…



「C# Magick.NETを使ってみる。その2」へ続きます…
http://1studying.blogspot.com/2017/05/c-magicknet_31.html




「標準画像/サンプルデータ」は「神奈川工科大学」のサイトの物を利用しました。
http://www.ess.ic.kanagawa-it.ac.jp/app_images_j.html
「ImageMagick コマンドリファレンス」
http://image-magick.com/

IM使用例→http://www.imagemagick.org/Usage/draw/


0 件のコメント:

コメントを投稿

↑Topへ