使用傅里葉變換來處理圖像,可以濾波,去噪。 但是網絡上java實現很少,可以運行的就更少,我總結了一些代碼,並加以調試,
保證速度的情況下得以運行成功。 這些代碼僅對灰度圖驗證通過。學習之用。
調用結果可以用http://cns-alumni.bu.edu/~slehar/fourier/fourier.html#filtering 網站的圖片驗證:
輸入圖像:ff8.bmp 輸出圖像fft_result.bmp
輸入圖像:ff8.bmp 輸出圖像fft_result.bmp
所有FFT變換都已經驗證通過。 但是傅里葉逆變換沒有成功。 有高手可以指點一下。 如何使逆變換的圖片顯示出來。
FFT 類負責一維變換:
package app.fourier;
public class FFT {
// double r_data[] = null;
// double i_data[] = null;
// compute the FFT of x[], assuming its length is a power of 2
public static void fft(Complex[] src, int row, int width, Complex[] dest) {
Complex[] temp = new Complex[width];
for (int k = 0; k < width; k++) {
temp[k] = src[row * width + k];
}
temp = fft(temp);
//set output
for (int k = 0; k < width; k++) {
dest[row * width + k] = temp[k];
}
}
public static Complex[] fft(Complex[] x) {
int N = x.length;
// base case
if (N == 1)
return new Complex[] { x[0] };
// radix 2 Cooley-Tukey FFT
if (N % 2 != 0) {
throw new RuntimeException("N is not a power of 2");
}
// fft of even terms
Complex[] even = new Complex[N / 2];
for (int k = 0; k < N / 2; k++) {
even[k] = x[2 * k];
}
Complex[] q = fft(even);
// fft of odd terms
Complex[] odd = even; // reuse the array
for (int k = 0; k < N / 2; k++) {
odd[k] = x[2 * k + 1];
}
Complex[] r = fft(odd);
// combine
Complex[] y = new Complex[N];
for (int k = 0; k < N / 2; k++) {
double kth = -2 * k * Math.PI / N;
Complex wk = new Complex(Math.cos(kth), Math.sin(kth));
y[k] = q[k].plus(wk.times(r[k]));
y[k + N / 2] = q[k].minus(wk.times(r[k]));
}
return y;
}
}
Complex類是複數類:
public class Complex {
public double re; // the real part
public double im; // the imaginary part
public Complex() {
re = 0;
im = 0;
}
// create a new object with the given real and imaginary parts
public Complex(double real, double imag) {
re = real;
im = imag;
}
// return a string representation of the invoking Complex object
public String toString() {
if (im == 0) return re + "";
if (re == 0) return im + "i";
if (im < 0) return re + " - " + (-im) + "i";
return re + " + " + im + "i";
}
// return abs/modulus/magnitude and angle/phase/argument
public double abs() { return Math.hypot(re, im); } // Math.sqrt(re*re + im*im)
public double phase() { return Math.atan2(im, re); } // between -pi and pi
// return a new Complex object whose value is (this + b)
public Complex plus(Complex b) {
Complex a = this; // invoking object
double real = a.re + b.re;
double imag = a.im + b.im;
return new Complex(real, imag);
}
// return a new Complex object whose value is (this - b)
public Complex minus(Complex b) {
Complex a = this;
double real = a.re - b.re;
double imag = a.im - b.im;
return new Complex(real, imag);
}
// return a new Complex object whose value is (this * b)
public Complex times(Complex b) {
Complex a = this;
double real = a.re * b.re - a.im * b.im;
double imag = a.re * b.im + a.im * b.re;
return new Complex(real, imag);
}
// scalar multiplication
// return a new object whose value is (this * alpha)
public Complex times(double alpha) {
return new Complex(alpha * re, alpha * im);
}
// return a new Complex object whose value is the conjugate of this
public Complex conjugate() { return new Complex(re, -im); }
// return a new Complex object whose value is the reciprocal of this
public Complex reciprocal() {
double scale = re*re + im*im;
return new Complex(re / scale, -im / scale);
}
// return the real or imaginary part
public double re() { return re; }
public double im() { return im; }
// return a / b
public Complex divides(Complex b) {
Complex a = this;
return a.times(b.reciprocal());
}
// return a new Complex object whose value is the complex exponential of this
public Complex exp() {
return new Complex(Math.exp(re) * Math.cos(im), Math.exp(re) * Math.sin(im));
}
// return a new Complex object whose value is the complex sine of this
public Complex sin() {
return new Complex(Math.sin(re) * Math.cosh(im), Math.cos(re) * Math.sinh(im));
}
// return a new Complex object whose value is the complex cosine of this
public Complex cos() {
return new Complex(Math.cos(re) * Math.cosh(im), -Math.sin(re) * Math.sinh(im));
}
// return a new Complex object whose value is the complex tangent of this
public Complex tan() {
return sin().divides(cos());
}
// a static version of plus
public static Complex plus(Complex a, Complex b) {
double real = a.re + b.re;
double imag = a.im + b.im;
Complex sum = new Complex(real, imag);
return sum;
}
}
FourierTransformer 爲主調類:
import java.awt.Graphics;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.PixelGrabber;
import java.awt.image.WritableRaster;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
@SuppressWarnings("serial")
public class FourierTransformer extends JFrame{
Image im;
BufferedImage imageAuth = null;
int iw;
int ih;
int[] pixels;
int[] newPixels;
public FourierTransformer() {
try {
this.im = ImageIO.read(getClass().getResource("fft8.bmp"));
} catch (IOException e1) {
e1.printStackTrace();
}
this.iw = im.getWidth(null);
this.ih = im.getHeight(null);
pixels = new int[iw * ih];
try {
PixelGrabber pg = new PixelGrabber(im, 0, 0, iw, ih, pixels, 0, iw);
pg.grabPixels();
} catch (InterruptedException e3) {
e3.printStackTrace();
}
}
public void paint(Graphics g) {
super.paint(g);
g.drawImage(this.im, 0, 100, this.iw, this.ih, this);
if(imageAuth != null)
g.drawImage(imageAuth, 250, 100, imageAuth.getWidth(), imageAuth.getHeight(), this);
}
public static void main(String[] args){
FourierTransformer frame = new FourierTransformer();
frame.setSize(600, 500);
frame.setTitle("ImageMenu");
frame.setName("hello my dongjing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
frame.convert(frame.getGraphics());
}
public Image convert(Graphics g) {
// 賦初值
int w = 1;
int h = 1;
// 計算進行付立葉變換的寬度和高度(2的整數次方)
while (w * 2 <= iw) {
w *= 2;
}
while (h * 2 <= ih) {
h *= 2;
}
// 分配內存
Complex[] src = new Complex[h * w];
Complex[] dest = new Complex[h * w];
newPixels = new int[h * w];
// 初始化newPixels
for (int i = 0; i < h; i++) {
for (int j = 0; j < w; j++) {
newPixels[i * w + j] = pixels[i * iw + j] & 0xff;
}
}
// 初始化src,dest
for (int i = 0; i < h; i++) {
for (int j = 0; j < w; j++) {
dest[i * w + j] = new Complex();
src[i * w + j] = new Complex(newPixels[i * w + j], 0);
}
}
// 在y方向上進行快速傅立葉變換
for (int i = 0; i < h; i++) {
FFT.fft(src, i, w, dest);
}
/**
* 以下一定要進行轉換,高手指點一下原因 (^ - ^)
*/
for (int i = 0; i < h; i++) {
for (int j = 0; j < w; j++) {
src[j * h + i] = dest[i * w + j];
// System.out.println("dest " + j*h+i + ", src " + i*w+j);
}
}
// 對x方向進行傅立葉變換
for (int i = 0; i < w; i++) {
FFT.fft(src, i, h, dest);
}
/**
* 將圖像看做二維函數,圖像灰度值爲函數在相應XY處的函數值,對其進行二維快速傅里葉變換,
* 得到一個複數矩陣,將此矩陣水平循環移動半寬,垂直循環移動半高。
*/
for (int i = 0; i < h; i++) {
for (int j = 0; j < w; j++) {
double re = dest[j * h + i].re;
double im = dest[j * h + i].im;
int ii = 0, jj = 0;
int temp = (int) (Math.sqrt(re * re + im * im) / 100);
if (temp > 255) {
temp = 255;
}
if (i < h / 2) {
ii = i + h / 2;
} else {
ii = i - h / 2;
}
if (j < w / 2) {
jj = j + w / 2;
} else {
jj = j - w / 2;
}
newPixels[ii * w + jj] = temp;
}
}
imageAuth = new BufferedImage(w, h, BufferedImage.TYPE_BYTE_GRAY);
ColorModel colorModel = imageAuth.getColorModel();
WritableRaster raster = colorModel.createCompatibleWritableRaster(w, h);
raster.setPixels(0, 0, w, h, newPixels);
imageAuth.setData(raster);
try {
ImageIO.write(imageAuth, "bmp", new File("fft_result.bmp"));
} catch (IOException e) {
e.printStackTrace();
}
this.update(g);
return imageAuth;
}
}