ランサーズ等のサービスを開発・運用する中で得た知識やノウハウを紹介しています。

HSV_cone

Labels:  PHP 投稿者:satoshi

PHP を用いた色検出アルゴリズム – ランサーズポートフォリオを支える技術

こんちにわ!ランサーズエンジニアのsatoshiです。本日は、2013年2月にリリースした、「ランサーズポートフォリオ」で使っているPHPでの色検出アルゴリズムについて紹介します。この機能は、ランサーズを卒業した ウーカシュ・クラフチック により作られました。とても良い機能なので、是非紹介したいと思います。

ランサーズポートフォリオについて

みなさん、「ランサーズポートフォリオ」という機能をご存知でしょうか?
ランサーズポートフォリオは、ランサーズに登録しているデザイナー様が、自らの作品を公開することができるサービスです。現在5万点近い作品が投稿されており、仕事を依頼したい方はこれらの作品を通じて、デザイナーを探すことが出来ます。

2013-11-05-00-05-32

そんなランサーズポートフォリオですが、登録されたポートフォリオ作品を「色」から検索する機能がついています。今回は、この「色検索」を PHP を使ってどのように実装するか、ご紹介したいと思います。

2013-11-05-00-01-49

画像のどの部分を判定するか?

まず始めに考えるべきは、「画像のどの部分を検出の対象とすべきか?」という問題です。対象となるエリアは ROI (Range Of Interest) とも呼ばれ、対象画像の中心部を長方形に切り取った形をしています。

roi_s

なぜこのように画像の中心部を抽出するのでしょうか? それは、「顔」や「ロゴ」といった、画像の主となる構成要素が大抵画像の中心部に配置されているためです。

どの部分を色検出の対象にするか決めたら、今度はどのくらいの粒度で検出を行うかを決めなければなりません。もちろん、粒度を細かくすれば色検出の精度は上がりますが、同時に処理時間が長くなってしまいます。今回、ランサーズでは縦解像度・横解像度それぞれの 1/10 の領域について色検出を行うことにしました。

    ... open image ...
    for ($x = $roi['x']['min']; $x <= $roi['x']['max']; $x += $pointDistance['x']) {
        for ($y = $roi['y']['min']; $y <= $roi['y']['max']; $y += $pointDistance['y']) {
            // get color vector of current pixel
            $colorVector = imageColorsForIndex($img, imageColorAt($img, $x, $y));
            ... do something with colour ...
        }
    }

色検出アルゴリズム

ランサーズポートフォリオでは、「赤」「オレンジ」「黄」「緑」「青緑」「青」「紫」「ピンク」「白」「グレー」「黒」「茶」の 12 種類に作品を分類しています。各作品をこれらのいずれかに分類する際に必要となるのが、「色の近似度の計算」になります。

ただ単に色情報を取得したいのであれば、先ほど示したコードから得られた色の情報をそのまま使えば問題ありません。ただし、今回のように、検出した色と特定の色の「近さ」を測りたい場合(色の近似度を調べたい場合)、いくつかのアルゴリズムを検討する必要があります。

RGB

最もよく知られており、かつ簡単なのが RGB 色空間を用いた近似度の検出でしょう。この場合、RGB で表現された 3 次元空間において 2 つの色を点で表現し、その幾何的距離によって近似度を計算することができます。

vector-distance

ほとんどの画像に対しいて、このアルゴリズムはうまくいきます。しかし、RGB 色空間には「淡い色をうまく表現できない」といった弱点があります。今回の例で言えば、「茶」「グレー」といった色がうまく識別されません。人間の目には「ピンク」と「茶」はだいぶ違って見えますが、RGB 空間においては、「ピンク」は「赤」よりも「茶」に近いと判定されてしまうのです。

    function colorDistance($a, $b) {
        return sqrt(pow($a['red'] - $b['red'], 2)
                    + pow($a['green'] - $b['green'], 2)
                    + pow($a['blue'] - $b['blue'], 2));
    }

HSV

HSV_cone-300x225

HSV 色空間を使えば、この問題は解決できます。HSV 色空間は上記画像のような円錐形をしています。RGB 色空間を HSV 色空間へと変換するには、次のようにします。

    function rgbToHsv($rgb) {
        $h = 0;
        $s = 0;
        $v = 0;
        $r = $rgb[0] / 255;
        $g = $rgb[1] / 255;
        $b = $rgb[2] / 255;
        $max = max( $r, $g, $b );
        $min = min( $r, $g, $b );
    
        if ( $max === $min ) $h = 0;
        else if ( $max === $r ) $h = ( 60 * ($g - $b) / ( $max - $min ) + 360 ) % 360;
        else if ( $max === $g ) $h = 60 * ( $b - $r ) / ( $max - $min ) + 120;
        else if ( $max === $b ) $h = 60 * ( $r - $g ) / ( $max - $min ) + 240;
    
        if ( $max === 0 ) $s = 0;
        else $s = 1 - $min / $max;
    
        $v = $max;
    
        $hsv = array($h, $s * 100, $v * 100);
        return $hsv;
    }

