root/graph/JavaPopWeb/src/jp/ac/nime/computer/grpsimulator/ImgPr/AnalyzeFourier.java

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. AnalyzeFourier
  2. XY
  3. Line
  4. add
  5. get
  6. size
  7. delTail
  8. clr
  9. Diag
  10. add
  11. get
  12. size
  13. delTail
  14. clr
  15. makeWB
  16. init
  17. calcPFFT
  18. writeDiag
  19. writeFFTDiag
  20. writeSrcDiag
  21. makeDiag
  22. chase8
  23. getNearPointData
  24. getNearPointData
  25. getPointData
  26. getPointData

package jp.ac.nime.computer.grpsimulator.ImgPr;

import java.awt.*;
import java.awt.image.*;
import java.awt.geom.*;
import java.util.*;
/** 画像解析 フーリエ級数展開
 * @author Kikuchi
 * @see FFT
 * @see Complex
 * @see ComplexF
 */
public class AnalyzeFourier {
        // クラス変数
        private int nWidth_;
        private int nHeight_;
        protected int nOp_;
        protected BufferedImage imgSrc_;
        protected BufferedImage imgDst_;
        protected int[] nBorder_;
        // 画像を線分で表した図形
        private Diag diag_;
        // FFTで一回圧縮展開した線分で表した図形
        private Diag diagFFT_;
        // fft用データバッファ
        private int[] numFFT_;
        private ComplexF[] fft_;
        /** 点を表すクラス
         */
        class XY {
                public XY(int x, int y) {
                        x_ = x;
                        y_ = y;
                }
                public int x_, y_;
        }
        /** 線分を表すクラス
         */
        class Line {
                private Vector v_;
                public Line() {
                        v_ = new Vector(0, 100);
                }
                public void add(int x, int y) {
                        v_.add(new XY(x, y));
                }
                public XY get(int n) {
                        if (n >= v_.size()) return null;
                        return (XY)v_.get(n);
                }
                public int size() {
                        return v_.size();
                }
                public void delTail() {
                        v_.remove(v_.size() - 1);
                }
                public void clr() {
                        v_.clear();
                }
        }
        
        /** 線分の塊(図形)を表すクラス
         */
        class Diag {
                private Vector v_;
                public Diag() {
                        v_ = new Vector();
                }               
                public void add(Line l) {
                        v_.add(l);
                }
                public Line get(int n) {
                        if (n >= v_.size()) return null;
                        return (Line)v_.get(n);
                }
                public int size() {
                        return v_.size();
                }
                public void delTail() {
                        v_.remove(v_.size() - 1);
                }
                public void clr() {
                        for (int i = 0; i < v_.size(); i ++) {
                                get(i).clr();
                        }
                        v_.clear();
                }
        }
        
        /** コンストラクタ
         */
        public AnalyzeFourier() {
                diag_ = new Diag();
                diagFFT_ = new Diag();
        }
        
        /** 白黒画像作成
         * @param flag  1のとき白黒反転する
         * @param imgSrc        YUV形式画像
         * @param imgDst        YUV形式画像
         */
        public static void makeWB(int flag, BufferedImage imgSrc, BufferedImage imgDst) {
                MeasureOp.makeWhiteBlack(flag, imgSrc, imgDst);
        }
        
