Package com.lightcrafts.mediax.jai.operator

Source Code of com.lightcrafts.mediax.jai.operator.FilteredSubsampleDescriptor

/*
* $RCSfile: FilteredSubsampleDescriptor.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:36 $
* $State: Exp $
*/package com.lightcrafts.mediax.jai.operator;

import com.lightcrafts.media.jai.util.PropertyGeneratorImpl;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.geom.AffineTransform;
import java.awt.image.RenderedImage;
import java.awt.image.renderable.ParameterBlock;
import com.lightcrafts.mediax.jai.Interpolation;
import com.lightcrafts.mediax.jai.InterpolationBicubic2;
import com.lightcrafts.mediax.jai.InterpolationBicubic;
import com.lightcrafts.mediax.jai.InterpolationBilinear;
import com.lightcrafts.mediax.jai.InterpolationNearest;
import com.lightcrafts.mediax.jai.JAI;
import com.lightcrafts.mediax.jai.OpImage;
import com.lightcrafts.mediax.jai.OperationDescriptorImpl;
import com.lightcrafts.mediax.jai.ParameterBlockJAI;
import com.lightcrafts.mediax.jai.PlanarImage;
import com.lightcrafts.mediax.jai.ROI;
import com.lightcrafts.mediax.jai.ROIShape;
import com.lightcrafts.mediax.jai.RenderableOp;
import com.lightcrafts.mediax.jai.RenderedOp;
import com.lightcrafts.mediax.jai.WarpOpImage;
import com.lightcrafts.mediax.jai.registry.RenderedRegistryMode;

/**
* <p> This property generator computes the properties for the operation
* "FilteredSubsample" dynamically.
*/
class FilteredSubsamplePropertyGenerator extends PropertyGeneratorImpl {

    /** Constructor. */
    public FilteredSubsamplePropertyGenerator() {
        super(new String[] {"FilteredSubsample"},
              new Class[] {boolean.class},
              new Class[] {RenderedOp.class, RenderableOp.class});
    }

    /**
     * Returns the specified property.
     *
     * @param name  Property name.
     * @param opNode Operation node.
     */
    public Object getProperty(String name,
                              Object opNode) {
        validate(name, opNode);

        if(opNode instanceof RenderedOp &&
           name.equalsIgnoreCase("roi")) {
            RenderedOp op = (RenderedOp)opNode;

      ParameterBlock pb = op.getParameterBlock();

      // Retrieve the rendered source image and its ROI.
      RenderedImage src = pb.getRenderedSource(0);
      Object property = src.getProperty("ROI");
      if (property == null ||
    property.equals(java.awt.Image.UndefinedProperty) ||
    !(property instanceof ROI)) {
    return null;
      }
      ROI srcROI = (ROI)property;

      // Determine the effective source bounds.
      Rectangle srcBounds = null;
      PlanarImage dst = op.getRendering();
      if(dst instanceof WarpOpImage && !((OpImage)dst).hasExtender(0)) {
    WarpOpImage warpIm = (WarpOpImage)dst;
    srcBounds =
        new Rectangle(src.getMinX() + warpIm.getLeftPadding(),
          src.getMinY() + warpIm.getTopPadding(),
          src.getWidth() - warpIm.getWidth() + 1,
          src.getHeight() - warpIm.getHeight() + 1);
      } else {
    srcBounds = new Rectangle(src.getMinX(),
            src.getMinY(),
            src.getWidth(),
            src.getHeight());
      }

      // If necessary, clip the ROI to the effective source bounds.
      if(!srcBounds.contains(srcROI.getBounds())) {
    srcROI = srcROI.intersect(new ROIShape(srcBounds));
      }

      // Retrieve the scale factors
      float sx = 1.0F/pb.getIntParameter(1);
      float sy = 1.0F/pb.getIntParameter(2);

      // Create an equivalent transform.
      AffineTransform transform =
    new AffineTransform(sx, 0.0, 0.0, sy, 0, 0);

      // Create the scaled ROI.
      ROI dstROI = srcROI.transform(transform);

      // Retrieve the destination bounds.
      Rectangle dstBounds = op.getBounds();

      // If necessary, clip the warped ROI to the destination bounds.
      if(!dstBounds.contains(dstROI.getBounds())) {
    dstROI = dstROI.intersect(new ROIShape(dstBounds));
      }

      // Return the warped and possibly clipped ROI.
      return dstROI;
  } else {
      return null;
  }
    }

