画像解析実践入門_指紋の稜線分析 1(前処理編)

# 概要
主にpythonで画像処理について本格的に学び始めたのですが、実際に使う機会が欲しくてテーマを探していました。
研究室に埋もれていた書籍「MATLAB画像処理入門」に指紋解析について乗っていましたので,python風にコードを書き換えながら画像解析について理解を深めます。

## もくじ
1. 指紋の画像データセットについて
2. 使用するpythonライブラリ
3. 画像の読み込み~二値化
までを行います

## 1.指紋の画像データセットについて
kaggleに<Sokoto Coventry Fingerprint Dataset (SOCOFing)>データセットがあったのでこちらを使います。
[https://www.kaggle.com/ruizgara/socofing]
ダウンロードしたデータの /SOCOFing/Real/* の画像を使いましょう。
##2.使用するpyhonライブラリ
- Numpy #行列計算に使用
- matplotlib #画像とグラフのプロット
- Scikit-image #画像処理に利用

の３つをインポートします。
ちなみに画像処理に使うライブラリですがopencvではなくあえてSkimageを使っています。

```import.py 
import numpy as np
from skimage import io
from skimage.util import crop
import matplotlib.pyplot as plt
```
## 3. 画像の読み込み～二値化
早速画像を読み込んで処理を実行していきますが今回

- 画像の読み込みと確認
- トリミング
- エッジを保持したノイズ処理
- 二値化

順で説明します。
###画像の読み込みと確認

```imread_and_plt.py
#画像をグレースケールで読み込み
img = io.imread('1__M_Left_thumb_finger.BMP',as_gray =True)
print(img.shape) #(103,93)

#画像を確認する
plt.imshow(img,cmap='gray')
```

![origin_finger.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/644898/cf216c78-5b84-ea0c-6190-f88c6bf60c55.png)

画像の読み込みと確認ができました。
### トリミング
読み込んだ画像は左、上に縁が写ってしまっているのと右、下に余白ができています。解析に余計な画素はトリミングしてしまいます。

```crop.py
#縁を切り取り
img_crop=crop(img,((7,6),(3,3)),copy=True)
print(img_crop.shape) #(90,90)
plt.imshow(img_crop,cmap='gray')
```
>crop関数について
>[https://scikit-image.org/docs/dev/api/skimage.util.html#skimage.util.crop]

![crop_fingerprint.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/644898/da981c63-d04a-2df4-89b0-5c92344af30a.png)

### エッジを保持したノイズ処理(ノンローカルミーンフィルタ)
読み込んだ画像には暗い画素が指紋の山の部分で明るい画素が指紋の谷部位もしくはなにもない領域と言えます。この指紋の稜線は本来同じ値の画素が一続きに並んだ「連結成分」だがこれとは関係ない
散発的に存在する画素(つまりノイズ)があるためこれを除去(ノイズフィルタリング)したいところです。
_ノイズフィルタリングとして有名なのは主にガウシアンフィルタ_だが、指紋においては山と谷の連続した線が非常に大切であるため、この線(エッジ)を残しつつフィルタリング処理をする必要がある。
そこでおすすめなのが__ノンローカルミーンフィルタ__による処理になります。
>ノンローカルミーンフィルタについて
>[https://scikit-image.org/docs/stable/auto_examples/filters/plot_nonlocal_means.html#sphx-glr-auto-examples-filters-plot-nonlocal-means-py]


```noise_reduction.py
from skimage.filters import gaussian
from skimage.restoration import denoise_nl_means, estimate_sigma

#ガウシアンフィルタ
gauss_img = gaussian(img_crop)

#ノンローカルミーンフィルタ
sigma_est = np.mean(estimate_sigma(img_crop))
nl_img = denoise_nl_means(img_crop,h = sigma_est*1.2)

#画像を比較
plt.figure(figsize = (15,15))
plt.subplot(131)
plt.imshow(img_crop,cmap = 'gray')
plt.title('origin')

plt.subplot(132)
plt.imshow(gauss_img,cmap = 'gray')
plt.title('gauusian')

plt.subplot(133)
plt.imshow(nl_img,cmap = 'gray')
plt.title('NL_means')
```

![3fig_dinoise.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/644898/e21c12bb-8a24-8880-9156-82149a6c9603.png)

左からオリジナル画像、ガウシアンフィルタ、ノンローカルミーンフィルタ による画像を表示しています。ノンローカルミーンフィルタによって明らかにノイズが減少し、かつ稜線もくっきりと残っていることが確認できます。

##　二値化
読み込んだ画像は0~255の値を取る8bitのデータです。これを
__指紋の山(暗い画素)  -> 1（解析対象）
指紋の谷(明るい画素)  -> 0 (背景)__
となるようにする(二値化)必要があります。
二値化することで画像に対して適応できる処理アルゴリズムが数多くあるからです。

二値化する方法ですが有名なのは大津法で求めたしきい値による二値化です。
>大津法について
>[https://scikit-image.org/docs/dev/api/skimage.filters.html#skimage.filters.threshold_otsu]

####大津法による二値化

```ootu.py
from skimage.filters import threshold_otsu
#大津法で二値化
bin_otsu = nl_img < threshold_otsu(nl_img)
#画像を比較
plt.figure(figsize = (12,12))
plt.subplot(121)
plt.title('origin')
plt.imshow(nl_img,cmap = 'gray')

plt.subplot(122)
plt.title('thresh_ootu')
plt.imshow(bin_otsu,cmap = 'gray')
```

![ootu.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/644898/59f3ffda-d391-3c05-a31f-a03df12161f6.png)

残念ながら良い結果とは言えません。
画像の上部分の稜線が消えているのと下中心部が潰れて稜線がつながっています。
これは指紋を読み取るとき指圧が領域によって異なってしまうことが原因として考えられます。
この場合は__指紋の読み取り面に対し指の下中心に強い指圧がかかり、逆に上部分に掛けて指圧が減少__しているのでしょう。
このような画像に対しての二値化手法として__適応的しきい値処理__が効果的です。
>適応的しきい値について
>[https://scikit-image.org/docs/stable/api/skimage.filters.html#skimage.filters.threshold_local]

####適応的しきい値処理

```adaptiveThreshold.py
from skimage.filters import threshold_local

#適応的しきい値処理による二値化
bin_locla=nl_img < threshold_local(nl_img,block_size=21)

plt.figure(figsize=(12,12))
plt.subplot(121)
plt.title('origin')
plt.imshow(nl_img,cmap='gray')

plt.subplot(122)
plt.title('thresh_local')
plt.imshow(bin_locla,cmap='gray')

```
![adapptiv.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/644898/419b10a7-8454-dd16-fb82-21fc75acbb00.png)

かなり良くなりました。

#まとめ
今回は画像の読み込みから二値化という、画像分析のための前処理についてまとめました。
次回からこの二値化画像を用いて様々な分析を行いますのでお楽しみに。
続く

https://qiita.com/kentarou524/items/7d26d6a1230fbfbb27ab

続きです