画像処理(初級)

多くのプログラマーが使うが、言語の美しさを求めるプログラマーが嫌う言語といえば、PHPだと思うのだが(いろいろな関数が同じ名前空間にある・・・)、あまり深追いしない趣味程度で書くときは何も考えずに書けて良い。
とくにGDという画像処理ライブラリがインストール不要でレンタルサーバにインストールされていたので、監視カメラの画像処理に使うことにした。

パターン認識等はOpenCVの出番だが、今回は人の動きをトレースするだけだ。
動体検知ソフトで撮影した9枚の連続画像を使って明度の差で背景と人物を分離し、時間軸方向で人の動きを見る。そして入室か退出かを判断する。

まずこれは、オリジナル画像。退出時の画像だ。
動体検知をしてくれるフリーソフトを使っている。
ちょうど5枚目が動体検知の瞬間である。このソフトは検知前数秒を遡って画像にしてくれる。日付が記入されない2枚目の画像を利用し、これを背景とみなして全ピクセルの明度を記憶する。
明度は、単純にRGBの平均(R+G+B)/3とする。明度には他にも加重平均するなどの算出方法がある。
out-orig
これが処理後の画像。人間の中心線(緑)と移動ベクトル(水色)が出ている。
out-vect

そして記憶した2枚目と3枚目以降を比較し、明度の差を使って、人物だけを浮かび上がらせるようにする。
差が《明度差閾値》を超えたピクセルを人物がいそうな怪しいピクセルとすれば良い。
そのピクセルを縦方向(Y軸)に沿って加算すると、あるX軸(X)において、差が《明度差閾値》を超えたピクセルの総和が出る。

これをX軸方向に順次繰り返し、配列 $detect_x に格納する。格納した配列を見れば、X軸方向に《明度差閾値》を超えたピクセルの数が入っている。

《明度差閾値》はここでは20である。

$detect_x = array();
for ($iy=0;$iy<HEIGHT;$iy++) {
    for ($ix=0;$ix<WIDTH;$ix++) {
        $rgb = imagecolorat($img, $ix,$iy);
        $matrix = (
            (($rgb >> 16) & 0xFF)+
            (($rgb >> 8) & 0xFF)+
            ($rgb & 0xFF)) /3;

        if (abs($matrix-$base_rgb[$iy][$ix]) > 20) {
            $detect_x[$ix]++;
        }
        else {
            imagesetpixel($img,$ix,$iy,$marker);
        }

    }
}
$detect_x = array_filter($detect_x,function($v){return ($v > 150);});

この $detect_x を以下のコードに通すと、キーをX座標、値をX軸上で閾値を超えたピクセルの数の「部分配列」ができる。

人がいそうなX座標の「クラスタ」、「グループ」を出してくれるといったイメージである。

arsort($detect_x);
$last = 0;
$last_sub_array = array();
$sub_arrays = array();
foreach ($detect_x as $k => $v)
{
    if ($k - $last < 3) {
        $last_sub_array[] = $k;
    }
    else {
        $sub_arrays[] = $last_sub_array;
        $last_sub_array = array($k);
    }
    $last = $k;   
}
$sub_arrays[] = $last_sub_array;

このクラスタを走査して、最大のクラスタを見つけ、クラスタの先頭と最後尾のX座標を足して2で割ると、クラスタの中央のX座標がでる。これが「軸」、人がいると思われる「重心」だ。

$max = 0;
$axis = null;
foreach ($sub_arrays as $sub_array)
{
    $c = count($sub_array);
    if ($max < $c) {
        $max = $c;

        if (($sub_array[0] - $sub_array[$c-1])>10) {
            $axis = ($sub_array[0] + $sub_array[$c-1])/2;

        }
    }
}

先ほどの画像をもう一度貼る。この$axisが緑の線だ。
out-vect

$axisを、前の画像と比べることで移動した距離のベクトルを出せる。それが水色の線。

「入室」時。
in-orig

条件が良ければ、PHPだけで入退室くらいは知ることができそうだ。
in-vect

二枚目で人がいると、
bad
以下のように背景としてしまい、おかしなことになる。
bad-vect

コメントを残す

メールアドレスが公開されることはありません。

次のHTML タグと属性が使えます: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>