はてなフォトライフの色別写真のように画像から特長のある色を抽出する方法を教えてください。

このような機能をPerlで実現させたいと思っています。よろしくお願いします。

回答の条件
  • 1人2回まで
  • 登録:2006/11/07 22:19:58
  • 終了:2006/11/09 00:02:35

ベストアンサー

id:chaobun No.1

chaobun回答回数38ベストアンサー獲得回数62006/11/08 21:07:29

ポイント100pt

Perlでの画像処理をしたことがないので実装方法まではわかりませんが、特長のある色の抽出方法ならば、いくつか思いつきます。

一番簡単だと思われるものは画像内の画素を白、黒、灰色と赤、青、緑…といった色クラスに振り分け、多数決をとって画像がどの色クラスに所属するか決める処理です。

画像の色を全てHSV(or HSI)表色系にいったん変換し、色相、彩度、輝度を得ます。

全体に占める割合の大きい色を得るのなら色相をn段階に分けて、そのヒストグラムが最大になるような色を選択すればよいでしょう。その際、白と黒に関しては色相が定義されないので彩度が閾値より低いものを上記のヒストグラムを得る処理からはずし、輝度によって白、黒、灰色にわけてカウントするのが直感的だと思います…ただし、カウントの仕方が別のカラーとは違うので多数決でこの彩度が低い三色と他の色を直接比較するのは出来ません。

あとは、こうやって投票された色のクラスのうち、上位2組を選べば特長ある2色が一応選べます。

アルゴリズムがややこしいので簡単にCでコードを書くと(すみません…perlは慣れていないので…)

#define HUE 0 // 色相を呼ぶインデックス (hsv[HUE]は色相値)
#define SAT 1 // hsv[SAT]は彩度
#define VAL 2 // hsv[VAL]は輝度
#define COLNUM 8 //彩度の十分高い色をCOLNUM色=8色に分ける
#define GRAYNUM 3 //グレイスケールをGRAYNUM色=3色に分ける
#define SATTHR 30 //色味があるかない(灰色)かを判断する閾値

int getSpecificColor(unsinged short *imageBuff[3], int imageWidth, int imageHeight, int* output[2]){
  int colorCls[COLNUM+GRAYNUM]; // 色クラスの数(ヒストグラム)をカウントするための入れ物
  int pxNum = imageWidth * imageHeight; //画像のピクセル数

  for(int i=0;i<pxNum;i++){
    double hsv[3] = convertRGB2HSV(imageBuff+i); //RGB値をHSVに変換する
    if(hsv[SAT]>SATTHR){ //彩度が閾値よりも高いものの処理
       colorCls[(int)(hsv[HUE]/COLNUM)]++;
    }
    else{ //彩度が低いものは灰色とみなして輝度で分類
       colorCls[COLNUM+(int)(hsv[SAT]/GRAYNUM)]++;
    }
  } //ヒストグラムの獲得完了
  selectSpecificColor(colorCls, output); //ヒストグラムから、なんらかの基準で2色を選んでoutputに色クラス番号を入れる
  return;
}

といった感じです、但し実装はしてないのでミスはあるかも…

この中でselectSpecificColorの作り方が一番みそになると思います。

単なる多数決だと直感にあわないでしょうし、第一、グレイスケールとカラーのカウントの仕方が違うので、それらは必ず別々に処理しなければなりません。

全体に占める割合がよっぽど高いときのみグレイスケールの色も採用して、それ以外の時は彩度の高い色クラスから選ぶのがいいと思います。

それで、一色が他の色に比べて圧倒的に多いときにだけoutput[0]と[1]の両方に同じ色を入れるとかすれば完璧かと…。


参考

HSV, RGBなどの表色系について

http://ja.wikipedia.org/wiki/%E8%89%B2%E7%A9%BA%E9%96%93

ヒストグラム(ここでは、いわゆる輝度分布のことのみを指し示す言葉ではないので注意)

http://ja.wikipedia.org/wiki/%E3%82%B0%E3%83%A9%E3%83%95#.E6.9F....


perlって指定されているのに、申し訳ありません…

id:wekenny

いやいやとんでもないです。詳しい回答ありがとうございました。

コードまで書いていただきすごいうれしいです。

HSV表色系っていうのがあるんですね~知りませんでした。

週末にいろいろ試してみます。ありがとうございました!

2006/11/08 23:59:08
  • id:chaobun
    RGBをHSV表色系に変換する関数のリンクを探して見ました。
    http://forum.doom9.org/showthread.php?p=895050

    それと、

    else{ //彩度が低いものは灰色とみなして輝度で分類
    colorCls[COLNUM+(int)(hsv[SAT]/GRAYNUM)]++;
    }

    ここ、
    else{ //彩度が低いものは灰色とみなして輝度で分類
    colorCls[COLNUM+(int)(hsv[VAL]/GRAYNUM)]++;
    }
    の間違いです…この分だと他にもありそう…。

    画像の画素値の読み込みとか(perlではしたことないので全然しりませんけれど)大変そうですが、それさえ出来れば色々考えて作ってみるのは楽しいと思います。頑張って下さいね。
  • id:chaobun
    ごはっ ━━(゜Д゜;)━━━
    会社の帰りに自転車こぎながらふと、間違えに気づきました…なんどもすみません!!wekennyさんがここをまだ見てればよいのですが…。

    colorCls[(int)(hsv[HUE]/COLNUM)]++;
    ↑HUEは通常360度なので、これをCOLNUM個のクラスに分けるなら
    colorCls[(int)(hsv[HUE]/ (360/COLNUM) )]++;
    ですね…。

    colorCls[COLNUM+(int)(hsv[VAL]/GRAYNUM)]++;
    も同様に
    colorCls[COLNUM+(int)(hsv[VAL]/ (256/GRAYNUM) )]++;
    です…。
  • id:wekenny
    修正のコメントありがとうございます。
    この質問について長い時間考えてくださっていたようで
    感謝しています。

    実際にいろいろな画像で試してみたところ、
    chaobunさんのおっしゃるとおりグレイスケールと
    カラーの判定が難しかったです・・・。
    ちょっとこの点についてはまだ解決していないので、
    今後いろいろと試してみます。
    でも、かなり直感に近い結果を取得できるようになりました。
    ありがとございました!

この質問への反応(ブックマークコメント)

「あの人に答えてほしい」「この質問はあの人が答えられそう」というときに、回答リクエストを送ってみてましょう。

これ以上回答リクエストを送信することはできません。制限について

絞り込み :
はてなココの「ともだち」を表示します。
回答リクエストを送信したユーザーはいません