【Java】Javaで離散cos変換
音声信号処理や画像圧縮技術に使用されている離散cos変換(DCT)をJavaで実装します。
DCTについてはこちらのウェブページを参考にさせて頂きました。
DCTはDFTと似ていますが、虚数計算が発生しないためコーディングは比較的容易でした。
離散COS変換の式
入力信号を、N行N列の変換行列とした時、離散COS変換後の係数は次のようになります。
逆離散COS変換の式
離散COS変換後の係数を、N行N列の変換行列とした時、逆変換後の信号Xは次のようになります。
以下にDCT,iDCTのコードを示します。Matrix2dクラスは次の記事で作成したものを使用しています。
emoson.hateblo.jp
public class DCT { private int N; //データ長 private Matrix2d D; //cosテーブル public DCT(int N){ this.N = N; double[][] d = new double[N][N]; for(int i=0; i<N; i++){ double k = i==0 ? 1.0/Math.sqrt(2.0) : 1.0; for(int j=0; j<N; j++){ d[i][j] = Math.sqrt(2.0/N) * k * Math.cos(i*(j+0.5)*Math.PI / (double)N); } } this.D = new Matrix2d(d); } //離散cos変換 public double[] dctArray(double[] x){ Matrix2d X = new Matrix2d(x); return Matrix2d.dot(D, X).T().getArrays()[0]; } public double[] dctArray(Matrix2d X){ return Matrix2d.dot(D, X).T().getArrays()[0]; } public Matrix2d dctMatrix(double[] x){ Matrix2d X = new Matrix2d(x); return Matrix2d.dot(D, X); } public Matrix2d dctMatrix(Matrix2d X){ return Matrix2d.dot(D, X); } //逆離散cos変換 public double[] idctArray(double[] x){ Matrix2d X = new Matrix2d(x); return Matrix2d.dot(D.T(), X).T().getArrays()[0]; } public double[] idctArray(Matrix2d X){ return Matrix2d.dot(D.T(), X).T().getArrays()[0]; } public Matrix2d idctMatrix(double[] x){ Matrix2d X = new Matrix2d(x); return Matrix2d.dot(D.T(), X); } public Matrix2d idctMatrix(Matrix2d X){ return Matrix2d.dot(D.T(), X); } //その場限り用の離散cos変換 public static double[] dctOne(double[] x){ int N = x.length; double[][] d = new double[N][N]; for(int i=0; i<N; i++){ double k = i==0 ? 1.0/Math.sqrt(2.0) : 1.0; for(int j=0; j<N; j++){ d[i][j] = Math.sqrt(2.0/N) * k * Math.cos(i*(j+0.5)*Math.PI / (double)N); } } Matrix2d D = new Matrix2d(d); Matrix2d X = new Matrix2d(x); return Matrix2d.dot(D, X).T().getArrays()[0]; } //その場限り用の逆離散cos変換 public static double[] idctOne(double[] c){ int N = c.length; double[][] d = new double[N][N]; for(int i=0; i<N; i++){ double k = i==0 ? 1.0/Math.sqrt(2.0) : 1.0; for(int j=0; j<N; j++){ d[i][j] = Math.sqrt(2.0/N) * k * Math.cos(i*(j+0.5)*Math.PI / (double)N); } } Matrix2d D = new Matrix2d(d); Matrix2d C = new Matrix2d(c); return Matrix2d.dot(D.T(), C).T().getArrays()[0]; } public static void main(String[] args){ double[] X = {1, 1, 1, 1, 2, 2, 2, 2, 2}; double[] C = DCT.dctOne(X); double[] _X = DCT.idctOne(C); System.out.println("入力信号\n"+new Matrix2d(X).T()); System.out.println("変換後の係数\n"+new Matrix2d(C).T()); System.out.println("逆変換後の信号\n"+new Matrix2d(_X).T()); } }
実行すると次のような結果が出力されます。
入力信号 | 1.00000 1.00000 1.00000 1.00000 2.00000 2.00000 2.00000 2.00000 2.00000 | 変換後の係数 | 4.66667 -1.33673 -0.23570 0.40825 0.23570 -0.19778 -0.23570 0.08579 0.23570 | 逆変換後の信号 | 1.00000 1.00000 1.00000 1.00000 2.00000 2.00000 2.00000 2.00000 2.00000 |
ちゃんと信号が復元されていますね。