HSV 色空間においては、最も明るい色が円錐の底面に、最も暗い色が円錐の頂点にきます。また、淡い色は円錐の中心部に集まるといった特徴があります。ここで注意すべきは、HSV のうち H (Hue) は底面の円周にそって、0-360 の値を取るということです(上図参照)。したがって、HSV 色空間において近似度を計算するには、以下のような処理が必要になります。

    function colorDistance($a, $b) {
        $hueDiff = 0;
        // folding "H" dimension
        if ($a[0] > $b[0]) {
            $hueDiff = min($a[0] - $b[0], $b[0] - $a[0] + 360);
        } else {
            $hueDiff = min($b[0] - $a[0], $a[0] - $b[0] + 360);
        }
    
        return sqrt(pow($hueDiff, 2)
                    + pow($a[1] - $b[1], 2)
                    + pow($a[2] - $b[2], 2));
    }

まとめ

今回は RGB, HSV といった 2 つの方法で 2 つの色の間の近似度を計算する方法をご紹介しました。実際には、このようなコードで得られた 2 色間の距離を比較し、もっとも近い色を先述した 12 色から選んでいます。

ご存じの方もいるかもしれませんが、実際には RGB, HSV 以外にも LAB といった色空間があります。色検出においては LAB の方が良い結果を出すことが多いですが、RGB <=> HSV のように簡単に色情報を変換できません。もし機会があれば、LAB に関しても記事を書いてみようと思います。

参考文献

英語版の記事 をウーカシュがブログで公開してくれているので、こちらも参考にしてください。

Color difference algorithm - Emanuele Feronato
http://www.emanueleferonato.com/2009/08/28/color-differences-algorithm/

Fun with Colour Difference - Steve Hanov's Programming Blog
http://stevehanov.ca/blog/index.php?id=116

Color difference - Wikipedia, the free encyclopedia
http://en.wikipedia.org/wiki/Color_difference

Change Detection in Color Images
http://homepages.inf.ed.ac.uk/rbf/PAPERS/iccv99.pdf

LARGE SCALE IMAGE-BASED ADULT-CONTENT FILTERING
http://www.cs.cmu.edu/~har/visapp2006.pdf

Piximilar
http://research.cs.wisc.edu/vision/piximilar/

ランサーズではサービスを成長させてくれるエンジニア、デザイナーを募集しています!
ご興味がある方は、以下URLよりご応募ください。


【中途採用】
サービスリードエンジニア
テックリード(アーキテクト)
フロントエンドエンジニア
サーバーサイドエンジニア
業務エンジニア(社内システム基盤・基幹システム)

【インターン・学生バイト】
19新卒対象サマーインターン
エンジニアインターン

その他採用情報

Comment

  1. 7m4mon さん  2016年2月28日 10:40 PM

    はじめまして。こちらの記事、大変参考になりました。
    ところで、HSV色空間上の距離は、参考文献の「Change Detection in Color Images」の第3.3章によると、
    vs * cos(2πh)、 vs * sin(2πh) として極座標から変換した後に距離を求めるのが良いようです。
    計算時間がすごく増えてしまいますが…
    実験結果を
    http://nomulabo.com/sam/color_reduction.html
    にまとめましたのでご参考になれば幸いです。

関連記事

ランサーズ開発ランチ(Lunchers#3)~ねこもり高橋さん PHPバージョンアップ編~

エンジニアのshinです。 ランサーズでは4月から、ゲストを招いてランチを食べながら話をするランサーズ開発ランチ(Lunchers)という取り組みを実施しています。 5/16に実施した第3回の内容をご紹介します。   第3回のゲスト紹介 合同会社ねこ …

PHPカンファレンス福岡2018に登壇しました

SREチームの金澤です。 2018/06/16(土)に、PHPカンファレンス福岡に登壇させていただきました。 昨年に続き、2度目の登壇となりました。 この度は、採択していただきありがとうございました。 登壇内容 CakePHP 1.3 + PHP 5.3 → …

開発環境のDocker化

インフラエンジニアの金澤です。 今回、ランサーズの開発環境をDocker化しましたので、その内容を紹介いたします。 Docker移行の決断 Dockerについては、ここ1年で技術的な調査はしていましたが、決定的なメリットを見出していたわけではなく、採用に踏み切 …