緑色のピクセルを数える

ニューヨークの衛星画像から領域内の緑色の部分を検出することで、緑地面積を計算します。今回は単純な明るさによる2値化のみでなく、色座標の変換などの内容を含みます。また、各画素に直接的にアクセスする方法のサンプルにもなっています。

次のような衛星写真を用意しました。この画像から緑色の部分の面積を求めます。
newyork

  • 碇シンジ

    if(abs(ref-target+180)<thr||abs(ref-target-180)<thr)return 1;

    IsSimiarメソッドの上記の文の意図を教えてください。+180と-180しているのがわかりません。

    • Yuki Mochizuki

      コメントありがとうございます。

      色相は一般的には0~360ですが、OpenCVではint型が0~255までの整数しか保持できないために、色相を一般的な値に1/2を掛けて保持しています。すなわち、一般的には120が緑ですが、OpenCVでは60が緑になります。

      さて、次の行でも180を足し引きして判定している部分ですが、まず、abs(ref-target)<thr の部分はそのままで、2つの色相の差がthr以下であればtrueを返すという意味になります。次の行ですが、今回は緑(60)を見つけたいため、targetが60になっていますので、実は不要になります。しかし、例えば、赤(0=180)を見つけたいときには色相環は環状になっていますので、10も170もどちらも赤に近いということになります。このときに先の一行だけだと、targetに0を指定した時に10だけが赤に近いと判定されてしまい、170についてはabs(170-10)=160ですので赤に近くないと判定されてしまいます。これを解消するために180足し引きして、もう一度判定しています。このように色相環において、値が近いか否かを示すためには0と180が一周回って同じ色相であることを考慮する必要があります。

    • 碇シンジ

      早速回答してくださり本当にありがとうございます。とても丁寧ですぐに理解することができましたm(_ _)m
      こちらは、お時間あるときでいいんですが、よろしければ、こちらについてもご教授願います。。 if(
      hsv_img.data[ y * hsv_img.step + x * hsv_img.elemSize() + 1 ]>100 &&

      このif文の判定は緑であるならば、その画素の彩度は100より大きいだろう。と言う理解で大丈夫でしょうか。

    • Yuki Mochizuki

      よかったです。hsv_img .data[ y * hsv_img.step + x * hsv_img.elemSize() + 1 ]>100としているのは、彩度sを取り出しているのですが、いくら色相が緑60に近くても彩度が小さければ灰色に近くなり緑っぽくはならないためです。そのため今回は彩度を100以上にしています。

    • 碇シンジ

      なるほど、とてもわかりやすいです!ありがとうございます。

      今回は明度については検討してないですが、色の判定には不要なのでしょうか?

    • Yuki Mochizuki

      どこまでやるかの問題になりますが、今回は若草色も深緑色も緑として取りたかったので明度では条件をつけていません。もちろんあまりに薄い緑や濃い緑を除外するなら明度でも条件をつけるのがベターだと思われます。

    • 碇シンジ

      本当にありがとうございました。とても助かりました。

      これからも利用させていただきますm(_ _)m