LoginSignup
4
6

More than 5 years have passed since last update.

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

Last updated at Posted at 2015-02-26

目的

150226-143939-screen-shot.jpg

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

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

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

フィルタのコード

以下は、

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

コードです。

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色つまり赤黄青緑の色相を抽出するようにしてみます。

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

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

150226-144702-screen-shot.jpg

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

150226-144944-screen-shot.jpg

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

150226-145207-screen-shot.jpg

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

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

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

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

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


ブログやってます:Weed.nagoya

4
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
6