画像の扱いについて、改めて書き直しています。 以下を参照ください。
画像ファイルの扱い方 (1)
プログラミングの入門書に書いてある通りに勉強していても、
自分のプログラムの結果がコンソールに出ているだけではなんだか味気なく感じてしまうことはありませんか?
ある程度プログラミングのやり方が分かってくると、画像を扱いたくなってくる人も多いでしょう。
それに、結果が画像の形で現れてくれると、プログラミングがなんだか一層楽しくなります。
でも、画像ファイルの扱い方は入門書にはほとんど書かれていませんね、
私も画像ファイルを扱いたいと思ったときどうすればいいのか調べるのに苦労しました。
ここでは、その画像ファイルのC言語での扱い方を解説したいと思います。
ここでやる内容は、画像ファイルのデータを書き出して、それをビューワなどで開いて確認するという形になります。
画像ファイルをC言語のプログラムで表示したりするというのとはちょっと違いますのでご注意を。
(こいつをするにはOSに依存したライブラリを使ったりしないとないといけないので純粋なC言語の範囲だけではできません)
ここで解説する内容はファイルの入出力など、
本当の入門者にとってはちょっとハードルの高い内容かもしれません。
使う技術そのものは、どの入門書にも書かれているような内容ですが、
例えば、ある関数の機能を深い意味で分かっていないと理解できないような処理があるかもしれません。
画像ファイルの扱い方-データ構造
まずは、自分のプログラムの中でどのような形で画像データを保持するかが問題です。
それぞれの画素の情報にアクセスしやすい形や、
変形、並べ替えのような処理のしやすい形、
汎用性を考えて、 OS で扱いやすい形などいろいろな形が考えられます。
Windows では表示させるのに BMP 形式のデータを渡すので、
この形で扱う場合が多いようです。
でも、ここでは画像データの中身の意味が分かりやすい形式をとりたいと思います。
この形が最もよいという意味ではありません。
あくまで私なりに分かりやすい形をとるということです(圧縮のことや処理の速度などは考慮していません)。
私自身それほど詳しくないので、どのような形がいいのかは、自分で考えてください。
それぞれのデータの意味がわかれば、どのようにでもカスタマイズできるでしょう。
最初に、画像に必要なデータとは何かを洗い出しましょう。
ご存知の方も多いとは思いますが、コンピュータの内部などでは、
画像の情報は点(ピクセルもしくはドットと呼ばれる)の集まりで表現され、
点の色をR(Red つまり赤)、G(Green つまり緑)、B(Blue つまり青)の光の三原色の濃度で表現しています。
数値が大きいほどその光の輝度(強さ)が大きいことを示しています。
すべての輝度が最小値とき黒になり、すべての輝度が最大値のとき白になります。
それぞれの濃度を 8bit (256階調)で表現したものがフルカラー(24bit カラー1677万7216色)と呼ばれます
(これにα値(透明度)を加えて 32bit とする時もあります)。
フルカラーの情報を保持するために256階調を表現できる型が3つ×ピクセル数必要です。
また、画像は2次元のデータです、これをどの順番で格納するかも考えなくてはいけません。
ここでは一般的な、左から右、上から下の走査方法をとります。
(横書きの文章(英文等)を読むのと同じ順序といえばわかりやすいかと思います。)
この生のピクセルデータともう1つ絶対に必要なのは画像の大きさです。
x方向(横)、y方向(縦)に何ピクセル分の大きさかを保持しなければなりません。
本当はもっと多くの情報を詰め込んで処理やメモリ消費の効率をあげたりするのでしょうが今回はこれだけで突っ走ります。
(途中、必要ならば付け足すかもしれません)
というわけで、とりあえずの画像データを保持する構造体を
typedef struct _Picture
{
int x; /* x方向のピクセル数 */
int y; /* y方向のピクセル数 */
unsigned char *r; /* R要素の輝度(0~255) */
unsigned char *g; /* G要素の輝度(0~255) */
unsigned char *b; /* B要素の輝度(0~255) */
unsigned char *a; /* α値(透明度)(0~255) */
/* ピクセルのデータは左から右、上から下へ走査した順で格納 */
}Picture;
※Carvaid さんのご指摘によりα値の要素を加えました。
と定義します。
例えば、左下のような5×5のサイズのイメージ(元画像自体拡大してあります) がどのように格納されるかというと(RGB の情報は右下のようになってます)
R:0 G:0 B:0 |
R:0 G:0 B:64 |
R:0 G:0 B:128 |
R:0 G:0 B:192 |
R:0 G:0 B:255 |
R:64 G:64 B:0 |
R:64 G:64 B:64 |
R:64 G:64 B:128 |
R:64 G:64 B:192 |
R:64 G:64 B:255 |
R:128 G:128 B:0 |
R:128 G:128 B:64 |
R:128 G:128 B:128 |
R:128 G:128 B:192 |
R:128 G:128 B:255 |
R:192 G:192 B:0 |
R:192 G:192 B:64 |
R:192 G:192 B:128 |
R:192 G:192 B:192 |
R:192 G:192 B:255 |
R:255 G:255 B:0 |
R:255 G:255 B:64 |
R:255 G:255 B:128 |
R:255 G:255 B:192 |
R:255 G:255 B:255 |
横方向の大きさが5ピクセルなので x に5、縦方向の大きさが5ピクセルなので y に5、が格納されます。
そして、左上から横方向に操作していったRBGそれぞれの輝度情報が r、g、b のポインタの先に格納されるので、
rには{0,0,0,0,0, 64,64,64,64,64, 128,128,128,128,128, 192,192,192,192,192, 255,255,255,255,255}
gには{0,0,0,0,0, 64,64,64,64,64, 128,128,128,128,128, 192,192,192,192,192, 255,255,255,255,255}
bには{0,64,128,192,255, 0,64,128,192,255, 0,64,128,192,255, 0,64,128,192,255, 0,64,128,192,255}
といった情報が格納されることとなります。