• 検索結果がありません。

プログラミング演習3 - Cプログラミング -

N/A
N/A
Protected

Academic year: 2021

シェア "プログラミング演習3 - Cプログラミング -"

Copied!
32
0
0

読み込み中.... (全文を見る)

全文

(1)

プログラミング演習3

集中講義版

-2日目資料&課題

花泉 弘

(2)

この回の目標

1.画像ファイルの構造を知る

2.画像ファイル(バイナリファイル)を読み込む

・読み込みには1日目に作成したreadcharline( ) を使用します

3.画像を作成して出力する

4.読み込んだ画像データを処理できる

・画像上の座標(ラインとカラム)で画素を指定して濃度値を得る

・指定された領域の画素濃度の平均値をR,G,Bごとに求める

(3)

1.画像ファイルの中身を知る その1

・・・ ・・・ ・・・ ・・・ ・・・ 白黒画像のイメージ: 画素が並んでいる それぞれの画素は濃淡値を持つ 0~255 (データは1byte/画素) 幅(width) 高さ (he ight ) カラー画像の場合は、三原色の 組合せで色を表すため、白黒画像の 3枚分の容量が必要になる。 カラー画像では、白黒画像の画素1個に対して、対応する RGB各成分合わせて3つ分の数値が連続して記録されている (RGBの順)。これらの数値を画素濃度と呼びます。それぞれ 赤緑青の波長帯(spectral band)における観測値なので、これ らの画像を3バンドデータと呼んだり、バンド数は3である、な どと言います。センサによっては可視から近赤外までN個の波 長帯で観測するものもあり、これらはNバンドデータと呼ばれ ます。 画像の情報 ・ヘッダー情報:画像のサイズ(幅と高さ)や1画素のビット数、バンド数(カラー 3、白黒1) ・データ本体:幅×高さ×バンド数分のバイナリデータ(テキストではない) これらの情報がファイルに記録されている

(4)

2.画像ファイルの構造を知る その2

pnm ファイルとよばれる種類の画像を例にとって、ファイルの構造を見ていきます。 pnm ファイルには、pgm ファイル、ppm ファイル、pbm ファイルがあります。 ・pgm ファイル:白黒画像で1画素を8ビット(0~255、0x00 ~ 0xff)で表す。拡張子は .pgm ・ppm ファイル:カラー画像で3バンド、8ビット/画素。拡張子は .ppm ・pbm ファイル:2値画像。1ビット/画素。拡張子は .pbm 構造:ファイルの初めにヘッダー部(テキストデータ)があり、 続いて画像データ(バイナリデータ)が順に詰まっている ヘッダー部 画像データ (1024 x 768 x 3 bytes) P6↓ 1024 768↓ 255↓ マジックナンバー 6:ppm, 5:pgm, 4:pbm width height 画素値の最大値 この例では 1 byte/pixel

(5)

3.画像ファイルの構造を知る その3

pmmファイルは、pnmファイル形式を拡張して、3つを超えた数の観測波長帯(バンド)の画像を 同じ形式で取り扱えるようにしたもので、 マジックナンバーは0、次の行に画像の幅、高さ、バンド数が入り、最後に濃度の最大値が 書かれています。 ヘッダー部 画像データ (1024 x 768 x 4 bytes) P0↓ 1024 768 4↓ 255↓

width height band

画素値の最大値

(6)

例題2-1 画像ファイル(pnm)の構造を確認しなさい

①では、確認してみましょう。ブラウザを使って、好きな画像をダウンロードしてください。 以前ダウンロードしたものがあれば、それで構いません。 ②ダウンロードしたものが .jpg ファイルであれば、pnm ファイルに変換します。 ③画像の入っているディレクトリに移動 ④画像を確認。 .jpg ファイルです。 ⑤変換には convert を用います。 ⑥確認します。 .ppm と .pgm ファイルができて いることがわかります。 ⑦画像も見てみます。 *pnm ファイルとは ppm, pgm, pbm ファイルの総称です 拡張子で区別

(7)

例題2-1 つづき

