CPUでの2次元FFTの実装は前の項目の図(図13.2「図:2次元FFTの手順」)で解説した通りとなります。
この手順のFFTでは、FFTの前にビット反転が行われるものとします。
この手順を実装すると下記のようなソースコードとなります。
FFTCPU2D.java.
package com.book.jocl.fft;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
import java.io.File;
import java.net.URL;
import java.nio.file.Paths;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
public class FFTCPU2D {
private static final String IMAGE_PATH = "SAMPLE.png";
private static ImageIcon img;
private static int width;
private static int height;
private static double _PI = Math.PI;
private static final int _RowSize = 256;
private static final int _ColSize = 256;
private static final int size = _RowSize*_ColSize;
private static int _SIGN = 1;
private static void forward(int N, int offset, double[] data, double[] F, int sign, int step) {
int N2 = N/2;
double c, s;
double real, imaginary;
step++;
if(N2 != 2) {
forward(N2,offset,data,F,sign,step);
forward(N2,offset+N2,data,F,sign,step);
for(int i = 0; i < N2; i+=2) {
// sin -x = -sin x
// cos -x = cos x
c = Math.cos(i*_PI/N2); // (_PI * 2 * k)
s = sign*Math.sin(i*_PI/N2); // (_PI * 2 * k)
// (c+si)*(p+qi) => (cp-sq, cq+sp)
real = F[i+N2+offset]*c + F[i+N2+1+offset]*s;
imaginary = F[i+N2+1+offset]*c - F[i+N2+offset]*s;
//System.out.println("pair: "+ (i+offset)/2+"-"+ (i+N2+offset)/2+" N2: " +N2+ " offset:"+offset + " step:"+step + " c="+c+" s="+s);
F[i+N2+offset] = F[i+offset] - real;
F[i+N2+1+offset] = F[i+1+offset] - imaginary;
F[i+offset] += real;
F[i+1+offset] += imaginary;
}
} else {
F[offset] = data[offset] + data[offset+N2];
F[offset+1] = data[offset+1] + data[offset+N2+1];
F[offset+N2] = data[offset] - data[offset+N2];
F[offset+N2+1] = data[offset+1] - data[offset+N2+1];
//System.out.println("pair: "+ (offset)/2+"-"+ (N2+offset)/2+" N2: " +N2+ " offset:"+offset + " FFT-2 " +F[offset]+" "+F[offset+N2]);
}
}
private static void inverse(int N, double[] data, double[] F) {
int N2 = N/2;
int inverseSign = -1;
forward(N,0,data,F,inverseSign,0);
for(int i = 0; i < N; i+=2) {
F[i] /= N2;
F[i+1] /= N2;
}
}
private static int reverseBit(int x, int maxBits) {
int bit = 0;
while (maxBits != 0){
bit <<=1;
bit |=( x &1 );
x >>=1;
maxBits>>=1;
}
return bit;
}
private static int resizeToPowerOfTwo(int x) {
int bit = 1;
while(x > bit) {
bit <<=1;
}
return bit;
}
private static void reverseData(int N, double[] data) {
for(int i = 2; i < N*2; i+=2) {
int rev = reverseBit(i/2,N-1) << 1;
if(i < rev) {
//System.out.println("i: "+i/2+" rev:"+rev/2);
double tmpR = data[i];
double tmpI = data[i+1];
data[i] = data[rev];
data[i+1] = data[rev+1];
data[rev] = tmpR;
data[rev+1] = tmpI;
}
}
}
public static void main(String[] args) throws Exception {
URL imgResource = FFTCPU2D.class.getResource(IMAGE_PATH);
String imgPath = Paths.get(imgResource.toURI()).toFile().getAbsolutePath();
System.out.println(imgPath);
img = new ImageIcon(imgResource);
width = img.getIconWidth();
height = img.getIconHeight();
BufferedImage bimg = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR);
Graphics g = bimg.createGraphics();
img.paintIcon(null, g, 0,0);
g.dispose();
int pixelLength;
if(bimg.getAlphaRaster() != null) {
pixelLength = 4;
} else {
pixelLength = 3;
}
DataBufferByte dbb = (DataBufferByte)bimg.getRaster().getDataBuffer();
byte[] byteArray = dbb.getData();
System.out.println(byteArray.length);
System.out.println(pixelLength);
//生データ、バイト
byte[] _rawBlue = new byte[width*height];
byte[] _rawRed = new byte[width*height];
byte[] _rawGreen = new byte[width*height];
//RGBチャネルとして独立した変数に記憶
for( int i = 0; i < width; i++ ) {
for( int j = 0; j < height; j++ ) {
int index = j*width*pixelLength + i*pixelLength;
_rawBlue[j*width + i] = byteArray[index++];
_rawGreen[j*width + i] = byteArray[index++];
_rawRed[j*width + i] = byteArray[index];
}
}
//色彩配列、倍精度
double[] arBlue = new double[size*2];
double[] arGreen = new double[size*2];
double[] arRed = new double[size*2];
// Single row vector:行スキャンで走査線を記憶する変数
double[] srvBlue = new double[_RowSize*2];
double[] srvGreen = new double[_RowSize*2];
double[] srvRed = new double[_RowSize*2];
// Single row output vector:行を変換した配列を記憶する変数
double[] sroBlue = new double[_RowSize*2];
double[] sroGreen = new double[_RowSize*2];
double[] sroRed = new double[_RowSize*2];
// Single column vector:列スキャンを記憶する変数
double[] sclBlue = new double[_ColSize*2];
double[] sclGreen = new double[_ColSize*2];
double[] sclRed = new double[_ColSize*2];
// Single column output vector:列を変換した配列を記憶する変数
double[] scoBlue = new double[_ColSize*2];
double[] scoGreen = new double[_ColSize*2];
double[] scoRed = new double[_ColSize*2];
// Row matrix:行をフーリエ変換した配列を連結した行列
double[] rowBlue = new double[size*2];
double[] rowGreen = new double[size*2];
double[] rowRed = new double[size*2];
// Column matrix:列をフーリエ変換した配列を連結した行列
double[] colBlue = new double[size*2];
double[] colGreen = new double[size*2];
double[] colRed = new double[size*2];
// Output matrix:出力のための行列
double[] outBlue = new double[size*2];
double[] outGreen = new double[size*2];
double[] outRed = new double[size*2];
// cast to double;倍精度にキャスト
for(int i = 0; i < _ColSize; i++) {
for(int j = 0; j < _RowSize*2; j+=2) {
arBlue[i*_RowSize*2 + j] = (double)(_rawBlue[i*width + j/2] & 0xff);
arBlue[i*_RowSize*2 + j + 1] = (double)0.0f;
arGreen[i*_RowSize*2 + j] = (double)(_rawGreen[i*width + j/2] & 0xff);
arGreen[i*_RowSize*2 + j + 1] = (double)0.0f;
arRed[i*_RowSize*2 + j] = (double)(_rawRed[i*width + j/2] & 0xff);
arRed[i*_RowSize*2 + j + 1] = (double)0.0f;
}
}
for(int i = 0; i < _ColSize; i++) {
//get row vector:行スキャン
for(int j = 0; j < _RowSize*2; j+=2) {
srvBlue[j] = arBlue[i*_RowSize*2 + j];
srvBlue[j+1] = arBlue[i*_RowSize*2 + j + 1];
srvGreen[j] = arGreen[i*_RowSize*2 + j];
srvGreen[j+1] = arGreen[i*_RowSize*2 + j + 1];
srvRed[j] = arRed[i*_RowSize*2 + j];
srvRed[j+1] = arRed[i*_RowSize*2 + j + 1];
}
//bit reversal:ビット反転
reverseData(_RowSize,srvBlue);
reverseData(_RowSize,srvGreen);
reverseData(_RowSize,srvRed);
//forward transformation:フーリエ変換
forward(_RowSize*2, 0, srvBlue, sroBlue, _SIGN, 0);
forward(_RowSize*2, 0, srvGreen, sroGreen, _SIGN, 0);
forward(_RowSize*2, 0, srvRed, sroRed, _SIGN, 0);
// form the row matrix
for(int k = 0; k < _RowSize*2; k+=2) {
rowBlue[i*_RowSize*2 + k] = sroBlue[k];
rowBlue[i*_RowSize*2 + k + 1] = sroBlue[k+1];
rowGreen[i*_RowSize*2 + k] = sroGreen[k];
rowGreen[i*_RowSize*2 + k + 1] = sroGreen[k+1];
rowRed[i*_RowSize*2 + k] = sroRed[k];
rowRed[i*_RowSize*2 + k + 1] = sroRed[k+1];
}
}
for(int i = 0; i <_RowSize*2; i+=2) {
//get column vector:列スキャンをして配列に記憶
for(int j = 0; j < _ColSize*2; j+=2) {
sclBlue[j] = rowBlue[j*_RowSize + i];
sclBlue[j+1] = rowBlue[j*_RowSize + i + 1];
sclGreen[j] = rowGreen[j*_RowSize + i];
sclGreen[j+1] = rowGreen[j*_RowSize + i + 1];
sclRed[j] = rowRed[j*_RowSize + i];
sclRed[j+1] = rowRed[j*_RowSize + i + 1];
}
//bit reversal:ビット反転
reverseData(_ColSize,sclBlue);
reverseData(_ColSize,sclGreen);
reverseData(_ColSize,sclRed);
//forward transformation:フーリエ変換
forward(_ColSize*2, 0, sclBlue, scoBlue, _SIGN, 0);
forward(_ColSize*2, 0, sclGreen, scoGreen, _SIGN, 0);
forward(_ColSize*2, 0, sclRed, scoRed, _SIGN, 0);
for(int k = 0; k < _ColSize*2; k+=2) {
colBlue[k*_RowSize + i] = scoBlue[k];
colBlue[k*_RowSize + i + 1] = scoBlue[k+1];
colGreen[k*_RowSize + i] = scoGreen[k];
colGreen[k*_RowSize + i + 1] = scoGreen[k+1];
colRed[k*_RowSize + i] = scoRed[k];
colRed[k*_RowSize + i + 1] = scoRed[k+1];
}
}
/*
*
* Apply High Pass Filter:
*
*/
for(int i = 0; i < _RowSize*2; i+=2) {
for(int k = 0; k < _ColSize; k++) {
colBlue[k*_RowSize + i] = colBlue[k*_RowSize + i] > 256*256*200 ? colBlue[k*_RowSize + i] : 0;
colBlue[k*_RowSize + i + 1] = colBlue[k*_RowSize + i + 1] > 256*256*200 ? colBlue[k*_RowSize + i + 1] : 0;
colGreen[k*_RowSize + i] = colGreen[k*_RowSize + i] > 256*256*200 ? colGreen[k*_RowSize + i] : 0;
colGreen[k*_RowSize + i + 1] = colGreen[k*_RowSize + i + 1] > 256*256*200 ? colGreen[k*_RowSize + i + 1] : 0;
colRed[k*_RowSize + i] = colRed[k*_RowSize + i] > 256*256*200 ? colRed[k*_RowSize + i] : 0;
colRed[k*_RowSize + i + 1] = colRed[k*_RowSize + i + 1] > 256*256*200 ? colRed[k*_RowSize + i + 1] : 0;
}
}
/*
*
* Inverse FFT:逆フーリエ変換
*
*/
for(int i = 0; i <_RowSize*2; i+=2) {
//get column vector:列スキャン
for(int j = 0; j < _ColSize*2; j+=2) {
sclBlue[j] = colBlue[j*_RowSize + i];
sclBlue[j+1] = colBlue[j*_RowSize + i + 1];
sclGreen[j] = colGreen[j*_RowSize + i];
sclGreen[j+1] = colGreen[j*_RowSize + i + 1];
sclRed[j] = colRed[j*_RowSize + i];
sclRed[j+1] = colRed[j*_RowSize + i + 1];
}
//bit reversal:ビット反転
reverseData(_ColSize, sclBlue);
reverseData(_ColSize, sclGreen);
reverseData(_ColSize, sclRed);
//inverse transformation:逆変換
inverse(_ColSize*2, sclBlue, scoBlue);
inverse(_ColSize*2, sclGreen, scoGreen);
inverse(_ColSize*2, sclRed, scoRed);
for(int k = 0; k < _ColSize*2; k+=2) {
colBlue[k*_RowSize + i] = scoBlue[k];
colBlue[k*_RowSize + i + 1] = scoBlue[k+1];
colGreen[k*_RowSize + i] = scoGreen[k];
colGreen[k*_RowSize + i + 1] = scoGreen[k+1];
colRed[k*_RowSize + i] = scoRed[k];
colRed[k*_RowSize + i + 1] = scoRed[k+1];
}
}
for(int i = 0; i <_ColSize; i++) {
//get row vector:行スキャン
for(int j = 0; j < _RowSize*2; j+=2) {
srvBlue[j] = colBlue[i*_RowSize*2 + j];
srvBlue[j+1] = colBlue[i*_RowSize*2 + j + 1];
srvGreen[j] = colGreen[i*_RowSize*2 + j];
srvGreen[j+1] = colGreen[i*_RowSize*2 + j + 1];
srvRed[j] = colRed[i*_RowSize*2 + j];
srvRed[j+1] = colRed[i*_RowSize*2 + j + 1];
}
//bit reversal:ビット反転
reverseData(_RowSize, srvBlue);
reverseData(_RowSize, srvGreen);
reverseData(_RowSize, srvRed);
// inverse transformation:逆変換
inverse(_RowSize*2, srvBlue, sroBlue);
inverse(_RowSize*2, srvGreen, sroGreen);
inverse(_RowSize*2, srvRed, sroRed);
for(int k = 0; k < _RowSize*2; k+=2) {
outBlue[i*_RowSize*2 + k] = sroBlue[k];
outBlue[i*_RowSize*2 + k + 1] = sroBlue[k+1];
outGreen[i*_RowSize*2 + k] = sroGreen[k];
outGreen[i*_RowSize*2 + k + 1] = sroGreen[k+1];
outRed[i*_RowSize*2 + k] = sroRed[k];
outRed[i*_RowSize*2 + k + 1] = sroRed[k+1];
}
}
// ファイルに保存するため整数型の変数をメモリ空間に割り当て
int[] output = new int[_RowSize*_ColSize];
BufferedImage outImage = new BufferedImage(_RowSize, _ColSize, BufferedImage.TYPE_INT_RGB);
// RGB色彩を整数型に集約
for( int i = 0; i < _RowSize*2; i+=2 ) {
for( int j = 0; j < _ColSize; j++ ) {
output[j*_RowSize+i/2] = 0xFF000000
| ((int) ((byte)outBlue[j*_RowSize*2 + i] & 0xff) << 16)
| ((int) ((byte)outGreen[j*_RowSize*2 + i] & 0xff) << 8)
| ((int) ((byte)outRed[j*_RowSize*2 + i] & 0xff));
}
}
// クラスパス(mavenプロジェクトの場合は/target/classes/pathToPackage)にimage.pngファイルを生成
outImage.setRGB(0, 0, _RowSize, _ColSize, output, 0, _RowSize);
URL resource = FFTCPU2D.class.getResource("./");
String path = Paths.get(resource.toURI()).toFile().getAbsolutePath();
System.out.println(path+"/image.png");
ImageIO.write(outImage, "png", new File(path+"/image.png"));
}
}
Copyright 2018-2019, by Masaki Komatsu