サイトアイコン CV & Technologies

緑色のピクセルを数える

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

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

画像を表示する

まず画像を表示することから始めましょう。これは画像へのパスは各々で画像の置いてある場所に合わせてください。

#include "opencv/cv.h"
#include "opencv/highgui.h"
using namespace cv;
int main(){
    Mat img = imread("newyork.jpg", IMREAD_UNCHANGED);
    imshow("IMAGE",img);
    waitKey(10000);
    return 0;
}

GBR座標からHSV座標に変換する

「緑色」というような色によって場合分けをして処理をしたい場合にはGBR座標は明るさなどの影響を受けやすいため不向きです。そこでHSV座標に変換してしまいます。ここで使う関数はcvtColor関数です。グレースケール化のときにも使った関数ですが、2つ目の引数をCV_BGR2HSVに変更することでBGRからHSVに変換することができます。

#include "opencv/cv.h"
#include "opencv/highgui.h"
using namespace cv;

int main(){
    Mat img = imread("newyork.jpg", IMREAD_UNCHANGED);
    Mat hsv_img;
    cvtColor(img, hsv_img, CV_BGR2HSV);
    return 0;
}

OpenCVでは色相の範囲が0~180であることに注意

一般的には色相は0~360の範囲で考えますが、OpenCVでは0~180で考えます。これは、8bitで扱える整数の最大値が255であることに起因するものです。緑色は約120度に当たる色相なので、この付近の色相を持つ画素を見つけ出します。今回は入力画像と同じ大きさの真っ黒な画像をもう1つ用意し、入力画像で緑色だった部分について、相当する位置の画素に255の値を入れていきます。これらの画像は2値画像になります。なお、色相が似ているかどうかを判別するためのIsSimilar関数を作っています。なお、0,360度が赤、60度が黄色、120度が緑、180度がシアン、240度が青、300度がマゼンタを表すおよその色相です。OpenCVでは0~180で色相を表すため、0,180が赤、30が黄色、60が緑、90がシアン、120が青、150がマゼンタを表すおよその色相になります。下の図はOpenCVでの色相と値の対応を示したものです。また、入力画像では緑色の部分の彩度が高いことから、彩度についても一定以上の値となるようなフィルターをかけ、精度の向上を試みています。

各画素に直接アクセスする

dataメソッドでアクセスできます。例えば、HSV画像のimgについて、座標(x,y)の画素のHSVの値を取得するには次のようなコードで表現します。

色相H : img.data[ y * img.step + x * img.elemSize() + 0 ]

彩度S : img.data[ y * img.step + x * img.elemSize() + 1 ]

明度V : img.data[ y * img.step + x * img.elemSize() + 2 ]

今回はHSV画像ですので関係ありませんが、BGR画像の場合には、0で青B、1で緑G、2で赤Rの値が取得できます。なお、img.stepはMatの横幅、img.elemSize()は1つの画素のデータのサイズを表しています。最後に緑色であると判定された画素の数と、全体に占める割合を表示しています。今回は、248518 pixels; 20.1405 %というような結果が得られました。下の画像は緑色であると判定された部分が白で表された2値画像です。

#include "opencv/cv.h"
#include "opencv/highgui.h"
#include <iostream>
using namespace cv;
using namespace std;

bool IsSimilar(int ref, int target, int thr){
    if(abs(ref-target)<thr)return 1;
    else if(abs(ref-target+180)<thr||abs(ref-target-180)<thr)return 1;
    else return 0;
}

int main(){
    Mat img = imread("newyork.jpg", IMREAD_UNCHANGED);
    Mat hsv_img;
    cvtColor(img, hsv_img, CV_BGR2HSV);
    
    Mat green_img=Mat(Size(img.cols,img.rows),CV_8U);
    Mat yellow_img=Mat(Size(img.cols,img.rows),CV_8U);
    
    int green_pixels=0;
    for(int y = 0; y < hsv_img.rows; ++y){
        for(int x = 0; x < hsv_img.cols; ++x){
            //OpenCVでは色相Hの範囲が0~180になっていることに注意
            //緑は120度 OpenCVでは 120/2=60 あたり
            if(
               hsv_img.data[ y * hsv_img.step + x * hsv_img.elemSize() + 1 ]>100 &&
               IsSimilar(hsv_img.data[ y * hsv_img.step + x * hsv_img.elemSize() + 0 ], 60, 20)
               )
            {
                green_img.data[y * green_img.step + x * green_img.elemSize()]=255;
                green_pixels++;
            }
        }
    }
    
    imshow("IMAGE",img);
    imshow("green-area",green_img);
    cout<<green_pixels<<" pixels; "<<(double)green_pixels/img.cols/img.rows*100<<" %"<<endl;
    waitKey(0);
    return 0;
}

モバイルバージョンを終了