OpenCV入門(python)-(2)


以下の記事の続きです(インプットの記事ばかりになってきたので何か作りたいですね・・・)

https://qiita.com/tom_S/items/b85aee14f34dcc856e54

## ヒストグラム

画像の中に赤が多いのか、青が多いのかなどの画素値の分布を表現できる。

以下にRGB値のヒストグラムをグラフにして表示するプログラムを示す。

```py
import cv2
import matplotlib.pyplot as plt # 画像プロット用のライブラリ
%matplotlib inline # 画像プロット用のライブラリ

img = cv2.imread("ここに画像ファイルのパス")

color_list = ["blue", "green","red"]
for i,j in enumerate(color_list):
    hist = cv2.calcHist([img],[i],None,[256],[0,256]) # blue, green,redの順に計算
    plt.plot(hist,color= j)
```


また、ヒストグラムを均一化することにより、明るい部分をより明るく、暗い部分もより暗くすることができる。
以下のようにしてequalizeHist関数を呼び出すことでヒストグラムを均一化する

```py
import cv2

img = cv2.imread("ここに画像ファイルのパス")
img_eq = cv2.equalizeHist(img)　# img_eqにヒストグラムを均一化した画像が入る
```

## ガンマ変換

ざっくりと言えば、画像の明るさの変更方法のことをガンマ変換と呼ぶ。以下の式のようなガンマテーブル(ルックアップテーブル)というものを用いて変換を行う。このrの値が1より大きいと明るくなり、1より小さいと暗くなる。

y = 255* (x/255)^(1/r) 

以下にソースコードを示す。
(ルックアップテーブルの説明は割愛)

```py
import cv2
import numpy as np

gamma = 1.5 # 上の式のr
img = cv2.imread("ここに画像ファイルのパス")
gamma_cvt = np.zeros((256,1), dtype= np.uint8)
for i in range(256):
    gamma_cvt[i][0] = 255 * (float(i)/255) ** (1.0/gamma)
    
img_gamma = cv2.LUT(img, gamma_cvt) #img_gammaにガンマ変換後の画像が入る
```

どんな効果があるかわかりにくいかと思うので、以下に参考になりそうなサイトのリンクを貼っておく
http://peaceandhilightandpython.hatenablog.com/entry/2016/02/05/004445


## アファイン変換

平行移動・回転を組み合わせて画像を動かすような動作や長方形を平行四辺形に変えてしまうような変換（せん断変形）のことをまとめてアファイン変換という。アファイン変換は行列計算を行うことで変換を行う

平行移動は行列の和を行う必要があるので、線形変換を縮めるために同次座標を行列に追加し、変換行列を作成することにより単数項同士の式にする。いかに並行移動、回転の両方のソースコードを示す

### 平行移動

```py
import cv2
import numpy as np

img = cv2.imread("ここに画像ファイルのパス")
h,w = img.shape[:2]
dx,dy = 30,30

afn_mat = np.float32([[1,0,dx],[0,1,dy]]) # 平行移動のみ。縦横30ずつずらす
img_afn = cv2.warpAffine(img, afn_mat,(w,h)) # img_afnに平行移動後の画像が入る
```

### 回転

```py
import cv2
import numpy as np

img = cv2.imread("ここに画像ファイルのパス")
h,w = img.shape[:2]

rot_mat = cv2.getRotationMatrix2D((w/2,h/2),40,1) # (回転する中心の座標,回転角度,画像のスケール(１はサイズはそのまま))を渡す
img_afn2 = cv2.warpAffine(img, rot_mat,(w,h)) # img_afnに回転後の画像が入る
```

## 畳み込み

周囲の情報を使って、自分の画素値を更新することを畳み込みと言う



### 畳み込みの仕方

  1. フィルターを用意する

  2. 着目画素の周囲で「 (画素値)×(フィルター) 」を行い足していく(これを畳み込むと言う)

  3. 全ての画素に置いて畳み込みを行う

### 畳み込みを使用するフィルター 

#### 平滑化フィルター

周りの情報から画素値を計算し、ノイズを除去する。以下にソースコードを示す

```py
import cv2 
import numpy as np

kernel = np.ones((3,3)) /9.0 # 畳み込みに使用するフィルター

img = cv2.imread("ここに画像ファイルのパス", 0)
img_kel = cv2.filter2D(img, -1, kernel) # 引数の「-1」は元の画像(img)を返すことを意味する.畳み込みを行う（平滑化）
```

#### sobelフィルター

微分することにより縦方向のエッジが検出できる。以下にソースコード例を示す

