SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

Javaで学ぶグラフィックス処理

画像を小刻みに回転して写真の傾きを補正する

アフィン変換の回転と拡大を同時に行う


  • X ポスト
  • このエントリーをはてなブックマークに追加

サンプルプログラムの概要

 サンプルプログラムは以下の内容から成っています(GUI関係は省略)。

準備(init()の内容)

  1. 画像ファイルを読み込み、Imageクラスのimg_srcを用意する
  2. img_srcを、一次元のR、G、B別データに分解する(ただしSIZEWIDTHxHEIGHT
    • R: src_red[SIZE]
    • G: src_green[SIZE]
    • B: src_blue[SIZE]
  3. 一次元のR、G、B別データを二次元化する
    • R: data[0][HEIGHT][WIDTH]
    • G: data[1][HEIGHT][WIDTH]
    • B: data[2][HEIGHT][WIDTH]

実行(paint()の内容)

  1. 原画像img_srcを回転し、拡大してimg_destを作成する
  2. 後述のメソッドrotateImage(img_src, theta)を使用する
  3. img_destを描画する
  4. その時の回転角thetaを表示する

メソッドrotateImage(img_src, theta)

  1. スクロールバーで与えられた回転角thetaから、拡大倍率zoomを計算する
  2. 回転と拡大を同時に行なうアフィン変換の係数sintcostを計算する
  3. 補正画像の中心座標(WIDTH/2, HEIGHT/2)を中心に、ijをスキャンし、データを取得すべき原画像の座標xxyy(一般的には整数値でない)を計算する
  4. xxyyを整数部と小数部に分け、整数部はxy、小数部はdxdyとする
  5. 整数部xyにより原画像の4点のR、G、B別の二次元データを取得する
  6. 4点のデータから、dxdyを用いて線形補間を行い、R、G、B別の補間値inter[0]inter[1]inter[2]を計算する
  7. R、G、B別の補間値から、一次元RGB値rgb_dest[SIZE]を得る
  8. rgb_dest[SIZE]からImageクラスの画像を作成する

プログラム

import java.applet.Applet;
import java.awt.*;
import java.awt.image.*;
import java.awt.event.*;

public class Rotate extends Applet
   implements AdjustmentListener,ActionListener{

   int WIDTH=640;
   int HEIGHT=480;
   int SIZE=307200;
   double ALPHA=Math.atan((double)WIDTH/HEIGHT);

   int X0=10;
   int Y0=40;

   int[] src_red=new int[SIZE];
   int[] src_green=new int[SIZE];
   int[] src_blue=new int[SIZE];

   int[][][] data=new int[3][HEIGHT][WIDTH];

   Label label0,label1,label2;
   Scrollbar scroll;
   Button button;

   float theta;

   Image img_src,img_dest;

   public void init(){

      setBackground(Color.yellow);

      Panel panel=new Panel();
      panel.setLayout(new GridLayout(1,7,0,10));
      add(panel,"North");

      scroll=new Scrollbar(Scrollbar.HORIZONTAL,0,5,-60,65);
      scroll.addAdjustmentListener(this);

      label0=new Label("                      ");
      label1=new Label("    左下がり(Max 30°)");
      label2=new Label(" 右下がり(Max 30°)   ");

      button=new Button("元に戻す");
      button.addActionListener(this);

      panel.add(label0);
      panel.add(label1);
      panel.add(scroll);
      panel.add(label2);
      panel.add(label0);
      panel.add(button);
      panel.add(label0);

      //画像ファイルを読み込み、Image画像img_srcにする
      img_src=readImageFile("trogir.jpg");

      //Image画像を一次元R,G,Bに分解する
      get1DRGBFromImage(img_src,src_red,src_green,src_blue);

      //一次元R,G,Bを二次元化する
      change1DRGBTo2D(src_red,data[0]);
      change1DRGBTo2D(src_green,data[1]);
      change1DRGBTo2D(src_blue,data[2]);

   }

   public void paint(Graphics g){

      //画像を回転する
      img_dest=rotateImage(img_src,theta);

      //回転した補正画像を描画する
      g.drawImage(img_dest,X0,Y0,null);

      //回転角を表示する
      g.drawString("回転角="+String.valueOf(theta)+"°",
         X0,Y0+20+HEIGHT);

   }

   //画像ファイルを読み込みImageクラスの画像にするメソッド
   public Image readImageFile(String filename){

      Image img=getImage(getDocumentBase(),filename);
      MediaTracker mtracker=new MediaTracker(this);
      mtracker.addImage(img,0);
      try{
         mtracker.waitForAll();
      }catch(Exception e){}
      return img;

   }

   //Image画像を一次元RGB値に分解するメソッド
   private void get1DRGBFromImage(Image img,
      int[] red, int[] green, int[] blue){

      Color color;

      int[] rgb=new int[SIZE];

      //Image画像imgを一次元RGBデータrgbに変換する
      PixelGrabber grabber=
         new PixelGrabber(img,0,0,WIDTH,HEIGHT,rgb,0,WIDTH);
      try{
         grabber.grabPixels();
      } catch(InterruptedException e){}

      //一次元RGBデータrgbを一次元red、green、blueデータに分解する
      for(int i=0;i<SIZE;i++){

         color=new Color(rgb[i]);
         red[i]=color.getRed();
         green[i]=color.getGreen();
         blue[i]=color.getBlue();

      }

   }

   //一次元R,G,Bを二次元化するメソッド
   public void change1DRGBTo2D(int[] oneD, int[][] twoD){

      for(int j=0;j<HEIGHT;j++)
         for(int i=0;i<WIDTH;i++)
            twoD[j][i]=oneD[WIDTH*j+i];

   }

   //画像を回転し拡大するメソッド
   public Image rotateImage(Image img, float theta){

      float xx,yy,dx,dy;
      int x,y;

      Color color;

      int[] rgb_dest=new int[SIZE];

      int data_0,data_1,data_2,data_3;
      int[] inter=new int[3];

      float zoom=(float)(Math.cos(ALPHA-Math.abs(theta)
         *Math.PI/180.0)/Math.cos(ALPHA)+0.01);
      float sint=(float)Math.sin(theta*Math.PI/180.0)/zoom;
      float cost=(float)Math.cos(theta*Math.PI/180.0)/zoom;

      int height2=HEIGHT/2;
      int width2=WIDTH/2;

      for(int i=-width2;i<width2;i++)
         for(int j=-height2;j<height2;j++){

            //原画像の座標値(実数)を計算する
            xx=i*cost+j*sint+width2;
            yy=j*cost-i*sint+height2;

            //負の無限大に近い整数を求める
            x=(int)Math.floor(xx);
            y=(int)Math.floor(yy);

            //補間に必要な端数を求める
            dx=xx-x;
            dy=yy-y;

            //原画像の4点のデータと端数から線形補間する
            for(int k=0;k<3;k++){

               data_0=data[k][y][x];
               data_1=data[k][y][x+1];
               data_2=data[k][y+1][x];
               data_3=data[k][y+1][x+1];
               inter[k]=(int)((data_0*(1.0f-dx)+data_1*dx)*
                  (1.0f-dy)+(data_2*(1.0f-dx)+data_3*dx)*dy+0.5f);

            }

            color=new Color(inter[0],inter[1],inter[2]);
            rgb_dest[(height2+j)*WIDTH+width2+i]=color.getRGB();

         }

         return createImage(new MemoryImageSource(
            WIDTH,HEIGHT,rgb_dest,0,WIDTH));

   }

   //スクロールバーを変化させた時のメソッド
   public void adjustmentValueChanged(AdjustmentEvent ae){

      theta=scroll.getValue()/2.0f;
      repaint();

   }

   //「元に戻す」ボタンをクリックした時のメソッド
   public void actionPerformed(ActionEvent ae){

      if(ae.getSource()==button){
         theta=0.0f;
         scroll.setValue(0);
         repaint();
      }

   }

}

次のページ
プログラムの使い方

修正履歴

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
Javaで学ぶグラフィックス処理連載記事一覧

もっと読む

この記事の著者

石立 喬(イシダテ タカシ)

1955年東京工大卒。同年、NECへ入社し、NEC初のコンピュータの開発に参画。磁気メモリ、半導体メモリの開発、LSI設計などを経て、1989年帝京大学理工学部教授。情報、通信、電子関係の教育を担当。2002年定年により退職し現在に至る。2000年より、Webサイト「Visual C++の勉強部屋」を公開。...

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/398 2008/03/16 10:30

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング