イメージの扱い

前回はイメージを画像ファイルから読み込んだり,オフスクリーンイメージ を作ってそれに描画したりする方法,イメージを画面に表示する方法を扱った. 今回は,イメージのピクセルデータをプログラムで扱えるようにしたり,それ を元に別のイメージを作るということをやってみる.

予備知識(PPM形式)

PPM形式(Portable PixMap形式)の画像ファイル に関して説明する.

イメージからピクセルデータの取得

イメージからピクセルのデータを整数値として取り出すには, PixelGrabberというクラスを用いる.以下は,ファイルから読み込んで 作成したImageを grab(つかむ)してPPMファイル形式で標準出力に出力するプ ログラムである.
  // 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形式に変換することができる.

ピクセルデータからイメージの作成

ピクセルデータからイメージを作成するには, MemoryImageSourceというImageProducerを作成して,それをcreateImageする.

以下のプログラム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);
  }
}

次に進む