BinaryFilters.java

package com.github.celldynamics.quimp.plugin.randomwalk;

import ij.IJ;
import ij.ImagePlus;
import ij.process.BinaryProcessor;
import ij.process.ImageProcessor;

/**
 * Provide sets of binary filters.
 * 
 * <p>Returned image is converted to BinaryProcessor and it is copy of input image.
 * 
 * @author p.baniukiewicz
 *
 */
public abstract class BinaryFilters {

  /**
   * Filters available in this class.
   * 
   * @author p.baniukiewicz
   *
   */
  public static enum Filters {
    /**
     * Do nothing.
     */
    NONE,
    /**
     * Simple filtering.
     * 
     * @see SimpleMorpho
     */
    SIMPLE,
    /**
     * Small median filter.
     * 
     * @see MedianMorpho
     */
    MEDIAN
  }

  /**
   * Available binary operations.
   * 
   * @author p.baniukiewicz
   * @see BinaryFilters#iterateMorphological(ImageProcessor, MorphoOperations, double)
   */
  public static enum MorphoOperations {
    /**
     * Denote ERODE operation.
     */
    ERODE,
    /**
     * Denote DILATE operation.
     */
    DILATE,
    /**
     * Run IJ open macro.
     */
    IJOPEN
  }

  /**
   * Factory of filters supported by this class.
   * 
   * @param type type of demanded filter
   * @return filter instance
   * @see Filters
   */
  public static BinaryFilters getFilter(Filters type) {
    switch (type) {
      case NONE:
        return new MedianMorpho.EmptyMorpho();
      case SIMPLE:
        return new MedianMorpho.SimpleMorpho();
      case MEDIAN:
        return new MedianMorpho.MedianMorpho();
      default:
        throw new IllegalArgumentException("Unknown filter type");
    }
  }

  /**
   * Filter image using morphological operations.
   * 
   * <p>Input image is copied and converted to BinaryProcessor.
   * 
   * @param input image to process (not modified)
   * @return processed image
   */
  abstract ImageProcessor filter(ImageProcessor input);

  /**
   * Perform median filtering with radius 2 on image.
   * 
   * @author p.baniukiewicz
   *
   */
  public static class MedianMorpho extends BinaryFilters {

    /*
     * (non-Javadoc)
     * 
     * @see com.github.celldynamics.quimp.plugin.randomwalk.BinaryFilters#filter(ij.process.
     * ImageProcessor)
     */
    @Override
    ImageProcessor filter(ImageProcessor input) {
      BinaryProcessor bp = getBinaryProcessor(input);
      bp.medianFilter();
      return bp;
    }

  }

  /**
   * Filter image using 1 iteration opening.
   * 
   * @author p.baniukiewicz
   *
   */
  public static class SimpleMorpho extends BinaryFilters {

    /*
     * (non-Javadoc)
     * 
     * @see BinaryFilters#filter(ij.process.ImageProcessor)
     */
    @Override
    ImageProcessor filter(ImageProcessor input) {
      return iterateMorphological(getBinaryProcessor(input), MorphoOperations.IJOPEN, 1);
    }

  }

  /**
   * Dummy filter that does nothing.
   * 
   * @author p.baniukiewicz
   *
   */
  public static class EmptyMorpho extends BinaryFilters {

    /*
     * (non-Javadoc)
     * 
     * @see
     * com.github.celldynamics.quimp.plugin.randomwalk.BinaryFilters#filter(ij.process.
     * ImageProcessor)
     */
    @Override
    ImageProcessor filter(ImageProcessor input) {
      BinaryProcessor ret = getBinaryProcessor(input);
      return ret;
    }

  }

  /**
   * Converts input image to BinaryProcessor duplicating it.
   * 
   * @param input image to convert
   * @return Image converted to BinaryProcessor
   */
  public static BinaryProcessor getBinaryProcessor(ImageProcessor input) {
    return new BinaryProcessor(input.duplicate().convertToByteProcessor());
  }

  /**
   * Run morphological operation on input image.
   * 
   * @param ip Image to process, must be IJ binary (8-bit-image with only 0 and 255)
   * @param oper Operator
   * @param iter number of iterations
   * @return Modified image (copy)
   */
  public static ImageProcessor iterateMorphological(ImageProcessor ip, MorphoOperations oper,
          double iter) {
    BinaryProcessor result;
    switch (oper) {
      case ERODE:
        result = getBinaryProcessor(ip);
        for (int i = 0; i < iter; i++) {
          result.erode(1, 0); // first param influence precision
        }
        break;
      case DILATE:
        result = getBinaryProcessor(ip);
        for (int i = 0; i < iter; i++) {
          result.dilate(1, 0);
        }
        break;
      case IJOPEN:
        // this approach duplicate image internally
        IJ.run("Options...", "iterations=" + iter + " count=1 black do=Nothing");
        IJ.run(new ImagePlus("before", ip), "Open", "");
        result = (BinaryProcessor) ip;
        break;
      default:
        throw new IllegalArgumentException("Binary operation not supported");
    }
    return result;
  }

}