試行錯誤ダイアリー

新卒エンジニアが日々の技術的な学び,働き方,日々感じたこと等を書きます

【openCV】画像の色抽出

OpenCVで色検知の方法を調べると,特定の色を検知するものは多いが,色の抽出をするものはそんなに多くないように感じた.

今回は画像をインプットしたときにその画像が大体何色なのかを判別したい.各ピクセルの平均を取れば結果としては出力できるが,求めた結果とは異なることが多い.今回はOpenCVを使って画像をHSV形式に変換した後,ピクセルのHue値のヒストグラムで可視化することで画像が直感的に何色なのか調べた.

openCV導入方法はこちら
関連記事:【Windows10】PythonでのopenCV導入方法 - 試行錯誤ダイアリー

色識別方法

openCV で画像を読み込んで, HSV方式に変換して検出する.

色検知に関してはRGB方式ではなくHSV方式でするのが一般的らしい.
RGBでは一つの色を表現するのにR,G,Bそれぞれのパラメータを細かく設定する必要があるが,
HSVではHue(色相)で色を調節できるので都合がいいらしい.
※HSV = Hue(色相),Saturation(彩度),Value(明度)

opneCVではそれぞれの値の範囲が
Hue(色相) : 0 ~ 180
Saturation(彩度) : 0 ~ 255
Value(明度) : 0 ~ 255
で表現される.

特に色相はこんな感じ(左が0で右は180)
f:id:appli-in:20180218183137p:plain

画像

今回用いる画像は以下の2つ.理由は特定の色が圧倒的に強い画像とHueの色空間をまんべんなく網羅した画像を比較することで,ヒストグラムによる可視化の効果を確認するため.

tomatoRGB
用いた画像(左:トマトの画像, 右:RGBの画像)

手順

まず画像を読み込みHSVに変換

img_scr = cv2.imread("picture_name.png")
img_hsv = cv2.cvtColor(img_scr, cv2.COLOR_BGR2HSV)

※openCVでは画像をimreadで読み込んだときはRGBではなくBGRとして読み込まれている.

画像のサイズを取得し,検出した色を格納する配列を用意

height = img_hsv.shape[0]
width = img_hsv.shape[1]
pixels = []

各ピクセルを調べてある閾値を満たしたらpixels[]に格納する.
ここでは白色や暗い色を検知したくないので , Saturation(彩度)を45以上
Value(明度)を32以上とした.

for y in range(height):
    for x in range(width):
        if (img_hsv[y, x, 1] > 45 and 32 < img_hsv[y, x, 2]):
            pixels.append(img_hsv[y, x, 0])

今回は色相を12分割して色を判断する.
ヒストグラムの出力

plt.xlim(0,180) # x軸の範囲
plt.xticks([0, 15, 30, 45, 60, 75, 90, 105, 120, 135, 150, 165, 180]) # x軸の目盛りの設定
plt.hist(pixels, bins=12, range=(0,180)) # ヒストグラムの出力 

その後最頻値を求めて その画像の色を判断する.

# 軸の作成 (0から180を13等分[12個の区間を出したいため]) 
bins = np.linspace(0, 180, 13)  

# pixelsをそれぞれの要素を12分割した色範囲に格納  
index = np.digitize(pixels,bins) 

# 最頻値を求めるためにCounterを使う
c = Counter(index)
print(c)
mode = c.most_common(1)

# 最頻値の出力
print(mode[0][0])

結果

トマトの画像

f:id:appli-in:20180218185351p:plain:w600
画像1の結果
Counter({1: 188904, 2: 4436, 3: 3005, 12: 2650, 4: 570, 5: 106, 6: 4})
最頻値 : 1

ちゃんとトマトの赤色が一番多い結果になっている.

RGBの画像

f:id:appli-in:20180218185445p:plain:w600
画像2の結果
Counter({1: 5373, 5: 5372, 9: 5368, 11: 1998, 3: 1996, 7: 1996, 2: 39, 10: 39, 6: 37, 4: 36, 8: 34, 12: 30})
最頻値 : 1

RGBのあたりがそれぞれ一番多い結果になっている.

まとめ

OpenCVで画像がどんな色なのかを抽出するために,HSV形式に変換して,結果的にヒストグラムにすることで色を判別した.結果的に画像がどんな色で構成されているかをヒストグラムで可視化することができた.今回はやっていないが,基本的に色を抽出したい物体は画像の中心に位置することが多いので,中心付近のピクセルからのみ抽出すれば,範囲を限定できるのでより効果的かもしれない.

ソースコード

# -*- coding:utf-8 -*-
import numpy as np
import matplotlib.pyplot as plt
import cv2
from collections import Counter

# 画像を読み込み
img_scr = cv2.imread("tomato.jpg")

# BGR->HSV に変換
img_hsv = cv2.cvtColor(img_scr, cv2.COLOR_BGR2HSV)

height = img_hsv.shape[0]
width = img_hsv.shape[1]

pixels = []

for y in range(height):
    for x in range(width):
        if (img_hsv[y, x, 1] > 45 and 32 < img_hsv[y, x, 2]):
            pixels.append(img_hsv[y, x, 0])

plt.xlim(0,180)
plt.xticks([0, 15, 30, 45, 60, 75, 90, 105, 120, 135, 150, 165, 180])
plt.hist(pixels, bins=12, range=(0,180), align="mid",rwidth=1.0,color="blue")

bins = np.linspace(0, 180, 13)
print(bins)
index = np.digitize(pixels,bins)
c = Counter(index)
print (c)
mode = c.most_common(1)
print(mode[0][0])