ページ 11

画像を2値化する

Posted: 2011年10月12日(水) 09:54
by カイト
c言語  Linux

下記のプログラム中で2値化するプログラムを書くところがあるのですが全くわかりません。
ググったりしても、わかりやすいものがなかったのでここで教えていただきたいと思います。
よろしくお願いします。

コード:

/* Hilditch の細線化のプログラム thinning.c */
#include<stdio.h>
#include<stdlib.h>
#include"mypgm.h"
#define GRAY 128

int func_nc8( int *b )
/* 注目画素の連結度(4連結)を求める関数 */
{
    int n_odd[4] = { 1, 3, 5, 7 }; /* 奇数の近傍番号 */
    int i, j, sum, d[10]; /* 制御変数,計算途中を代入する変数 */
    
    for ( i = 0; i <= 9; i ++ ){
        j = i;    if (i==9) j = 1;
        if ( abs( *( b + j ) ) == 1 ) d[i] = 1;
        else d[i] = 0;
    }
    sum = 0;
    for ( i = 0; i < 4; i ++ ){
        j = n_odd[i];
        sum = sum + d[j] - d[j] * d[j+1] * d[j+2];
    }
    return( sum );
}

void thinning( img1,img2,  x_sz, y_sz)
	unsigned char img1[MAX_SIZE][MAX_SIZE];
	unsigned char img2[MAX_SIZE][MAX_SIZE];
	int x_sz, y_sz;
/* 2値画像の細線化処理(img2[y][x] を最初は作業領域として使う)*/
/* (  = MAX_BRIGHTNESS --> 1, GRAY --> -1, 0 --> 0 とみなす)  */
/* 原画像img1[y][x] ===> 処理後の画像img2[y][x]             */
{
    int offset[9][2] = {{0,0},{1,0},{1,-1},{0,-1},{-1,-1},
           {-1,0},{-1,1},{0,1},{1,1} }; /* 近傍画素へのオフセット値 */
    int n_odd[4] = { 1, 3, 5, 7 };      /* 近傍番号(奇数)         */
    int px, py;                         /* 近傍画素の(x,y)絶対座標  */
    int b[9];                           /* 自分を含む9近傍の値     */
    int condition[6];               /* 条件1~6:=1のとき“成立” */
    int counter;                    /* ラベルに変化が生じた画素の数 */
    int i, x, y, copy, sum;         /* 制御変数など                 */
    int path;                       /* 画像の走査回数を表す変数     */

    printf("Begin thinning processing.\n");
    /* 作業配列の初期化 */


    for ( y = 0; y < y_sz; y ++ )
        for ( x = 0; x < x_sz; x ++ )
            img2[y][x] = img1[y][x];
    /* 処理開始 */
    path = 1;
    do {
        printf("path : %d\n", path);
        counter = 0;
        for ( y = 0; y < y_sz; y ++ ){
            for ( x = 0; x < x_sz; x ++ ){
                /* (x,y)を含む9近傍にデータを代入する */
                for ( i = 0; i < 9; i ++ ){
                    b[i] = 0;
                    px = x + offset[i][0];    py = y + offset[i][1];
                    if ( px >= 0 && px < x_sz && 
                         py >= 0 && py < y_sz )
                         if ( img2[py][px] == MAX_BRIGHTNESS )
                             b[i] = 1; 
                         else if ( img2[py][px] == GRAY ) b[i] = -1;
                }
                for ( i = 0; i < 6; i ++ ) condition[i] = 0;
                
                /* 条件1:図形画素である */
                if ( b[0] == 1 ) condition[0] = 1;
                
                /* 条件2:境界点である */
                sum = 0;
                for ( i = 0; i < 4; i ++ )
                    sum = sum + 1 - abs( b[ n_odd[i] ] );
                if ( sum >= 1 ) condition[1] = 1;
                
                /* 条件3:端点を除去しない */
                sum = 0;
                for ( i = 1; i <= 8; i ++ )
                    sum = sum + abs( b[i] );
                if ( sum >= 2 ) condition[2] = 1;
                
                /* 条件4:孤立点を保存する */
                sum = 0;
                for ( i = 1; i <= 8; i ++ )
                    if ( b[i] == 1 ) sum++;
                if ( sum >= 1 ) condition[3] = 1;
                
                /* 条件5:連結性を保存する */
                if ( func_nc8( b ) == 1 ) condition[4] = 1;
                
                /* 条件6:線幅2の線分の片側だけを削除する */
                sum = 0;
                for ( i = 1; i <= 8; i ++ )
                    if ( b[i] != -1 ) sum ++;
                    else {
                        copy = b[i];    
                        b[i] = 0;
                        if ( func_nc8( b ) == 1 ) sum ++;
                        b[i] = copy;
                    }
                if ( sum == 8 ) condition[5] = 1;
                
                /* 最終判定 */
                if ( condition[0] && condition[1] && condition[2] &&
                     condition[3] && condition[4] && condition[5] ){
                    img2[y][x] = GRAY; /* = -1 を表している */
                    counter ++;
                }
            } /* end of x */
        } /* end of y */
        if ( counter != 0 ){
            for ( y=0; y<y_sz; y++)
                for ( x=0; x<x_sz; x++)
                    if ( img2[y][x] == GRAY ) img2[y][x] = 0;
        }
        path ++;
    } while ( counter != 0 );
}

