Package com.lightcrafts.mediax.jai

Source Code of com.lightcrafts.mediax.jai.ROI

/*
* $RCSfile: ROI.java,v $
*
* Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
*
* Use is subject to license terms.
*
* $Revision: 1.1 $
* $Date: 2005/02/11 04:57:18 $
* $State: Exp $
*/
package com.lightcrafts.mediax.jai;

import com.lightcrafts.media.jai.util.ImageUtil;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.awt.image.DataBuffer;
import java.awt.image.MultiPixelPackedSampleModel;
import java.awt.image.RenderedImage;
import java.awt.image.SampleModel;
import java.awt.image.renderable.ParameterBlock;
import java.awt.image.renderable.RenderedImageFactory;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.ListIterator;
import java.util.Vector;
import com.lightcrafts.mediax.jai.iterator.RandomIter;
import com.lightcrafts.mediax.jai.iterator.RandomIterFactory;
import com.lightcrafts.mediax.jai.remote.SerializableState;
import com.lightcrafts.mediax.jai.remote.SerializerFactory;

/**
* The parent class for representations of a region of interest of an
* image (currently only single band images with integral data types
* are supported).
* This class represents region information in image form, and
* can thus be used as a fallback where a <code>Shape</code>
* representation is unavailable.  Where possible, subclasses such as
* ROIShape are used since they provide a more compact means of
* storage for large regions.
*
* <p> The getAsShape() method may be called optimistically on any
* instance of ROI; however, it may return null to indicate that a
* <code>Shape</code> representation of the ROI is not available.  In
* this case, getAsImage() should be called as a fallback.
*
* <p> Inclusion and exclusion of pixels is defined by a threshold value.
* Pixel values greater than or equal to the threshold indicate inclusion.
*
*/
public class ROI implements Serializable {

    /** A RandomIter used to grab pixels from the ROI. */
    private transient RandomIter iter = null;

    /** The <code>PlanarImage</code> representation of the ROI. */
    transient PlanarImage theImage = null;

    /** The inclusion/exclusion threshold of the ROI. */
    int threshold = 127;

    /**
     * Merge a <code>LinkedList</code> of <code>Rectangle</code>s
     * representing run lengths of pixels in the ROI into a minimal
     * list wherein vertically abutting <code>Rectangle</code>s are
     * merged. The operation is effected in place.
     *
     * @param rectList The list of run length <code>Rectangle</code>s.
     * @throws IllegalArgumentException if rectList is null.
     * @return The merged list.
     */
    protected static LinkedList mergeRunLengthList(LinkedList rectList) {

        if ( rectList == null ) {
            throw new IllegalArgumentException(JaiI18N.getString("Generic0"));
        }

        // Merge the run length rectangles if more than one was detected.
        if (rectList.size() > 1) {
            // Traverse the list sequentially merging all subsequent
            // vertically abutting Rectangles with the same abscissa
            // origin and width with the current starting Rectangle.
            for (int mergeIndex = 0;
                mergeIndex < rectList.size() - 1;
                mergeIndex++) {

                ListIterator rectIter = rectList.listIterator(mergeIndex);
                Rectangle mergeRect = (Rectangle)rectIter.next();

                while (rectIter.hasNext()) {
                    Rectangle runRect = (Rectangle)rectIter.next();

                    // Calculate ordinate value of abutting rectangle.
                    int abuttingY = mergeRect.y + mergeRect.height;

                    if (runRect.y == abuttingY &&
                       runRect.x == mergeRect.x &&
                       runRect.width == mergeRect.width) {
                        mergeRect =
                            new Rectangle(mergeRect.x, mergeRect.y,
                                          mergeRect.width,
                                          mergeRect.height + runRect.height);

                        // Remove "runRect" from the list.
                        rectIter.remove();

                        // Replace "mergeRect" with updated version.
                        rectList.set(mergeIndex, (Object)mergeRect);
                    } else if (runRect.y > abuttingY) {
                        // All Rectangles in the list with index greater than
                        // mergeIndex are runlength Rectangles and are sorted
                        // in non-decreasing ordinate order. Therefore there
                        // are no more Rectangles which could possibly be
                        // merged with mergeRect.
                        break;
                    }
                }
            }
        }

        return rectList;
    }

    /**
      * The default constructor.
      *
      * Using this constructor means that the subclass must override
      * all methods that reference theImage.
      */
    protected ROI() {}

