OpenCVでリサイズ・モザイク・顔検出、ときどき象印

# はじめに
だんだん盛りだくさんになってきました。

# リサイズ　cv2.resize(src, dsize, fx, fy, interpolation)
OpenCVでの画像リサイズは2種類の指定方法がある。倍率指定とサイズ指定だ。
*src*と*dsize*が必須。

 - *src*は元画像。
 - *dsize*は出力するサイズを**タプルで(width, height)の順で指定する。**倍率指定の場合でも必須で、その際は(0, 0)もしくはNoneを指定する。当然整数。
 -
 - *fx*と*fy*は幅方向および高さ方向の倍率。小数でもよい。
 - *interpolation*で画像の補完方法を指示する。いくつかあるが以下の二つを覚えておけばよいだろう。cv2.INTER_LINEARはデフォ値なのでcv2.INTER_NEARESTだけでも十分だ。
    - cv2.INTER_LINEAR　バイリニア補間 (デフォルト)
    - cv2.INTER_NEAREST　最近傍補間

背景や人物のjpeg画像では違いがわかりにくいが、ドット絵だとよくわかる。

| 元画像|cv2.INTER_LINEAR|cv2.INTER_NEAREST|
|:----------------:|:-----------------:|:------------------:|
|ここにいる<br>↓<br>![dote.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/520216/45ebe6c0-1bcd-c517-bb65-22c27ed7d359.png)|![INTER_LINEAR.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/520216/358dd9b7-e159-3f5e-3194-a6fe09324813.png)|![INTER_NEAREST.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/520216/1acafe1d-7189-6b81-f5d8-0f520fce3394.png)|