main( )
{
  /* 細線化する前に、元画像を2値化する必要がある。それを書いてみよ。*/
  /* 2値化の結果をさらに、平滑化を試みると細線化の結果にどんな影響が出るかも検討してみよ。*/
    int x, y;
    
    load_image_data( image1, "test.pgm");
    for(y = 0; y<y_size; y++)
      for(x = 0; x <x_size; x++){
	if (image1[y][x] > 190)
	  image1[y][x] = 255;
	else 
	  image1[y][x] = 0;
      }
    save_image_data(image1, x_size, y_size);
    thinning(image1, image2,  x_size, y_size ); 
    save_image_data(image2, x_size, y_size);
    return 0;
}

Re: 画像を2値化する

Posted: 2011年10月12日(水) 10:26
by バグ
初心者だというのは免罪符ではありません。
書店に出向き、実際に手にとって、自分にとって分かりやすい書籍を自腹で購入して、必死に勉強することをオススメします。


というか、「画像処理」をキーワードにググるだけでも結構な数がヒットしますけどね。
分かりやすそうなところを紹介しときます。

サイト
http://homepage2.nifty.com/tsugu/sotuke ... ec3-2.html
http://msdn.microsoft.com/ja-jp/academic/cc998604

Re: 画像を2値化する

Posted: 2011年10月13日(木) 14:41
by softya(ソフト屋)
多重トピックに付き転記:
プログラム中で画像の細線化をする前に2値化しなければいけないのですが、画像処理(正直プログラミングも)はとても苦手なので助けて頂きたいです。
ある基準値を求めて、それより高いか低いかで白黒を決めるということをすればいいということはわかったのですが、それをプログラムにしようとしてもうまくいきません。
コメントの「ここで2値化」というところに当てはまる形の2値化のプログラムを教えてください。
よろしくお願いします。

環境:Linux c言語

画像: http://www.dotup.org/uploda/www.dotup.org2134415.jpg

「画像の2値化 • C言語交流フォーラム ~ mixC++ ~」
http://dixq.net/forum/viewtopic.php?f=3&t=9383
まず、フォーラムルールにあるテンプレートに記入願えますか。特に大事なのはC言語の理解度です。
http://dixq.net/board/board.html
フォーラムルールを熟読いただきたいのですが、この掲示板は課題丸投げは原則禁止となっています。
お手伝いはさせて頂きますので、理解できところを出来るだけ詳しく説明してください。

ちなみに /* ここで2値化*/ とコメントで書かれている場所はC言語のプログラムの流れとして著しく問題がある場所です。

[追記]
#include"mypgm.h"
のmypgm.hをいただかないとコンパイルできません。pgm関係のライブラリもあったらセットでお願いします。

Re: 画像を2値化する

Posted: 2011年10月13日(木) 15:52
by カイト
御丁寧に対応していただきありがとうございます。
課題の期限が今日中なのであせってしまいました。

[1] 質問文
 [1.1] 自分が今行いたい事は何か
先ほど提示したプログラムを完成させ、Xwindowシステムで任意の画像のsobel変換と細線化処理結果を以下のように表示させる、という課題です。(わかりにくいですが、要するに3つの画像を1つのwindowに表示させるということです)
     ___________
    |   |       |
    |   |細線化の結果 |
    |元画像|______ |
    |   |     |
    |   |sobel変換の結|
    |   |果      |
    |___________

先ほどの未完プログラムの他にもファイルが4つ(ヘッダーファイル2つ)あります。
http://www.dotup.org/uploda/www.dotup.org2134496.h.html
http://www.dotup.org/uploda/www.dotup.org2134545.h.html
http://www.dotup.org/uploda/www.dotup.org2134548.c.html
http://www.dotup.org/uploda/www.dotup.org2134554.c.html

 [1.2-1.4] どのように取り組んだか(プログラムコードがある場合記載)