        /** 計算に必要なパラメタ設定 (他のクラスと違って出力にRGB形式画像を渡すこと)
         * @param nOp   0:操作対象が黒 1:操作対象が白
         * @param imgSrc        YUV形式画像(二値化されていることが前提)
         */
        public void init(int nOp, BufferedImage imgSrc) {
                // 記憶
                imgSrc_ = imgSrc;
                nWidth_ = imgSrc_.getWidth();
                nHeight_ = imgSrc_.getHeight();
                // 線分データ作成時に使う境界線データ
                nBorder_ = new int [nWidth_ * nHeight_];
                Arrays.fill(nBorder_, 0);
                // 線分データを生成する
                makeDiag();
                // FFT用バッファを作る
                numFFT_ = new int [diag_.size()];
                fft_ = new ComplexF[32768];
                for (int i = 0; i < fft_.length; i ++) {
                        fft_[i] = new ComplexF(0.0, 0.0);
                }
//System.out.println("SrcDiag = " + diag_.size() + " lines");
        }
        
        
        /** P形フーリエ変換・逆変換
         */
        public void calcPFFT(int nCutOff) {
                // 2の累乗のデータ
                int[] rui2 = new int [16];
                rui2[0] = 1;
                for (int i = 1; i < 16; i++) {
                        rui2[i] = (int)Math.pow(2, i);
                }
                // 線分ごとにフーリエ変換 -> LowPass -> 逆変換 -> 新しい線分 を繰り替えすループ
                diagFFT_.clr();
                for (int i = 0; i < diag_.size(); i ++) {
                        // FFT用データの個数決定
                        int nPoint = diag_.get(i).size();
                        int nFFT = 0;
                        for (int j = 1; j < 16; j ++) { // 32768 まで対応
                                if (nPoint <= rui2[j]) {
                                        nFFT = rui2[j];
                                        break;
                                }
                        }
//System.out.println("SrcDiag Line(" + i + ") = " + nPoint + " points");
                        // FFT用のデータ作成
                        double[] dbDiff = new double[nPoint - 1]; // 点と点の間の距離
                        for (int k = 0; k < nFFT; k ++) {
                                fft_[k].set(0, 0); // 計算に必要分はクリア
                        }                       
                        for (int k = 0; k < nPoint - 1; k ++) {
                                XY xy1 = diag_.get(i).get(k);
                                XY xy2 = diag_.get(i).get(k + 1);
                                dbDiff[k] = Math.sqrt(((xy1.x_ - xy2.x_) * (xy1.x_ - xy2.x_)) + ((xy1.y_ - xy2.y_) * (xy1.y_ - xy2.y_)));
                                // 1data作る
                                fft_[k].set((xy2.x_ - xy1.x_) / dbDiff[k], (xy2.y_ - xy1.y_) / dbDiff[k]);
                        }
                        // フーリエ変換
                        FFT.fft(1, fft_, nFFT);
                        // LowPass
                        for (int k = nCutOff + 1; k < nFFT - nCutOff; k ++) {
                                fft_[k].set(0, 0);
                        }
                        // 逆変換
                        FFT.fft(-1, fft_, nFFT);
                        
                        // 新しい線分作成
                        Line line = new Line();
                        // 始点
                        XY xy0 = diag_.get(i).get(0);
                        line.add(xy0.x_, xy0.y_);
                        for (int k = 0; k < nPoint - 1; k ++) {
                                // 新しい点作成
                                int x = (int)((line.get(k).x_ + dbDiff[k] * fft_[k].getReal()) + 0.5);
                                int y = (int)((line.get(k).y_ + dbDiff[k] * fft_[k].getImag()) + 0.5);
                                line.add(x, y);
                        }
                        // 図形に登録
                        diagFFT_.add(line);
                }
//System.out.println("FFTDiag = " + diagFFT_.size() + " lines");
        }

        /** 図形を描画する
         * @param diag 図形
         * @param img  図形を描画する画像バッファ(RGB形式)
         */
        private static void writeDiag(Diag diag, BufferedImage img) {
                Graphics2D g = img.createGraphics();
                // 白でクリア
                g.setBackground(Color.white);
                g.clearRect(0, 0, 320, 240);
                g.setStroke(new BasicStroke(0.0f)); // 一番細く 引数はfloat
                g.setPaint(Color.black); // 黒
                // 図形
                for (int i = 0; i < diag.size(); i ++) {
                        // 線分のループ
                        Line line = diag.get(i);
                        for (int k = 0; k < line.size() - 1; k ++) {
                                // 一つのライン
                                g.draw(new Line2D.Double(line.get(k).x_, line.get(k).y_, line.get(k + 1).x_, line.get(k + 1).y_));
                        }
                }
            g.dispose();
        }

        /** FFT展開計算後の新しい図形を描画する
         * @param img 画像出力先(RGB形式であること)
         */
        public void writeFFTDiag(BufferedImage img) {
                writeDiag(diagFFT_, img);
        }

