行列によるグレースケール化編　～Python画像処理の再発明家～

画像処理ライブラリに頼らず、行列演算だけで画像処理・グレースケール化をするお話。Pythonistaでも可能

**[基礎編はこちらから](http://qiita.com/secang0/items/1229212a37d8c9922901)**

#「再発明家」とは
Open CVとかPillowに頼らず、numpyとmatplotlibを使って、様々な画像処理を実際に書いてみる。iOSアプリのPythonistaでも使える組み合わせだ。

```py3
import numpy as np
import matplotlib.pyplot as plt
```

また、画像表示には以下の関数が便利である。（詳しくは[基礎編](http://qiita.com/secang0/items/1229212a37d8c9922901)）

```py3
def img_show(img : np.ndarray, cmap = 'gray', vmin = 0, vmax = 255, interpolation = 'none') -> None:
    '''np.arrayを引数とし、画像を表示する。'''
    
    #dtypeをuint8にする
    img = np.clip(img,,vmin,vmax).astype(np.uint8)
    
    #画像を表示
    plt.imshow(img, cmap = cmap, vmin = vmin, vmax = vmax, interpolation = interpolation)
    plt.show()
    plt.close()
```

#グレースケール化
グレースケール化、とは各ピクセルに充てられたRGBの値を白黒の値Yを計算する手法である。
ここでは、[基礎編](http://qiita.com/secang0/items/1229212a37d8c9922901)で扱わなかった[様々なグレースケールの手法](https://ofo.jp/osakana/cgtips/grayscale.phtml)も試してみる。
詳しい説明はリンクを参照。同じ順序で扱っている。

使用画像は'[tiger.jpeg](http://imgur.com/AfaVcBI)'
![tiger.jpeg](https://qiita-image-store.s3.amazonaws.com/0/172464/10a0ce3e-a08e-05a9-59c7-bada369fa558.jpeg)


```py3
img = plt.imread('tiger.jpeg')
R, G, B = img[...,0], img[...,1], img[...,2]
```

カラーと白黒を比較して並べるための関数をここで定義しておく。

```py3
def align_show(img_gray):
    #img_grayをN*M*3に変換
    img_pseudogray = np.einsum('ij,k->ijk',img_gray,[1,1,1])

    #横に並べて表示
    img_show(np.concatenate((img,img_pseudogray), axis = 1))
```

##中間値法
$\rm Y=\frac{\max(R,G,B) + \min(R,G,B)}{2}$とする。手法である。要するに、最大値と最小値の平均。
なお実際の計算ではオーバーフローを避けるために上式とは一部、計算の順序を変えてある。

```py3
img_mid_v = np.max(img, axis = 2)/2 +np.min(img, axis = 2)/2
img_show(img,img_mid_v)
```

![mid_v.png](https://qiita-image-store.s3.amazonaws.com/0/172464/503a792b-575b-1c0b-8374-1610a96adc92.png)

パッと見問題ないが、迷路の真ん中のボタン（？）が見えにくい。

##NTSC 係数による加重平均法
$\rm Y = ( 0.298912 R + 0.586611 G + 0.114478 B )$とする手法である。これらの係数は、各RGBが人間の目に与える影響（心理的加重）を考慮に入れた結果である。

```py3
img_ntsc = (0.298912 * R + 0.586611 * G + 0.114478 * B)
align_show(img_ntsc)
```

![ntsc.png](https://qiita-image-store.s3.amazonaws.com/0/172464/e1789a08-1da7-01cc-4368-b20d36c58656.png)

良さげ

##HDTV 係数による加重平均と補正
$\rm Y = ((0.222015 R)^X + (0.706655 G)^X + (0.071330 B)^X )^{1/X}$とする手法である。これも心理的加重を取り入れている。

```py3
X = 2.2
img_hdtv = ((0.222015*R)**X + (0.706655*G)**X + (0.071330*B)**X)**(1/X)
align_show(img_hdtv)
```

![hdtv.png](https://qiita-image-store.s3.amazonaws.com/0/172464/9d9e641e-3495-1f03-643b-59a04bbffc93.png)

私にはNTSC法との違いがわからないくらいの違いである。

##単純平均法
$\rm Y = \frac{R + G + B}{3}$という平均をとる方法である。一番直感的なやり方だろう。加重平均をとる前のNTSC法ともいえるかも。

```py3
img_mean = np.mean(img)
align_show(img_mean)
```

![mean.png](https://qiita-image-store.s3.amazonaws.com/0/172464/73bca517-76f7-dc44-a1bd-52785a777b16.png)

##Gチャンネル法
$\rm Y = G$というGチャンネルのみを取り出す方法。一番速そう。

```py3
img_g_channel = G
align_show(img_g_channel)
```

![g_channel.png](https://qiita-image-store.s3.amazonaws.com/0/172464/09d85e05-5a20-2b31-8d51-daa5d01042ce.png)

赤色部分がちょっと濃すぎるかな…

##中央値法
$\rm Y = median(R, G, B)$という中央値を取り出す方法。中間値法と似た発想な気がする。

```py3
img_median = np.median(img, axis = 2)
align_show(img_median)
```

![median.png](https://qiita-image-store.s3.amazonaws.com/0/172464/9568a984-54e4-68f2-dca1-98c4f402bbbd.png)


中間値法と同じく、緑が濃すぎる。


##まとめ

|中間値  |NTSC  |HDTV  |
|---|---|---|
|  |元画像  |  |
|単純平均  |Gチャンネル  |中央値  |

![sumup.png](https://qiita-image-store.s3.amazonaws.com/0/172464/d7b4d136-dae0-1d89-9ac4-85a25e504a01.png)