    /** Returns the valid property names for the operation "FilteredSubsample". */
    public String[] getPropertyNames() {
        String[] properties = new String[1];
        properties[0] = "roi";
        return(properties);
    }

}

/**
* An <code>OperationDescriptor</code> describing the "FilteredSubsample"
* operation.
*
* <p> The "FilteredSubsample" operation subsamples an image by integral
* factors. The furnished scale factors express the ratio of the
* source dimensions to the destination dimensions.  The input filter is
* symmetric about the center pixel and is specified by values from the
* center outward.  Both filter axes use the same input filter values.

* <p> When applying scale factors of scaleX, scaleY to a source image
* with width of src_width and height of src_height, the resulting image
* is defined to have the following bounds:
*
* <code></pre>
*       dst_minX  = round(src_minX  / scaleX) <br>
*       dst_minY  = round(src_minY  / scaleY) <br>
*       dst_width  =  round(src_width  / scaleX) <br>
*       dst_height =  round(src_height / scaleY) <br>
* </pre></code>
*
* <p> The input filter is quadrant symmetric (typically antialias). The
* filter is product-separable, quadrant symmetric, and is defined by half of its
* span. For example, if the input filter, qsFilter, was of size 3, it would have
* width and height 5 and have the symmetric form: <br>
*   <code> qs[2] qs[1] qs[0] qs[1] qs[2] </code> <br>
*
* <p> A fully expanded 5 by 5 kernel has the following format (25 entries
* defined by only 3 entries):
*
*   <code>
*   <p align=center> qs[2]*qs[2]  qs[2]*qs[1]  qs[2]*qs[0]  qs[2]*qs[1]  qs[2]*qs[2] <br>
*
*                    qs[1]*qs[2]  qs[1]*qs[1]  qs[1]*qs[0]  qs[1]*qs[1]  qs[1]*qs[2] <br>
*
*                    qs[0]*qs[2]  qs[0]*qs[1]  qs[0]*qs[0]  qs[0]*qs[1]  qs[0]*qs[2] <br>
*
*                    qs[1]*qs[2]  qs[1]*qs[1]  qs[1]*qs[0]  qs[1]*qs[1]  qs[1]*qs[2] <br>
*
*                    qs[2]*qs[2]  qs[2]*qs[1]  qs[2]*qs[0]  qs[2]*qs[1]  qs[2]*qs[2]
*   </p> </code>
*
* <p> This operator is similar to the image scale operator.  Important
* differences are described here.  The coordinate transformation differences
* between the FilteredDownsampleOpImage and the ScaleOpImage operators can be
* understood by comparing their mapping equations directly.
*
* <p> For the scale operator, the destination (D) to source (S) mapping
* equations are given by
*
* <code>
*   <p> xS = (xD - xTrans)/xScale <br>
*       yS = (yD - yTrans)/yScale
* </code>
*
* <p> The scale and translation terms are floating point values in D-frame
* pixel units.  For scale this means that one S pixel maps to xScale
* by yScale D-frame pixels.  The translation vector, (xTrans, yTrans),
* is in D-frame pixel units.
*
* <p> The FilteredSubsample operator mapping equations are given by
*
* <code>
*   <p> xS = xD*scaleX <br>
*   yS = yD*scaleY
* </code>
*
* <p> The scale terms for this operation are integral values in the
* S-Frame; there are no translation terms for this operation.
*
* <p> The downsample terms are restricted to positive integral values.
* Geometrically, one D-frame pixel maps to scaleX * scaleY S-frame
* pixels.  The combination of downsampling and filtering has performance
* benefits over sequential operator usage in part due to the symmetry
* constraints imposed by only allowing integer parameters for scaling and
* only allowing separable symmetric filters.  With odd scale factors, D-frame
* pixels map directly onto S-frame pixel centers.  With even scale factors,
* D-frame pixels map squarely between S-frame pixel centers.  Below are
* examples of even, odd, and combination cases.
*
*   <p>  s = S-frame pixel centers <br>
*        d = D-frame pixel centers mapped to S-frame
*   </p>
*   <kbd>
*   <pre> s   s   s   s   s   s           s   s   s   s   s   s  </pre>
*   <pre>   d       d       d  </pre>
*   <pre> s   s   s   s   s   s           s   d   s   s   d   s  </pre>
*   <pre>  </pre>
*   <pre> s   s   s   s   s   s           s   s   s   s   s   s  </pre>
*   <pre>   d       d       d  </pre>
*   <pre> s   s   s   s   s   s           s   s   s   s   s   s  </pre>
*   <pre>  </pre>
*   <pre> s   s   s   s   s   s           s   d   s   s   d   s  </pre>
*   <pre>   d       d       d  </pre>
*   <pre> s   s   s   s   s   s           s   s   s   s   s   s  </pre>
*   <pre>  </pre>
*   <pre> Even scaleX/Y factors            Odd scaleX/Y factors  </pre>
*   <pre>   </pre>
*   <pre> s   s   s   s   s   s           s   s   s   s   s   s  </pre>
*   <pre>     d           d    </pre>
*   <pre> s   s   s   s   s   s           s d s   s d s   s d s  </pre>
*   <pre>   </pre>
*   <pre> s   s   s   s   s   s           s   s   s   s   s   s  </pre>
*   <pre>     d           d    </pre>
*   <pre> s   s   s   s   s   s           s   s   s   s   s   s  </pre>
*   <pre>   </pre>
*   <pre> s   s   s   s   s   s           s d s   s d s   s d s  </pre>
*   <pre>     d           d    </pre>
*   <pre> s   s   s   s   s   s           s   s   s   s   s   s  </pre>
*   <pre>   </pre>
* <pre>  Odd/even scaleX/Y factors      Even/odd scaleX/Y factors  </pre> <br>
*   </kbd>
*
* <p> The convolution kernel is restricted to have quadrant symmetry (qs). This
* type of symmetry is also product separable.  The qsFilter is specified by
* a floating array.  If qsFilter[0], qsFilter[1], ... ,
* qsFilter[qsFilter.length - 1]
* is the filter input, then the entire separable kernel is given by <br>
* qsFilter[qsFilter.length - 1], ... , qsFilter[0], ... ,
* qsFilter[qsFilter.length - 1] <br>
*
* <p> The restriction of integer parameter constraints allows full product
* separablity and symmetry when applying the combined resample and filter
* convolution operations.
*
* <p> If Bilinear or Bicubic interpolation is specified, the source needs
* to be extended such that it has the extra pixels needed to compute all
* the destination pixels. This extension is performed via the
* <code>BorderExtender</code> class. The type of border extension can be
* specified as a <code>RenderingHint</code> to the <code>JAI.create</code>
* method.
*
* <p> If no <code>BorderExtender</code> is specified, the source will
* not be extended.  The output image size is still calculated
* according to the formula specified above. However since there is not
* enough source to compute all the destination pixels, only that
* subset of the destination image's pixels which can be computed,
* will be written in the destination. The rest of the destination
* will be set to zeros.
*
* <p> It should be noted that this operation automatically adds a
* value of <code>Boolean.TRUE</code> for the
* <code>JAI.KEY_REPLACE_INDEX_COLOR_MODEL</code> to the given
* <code>configuration</code> so that the operation is performed
* on the pixel values instead of being performed on the indices into
* the color map if the source(s) have an <code>IndexColorModel</code>.
* This addition will take place only if a value for the
* <code>JAI.KEY_REPLACE_INDEX_COLOR_MODEL</code> has not already been
* provided by the user. Note that the <code>configuration</code> Map
* is cloned before the new hint is added to it. The operation can be
* smart about the value of the <code>JAI.KEY_REPLACE_INDEX_COLOR_MODEL</code>
* <code>RenderingHints</code>, i.e. while the default value for the
* <code>JAI.KEY_REPLACE_INDEX_COLOR_MODEL</code> is
* <code>Boolean.TRUE</code>, in some cases the operator could set the
* default.
*
* <p> "FilteredSubsample" defines a PropertyGenerator that performs an identical
* transformation on the "ROI" property of the source image, which can
* be retrieved by calling the <code>getProperty</code> method with
* "ROI" as the property name.
*
* <p> One design purpose of this operation is anti-aliasing when
* downsampling.  The technique of anti-aliasing applies a good
* filter to the area of rendering to generate better results.  Generally,
* this filter is required to be (quadrant) symmetric and separable
* to obtain good performance.  The widely-used Gaussian filter
* satisfies all these requirements.  Thus, the default value for the
* parameter "qsFilter" is generated from a Gaussian kernel
* based on the following procedure:
*
* <p> Define the Gaussian function <code>G(x)</code> as
* <p>  <code>G(x) = e<sup>-x<sup>2</sup>/(2s<sup>2</sup>)</sup>/(
* (2pi)<sup>&#189;</sup>s)</code>,
* <p>where <code>s</code> is the standard deviation, and <code>pi</code>
* is the ratio of the circumference of a circle to its diameter.
*
* <p> For a one-dimensional Gaussian kernel with a size of <code>2N+1</code>,
*  the standard deviation of the Gaussian function to generate this kernel
*  is chosen as <code>N/3</code>.  The
*  one-dimensional Gaussian kernel <code>K<sub>N</sub>(1:2N+1)</code> is
<p><code>(G(-N)/S, G(-N+1)/S, ..., G(0),..., G(N-1)/S, G(N)/S), </code>
* <p> where <code>S</code> is the summation of <code>G(-N), G(-N+1), ...,G(0),
* ..., G(N-1)</code>, and <code>G(N)</code>.  A two-dimensional Gaussian
* kernel with a size of <code>(2N+1) x (2N+1)</code> is constructed as the
* outer product of two <code>K<sub>N</sub></code>s:
* the <code>(i, j)</code>th element is
* <code>K<sub>N</sub>(i)K<sub>N</sub>(j)</code>.
* The quadrant symmetric filter corresponding to the
* <code>(2N+1) x (2N+1)</code> Gaussian kernel is simply
* <p> <code>(G(0)/S, G(1)/S, ..., G(N)/S)</code>, or
* <p> <code>(K<sub>N</sub>(N+1), ..., K<sub>N</sub>(2N+1)</code>.
*
* <p> Denote the maximum of the X and Y subsample factors as <code>M</code>.
* If <code>M</code> is even, the default "qsFilter" is the quadrant symmetric
* filter derived from the two-dimensional <code>(M+1) x (M+1)</code>
* Gaussian kernel. If <code>M</code> is odd, the default
* "qsFilter" is the quadrant symmetric filter derived from the
* two-dimensional <code>M x M</code> Gaussian kernel.
*
* <p><table border=1>
* <caption>Resource List</caption>
* <tr><th>Name</th>        <th>Value</th></tr>
* <tr><td>GlobalName</td>  <td>FilteredSubsample</td></tr>
* <tr><td>LocalName</td>   <td>FilteredSubsample</td></tr>
* <tr><td>Vendor</td>      <td>com.lightcrafts.media.jai</td></tr>
* <tr><td>Description</td> <td>Filters and subsamples an image.</td></tr>
* <tr><td>DocURL</td>      <td>http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/FilteredSubsample.html</td></tr>
* <tr><td>Version</td>     <td>1.1</td></tr>
* <tr><td>arg0Desc</td>    <td>The X subsample factor.</td></tr>
* <tr><td>arg1Desc</td>    <td>The Y subsample factor.</td></tr>
* <tr><td>arg2Desc</td>    <td>Symmetric filter coefficients.</td></tr>
* <tr><td>arg3Desc</td>    <td>The interpolation object for
*                              resampling.</td></tr>
* </table></p>
*
* <p><table border=1>
* <caption>Parameter List</caption>
* <tr><th>Name</th>          <th>Class Type</th>
*                            <th>Default Value</th></tr>
* <tr><td>scaleX</td>        <td>java.lang.Integer</td>
*                            <td>2</td>
* <tr><td>scaleY</td>        <td>java.lang.Integer</td>
*                            <td>2</td>
* <tr><td>qsFilter</td>      <td>java.lang.Float []</td>
*                            <td>A quadrant symmetric filter
*          generated from a Gaussian kernel
*          as described above.</td>
* <tr><td>interpolation</td> <td>com.lightcrafts.mediax.jai.Interpolation</td>
*                            <td>InterpolationNearest</td>
* </table></p>
*
* @see com.lightcrafts.mediax.jai.Interpolation
* @see com.lightcrafts.mediax.jai.BorderExtender
* @see com.lightcrafts.mediax.jai.OperationDescriptor
*
* @since JAI 1.1
*/
public class FilteredSubsampleDescriptor extends OperationDescriptorImpl {