        /** 元図形(境界線)を描画する
         * @param img 画像出力先(RGB形式であること)
         */
        public void writeSrcDiag(BufferedImage img) {
                writeDiag(diag_, img);
        }

        
        /** 図形を切り出して、線分データを生成する
         */             
        private int makeDiag() {
                diag_.clr();
                // 始点の検索
                for (int y = 0; y < nHeight_; y ++) {
                        for (int x = 0; x < nWidth_; x ++) {
                                // 注目点が有効かつ未検索データだったら始点とする
                                if ((getPointData(x, y) == 1) && (nBorder_[x + (y * nWidth_)] == 0)) {
                                        // 外側境界 or 内側境界
                                        if (getPointData(x - 1, y) == 0) {
                                                Line line = new Line();
                                                // 外側境界探索
                                                chase8(x, y, 7, line);
                                                // 線分追加
                                                diag_.add(line);
                                        } else if (getPointData(x + 1, y) == 0) {
                                                Line line = new Line();
                                                // 内側境界探索
                                                chase8(x, y, 3, line);
                                                // 線分追加
                                                diag_.add(line);
                                        }
                                }
                        }
                }
                return 0;
        }
                
        /** 8連結探索しながら線分データ取得
         * @param x     始点x
         * @param y 始点y
         * @param nChase 最初の探索方向
         * @param line 抜き出した線分の格納先
         */
        private void chase8(int x, int y, int nChase, Line line) {
                int x0 = x, y0 = y;
                int x1, y1, x2, y2;
                boolean chk;    // 探索でみつかったか
                XY xy;
                // 初期化
                x1 = x0; y1 = y0;
                x2 = -1; y2 = -1;
                chk = false;
                // 始点を記憶
                line.add(x0, y0);
                // 探索ループ 初期座標に帰ってくるまでのループ
                while ((x2 != x0) || y2 != y0) {
                        chk = false;
                        switch (nChase) {
                        case 0:         // 下を探索
                                x2 = x1;
                                y2 = y1 + 1;                                    
                                if (getPointData(x2, y2) == 1) {
                                        // あった
                                        nChase = 6;
                                        chk = true;
                                } else {
                                        // 無かった
                                        nChase = 1;
                                }
                                break;
                        case 1:         // 右下
                                x2 = x1 + 1;
                                y2 = y1 + 1;                                    
                                if (getPointData(x2, y2) == 1) {
                                        // あった
                                        nChase = 7;
                                        chk = true;
                                } else {
                                        // 無かった
                                        nChase = 2;
                                }
                                break;
                        case 2:         // 右
                                x2 = x1 + 1;
                                y2 = y1;                                        
                                if (getPointData(x2, y2) == 1) {
                                        // あった
                                        nChase = 0;
                                        chk = true;
                                } else {
                                        // 無かった
                                        nChase = 3;
                                }
                                break;
                        case 3:         // 右上
                                x2 = x1 + 1;
                                y2 = y1 - 1;                                    
                                if (getPointData(x2, y2) == 1) {
                                        // あった
                                        nChase = 1;
                                        chk = true;
                                } else {
                                        // 無かった
                                        nChase = 4;
                                }
                                break;
                        case 4:         // 上
                                x2 = x1;
                                y2 = y1 - 1;                                    
                                if (getPointData(x2, y2) == 1) {
                                        // あった
                                        nChase = 2;
                                        chk = true;
                                } else {
                                        // 無かった
                                        nChase = 5;
                                }
                                break;
                        case 5:         // 左上
                                x2 = x1 - 1;
                                y2 = y1 - 1;                                    
                                if (getPointData(x2, y2) == 1) {
                                        // あった
                                        nChase = 3;
                                        chk = true;
                                } else {
                                        // 無かった
                                        nChase = 6;
                                }
                                break;
                        case 6:         // 左
                                x2 = x1 - 1;
                                y2 = y1;                                        
                                if (getPointData(x2, y2) == 1) {
                                        // あった
                                        nChase = 4;
                                        chk = true;
                                } else {
                                        // 無かった
                                        nChase = 7;
                                }
                                break;
                        case 7:         // 左下
                                x2 = x1 - 1;
                                y2 = y1 + 1;                                    
                                if (getPointData(x2, y2) == 1) {
                                        // あった
                                        nChase = 5;
                                        chk = true;
                                } else {
                                        // 無かった
                                        nChase = 0;
                                }
                                break;
                        }
                        // ラベリング
                        if (chk) {
                                // 記憶
                                nBorder_[x2 + y2 * nWidth_] = 1;
                                line.add(x2, y2);
                                // 座標移動
                                x1 = x2;
                                y1 = y2;
                        }
                }
                // ここの状態で 始点と終点が一致している。
                // そしてvectorの最後に終点が入っているので取り除く
                line.delTail();
        }
        
