Package ij.plugin.filter

Source Code of ij.plugin.filter.Convolver

package ij.plugin.filter;
import ij.*;
import ij.process.*;
import ij.gui.*;
import ij.io.*;
import ij.plugin.TextReader;
import ij.plugin.frame.Recorder;
import ij.util.Tools;
import java.awt.*;
import java.util.*;
import java.awt.event.*;
import java.io.*;

/** This plugin convolves images using user user defined kernels. */
public class Convolver implements ExtendedPlugInFilter, DialogListener, ActionListener {

  private ImagePlus imp;
  private int kw, kh;
  private boolean canceled;
  private float[] kernel;
  private boolean isLineRoi;
  private Button open, save;
  private GenericDialog gd;
  private boolean normalize = true;
  private int nSlices;
  private int flags = DOES_ALL|CONVERT_TO_FLOAT|SUPPORTS_MASKING|KEEP_PREVIEW|FINAL_PROCESSING|SNAPSHOT;
  private int nPasses = 1;
  private boolean kernelError;
  private PlugInFilterRunner pfr;
  private Thread mainThread;
  private int pass;

 
  static String kernelText = "-1 -1 -1 -1 -1\n-1 -1 -1 -1 -1\n-1 -1 24 -1 -1\n-1 -1 -1 -1 -1\n-1 -1 -1 -1 -1\n";
  static boolean normalizeFlag = true;

  public int setup(String arg, ImagePlus imp) {
     this.imp = imp;
        mainThread = Thread.currentThread();
    if (imp==null)
      {IJ.noImage(); return DONE;}
    if (arg.equals("final")&&imp.getRoi()==null) {
      imp.getProcessor().resetMinAndMax();
      imp.updateAndDraw();
      return DONE;
    }
    IJ.resetEscape();
    Roi roi = imp.getRoi();
    isLineRoi= roi!=null && roi.isLine();
    nSlices = imp.getStackSize();
    if (imp.getStackSize()==1)
      flags |= PARALLELIZE_IMAGES;
    else
      flags |= PARALLELIZE_STACKS;
    imp.startTiming();
    return flags;
  }

  public void run(ImageProcessor ip) {
    if (canceled) return;
    if (isLineRoi) ip.resetRoi();
    if (!kernelError)
      convolve(ip, kernel, kw, kh);
  }
 
  public int showDialog(ImagePlus imp, String command, PlugInFilterRunner pfr) {
    gd = new GenericDialog("Convolver...", IJ.getInstance());
    gd.addTextAreas(kernelText, null, 10, 30);
    gd.addPanel(makeButtonPanel(gd));
    gd.addCheckbox("Normalize Kernel", normalizeFlag);
        gd.addPreviewCheckbox(pfr);
        gd.addDialogListener(this);
    gd.showDialog();
    if (gd.wasCanceled()) return DONE;
        this.pfr = pfr;
    return IJ.setupDialog(imp, flags);
  }

    public boolean dialogItemChanged(GenericDialog gd, AWTEvent e) {
    kernelText = gd.getNextText();
    normalizeFlag = gd.getNextBoolean();
    normalize = normalizeFlag;
    kernelError = !decodeKernel(kernelText);
    if (!kernelError) {
      IJ.showStatus("Convolve: "+kw+"x"+kh+" kernel");
      return true;
    } else
      return !gd.getPreviewCheckbox().getState();
    }
   
    boolean decodeKernel(String text) {
      if (Macro.getOptions()!=null && !hasNewLine(text))
        return decodeSquareKernel(text);
    String[] rows = Tools.split(text, "\n");
    kh = rows.length;
    if (kh==0) return false;
    String[] values = Tools.split(rows[0]);
    kw = values.length;
    kernel = new float[kw*kh];
    boolean done = gd.wasOKed();
    int i = 0;
    for (int y=0; y<kh; y++) {
      values = Tools.split(rows[y])
      if (values.length!=kw) {
        String err = "Row "+(y+1)+" is not the same length as the first row";
        if (done)
          IJ.error("Convolver", err);
        else
          IJ.showStatus(err);
        return false;
      }
      for (int x=0; x<kw; x++)
        kernel[i++] = (float)Tools.parseDouble(values[x], 0.0);
    }
    if ((kw&1)!=1 || (kh&1)!=1) {
      String err = "Kernel must have odd width and height. This one is "+kw+"x"+kh+".";
      if (done)
        IJ.error("Convolver", err);
      else
        IJ.showStatus(err);
      return false;
    }
    return true;
    }
   
  boolean hasNewLine(String text) {
    for (int i=0; i<text.length(); i++) {
      if (text.charAt(i)=='\n') return true;
    }
    return false;
  }

