python + openCV で画像処理のさわりだけやってみた

テクスチャ画像認識やることになったので、フーリエ変換とか出来るようにopenCVを触ってみました。

## 試行環境
- Windows 10 64bit
- Python 3.6.6
- opencv-python==4.0.0.21
- Pillow==5.3.0
- jupyter notebook

## 1. PILなのかopenCVなのか
pythonの画像処理パッケージではopenCVとPILが人気のようで「python 画像処理」あたりで検索するとこの2つについての記事がほとんどです。歴史ある画像処理のデファクトスタンダードであるopenCVと、名称通りpythonの標準的な画像パッケージであるPIL(Python Image Library)の２つのパッケージですが現在はどちらもpip installで手軽に試せちゃうみたいです。どちらが良いか分からなかったので両方ちょっと触ってみました。

|                  |  openCV   |   PIL    |
|:-----------------:|:------------------:|:------------------:|
| インストール      |  pipだけで出来る |  pipだけで出来る  |
| 日本語PATH       |    読めない      |  読める  |
| カラー画像の形式  | BGR     | RGB |
| グレースケール化  |  できる | できる        |
| HSV化  |  できる | できる        |
| CIE Lab化  |  できる | できる        |
| フーリエ変換 |  numpyでやる |  numpyでやる  |

どうやらやりたい事はどちらも出来るようです。もっと高度な処理になると画像処理のデファクトスタンダードであるopenCVにしかない機能がたくさんあるようですが、今回はちょろっと変換するだけなのでPILでも大丈夫みたいですね。

ただし、openCVは日本語PATHが入ると読めなくなっちゃうし、データがRGBじゃなくBGR順で入っててややこしいのが難点です。「開きたい画像ファイル1000個が全部日本語名です」みたいな時はリネームしなくても読めるPILでやった方が楽かもですね。openCVは画像データをnumpy.arrayそのままで扱えますし、PILもnumpy.arrayに変換して戻してが出来ますから、PILで読んでopenCVに渡してというのでも良いのかもしれません。

僕は今後高度な処理をやっていく可能性があるのでopenCVの方でやっていく事にしました。

## 2. openCVのインストールとimport
pipでサクッとインストールできます。インストールするときと使うときで名前が違うので注意。

```
$ pip install opencv-python
```
```
>>> import cv2
```

## 3. 画像の読み込みと表示
cv2.imread(画像ファイルのPATH)で読めます。相対PATHでも絶対PATHでも良いようです。cv2.imread()の返り値はnp.ndarrayで返ってくるので、値を直接いじったりも簡単に出来ちゃいます。

```
>>> import cv2
>>>
>>> im = cv2.imread('image.png')
>>> print(im.shape, type(im))

(183, 275, 3) <class 'numpy.ndarray'>
```
解像度が183 x 275 pixelのRGB画像だと上記のように読み込まれます。3次元のnumpy.arrayになってるので1チャンネルだけ取り出すのも簡単です。

```
>>> im[:, :, 0]

array([[255, 255, 255, ..., 250, 250, 250],
       [255, 255, 255, ..., 250, 250, 250],
       [255, 255, 255, ..., 250, 250, 250],
       ...,
       [255, 255, 255, ..., 255, 255, 255],
       [255, 255, 255, ..., 255, 255, 255],
       [255, 255, 255, ..., 255, 255, 255]], dtype=uint8)
```

jupyter notebookで表示する場合は、cv2.imshow()が使えないようですがmatplotlib.pyplot.imshow()で表示できます。読んできた値はBlue, Green, Redの順で格納されているんですが、matplotlib.pyplot.imshow()はRGB順でデータが来るつもりで表示しちゃうので、cv2.cvtColor()で並べ替えして表示します。pyplotのメソッドなので表示解像度を変えたり並べて表示したりも出来て便利です。

```
import matplotlib.pyplot as plt
%matplotlib inline

plt.figure(dpi=50)
plt.imshow(cv2.cvtColor(im, cv2.COLOR_BGR2RGB))
plt.show()
```
![image.png](https://qiita-image-store.s3.amazonaws.com/0/277233/f85a9531-384e-0da7-97c6-e32edda50dbf.png)

ちなみにcm2.cvtColor()で変換しないで表示してみると、RGBが入れ替わってるので色が変になります。

```
plt.imshow(im)
```
![image.png](https://qiita-image-store.s3.amazonaws.com/0/277233/5eecbc13-660d-d20f-2ab8-bef60a971625.png)

ゲルバナ感あります。

## 4. グレースケール化、HSV化、Lab化

PILだと使うメソッドがそれぞれ違ってしまうのでややこしいんですが、openCVだといずれもcv2.cvtColor()で変換できます。

グレースケール化

```
gray = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY)
plt.figure(dpi=50)
plt.imshow(gray, 'gray')
plt.show()
```
![image.png](https://qiita-image-store.s3.amazonaws.com/0/277233/0f6fc653-482e-be4d-6fe5-73978fbe9b4d.png)

HSV化

```
hsv = cv2.cvtColor(im, cv2.COLOR_BGR2HSV)
plt.figure(dpi=50)
plt.imshow(cv2.cvtColor(hsv, cv2.COLOR_BGR2RGB))
plt.show()
```
![image.png](https://qiita-image-store.s3.amazonaws.com/0/277233/1656a5b5-67fd-28c8-bff0-3e0e5ea545ea.png)

Lab化

```
Lab = cv2.cvtColor(im, cv2.COLOR_BGR2Lab)
plt.figure(dpi=50)
plt.imshow(cv2.cvtColor(Lab, cv2.COLOR_BGR2RGB))
plt.show()
```
![image.png](https://qiita-image-store.s3.amazonaws.com/0/277233/78d43584-923b-3078-bc9e-53fb7cb72e28.png)

## 5. フーリエ変換
グレースケール化してからパワースペクトル出してみます。

```
import numpy as np

gray = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY)
fim = np.fft.fft2(gray)
fshift = np.fft.fftshift(fim)
fMagnitudeSpectrum = 20 * np.log(np.abs(fshift))
plt.figure(dpi=50)
plt.imshow(fMagnitudeSpectrum, 'gray')
plt.show()
```
![image.png](https://qiita-image-store.s3.amazonaws.com/0/277233/89dc2c53-c57b-212e-d291-bba1e3269239.png)

##### エンジョイ！