    /**
     * The resource strings that provide the general documentation
     * and specify the parameter list for this operation.
     */
    private static final String[][] resources = {
        {"GlobalName""FilteredSubsample"},
        {"LocalName",   "FilteredSubsample"},
        {"Vendor",      "com.lightcrafts.media.jai"},
        {"Description", JaiI18N.getString("FilteredSubsampleDescriptor0")},
        {"DocURL",      "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/FilteredSubsampleDescriptor.html"},
        {"Version",     "1.0"},
        {"arg0Desc",    "The X subsample factor."},
        {"arg1Desc",    "The Y subsample factor."},
  {"arg2Desc",    "Symmetric filter coefficients."},
  {"arg3Desc",    "Interpolation object."}
    };

    /** The parameter class list for this operation. */
    private static final Class[] paramClasses = {
        java.lang.Integer.class, java.lang.Integer.class,
  float[].class, Interpolation.class
    };

    /** The parameter name list for this operation. */
    private static final String[] paramNames = {
        "scaleX", "scaleY", "qsFilterArray", "interpolation",
    };

    /** The parameter default value list for this operation. */
    private static final Object[] paramDefaults = {
        new Integer(2),
        new Integer(2),
        null,
        Interpolation.getInstance(Interpolation.INTERP_NEAREST)
    };