[トリミング](https://qiita.com/mo256man/items/e36797f9f44a64caf81c)のときは行列だからという理由で行・列の順で img[r: r+h, c: c+w] と書くと覚えたが、リサイズは行列とは関係ないので(w, h)の順。これはややこしい。
倍率指定の場合もサイズの記述が必須でわざわざNoneと書く必要があるというのもややこしい。


## 基本的なプログラム

```Python:resize.py
import cv2

filename = "hoge.jpg"
img = cv2.imread(filename)

fx, fy = 3.5, 2  # 倍率は小数でも可

# dsizeで指定する方法
h, w = img.shape[:2]
h_resize, w_resize = round(h*fy), round(w*fx)  # サイズは整数
img_resize = cv2.resize(img, dsize=(w_resize, h_resize) ,interpolation=cv2.INTER_LINEAR)

# fx,fyで指定する方法
#img_resize = cv2.resize(img, dsize=(0,0), fx=fx, fy=fy ,interpolation=cv2.INTER_LINEAR)

# これではエラーになる
#img_resize = cv2.resize(img, fx=fx, fy=fy ,interpolation=cv2.INTER_LINEAR)

cv2.imshow("originai image",img)
cv2.imshow("resized image",img_resize)
cv2.waitKey(0)
cv2.destroyAllWindows()
```

## モザイク
モザイクはモザイクをかけるという特別なメソッドがあるわけではない。縮小して元のサイズに拡大しなおすという処理をするだけだ。もちろんcv2.INTER_NEARESTで。

### 応用例1　ヒントでピント

モザイクを徐々に密にしていけば、ヒントでピントのようなクイズが作れる。
下記ソースではアニメGIFを作る部分は省略しているので適当にキーをポンポン叩いてみてください。

```Python:hinto_de_pinto.py
import cv2

filename = "hoge.jpg"
img_origin = cv2.imread(filename)
imgH, imgW = img_origin.shape[:2]

i = 2
isComplete = False
while not isComplete:
    ratio = 1/2**(8-i*0.6)  # 微妙な計算をしているがご自由に変更してください
    print (ratio)
    if ratio > 0.7:
        img_mosaic = img_origin
        isComplete = True
    else :
        img_mosaic = cv2.resize(img_origin, dsize=None ,fx=ratio, fy=ratio)
        img_mosaic = cv2.resize(img_mosaic, dsize=(imgW, imgH),
                                interpolation=cv2.INTER_NEAREST)
    cv2.imshow("mosaic", img_mosaic)
    cv2.waitKey(0)
    i += 1
    
cv2.destroyAllWindows()
```

元画像は例によって[スキマナース](https://nurse-web.jp/photo/archives/214)。
![hinto_de_pinto.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/520216/80435cd7-a46a-3832-46cf-b07cb1246eac.gif)

# 顔検出

OpenCVのカスケード分類器によって顔検出ができる。分類器はネット上に多数あり、また自分で作ることも可能だ。

仕組みについてはこちらが参考になる。
　【入門者向け解説】openCV顔検出の仕組と実践(detectMultiScale)
　https://qiita.com/FukuharaYohei/items/ec6dce7cc5ea21a51a82

## 基本的なプログラム

```Python:face_detect.py
import cv2

filename = "hoge.jpg"
img_origin = cv2.imread(filename)
img_gray= cv2.cvtColor(img_origin, cv2.COLOR_BGR2GRAY) # 高速化のためにグレースケールにする

# https://github.com/opencv/opencv/tree/master/data/haarcascades　からカスケードファイルを入手する
cascade_path = "./models/haarcascade_frontalface_alt2.xml"
cascade = cv2.CascadeClassifier(cascade_path)
faces = cascade.detectMultiScale(img_gray)

print (faces)
```
これまで各種パラメータについて説明してきたが、ここでは出力結果のみについて説明する。
まずは先程紹介しなかった元画像。

![nurse.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/520216/ccedca7e-204e-746a-fd30-2447fc2e9701.jpeg)

この画像についてプログラムを走らせると、次のような結果が得られる。

```Python:結果
[[374  78  88  88]
 [137  90 127 127]]
```

[公式のリファレンス](http://opencv.jp/opencv-2.1/cpp/object_detection.html#cv-cascadeclassifier-detectmultiscale)には「矩形のリスト」としか書かれていないが、より具体的に書くと**「検出された矩形が x, y, w, h の順で格納されているリスト」のリスト**だ。リストのリストだから検出結果が1個でもブラケットは二重になっていることを忘れてはいけない。

### 応用例2　顔検出と連動したモザイク
顔検出といえば検出したエリアを四角で囲むプログラムをよく目にするが、出力を理解すればモザイクにするのも容易だ。
画像でなくカメラで取得した映像に対しておこなえば笑い男はもうすぐ。攻殻機動隊って見たことないけど。

```Python:rectangle_and_mosaic.py
import cv2

filename = "hoge.jpg"
img_origin = cv2.imread(filename)
img_gray = cv2.imread(filename,0)  # cvtColorで変換するのでなくグレースケールで読み込むという手もある

# img_origin.copy()でなくimg_originそのものだとどうなるか確認してみよう
img_rect = img_origin.copy()
img_mosaic = img_origin.copy()

cascade_path = "./models/haarcascade_frontalface_alt2.xml"
cascade = cv2.CascadeClassifier(cascade_path)
faces = cascade.detectMultiScale(img_gray)

if len(faces) > 0:
    for face in faces:
        x, y, w, h = face

        # 検出した顔の範囲を四角で囲む
        img_rect = cv2.rectangle(img_rect, (x, y), (x+w, y+h), color=(255, 255, 255), thickness=2)

        # 検出した顔の範囲をモザイクする
        roi = img_mosaic[y:y+h, x:x+w]
        roi = cv2.resize(roi, (w//10, h//10))
        roi = cv2.resize(roi, (w, h), interpolation=cv2.INTER_NEAREST)
        img_mosaic[y:y+h, x:x+w] = roi               

cv2.imshow("face originai", img_origin)
cv2.imshow("face rectangle", img_rect)
cv2.imshow("face mosaic", img_mosaic)
cv2.waitKey(0)
cv2.destroyAllWindows()

```

![face detection_rect.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/520216/2a53c79e-81ef-bf59-be00-67c492525324.jpeg)
![face detection_mosaic.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/520216/d190c3d4-e786-3248-516a-ce514905cf38.jpeg)

### 応用例3　痩せて見える鏡

顔検出と拡大縮小を利用した、痩せて見える鏡を思いついた。ピンチイン・ピンチアウトで痩せたり太らせたりできるスマホアプリを公開すれば全国の女子高生の皆さんに大人気だと妄想したが、残念ながらスマホアプリを作る技術は私にはなかった。
というかSNOWに似た機能があった。SNOWのビューティー機能はこんな簡便なものではないと思うけど。

上：元画像（[ぱくたそ](https://www.pakutaso.com/20160730188post-8323.html)）
中：加工後
下：調子に乗って作ったアニメGIF

![original.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/520216/08f38a5d-abcd-7b91-5569-8039f8c86a1d.jpeg)
![slim80.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/520216/160866c4-b6c6-f234-4d33-4dc369b33dff.png)
![slim.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/520216/ff3d8149-6af2-6bb5-67d9-956ef55ce519.gif)

さて、これのソースだが…それを書くにはここの余白は狭すぎる。

# 終わりに
OpenCVは面白いが、早く本丸であるディープラーニングについても勉強しなくては。