P1 320 160 0 0 0 0 1 0 1 0 ..... 1 1 0 0 0 1 0 0 1 1 0 1 1 0 1 ..... 0 0 1 0

プログラミング入門
第12回講義
2次元配列の復習と応用
2次元配列の復習(2)
標準入出力・エラー出力(5)
リダイレクト(8)
パイプ(12)
2次元配列の応用(画像処理)(14)
マークのあるサンプルプログラムは
/home/course/prog0/public_html/2013/lec/source/
下に置いてありますから、各自自分のディレクトリに
コピーして、コンパイル・実行してみてください
Prog-0 2013 Lec12-1
Copyright (C) 1999 – 2013 by Programming-0 Group
2次元配列の宣言と構造
int data[3][5];
全体は配列
data
3行5列の
int型
2次元配列
列添字は0から
列数-1まで
[0][0] [0][1] [0][2] [0][3] [0][4]
[1][0] [1][1] [1][2] [1][3] [1][4]
[2][0] [2][1] [2][2] [2][3] [2][4]
行添字は0から
行数-1まで
Prog-0 2013 lec12-2
一つずつを「要素」と言う
data[行添字][列添字]
Copyright (C) 1999 – 2013 by Programming-0 Group
マクロを利用した
2次元配列の宣言と初期化
マクロを利用した二次元配列宣言例
マクロ名
マクロの
値
#define
GYOU
3
#define
RETSU 5
int data[GYOU][RETSU] = {{1,2,3,4,5},
{2,3,4,5,6},
{3,4,5,6,7}};
行サイズを省略可能
(初期化データ
の個数で判断)
Prog-0 2013 lec12-3
列優先で
初期化
Copyright (C) 1999 – 2013 by Programming-0 Group
2次元配列要素の入出力
#include <stdio.h>
#define
GYOU
3
#define
RETSU 5
main(){
int data[GYOU][RETSU], i, j;
for(i = 0; i < GYOU; i++){
for(j = 0; j < RETSU; j++){
scanf("%d", &data[i][j]);
}
}
for(i = 0; i < GYOU; i++){
for(j = 0; j < RETSU; j++){
printf("%d ", data[i][j]);
}
printf("\n");
}
}
Prog-0 2013 lec12-4
std1dc1{s1000000}1: ./a.out
0 1 2 3 4 1 2 3 4 5 2 3 4 5 6
0 1 2 3 4
1 2 3 4 5
2 3 4 5 6
std1dc1{s1000000}2:
/home/course/prog0/public_html/2013/lec/source/lec12-1.c
Copyright (C) 1999 – 2013 by Programming-0 Group
標準入出力
scanf(),printf()は、それぞれ標準入力ファイル(キーボード)、
標準出力ファイル(ディスプレイ)への出力に対応
標
準
入
力
フ
ァ
イ
ル
Prog-0 2013 Lec12-5
C
プ
ロ
グ
ラ
ム
フ標
ァ準
イ出
ル力
標
準
フエ
ァラ
イー
ル
出
力
lec12-6
参照
Copyright (C) 1999 – 2013 by Programming-0 Group
標準エラー出力(p.47,349)
標準出力とは 別に 出力したい
1
出力をリダイレクトやパイプする場合に、エラーメッセージを標準出力
に出力していると、エラーメッセージも画面に表示されず、リダイレクト
やパイプの対象になってしまう。
このためエラー出力用の標準ファイルを別に設け(標準エラー出力と
呼ぶ)、リダイレクト・パイプの場合も画面にエラーメッセージを表示す
るようにしている。標準エラー出力への出力は以下のようにして行う
fprintf(stderr,"書式",変数リスト);
例:fprintf(stderr,"入力した値がおかしい\n");
Prog-0 2013 Lec12-6
Copyright (C) 1999 – 2013 by Programming-0 Group
プログラム内の標準入出力と標準エラー
(p.47)
#include <stdio.h>
main()
この中で
定義
{
scanf( … );
標準入力から入力
…
printf( … );
標準出力ヘ出力
…
fprintf(stderr ,…);
2
標準エラーヘ出力
}
Prog-0 2013 Lec12-7
Copyright (C) 1999 – 2013 by Programming-0 Group
標準入出力とリダイレクション
(p.365)
リダイレクションとは
標準入出力ファイルの 切り替え
をプログラムの外で行う機能
3
リダイレクションの例
キーボード入力 → テキストファイル入力に変更
(予めデータを格納しておいたテキストファイルを入力として使用する)
ディスプレイ表示 → テキストファイル出力に変更
(出力結果をテキストファイルに保存して後からでも見れるようにする)
Prog-0 2013 Lec12-8
Copyright (C) 1999 – 2013 by Programming-0 Group
標準入出力のリダイレクションの書式
実行ファイル名 < 入力ファイル名
例: プログラム ./a.out の標準入力を test.dat から読み込む
./a.out
<
test.dat
実行ファイル名 > 出力ファイル名
実行ファイル名 >> 出力ファイル名
4
例: ./a.out の標準出力を test.dat に書き込む
./a.out > test.dat
./a.out >> test.dat
Prog-0 2013 Lec12-9
先頭から
書込(上書き)
末尾に
追記書き
Copyright (C) 1999 – 2013 by Programming-0 Group
リダイレクションの例(1)
例1. ファイル一覧をファイルlist.txtに書込む
ls > list.txt
例2. list.txtの行数、単語数、文字数を表示する
wc < list.txt
例3. list.txtから 文字列「Aizu」を含む行 だけ
file.txtに書き込む
5
grep Aizu < list.txt > file.txt
Prog-0 2013 Lec12-10
入力ファイルCopyright (C) 1999
出力ファイル
– 2013 by Programming-0 Group
リダイレクションの例(2)
リダイレクションでファイル中のデータの平均を計算する
#include <stdio.h>
#include <stdlib.h>
std1dc1{s1000000}1: ./a.out
Control+dは入力の
1 2 3 4 5
終わりを示す
2 3 4 5 6
Control+d(コントロールキーとDキーを同時に押す)
Average = 3.500000
std1dc1{s1000000}2: ./a.out < lec12-2.data
Average = 3.500000
std1dc1{s1000000}3: cat lec12-2.data
1 2 3 4 5
2 3 4 5 6
std1dc1{s1000000}4:
main()
{
int i, data, result, sum = 0;
for(i = 0;; i++){
result = scanf("%d",&data);
if (result == EOF) break;
if (result != 1) exit(1);
sum += data;
}
printf("Average = %f\n",(double)sum/i);
}
/home/course/prog0/public_html/2013/lec/source/lec12-2.c
Prog-0 2013 Lec12-11
Copyright (C) 1999 – 2013 by Programming-0 Group
標準入出力とパイプ(p.21)
パイプとは
二つのプログラムの標準出力と標準入力を 結合
する機能。コマンド間に「|」を挟む
パイプの例
cat lec12-2.c
|
grep result
6
lec12-2.c中の単語
「result」がある行を
全て表示する
catの出力が、grepの入力となる
パイプ
catコマンド
ファイル
出力処理
Prog-0 2013 Lec12-12
grepコマンド
標準
出力
標準
入力
ファイル
入力処理
Copyright (C) 1999 – 2013 by Programming-0 Group
パイプの例
例1. ディレクトリ内のファイルをファイル容量の大きい順に表示
ls -l | sort -k 4,4 -nr
例2. そのディレクトリ内のファイルの数を数える
ls | wc -l
例3. result.txt(IDと点数のペアデータ)
10人表示する
から 得点の高い順に
7
cat result.txt | sort -k 2,2 -nr | head -10
Prog-0 2013 Lec12-13
Copyright (C) 1999 – 2013 by Programming-0 Group
2次元配列の応用
(簡単な画像処理)
画像を標準入力から入力
入力された画像を処理(例えば左に90度回転)
画像を標準出力に出力
エラー情報は 標準エラー出力 に表示
8
標準
入力
標準
出力
画
像
処
理
標準エラー出力
エラー
Prog-0 2013 Lec12-14
Copyright (C) 1999 – 2013 by Programming-0 Group
簡単な画像形式
Plain PBM(Portable Bitmap)形式
原点
横(x)軸
縦
(y)
軸
白黒2値画像
Plain PBM形式は画像を保存する形式で、display
コマンド等で画像として表示できますが、テキスト
ファイルなので、0/1のデータをそのままテキスト
として目で見ることも出来ます
必ず「P1」(magic number)
横x×縦yドットの白
黒2値画像をデジタ
ルデータとして表現
1:黒
0:白
(左上から横に)
今回のプログラムでは、データ部分を
Prog-0 2013 Lec12-15
2次元配列に格納する。
画像の大きさ:横xドット、縦yドット
P1
(この場合は横320×縦160)
320 160
0 0 0 0 1 0 1 0 ..... 1 1 0 0 0 1 0
0 1 1 0 1 1 0 1 ..... 0 0 1 0 1 0 1
..... PBM形式白黒2値画像データ(テキストファイル)
.....
0 1 1 0 1 1 0 1 ..... 0 0 1 0 1 0 1
0 0 0 0 1 0 1 0 ..... 1 1 0 0 0 1 0
0 1 1 0 1 1 0 1 ..... 0 0 1 0 1 0 1
y列
1行のデータ数はx個
Copyright (C) 1999 – 2013 by Programming-0 Group
データ入力部分(共通部分)
#include <stdio.h>
#include <stdlib.h>
#define MAX_X 800
#define MAX_Y 800
#define BLACK 1
#define WHITE 0
取り扱える
画像の最大
の大きさ
(縦、横)
画素データ
読み込み
最初に「P1」と書いていないものは、デー
タ形式が違う
この部分はプログラミング入門では扱わ
ない文字型を使用しているので、今のとこ
ろは呪文だと思っておいてください
(詳しくはp.67,p.139参照)
画像用二次元配列
main(){
int img_data[MAX_Y][MAX_X];
int i, j, x_size, y_size;
if (getchar() != 'P' || getchar() != '1'){
fprintf(stderr, "データの形式が違います\n");
exit(1);
x,yそれぞれの画素数を得る
}
scanf("%d", &x_size);
画素数が多すぎる場合
scanf("%d", &y_size);
if (x_size > MAX_X || y_size > MAX_Y){
エラーメッセージは全て標
fprintf(stderr, "データが大きすぎます\n");
準エラー出力へ
exit(2);
}
scanf入力データがおかしいか
for (i = 0; i < y_size; i++){
個数より早くEOFになった場合
for (j = 0; j < x_size; j++){
if(scanf("%d",&img_data[i][j]) != 1){
fprintf(stderr, "データ入力に異常があります\n");
データが白黒ではない場合
exit(3);
}
if(img_data[i][j] != WHITE && img_data[i][j] != BLACK){
fprintf(stderr, "データが異常でした\n");
exit(4);
}
各エラーコードに対応したサンプルデータが
}
/home/course/prog0/public_html/2013/lec/source/lec12-err{1,2,3a,3b,4}.pbm にあ
}
るので試してみるとよい
Prog-0 2013 Lec12-16
Copyright (C) 1999 – 2013 by Programming-0 Group
左90度回転出力
最初にP1と画素
数を出力
データ
出力
printf("P1\n");
縦横反転するので
printf("%d %d\n", y_size, x_size);
y,xの順となる
for (i = 0; i < x_size; i++){
for (j = 0; j < y_size; j++){
printf("%d ",img_data[j][x_size-1-i]);
}
n個
printf("\n");
1行分終了で改行
(y_size)
左90度回転
}
m個 (x_size)
左90度
[0][0] [0][1]
[0][m-2] [0][m-1]
[1][0] [1][1]
[2][0]
[3][0]
[1][m-1]
[n-1][0] [n-1][1]
[n-1][m-2][n-1][m-1]
[2][m-1]
[3][m-1]
n個 (y_size)
m個
[0][m-1] [1][m-1]
[n-2][m-1][n-1][m-1]
[0][m-2] [1][m-2]
[n-1][m-2]
[0][m-3]
[n-1][m-3]
[0][m-4]
[n-1][m-4]
(x_size)
/home/course/prog0/public_html/2013/lec/source/lec12-rl.c
Prog-0 2013 Lec12-17
[0][0] [1][0]
[n-2][0] [n-1][0]
Copyright (C) 1999 – 2013 by Programming-0 Group
実行結果
元データの表示
実行ファイルにrotlと
std1dc1{s1000000}1: display lec12-1.pbm &
言う名前を付ける
std1dc1{s1000000}2: gcc lec12-rl.c -o rotl
std1dc1{s1000000}3: ./rotl < lec12-1.pbm | display &
std1dc1{s1000000}4:
元画像
Prog-0 2013 Lec12-18
元画像ファイルを
リダイレクトで入力
結果をdisplay コマンド
にパイプ
Copyright (C) 1999 – 2013 by Programming-0 Group
白黒反転プログラム
最初にP1とx,yの画
素数を出力
データ
出力
printf("P1\n");
printf("%d %d\n", x_size, y_size);
for (i = 0; i < y_size; i++){
for (j = 0; j < x_size; j++){
if(img_data[i][j] == BLACK) printf("%d ",WHITE);
else printf("%d ",BLACK);
}
白なら黒を出力
printf("\n");
}
黒なら白を出力
/home/course/prog0/public_html/2013/lec/source/lec12-iv.c
その他以下のプログラムソースがある(注:演習問題の関係で、公開していないものがあります)
右90度回転
左右反転
上下反転
辺縁検出
Prog-0 2013 Lec12-19
/home/course/prog0/public_html/2013/lec/source/lec12-rr.c
/home/course/prog0/public_html/2013/lec/source/lec12-lr.c
/home/course/prog0/public_html/2013/lec/source/lec12-ud.c
/home/course/prog0/public_html/2013/lec/source/lec12-eg.c
Copyright (C) 1999 – 2013 by Programming-0 Group
実行結果
左90度回転の後
白黒反転
std1dc1{s1000000}1: gcc lec12-iv.c -o invrt
std1dc1{s1000000}2: ./rotl < lec12-1.pbm | ./invrt | display &
std1dc1{s1000000}3:
ファイルからリダイレクト
元画像
Prog-0 2013 Lec12-20
このようにパ
イプで処理を
どんどん繋
いで行く事が
出来る
Copyright (C) 1999 – 2013 by Programming-0 Group
ノイズ除去
意味のある画像だと、ぽつんと点があることはあまりない
そこで、ある点を見た時
自分が黒点で、両隣の点が白い場合
自分が白点で、両隣の点が黒い場合
は「ノイズ=雑音(この場合は画像中のゴミ)」と認識して
両隣の色で置き換えることにする。
つまり:
例えば以下のような感じで画像が改善される
Prog-0 2013 Lec12-21
Copyright (C) 1999 – 2013 by Programming-0 Group
ノイズ除去プログラム
printf("P1\n");
printf("%d %d\n", x_size, y_size);
for (i = 0; i < y_size; i++){
行の先頭は隣がないのでそのまま出力
printf("%d ",img_data[i][0]);
横方向の両端を除いた点に
for (j = 1; j < x_size-1; j++){
関して処理を行う
if(img_data[i][j-1] == WHITE &&
img_data[i][j]
== BLACK &&
img_data[i][j+1] == WHITE){
白黒白なら白を出力
printf("%d ",WHITE);
}
else if(img_data[i][j-1] == BLACK &&
img_data[i][j]
== WHITE &&
img_data[i][j+1] == BLACK){
printf("%d ",BLACK);
黒白黒なら黒を出力
}
else printf("%d ",img_data[i][j]);
2つのケース以外の場合はそのまま出力
}
行の最後は隣がないのでそのまま出力
printf("%d ",img_data[i][x_size-1]);
printf("\n");
}
/home/course/prog0/public_html/2013/lec/source/lec12-nc.c
Prog-0 2013 Lec12-22
Copyright (C) 1999 – 2013 by Programming-0 Group
実行結果
結果を display コマンドに
パイプ
std1dc1{s1000000}1: gcc lec12-nc.c -o hncut
std1dc1{s1000000}2: ./hncut < lec12-2.pbm | display &
std1dc1{s1000000}3:
ファイルからリダイレクト
元画像
細かい点状のノイズを加えた
Prog-0 2013 Lec12-23
ノイズは減ったが一部細い線が欠落した
Copyright (C) 1999 – 2013 by Programming-0 Group
パイプとリダイレクションのまとめ
パイプのまとめ
標準入力の切り替え → コマンドの前に「|」 例:ls | wc -l
標準出力の切り替え → コマンドの後に「|」
例:cat hoge.txt | head
リダイレクションまとめ
標準入力の切り替え → <
例:./a.out < in.txt
標準出力の切り替え(ファイル書き換え) → >
標準出力の切り替え(ファイルに追加)
Prog-0 2013 Lec12-24
例:ls > out.txt
→ >> 例:ls >> out.txt
Copyright (C) 1999 – 2013 by Programming-0 Group