        /**
         * 画像の指定位置の近傍データ(3x3)を出力する
         * 出力データは、操作対象によって判断される。
         * 有効点が、1になる。非有効点が、0。
         * 領域外は、 0 になる。
         * imgSrc_が対象画像。
         * 
         * @param ptx   データが欲しい中心座標のx
         * @param pty   データが欲しい中心座標のy
         * @return 対象データを1とするデータ それ以外は 0
         */
        private int[] getNearPointData(int ptx, int pty) {
                return getNearPointData(imgSrc_, 3, ptx, pty);
        }
        
        /**
         * 画像の指定位置の近傍データを出力する
         * 出力データは、操作対象によって判断される。
         * 有効点が、1になる。非有効点が、0。
         * 領域外は、 0 になる。
         * @param img   画像データ
         * @param nN    近傍データの矩形サイズ(3x3なら 3を指定)
         * @param ptx   データが欲しい中心座標のx
         * @param pty   データが欲しい中心座標のy
         * @return 対象データを1とするデータ それ以外は 0
         */
        private int[] getNearPointData(BufferedImage img, int nN, int ptx, int pty) {
                // 戻り値を獲得する
                int[] result = new int[nN * nN];
                // 初期化
                for (int i = 0; i < result.length; i++) result[i] = 0;
                // データ取得
                int nHalf = nN / 2;
                for (int y = 0; y < nN; y++) {
                        int iy = pty + y - nHalf;
                        if (iy < 0 || iy >= img.getHeight()) continue;
                        for (int x = 0; x < nN; x++) {
                                int ix = ptx + x - nHalf;
                                if (ix < 0 || ix >= img.getWidth()) continue;
                                int yy = (img.getRGB(ix,iy) & 0x00FF0000) >> 16; // 輝度情報
                                int a = (img.getRGB(ix,iy) & 0xFF000000) >> 24; // alpha
                                if (a != 0) {
                                        if (nOp_ == 0) { // 操作対象
                                                // 操作対象が 黒
                                                if (yy < 128) {
                                                        result[x + y * nN] = 1;
                                                } else {
                                                        result[x + y * nN] = 0;
                                                }
                                        } else {
                                                // 操作対象が 白
                                                if (yy < 128) {
                                                        result[x + y * nN] = 0;
                                                } else {
                                                        result[x + y * nN] = 1;
                                                }
                                        }
                                } else {
                                        // 領域外
                                        result[x + y * nN] = 0;
                                }
                        }
                }
                return result;
        }
                
        /**
         * 画像の指定位置の点が操作対象データだったら、1を返す
         * 領域外は、 0 になる。
         * imgSrc_が対象画像
         * 
         * @param ptx   データが欲しい座標のx
         * @param pty   データが欲しい座標のy
         * @return 対象データを1とするデータ それ以外は 0
         */
        private int getPointData(int ptx, int pty) {
                return getPointData(imgSrc_, ptx, pty);
        }
        
        /**
         * 画像の指定位置の点が操作対象データだったら、1を返す
         * 領域外は、 0 になる。
         * @param img   対象画像データ
         * @param ptx   データが欲しい座標のx
         * @param pty   データが欲しい座標のy
         * @return 対象データを1とするデータ それ以外は 0
         */
        private int getPointData(BufferedImage img, int ptx, int pty) {
                // 範囲チェック
                if (ptx < 0 || ptx >= img.getWidth()) return 0;
                if (pty < 0 || pty >= img.getHeight()) return 0;
                // 輝度データ   
                int yy = (img.getRGB(ptx,pty) & 0x00FF0000) >> 16; // 輝度情報
                int a = (img.getRGB(ptx,pty) & 0xFF000000) >> 24; // alpha
                int result = 0;
                if (a != 0) {
                        if (nOp_ == 0) { // 操作対象
                                // 操作対象が 黒
                                if (yy < 128) {
                                        result = 1;
                                } else {
                                        result = 0;
                                }
                        } else {
                                // 操作対象が 白
                                if (yy < 128) {
                                        result = 0;
                                } else {
                                        result = 1;
                                }
                        }
                } else {
                        // 領域外
                        result = 0;
                }
                return result;
        }
}

/* [<][>][^][v][top][bottom][index][help] */