画像の空間フィルタリングを題材として,汎用的な関数を作成して利 用するための C プログラミング技術を習得する。
プログラム中でディジタル画像を取り扱うには二次元配列を用いればよい。 C では, 例えば
int img[64][128];として二次元配列を宣言することにより, 縦64個,横128個の画素を持つ 画像を配列 img に格納できるようになる。 画素
なお,二次元配列内の画像を表示するには,濃度に対応した明るさの光を画面に 点灯させる必要があるが,ここでは簡単化のために,濃度を数値として画面に出 力することによって,画像を表示したこととみなす。
与えられた画像(原画像)に対してフィルタ処理を行い,原画像とは異なる画像 を得るための処理を,画像の空間フィルタリングと呼ぶ。以降,空間フィルタリ ングの対象となる画像を入力画像と呼び,フィルタリング後の画像を出力画像と 呼ぶ。
この積和計算を,画像の端点を除くすべての画素 に対して行うことで,
画像の空間フィルタリングが実行できる2。
なお,ラプラシアン・フィルタによる空間フィルタリングは,入力画像の画素
を中心として,縦と横の 4 方向との差分をとるものであり,画像内の
図形のエッジ(明るさが急激に変化する場所)等を検出して出力するために用い
ることができる。
そのために,まず図 3 の内容をファイル imgfilter.h に保存 せよ。次に,図 4の内容を imgfilter.c に保存せよ。 imgfilter.h と imgfilter.c は同じディレクトリに置くこと。 さらに,図 4において空白となっている,関数 productSum の 定義部を作成し,imgfilter.c を完成させよ。
関数 productSum は,積和計算の対象となる入力画像全体(2次元配列)を第 1
引数を通して,また空間フィルタの係数全部を第4引数を通して受け取ることと
する3。productSum 内では,入
力画像の第 画素を中心とする 3
3 領域と空間フィルタとの積
和を求めて,戻り値として返す。
と
は,各々,第 2 および第 3 引数
を通じて渡される。
図 4には,関数 productSum の動作確認のために, productSum を呼び出して利用する関数 main の定義も含まれている。関数 main は,二次元配列 img に格納された画像と lap に格納された空間フィ ルタとの積和を,関数 productSum の呼び出しにより求めて表示する。
imgfilter.c をプログラムとして実行するには,
cc -DTEST imgfilter.cとして,コンパイルする必要がある。プログラムを実行し,結果が正しいこ とを確認せよ。
#include <file.h>または
#include "file.h"であり,ヘッダファイル file.h の内容を #include 命令の箇所に「取り込む」。プリプロセッサは,コンパイル の最初のフェーズで行われる処理を実行するものであり, #include 処理もコンパイルの最初の段階で行われる。ファ イル名を <file.h> で指定した場合には,処 理系によって定められた場所から file.h を探すのに 対し,"file.h" では,この命令が記述されたソースファ イルと同一のディレクトリから file.h の探索が始ま るので,自作のヘッダファイルを include するには後者を使 う。
ヘッダファイルは,複数のソースファイルで共通に利用する 関数のプロトタイプ宣言や記号定数の定義を記述するために 利用する。
一方,配列を受け取る関数の実引数(関数呼び出しにおける 引数)では,配列の次元数に拘らず配列名のみ4を記述する。
#ifdef
name
である。この命令は,name が定義されている(ソース
ファイル中に,既に #define
name の記述があ
る)ならば,対応する #endif
までの間のコードを有
効にする。逆に name が定義されていなければ,コン
パイラは #endif
までのコードを無視する5。
ソースファイル中に #define
name と書いて
name を定義するのと同じ効果は,コンパイル時にオプ
ション -Dname を付加しても得られる。問題で
は,imgfilter.c をコンパイルして,一つのプログラムとし
て実行する必要があるので,関数 main の定義を有効にしな
ければならない。オプション -DTEST を付けてコンパ
イルするのは,そのためである。
imgfilter.c において,関数 main の定義をコンパイルオプ ション -D が無い場合に無効としているのは,後の問題にお いて,imgfilter.c 中の productSum を別のソースファイル から呼び出すためである。次の問題では,一つのプログラム を作成するために,複数のソースファイルにプログラムを分 割して記述し,これらを結合して実行ファイルを作成する (分割コンパイル)。その場合,同じ名前の関数(今 の場合は main)を,複数のソースファイルで定義することは 許されない6。 imgfilter.c 中の関数 productSum は,次の問題のためのプ ログラム用「部品」として作成しているのであり,同じファ イル内の main は,あくまで productSum のテスト用である。
プログラム内で入力画像を生成せずに,既存のデータ画像を標準入 力から読み取ることとする。
読み取りに scanf を使う場合, 区切り文字を含まない数字の並びを正しく読み取るには, 第一引数で数字の桁数を明示する必要がある点に注意。
imgfilter.c で定義した積和関数 productSum を用いる。フィルタ リング後の画像は,入力画像とは別の配列に格納する。
ラプラシアンフィルタリングによって得られた画像からエッジ部分 のみを抽出するために,正の濃度を持つ画素を1 とし,それ以外の 画素では濃度を0とする後処理を行うこととする。
標準出力への出力とする。
プログラム作成においては,上記の各処理部をプログラム中で分離して記述 することにより,プログラムの構造を明確にすることが重要である。
ソースファイルは imgfilter.c とは別に作成する。そのソースファイルから, imgfilter.c で定義した関数 productSum を呼び出して使う。 したがって,ソースファイルに productSum の定義を書かないこと。 プログラム作成に際しては,imgfilter.c 内の関数 main も参考にせよ。
一般に,関数を呼び出す(使う)ためには,その関数のプロトタイプ宣言が必要
であり,この問題の場合にはヘッダファイルをインクルードする必要がある。ま
た,記号定数は,それを定義 (#define
) したファイル内の,定義箇所以
降でのみ有効であるので,そのためにもヘッダファイルのインクルードが必要で
ある。
複数のファイルに分割されたソースファイルから,実行可能ファイルを作成する ためにはコンパイラのコマンドラインの引数にソースファイルをすべて指定する。 例えば
cc laplacian.c imgfilter.c等とする。
画像の入出力に標準入出力を使うプログラムとしているので,パイプを使ったプ ログラムの実行が便利である。