1 package ij.plugin.filter;
6 import ij.plugin.ContrastEnhancer;
19 private static int filterIndex = 1;
22 private int stackSize = 1;
24 private static double filterLargeDia = 40.0;
25 private static double filterSmallDia = 3.0;
26 private static int choiceIndex = 0;
27 private static String[] choices = {
"None",
"Horizontal",
"Vertical"};
28 private static String choiceDia = choices[0];
29 private static double toleranceDia = 5.0;
30 private static boolean doScalingDia =
true;
31 private static boolean saturateDia =
true;
32 private static boolean displayFilter;
33 private static boolean processStack;
43 IJ.
showMessage(
"FFT Filter",
"Spatial domain image required");
46 if (!showBandpassDialog(imp))
52 public void run(ImageProcessor ip) {
57 void filter(ImageProcessor ip) {
58 ImageProcessor ip2 = ip;
60 showStatus(
"Extracting brightness");
61 ip2 = ((ColorProcessor)ip2).getBrightness();
63 Rectangle roiRect = ip2.getRoi();
64 int maxN = Math.max(roiRect.width, roiRect.height);
66 double filterLarge = filterLargeDia / maxN;
67 double filterSmall = filterSmallDia / maxN;
68 double sharpness = (100.0 - toleranceDia) / 100.0;
69 boolean doScaling = doScalingDia;
70 boolean saturate = saturateDia;
79 while(i<1.5 * maxN) i *= 2;
82 Rectangle fitRect =
new Rectangle();
83 fitRect.x = (int) Math.round( (i - roiRect.width) / 2.0 );
84 fitRect.y = (int) Math.round( (i - roiRect.height) / 2.0 );
85 fitRect.width = roiRect.width;
86 fitRect.height = roiRect.height;
90 showStatus(
"Pad to "+i+
"x"+i);
91 ip2 = tileMirror(ip2, i, i, fitRect.x, fitRect.y);
95 showStatus(i+
"x"+i+
" forward transform");
96 FHT fht =
new FHT(ip2);
97 fht.setShowProgress(
false);
104 showStatus(
"Filter in frequency domain");
105 filterLargeSmall(fht, filterLarge, filterSmall, choiceIndex, sharpness);
110 showStatus(
"Inverse transform");
111 fht.inverseTransform();
116 showStatus(
"Crop and convert to original type");
128 case 8: ip2 = ip2.convertToByte(doScaling);
break;
129 case 16: ip2 = ip2.convertToShort(doScaling);
break;
132 showStatus(
"Setting brightness");
133 ((ColorProcessor)ip).setBrightness((FloatProcessor)ip2);
141 ip.copyBits(ip2, roiRect.x, roiRect.y, Blitter.COPY);
145 IJ.showProgress(20,20);
148 void showStatus(String msg) {
149 if (stackSize>1 && processStack)
150 IJ.showStatus(
"FFT Filter: "+slice+
"/"+stackSize);
157 ImageProcessor tileMirror(ImageProcessor ip,
int width,
int height,
int x,
int y) {
159 if (x < 0 || x > (width -1) || y < 0 || y > (height -1)) {
160 IJ.error(
"Image to be tiled is out of bounds.");
164 ImageProcessor ipout = ip.createProcessor(width, height);
166 ImageProcessor ip2 = ip.crop();
167 int w2 = ip2.getWidth();
168 int h2 = ip2.getHeight();
171 int i1 = (int) Math.ceil(x / (
double) w2);
172 int i2 = (int) Math.ceil( (width - x) / (
double) w2);
173 int j1 = (int) Math.ceil(y / (
double) h2);
174 int j2 = (int) Math.ceil( (height - y) / (
double) h2);
178 ip2.flipHorizontal();
182 for (
int i=-i1; i<i2; i += 2) {
183 for (
int j=-j1; j<j2; j += 2) {
184 ipout.insert(ip2, x-i*w2, y-j*h2);
188 ip2.flipHorizontal();
189 for (
int i=-i1+1; i<i2; i += 2) {
190 for (
int j=-j1; j<j2; j += 2) {
191 ipout.insert(ip2, x-i*w2, y-j*h2);
196 for (
int i=-i1+1; i<i2; i += 2) {
197 for (
int j=-j1+1; j<j2; j += 2) {
198 ipout.insert(ip2, x-i*w2, y-j*h2);
202 ip2.flipHorizontal();
203 for (
int i=-i1; i<i2; i += 2) {
204 for (
int j=-j1+1; j<j2; j += 2) {
205 ipout.insert(ip2, x-i*w2, y-j*h2);
222 void filterLargeSmall(ImageProcessor ip,
double filterLarge,
double filterSmall,
int stripesHorVert,
double scaleStripes) {
224 int maxN = ip.getWidth();
226 float[] fht = (
float[])ip.getPixels();
227 float[] filter =
new float[maxN*maxN];
228 for (
int i=0; i<maxN*maxN; i++)
246 double scaleLarge = filterLarge*filterLarge;
247 double scaleSmall = filterSmall*filterSmall;
248 scaleStripes = scaleStripes*scaleStripes;
252 for (
int j=1; j<maxN/2; j++) {
254 backrow = (maxN-j)*maxN;
255 rowFactLarge = (float) Math.exp(-(j*j) * scaleLarge);
256 rowFactSmall = (float) Math.exp(-(j*j) * scaleSmall);
260 for (col=1; col<maxN/2; col++){
262 colFactLarge = (float) Math.exp(- (col*col) * scaleLarge);
263 colFactSmall = (float) Math.exp(- (col*col) * scaleSmall);
264 factor = (1 - rowFactLarge*colFactLarge) * rowFactSmall*colFactSmall;
265 switch (stripesHorVert) {
266 case 1: factor *= (1 - (float) Math.exp(- (col*col) * scaleStripes));
break;
267 case 2: factor *= (1 - (float) Math.exp(- (j*j) * scaleStripes));
270 fht[col+row] *= factor;
271 fht[col+backrow] *= factor;
272 fht[backcol+row] *= factor;
273 fht[backcol+backrow] *= factor;
274 filter[col+row] *= factor;
275 filter[col+backrow] *= factor;
276 filter[backcol+row] *= factor;
277 filter[backcol+backrow] *= factor;
282 int rowmid = maxN * (maxN/2);
283 rowFactLarge = (float) Math.exp(- (maxN/2)*(maxN/2) * scaleLarge);
284 rowFactSmall = (float) Math.exp(- (maxN/2)*(maxN/2) * scaleSmall);
285 factStripes = (float) Math.exp(- (maxN/2)*(maxN/2) * scaleStripes);
287 fht[maxN/2] *= (1 - rowFactLarge) * rowFactSmall;
288 fht[rowmid] *= (1 - rowFactLarge) * rowFactSmall;
289 fht[maxN/2 + rowmid] *= (1 - rowFactLarge*rowFactLarge) * rowFactSmall*rowFactSmall;
290 filter[maxN/2] *= (1 - rowFactLarge) * rowFactSmall;
291 filter[rowmid] *= (1 - rowFactLarge) * rowFactSmall;
292 filter[maxN/2 + rowmid] *= (1 - rowFactLarge*rowFactLarge) * rowFactSmall*rowFactSmall;
294 switch (stripesHorVert) {
295 case 1: fht[maxN/2] *= (1 - factStripes);
297 fht[maxN/2 + rowmid] *= (1 - factStripes);
298 filter[maxN/2] *= (1 - factStripes);
300 filter[maxN/2 + rowmid] *= (1 - factStripes);
302 case 2: fht[maxN/2] = 0;
303 fht[rowmid] *= (1 - factStripes);
304 fht[maxN/2 + rowmid] *= (1 - factStripes);
306 filter[rowmid] *= (1 - factStripes);
307 filter[maxN/2 + rowmid] *= (1 - factStripes);
312 rowFactLarge = (float) Math.exp(- (maxN/2)*(maxN/2) * scaleLarge);
313 rowFactSmall = (float) Math.exp(- (maxN/2)*(maxN/2) * scaleSmall);
314 for (col=1; col<maxN/2; col++){
316 colFactLarge = (float) Math.exp(- (col*col) * scaleLarge);
317 colFactSmall = (float) Math.exp(- (col*col) * scaleSmall);
319 switch (stripesHorVert) {
321 fht[col] *= (1 - colFactLarge) * colFactSmall;
322 fht[backcol] *= (1 - colFactLarge) * colFactSmall;
323 fht[col+rowmid] *= (1 - colFactLarge*rowFactLarge) * colFactSmall*rowFactSmall;
324 fht[backcol+rowmid] *= (1 - colFactLarge*rowFactLarge) * colFactSmall*rowFactSmall;
325 filter[col] *= (1 - colFactLarge) * colFactSmall;
326 filter[backcol] *= (1 - colFactLarge) * colFactSmall;
327 filter[col+rowmid] *= (1 - colFactLarge*rowFactLarge) * colFactSmall*rowFactSmall;
328 filter[backcol+rowmid] *= (1 - colFactLarge*rowFactLarge) * colFactSmall*rowFactSmall;
331 factStripes = (float) Math.exp(- (col*col) * scaleStripes);
332 fht[col] *= (1 - colFactLarge) * colFactSmall * (1 - factStripes);
333 fht[backcol] *= (1 - colFactLarge) * colFactSmall * (1 - factStripes);
334 fht[col+rowmid] *= (1 - colFactLarge*rowFactLarge) * colFactSmall*rowFactSmall * (1 - factStripes);
335 fht[backcol+rowmid] *= (1 - colFactLarge*rowFactLarge) * colFactSmall*rowFactSmall * (1 - factStripes);
336 filter[col] *= (1 - colFactLarge) * colFactSmall * (1 - factStripes);
337 filter[backcol] *= (1 - colFactLarge) * colFactSmall * (1 - factStripes);
338 filter[col+rowmid] *= (1 - colFactLarge*rowFactLarge) * colFactSmall*rowFactSmall * (1 - factStripes);
339 filter[backcol+rowmid] *= (1 - colFactLarge*rowFactLarge) * colFactSmall*rowFactSmall * (1 - factStripes);
342 factStripes = (float) Math.exp(- (maxN/2)*(maxN/2) * scaleStripes);
345 fht[col+rowmid] *= (1 - colFactLarge*rowFactLarge) * colFactSmall*rowFactSmall * (1 - factStripes);
346 fht[backcol+rowmid] *= (1 - colFactLarge*rowFactLarge) * colFactSmall*rowFactSmall * (1 - factStripes);
349 filter[col+rowmid] *= (1 - colFactLarge*rowFactLarge) * colFactSmall*rowFactSmall * (1 - factStripes);
350 filter[backcol+rowmid] *= (1 - colFactLarge*rowFactLarge) * colFactSmall*rowFactSmall * (1 - factStripes);
355 colFactLarge = (float) Math.exp(- (maxN/2)*(maxN/2) * scaleLarge);
356 colFactSmall = (float) Math.exp(- (maxN/2)*(maxN/2) * scaleSmall);
357 for (
int j=1; j<maxN/2; j++) {
359 backrow = (maxN-j)*maxN;
360 rowFactLarge = (float) Math.exp(- (j*j) * scaleLarge);
361 rowFactSmall = (float) Math.exp(- (j*j) * scaleSmall);
363 switch (stripesHorVert) {
365 fht[row] *= (1 - rowFactLarge) * rowFactSmall;
366 fht[backrow] *= (1 - rowFactLarge) * rowFactSmall;
367 fht[row+maxN/2] *= (1 - rowFactLarge*colFactLarge) * rowFactSmall*colFactSmall;
368 fht[backrow+maxN/2] *= (1 - rowFactLarge*colFactLarge) * rowFactSmall*colFactSmall;
369 filter[row] *= (1 - rowFactLarge) * rowFactSmall;
370 filter[backrow] *= (1 - rowFactLarge) * rowFactSmall;
371 filter[row+maxN/2] *= (1 - rowFactLarge*colFactLarge) * rowFactSmall*colFactSmall;
372 filter[backrow+maxN/2] *= (1 - rowFactLarge*colFactLarge) * rowFactSmall*colFactSmall;
375 factStripes = (float) Math.exp(- (maxN/2)*(maxN/2) * scaleStripes);
378 fht[row+maxN/2] *= (1 - rowFactLarge*colFactLarge) * rowFactSmall*colFactSmall * (1 - factStripes);
379 fht[backrow+maxN/2] *= (1 - rowFactLarge*colFactLarge) * rowFactSmall*colFactSmall * (1 - factStripes);
382 filter[row+maxN/2] *= (1 - rowFactLarge*colFactLarge) * rowFactSmall*colFactSmall * (1 - factStripes);
383 filter[backrow+maxN/2] *= (1 - rowFactLarge*colFactLarge) * rowFactSmall*colFactSmall * (1 - factStripes);
386 factStripes = (float) Math.exp(- (j*j) * scaleStripes);
387 fht[row] *= (1 - rowFactLarge) * rowFactSmall * (1 - factStripes);
388 fht[backrow] *= (1 - rowFactLarge) * rowFactSmall * (1 - factStripes);
389 fht[row+maxN/2] *= (1 - rowFactLarge*colFactLarge) * rowFactSmall*colFactSmall * (1 - factStripes);
390 fht[backrow+maxN/2] *= (1 - rowFactLarge*colFactLarge) * rowFactSmall*colFactSmall * (1 - factStripes);
391 filter[row] *= (1 - rowFactLarge) * rowFactSmall * (1 - factStripes);
392 filter[backrow] *= (1 - rowFactLarge) * rowFactSmall * (1 - factStripes);
393 filter[row+maxN/2] *= (1 - rowFactLarge*colFactLarge) * rowFactSmall*colFactSmall * (1 - factStripes);
394 filter[backrow+maxN/2] *= (1 - rowFactLarge*colFactLarge) * rowFactSmall*colFactSmall * (1 - factStripes);
397 if (displayFilter && slice==1) {
398 FHT f =
new FHT(
new FloatProcessor(maxN, maxN, filter, null));
400 new ImagePlus(
"Filter", f).show();
404 boolean showBandpassDialog(ImagePlus imp) {
405 GenericDialog gd =
new GenericDialog(
"FFT Bandpass Filter");
406 gd.addNumericField(
"Filter_Large Structures Down to", filterLargeDia, 0, 4,
"pixels");
407 gd.addNumericField(
"Filter_Small Structures Up to", filterSmallDia, 0, 4,
"pixels");
408 gd.addChoice(
"Suppress Stripes:", choices, choiceDia);
409 gd.addNumericField(
"Tolerance of Direction:", toleranceDia, 0, 2,
"%");
410 gd.addCheckbox(
"Autoscale After Filtering", doScalingDia);
411 gd.addCheckbox(
"Saturate Image when Autoscaling", saturateDia);
412 gd.addCheckbox(
"Display Filter", displayFilter);
414 gd.addCheckbox(
"Process Entire Stack", processStack);
418 if(gd.invalidNumber()) {
419 IJ.showMessage(
"Error",
"Invalid input number");
422 filterLargeDia = gd.getNextNumber();
423 filterSmallDia = gd.getNextNumber();
424 choiceIndex = gd.getNextChoiceIndex();
425 choiceDia = choices[choiceIndex];
426 toleranceDia = gd.getNextNumber();
427 doScalingDia = gd.getNextBoolean();
428 saturateDia = gd.getNextBoolean();
429 displayFilter = gd.getNextBoolean();
431 processStack = gd.getNextBoolean();