    boolean decodeSquareKernel(String text) {
    String[] values = Tools.split(text);
    int n = values.length;
    kw = (int)Math.sqrt(n);
    kh = kw;
    n = kw*kh;
    kernel = new float[n];
    for (int i=0; i<n; i++)
      kernel[i] = (float)Tools.parseDouble(values[i]);
    if (kw>=3 && (kw&1)==1) {
      StringBuffer sb = new StringBuffer();
      int i = 0;
      for (int y=0; y<kh; y++) {
        for (int x=0; x<kw; x++) {
          sb.append(""+kernel[i++]);
          if (x<kw-1) sb.append(" ");
        }
        sb.append("\n");
      }
      kernelText = new String(sb);
      gd.getTextArea1().setText(new String(sb));
      return true;
    } else {
      IJ.error("Kernel must be square with odd width. This one is "+kw+"x"+kh+".");
      return false;
    }
  }
 
  /** Creates a panel containing "Save...", "Save..." and "Preview" buttons. */
  Panel makeButtonPanel(GenericDialog gd) {
    Panel buttons = new Panel();
      buttons.setLayout(new FlowLayout(FlowLayout.CENTER, 5, 0));
    open = new Button("Open...");
    open.addActionListener(this);
    buttons.add(open);
    save = new Button("Save...");
    save.addActionListener(this);
    buttons.add(save);
    return buttons;
  }

  /** Convolves <code>ip</code> with a kernel of width <code>kw</code> and
    height <code>kh</code>. Returns false if the user cancels the operation. */
  public boolean convolve(ImageProcessor ip, float[] kernel, int kw, int kh) {
    if (canceled || kw*kh!=kernel.length) return false;
    if ((kw&1)!=1 || (kh&1)!=1)
      throw new IllegalArgumentException("Kernel width or height not odd ("+kw+"x"+kh+")");
    boolean notFloat = !(ip instanceof FloatProcessor);
    ImageProcessor ip2 = ip;
    if (notFloat) {
      if (ip2 instanceof ColorProcessor)
        throw new IllegalArgumentException("RGB images not supported");
      ip2 = ip2.convertToFloat();
    }
    if (kw==1 || kh==1)
      convolveFloat1D(ip2, kernel, kw, kh);
    else
      convolveFloat(ip2, kernel, kw, kh);
    if (notFloat) {
      if (ip instanceof ByteProcessor)
        ip2 = ip2.convertToByte(false);
      else
        ip2 = ip2.convertToShort(false);
      ip.setPixels(ip2.getPixels());
    }
    return !canceled;
  }
 
  public void setNormalize(boolean normalizeKernel) {
    normalize = normalizeKernel;
  }
 
  /** Convolves the float image <code>ip</code> with a kernel of width
    <code>kw</code> and height <code>kh</code>. Returns false if
    the user cancels the operation by pressing 'Esc'. */
  public boolean convolveFloat(ImageProcessor ip, float[] kernel, int kw, int kh) {
    if (!(ip instanceof FloatProcessor))
      throw new IllegalArgumentException("FloatProcessor required");
    if (canceled) return false;
    int width = ip.getWidth();
    int height = ip.getHeight();
    Rectangle r = ip.getRoi();
    //boolean nonRectRoi = ip.getMask()!=null;
    //if (nonRectRoi)
    //  ip.snapshot();
    int x1 = r.x;
    int y1 = r.y;
    int x2 = x1 + r.width;
    int y2 = y1 + r.height;
    int uc = kw/2;   
    int vc = kh/2;
    float[] pixels = (float[])ip.getPixels();
    float[] pixels2 = (float[])ip.getSnapshotPixels();
    if (pixels2==null)
      pixels2 = (float[])ip.getPixelsCopy();
    double scale = getScale(kernel);
        Thread thread = Thread.currentThread();
        boolean isMainThread = thread==mainThread || thread.getName().indexOf("Preview")!=-1;
        if (isMainThread) pass++;
    double sum;
    int offset, i;
    boolean edgePixel;
    int xedge = width-uc;
    int yedge = height-vc;
    long lastTime = System.currentTimeMillis();
    for(int y=y1; y<y2; y++) {
      long time = System.currentTimeMillis();
      if (time-lastTime>100) {
        lastTime = time;
        if (thread.isInterrupted()) return false;
        if (isMainThread) {
          if (IJ.escapePressed()) {
            canceled = true;
            ip.reset();
            ImageProcessor originalIp = imp.getProcessor();
            if (originalIp.getNChannels() > 1)
              originalIp.reset();
            return false;
          }
          showProgress((y-y1)/(double)(y2-y1));
        }
      }
      for(int x=x1; x<x2; x++) {
        if (canceled) return false;
        sum = 0.0;
        i = 0;
        edgePixel = y<vc || y>=yedge || x<uc || x>=xedge;
        for(int v=-vc; v <= vc; v++) {
          offset = x+(y+v)*width;
          for(int u = -uc; u <= uc; u++) {
            if (edgePixel) {
               if (i>=kernel.length) // work around for JIT compiler bug on Linux
                 IJ.log("kernel index error: "+i);
              sum += getPixel(x+u, y+v, pixels2, width, height)*kernel[i++];
            } else
              sum += pixels2[offset+u]*kernel[i++];
          }
          }
        pixels[x+y*width] = (float)(sum*scale);
      }
      }
    //if (nonRectRoi)
    //  ip.reset(ip.getMask());
       return true;
      }

