質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

Q&A

1回答

2253閲覧

白黒画像の数を求めるプログラムがどうやっていいのかわかりません。

chihiro_0701

総合スコア10

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

0グッド

0クリップ

投稿2018/11/25 12:19

###今回の問題点
C言語初心者です。
下記のプログラムをサンプルプログラムを見ながら作成しては見たものの、うまくいかずに苦戦しています。
わかりづらい部分や、丸投げになってしまうかもしれませんが、どうぞよろしくお願いします。

###問題
下記を参考にして,次の画像データで同様にある地点からぬりつぶし処理を行った結果,連結された画素数を答えるプログラムを作成せよ.塗りつぶし開始位置は,キーボードから入力させた数字がxのとき,座標(x,x)からとする.画像データは上記と異なり,12×12とする.当然,課題のプログラム中のデータはこれに合わすように変更する点に注意.

###補足
画像処理や迷路を探索するロボットのプログラムでは,塗りつぶしルーチンによく用いられる.例えば9×9の白黒二値画像があったとする.このまんなかの(5,5)から塗りつぶしを行う.塗りつぶしとはその座標に連結している黒画素を抜き出す操作である.

□■■□□□□□□
■■□■■■■■□
□□□□■□■□□
■■□□■■■□□
■□□□■■□□□
□□□□□■□□□
□■■■■■■□■
□■□□□□□■□
■□□□□□□■□
この結果次の画像が出力されるはずである.

□□□□□□□□□
□□□■■■■■□
□□□□■□■□□
□□□□■■■□□
□□□□■■□□□
□□□□□■□□□
□■■■■■■□□
□■□□□□□□□
□□□□□□□□□
ここで,連結しているかどうかは上下左右の4方向だけで判定する.(これを4連結と呼ぶ.ちなみに斜めも含めて判定する場合には,8連結と呼ぶ.)

###出題画像
□□□□□□□■■■■■
□□■■□□□□□□□■
□■■□■■■■■■■■
□□□□□■□■□□□■
□■■■□■■■□■■■
□■□□□■■□□■■■
□□□□□□□□□■□■
□□■■■■■■□■□■
□■■□□□■□■□□■
□■□□□□■□■□□■
□■■■■■■■■□□■
■□□□□□□□□■■■
###参考
このような塗りつぶしを実現するには,再帰を用いる.

これを再帰呼出を利用して実現するには,次のような関数を用意するものとする.

int fill(int x, int y); //塗りつぶし関数
入力画像のデータは上記の画像の白を0,黒を1としてあらかじめ初期化しておく.出力画像のデータはすべて白(0)とする.

関数fillは,座標(x,y)から塗りつぶしを始めて,塗りつぶした画素の数を返すとする.ただし, 座標は左上を(0,0)とし右下を(8,8)とする.また,返値だけでなく塗りつぶした画素位置に相当するoutputの(x,y)をすべて1,ぬりつぶした領域外は0のままとする.

###実際にプログラムを作成してみたができない。
#include <stdio.h>
int fill(int x, int y);
int input[12][12] = {
{0,0,0,0,0,0,0,1,1,1,1,1},
{0,0,1,1,0,0,0,0,0,0,0,1},
{0,1,0,1,1,1,1,1,1,1,1,1},
{0,0,0,0,0,1,0,1,0,0,0,1},
{0,1,1,1,0,1,1,1,0,1,1,1},
{0,1,0,0,0,1,1,0,0,1,1,1},
{0,0,0,0,0,0,0,0,0,1,0,1},
{0,0,1,1,1,1,1,1,0,1,0,1},
{0,1,1,0,0,0,1,0,1,0,0,1},
{0,1,0,0,0,0,1,0,1,0,0,1},
{0,0,1,1,1,1,1,1,1,1,0,1},
{1,0,0,0,0,0,0,0,0,1,1,1},
};

int output[12][12] = {
{0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0},
};