① less コマンドを使います。 ② 「y」 を入力します。 ③ヘッダー部が確認できました。 マジックナンバーの違いに注意 ④ 「q」 を入力して終了します。

(8)

4.画像ファイル(バイナリファイル)を読み込む

画像ファイルのオープンには fopen()を用います。ここでは、バイナリファイルを 扱うので、fopen(file.ppm, “rb”) のように、「b」を追加して指定します。 テキストファイルは、「r」だったわけですが、違いがわかりますか? r:入力時は ‘¥r’,’¥n’ →’¥n’ へ変換、(ファイルへの)出力時には’¥n’ → ‘¥r’,’¥n’への変換 rb:何もしない の違いがあります。 ヘッダー部(テキスト) 画像データ(バイナリ) (1024×768×3 bytes) pnm/pmm形式の画像ファイルは左図の ように、最初にテキストデータがあり、その 後ろにバイナリデータがあります。 方針: テキストデータに関しては、行末の ’¥n’ に着目し、 その内容から読み込むべき後半の画像データの 個数を知り、その個数分バッファに蓄える。 注意: 画素データはunsigned char 型の配列に代入します。 (関数等の宣言もchar 型からunsigned char 型へ変更)

(9)

チェック用の main 文 ex202/main_charline.c

例題2-2 画像データのヘッダー部を読み込む

あとで画像を読むので配列は大きめに bを追加して ここは3限定で pmm と ppmの違いがわかりますか? 作成した readcharline() を使って、pnm/pmm ファイルのヘッダー部を読んでみましょう。 指定したバイト数を読み込むだけでなく、1ライン分のデータも読めることがわかります。

(10)

さて、ここまでの処理で pnm/pmm 画像のヘッダー部を文字列と

して読み込むことができました。ただ、文字列のままでは使えま

せんのでそれらを数値として認識することが必要です。

Cでは keyboard からの入力用の関数として scanf(“%d”, &k); を

用意していますが、それと同様にバッファからの読み取りには、

sscanf(buf, “%d”, &k); が使えます。

pnm/pmm 画像をから3回続けて1行分読み取ることを考えます。

buf には文字列としてそれぞれの回の読み込みに対して例えば

P6

1024 768

255

が入るので、1回目に1行分読み取ったところで、

buf[0] == ‘P’ が真なら pnm/pmm であることがわかり、さらに

buf[1] == ‘6’ が真なら ppm 画像で band = 3 であることがわかり、

buf[1] == ‘5’ が真なら pgm 画像で band = 1 とわかります。また、

buf[1] == ‘0’ が真なら pmm 画像なので、未決ということで、

仮にband = 0 としておきます。

(11)

2回目に1行分読み取ったところで、

band > 0 が真ならすでに pnm 画像であることがわかっているので

sscanf(buf, “%d%d”, &width, &height);

とすることで、width = 1024, height = 768 といった具合にint 型の

変数width と height に数値を格納することができます。

同様に、band == 0 が真であれば

sscanf(buf, “%d%d%d”, &width, &height, &band);

とすることで、width, height に加えて band にも正しい値が入ります。

3つの値がそろったら、続けて

3回目に1行分読み取ります。その時の値が255であれば、1画素

1バイトでこの画像が構成されていることがわかります。なお、この

値は65535 のこともあり、この場合は、1画素2バイトで構成されて

いることになります。当然、画像を読み込むバッファもunsigned char

ではなくて、unsigned short int になります。

話を戻します。3つの値がそろったら、読み込むべき画素数は

width * height * band のように求めることができます。この値を設

定すれば、buf に画像データを読み込むことができます。

(12)

例題2-3 画像データを読み込む

結局のところ、pnm/pmm ファイルを読み込むには、テキスト部を読み込んで ・ファイルの種類(pgm/ppm/pmm)を判別し(pgm/ppmの場合はバンド数がわかる) ・画像の幅と高さ(とpmmの場合は、バンド数)を知る。 ・濃度の最大値も知る 求めた幅、高さ、バンド数の情報から ・連続して必要なバイト数分だけデータを読み込む という流れで画像を読み込むことができる。 *imagefile : 画像ファイルの名前が格納された配列 *img : 画像を格納するバッファ(大きめに確保しておく; これまでは *buf であった) *width, *height, *band : 画像の幅、高さ、バンド数

