C 言語による画像処理基本パッケージ使用マニュアル
森尾 吉成
目 次 はじめに ... ii 受講に必要な知識およびスキル... ii 準備するもの... ii 取り扱う画像ファイル ... ii RAWファイルを閲覧するソフト(付録している)... ii CCDカメラとデジタル画像 ... iii 色の表現(色の 3 原色) ... iv デジタル画像と量子化精度 ... iv モノクロ表現 ... iv 画像平面と画像座標xy ... iv 画素走査... v 画素配列とC言語による 2 次元配列表現 ... v 画素へのアクセス方法... v パッケージ構成... vi 1.サンプルプログラムソースファイルと実行ファイル... vi 2.サンプル画像ファイル ... vi 3.使用マニュアル ... vi 画像処理プログラミング ... 1 画像処理プログラムの基本形 ... 2 画像読み込みプログラミングの作成 ... 3 モノクロ画像へ変換する ... 4 モノクロ画像を保存する... 5 2 値化... 6 サンプルプログラム(sample.exe)を使って 2 値化処理... 7 サンプルプログラム(sample.exe)を使ってヒストグラムの計算... 8 サンプルプログラムを使ってRGB値からxy色度を計算 ... 9 フレーム間差分... 10 メニュー画面の作り方 ...11
はじめに
受講に必要な知識およびスキル
次の基本的な C 言語プログラミングを修得していること.
printf 文,scanf 文,for 文,switch 文,関数の宣言,配列,fopen や fclose 関数を用いたファイルの入出力, fgetc と fputc 関数,#include 文,#define 文
準備するもの
C 言語用コンパイラおよびプログラミングエディタを用意して,プログラミングできる環境を準備しておいてく ださい.著者は普段 Microsoft 社の VisualStudio.NET を使用しています.フリーで使用できるコンパイラには, BorlandC++5.5 や GCC などがあります.プログラミングエディタには,VisualWindows for BCC++(BCC コン パイラ含む)や phoebe,あるいはメモ帳も使用できます.VisualWindowsforBCC++は,コンパイラが付いてき ますので,インストールが簡単です.
VisualWindows for BCC++ http://phys.cool.ne.jp/physjpn/bccwin.htm Phoebe http://homepage3.nifty.com/aokura/
取り扱う画像ファイル
画像ファイルとして,RAW 形式を採用します.RAW ファイルでは各画素の RGB 値を圧縮しないで,RGB 順または BGR 順で 1 バイトずつ記録しています.C 言語を用いて RAW ファイルにアクセスする場合,1 バイ トずつデータを読み書きする fgetc()または fputc()関数を使う.
RAW ファイルの閲覧には,Paint Shop Pro や Photo Shop Elements などの有料ソフトはじめ,今回ご紹介す る無料ソフト ViX を利用します.ビットマップ画像や JPG 画像を取り扱う場合,有料ソフト等を使って,あらかじ め RAW 形式に変換します. RAW ファイルを閲覧するソフト(付録している) 統合画像ビュアー ViX2.21 (Windows95/98/Me/NT4.0/2000/XP 用,紹介・転載・再配布とも一切自由なフリーソフトウェアです) http://homepage1.nifty.com/k_okada/
RAW ファイルを閲覧する方法は,RAW ファイルと同名の FAL ファイル(NIFTY-Serve FPICS フォーラム謹 製のフォーマット)を用意します.例えば,RAW ファイルが image1.rgb であれば,FAL ファイルとして image1.fal をテキストエディターで作成し,同一フォルダに保存します. image1.fal の中身(タブ区切り) F_ALL(V1.00) 640 480 0 0 1.00 255 255 255 0 (解説)2 行目の 640,480 が画像の幅と高さになります.それ以外はこのままで変更する必要はありません. 仮に,640×512 の画像であれば,480 を 512 に変更するだけです.
CCD カメラとデジタル画像
CCD カメラには,下図に示すような光を電気信号に変換する(光電効果)受光素子を数百万個縦横に配列 したカメラヘッドが搭載されている.受光素子は,フォトダイオードと CCD(Charge Coupled Device:電荷結合 素子)から構成され,フォトダイオードが光を電荷に変換し,CCD が電荷を取り出して出力する役割を果たし ている.1 画素とは,1 つの受光部のことを指す.受光素子を総称して固体撮像素子とも呼ぶ.受光素子は, 光の明るさのみを電荷に変換するため,色を求めるためには,図に示すように各画素の上に色フィルタを付 けることにより RGB 情報を取得する.ただし,色フィルタを使用する方法は,1CCD カメラの場合であり, 3CCD カメラの場合は固体撮像素子が 3 板あり,RGB 情報はプリズム(屈折率の違い)を利用してそれぞれの CCD に分解する. (a)CCD カメラ (b)カメラヘッド (c)受光素子配列 CCD カメラの構造 水平転送CCD 垂直転送 CCD フォトダイオード CCD
緑
青
赤
青
緑
青
緑
青
青
青
青
青
青
青
青
青
緑
緑
緑
赤
赤
赤
赤
赤
1画素 (a)インターライントランスファー型 CCD (b)色フィルタ CCD の原理と色フィルタ色の表現(色の 3 原色) RGB とその補色(CMY) 青 シアン 緑 赤 マゼンタ 黄 白 黄 マゼンタ シアン 赤 緑 黒 青 加法混色 減法混色 混色 デジタル画像と量子化精度 RGB 各 256 階調で表現する.各色は 0-255 の値をとる. モノクロ表現 黒 → 白 (0,0,0),(1,1,1),(2,2,2),→(255,255,255) モノクロ画像と RGB 値 像平面と画像座標 xy 画 画像座標 xy
画素走査
ラスタ走査.左上から右下に向かう走査.
ラスタ走査
画素配列と C 言語による 2 次元配列表現
例えば,縦 480×横 640 画素の画像情報を格納する配列を宣言する.
unsigned char image[480][640];
(注意)unsigned char とは 0-255 までの値をとる配列変数であることを宣言しています.
2 次元配列と座標表現
画素へのアクセス方法
パッケージ構成
.サンプルプログラムソースファイルと実行ファイル 画像処理の基本機能を実現するソースファイルおよび実行可能ファイルを提供する. :sample.cpp T でコンパイル) 提供する機能: 1)メニュー画面の作成 2)RGB フルカラー画像ファイルの読み込み 3)画像ファイルの保存 4)モノクロ画像に変換 5)2 値化 6)エッジ検出 7)フレーム間差分法 8)ヒストグラム計算 9)xy 色度計算 2.サンプル画像ファイル 640×480 画素の大きさを持つ RGB フルカラー画像を 21 枚同封しています.画像中央を歩行する人間を, 約 130ms 毎に 20 枚連続撮影した f51.rgb から f70.rgb までの 20 枚と,背景が青色の手の画像 hand.rgb の 1 枚です. 3.使用マニュアル 本マニュアルを指す. 1 ソースファイル名 実行ファイル名:sample.exe (VisualStudio.NE画像処理プログラムの基本形 #include "stdio.h"
#include “stdlib.h”
age
#define IMG_YSIZE 480// the y-size of a age
#define HIGH 255 // max gray level
#define LOW 0 // minimum gray level // 画像処理用配列宣言
unsigned char r[IMG_YSIZE][IMG_XSIZE],g[IMG_YSIZE][IMG_XSIZE],b[IMG_YSIZE][IMG_XSIZE];
unsigned char Wrk[IMG_YSIZE][IMG_XSIZE], Wrk2[IMG_YSIZE][IMG_XSIZE], Wrk3[IMG_YSIZE][IMG_XSIZE];
void main(void) {
}
// 画像サイズ
#define IMG_XSIZE 640 //the x-size of an im n im #include 文 プログラムに必要なヘッダーファイルをインクルードしておく.数学関数が必要な場合 math.h をインクルード しておいてください. #define 文 画像サイズ IMG_XSIZE,IMG_YSIZE と,2 値画像処理で使用する白と黒の値 HIGH,LOW を定義してい る. 画像用配列の宣言 画像データは,1 バイト単位で情報を表現しますので,unsigned char 型を使って,0 から 255 までの値を格 納します. main 関数 void main(void)と引数も戻り値もしていませんが,必要に応じて,適宜追加修正を加えてください.
画像読み込みプログラミングの作成
る ReadRgbImage 関数は,fgetc()関数を使って RGB 値を 1 バイトずつ順番 読み込む.
id ReadRgbImage(char *ReadFilePath, unsigned char r[IMG_YSIZE][IMG_XSIZE], unsigned char g[IMG_YSIZE][IMG_XSIZE], signed char b[IMG_YSIZE][IMG_XSIZE]);
画像処理用配列宣言
igned char r[IMG_YSIZE][IMG_XSIZE],g[IMG_YSIZE][IMG_XSIZE],b[IMG_YSIZE][IMG_XSIZE];
signed char Wrk[IMG_YSIZE][IMG_XSIZE], Wrk2[IMG_YSIZE][IMG_XSIZE], Wrk3[IMG_YSIZE][IMG_XSIZE];
id main(void) r sFilePath[500]; file name ... "); FilePath); ReadRgbImage(sFilePath,r,g,b); //RGB 画像読み込み B カラー画像(RAW 形式)ファイルの読み込み関数
ReadRgbImage(char *ReadFilePath, unsigned char r[IMG_YSIZE][IMG_XSIZE], unsigned char g[IMG_YSIZE][IMG_XSIZE], gned char b[IMG_YSIZE][IMG_XSIZE])
FILE *fp_r;
(fp_r = fopen( ReadFilePath, "rb")) == NULL ){ printf("cannot %s open!", ReadFilePath); exit(-1); } r( j = 0; j < IMG_YSIZE; j++ ){ rw = fgetc(fp_r); gw = fgetc(fp_r); bw = fgetc(fp_r); r[j][i] = rw; g[j][i] = gw; b[j][i] = bw; } } fclose(fp_r); } RGB 画像ファイルへアクセスす に #include "stdio.h" #include “stdlib.h” // 画像サイズ
#define IMG_XSIZE 640 //the x-size of an image
#define IMG_YSIZE 480// the y-size of an image
#define HIGH 255 // max gray level
#define LOW 0 // minimum gray level //関数のプロトタイプ宣言 vo un // uns un vo { cha char sSaveFilePath[500]; printf("input scanf("%s", s } // RG void unsi { int i,j; unsigned char rw, gw, bw; if( fo
モノクロ画像へ変換する
NTSC の明度に変換する式を用いて RGB 画像をモノクロ画像に変換する. #include "stdio.
#include “stdlib.h” h"
//the x-size of an image // the y-size of an image
id ReadRgbImage(char *ReadFilePath, unsigned char r[IMG_YSIZE][IMG_XSIZE], unsigned char g[IMG_YSIZE][IMG_XSIZE], MG_XSIZE]); 画像処理用配列宣言 ][IMG_XSIZE],g[IMG_YSIZE][IMG_XSIZE],b[IMG_YSIZE][IMG_XSIZE]; _XSIZE]; id main(void) char sFilePath[500]; ]; "); g,b);//RGB 画像読み込み rk[IMG_YSIZE][IMG_XSIZE]) j++ ){ _XSIZE; i++ ){ // 画像サイズ #define IMG_XSIZE 640 ZE 480 #define IMG_YSI
#define HIGH 255 // max gray level
#define LOW 0 // minimum gray level 関数のプロトタイプ宣言
//
vo
unsigned char b[IMG_YSIZE][I
void GrayImage(unsigned char r[IMG_YSIZE][IMG_XSIZE], unsigned char g[IMG_YSIZE][IMG_XSIZE], unsigned char b[IMG_YSIZE][IMG_XSIZE], unsigned char wrk[IMG_YSIZE][IMG_XSIZE]);
//
unsigned char r[IMG_YSIZE
unsigned char Wrk[IMG_YSIZE][IMG_XSIZE], Wrk2[IMG_YSIZE][IMG_XSIZE], Wrk3[IMG_YSIZE][IMG
vo
{
char sSaveFilePath[500 printf("input file name ... scanf("%s", sFilePath); ReadRgbImage(sFilePath,r,
GrayImage(r,g,b,Wrk);//モノクロ画像に変換
}
//ReadRgbImage 関数は省略
void GrayImage(unsigned char r[IMG_YSIZE][IMG_XSIZE], unsigned char g[IMG_YSIZE][IMG_XSIZE], unsigned char MG_YSIZE][IMG_XSIZE], unsigned char w
b[I {
int i,j;
for( j = 0; j < IMG_YSIZE; for( i = 0; i < IMG
wrk[j][i] = (unsigned char)(0.30*r[j][i] + 0.59*g[j][i] + 0.11*b[j][i] ); }
} }
モノクロ画像を保存する
Wrk 配列に保存されているモノクロ画像をファイルに保存する. #include "stdio.h"
#include “stdlib.h”
// 画像サイズ
#define IMG_XSIZE 640 //the x-size of an image an image
efine HIGH 255 // max gray level gray level
SIZE],
id GrayImage(unsigned char r[IMG_YSIZE][IMG_XSIZE], unsigned char g[IMG_YSIZE][IMG_XSIZE], unsigned char IMG_YSIZE][IMG_XSIZE], unsigned char wrk[IMG_YSIZE][IMG_XSIZE]);
宣言
igned char r[IMG_YSIZE][IMG_XSIZE],g[IMG_YSIZE][IMG_XSIZE],b[IMG_YSIZE][IMG_XSIZE];
signed char SIZE], Wrk2[IMG_YSIZE][IMG_XSIZE], Wrk3[IMG_YSIZE][IMG_XSIZE];
id main(voi scanf("%s", sFilePath); ReadRgbImage(sFilePath,r,g,b);//RGB 画像読み込み );//モノクロ画像に変換し,Wrk 配列に保存 , sSaveFilePath); rk 画像の保存
aveFilePath, unsigned char a[IMG_YSIZE][IMG_XSIZE]) FILE *fp_w;
int i, j; unsigned char w;
if( (fp_w = fopen( SaveFilePath, "wb")) == NULL ){ printf("cannot file %s open", SaveFilePath); exit(-1);
}
for( j = 0; j < IMG_YSIZE; j++ ){
for( i = 0; i < IMG_XSIZE; i++ ){ w = a[j][i]; fputc(w, fp_w); } } fclose(fp_w); }
#define IMG_YSIZE 480// the y-size of
#d
#define LOW 0 // minimum //関数のプロトタイプ宣言
void ReadRgbImage(char *ReadFilePath, unsigned char r[IMG_YSIZE][IMG_XSIZE], unsigned char g[IMG_YSIZE][IMG_X unsigned char b[IMG_YSIZE][IMG_XSIZE]);
vo b[
void SaveWrkImage(char *SaveFilePath, unsigned char a[IMG_YSIZE][IMG_XSIZE]);
// 画像処理用配列 uns un Wrk[IMG_YSIZE][IMG_X vo d) { char sFilePath[500]; char sSaveFilePath[500]; printf("input file name ... ");
GrayImage(r,g,b,Wrk
printf("input savefile name ... "); scanf("%s" SaveWrkImage( sSaveFilePath, Wrk);//W } //ReadRgbImage 関数,GrayImage 関数は省略 void SaveWrkImage(char *S {
2 値化
これまでの知識を使うと,2 値化は簡単に実現できる.処理の流れは,次の 4 つの手順を踏む. 像を読み込む
4)2 値画像を保存する
efine IMG_YSIZE 480// the y-size of an image
efine HIGH 255 // max gray level
efine LOW 0 // minimum gray level
ZE][IMG_XSIZE], signed char b[IMG_YSIZE][IMG_XSIZE]);
signed char r[IMG_YSIZE][IMG_XSIZE], unsigned char g[IMG_YSIZE][IMG_XSIZE], unsigned char XSIZE], unsigned char wrk[IMG_YSIZE][IMG_XSIZE]);
d SaveWrkImage(char *SaveFilePath, unsigned char a[IMG_YSIZE][IMG_XSIZE]); id Binar rk[IMG_YSIZE][IMG_XSIZE], int nTh);
画像処理
signed c E],g[IMG_YSIZE][IMG_XSIZE],b[IMG_YSIZE][IMG_XSIZE];
signed c _XSIZE], Wrk3[IMG_YSIZE][IMG_XSIZE];
id main(
printf("input file name ... "); scanf("%s", sFilePath); RGB 画像読み込み 変換し,Wrk 配列に保存 nThresh); rk, nThresh);//2 値化 file name ... "); ilePath); 数,SaveWrkImage 関数は省略 char wrk[IMG_YSIZE][IMG_XSIZE], int nTh)
r( j = 0 r( i = 0 ; i++ ){ nTh ){ j][i] = HIGH; lse{ wrk[j][i] = LOW; } } } } 1)RGB 画 2)モノクロ画像に変換する 3)2 値化処理 #include "stdio.h" include “stdlib.h” # // 画像サイズ
define IMG_XSIZE 640 //the x-size of an image
# #d #d #d
//関数のプロトタイプ宣言
oid ReadRgbImage(char *ReadFilePath, unsigned char r[IMG_YSIZE][IMG_XSIZE], unsigned char g[IMG_YSI v un void GrayImage(un IMG_YSIZE][IMG_ b[ oi v vo yImage(unsigned char w // 用配列宣言 har r[IMG_YSIZE][IMG_XSIZ un
un har Wrk[IMG_YSIZE][IMG_XSIZE], Wrk2[IMG_YSIZE][IMG
void) vo { char sFilePath[500]; char sSaveFilePath[500]; int nThresh; ReadRgbImage(sFilePath,r,g,b);// GrayImage(r,g,b,Wrk);//モノクロ画像に printf("input Thresholding value ... "); scanf("%d", & BinaryImage(W printf("input save scanf("%s", sSaveF SaveWrkImage( sSaveFilePath, Wrk);//Wrk 画像の保存 } ayImage 関 //ReadRgbImage 関数,Gr oid BinaryImage(unsigned v { int i,j; fo ; j < IMG_YSIZE; j++ ){ fo ; i < IMG_XSIZE i] >= if( wrk[j][ wrk[ }e
サンプルプログラム(sample.exe)を使って 2 値化処理 1)sample を実行 2)コマンド 1: RGB 画像読み込み (例)f51.rgb ロ画像変換 3)コマンド 11: モノク 4)コマンド 12: 2 値化 (例)しきい値 100 5)コマンド2: 処理画像の保存 (例)f51_binary.wrk
サンプルプログラム(sample.exe)を使ってヒストグラムの計算 計算する 1) コマンド番号“1” (例)f51.rgb をモノクロ化し,ヒストグラムを 2) コマンド番号“11” モノクロ画像化 3) コマンド番号“14” ヒストグラムを計算し,保存する f51_gray_hist.dat gray level 0 50 100 150 200 250 th e number of pix el 0 1000 2000 3000 4000
サンプルプログラムを使って RGB 値から xy 色度を計算
1)コマンド 16: 実行
2) 手の画像「hand.rgb」を入力
フレーム間差分 サンプルプログラムの該当箇所を以下に示す. フ レ ー ム 間 差 分 は , 連 続 す る 3 枚 の 画 像 を 配 列 Wrk , Wrk2 , Wrk3 に そ れ ぞ れ 読 み 込 み , InterframeDifferenceWithTh 関数の 4 番目の引数に,差分に設定するしきい値(例えば 30)を渡すことにより 計算できる.3 枚の画像は,モノクロ画像以外に,エッジを検出した画像を使用する場合もあり,照明変化に 安定した移動物体検出法としてよく用いられる. se 15: み込む e 1 ... "); scanf("%s", sFilePath); ReadWrkImage( sFilePath, Wrk ); // フレーム 2 を読み込む printf("input image 2 ... "); scanf("%s", sFilePath); ReadWrkImage( sFilePath, Wrk2 ); // フレーム 3 を読み込む printf("input image 3 ... "); scanf("%s", sFilePath); ReadWrkImage( sFilePath, Wrk3 ); フレーム間差分へのしきい値を設定 printf("input thresholding value ... ");
f("%d resh); Wrk, Wrk2, Wrk3, nThresh ); break; ca // フレーム1を読 printf("input imag // scan ", &nTh InterframeDifferenceWithTh(
void InterframeDifferenceWithTh(unsigned char Wrk[IMG_YSIZE][IMG_XSIZE], unsigned char Wrk2[IMG_YSIZE][IMG_XSIZE],
unsigned char Wrk3[IMG_YSIZE][IMG_XSIZE], int nTh) {
int i,j;
int nSub1, nSub2;
unsigned char bSub1, bSub2;
for( j = 0; j < IMG_YSIZE; j++
for( i = 0; i < IMG IZE; i++ ){
nSub1 = abs( Wrk2[j][i] - Wrk[j][i] ); nSub2 = ][i] - Wrk3[j][i] );
bSub1 = HIGH; }else{ bSub1 = LOW; } if( nSub2 > nTh ){ bSub2 = HIGH; }else{ bSub2 = LOW; }
Wrk[j][i]= bSub1 & bSub2; } } } ){ _XS abs( Wrk2[j if( nSub1 > nTh ){
メニュー画面の作り方 ニュー画面を表示する ニュー画面を常に表示させる 画面の基本構造 *******************¥n"); "); ***************************¥n"); ReadRgbImage(sFilePath,r,g,b); break; nFlag_Exit = -1; break; break; }// switch 1) printf 関数でメ 2) 一つの処理が完了するたびに,while 関数でメ
while 関数の繰り返し条件には,Flag を用意し,Flag が正の値(この例では 1)の間,処理を繰り返させる. プログラムを終了させる時には,while 関数の Flag を負の値(この例では-1)に設定する 3) コマンドの選択は,switch 文で行う. メニュー int nFlag_Exit = 1; int nCommand; while( nFlag_Exit > 0 ){ printf("¥n"); printf("************************ printf("<< 1 >> : read rgb image¥n
>> : exit ¥n"); printf("<< 99 printf("**************** printf("¥n"); printf(" command ? : "); scanf("%d", &nCommand); switch( nCommand ){ case 1:
printf("input file name ... "); scanf("%s", sFilePath);
case 99:
default:
【著者】 森尾 吉成(もりお よしなり) 1971 年,和歌山県生まれ.京都大学農学研究科助手を経て,現在,三重大学生物資源学部助教授. 「知的作業支援システム」をはじめ,「斑紋による乳牛の個体識別」,「切り花の 3 次元形状計測」など,農作業 タビジョンに関する研究を行っている. ッケージ使用マニュアル Copy right 森尾吉成 2005 現場を中心にコンピュー E-mail: morio@bio.mie-u.ac.jp C 言語による画像処理基本パ 平成 17 年 6 月 13 日 第 1 版 第 1 刷発行 著者 森 尾 吉 成 発行者 MORIO FARM 社 代表者 森尾 吉成 発行所 MORIO FARM 社 郵便番号 514-8507 三重県津市上浜町 1577 三重大学生物資源学部 電話 059-231-9602 morio@bio.mie-u.ac.jp Printed in Japan