man ppmで見ることができる.PPM形式にはテキスト形式とバイナリ形式があるが,こ こではテキスト形式のみ扱う(convert コマンドで作成されるPPMファイルはバ イナリ形式の場合があるが,pnmnorawコマンドで変換できる).実際の例で見 てみる.
P3 4 4 255 0 0 0 0 0 0 0 0 0 255 0 255 0 0 0 0 255 127 0 0 0 0 0 0 0 0 0 0 0 0 0 255 127 0 0 0 255 0 255 0 0 0 0 0 0 0 0 0上の部分をt.ppmというファイルで保存して,
gimp t.ppmでみると,幅4ドット,高さ4ドットの米粒のような画像が現れるがこれを zoom in していくと,
P3の部分がマジックナンバーという部分で,このファイルが他の画像ファイルで はなくPPM形式であることを示す(Windowsの多くのアプリケーションのように, 拡張子が .ppm でああることで判断しているわけではない).PPM形式では空白 (White Space, Spaceコードだけではなく改行,タブも含まれる)はいくついれ ても良いが,P3の前に入れてはいけない.マジックナンバーの後に 空白 があり,4 4 255で,画像の幅,高さ,色の階調数を順に指定する.階調の最大値は,一般には R(red), G(Green), B(Blue)それぞれ 0(暗い)から255(明るい)までの256階調 あれば人間に自然な画像を表せるといわれているので,ここでは 255 を指定 している.詳しくは, 情報処理システム:画像を参照すること.その後で,
0 0 0 0 0 0 0 0 0 255 0 255 0 0 0 0 255 127 0 0 0 0 0 0 n 0 0 0 0 0 0 0 255 127 0 0 0 255 0 255 0 0 0 0 0 0 0 0 0のように,4x4=16ピクセルの RGB値を順に指定している.ピクセルの指定の順 番は横書きの文章と同じように左上から右下へ横方向に走査していく.1ピク セル内の指定の順番は R G Bの順番になっている.上の例で現れる色は
// AWT を使うので,java.awt.* を import する.
import java.awt.*;
// イベント処理をおこなうので,
import java.awt.event.*;
// イメージを使うので
import java.awt.image.*;
// 独立したウィンドウを作るので,ImageTransは Frameのサブクラスとして定義
class ImageTrans extends Frame{
// イメージを表す Image クラスの変数 image の宣言
Image image;
// ImageTransクラスのコンストラクタ
String fileName;
int w,h;
public ImageTrans(String fileName){
// 親クラスである Frameクラスのコンストラクタを
// タイトル文字列を "ImageTrans"として呼ぶ.
super("ImageTrans");
// 現在の Toolkit を得て,getImageで GIF 形式のファイルを指定して,
// イメージを得る.
// インスタンス変数 fileNameへコンストラクタの引数 fileNameの代入
this.fileName=fileName;
image=Toolkit.getDefaultToolkit().getImage(fileName);
addMouseListener(new MouseAdapter(){
// マウスのボタンを押した際に発生するイベントを以下のメソッドで捕まえる.
public void mousePressed(MouseEvent e){
int w=image.getWidth(ImageTrans.this);
int h=image.getHeight(ImageTrans.this);
System.err.println(w+","+h);
// imageの各ピクセルを収める intの配列を作成する
int[] pixels=new int[w * h];
// imageからpixelsにgrabするためのオブジェクトを生成する.
PixelGrabber pg = new PixelGrabber(image, 0, 0, w, h, pixels, 0, w);
try {
// grabを開始する.
pg.grabPixels();
} catch (InterruptedException ie) {
System.err.println("interrupted waiting for pixels!");
System.exit(1);
}
// 失敗したかどうか?
if ((pg.getStatus() & ImageObserver.ABORT) != 0) {
System.err.println("image fetch aborted or errored");
return;
}
printPpmHeader(w,h);
for (int j = 0; j < h; j++) {
for (int i = 0; i < w; i++) {
// ピクセルの内容をPPM形式で出力する
printPpmPixel(pixels[j * w + i]);
}
}
System.exit(0);
}
});
// ウィンドウのサイズを300 x 300 に指定する.
setSize(300,300);
// ウィンドウを表示する.
setVisible(true);
}
public static void main(String [] args){
// ImageTransクラスのインスタンスを作る.
ImageTrans frame=new ImageTrans(args[0]);
}
public void paint(Graphics g){
// イメージ image を (100,100)を左上にして表示する.
g.drawImage(image,100,100,this);
}
// PPM形式のヘッダを出力する
public void printPpmHeader(int w, int h){
System.out.println("P3\n"+w+" "+h+"\n255");
}
// PPM形式の各ピクセルを出力する
// ピクセルの情報は int型32ビットの中に
// +-------+-------+-------+-------+
// |alpha | 赤 | 緑 | 青 |
// +-------+-------+-------+-------+
// と8ビットづつ入っている.alphaは重ね合わせた時の透過度などに使われる
// フィールドで通常無視して良い.
public void printPpmPixel(int pixel) {
int alpha = (pixel >> 24) & 0xff;
int red = (pixel >> 16) & 0xff;
int green = (pixel >> 8) & 0xff;
int blue = (pixel ) & 0xff;
System.out.println(red+" "+green+" "+blue);
}
}
このプログラムを使って,
java ImageTrans ecc.gif > ecc.ppmを実行して,マウスをクリックすると,画像ファイルをPPM形式に変換することができる.
以下のプログラムImageLensは,元の絵の一部をクリックすると,それを取り 込んで,変形してイメージを作るものである.ただし,repaintが適切なタイ ミングで起きないため,画面があまりきれいにならない.
// AWT を使うので,java.awt.* を import する.
import java.awt.*;
// イベント処理をおこなうので,
import java.awt.event.*;
// イメージを使うので
import java.awt.image.*;
// 独立したウィンドウを作るので,ImageLensは Frameのサブクラスとして定義
class ImageLens extends Frame{
// イメージを表す Image クラスの変数 image の宣言
Image image,showImage;
int lensSize=50;
int[] pixels;
int[] showPixels;
// ImageLensクラスのコンストラクタ
String fileName;
// (x,y)に大きさlensSize(50)のレンズを置いたイメージをshowImageにして,repaintする
void transImage(int x, int y){
int w=image.getWidth(this);
int h=image.getHeight(this);
// System.err.println(w+","+h);
if(pixels==null || pixels.length< w*h){
// imageの各ピクセルを収める intの配列を作成する
pixels=new int[w * h];
showPixels=new int[w * h];
// imageからpixelsにgrabするためのオブジェクトを生成する.
PixelGrabber pg = new PixelGrabber(image, 0, 0, w, h, pixels, 0, w);
try {
// grabを開始する.
pg.grabPixels();
} catch (InterruptedException ie) {
System.err.println("interrupted waiting for pixels!");
System.exit(1);
}
// 失敗したかどうか?
if ((pg.getStatus() & ImageObserver.ABORT) != 0) {
System.err.println("image fetch aborted or errored");
return;
}
System.err.println("end of grab");
}
System.arraycopy(pixels,0,showPixels,0,w*h);
for (int j = Math.max(0,y-lensSize); j < Math.min(h,y+lensSize); j++) {
int dy=j-y;
for (int i = Math.max(0,x-lensSize); i < Math.min(w,x+lensSize); i++) {
int dx=i-x;
double r=Math.sqrt((double)(dx*dx+dy*dy)/(double)(lensSize*lensSize));
if(r<1.0){
showPixels[j*w+i]=pixels[(y+(int)(dy*r))*w+(x+(int)(dx*r))];
}
}
}
showImage=createImage(new MemoryImageSource(w,h,showPixels,0,w));
System.err.println("start repaint");
repaint();
}
public ImageLens(String fileName){
// 親クラスである Frameクラスのコンストラクタを
// タイトル文字列を "ImageLens"として呼ぶ.
super("ImageLens");
// 現在の Toolkit を得て,getImageで GIF 形式のファイルを指定して,
// イメージを得る.
// インスタンス変数 fileNameへコンストラクタの引数 fileNameの代入
this.fileName=fileName;
showImage=image=Toolkit.getDefaultToolkit().getImage(fileName);
addMouseListener(new MouseAdapter(){
// マウスのボタンを押した際に発生するイベントを以下のメソッドで捕まえる.
public void mousePressed(MouseEvent e){
System.err.println("mousePressed("+e.getX()+","+e.getY()+")");
transImage(e.getX()-100,e.getY()-100);
}
});
// ウィンドウのサイズを300 x 300 に指定する.
setSize(300,300);
// ウィンドウを表示する.
setVisible(true);
}
public static void main(String [] args){
// ImageLensクラスのインスタンスを作る.
ImageLens frame=new ImageLens(args[0]);
}
public void update(Graphics g){
paint(g);
}
public void paint(Graphics g){
// イメージ image を (100,100)を左上にして表示する.
g.drawImage(showImage,100,100,this);
}
}