    /**
     * Constructs an ROI from a RenderedImage.  The inclusion
     * threshold is taken to be halfway between the minimum and maximum
     * sample values specified by the image's SampleModel.
     *
     * @param im A single-banded RenderedImage.
     *
     * @throws IllegalArgumentException if im is null.
     * @throws IllegalArgumentException if im does not have exactly one band
     */
    public ROI(RenderedImage im) {
  this(im, 127);
    }

    /**
     * Constructs an ROI from a RenderedImage.  The inclusion
     * threshold is specified explicitly.
     *
     * @param im A single-banded RenderedImage.
     * @param threshold The desired inclusion threshold.
     *
     * @throws IllegalArgumentException if im is null.
     * @throws IllegalArgumentException if im does not have exactly one band
     */
    public ROI(RenderedImage im, int threshold) {

  if (im == null) {
      throw new IllegalArgumentException(JaiI18N.getString("Generic0"));
  }

  SampleModel sm = im.getSampleModel();

        if (sm.getNumBands() != 1) {
      throw new IllegalArgumentException(JaiI18N.getString("ROI0"));
        }

        this.threshold = threshold;

  // If the image is already binary and the threshold is >1
  // then there is no work to do.
  if ((threshold >= 1) && ImageUtil.isBinary(sm)) {
      theImage = PlanarImage.wrapRenderedImage(im);

  // Otherwise binarize the image for efficiency.
  } else {

      ParameterBlockJAI pbj = new ParameterBlockJAI("binarize");

      pbj.setSource("source0", im);
      pbj.setParameter("threshold", (double)threshold);

      theImage = JAI.create("binarize", pbj, null);
  }
    }

    /** Get the iterator, construct it if need be. */
    private RandomIter getIter() {
        if (iter == null) {
            iter = RandomIterFactory.create(theImage, null);
        }
        return iter;
    }

    /** Returns the inclusion/exclusion threshold value. */
    public int getThreshold() {
        return threshold;
    }

    /** Sets the inclusion/exclusion threshold value. */
    public void setThreshold(int threshold) {
        this.threshold = threshold;
  ((RenderedOp)theImage).setParameter((double)threshold, 0);
  iter = null;
  getIter();
    }

    /** Returns the bounds of the ROI as a <code>Rectangle</code>. */
    public Rectangle getBounds() {
        return new Rectangle(theImage.getMinX(),
                             theImage.getMinY(),
                             theImage.getWidth(),
                             theImage.getHeight());
    }

    /** Returns the bounds of the ROI as a <code>Rectangle2D</code>. */
    public Rectangle2D getBounds2D() {
        return new Rectangle2D.Float((float) theImage.getMinX(),
                                     (float) theImage.getMinY(),
                                     (float) theImage.getWidth(),
                                     (float) theImage.getHeight());
    }

    /**
     * Returns <code>true</code> if the ROI contains a given Point.
     *
     * @param p A Point identifying the pixel to be queried.
     * @throws IllegalArgumentException if p is null.
     * @return <code>true</code> if the pixel lies within the ROI.
     */
    public boolean contains(Point p) {
        if ( p == null ) {
            throw new IllegalArgumentException(JaiI18N.getString("Generic0"));
        }

        return contains(p.x, p.y);
    }

    /**
     * Returns <code>true</code> if the ROI contains a given Point2D.
     *
     * @param p A Point2D identifying the pixel to be queried.
     * @throws IllegalArgumentException if p is null.
     * @return <code>true</code> if the pixel lies within the ROI.
     */
    public boolean contains(Point2D p) {
        if ( p == null ) {
            throw new IllegalArgumentException(JaiI18N.getString("Generic0"));
        }

        return contains((int) p.getX(), (int) p.getY());
    }

    /**
     * Returns <code>true</code> if the ROI contains the point (x, y).
     *
     * @param x An int specifying the X coordinate of the pixel to be queried.
     * @param y An int specifying the Y coordinate of the pixel to be queried.
     * @return <code>true</code> if the pixel lies within the ROI.
     */
    public boolean contains(int x, int y) {
        int minX = theImage.getMinX();
        int minY = theImage.getMinY();

        return (x >= minX && x < minX + theImage.getWidth()) &&
         (y >= minY && y < minY + theImage.getHeight()) &&
               (getIter().getSample(x, y, 0) >= 1);
    }