```py
import cv2 
import numpy as np

kernel2 = np.zeros((3,3))
kernel2[0,0] = 1
kernel2[1,0] = 2
kernel2[2,0] = 1
kernel2[0,2] = -1
kernel2[1,2] = -2
kernel2[2,2] = -1

img = cv2.imread("ここに画像ファイルのパス", 0)
img_ke2 = cv2.filter2D(img, -1,kernel2) # sobel フィルター

```

### 平滑化フィルター

- 線形変換

全体を万遍なく平滑化してぼかした状態にするフィルター。以下にソースコードを示す。

```py
import cv2 

img = cv2.imread("ここに画像ファイルのパス")

img_blur = cv2.blur(img, (3,3)) # 平滑化フィルター
img_ga = cv2.GaussianBlur(img,(9,9), 2) # 2は分散の値。この値が大きければ大きいほどボケる (ガウシャン)
img_me = cv2.medianBlur(img, 5) # 最頻値で書き換える(ぼけが大きい) メディアンフィルター
```

- 非線形変換

非線形変換にはバイラテラルフィルターがある。これは輝度変化が激しい部分は残し、輝度変化が緩やかな部分は平滑化するものである。以下にソースコードを示す。

```py
import cv2 

img = cv2.imread("ここに画像ファイルのパス")

img_bi = cv2.bilateralFilter(img,20,30,30)
```

## モルフォロジー演算

膨張と収縮からなる演算のこと収縮させることで画像を分離したり、膨張させることで一体化させるために使う。また、収縮させ、膨張させることでノイズも消せる。以下にソースコードを示す。

```py
import cv2

img = cv2.imread("ここに画像ファイルのパス")
ret, img_th = cv2.threshold(img, 110, 255, cv2.THRESH_BINARY) # 2値化

kernel = np.ones((3,3),dtype = np.uint8) #周囲８近傍に白があれば白、黒が多いなら黒にするためのもの
img_d = cv2.dilate(img_th, kernel)
img_e = cv2.erode(img_th,kernel)

img_c = cv2.morphologyEx(img_th,cv2.MORPH_CLOSE, kernel) # img_cに演算後の画像が入る
# MORPH_CLOSE:膨張の後に収縮をする処理を行う
# (MORPH_OPENを指定することで、収縮の後に膨張を行うことも可能)

```

## 特徴抽出

- 特徴とは情報の多い部分のこと。特徴には以下の3つがある。特徴抽出のためには、画像の中から固有値の大きい固有ベクトルを見つけることになる

  - フラット  :  画像の中に方向性がない。固有ベクトルが偏りがなく固有値が小さい(特徴が少ない)

  - エッジ    :  画像の中に、固有値が大きい固有ベクトルが1つある(フラットより特徴がある)

  - コーナー    :  画像の中に、固有値が大きい固有ベクトルが2つある(特徴が多い)


### 特徴抽出器

| 抽出器 | 説明 |
| :--------------- | :------------------------------------ |
| Harris | スケールがかわると精度が落ちる。固有値・固有ベクトルにより特徴を抽出
| SIFT  |  特徴量が１２８次元。特許取得のため商用利用不可
| SURF  |  SIFTの高速化。特許取得のため商用利用不可
| ORB   |  特徴量を2値化。手軽でオススメ
| KAZE  |  非線型フィルタを使用。手軽でオススメ
| AKAZE  | KAZEの高速化版。手軽でオススメ

抽出器ごとのソースコードをいかに示す。

```py
import cv2
import numpy as np
import copy

img = cv2.imread("ここに画像ファイルのパス")
img_g = cv2.imread("ここに画像ファイルのパス",0)

# Harris
img_harris = copy.deepcopy(img)
img_dst = cv2.cornerHarris(img_g, 2,3,0.04)
# 2はブロックサイズ。どれくらいの範囲で検出を行うか。おおきくすると抽出量が増える

# KAZE
img_kaze = copy.deepcopy(img)
kaze = cv2.KAZE_create() # 特徴抽出器を作成(KAZE)
kp1 = kaze.detect(img,None)
img_kaze = cv2.drawKeypoints(img_kaze, kp1, None) #特徴部分にマークした画像がimg_kazeに入る

# AKAZE
img_kaze = copy.deepcopy(img)
kaze = cv2.AKAZE_create() # 特徴抽出器を作成(AKAZE)
kp1 = kaze.detect(img,None)
img_kaze = cv2.drawKeypoints(img_kaze, kp1, None) #特徴部分にマークした画像がimg_kazeに入る

# ORB
img_orb = copy.deepcopy(img)
orb = cv2.ORB_create() # 特徴抽出器を作成(ORB)
kp2 = orb.detect(img_orb)
img_orb = cv2.drawKeypoints(img_orb, kp2, None) #特徴部分にマークした画像がimg_orbに入る
```