Python＋OpenCVで色抽出すると、グリーンバックの謎が解けた

### 目的

![150226-143939-screen-shot.jpg](https://qiita-image-store.s3.amazonaws.com/0/12939/d67baf7d-eeab-174a-37b5-5ad271e5dbed.jpeg)

いきなりむさいおじさん画像で申し訳ありません。

「赤黄青緑の4色の木の玉を、それぞれ抽出できないか」、やってみました。

Python歴OpenCV歴ともに1ヶ月程度ですので、拙い点はぜひご指摘下さい（本当に）。

### フィルタのコード

以下は、

1. 指定した色相（`hue`）のピクセル（`hTarget`）を明るくして
2. 該当しないピクセル（`hMask2`）をグレースケールにする

コードです。

```py:filters.py
...

def hueMask(src, dst, hue, hueRange):
    src = cv2.cvtColor(src, cv2.COLOR_BGR2HSV)
    h, s, v = cv2.split(src)
    hOrg = h.copy()
    hTarget = h.copy()

    cv2.threshold(hTarget, hue + hueRange, hue, cv2.THRESH_TOZERO_INV, hTarget)
    # 「src(x,y)がthreshより大きければ、dst(x,y)は0」

    # 0
    # 0
    # 0
    # thresh
    # src
    # src
    # src
    # src
    # src

    cv2.threshold(hTarget, hue - hueRange, hue, cv2.THRESH_BINARY, hTarget)
    # 「src(x,y)がthreshより小さければ、dst(x,y)は0」

    # 0(src)
    # 0(src)
    # 0(src)
    # src
    # src
    # thresh
    # 0
    # 0
    # 0

    # 結果

    # 0
    # 0
    # 0
    # thresh
    # src
    # thresh
    # 0
    # 0
    # 0

    # 蛍光灯の光（黄）や髪の毛（青）を除外するため、
    # 極端に彩度の低いピクセルをターゲット範囲から除外する
    sNotVeryLow = s.copy() # 極端に彩度が低くないところ
    # 彩度が31より高いならターゲット範囲（255）に入れる。
    # さもなくば非ターゲット範囲（0）。
    cv2.threshold(sNotVeryLow, 31, 255, cv2.THRESH_BINARY, sNotVeryLow)

    # hTargetとvNotHighlightの論理積が新しいhTarget
    cv2.bitwise_and(hTarget, vNotVeryLow, hTarget)

    # 明度画像のコピーをとる。
    vBrightened = v.copy()
    # それに+96のガンマ補正をかけ、明るくする
    cv2.addWeighted(v, 0.625, v, 0.0, 96, vBrightened)
    # 明度画像のターゲット範囲のみ、ガンマ補正済み明度画像を入れる
    cv2.bitwise_and(vBrightened, 255, v, hTarget)

    hMask2 = h.copy()
    # hTarget（1チャンネル画像）の該当ピクセルが0のとき、
    # hMask2（1チャンネル画像）の該当ピクセルを255にセットする。
    # さもなくば、0にセットする。
    # 要するにhMask2はhTargetマスク画像を反転させたもの。
    cv2.compare(hTarget, 0, cv2.CMP_EQ, hMask2)

    cv2.bitwise_and(s, 0, s, hMask2) # 論理積

    cv2.merge((hOrg, s, v), src)
    cv2.cvtColor(src, cv2.COLOR_HSV2BGR, dst)

...
```

なんだか変数の名前が汚いですが・・・これで任意の色相を抽出できるようになりました。

### アプリケーションのコード

次に4色つまり赤黄青緑の色相を抽出するようにしてみます。

```py:App.py
...
class App(object):
    def __init__(self):
        ...
        self._shouldHueMask                = False
        ...
    def run(self):
        ...
        while self._windowManager.isWindowCreated:
            ...
            if self._shouldHueMask:
                filters.hueMask(frame, frame, self._hue, self._hueRange)
            ...
    def onKeypress(self, keycode):
        ...
        elif keycode == ord('B'): # 青
            self._hue      = 110
            self._hueRange = 10
            self._shouldHueMask = \
                not self._shouldHueMask
        elif keycode == ord('G'): # 緑
            self._hue      = 70
            self._hueRange = 25
            self._shouldHueMask = \
                not self._shouldHueMask
        elif keycode == ord('R'): # 赤
            self._hue      = 5
            self._hueRange = 5
            self._shouldHueMask = \
                not self._shouldHueMask
        elif keycode == ord('Y'): # 黄
            self._hue      = 30
            self._hueRange = 15
            self._shouldHueMask = \
                not self._shouldHueMask
        ...
if __name__=="__main__":
    App().run()
```

赤黄青緑それぞれの色相値と幅は、試行錯誤の末に決めました。

### 結果

#### 赤

![150226-144313-screen-shot.jpg](https://qiita-image-store.s3.amazonaws.com/0/12939/897f5a01-a2a0-2c47-c31f-db7887f646aa.jpeg)

うーん、肌の色が「赤色抽出」に反応してしまっているようです。これでは木の玉「だけ」を抽出することはできません。

#### 黄

![150226-144702-screen-shot.jpg](https://qiita-image-store.s3.amazonaws.com/0/12939/12e28596-67e0-25f0-4dde-71ef80315490.jpeg)

うーん、今度は天井が「黄色抽出」に反応してしまっています。

#### 青

![150226-144944-screen-shot.jpg](https://qiita-image-store.s3.amazonaws.com/0/12939/21d39ef6-c370-3a3b-fb73-a0cb83c59276.jpeg)


今度は髪の毛と黒いセーターが「青色抽出」に反応してしまっています。

#### 緑

![150226-145207-screen-shot.jpg](https://qiita-image-store.s3.amazonaws.com/0/12939/76e62f77-97c8-ec2f-0f34-b251fced51bd.jpeg)

いちばんきれいに木の玉を抽出することができました。

映像でも合成するときはグリーンバックといって緑色の布の前に立ちますよね。あれが緑色だったのには理由があるわけですね。

緑色が心に和む色なのも、普段の生活の中に出てくる色とは違い、目が新たな刺激を快いと感じるからなのかもしれません。

ひょっとしたら黒板が緑色なのも、それが理由かも？

・・・などと議論の余地を残して筆を置きます。

---
ブログやってます：[Weed.nagoya](http://www.weed.nagoya/)