    /**
     * Returns <code>true</code> if the ROI contain the point (x, y).
     *
     * @param x A double specifying the X coordinate of the pixel
     *        to be queried.
     * @param y A double specifying the Y coordinate of the pixel
     *        to be queried.
     * @return <code>true</code> if the pixel lies within the ROI.
     */
    public boolean contains(double x, double y) {
        return contains((int) x, (int) y);
    }

    /**
     * Returns <code>true</code> if a given <code>Rectangle</code> is
     * entirely included within the ROI.
     *
     * @param rect A <code>Rectangle</code> specifying the region to be tested
     *        for inclusion.
     * @throws IllegalArgumentException if rect is null.
     * @return <code>true</code> if the rectangle is entirely
     *         contained within the ROI.
     */
    public boolean contains(Rectangle rect) {
        if ( rect == null ) {
            throw new IllegalArgumentException(JaiI18N.getString("Generic0"));
        }

        if (!rect.equals(rect.intersection(getBounds()))) {
            return false;
        }

  byte[] packedData =
      ImageUtil.getPackedBinaryData(theImage.getData(), rect);

  // ImageUtil.getPackedBinaryData does not zero out the extra
  // bits used to pad to the nearest byte - therefore ignore
  // these bits.
  int leftover = rect.width % 8;

  if (leftover == 0) {

      for (int i = 0; i < packedData.length; i++)
    if ((packedData[i] & 0xff) != 0xff)
        return false;

  } else {

      int mask = ((1 << leftover) - 1) << (8 - leftover);

      for (int y = 0, k = 0; y < rect.height; y++) {
    for (int x = 0 ; x < rect.width-leftover; x += 8, k++) {
        if ((packedData[k] & 0xff) != 0xff)
      return false;
    }

    if ((packedData[k] & mask) != mask)
        return false;

    k++;
      }
  }

        return true;
    }

    /**
     * Returns <code>true</code> if a given <code>Rectangle2D</code> is
     * entirely included within the ROI.
     *
     * @param rect A <code>Rectangle2D</code> specifying the region to be
     *        tested for inclusion.
     * @throws IllegalArgumentException if rect is null.
     * @return <code>true</code> if the rectangle is entirely contained
     *         within the ROI.
     */
    public boolean contains(Rectangle2D rect) {
        if ( rect == null ) {
            throw new IllegalArgumentException(JaiI18N.getString("Generic0"));
        }
        Rectangle r = new Rectangle((int) rect.getX(),
                                    (int) rect.getY(),
                                    (int) rect.getWidth(),
                                    (int) rect.getHeight());
        return contains(r);
    }

    /**
     * Returns <code>true</code> if a given rectangle (x, y, w, h) is entirely
     * included within the ROI.
     *
     * @param x The int X coordinate of the upper left corner of the region.
     * @param y The int Y coordinate of the upper left corner of the region.
     * @param w The int width of the region.
     * @param h The int height of the region.
     * @return <code>true</code> if the rectangle is entirely contained
     *         within the ROI.
     */
    public boolean contains(int x, int y, int w, int h) {
        Rectangle r = new Rectangle(x, y, w, h);
        return contains(r);
    }

    /**
     * Returns <code>true</code> if a given rectangle (x, y, w, h) is entirely
     * included within the ROI.
     *
     * @param x The double X coordinate of the upper left corner of the region.
     * @param y The double Y coordinate of the upper left corner of the region.
     * @param w The double width of the region.
     * @param h The double height of the region.
     *
     * @return <code>true</code> if the rectangle is entirely
     * contained within the ROI.
     */
    public boolean contains(double x, double y, double w, double h) {
        Rectangle rect = new Rectangle((int) x, (int) y,
                                       (int) w, (int) h);
        return contains(rect);
    }