    private static final String[] supportedModes = {
  "rendered"
    };

    /** <p> Constructor. */
    public FilteredSubsampleDescriptor() {
        super(resources, supportedModes, 1,
              paramNames, paramClasses, paramDefaults, null);
    }

    /**
     * Validates the input parameters.
     *
     * <p> In addition to the standard checks performed by the
     * superclass method, this method checks that "scaleX" and "scaleY"
     * are both greater than 0 and that the interpolation type
     * is one of 4 standard types: <br>
     * <p> <code>
     *    com.lightcrafts.mediax.jai.InterpolationNearest <br>
     *    com.lightcrafts.mediax.jai.InterpolationBilinear <br>
     *    com.lightcrafts.mediax.jai.InterpolationBicubic <br>
     *    com.lightcrafts.mediax.jai.InterpolationBicubic2
     * </code>
     */
    protected boolean validateParameters(String modeName,
           ParameterBlock args,
                                         StringBuffer msg) {
  if (!super.validateParameters(modeName, args, msg))
      return false;

        int scaleX = args.getIntParameter(0);
        int scaleY = args.getIntParameter(1);
        if (scaleX < 1 || scaleY < 1) {
            msg.append(getName() + " " +
                       JaiI18N.getString("FilteredSubsampleDescriptor1"));
            return false;
        }

  float[] filter = (float[])args.getObjectParameter(2);

  // if this parameter is null, generate the kernel based on the
  // procedure described above.
  if (filter == null) {
      int m = scaleX > scaleY ? scaleX: scaleY;
      if ((m & 1) == 0)
    m++;

      double sigma = (m - 1) / 6.0;

      // when m is 1, sigma is 0; will generate NaN.  Give any number
      // to sigma will generate the correct kernel {1.0}
      if (m == 1)
    sigma = 1.0;

      filter = new float[m/2 + 1];
      float sum = 0;

      for (int i = 0; i < filter.length; i++) {
    filter[i] = (float)gaussian((double)i, sigma);
    if (i == 0)
        sum += filter[i];
    else
        sum += filter[i] * 2;
      }

      for (int i = 0; i < filter.length; i++) {
    filter[i] /= sum;
      }

      args.set(filter, 2);
  }

        Interpolation interp = (Interpolation)args.getObjectParameter(3);

        // Determine the interpolation type, if not supported throw exception
        if (!((interp instanceof InterpolationNearest||
            (interp instanceof InterpolationBilinear) ||
            (interp instanceof InterpolationBicubic||
            (interp instanceof InterpolationBicubic2))) {
           msg.append(getName() + " " +
                       JaiI18N.getString("FilteredSubsampleDescriptor2"));
           return false;
        }
        return true;

    } // validateParameters

    /** Computes the value of Gaussian function at <code>x</code>.
     * @param x The coordinate at where the value is computed.
     * @param sigma The standard deviation for the Gaussian function.
     */
    private double gaussian(double x, double sigma) {
  return Math.exp(-x*x/(2 * sigma * sigma)) / sigma / Math.sqrt(2 * Math.PI);
    }



    /**
     * Filters and subsamples an image.
     *
     * <p>Creates a <code>ParameterBlockJAI</code> from all
     * supplied arguments except <code>hints</code> and invokes
     * {@link JAI#create(String,ParameterBlock,RenderingHints)}.
     *
     * @see JAI
     * @see ParameterBlockJAI
     * @see RenderedOp
     *
     * @param source0 <code>RenderedImage</code> source 0.
     * @param scaleX The X subsample factor.
     * May be <code>null</code>.
     * @param scaleY The Y subsample factor.
     * May be <code>null</code>.
     * @param qsFilterArray Symmetric filter coefficients.
     * May be <code>null</code>.
     * @param interpolation Interpolation object.
     * May be <code>null</code>.
     * @param hints The <code>RenderingHints</code> to use.
     * May be <code>null</code>.
     * @return The <code>RenderedOp</code> destination.
     * @throws IllegalArgumentException if <code>source0</code> is <code>null</code>.
     */
    public static RenderedOp create(RenderedImage source0,
                                    Integer scaleX,
                                    Integer scaleY,
                                    float[] qsFilterArray,
                                    Interpolation interpolation,
                                    RenderingHints hints)  {
        ParameterBlockJAI pb =
            new ParameterBlockJAI("FilteredSubsample",
                                  RenderedRegistryMode.MODE_NAME);

        pb.setSource("source0", source0);

        pb.setParameter("scaleX", scaleX);
        pb.setParameter("scaleY", scaleY);
        pb.setParameter("qsFilterArray", qsFilterArray);
        pb.setParameter("interpolation", interpolation);

        return JAI.create("FilteredSubsample", pb, hints);
    }
} // FilteredSubsampleDescriptor
TOP

Related Classes of com.lightcrafts.mediax.jai.operator.FilteredSubsampleDescriptor

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.