画像処理事始
はじめに
無料で自由に利用できる、高機能な画像処理パッケージ OpenCV が提供されていますので、あえて画像処理に
関するプログラムを提供する必要は無いかもしれません。 ですが、C言語を使って、画像処理を学習したい方が
利用出来る様に、簡単な雛形を用意してみました。
C言語を使ってプログラムを書く場合は、ハードウエアの知識が有ればより理解しやすいのですが、特に知識が
無くても開発はできます。 多分知識の差は、コンパイラの最適化にヒントを提供できるかどうかだと思います。
・ C言語を使う上で押えておきたいH/Wの知識
・ 画像処理用のCプログラムの雛形
に関する情報を提供できればと思っております。
補足事項:以下のプログラムで利用できる BMPファイルは 24or32 bit 無圧縮のWindows形式のみ
サンプルプログラム #1 のソースコードと実行形式
|
(Windows版 実行形式を含む) |
2011/03/12 |
ファイル名 |
機能 |
nd5sum |
bmpLoad.zip |
bmpファイル表示プログラム |
e85dfd24512abf6ba52ae9aa25806024 |
imageProcessing.zip imageProcessing.zip |
画像処理の雛形プログラム |
c5edca6bc97ea0231d277af10c9c936c 05362ae58e191fb525f7e65090d8d97a |
nega.zip nega.zip |
ネガ画像作成プログラム |
1bca225202f38add5896a99a00e818e6 a26fcbc9c7cf8194d34e977e91ee63ee |
ave3x3.zip ave3x3.zip |
平滑化フィルタプログラム |
1328be48f9e30870013494c2291f8d0e bce0137ec6caa3dce07536390d1e4534 |
説明に先立ち、まずサンプルプログラムを用意しました。
bmpLoad.zip
・
画像処理結果として作成されたBMPファイルの目視確認用プログラム (
ファイル名の表示も必要な場合)
"
Drag
& Drop の利用方法"で説明したGUIプログラムの、
DragAndDropExArg.c
をベースに、画像の読み込みと
表示機能を追加したものです。 プログラムは、C言語で記述され、コメントはSJISで記載されています。
実行形式は、Windows XP SP3 環境での動作を確認しています。
本プログラムのみ Windows 専用
BMPファイル自体の構成に関しては、
BMP ファイルの構造に関する資料 を参照して下さい。
imageProcessing.zip
・
画像処理用のプログラムの雛形(入力されたカラーBMPの複製を作成)
24/32ビットのBMPファイルを読み込み、画像を1次元の配列としてアクセス可能にし、同時に処理結果を格納する
1次元の配列を生成して、画像処理プログラム(サブルーチン)を呼び出します。
画像処理プログラムの先頭部で、画像と処理結果を格納する1次元配列に対して、2次元配列としてのアクセスを
可能にする、2次元配列用のインデックスを生成します。
サブルーチン内では、入力画像の各画素データを、処理結果を格納する配列にコピーします。
サブルーチンの終了ステータスを 0 (正常) で終了すると、処理結果を32ビットのBMPファイルとして保存します。
実行形式は、Windows XP SP3 環境での動作を確認しています。
nega.zip
・
画像処理用の雛形を元に、入力されたカラーBMPのネガ画像を作成するプログラム
24/32ビットのBMPファイルを読み込み、画像を1次元の配列としてアクセス可能にし、同時に処理結果を格納する
1次元の配列を生成して、画像処理プログラム(サブルーチン)を呼び出します。
画像処理プログラムの先頭部で、画像と処理結果を格納する1次元配列に対して、2次元配列としてのアクセスを
可能にする、2次元配列用のインデックスを生成します。
サブルーチン内では、入力画像の各画素データの、ネガ(反転)データを処理結果を格納する配列に記録します。
サブルーチンの終了ステータスを 0 (正常) で終了し、処理結果を32ビットのBMPファイルとして保存します。
実行形式は、Windows XP SP3 環境での動作を確認しています。
ave3x3.zip
・
画像処理用の雛形を元に、入力されたカラーBMPの3x3画素の平滑画像を作成するプログラム
24/32ビットのBMPファイルを読み込み、画像を1次元の配列としてアクセス可能にし、同時に処理結果を格納する
1次元の配列を生成して、画像処理プログラム(サブルーチン)を呼び出します。
画像処理プログラムの先頭部で、画像と処理結果を格納する1次元配列に対して、2次元配列としてのアクセスを
可能にする、2次元配列用のインデックスを生成します。
サブルーチン内では、入力画像のR,G,Bの各チャンネル毎に、各画素とその周辺の8画素(計9画素)の平均値を、
計算して、処理結果を格納する配列に記録します。 (sobel、Laplacianフィルターやコンボリューションの参考例)
サブルーチンの終了ステータスを 0 (正常) で終了し、処理結果を32ビットのBMPファイルとして保存します。
実行形式は、Windows XP SP3 環境での動作を確認しています。
サンプルプログラム #2 のソースコードと実行形式
|
(Windows版 実行形式を含む) |
2011/03/14 |
ファイル名 |
機能 |
nd5sum |
color2gray.zip color2gray.zip |
カラー画像を濃淡画像に変換 |
87357dae1b49eacdcd828c190d5cd498 8ab666ad4e08189f14b0e22b75f9872e |
sobel.zip sobel.zip |
Sobel フィルタプログラム |
1898ae27f6c633a6d0b2e70185836f03 3fb8682ad3ac91f904801d12785119bb |
laplacian.zip laplacian.zip |
Laplacianフィルタプログラム |
1cf7c9fd88c409bcc9c2950d049918bf 88de45ea2d39ae81a6f972121bbf1184 |
説明に先立ち、まずサンプルプログラムを用意しました。
color2gray.zip
・
画像処理用の雛形を元に、入力されたカラーBMPから濃淡画像を作成するプログラム
24/32ビットのBMPファイルを読み込み、画像を1次元の配列としてアクセス可能にし、同時に処理結果を格納する
1次元の配列を生成して、画像処理プログラム(サブルーチン)を呼び出します。
画像処理プログラムの先頭部で、画像と処理結果を格納する1次元配列に対して、2次元配列としてのアクセスを
可能にする、2次元配列用のインデックスを生成します。
サブルーチン内では、入力画像の各画素データ(R、G、B)から、濃淡画像データ(変換式参照)を作成し、結果を
出力用の配列に記録します。 出力は32ビットR、G、B形式、各色要素に同じ値を指定して濃淡画像を表記する
変換式: 0.299*R + 0.587*G + 0.114*B ⇒ Y (高速整数演算の場合: y =
(306*r + 601*g + 117*b)>>10)
サブルーチンの終了ステータスを 0 (正常) で終了し、処理結果を32ビットのBMPファイルとして保存します。
実行形式は、Windows XP SP3 環境での動作を確認しています。
Sobel.zip
・
画像処理用の雛形を元に、入力されたカラーBMPから濃淡画像を作成し、Sobel フィルターを施すプログラム
24/32ビットのBMPファイルを読み込み、画像を1次元の配列としてアクセス可能にし、同時に処理結果を格納する
1次元の配列を生成して、画像処理プログラム(サブルーチン)を呼び出します。
画像処理プログラムの先頭部で、画像と処理結果を格納する1次元配列に対して、2次元配列としてのアクセスを
可能にする、2次元配列用のインデックスを生成します。
サブルーチン内では、入力画像のR,G,Bの値から、濃淡情報を生成します。 この濃淡データに対して、 Soble の
オペレーションを行い、処理結果の最小値を0に、最大値を255になる様に変換します。
出力用配列のRGB値に変換した値(0 ~ 255)を記録します。
サブルーチンの終了ステータスを 0 (正常) で終了し、処理結果を32ビットのBMPファイルとして保存します。
実行形式は、Windows XP SP3 環境での動作を確認しています。
Laplacian.zip
・
画像処理用の雛形を元に、入力されたカラーBMPから濃淡画像を作成し、Laplacian フィルターを施すプログラム
24/32ビットのBMPファイルを読み込み、画像を1次元の配列としてアクセス可能にし、同時に処理結果を格納する
1次元の配列を生成して、画像処理プログラム(サブルーチン)を呼び出します。
画像処理プログラムの先頭部で、画像と処理結果を格納する1次元配列に対して、2次元配列としてのアクセスを
可能にする、2次元配列用のインデックスを生成します。
サブルーチン内では、入力画像のR,G,Bの値から、濃淡情報を生成します。 この濃淡データに対して、 Soble の
オペレーションを行い、処理結果の最小値を0に、最大値を255になる様に変換します。
これはサンプル処理なので、ガンマ補正(γ=0.6) も行います。(本来の処理では不要だが学習用) まず、
変換に使用するL:UT(lookup table)を作成します。入力値(0 ~ 255)、出力値(0 ~ 255)で、変換テーブルがγ。
Laplacian値を、0 ~ 255の範囲に変換後、ガンマー変換用のLUTを使ってカンマ補正し、出力配列に記録します。
サブルーチンの終了ステータスを 0 (正常) で終了し、処理結果を32ビットのBMPファイルとして保存します。
実行形式は、Windows XP SP3 環境での動作を確認しています。
サンプルプログラム #3 のソースコードと実行形式
|
(Windows版 実行形式を含む) |
2011/05/03 |
ファイル名 |
機能 |
nd5sum |
labeling.zip labeling.zip |
二値画像の8連結ラベリング |
a6bebe2498c7c9972f18ae05b0b4e186 edfc0d58d957ea2cbf78cc7a8c096be6 |
labeling4.zip labeling4.zip |
二値画像の4連結ラベリング |
bc664e7176746eab9c9347dbc5d2da03 1a1aaa4e03aa6965d94a1ca621e33e33 |
labeling_x64.zip labeling_x64.zip |
二値画像のラベリング(x64版) 8連結,4連結の実行形式のみ |
64ビット用バイナリ 2017/03/03 New 768d28a0cac805c2766239658772c1df |
説明に先立ち、まずサンプルプログラムを用意しました。このラベリングプログラムは、原理の理解を目的にした実装の為、複雑な画像のラベリングには時間を要します。
labeling.zip (ラベリング処理の8連結、4連結共通コア部分)
・
画像処理用の雛形を元に、入力されたBMP(二値画像)に対して、8連結のラベリング処理を行うをプログラム
24/32ビットのBMPファイルを読み込み、画像を1次元の配列としてアクセス可能にし、同時に処理結果を格納する
1次元の配列を生成して、画像処理プログラム(サブルーチン)を呼び出します。
ラスタースキャン方式のラベリング処理では、処理対象画素の、上下左右1画素に対して連結性(8箇所)の評価
をしますが、処理の重複(ラベリング処理の完了している画素の評価のみで良い)を除くと、4箇所の評価で完了
します。 処理対象画素ラベル値の決定により、処理対象画素以前までは異なっている対象物と認識していた
領域が、単一の対象物を構成する領域である事が判明する事があり、この場合にはどちらかのラベル番号に、
統一(displacementにて対応 "この関数はインライン展開される")する必要が生じます。
また、処理対象画素の周囲の画素を参照する事は、参照する画像の欠落する周辺部では処理が破綻する事を
意味します。この問題は、無限の大きさを持つ背景の中に画像を配置すれば回避出来ますが、必要十分条件を
満たす実装は、画像の上下左右に1画素分の背景を拡張し、ラベリング処理を本来に画像部に対してのみ行う
方法です。画像の周辺部の処理では、本来欠落するデータを参照した場合に、背景データ(ハードウエア処理時
も同様)を参照する事になり、単一のアルゴリズムで正しいラベリングが可能になります。
入力画像の大きさが、縦横がそれぞれ、LX、LY の場合、LX+2、LY+2 の大きさのエリアを確保し、エリアの境界
の値を全て、背景を表す値(0)にし、その内側に入力画像を展開します。展開後、画像データに対し、ラベリング
処理を施し、結果を 関数名.cfg 関数名.lbl ファイルとして保存します。
cfg、lbl ファイルの詳細は、"
1B ラベルファイルの構成"を参照して下さい。
ラベルデータは単なる自然数で可読性が劣るので、同時に擬似カラー化し、画像としての確認を可能にします。
ラベルデータ(0~0xffffff)から擬似カラーデータ変換は可逆変換を採用していますので、擬似カラーデータから、
ラベルデータ値を知る事が出来ます。変換方法は、擬似カラーデータをラベルデータに変換するプログラム
unsigned int pseudoColor2Int( unsigned int v ) を使用してください。("
1B ラベルファイルの構成"を参照)
ラベルデータの形式に関しては、"
1B ラベルファイルの構成"を参照してください。 またラベルデータを利用する
例に関しては、"
画像データとラベルデータの処理用サンプルプログラム(C言語)の実施例"を参照下さい。
実行形式は、Windows XP SP3 環境での動作を確認しています。
このラベリングソフトを使った処理手順のチュートリアル(md5sum: e96c4278e6716a121e7bc6a5f271f42e)
"画像処理のための高速ラベリングソフト+α"のチュートリアル(詳細はこちらを参照して下さい)相当の処理
labeling4.zip (ラベリング処理の8連結、4連結共通コア部分)
・
画像処理用の雛形を元に、入力されたBMP(二値画像)に対して、4連結のラベリング処理を行うをプログラム
24/32ビットのBMPファイルを読み込み、画像を1次元の配列としてアクセス可能にし、同時に処理結果を格納する
1次元の配列を生成して、画像処理プログラム(サブルーチン)を呼び出します。
ラスタースキャン方式のラベリング処理では、処理対象画素の、上下左右1画素に対して連結性(4箇所)の評価
をしますが、処理の重複(ラベリング処理の完了している画素の評価のみで良い)を除くと、2箇所の評価で完了
します。 処理対象画素ラベル値の決定により、処理対象画素以前までは異なっている対象物と認識していた
領域が、単一の対象物を構成する領域である事が判明する事があり、この場合にはどちらかのラベル番号に、
統一(displacementにて対応 "この関数はインライン展開される")する必要が生じます。
また、処理対象画素の周囲の画素を参照する事は、参照する画像の欠落する周辺部では処理が破綻する事を
意味します。この問題は、無限の大きさを持つ背景の中に画像を配置すれば回避出来ますが、必要十分条件を
満たす実装は、画像の上下左右に1画素分の背景を拡張し、ラベリング処理を本来に画像部に対してのみ行う
方法です。画像の周辺部の処理では、本来欠落するデータを参照した場合に、背景データ(ハードウエア処理時
も同様)を参照する事になり、単一のアルゴリズムで正しいラベリングが可能になります。
入力画像の大きさが、縦横がそれぞれ、LX、LY の場合、LX+2、LY+2 の大きさのエリアを確保し、エリアの境界
の値を全て、背景を表す値(0)にし、その内側に入力画像を展開します。展開後、画像データに対し、ラベリング
処理を施し、結果を 関数名.cfg 関数名.lbl ファイルとして保存します。
cfg、lbl ファイルの詳細は、"
1B ラベルファイルの構成"を参照して下さい。
ラベルデータは単なる自然数で可読性が劣るので、同時に擬似カラー化し、画像としての確認を可能にします。
ラベルデータ(0~0xffffff)から擬似カラーデータ変換は可逆変換を採用していますので、擬似カラーデータから、
ラベルデータ値を知る事が出来ます。変換方法は、擬似カラーデータをラベルデータに変換するプログラム
unsigned int pseudoColor2Int( unsigned int v ) を使用してください。("
1B ラベルファイルの構成"を参照)
ラベルデータの形式に関しては、"
1B ラベルファイルの構成"を参照してください。 またラベルデータを利用する
例に関しては、"
画像データとラベルデータの処理用サンプルプログラム(C言語)の実施例"を参照下さい。
実行形式は、Windows XP SP3 環境での動作を確認しています。
サンプルプログラム #4 のソースコードと実行形式
|
(Windows版 実行形式を含む) |
2011/06/26 |
ファイル名 |
機能 |
nd5sum |
bmpCalc1.zip |
BMP形式の画像間での演算 |
ae63df77d9024b17641a0ab39b776922 |
op4bmp.zip |
BMP形式の画像間での演算 |
49f1b1a07bceefc9a8bfa296b09661ae |
説明に先立ち、まずサンプルプログラムを用意しました。
op4bmp.zip
・
カラーBMP画像間(画像同士又は、画像と数値)で算術或いは論理演算を行うプログラム
動的2次元配列、動的3次元配列の確保を実装したインクルードファイル allocArray.h
alloc2D、alloc3D、alloc2Darray、alloc3Darray、calloc2Darray、calloc3Darray、malloc2Darray、malloc3Darray、
で確保した領域(へのポインタ)は、使用後 free() 関数を用いて開放してください。
24/32ビットのBMPファイルを読み込み、カラー画像を
2次元の配列としてアクセス可能にし、同時に処理結果を
格納する2次元の配列を生成して、画像処理プログラム(サブルーチン)を呼び出します。
動的2次元配列(2次元配列として管理するインデックス部とデータを格納するデータ領域)が生成されます。
データを格納するデータ領域に直接アクセスする場合は、配列の最初のアドレス(&arry[0][0])から、横×縦個の
連続したデータ並びとして参照します。単項演算では、入力画像の反転(ネガポジ)と特定の値の設定が行えます。
画像間演算(画像同士の演算或いは、画像と数値の演算)には算術演算と論理演算の二種類があります。
論理演算では、AND、OR、EXOR が行え、算術演算では、ADD、SUB、MUL、DIV の演算が行えます。
数値を指定する場合は、0~255 0xff の範囲。 set コマンド用に数値のチェックは意図的行っていないので、
利用時には注意してください。 ( set コマンドは、BGRA構成の画素を4バイト整数として見做して処理)
カラー画像は、RGBそれぞれが符号なし 8 ビットなので、0~255 の値しか保持できません、乗算、加算処理時に
結果が 255 を超えた場合は、255 がセットされます。減算処理時に値が負になった場合は、0 がセットされます。
サブルーチンの終了ステータスを 0 (正常) で終了し、処理結果を32ビットのBMPファイルとして保存します。
実行形式は、Windows XP SP3 環境での動作を確認しています。
補足事項
ポインターのサイズは、対象の如何に関わらず一律の大きさとなります。そのサイズは、システムがアクセスする
事が可能な任意の場所を指定出来る大きさ(32ビット環境なら4バイト、64ビット環境なら8バイト)となります。
void のポインタも、関数のポインタも、ポインタのポインタも大きさは同じです。
チュートリアル
ラベリングデータを利用した画像処理例
md5sum: e96c4278e6716a121e7bc6a5f271f42e |
Tutorial |
次回予告
まずは、プログラムの公開を行います。
次回は、詳しく説明を行います。
補足事項
プログラムは、windows.h を必要としないように記述さえれていますが、以下のプログラムがパスする環境が必要です
#include <stdio.h>
#include <stdlib.h>
#pragma pack(push,1)
typedef struct PIXEL { unsigned char B, G, R, A; } PIXEL ;
#pragma pack(pop)
#pragma pack(push,2)
typedef struct HEADER {
unsigned short bfType; // 0x4D42 BMP identification 0x42='B', 0x4D='M'
unsigned int bfSize; // ??
unsigned short bfReserved1; // 0
unsigned short bfReserved2; // 0
unsigned int bfOffBits; // 54 sizeof(HEADER)
unsigned int biSize; // 40 sizeof(biSize ~ biClrImportant)
signed int biWidth; // lx
signed int biHeight; // ly
unsigned short biPlanes; // 1
unsigned short biBitCount; // 32
unsigned int biCompression; // 0
unsigned int biSizeImage; // ??
signed int biXPelsPerMeter; // 0
signed int biYPelsPerMeter; // 0
unsigned int biClrUsed; // 0
unsigned int biClrImportant; // 0
} HEADER ;
#pragma pack(pop)
void environmentalConditionCheck()
{
if( (sizeof(int)!=4) || (sizeof(PIXEL)!=4) || (sizeof(HEADER)!=54) ) {
fprintf( stderr, "\nBad environmental condition.\n" ) ;
fprintf( stderr, "\tsizeof(int) must be 4 Byte. (%d)\n", sizeof(int) ) ;
fprintf( stderr, "\tsizeof(PIXEL) must be 4 Byte. (%d)\n", sizeof(PIXEL) ) ;
fprintf( stderr, "\tsizeof(HEADER) must be 54 Byte. (%d)\n", sizeof(HEADER) ) ;
exit(1) ;
}
}
int main(){ environmentalConditionCheck(); return 0; }