    /**
     * Returns <code>true</code> if a given <code>Rectangle</code>
     * intersects the ROI.
     *
     * @param rect A <code>Rectangle</code> specifying the region to be tested
     *        for inclusion.
     * @throws IllegalArgumentException if rect is null.
     * @return <code>true</code> if the rectangle intersects the ROI.
     */
    public boolean intersects(Rectangle rect) {
        if ( rect == null ) {
            throw new IllegalArgumentException(JaiI18N.getString("Generic0"));
        }

        Rectangle r = rect.intersection(getBounds());

        if (r.isEmpty()) {
            return false;
        }

  byte[] packedData =
      ImageUtil.getPackedBinaryData(theImage.getData(), r);

  // ImageUtil.getPackedBinaryData does not zero out the extra
  // bits used to pad to the nearest byte - therefore ignore
  // these bits.
  int leftover = r.width % 8;

  if (leftover == 0) {

      for (int i = 0; i < packedData.length; i++)
    if ((packedData[i] & 0xff) != 0)
        return true;

  } else {

      int mask = ((1 << leftover) - 1) << (8 - leftover);

      for (int y = 0, k = 0; y < r.height; y++) {
    for (int x = 0 ; x < r.width-leftover; x += 8, k++) {
        if ((packedData[k] & 0xff) != 0)
      return true;
    }
    if ((packedData[k] & mask) != 0)
        return true;
    k++;
      }
  }

        return false;
    }

    /**
     * Returns <code>true</code> if a given <code>Rectangle2D</code>
     * intersects the ROI.
     *
     * @param r A <code>Rectangle2D</code> specifying the region to be tested
     *        for inclusion.
     * @throws IllegalArgumentException if r is null.
     * @return <code>true</code> if the rectangle intersects the ROI.
     */
    public boolean intersects(Rectangle2D r) {
        if ( r == null ) {
            throw new IllegalArgumentException(JaiI18N.getString("Generic0"));
        }

        Rectangle rect = new Rectangle((int) r.getX(),
                                       (int) r.getY(),
                                       (int) r.getWidth(),
                                       (int) r.getHeight());
        return intersects(rect);
    }

    /**
     * Returns <code>true</code> if a given rectangular region
     * intersects the ROI.
     *
     * @param x The int X coordinate of the upper left corner of the region.
     * @param y The int Y coordinate of the upper left corner of the region.
     * @param w The int width of the region.
     * @param h The int height of the region.
     * @return <code>true</code> if the rectangle intersects the ROI.
     */
    public boolean intersects(int x, int y, int w, int h) {
        Rectangle rect = new Rectangle(x, y, w, h);
        return intersects(rect);
    }

    /**
     * Returns <code>true</code> if a given rectangular region
     * intersects the ROI.
     *
     * @param x The double X coordinate of the upper left corner of the region.
     * @param y The double Y coordinate of the upper left corner of the region.
     * @param w The double width of the region.
     * @param h The double height of the region.
     * @return <code>true</code> if the rectangle intersects the ROI.
     */
    public boolean intersects(double x, double y, double w, double h) {
        Rectangle rect = new Rectangle((int) x, (int) y,
                                       (int) w, (int) h);
        return intersects(rect);
    }

    /**
     * Create a binary PlanarImage of the size/bounds specified by
     * the rectangle.
     */
    private static PlanarImage createBinaryImage(Rectangle r) {

  if ((r.x == 0) && (r.y == 0)) {

      BufferedImage bi =
        new BufferedImage(r.width, r.height,
          BufferedImage.TYPE_BYTE_BINARY);

      return PlanarImage.wrapRenderedImage(bi);

  } else {

      SampleModel sm =
    new MultiPixelPackedSampleModel(
      DataBuffer.TYPE_BYTE, r.width, r.height, 1);

      // Create a TiledImage into which to write.
      return new TiledImage(r.x, r.y, r.width, r.height, r.x, r.y,
        sm, PlanarImage.createColorModel(sm));
  }
    }

    /**
     * Creates a merged ROI by performing the specified image operation
     * on <code>this</code> image and the image of the specified ROI.
     *
     * @param ROI the ROI to merge with <code>this</code>
     * @param op  the JAI operator to use for merge.
     *
     * @return the merged ROI
     */
    private ROI createOpROI(ROI roi, String op) {

        if (roi == null) {
            throw new IllegalArgumentException(JaiI18N.getString("Generic0"));
        }

  PlanarImage imThis = this.getAsImage();
  PlanarImage imROI  =  roi.getAsImage();
  PlanarImage imDest;

        Rectangle boundsThis = imThis.getBounds();
        Rectangle boundsROI  =  imROI.getBounds();

  // If the bounds of the two images do not match, then
  // expand as necessary to the union of the two bounds
  // using the "overlay" operator and then perform the JAI
  // operation.
  if (op.equals("and") || boundsThis.equals(boundsROI)) {
      imDest = JAI.create(op, imThis, imROI);

  } else if (op.equals("subtract") || boundsThis.contains(boundsROI)) {

      PlanarImage imBounds = createBinaryImage(boundsThis);

      imBounds = JAI.create("overlay", imBounds, imROI);
      imDest   = JAI.create(op       , imThis, imBounds);

  } else if (boundsROI.contains(boundsThis)) {

      PlanarImage imBounds = createBinaryImage(boundsROI);

      imBounds = JAI.create("overlay", imBounds, imThis);
      imDest   = JAI.create(op       , imBounds, imROI);

  } else {

      Rectangle merged = boundsThis.union(boundsROI);

      PlanarImage imBoundsThis = createBinaryImage(merged);
      PlanarImage imBoundsROI  = createBinaryImage(merged);

      imBoundsThis = JAI.create("overlay", imBoundsThis, imThis);
      imBoundsROI  = JAI.create("overlay", imBoundsROI , imROI );
      imDest   = JAI.create(op       , imBoundsThis, imBoundsROI);
  }

  return new ROI(imDest, threshold);
    }

