はじめに
プログラムを作るにもウェブサイトを作るにも必ず入るイメージ。でも、実際にコンピューターが中でどんな処理を行っているのかを知って入れている人は居ないと見ても良いでしょう。
カメラとかの電子機器全部の動作原理を理解して使うというのはバカみたいな話ですし、イメージもまたそれと同じ話とも言えます。でも、プログラマーとして、イメージというものを自分好みにコードで修正が出来るようになったら、そこからまた夢が広がるので、何回かに分けて画像処理というものに対して話をして行きたいと思います。
イメージの構造
イメージを私達が好き勝手に弄る前に、こいつがどんな構造をしているのかを把握する必要があります。それで、上のようにイメージを一つ準備しましたが、実はプログラムを利用して作ったイメージファイルだったりします。
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
public class imageProcess01_1 {
public static void main(String[] args) {
int width = 100; // イメージの幅
int height = 100; // イメージの縦
int color = 0; // 1Pixelの色情報
int[] rgb = new int[width * height]; // イメージの色情報全体
BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_4BYTE_ABGR);
for(int i = 0; i < height; i++) {
for(int j = 0; j < width; j++) {
// 配列の実座標を割り出す
int index = (i * width) + j;
// 座標によって色を変える
// 色情報は16進数で保存する。
switch(index / 2500) {
case 0: color = 0xFFFF0000; break;
case 1: color = 0xFF00FF00; break;
case 2: color = 0xFF0000FF; break;
default: color = 0xFF000000; break;
}
rgb[index] = color;
}
}
img.setRGB(0, 0, width, height, rgb, 0, width);
try {
ImageIO.write(img, "png", new File("img.png"));
} catch (IOException e) {
e.printStackTrace();
}
}
}
上記のように、JavaのBufferedImageクラスを使いイメージファイルの生成が可能です。
ここで重要なのはindex変数で配列の座標を作るところとswitchで振り分けられる16進数です。一つ一つ見ていきましょう。
色情報の配列と実座標
私達はイメージを見る時、常に四角い形のイメージを見ることになります。でも、実際のイメージの色は上のコードでも確認出来るように二次元配列ではなく、普通の配列です。では、実際はどんな風に色を表示しているのかというと、縦の長さは考えずに幅の長さを使って単純に順番通り振り分けているだけです。
0 | 1 | 2 | 3 | 4 | ... | ... | ... | ... | 99 |
---|---|---|---|---|---|---|---|---|---|
100 | 101 | 102 | ... | ... | ... | ... | ... | ... | 199 |
200 | 201 | 202 | ... | ... | ... | ... | ... | ... | 299 |
300 | 301 | 302 | ... | ... | ... | ... | ... | ... | 399 |
400 | 401 | 402 | ... | ... | ... | ... | ... | ... | 499 |
500 | 501 | 502 | ... | ... | ... | ... | ... | ... | 599 |
600 | 601 | 602 | ... | ... | ... | ... | ... | ... | 699 |
700 | 701 | 702 | ... | ... | ... | ... | ... | ... | 799 |
800 | 801 | 802 | ... | ... | ... | ... | ... | ... | 899 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
9900 | 9901 | 9902 | ... | ... | ... | ... | ... | ... | 9999 |
実際のイメージの色はこんな感じで表示されます。
for(int i = 0; i < height; i++) {
for(int j = 0; j < width; j++) {
// 配列の実座標を割り出す
int index = (i * width) + j;
}
}
なので、このようにfor文を2つ使って幅と縦の座標を別々に数える場合は上のような公式を使わないと配列の実座標を割り出すことはできません。
###色情報の構造
次は色情報が実際にどんなデータを持っているのかを見てみます。
色情報のデータタイプはInt型です。Int型が保存できる容量は全部で4Byteですが、これを1Byteずつ分けてそれぞれのByteが色に関する情報を保存するようになります。
Alpha | Red | Green | Blue |
---|---|---|---|
不透明度 | 赤 | 緑 | 青 |
0 ~ 255 | 0 ~ 255 | 0 ~ 255 | 0 ~ 255 |
最初の1Byteからこのような順番で保存されていて、それぞれが0から255、全部で256個のデータを保存することができます。
そして、255という数字は、16進数で表すとちょうどFFになるので、プログラムで実際に利用する時は16進数で制御した方が楽です。
この表を元に、上のソースに書かれている16進数を見てみましょう。
// 座標によって色を変える
// 色情報は16進数で保存する。
switch(index / 2500) {
case 0: color = 0xFFFF0000; break;
case 1: color = 0xFF00FF00; break;
case 2: color = 0xFF0000FF; break;
default: color = 0xFF000000; break;
}
Javaの場合、0xと始まる数字は全部16進数で表現されたものです。そして、1Byteは文字2つを使って表示するので、Case 0:を例にしてみると、こうなります。
Alpha | Red | Green | Blue |
---|---|---|---|
FF | FF | 00 | 00 |
255 | 255 | 00 | 00 |
不透明度と赤は255、つまり透明でない赤色ということになります。配列は100x100なので10000の配列の中で最初の0から2499までが赤になるということですね。画像の色はこのように入れることができます。
終わりに
ここまでがプログラムが読み込むイメージ基本構造になります。この構造さえわかってしまえば、これだけでも簡単なイメージ修正がコードだけで可能になるのですが、何か思い付くものはいるでしょうか。
一応、実際の画像処理の話もしたいとは思いますが、一番最初の基礎を知らずに入ってしまうのも拙いかと思って、ご存知の方も多いとは思ったのですが、これを書きました。
最近は色々便利なライブラリーなども多く存在していて、実際にこのような画像処理機能を直接作るというのは無いかも知れませんが、原理を知っているか否かによってその理解度は天と地ほどの差が生まれると思います。
この機会に簡単な画像処理ぐらいはライブラリーなんか頼らないで自分で作れるようになっちゃいましょう。
では、また次回お会いしましょう。