package ij.plugin.filter;
import ij.*;
import ij.process.*;
import ij.gui.*;
import ij.measure.*;
import ij.plugin.ContrastEnhancer;
import java.awt.*;
import java.util.*;
public class FFTCustomFilter implements PlugInFilter, Measurements {
private ImagePlus imp;
private static int filterIndex = 1;
private int slice;
private int stackSize;
private boolean done;
private ImageProcessor filter;
private static boolean processStack;
private boolean padded;
private int originalWidth;
private int originalHeight;
private Rectangle rect = new Rectangle();
public int setup(String arg, ImagePlus imp) {
this.imp = imp;
if (imp==null)
{IJ.noImage(); return DONE;}
stackSize = imp.getStackSize();
if (imp.getProperty("FHT")!=null) {
IJ.error("FFT Custom Filter", "Spatial domain (non-FFT) image required");
return DONE;
}
else
return processStack?DOES_ALL+DOES_STACKS:DOES_ALL;
}
public void run(ImageProcessor ip) {
slice++;
if (done)
return;
FHT fht = newFHT(ip);
if (slice==1) {
filter = getFilter(fht.getWidth());
if (filter==null) {
done = true;
return;
}
}
((FHT)fht).transform();
customFilter(fht);
doInverseTransform(fht, ip);
if (slice==1)
ip.resetMinAndMax();
if (slice==stackSize) {
new ContrastEnhancer().stretchHistogram(imp, 0.0);
imp.updateAndDraw();
}
IJ.showProgress(1.0);
}
void doInverseTransform(FHT fht, ImageProcessor ip) {
showStatus("Inverse transform");
fht.inverseTransform();
fht.resetMinAndMax();
ImageProcessor ip2 = fht;
fht.setRoi(rect.x, rect.y, rect.width, rect.height);
ip2 = fht.crop();
int bitDepth = fht.originalBitDepth>0?fht.originalBitDepth:imp.getBitDepth();
switch (bitDepth) {
case 8: ip2 = ip2.convertToByte(true); break;
case 16: ip2 = ip2.convertToShort(true); break;
case 24:
showStatus("Setting brightness");
fht.rgb.setBrightness((FloatProcessor)ip2);
ip2 = fht.rgb;
fht.rgb = null;
break;
case 32: break;
}
ip.insert(ip2, 0, 0);
}
FHT newFHT(ImageProcessor ip) {
FHT fht;
int width = ip.getWidth();
int height = ip.getHeight();
int maxN = Math.max(width, height);
int size = 2;
while (size<1.5*maxN) size *= 2;
rect.x = (int)Math.round((size-width)/2.0);
rect.y = (int)Math.round((size-height)/2.0);
rect.width = width;
rect.height = height;
FFTFilter fftFilter = new FFTFilter();
if (ip instanceof ColorProcessor) {
showStatus("Extracting brightness");
ImageProcessor ip2 = ((ColorProcessor)ip).getBrightness();
fht = new FHT(fftFilter.tileMirror(ip2, size, size, rect.x, rect.y));
fht.rgb = (ColorProcessor)ip.duplicate(); } else
fht = new FHT(fftFilter.tileMirror(ip, size, size, rect.x, rect.y));
fht.originalWidth = originalWidth;
fht.originalHeight = originalHeight;
fht.originalBitDepth = imp.getBitDepth();
return fht;
}
void showStatus(String msg) {
if (stackSize>1)
IJ.showStatus("FFT: " + slice+"/"+stackSize);
else
IJ.showStatus(msg);
}
void customFilter(FHT fht) {
int size = fht.getWidth();
showStatus("Filtering");
fht.swapQuadrants(filter);
float[] fhtPixels = (float[])fht.getPixels();
byte[] filterPixels = (byte[])filter.getPixels();
for (int i=0; i<fhtPixels.length; i++)
fhtPixels[i] = (float)(fhtPixels[i]*(filterPixels[i]&255)/255.0);
fht.swapQuadrants(filter);
}
ImageProcessor getFilter(int size) {
int[] wList = WindowManager.getIDList();
if (wList==null || wList.length<2) {
IJ.error("FFT", "A filter (as an open image) is required.");
return null;
}
String[] titles = new String[wList.length];
for (int i=0; i<wList.length; i++) {
ImagePlus imp = WindowManager.getImage(wList[i]);
titles[i] = imp!=null?imp.getTitle():"";
}
if (filterIndex<0 || filterIndex>=titles.length)
filterIndex = 1;
GenericDialog gd = new GenericDialog("FFT Filter");
gd.addChoice("Filter:", titles, titles[filterIndex]);
if (stackSize>1)
gd.addCheckbox("Process entire stack", processStack);
gd.addHelp(IJ.URL+"/docs/menus/process.html#fft-filter");
gd.showDialog();
if (gd.wasCanceled())
return null;
filterIndex = gd.getNextChoiceIndex();
if (stackSize>1)
processStack = gd.getNextBoolean();
ImagePlus filterImp = WindowManager.getImage(wList[filterIndex]);
if (filterImp==imp) {
IJ.error("FFT", "The filter cannot be the same as the image being filtered.");
return null;
}
if (filterImp.getStackSize()>1) {
IJ.error("FFT", "The filter cannot be a stack.");
return null;
}
ImageProcessor filter = filterImp.getProcessor();
filter = filter.convertToByte(true);
filter = resizeFilter(filter, size);
return filter;
}
ImageProcessor resizeFilter(ImageProcessor ip, int maxN) {
int width = ip.getWidth();
int height = ip.getHeight();
if (width==maxN && height==maxN)
return ip;
showStatus("Scaling filter to "+ maxN + "x" + maxN);
return ip.resize(maxN, maxN);
}
}