戻り値:正常読み取りはEXIT_SUCCESS、異常はEXIT_FAILURE(ファイルが存在しない、 pnm/pmmでない、読み込みエラー(個数分だけデータがない)) int readimage(char *imagefile, unsigned char *img, int *width, int *height, int *band)

を作成しなさい。仕様は以下の通り。main側からは

k = readimage(filename, img, &width, &height, &band); のように用いる。

以下、演習題ごとにディレクトリを区別して(例えば、ex202 など)に作成すること。

(13)

例題2-3(続き) 画像ファイルの読み込み

ex203/readimage.c 負数を与えて1行目を読む 正数を与えて画素数分読む 2行目を読む 4行目を読む ここは、unsigned char で

(14)

例題2-3(続き) 動作確認(画像を表示させてみる)

使い方

$ imgout mountain.ppm | xv - & で画像が表示されたらOK.

(15)

いかがですか? 無事画像は出力されましたか?

ここまでで、一応は画像を読み込むことができるようになりました。

例題2-3 では、画像の出力の仕方についても少し書いてあったの

ですが、後ほど、詳しく見ていきます。

まず、ちょっと不便なところが残っているreadimage( )をもう少し便

利にすることを考えます。

不便な点とは、readimage( ) を呼んで width, height, band を知る

前に、画像を読み込むための配列の大きさを決定しなければなら

ない点です。width, height, band を知ってから配列を設定すること

ができれば、すごくすっきりするはずです。

(16)

読み込んだ画素データを格納する配列を動的に作成するには、malloc( )や calloc( ) を用い ます。画素濃度を格納する配列(ポインタ)を img とすると

img = calloc(割り当てる要素数, 1要素あたりのバイト数) か、もしくは img = malloc(必要バイト数)

のように用います。これらの例では要素数=width * height * band で、1要素あたりの

バイト数は sizeof(unsigned char) で求めます。malloc( ) の必要バイト数はそれらの積になり ます。どちらも、成功すると割り当てられたメモリ領域の先頭アドレスが img に格納され、失 敗すると NULL が格納されます。calloc( ) の場合には、メモリは全ビット0にクリアされます。 malloc( ) の場合は、何もしないので以前の内容が残ったままになっています。

calloc( ) や malloc( ) で割り当てられたメモリ領域が不要になった場合は、free( ) で開放

します。プログラムが終了すると自動的にfree( ) が行われるので、特に意識する必要はあり ません。以下のように用います。

If((img = calloc(width * height * band, sizeof(unsigned char))) == NULL){//メモリの割り当て

fprintf(stderr, “memory allocation error¥n”);

return (NULL); //これを含む関数の戻り値がunsigned char型のポインタなのでNULLを返す

}

何かの処理

free(img); //メモリの解放(通常は意識しなくてもよい)

5.動的なメモリ領域の割り当て

(17)

例題2-4 ファイル名を与えてポインタで受ける

演習2-3 では、画像の大きさを想定して、main 側で大き目の配列を用意していましたが、 ここでは、width, height, band が得られた後に、動的に配列用のメモリを確保します。 さっそく、calloc( ) を使ってみましょう。せっかくなので、readimage( ) を改造していきます。

int readimage(char *imagefile, unsigned char *img, int *width, int *height, int *band) を作成。main側からは

k = readimage(filename, img, &width, &height, &band); のように用いる。

unsigned char *readimage2(char *imagefile, int *width, int *height, int *band) を作成。main側からは

unsigned char *img; の宣言をした後、

img = readimage2(filename, &width, &height, &band); のように用いる。

ファイル名を与えて、幅、高さ、バンド数と共に、画像本体を受け取る形になり、 非常にすっきりする

改造前(例題2-3)

(18)

例題2-4 (続き)画像ファイルの読み込み (すっきり版)

ex204/readimage2.c

j 個の unsigned char 領域 (合計 j バイト)を確保

(19)

例題2-4 (続き) 動作確認(画像を表示させてみる)

使い方