2値化のアルゴリズムは理解できたのですが、それを実際にプログラムする段階で行き詰まってしまいました。
色々とググって以下のようなヒントを見つけましたが

コード:

unsigned char LUT[256] = {0};

//二値化ルックアップテーブルの作成

for ( i = Threshold; i < 256; i++)

    LUT[i] = 255;

//二値化処理

for ( i = 0; i < Width * Height; i++)

    pDst[i] =  LUT[ pSrc[i] ];
これをまんまやっても当然エラーが出ますし、どこを変えればいいのかわからず行き詰まってしまいました。

[2] 環境  
 [2.1] OS :Linux
 [2.2] コンパイラ名 :gcc

[3] その他
学びは始めて2年ちょっとですが、アルゴリズムが理解できてもそれをプログラム化するのは不得意で、転部を考えるくらい自分には不向きだと感じています。丸投げは自分のためにならないと思いますが、プログラマやSEになろうとは思っていないので正直単位さえとれればと考えています。それでも理解しなければいけないかもしれませんが、提出期限が今日までなので焦っています。

ちなみに /* ここで2値化*/ とコメントで書かれている場所はC言語のプログラムの流れとして著しく問題がある場所です。
課題を出した先生もプログラムにはバグがあるといっていたのでそれかもしれません。

Re: 画像を2値化する

Posted: 2011年10月13日(木) 15:56
by softya(ソフト屋)
申し訳ありません。
夜にでも考えようと思っていたので私ではお役に立てないかも知れません。
昼間はちょっと手を付けれないです。

Re: 画像を2値化する

Posted: 2011年10月13日(木) 16:24
by カイト
softya(ソフト屋) さんが書きました:申し訳ありません。
夜にでも考えようと思っていたので私ではお役に立てないかも知れません。
昼間はちょっと手を付けれないです。
夜でも全然構いません。
その間、少しでも自分で理解できるよう考えておきます。
期限を過ぎても多少減点されるだけなので、明日になっても大丈夫です。
急かしてすみません。

Re: 画像を2値化する

Posted: 2011年10月13日(木) 18:26
by hss12
今手元にLinuxのパソコンがないので勘での回答ですが

コード:

for(y = 0; y<y_size; y++)
	for(x = 0; x <x_size; x++){
		if (image1[y][x] > 190)
			image1[y][x] = 255;
		else 
			image1[y][x] = 0;
}
この部分ですでに答えが書いてあると思われるのですが。
濃度が190より大きければ255(白?)
小さければ0(黒?)になって2値化されていると思われます。


平滑化のプログラムはこんな感じでしょうか

コード:

int i, j, m, n, x, y;
double sum;

for (i = 0; i < y_size; i++) {
	for (j = 0; j < x_size; j++) {
		sum = 0.0;
		for (m = -1; m <= 1; m++) {
			for (n = -1; n <= 1; n++) {
				y = i + m;
				x = j + n;
				if ( y < 0) y = 0;
				if ( x < 0) x = 0;
				if ( y > y_size) y = y_size;
				if ( x > x_size) x = x_size;
				sum += image1[y][x];
			}
		}
		sum = sum / 9;
		if (sum <   0) sum =   0;
		if (sum > 255) sum = 255;
		image1[i][j] = (unsigned char)sum;
	}
}
3x3を想定していますが5x5にしたければ範囲を-2~2、sum/25にすればいけるかな。
このままコピーして動くかわかりませんが考え方はこんな感じかと。
このコードはオーム社C言語で学ぶ実践画像処理を参考にして書いてます。

Re: 画像を2値化する

Posted: 2011年10月13日(木) 22:17
by softya(ソフト屋)
こちらもXwindowをコンパイル出来るLinux環境が無いので準備に時間がかかりそうですね。
Ubuntu環境に構築してみます。

下記の本当のファイル名も教えて下さい。
http://www.dotup.org/uploda/www.dotup.org2134496.h.html
http://www.dotup.org/uploda/www.dotup.org2134545.h.html
http://www.dotup.org/uploda/www.dotup.org2134548.c.html
http://www.dotup.org/uploda/www.dotup.org2134554.c.html

まず、hss12さんの書き込み元にやってみてくださいね。

[追記]
ちょっとやって見ましたが、プログラムがコンパイルが通る以前の問題だったり、pgmフォーマットをよく知らなかったり、Xwindowに詳しくなかったりで動くまで持っていくのは私では時間がかかりそうです。どなたか詳しい人がいたらHELPをお願いします。

Re: 画像を2値化する

Posted: 2011年10月20日(木) 07:00
by カイト
うまくいきました!
ありがとうございました<(_ _)>