Since JavaFX lacks an java.awt.image.ConvolveOp equivalent I started writing a made from scratch Image convolution method. My first approach was based on direct convolution in the spatial domain but it was so slow so I learned about Discrete Fourier Transform. Theory says that multiplying functions in the frequency domain is equivalent to convolving with the Fourier transform in the space domain. How ever I haven’t figure out yet how to perform a proper element wise multiplication between the input image DFT and the kernel DFT. Result image is barely recognizable and full of square artifacts.
My code goes as follows:
public static Image POC1(Image img){
int w = (int) img.getWidth(); int h = (int) img.getHeight();
WritableImage res = new WritableImage(w,h);
double[] input = new double[w * h * 2];
double[] output = new double[w * h * 2];
IntStream.range(0, w).forEach(i -> {
IntStream.range(0, h).forEach(j -> {
input[i * h * 2 + 2* j] = (img.getPixelReader().getArgb(i, j) & 0xFF) * Math.pow(-1, i + j);
});
});
DoubleFFT_2D inputFFT = new DoubleFFT_2D(w, h);
inputFFT.complexForward(input);
double[][] kernel = new double[][]{{1,4,1},{4,-20,4},{1,4,1}};
double[] kernelInput = new double[kernel.length * 2 * kernel.length];
IntStream.range(0, kernel.length).forEach(i -> {
IntStream.range(0, kernel.length).forEach(j -> {
kernelInput[i * 3 * 2 + 2* j] = kernel[i][j]/9d;
});
});
DoubleFFT_2D kernelFFT = new DoubleFFT_2D(kernel.length,kernel.length);
kernelFFT.complexForward(kernelInput);
//Aquí va F(u,v) * H(u,v)
IntStream.range(0, (input.length/2)-1).forEach(i -> {
double realImg = input[2*i]; double realKernel = kernelInput[(2*i) % kernelInput.length];
double imImg = input[2*i+1]; double imKernel = kernelInput[(2*i+1) % kernelInput.length];
output[2*i] = realImg * realKernel - imImg * imKernel;
output[2*i+1] = imImg * realKernel + realImg * imKernel;
});
inputFFT.complexInverse(output, true);
IntStream.range(0, w).forEach(i -> {
IntStream.range(0, h).forEach(j -> {
int brightness = (int) (output[i * h * 2 + 2* j] * Math.pow(-1, i + j));
brightness = brightness < 0 ? 0 : (brightness > 255 ? 255 : brightness);
res.getPixelWriter().setArgb(i, j, (255 << 24) | (brightness << 16) | (brightness << 8) | brightness);
});
});
return res;
}
Desired image (Using Java2D ConvolveOp) /// Obtained image (My Method)
Comparison
Alejandro Quiroz is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.