Pythonでテクスチャ解析をやってみる

Pythonでテクスチャ解析をやる方法についてまとめました。

# テクスチャ解析とは
テクスチャ解析は、画像の質感を数値化する画像処理の手法のひとつです。
画像の滑らかさ、粗さ、周期性などの度合いを数値化します。
[テクスチャ解析についてはこちらの記事でまとめてくれています。](https://qiita.com/T_Shinaji/items/4b1e1516444dffb6f823)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1447216/5f5b6b4d-0bfc-5b0f-6038-5a7e1561b73d.png)
																																
																																
																																
																																
																																
																																
																																
																																
																																
																																
																																


画像によってサイズやピクセルごとに取り得る値の範囲などが一定でないため、
これらの影響を少なくするためにテクスチャ解析前には画像の正規化が必要になります。

正規化するためのアルゴリズムには
- GLCM (Gray-Level Co-occurrence Matrix)
- GLSZM (Gray Level Size Zone Matrix)
- NGTDM (Neighbourhood Gray-Tone-Difference Matrix)
などがありますが、どれも画像を行列に変換することで正規化します。

今回はテクスチャ解析で最も基本的なGLCMをつかってみます。
[GLCMの詳細についてはこちらの記事でまとめてくれています。](https://qiita.com/tatsunidas/items/c13674baf0bd499271b2)


# scikit-imageによるテクスチャ解析
今回は画像処理をおこなうPythonモジュールのひとつ、
scikit-imageを用いてテクスチャ解析をしてみます。
[scikit-imageの公式ドキュメント](https://scikit-image.org/docs/0.18.x/auto_examples/features_detection/plot_glcm.html?highlight=glcm%20texture%20features)を参考にしてやってみます。

### 環境
- Windows 10
- Python 3.8
- scikit-image 0.18.1
- jupyter lab

### モジュールのインポート
```python
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from skimage.feature import greycomatrix, greycoprops
from skimage import data
```
### データの読み込み
今回はscikit-imageに入っているサンプル画像gravelを使います。

```
image = data.gravel()
plt.imshow(image, cmap=plt.cm.gray)
plt.show()
```
![gravel.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1447216/b14f5f91-72ba-22fa-c8df-8f63afdb90d1.png)

### パッチ画像の作成
PATCH_SIZEは今回は11にします。
locationsは作成するパッチの中心座標のリストです。

```
PATCH_SIZE = 11
locations = [(480, 460), (122, 203), (444, 192), (455, 453), (345, 145)]
patches = []
for loc in locations:
    patches.append(image[loc[0]-int(PATCH_SIZE/2):loc[0]+int(PATCH_SIZE/2+1),
                         loc[1]-int(PATCH_SIZE/2):loc[1]+int(PATCH_SIZE/2+1)])
```

作成したパッチを表示してみます。

```
fig = plt.figure(figsize=(10, 10))
for i, patch in enumerate(patches):
    ax = fig.add_subplot(3, len(patches), len(patches)*1 + i + 1)
    ax.imshow(patch, cmap=plt.cm.gray,vmin=0, vmax=255)
    ax.set_xlabel('location %d' % (i + 1))
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1447216/6643ae5b-7671-8389-b687-818d904e72c1.png)



### パラメータ設定
- ピクセル間の距離
- ピクセル間の角度

今回は4つ右隣のピクセルとの関係をみる。
  
```
distances = [4]
angles = np.deg2rad([0]) # 度で入れラジアンに変換
```
どちらもリストだが1つ目しか反映されない？
### GLCMの作成と各指標の計算
パッチごとにGLCMを求め、指標を算出する。

GLCM作成のイメージ図（distances = [1], angles = [0]の場合）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1447216/17c0edbf-5a08-f732-e645-9c120dd58f57.png)
1つ右に隣接するピクセルとの関係をみるので、
(1,2)のペアは1つ、(4,4)のペアは3つあることがわかる。

scikit-imageで使用できるテクスチャ解析の指標は以下のもの。
似たような指標が多いですが求め方が異なります。
- contrast
- dissimilarity (不均一性)
- homogeneity (均一性)
- ASM (一様性)
- energy
- correlation


定義は以下の通り。PはGLCM。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1447216/6b791dba-7345-b1c9-a53d-0145e8d290c8.png)


```
contrast = []
dissimilarity = []
homogeneity = []
ASM = []
energy = []
correlation = []
for patch in patches:
    # GLCMを求める
    glcm = greycomatrix(patch, distances=distances, angles=angles, levels=256,
                        symmetric=True, normed=True)
    # 指標の算出
    contrast.append(greycoprops(glcm, 'contrast')[0, 0])
    dissimilarity.append(greycoprops(glcm, 'dissimilarity')[0, 0])
    homogeneity.append(greycoprops(glcm, 'homogeneity')[0, 0])
    ASM.append(greycoprops(glcm, 'ASM')[0, 0])
    energy.append(greycoprops(glcm, 'energy')[0, 0])
    correlation.append(greycoprops(glcm, 'correlation')[0, 0])
```

### 結果の確認
各locationの値を確認してみる。
energyはASMのルートをとっただけのものなので除外しました。

```
index = ['location {}'.format(i+1) for i in range(len(locations))]
df = pd.DataFrame({'contrast':contrast, 'dissimilarity':dissimilarity,
             'homogeneity':homogeneity, 'ASM':ASM, 'correlation':correlation}, index=index)
df
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1447216/1618702c-7b68-5ce6-6f20-c3573f0330f0.png)



```
# 標準化
df = (df - df.min()) / (df.max() - df.min())
# バープロット
df.T.plot.bar(rot=0, figsize=(10, 6)).legend(bbox_to_anchor=(1.02, 0.3))
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1447216/a2e1bc90-e402-55b7-aa76-b269cc0c8afc.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1447216/6643ae5b-7671-8389-b687-818d904e72c1.png)

- 全体的に同色のパッチはcontrastやdissimilarityの値が小さくなります。
- location 2は横縞模様に近く今回のパラメータではピクセル間の値の差が小さなるためcorrelationの値が大きくなっています。

# 最後に
今回は4つ左のピクセルとの関係を見てみました。
様々なテクスチャに対して距離と角度のパラメータを変えながらいろいろとやってみたいですね。