    /**
     * Adds another <code>ROI</code> to this one and returns the result
     * as a new <code>ROI</code>. The supplied <code>ROI</code> will
     * be converted to a rendered form if necessary. The bounds of the
     * resultant <code>ROI</code> will be the union of the bounds of the
     * two <code>ROI</code>s being merged.
     *
     * @param roi An ROI.
     * @throws IllegalArgumentException if roi is null.
     * @return A new ROI containing the new ROI data.
     */
    public ROI add(ROI roi) {
        return createOpROI(roi, "add");
    }

    /**
     * Subtracts another <code>ROI</code> from this one and returns the
     * result as a new <code>ROI</code>. The supplied <code>ROI</code>
     * will be converted to a rendered form if necessary. The
     * bounds of the resultant <code>ROI</code> will be the same as
     * <code>this</code> <code>ROI</code>.
     *
     * @param roi An ROI.
     * @throws IllegalArgumentException if roi is null.
     * @return A new ROI containing the new ROI data.
     */
    public ROI subtract(ROI roi) {
        return createOpROI(roi, "subtract");
    }

    /**
     * Intersects the <code>ROI</code> with another <code>ROI</code> and returns the result as
     * a new <code>ROI</code>. The supplied <code>ROI</code> will be converted to a rendered
     * form if necessary. The bounds of the resultant <code>ROI</code> will be the
     * intersection of the bounds of the two <code>ROI</code>s being merged.
     *
     * @param roi An ROI.
     * @throws IllegalArgumentException if roi is null.
     * @return A new ROI containing the new ROI data.
     */
    public ROI intersect(ROI roi) {
        return createOpROI(roi, "and");
    }

    /**
     * Exclusive-ors the <code>ROI</code> with another <code>ROI</code>
     * and returns the result as a new <code>ROI</code>. The supplied
     * <code>ROI</code> will be converted to a rendered form if
     * necessary. The bounds of the resultant <code>ROI</code> will
     * be the union of the bounds of the two <code>ROI</code>s being
     * merged.
     *
     * @param roi An ROI.
     * @throws IllegalArgumentException if roi is null.
     * @return A new ROI containing the new ROI data.
     */
    public ROI exclusiveOr(ROI roi) {
        return createOpROI(roi, "xor");
    }

    /**
     * Performs an affine transformation and returns the result as a new
     * ROI.  The transformation is performed by an "Affine" RIF using the
     * indicated interpolation method.
     *
     * @param at an AffineTransform specifying the transformation.
     * @param interp the Interpolation to be used.
     * @throws IllegalArgumentException if at is null.
     * @throws IllegalArgumentException if interp is null.
     * @return a new ROI containing the transformed ROI data.
     */
    public ROI transform(AffineTransform at, Interpolation interp) {

  if (at == null) {
      throw new IllegalArgumentException(JaiI18N.getString("ROI5"));
  }

  if (interp == null) {
      throw new IllegalArgumentException(JaiI18N.getString("ROI6"));
  }

        ParameterBlock paramBlock = new ParameterBlock();
        paramBlock.add(at);
        paramBlock.add(interp);
        return performImageOp("Affine", paramBlock, 0, null);
    }

