C#と8bit grayscale TIFFの表示

MATLAB

はじめに

蛍光画像などでは8bitグレースケールを扱うことがあります。
Image-Jなど画像処理ソフトウェアを使っても良いですが、仕組みを知っていると効率も上がるので学習してみました。
そのひとつとしてC#とLibTiff.Netを使って8bitグレースケールsingle TIFFを表示する方法を紹介します。
WindowsのVisual C#でコンパイルして動かしました。
実行ファイルはLinuxでも動作します。

環境

WindowsではMicrosoftからVisual C#をダウンロード、プロジェクトを作成して実行ファイルを作成します。

Visual Studio Tools のダウンロード - Windows、Mac、Linux 用の無料インストール
Visual Studio IDE または VS Code を無料でダウンロードします。 Windows、Mac で Visual Studio Professional または Enterprise...

Linuxで動かす場合はWindowsで使用した「.NET Framework」のバージョンに対応したパッケージをインストールする必要があります。

How to Install Mono on Debian 10
This article provides information on how to install Mono on Debian 10. Mono is a free and open-sourc...

LibTiff.Net

libtiffのC#版でWindowsとLinuxで使います。

Read and write TIFF images in C# and VB.NET | LibTiff.Net
LibTiff.Net is a .NET version of libtiff library for reading and writing TIFF images.

コンパイル(環境1)

「スタート」→「Microsoft Visual Studio 2019」→「Visual Studio コマンドプロンプト(2019)」を使います。
ソース名がhoge.cs、実行ファイル名をout.exeとするとき、以下のようにコンパイルします。

csc /lib:.\ /reference:BitMiracle.LibTiff.NET.dll /out:out.exe hoge.cs

ファイルが複数ある場合

csc /lib:.\ /reference:BitMiracle.LibTiff.NET.dll /out:out.exe hoge1.cs hoge2.cs

コンパイル(環境2)

ソースの改行コードはLFにしてください。

mcs -r:./BitMiracle.LibTiff.NET.dll -out:out.exe hoge.cs
又は、
dmcs -r:./BitMiracle.LibTiff.NET.dll -out:out.exe hoge.cs

ファイルが複数ある場合
mcs -r:./BitMiracle.LibTiff.NET.dll -out:out.exe hoge1.cs hoge2.cs
又は、
dmcs -r:./BitMiracle.LibTiff.NET.dll -out:out.exe hoge1.cs hoge2.cs

実行は以下。
chmod 755 ./out.exe
./out.exe

実行結果

Linuxで実行したときのスクリーンショットです。
Windowsでも同様になります。

DLLを登録する

TIFFの解析に「BitMiracle.LibTiff.NET.dll」を使うので「MS Visual C#」に登録します。
「プロジェクト」→「参照の追加」→「参照」タブからファイルを指定する。

出力ウィンドウの表示

「MS Visual C#」を使う場合、「Debug.WriteLine」などでデバッグ情報を出力します。
デバッグ情報は、標準では表示されないので、デバッグ中にメニューバーから操作して表示します。
「デバッグ」→「ウィンドウ」→「出力」

ソース

「8bit.tif」は自前で準備するか、以前の記事のソースを使って作成してください。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

// PixelFormat
using System.Drawing.Imaging;

// Marshal.Copy
using System.Runtime.InteropServices;

// libtiff.net
using BitMiracle.LibTiff.Classic;

// Debug
using System.Diagnostics;

namespace Tiff8bitShowForm
{
    public partial class Form1 : Form
    {
        // TIFFの縦横サイズ
        static public int tif_width, tif_height;

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            int i;
            string fileName = "8bit.tif";

            byte[] tif_byte = readTiff(fileName);

            Debug.WriteLine(tif_width);
            Debug.WriteLine(tif_height);

            PixelFormat bmpPixelFormat = PixelFormat.Format8bppIndexed;
            Bitmap bmp = new Bitmap(tif_width, tif_height, bmpPixelFormat);

