プログラミング入門 第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
© Copyright 2024 ExpyDoc