int main(void)
{
int a,x,n,left,right,up,down;
scanf("%d", &x); //xをキーボードから入力
if(x>=12){
n=0;
printf("%d",n);
return 0;
}
n = fill(x, x); //座標x,xから塗りつぶすことにする
while((left==0)&&(right==0)&&(up==0)&&(down==0)){
n = fill(x-1,x);
left=left+n;
n = fill(x+1,x);
right=right+n;
n = fill(x,x-1);
up=up+n;
n = fill(x,x+1);
down=down+n;
}
a=left+right+up+down;
printf("%d\n",a);
return 0;
}

int fill(int x, int y)
{
int a,n;
if(input[x][x]==0){
n=0;
return n;
}
if(output[x][x]==1){
n=1;
return n;
}else{
output[x][x]=1;
n=0;
return n;
}
}
###サンプルプログラム
#include <stdio.h>
int fill(int x, int y);
int input[9][9] = {
{0,1,1,0,0,0,0,0,0},
{1,1,0,1,1,1,1,1,0},
{0,0,0,0,1,0,1,0,0},
{1,1,0,0,1,1,1,0,0},
{1,0,0,0,1,1,0,0,0},
{0,0,0,0,0,1,0,0,0},
{0,1,1,1,1,1,1,0,1},
{0,1,0,0,0,0,0,1,0},
{1,0,0,0,0,0,0,1,0}
};
int output[9][9] = {
{0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0},
};

int main(void)
{
int x,n;
scanf("%d", &x); //xをキーボードから入力
n = fill(x, x); //座標x,xから塗りつぶすことにする
printf("%d\n",n);
return 0;
}

int fill(int x, int y)
{
int left,right,up,down;
座標(x,y)がinputの範囲外であればダメ(0を返す)
inputの座標(x,y)を調べる.黒(1)でなければダメ(0を返す)
outputの座標(x,y)を調べる.すでに出力があれば(1ならば)ダメ(0を返す)
そうでないなら
outputの(x,y)を黒(1)に置き換える.
自分の上下左右を調べる.
つまり,left = fill(x-1, y);
right = fill(x+1,y);
up = fill(x,y-1);
down = fill(x,y+1);
返値は,left+right+up+down+1; //+1は自分自身の1画素分
}
たったこれだけで,複雑な塗りつぶしルーチンを記述できてしまう.

再帰プログラミングのポイントは,作成中の関数が予定どおりに動作できると思い込んでプログラムすることである.この場合,fillがちゃんと動作して,ちゃんと塗りつぶしを行って面積も正しく返してくれることを期待して,このように書き下す.

###実行例

5 ←キーボードから入力
20 ←20画素が連結されていた

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

fana

2018/11/26 01:33

サンプル(というかもう答えそのものに見えるが)があるようですし,まずはそれに沿った形でやってみた方がよろしいのでは…?
guest

回答1

0

簡単には、二重ループで、変化が無くなった時点で終了とする。で、できると思いますが、、。

C疑似

1input(x, y) = 2; 2bool found; 3 4do { 5 int xx, yy; 6 found = false; 7 for (yy = 0; yy < 9; yy++) { 8 for (xx = 0; xx < 9; xx++) { 9    input(xx, yy)1 で、 10 座標 (xx, yy)4連結に '2' となる input(?, ?)があれば、 11 input(xx, yy) = 2; //とする。 12 flag = true; 13 } 14 } 15} while (found); 16抜けた時の input の 2の場所が連結要素。

こんなのではダメ? (再帰じゃないけど) 効率は悪そう、、。
コード書いてみたいけど、時間が無くなったので。


沢山、質問を上げているようですが、全て同時? それとも宿題の山?

[追記]
再帰で書いてみました。もうちょっと面白いかと思ったのですが、簡単だったので、そのまま。
(元の入力データを書き換えています)

C

1void fill(int x, int y) 2{ 3 if ((x < 0) || (y < 0) || (x >= 12) || (y >= 12)) return; 4 if (input[x][y] != 1) return; 5 input[x][y] = 2; 6 fill(x - 1, y); 7 fill(x, y - 1); 8 fill(x + 1, y); 9 fill(x, y + 1); 10}

ところで、質問の主旨は何だったのでしょうか?

投稿2018/11/25 12:39

編集2018/11/26 14:25
pepperleaf

総合スコア6383

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問