    /**
     * Performs an affine transformation and returns the result as a new
     * ROI.  The transformation is performed by an "Affine" RIF using
     * nearest neighbor interpolation.
     *
     * @param at an AffineTransform specifying the transformation.
     * @throws IllegalArgumentException if at is null.
     * @return a new ROI containing the transformed ROI data.
     */
    public ROI transform(AffineTransform at) {
        if ( at == null ) {
            throw new IllegalArgumentException(JaiI18N.getString("Generic0"));
        }

        return transform(at,
                      Interpolation.getInstance(Interpolation.INTERP_NEAREST));
    }

    /**
     * Transforms an ROI using an imaging operation.  The operation is
     * specified by a <code>RenderedImageFactory</code>.  The
     * operation's <code>ParameterBlock</code>, minus the image source
     * itself is supplied, along with an index indicating where to
     * insert the ROI image.  The <code>renderHints</code> argument
     * allows rendering hints to be passed in.
     *
     * @param RIF A <code>RenderedImageFactory</code> that will be used
     *        to create the op.
     * @param paramBlock A <code>ParameterBlock</code> containing all
     *        sources and parameters for the op except for the ROI itself.
     * @param sourceIndex The index of the <code>ParameterBlock</code>'s
     *        sources where the ROI is to be inserted.
     * @param renderHints A <code>RenderingHints</code> object containing
     *        rendering hints, or null.
     * @throws IllegalArgumentException if RIF is null.
     * @throws IllegalArgumentException if paramBlock is null.
     */
    public ROI performImageOp(RenderedImageFactory RIF,
                              ParameterBlock paramBlock,
                              int sourceIndex,
                              RenderingHints renderHints) {

        if RIF == null || paramBlock == null ) {
            throw new IllegalArgumentException(JaiI18N.getString("Generic0"));
        }

        // Clone the ParameterBlock and insert a source
        ParameterBlock pb = (ParameterBlock) paramBlock.clone();
        Vector sources = pb.getSources();
        sources.insertElementAt(this.getAsImage(), sourceIndex);

