【画像処理】ImageJで画像を処理してみる

第9回 ImageJで画像を処理してみる
前回の記事→「[第8回 ヘッダ情報の確認とバイナリエディタからの画像編集(BMP編)](https://qiita.com/spc_ehara/items/03d179f4901faeadb184)」

サイエンスパーク株式会社の@spc_ysuzukiです。
今回は私が担当させていただきます。

# ImageJとは

## 概要

ImageJはJavaで実装されたクロスプラットフォーム対応のオープンソース画像処理ソフトウェアです。
プラグインにより拡張や、スクリプトを使用して処理を自動化することもできます。
今回は画像処理は実際にどのような手順で進めていくかといった雰囲気を掴むため、ImageJを使用して画像処理を行っていきます。

https://imagej.nih.gov/ij/

https://imagej.net/

今回はImageJに多数のプラグインを追加したFiji (Fiji Is Just ImageJ)を使用します。

OSはWindows 10を使用しますが、ImageJはクロスプラットフォーム対応なので他のOSでも同様に使用できるはずです。

## ImageJのダウンロード

以下のURLから使用しているOSを選択することでダウンロードできます。
Zip形式になっているのでインストールは必要ありません。
展開して適当な場所に置いておきます。

https://imagej.net/software/fiji/downloads

## ImageJの起動

実行ファイル（64bit版Windowsの場合はImageJ-win64.exe）を実行してImageJを起動します。
初回起動時はアップデートを行うか聞かれるので、最新版にアップデートしておきます。

起動すると以下のようなウィンドウが開くはずです。
このウィンドウをメインウィンドウと呼びます。
ImageJはこのウィンドウを使用して操作を行います。

![imagej-main.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1934111/0f256dc1-bb02-8b47-544b-1892473243ca.png)

# 画像内の文字数をカウントする

それでは、実際に画像処理を行ってみます。
画像処理技術を駆使して以下の画像から白い部分内部の文字のみを抽出し、その数をカウントしてみます。
ファイル名はsciencepark.jpgとします。

![sciencepark.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1934111/a744bfbc-4028-9d6a-f2b3-5abe87e6673c.jpeg)

## 画像を開く

ImageJのメインウィンドウのメニューからFile→Openを選択し、sciencepark.jpgを開きます。
以下のようなウィンドウが表示されます。

![imagej-sciencepark.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1934111/567802f8-7f96-b284-e7b9-c6c1f0234857.png)

## グレースケールに変換

ほとんどの画像処理は色のないグレースケール画像に対して行います。
現在開いている画像はカラー画像なので、グレースケールに変換します。

1.  変換アルゴリズムの指定

    メニューのEdit→Options→Conversions...を選択し、表示されたウィンドウのWeighted RGB conversionsのチェックを外してOKを押します。

    今回は単純なRGBの平均値を計算するアルゴリズムでグレースケールに変換します。
    変化アルゴリズムについては第3章 第11回で詳しく解説する予定です。

1. グレースケールに変換

    メインウィンドウのメニューからImage→Type→8-bitを選択します。
    1.で指定したアルゴリズムでグレースケールに変換します。

以下のように色が消えたグレースケールに変換されます。

![imagej-sciencepark-gray.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1934111/daf406f4-4f70-507a-95dd-73489dbbce41.png)

## 二値化

グレースケール化に続いて画像を白と黒のみで表す二値化を行います。
これにより背景と文字をコンピュータが認識できるようになります。

メニューのProcess→Binary→Make Binaryを選択すると、以下のように二値化されます。
自動的にしきい値を計算し、それより暗い部分が白、明るい部分が黒に変換されます。
このアルゴリズムに関しては、第6章 第25回で詳しく解説する予定です。
二値化画像では**白を物体**、**黒を背景**として扱います。

設定によっては以下の画像と比較すると色が反転していて、画像の上の部分に(inverting LUT)と表示される場合があります。
この場合は反転しているのは表示だけで、メニューのImage→Lookup Table→Graysを選択すると同じ表示になります。

![imagej-sciencepark-bin.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1934111/2dda7d8c-c7ec-2764-e204-2dcf48b9edb9.png)

## モルフォロジー変換

二値化が終わったので早速文字の抽出をしたいところです。
今回は白い島の数を数えることで文字数を数える予定なのですが、このままでは以下のようにいくつか問題があります。

1. 「i」や「j」、「:」は1文字に対して島が2つある

1. 背景やロゴ、木目、少し見づらいですが白い部分の外周の画鋲など、文字とは関係のない島がある

背景やロゴに対しては文字とみなす島の面積に上限を設ければ良いのですが、他の問題はこのままでは対処できません。
そこでモルフォロジー変換のクロージングを使用した画像の処理を行います。

モルフォロジー変換には処理方法が何種類かありますが、今回使用するクロージングは近い島を繋げる処理です。
この処理により「i」や「j」、「:」、画鋲と背景、木目による島と背景をそれぞれ繋げることができます。
横方向に繋げてしまうと複数の文字が1つの島になってしまうので、縦方向にのみ繋げるようにします。
文章だけでは分かりづらいので実際にやってみます。

メニューからProcess→Morphology→Gray Morphologyを選択し、パラメータを設定するウィンドウを開きます。
このウィンドウでは以下の図(a)のように設定します。
Radius of the structure element (pixel)で繋げる隙間の距離（の1/2）を5ピクセルに、Type of structure elementで繋げる方向を縦のみに、Operatorで処理方法をクロージングに設定しています。
OKを押すと以下の変換前の図(b)が処理され図(c)のようになります。

「i」や「j」、「:」は隙間が埋まり1つの島になり、画鋲も背景と一体化、木目も白い島がなくなったことになり、先ほど挙げた問題がすべて解決しました。
「S」や「e」などの文字はつぶれてしまっていますが、今回は文字数を数えるだけなので問題ありません。
さらに処理を追加することで下に残ってしまった木目を消すこともできますが、次の作業に影響はないのでこれで良しとします。
モルフォロジー変換については第6章 第27回で詳しく解説する予定です。

| (a) パラメータ | (b) モルフォロジー変換前 | (c) モルフォロジー変換後 |
|:--------------:|:------------------------:|:------------------------:|
| ![imagej-morphology-params.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1934111/a530a2ae-c0ad-3f51-b85d-a6866aa13e7b.png) | ![imagej-sciencepark-bin.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1934111/2dda7d8c-c7ec-2764-e204-2dcf48b9edb9.png) |![imagej-sciencepark-morphology.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1934111/26eaf52f-4868-44a3-c71b-bed9e8e6bcfb.png) |

## 文字数を数える

最後に島の数を数えることで文字数を数えます。
まだ余計な島がいくつかありますが、全て文字よりも十分大きいので文字としてカウントする島の面積に上限を付けてカウント対象に含めないようにします。

メニューからAnalyze→Analyze Particles...を選択します。
表示された設定画面で以下の図(a)のように設定します。
Size (pixel^2)で対象とする島の面積を0から1500にしています。
これにより文字ではない島を文字としてカウントしないようにします。
ShowをOverlayに設定することで画像の上に検出結果が表示されます。
Display resultsにチェックを入れることで島一覧が表示されるようになります。
設定ができたらOKを押します。

処理結果は以下の図(b)のような処理画像へのオーバーレイと(c)のような島一覧ウィンドウとして表示されます。
(b)から正しく文字の島のみが抽出され、少し見づらいですがそれぞれの島に番号が振られていることが分かります。
また、(c)の島一覧の一番下の行の左の列から、57個の島が見つかったことが分かります（この画像では見えませんが一番上は1から始まっています）。
これにより画像の白い部分内部の文字数は57文字であることが分かりました。

このような島の検出と分類はラベリング処理と呼ばれます。
アルゴリズムやOpenCVでの使い方については[PythonでOpenCVを使わずにラベリング処理](https://qiita.com/spc_ehara/items/8d2348f38de54b6a24dc)や[PythonでOpenCVを使ったラベリング処理](https://qiita.com/spc_ehara/items/72e878fc131b151c01fa)で詳しく解説します。

| (a) パラメータ | (b) 処理結果画像 | (c) 島一覧 |
|:--------------:|:----------------:|:----------:|
| ![imagej-analyze-particles.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1934111/df5e48af-d4e9-6d4b-ddff-32e38b46e2d1.png) | ![imagej-sciencepark-analyze-particles.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1934111/69e24e22-c9cf-c365-d249-4dac0199820c.png) | ![imagej-sciencepark-analyze-particles-result.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1934111/8c7c69c8-58f4-4ed1-c53f-688f88d3b8e1.png) |

# まとめ

今回はImageJの紹介とこれを使用した画像処理を行いました。
画像処理というと難しそうに聞こえますが、比較的シンプルな処理の組み合わせでしかありません。
しかし、どの処理を選びどう組み合わせるかによって性能が変わってきます。
今後はこれらの個々の処理やその理解に必要な概念について詳しく解説していきます。

この講座の目次一覧・構成はこちら↓です。
[【連載】サルでもわかる画像処理講座](https://qiita.com/spc_qiita/items/9825260d89948f4fa1a4)

# 参考

1. https://imagej.nih.gov/ij/
1. https://imagej.net/
1. https://imagej.nih.gov/ij/docs/index.html