使用傅里叶变换来处理图像,可以滤波,去噪。 但是网络上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;
- }
- }