        // Create a new RenderedImage based on the RIF
        // and ParameterBlock.
        RenderedImage im = RIF.create(pb, renderHints);
        return new ROI(im, threshold);
    }

    /**
     * Transforms an ROI using an imaging operation.  The
     * operation is specified by name; the default JAI registry is
     * used to resolve this into a RIF.  The operation's
     * <code>ParameterBlock</code>, minus the image source itself is supplied,
     * along with an index indicating where to insert the ROI image.
     * The <code>renderHints</code> argument allows rendering hints to
     * be passed in.
     *
     * @param name The name of the operation to perform.
     * @param paramBlock A <code>ParameterBlock</code> containing all
     *        sources and parameters for the op except for the ROI itself.
     * @param sourceIndex The index of the <code>ParameterBlock</code>'s
     *        sources where the ROI is to be inserted.
     * @param renderHints A <code>RenderingHints</code> object containing
     *        rendering hints, or null.
     * @throws IllegalArgumentException if name is null.
     * @throws IllegalArgumentException if paramBlock is null.
     */
    public ROI performImageOp(String name,
                              ParameterBlock paramBlock,
                              int sourceIndex,
                              RenderingHints renderHints) {

        if ( name == null || paramBlock == null ) {
            throw new IllegalArgumentException(JaiI18N.getString("Generic0"));
        }

        // Clone the ParameterBlock and insert a source
        ParameterBlock pb = (ParameterBlock) paramBlock.clone();
        Vector sources = pb.getSources();
        sources.insertElementAt(this.getAsImage(), sourceIndex);

        // Create a new RenderedImage based on the operation name
        // and ParameterBlock using the default registry.
        RenderedImage im = JAI.create(name, pb, renderHints);
        return new ROI(im, threshold);
    }

    /**
     * Returns a <code>Shape</code> representation of the
     * <code>ROI</code>, if possible. If none is available, null is
     * returned. A proper instance of <code>ROI</code> (one that is not
     * an instance of any subclass of <code>ROI</code>) will always
     * return null.
     *
     * @return The <code>ROI</code> as a <code>Shape</code>.
     */
    public Shape getAsShape() {
        return null;
    }

    /**
     * Returns a <code>PlanarImage</code> representation of the
     * <code>ROI</code>. This method will always succeed. This method
     * returns a (bilevel) image whose <code>SampleModel</code> is an
     * instance of <code>MultiPixelPackedSampleModel</code>.
     *
     * @return The <code>ROI</code> as a <code>PlanarImage</code>.
     */
    public PlanarImage getAsImage() {
        return theImage;
    }

    /**
     * Returns a bitmask for a given rectangular region of the ROI
     * indicating whether the pixel is included in the region of
     * interest.  The results are packed into 32-bit integers, with
     * the MSB considered to lie on the left.  The last entry in each
     * row of the result may have bits that lie outside of the
     * requested rectangle.  These bits are guaranteed to be zeroed.
     *
     * <p> The <code>mask</code> array, if supplied, must be of length
     * equal to or greater than <code>height</code> and each of its
     * subarrays must have length equal to or greater than (width +
     * 31)/32.  If <code>null</code> is passed in, a suitable array
     * will be constructed.  If the mask is non-null but has
     * insufficient size, an exception will be thrown.
     *
     * @param x The X coordinate of the upper left corner of the rectangle.
     * @param y The Y coordinate of the upper left corner of the rectangle.
     * @param width The width of the rectangle.
     * @param height The height of the rectangle.
     * @param mask A two-dimensional array of ints at least
     *        (width + 31)/32 entries wide and (height) entries tall,
     *        or null.
     * @return A reference to the <code>mask</code> parameter, or
     *         to a newly constructed array if <code>mask</code> is
     *         <code>null</code>. If the specified rectangle does
     *         intersect with the image bounds then a <code>null</code>
     *         is returned.
     */
    public int[][] getAsBitmask(int x, int y,
                                int width, int height,
                                int[][] mask) {

        Rectangle rect =
            getBounds().intersection(new Rectangle(x, y, width, height));

        // Verify that the requested area actually intersects the ROI image.
        if (rect.isEmpty()) {
            return null;
        }

        // Determine the minimum required width of the bitmask in integers.
        int bitmaskIntWidth = (width + 31)/32;

        // Construct bitmask array if argument is null.
        if (mask == null) {
      mask = new int[height][bitmaskIntWidth];
  } else if (mask.length < height || mask[0].length < bitmaskIntWidth) {
            throw new RuntimeException(JaiI18N.getString("ROI3"));
        }

  byte[] data = ImageUtil.getPackedBinaryData(
            theImage.getData(), rect);

  // ImageUtil.getPackedBinaryData does not zero out the extra
  // bits used to pad to the nearest byte - so zero these
  // bits out.
  int leftover = rect.width % 8;

  if (leftover != 0) {
      int datamask = ((1 << leftover) - 1) << (8 - leftover);
      int linestride = (width + 7)/8;

      for (int i = linestride-1; i < data.length; i += linestride) {
    data[i] = (byte)(data[i] & datamask);
      }
  }

  int lineStride = (rect.width + 7)/8;
  int leftOver   = lineStride % 4;

  int row, col, k;
  int ncols = (lineStride - leftOver) / 4;

  for (row = 0, k = 0; row < rect.height; row++) {
            int[] maskRow = mask[row];

      for (col = 0; col < ncols; col++) {
    maskRow[col] = ((data[] & 0xff) << 24) |
             ((data[k+1] & 0xff) << 16) |
             ((data[k+2] & 0xff) <<  8) |
             ((data[k+3] & 0xff) <<  0);
    k += 4;
      }

      switch(leftOver) {
      case 0: break;
      case 1: maskRow[col++] = ((data[] & 0xff) << 24);
        break;
      case 2: maskRow[col++] = ((data[] & 0xff) << 24) |
             ((data[k+1] & 0xff) << 16);
        break;
      case 3: maskRow[col++] = ((data[] & 0xff) << 24) |
             ((data[k+1] & 0xff) << 16) |
             ((data[k+2] & 0xff) <<  8);
        break;
      }

      k += leftOver;

      Arrays.fill(maskRow, col, bitmaskIntWidth, 0);
  }

        // Clear any trailing rows.
        for (row = rect.height; row < height; row++) {
            Arrays.fill(mask[row], 0);
        }

  return mask;
    }

    /**
     * Returns a <code>LinkedList</code> of <code>Rectangle</code>s
     * for a given rectangular region of the ROI. The
     * <code>Rectangle</code>s in the list are merged into a minimal
     * set.
     *
     * @param x The X coordinate of the upper left corner of the rectangle.
     * @param y The Y coordinate of the upper left corner of the rectangle.
     * @param width The width of the rectangle.
     * @param height The height of the rectangle.
     * @return A <code>LinkedList</code> of <code>Rectangle</code>s.
     *         If the specified rectangle does intersect with the image
     *         bounds then a <code>null</code> is returned.
     */
    public LinkedList getAsRectangleList(int x, int y,
                                         int width, int height) {
        return getAsRectangleList(x, y, width, height, true);
    }

    /**
     * Returns a <code>LinkedList</code> of <code>Rectangle</code>s for
     * a given rectangular region of the ROI.
     *
     * @param x The X coordinate of the upper left corner of the rectangle.
     * @param y The Y coordinate of the upper left corner of the rectangle.
     * @param width The width of the rectangle.
     * @param height The height of the rectangle.
     * @param mergeRectangles <code>true</code> if the <code>Rectangle</code>s
     *        are to be merged into a minimal set.
     * @return A <code>LinkedList</code> of <code>Rectangle</code>s.
     *         If the specified rectangle does intersect with the image
     *         bounds then a <code>null</code> is returned.
     */
    protected LinkedList getAsRectangleList(int x, int y,
                                            int width, int height,
                                            boolean mergeRectangles) {

        // Verify that the requested area actually intersects the ROI image.
        Rectangle bounds = getBounds();
        Rectangle rect = new Rectangle(x, y, width, height);
        if (!bounds.intersects(rect)) {
            return null;
        }

        // Clip the requested area to the ROI image if necessary.
        if (!bounds.contains(rect)) {
            rect = bounds.intersection(rect);
            x = rect.x;
            y = rect.y;
            width = rect.width;
            height = rect.height;
        }

  byte[] data = ImageUtil.getPackedBinaryData(
            theImage.getData(), rect);

  // ImageUtil.getPackedBinaryData does not zero out the extra
  // bits used to pad to the nearest byte - therefore ignore
  // these bits.
  int lineStride = (width + 7)/8;
  int leftover = width % 8;
  int mask = (leftover == 0) ? 0xff :
      ((1 << leftover) - 1) << (8 - leftover);

        LinkedList rectList = new LinkedList();

        // Calculate the initial list of rectangles as a list of run
        // lengths which are in fact rectangles of unit height.

  int row, col, k, start, val, cnt;

        for (row = 0, k = 0; row < height; row++) {

      start = -1;

            for (col = 0, cnt = 0; col < lineStride; col++, k++) {

    val = data[k] & ((col == lineStride-1) ? mask : 0xff);

    if (val == 0) {
        if (start >= 0) {
      rectList.addLast(
          new Rectangle(x+start, y+row, col*8 - start, 1));
      start = -1;
        }

    } else if (val == 0xff) {
        if (start < 0) {
      start = col*8;
        }

    } else {
        for (int bit = 7; bit >= 0; bit--) {
      if ((val & (1 << bit)) == 0x00) {
          if (start >= 0) {
        rectList.addLast(new Rectangle(
            x+start, y+row, col*8 + (7 - bit) - start, 1));
        start = -1;
          }
      } else {
          if (start < 0) {
        start = col*8 + (7 - bit);
          }
      }
        }
    }
            }

      if (start >= 0) {
    rectList.addLast(
        new Rectangle(x+start, y+row, col*8 - start, 1));
      }
        }

        // Return the list of Rectangles possibly merged into a minimal set.
        return mergeRectangles ? mergeRunLengthList(rectList) : rectList;
    }

    /**
      * Serialize the <code>ROI</code>.
      *
      * @param out The <code>ObjectOutputStream</code>.
      */
    private void writeObject(ObjectOutputStream out) throws IOException {
        out.defaultWriteObject();
        if (theImage != null) {
            out.writeBoolean(true);
            RenderingHints hints = new RenderingHints(null);
            hints.put(JAI.KEY_SERIALIZE_DEEP_COPY, new Boolean(true));
            out.writeObject(SerializerFactory.getState(theImage, hints));
        } else {
            out.writeBoolean(false);
        }
    }

    /**
      * Deserialize the <code>ROI</code>.
      *
      * @param in The <code>ObjectInputStream</code>.
      */
    private void readObject(ObjectInputStream in)
        throws IOException, ClassNotFoundException {
        in.defaultReadObject();
        if ((boolean)in.readBoolean()) {
            SerializableState ss = (SerializableState)in.readObject();
            RenderedImage ri =(RenderedImage)(ss.getObject());
            theImage = PlanarImage.wrapRenderedImage(ri);
        } else {
            theImage = null;
        }
        iter = null;
    }
}
TOP

Related Classes of com.lightcrafts.mediax.jai.ROI

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.