  /** Convolves the image <code>ip</code> with a kernel of width
    <code>kw</code> and height <code>kh</code>. */
  void convolveFloat1D(ImageProcessor ip, float[] kernel, int kw, int kh) {
    if (!(ip instanceof FloatProcessor))
      throw new IllegalArgumentException("FloatProcessor required");
    int width = ip.getWidth();
    int height = ip.getHeight();
    Rectangle r = ip.getRoi();
    int x1 = r.x;
    int y1 = r.y;
    int x2 = x1 + r.width;
    int y2 = y1 + r.height;
    int uc = kw/2;   
    int vc = kh/2;
    float[] pixels = (float[])ip.getPixels();
    float[] pixels2 = (float[])ip.getSnapshotPixels();
    if (pixels2==null)
      pixels2 = (float[])ip.getPixelsCopy();
    double scale = getScale(kernel);
    boolean vertical = kw==1;

    double sum;
    int offset, i;
    boolean edgePixel;
    int xedge = width-uc;
    int yedge = height-vc;
    for(int y=y1; y<y2; y++) {
      for(int x=x1; x<x2; x++) {
        sum = 0.0;
        i = 0;
        if (vertical) {
          edgePixel = y<vc || y>=yedge;
          offset = x+(y-vc)*width;
          for(int v=-vc; v<=vc; v++) {
            if (edgePixel)
              sum += getPixel(x+uc, y+v, pixels2, width, height)*kernel[i++];
            else
              sum += pixels2[offset+uc]*kernel[i++];
            offset += width;
          }
        } else {
          edgePixel = x<uc || x>=xedge;
          offset = x+(y-vc)*width;
          for(int u = -uc; u<=uc; u++) {
            if (edgePixel)
              sum += getPixel(x+u, y+vc, pixels2, width, height)*kernel[i++];
            else
              sum += pixels2[offset+u]*kernel[i++];
          }
        }
        pixels[x+y*width] = (float)(sum*scale);
      }
      }
    }
     
  double getScale(float[] kernel) {
    double scale = 1.0;
    if (normalize) {
      double sum = 0.0;
      for (int i=0; i<kernel.length; i++)
        sum += kernel[i];
      if (sum!=0.0)
        scale = (float)(1.0/sum);
    }
    return scale;
  }

  private float getPixel(int x, int y, float[] pixels, int width, int height) {
    if (x<=0) x = 0;
    if (x>=width) x = width-1;
    if (y<=0) y = 0;
    if (y>=height) y = height-1;
    return pixels[x+y*width];
  }
 
  void save() {
    TextArea ta1 = gd.getTextArea1();
    ta1.selectAll();
    String text = ta1.getText();
    ta1.select(0, 0);
    if (text==null || text.length()==0)
      return;
    text += "\n";
    SaveDialog sd = new SaveDialog("Save as Text...", "kernel", ".txt");
    String name = sd.getFileName();
    if (name == null)
      return;
    String directory = sd.getDirectory();
    PrintWriter pw = null;
    try {
      FileOutputStream fos = new FileOutputStream(directory+name);
      BufferedOutputStream bos = new BufferedOutputStream(fos);
      pw = new PrintWriter(bos);
    }
    catch (IOException e) {
      IJ.error("" + e);
      return;
    }
    IJ.wait(250)// give system time to redraw ImageJ window
    pw.print(text);
    pw.close();
  }
 
  void open() {
    OpenDialog od = new OpenDialog("Open Kernel...", "");
    String directory = od.getDirectory();
    String name = od.getFileName();
    if (name==null)
      return;
    String path = directory + name;
    TextReader tr = new TextReader();
    ImageProcessor ip = tr.open(path);
    if (ip==null)
      return;
    int width = ip.getWidth();
    int height = ip.getHeight();
    if ((width&1)!=1 || (height&1)!=1) {
      IJ.error("Convolver", "Kernel must be have odd width and height");
      return;
    }
    StringBuffer sb = new StringBuffer();
    boolean integers = true;
    for (int y=0; y<height; y++) {
      for (int x=0; x<width; x++) {
        double v = ip.getPixelValue(x, y);
        if ((int)v!=v)
          integers = false;
      }
    }
    for (int y=0; y<height; y++) {
      for (int x=0; x<width; x++) {
        if (x!=0) sb.append(" ");
        double v = ip.getPixelValue(x, y);
        if (integers)
          sb.append(IJ.d2s(ip.getPixelValue(x, y),0));
        else
          sb.append(""+ip.getPixelValue(x, y));
      }
      if (y!=height-1)
        sb.append("\n");
    }
    gd.getTextArea1().setText(new String(sb));
  }
 
  public void setNPasses(int nPasses) {
    this.nPasses = nPasses;
    pass = 0;
  }

    private void showProgress(double percent) {
        percent = (double)(pass-1)/nPasses + percent/nPasses;
        IJ.showProgress(percent);
    }

  public void actionPerformed(ActionEvent e) {
    Object source = e.getSource();
    Recorder.disablePathRecording();
    if (source==save)
      save();
    else if (source==open)
      open();
  }

}

TOP

Related Classes of ij.plugin.filter.Convolver

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.