$ imgout2 mountain.ppm | xv - & で画像が表示されたらOK.

(20)

例題2-5 さらに一般化する(#で始まる行の読み飛ばし)

pnm/pmm ファイルには、場合によっては、下の例のようにコメントが付加されるので、 コメントをスキップするように改造する(ex205 に readimage3()として作成する)。

(21)

例題2-5 (続き) #行の読み飛ばし

ex205/readimage3.c

do while 文で、行頭が#の 場合に読み飛ばす

(22)

例題2-5 (続き)動作確認(画像を表示させてみる)

ex205/imgout3.c

使い方

$ imgout3 mountain.ppm | xv - & で画像が表示されたらOK.

(23)

6.ここまで作成してきた画像読み込み関数の簡単なまとめ

readimage( ): 関数にファイル名を渡す。関数側でファイルをオープンし、読み取った画像のサイズ情報 に基づいて、main 側で定義した配列に画素濃度を格納して戻る。 readimage2( ) 関数にファイル名を渡す。関数側でファイルをオープンし、読み取った画像のサイズ情報 に基づいて、関数側で画素濃度を格納する配列にメモリを動的に割り当て、その配列の ポインタを戻す。 readimage3( ) 関数にファイル名を渡し、関数側でファイルをオープンする。画像のサイズ情報を読み 取る際に、画像ヘッダー部の#を読み飛ばす。関数側で画素濃度を格納する配列に 動的にメモリを割り当てその配列のポインタを戻すところは readimage2( ) と同じ。 readimage2( ) 以降では、画素濃度を格納する配列を動的に作成していますが、malloc( )や calloc( ) を用います。画素濃度を格納する配列(ポインタ)を img とすると

img = calloc(割り当てる要素数, 1要素あたりのバイト数) / img = malloc(必要バイト数) のように用います。これらの例では要素数=width * height * band で、1要素あたりの

バイト数は sizeof(unsigned char) で求めます。malloc( ) の必要バイト数はその積になります。 どちらも、成功すると割り当てられたメモリ領域の先頭アドレスが img に格納され、失敗する と NULL が格納されます。calloc( ) の場合に限り、メモリは全ビット0にクリアされます。

(24)

7.画像ファイルの出力

これまで、何気なく画像を出力してきましたが、読み込んだ画像ファイルや処理した結果を 画像として表示させたり、ファイルとして保存する方法として、以下の3つの手法を試してみ ます。 1.標準出力に書き出す(今まで使っていたもので、リダイレクトを使って、ファイルに保存し たり、パイプを使って表示ソフトへ流し込んだりできます)。 2.ファイルをオープンして書き出す 3.popen()を使って、表示ソフトへ直接送り込む 1.標準出力に書き出す ファイルが生成 リダイレクト パイプ いろいろに使えて便利 だが、ファイルは1個しか 出力できない。 ソースは次ページ imgout は次ページ参照

(25)

例題2-6 標準出力に書き出す

上で作ったものを 早速使っています この部分が出力部 メモリの開放を 忘れずに。 ただし、この例の ようにすぐに終了 するのであれば、 不要です。次の 2,3の例でも同様 です。 この内容をex206/imgout4.c に 保存し、$ gcccomp imgout4 ↓ 動作確認 4 4

(26)

例題2-7 ファイルをオープンして書き出す

ファイルをオープン ファイルクローズ printf() が fprintf(fp, ) になっただけ 動作確認 ex207/imgout5.c に保存

(27)

例題2-8 popen()を使って、表示ソフトへ直接送り込む

慣れないと感じがつかめ ないかもしれませんが、 大変便利なものです。 動作確認 コマンドをオープンする形です。 その他は、fopen()と変わりません。 ただし、画像が出たら処理はそこで ストップしています。xvを終了させ ないと、次に進みません。 ex208/imgout6.c に保存

(28)

8.画像データの処理

-画素データへのアクセスー

…..

unsigned char *img, *pa, *pb, *pc, *pd; int I, j, k, width, height, band; ………. img = readimage3(…. x y width * band heigh t (xs, ys) (xe, ye) ① ② ③ 左のような宣言と画像の読み込みを行った とすると、読み込まれ画像データは、左下の ようになっている。

(xs, ys) – (xe, ye) で決まる矩形領域内の 画素へのアクセスを考える。 ポインタ img は画像データの開始点を 保持しているので、別の変数を使用する。 3種類のポインタを使用する。 pa : 初期値が②ysで、ループが回るたびに 次の行④へ移動する(+= width * band)。 pb : ③(②+band * xs)から始まり、band 数ずつ 進む。③の次は⑤に移動する(+= band)。 pc : pb ③から始まり、band の数だけ 増分1で移動する③ の次は⑥(+= 1)。 pa = img + ys * width * band; // 初期値

pa += width * band; //増分 pb = pa + xs * band; // 初期値 pb += band; //増分 pc = pb; // 初期値 pc++; //増分 ⑤ ④ ⑥

(29)

例題2-9 画像ファイル入力し、ネガポジ変換した

画像を表示させなさい。

ある画素の濃度(ピクセルの持つ値)をd とするとき、ネガポジ変換はその画素 の濃度を255 – d とすることである。

画素値のアクセスは3重ループを用いる。

for( i = 0; i < height; i++){ //line 数に対するループ

for( j = 0; j < width; j++ ){ //column 数に対するループ for( k = 0; k < band; k++){ //band 数に対するループ

ここに処理の手続きを書く

} } }

(30)

例題2-9 (続き) stdout へ出力する方法で行います

ex209/nega.c

動作確認

画像全体なので

i = 0, j = 0 かつ i < height, j < width

(xs, ys) – (xe, ye) の矩形領域なら i = ys, j = xs かつ i <= ye, j <= xe

(31)

x y width * band heigh t (xs, ys) (xe, ye) ① ② ③ 3種類のポインタを使用する。 pa : 初期値が②ysで、ループが回るたびに 次の行④へ移動する(+= width * band)。 pb : ③(②+band * xs)から始まり、band 数ずつ 進む。③の次は⑤に移動する(+= band)。 pc : pb ③から始まり、band の数だけ 増分1で移動する③ の次は⑥(+= 1)。 pa = img + ys * width * band; // 初期値

pa += width * band; //増分 pb = pa + xs * band; // 初期値 pb += band; //増分 pc = pb; // 初期値 pc++; //増分 ⑤ ④ ⑥ この部分は、非常に重要なので、もう一度ポインターの設定法とプログラム上で それらがどのようにふるまうのかを確認してください。 i = ys i <= ye J = xs j <= xe 画像全体 矩形領域

(32)

ここまでで、画像の入出力とそれぞれの画素へのアクセス方法が

分かったと思います。それを前提として、以下の課題に取り組んで

ください。

2-1.一様な色の画像を生成し、表示させてください。

画像のサイズと色はコマンドラインから与えるものとしてください。

$ singlecolor width height red green blue

のような感じで使うことを想定しています。red, green, blueは

0~255の値をとります。

2-2.サイズは自由でよいので、画像の中に

・円(中心位置(xc, yc)を指定する)

・四角形(左上と右下の座標(xs, ys) – (xe, ye)を指定する)

・三角形(3つの頂点の座標(x1, y1), (x2,y2), (x3, y3)を指定する)

を何個か描いてください。色は指定するか、もしくはそこだけ

ネガにするのもありです。

参照

関連したドキュメント

approah, whih is based on a step by step onstrution of the walks [6, 5℄.. We repeat in Setion 3 the proof

&amp; Shipyarrd PFIs.. &amp;

54 Zero Emission Tokyo 2020 Update &amp; Report Zero Emission Tokyo 2020 Update &amp; Report 55

岣&amp;DUQLYDO+RUL]RQ岤.

[r]

Copyright(C) 2020 JETRO, Nagashima Ohno &amp; Tsunematsu All rights reserved... a)

2. 「STOP&amp;GO ボディ・シェイプ編」 3. 「STOP&amp;GO

Title of Change: D2PAK single reel packing box change(include dry packing)&amp;remove sponge pad fill for dry packing for D2PAK/DPAK &amp; SPM5 &amp; Remove 1ea bubble bag