            // パレットを設定する(グレースケール)
            ColorPalette palette = bmp.Palette;
            Color[] entry = palette.Entries;
            for (i = 0; i < entry.Length; i++)
            {
                entry[i] = Color.FromArgb(i, i, i);
            }
            bmp.Palette = palette;
            
            // Pixelデータの領域を確保
            //Rectangle rect = new Rectangle(0, 0, tif_width, tif_height);
            Rectangle rect = new Rectangle(Point.Empty, bmp.Size);

            // BitmapDataのインスタンスを生成・BitmapのLock
            // WriteOnly / ReadWrite / ReadOnly / UserInputBuffer
            BitmapData bmpData = bmp.LockBits(rect, ImageLockMode.ReadWrite, bmpPixelFormat);

            /* 方法1
            // 方法1
            for (i = 0; i < tif_height; i++)
            {
                IntPtr line = (IntPtr)((Int64)bmpData.Scan0 + i * bmpData.Stride);
                Marshal.Copy(tif_byte, i * tif_width, line, tif_height);
            }
            */

            ///*
            // 方法2
            // 各項目を抽出
            IntPtr ptr = bmpData.Scan0;

            // マネージ配列からアンマネージメモリ、又はその逆のコピー。
            // アンマネージメモリとは、CLR(Common Language Runtime)に
            // 準拠していないコードを含んでいるコードの総称。
            Marshal.Copy(tif_byte, 0, ptr, tif_byte.Length);
            //*/

            // BitmapのLock解除
            bmp.UnlockBits(bmpData);

            // PictureBoxのサイズ設定・描画
            pictureBox1.Image    = bmp;
            pictureBox1.Location = new Point(0, 0);
            pictureBox1.Size     = bmp.Size;

            // Formのサイズ設定・リサイズ禁止
            //this.ClientSize = new System.Drawing.Size(tif_width, tif_height);
            this.ClientSize = bmp.Size;
            this.FormBorderStyle = FormBorderStyle.FixedSingle;
        }

        // read TIFF
        private static byte[] readTiff(string fname)
        {
            int    i, j;
            ushort samplesperpixel, bitspersample;
            double resolution_x, resolution_y;

            using (Tiff tif = Tiff.Open(fname, "r"))
            {
                if (tif == null)
                {
                    Debug.WriteLine("Can not read Tiff file.");
                }

                tif_width  = tif.GetField(TiffTag.IMAGEWIDTH)[0].ToInt();
                tif_height = tif.GetField(TiffTag.IMAGELENGTH)[0].ToInt();

                resolution_x    = tif.GetField(TiffTag.XRESOLUTION)[0].ToDouble();
                resolution_y    = tif.GetField(TiffTag.YRESOLUTION)[0].ToDouble();
                bitspersample   = tif.GetField(TiffTag.BITSPERSAMPLE)[0].ToUShort();
                samplesperpixel = tif.GetField(TiffTag.SAMPLESPERPIXEL)[0].ToUShort();

                Debug.WriteLine(tif_width);
                Debug.WriteLine(tif_height);
                Debug.WriteLine(resolution_x);
                Debug.WriteLine(resolution_y);
                Debug.WriteLine(bitspersample);
                Debug.WriteLine(samplesperpixel);

                // 画像データの確保
                byte[] buf_byte = new byte[tif_width * tif_height * sizeof(byte)];

                // 1ラインのデータ配列
                byte[] scanline = new byte[tif.ScanlineSize()];

                Debug.Indent();

                // 読み取ったデータを格納
                for (i = 0; i < tif_height; i++)
                {
                    tif.ReadScanline(scanline, i);
                    for (j = 0; j < tif_width; j++)
                    {
                        buf_byte[tif_width * i + j] = scanline[j];
                        //Debug.WriteLine(scanline[j]);
                    }
                }

                return buf_byte;
            }
        }
    }
}

Comments