色相のみでヒストグラムを計算表示してみる

大雑把に、HSV 変換をした後に色相(Hue)だけを取り出して、ヒストグラムにしてみる。
こうすると、対象物(この場合はゲームの駒)が持っている「色」という特徴量がわかる…ハズ。原子分析みたいなものかも。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
#include "stdafx.h"
#include <iostream>
#include "opencv/cv.h"
#include "opencv/highgui.h"
using namespace std;
 
int main(int argc, char* argv[])
{
    char *fname = argv[1];
    cv::Mat image = cv::imread( fname );
    cv::Mat imageHsv;
 
    cv::cvtColor( image, imageHsv, CV_RGB2HSV );
    for ( int y=0; y<imageHsv.rows; ++y ) {
        for ( int x=0; x<imageHsv.cols; ++x ) {
            cv::Vec3b &v = imageHsv.at<cv::Vec3b>(y,x);
            v[0] = v[0]; // H
            if ( v[1] < 64 || v[2] < 64 ) v[0] = 300; // 閾値以下は範囲外とする
            v[1] = ( v[1] < 64 )? 0: 255; // S 閾値以下は白とみなす
            v[2] = ( v[2] < 64 )? 0: 255; // V 閾値以下は黒とみなす
        }
    }
 
    // ヒストグラム用
    const int ch_width = 180, ch_height=200;
    cv::Mat imageHist( cv::Size( ch_width, ch_height), CV_8UC3, cv::Scalar::all(255));
    cv::cvtColor( imageHist, imageHist, CV_RGB2HSV );
    for ( int y=0; y<imageHist.rows; ++y ) {
        for ( int x=0; x<imageHist.cols; ++x ) {
            cv::Vec3b &v = imageHist.at<cv::Vec3b>(y,x);
            v[0] = x;        // H
            v[1] = 100;        // S
            v[2] = 255;        // V
        }
    }
    cv::cvtColor( imageHist, imageHist, CV_HSV2RGB );
 
    cv::Mat hist;
    const int hdims[] = {180}; // 次元毎のヒストグラムサイズ
    const float hranges[] = {0,180};
    const float* ranges[] = {hranges}; // 次元毎のビンの下限上限
    const int chs[] = {0};
    double max_val = .0;
    cv::calcHist(&imageHsv, 1, chs, cv::Mat(), hist, 1, hdims, ranges);
    // 最大値の計算
    cv::minMaxLoc(hist, 0, &max_val);
    cv::Scalar color = cv::Scalar::all(100);
    hist = hist * (max_val? ch_height/max_val:0.);
    for(int j=0; j<hdims[0]; ++j) {
        int bin_w = cv::saturate_cast<int>((double)ch_width/hdims[0]);
        cv::rectangle(imageHist,
            cv::Point(j*bin_w, imageHist.rows),
            cv::Point((j+1)*bin_w, imageHist.rows-cv::saturate_cast<int>(hist.at<float>(j))),
            color, -1);
    }
    cv::cvtColor( imageHsv, imageHsv, CV_HSV2RGB );
 
    cv::namedWindow("original", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
    cv::namedWindow("result", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
    cv::namedWindow("hist", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
    cv::imshow("original",image);
    cv::imshow("result",imageHsv);
    cv::imshow("hist",imageHist);
    cv::waitKey(0);
 
    return 0;
}




 

背景がオレンジ色なので、それにヒストグラムが引っ張られてしまうのは後で検討するとして、色相が180の幅のままだと分解能が細かく過ぎてピークがとんがり過ぎるかなと。

試しに実画像を調べてみると、



のように広がりを持つので、相関係数でマッチさせるか、もっと単純に色を積算して max だけを取り出すか。色の検出自体は、おおまかで良い(白黒+6色ぐらい)ので、このぐらいであれば、どれだけ検出箇所が多くてもあっという間に終わるハズ。

カテゴリー: C++, OpenCV パーマリンク