C言語による輪郭追跡処理について

新規作成:2000/08/29
最終更新:2000/09/23
酒井理雄(TSUGU)

はじめに

ここでは、私、酒井理雄(TSUGU)が卒業研究において作成した DIB画像の輪郭追跡プログラムのアルゴリズムについて解説をしたいと思います。
ご意見やご感想、また、ここおかしいんじゃない?というような所があれば連絡していただけると作者が喜びます。

最初にこのドキュメント中の画像の見方について説明します。
下のような画像が多数出てきますが、その色は次のような意図で塗られています。

抽出した輪郭点

背景色画素
灰色
有効(前景色)画素
青丸
近傍を調査中の点

輪郭追跡処理とは?
考え方
調査開始点の指定
実際のプログラム

TOP

 

輪郭追跡処理とは?

通常、輪郭抽出にはソーベルフィルタなどを用いた演算を行いますが、処理対象画像が2値の場合境界線をた どるようにして輪郭線を抽出することができます。この方法をここでは輪郭追跡処理と呼びます。
下にそのイメージ図を示します。左はオリジナル(入力)画像、右は抽出した輪郭を黒色で表したもので、赤丸のある格子から 追跡を開始し、赤の矢印の順に輪郭を追跡していきます。


オリジナル(入力)画像

輪郭抽出結果(出力)画像

TOP

 

考え方

輪郭追跡処理の考え方は簡単で次のような手順を踏みます。

  1. 画像内を左上から右下に向かって走査して輪郭追跡の開始点を決定する。
  1. この追跡開始点を追跡点とし、その追跡点を中心とした8近傍を図の1の所から順に 反時計回りで調べる。
  1. での1から4の調査点において画素が存在しない場合は孤立点であるの でに戻る。

  1. 一番最初に現れた画素を新たな追跡点として、その追跡点を中心とした8近傍を反時計回りで調べる。 (調査開始点の指定は後述
  1. の操作を繰り返していくと1本の輪郭線を得ることが出来る。
  1. 追跡開始点と追跡点が同じ座標になったら追跡処理終了。
    別の輪郭線を求めるため、へもどる。

TOP

 

調査開始点の指定

考え方の4番目の項目にある調査開始点は、前回の追跡点との位置関係 によって決まります。
この調査の順番を間違えてしまうと最悪の場合無限ループに陥ってしまうので気を付けてください。(やった人 > 私(^^;)
その法則は以下の様に考えることが出来ます。

このような順序で画像内の輪郭を追跡中であることを考えます。 ここで水色の格子は調査を完了した点であることを示しています。
周りの点を調査するときは必ず左回りに調査するという決め事があるので、この図のように青丸のある格子で調査するときは、 水色の格子の所は全て調査済みであり、必ず有効画素ではないことがわかります。 ですから、この図のように前の追跡点が右上にある場合は右上は調査済みであるのでこれを最後に調査する 順番、つまりこのような順に調査すればよいことがわかります。


この前回の追跡点の位置と調査の順番をまとめたものを下に示します。
(この図は前回の追跡点が「前回の追跡点」と書いてある方向を向くように配置されています。)

前回の
追跡点

TOP

 

実際のプログラム
実際にプログラムにする前に一つ決めておくことがあります。それは調査する点の向きに対応する数値を決めておくことです。
例えば左上を調査するときは0、左を調査するときは1というようにです。
これは統一してあればどのような番号を割り振っても問題ないのですが、私は次のように番号を割り振りました。以下これを用いて 説明します。

調査点に対応する番号

さて、これから実際のプログラムに入りますが、考え方の処理はみなさんはどのようになると思いますか? 私は最初whileforなどを使って上手くできないものかと考えていたのですが、ある参考書の似たような処理の所を見つけて 目から鱗が落ちました。 switch~case文を使うのです。
以下にそのプログラムを紹介します。

//pget(int x, int y)        : 入力画像の指定したx,y座標からピクセルの色を読み出す(0か1)
//pset(int x, int y, int c) : 出力画像の指定したx,y座標に色をセットする(cは0か1)
//width                     : 画像幅
//height                    : 画像高さ
int sx, sy;    //追跡開始点
int px, py;    //追跡点
int vec;       //調査点フラグ
int IsFirst;   //

//画像内を捜査し有効画素を探す
for(sy=0; sy < height; sy++) {
    for(sx=0; sx < width; sx++) {
        //有効画素があった場合ループから抜ける
        if( pget(sx, sy) != 0 ) {
            break;
        }
    }
}

//有効画素が見つかっていた場合、追跡処理に入る
if( sx < width ) {
    px = sx;
    py = sy;
    pset(px, py, 1);
    vec = 2;      //最初の調査点を左下にセットする
    IsFirst = 1;

    //追跡開始点と追跡点が同じ座標なるまで輪郭追跡処理
    while( px != sx  ||  py != sy  ||  IsFirst == 1 ) {
        switch(vec) {
            case 0:    //左上を調査
                if( pget(px-1, py-1) != 0 ) {
                    pset(px-1, py-1, 1);
                    px = px-1; py = py-1;
                    vec = 6;
                    break;
                }
            case 1:    //左を調査
                if( pget(px-1, py) != 0 ) {
                    pset(px-1, py, 1);
                    px = px-1;
                    vec = 0;
                    break;
                }
            case 2:    //左下を調査
                if( pget(px-1, py+1) != 0 ) {
                    pset(px-1, py+1, 1);
                    px = px-1; py = py+1;
                    IsFirst = 0;
                    vec = 0;
                    break;
                }
            case 3:    //下を調査
                if( pget(px, py+1) != 0 ) {
                    pset(px, py+1, 1);
                    py = py+1;
                    IsFirst = 0;
                    vec = 2;
                    break;
                }
            case 4:    //右下を調査
                if( pget(px+1, py+1) != 0 ) {
                    pset(px+1, py+1, 1);
                    px = px+1; py = py+1;
                    IsFirst = 0;
                    vec = 2;
                    break;
                }
            case 5:    //右を調査
                if( pget(px+1, py) != 0 ) {
                    pset(px+1, py, 1);
                    px = px+1;
                    IsFirst = 0;
                    vec = 4;
                    break;
                }
                else {
                    //孤立点であった場合
                    if( IsFirst == 1 ) {
                        IsFirst = 0;
                        break;
                    }
                }
            case 6:    //右上を調査
                if( pget(px+1, py-1) != 0 ) {
                    pset(px+1, py-1, 1);
                    px = px+1; py = py-1;
                    vec = 4;
                    break;
                }
            case 7:    //上を調査
                if( pget(px, py-1) != 0 ) {
                    pset(px, py-1, 1);
                    px = px; py = py-1;
                    vec = 6;
                    break;
                }
            vec = 0;
        }
    }
}

2000/09/20 プログラム中psetの引数が足りなかったのを修正しました
2000/09/23 上記のミスが直っていない場所があったのを修正しました。

TOP

戻る

Counter