All Downloads are FREE. Search and download functionalities are using the official Maven repository.

jaitools.media.jai.kernel.KernelFactory Maven / Gradle / Ivy

/*
 * Copyright 2009 Michael Bedward
 *
 * This file is part of jai-tools.

 * jai-tools is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.

 * jai-tools is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.

 * You should have received a copy of the GNU Lesser General Public
 * License along with jai-tools.  If not, see .
 *
 */
package jaitools.media.jai.kernel;

import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Envelope;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.MultiPoint;
import jaitools.CollectionFactory;
import java.awt.Point;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.PathIterator;
import java.awt.geom.Point2D;
import java.util.List;
import javax.media.jai.KernelJAI;

/**
 * A factory class with static methods to create a variety of
 * KernelJAI objects with specified geometries
 *
 * @author Michael Bedward
 * @since 1.0
 * @version $Id: KernelFactory.java 1383 2011-02-10 11:22:29Z michael.bedward $
 */
public class KernelFactory {

    /**
     * Meaning of the kernel values
     */
    public static enum ValueType {

        /**
         * Simple binary kernel with values of 1 or 0
         */
        BINARY,
        /**
         * The value of each kernel element is its distance to the
         * kernel's key element (the element that is placed over
         * image pixels during kernel operations).
         */
        DISTANCE,
        /**
         * The value of each kernel element is the inverse distance to the
         * kernel's key element (the element that is placed over
         * image pixels during kernel operations).
         */
        INVERSE_DISTANCE;
    };

    /**
     * Create a new KernelJAI object with a circular configuration.
     * Kernel elements within the circle will have value 1.0f; those
     * outside will have value 0.0f.
     * 

* This is equivalent to, but faster than, calling... *

* {@code createCircle(radius, Kernel.ValueType.BINARY, 1.0f) } * * @param radius radius of the circle * @return a new instance of KernelJAI */ public static KernelJAI createCircle(int radius) { if (radius <= 0) { throw new IllegalArgumentException( "Invalid radius (" + radius + "); must be > 0"); } KernelFactoryHelper kh = new KernelFactoryHelper(); float[] weights = kh.makeCircle(radius); int w = 2*radius + 1; kh.rowFill(weights, w, w); return new KernelJAI(w, w, weights); } /** * Creates a new KernelJAI object with a circular configuration. * The kernel width is 2*radius + 1. * The kernel's key element is at position x=radius, y=radius * * @param radius the radius of the circle expressed in pixels * * @param type one of * {@linkplain ValueType#BINARY}, * {@linkplain ValueType#DISTANCE} or * {@linkplain ValueType#INVERSE_DISTANCE} * * @param centreValue the value to assign to the kernel centre (key element) * * @return a new instance of KernelJAI */ public static KernelJAI createCircle(int radius, ValueType type, float centreValue) { if (radius <= 0) { throw new IllegalArgumentException( "Invalid radius (" + radius + "); must be > 0"); } KernelFactoryHelper kh = new KernelFactoryHelper(); int width = 2 * radius + 1; float[] weights = new float[width * width]; float r2 = radius * radius; int k0 = 0; int k1 = weights.length - 1; for (int y = radius; y > 0; y--) { int y2 = y * y; for (int x = -radius; x <= radius; x++, k0++, k1--) { float dist2 = x * x + y2; float value = 0f; if (kh.fcomp(r2, dist2) >= 0) { if (type == ValueType.DISTANCE) { value = (float) Math.sqrt(dist2); } else if (type == ValueType.INVERSE_DISTANCE) { value = 1.0f / (float) Math.sqrt(dist2); } else { value = 1.0f; } weights[k0] = weights[k1] = value; } } } for (int x = -radius; x <= radius; x++, k0++) { float value; if (x == 0) { value = centreValue; } else { if (type == ValueType.DISTANCE) { value = (float) Math.sqrt(x * x); } else if (type == ValueType.INVERSE_DISTANCE) { value = 1.0f / (float) Math.sqrt(x * x); } else { value = 1.0f; } } weights[k0] = value; } return new KernelJAI(width, width, weights); } /** * Creates a new KernelJAI object with an annular configuration * (like a doughnut). * * The kernel width is 2*outerRadius + 1. * The kernel's key element is at position x=outerRadius, y=outerRadius * *

* An IllegalArgumentException will be thrown if: *

    *
  • The value of outerRadius not greater than 0 *
  • The value of innerRadius is not less than outerRadius *
* * Calling this method with innerRadius == 0 is equivalent to * calling {@linkplain #createCircle } * * @param outerRadius the radius of the circle expressed in pixels * @param innerRadius the radius of the 'hole' of the annulus * * @param type one of * {@linkplain ValueType#BINARY}, * {@linkplain ValueType#DISTANCE} or * {@linkplain ValueType#INVERSE_DISTANCE} * * @param centreValue the value to assign to the kernel centre (key element) * * @return a new instance of KernelJAI * */ public static KernelJAI createAnnulus(int outerRadius, int innerRadius, ValueType type, float centreValue) { if (innerRadius < 0) { throw new IllegalArgumentException( "Invalid innerRadius (" + innerRadius + "); must be >= 0"); } if (outerRadius <= innerRadius) { throw new IllegalArgumentException("outerRadius must be greater than innerRadius"); } if (innerRadius == 0) { return createCircle(outerRadius, type, centreValue); } KernelFactoryHelper kh = new KernelFactoryHelper(); int width = 2 * outerRadius + 1; float[] weights = new float[width * width]; int outer2 = outerRadius * outerRadius; float inner2 = innerRadius * innerRadius; int k0 = 0; int k1 = weights.length - 1; for (int y = outerRadius; y > 0; y--) { int y2 = y * y; for (int x = -outerRadius; x <= outerRadius; x++, k0++, k1--) { float dist2 = x * x + y2; float value = 0f; if (kh.fcomp(dist2, outer2) <= 0 && kh.fcomp(dist2, inner2) > 0) { if (type == ValueType.DISTANCE) { value = (float) Math.sqrt(dist2); } else if (type == ValueType.INVERSE_DISTANCE) { value = 1.0f / (float) Math.sqrt(dist2); } else { value = 1.0f; } weights[k0] = weights[k1] = value; } } } for (int x = -outerRadius; x <= outerRadius; x++, k0++) { float value = 0f; if (x == 0) { value = centreValue; } else if (x < -innerRadius || x > innerRadius) { if (type == ValueType.DISTANCE) { value = (float) Math.sqrt(x * x); } else if (type == ValueType.INVERSE_DISTANCE) { value = 1.0f / (float) Math.sqrt(x * x); } else { value = 1.0f; } } weights[k0] = value; } return new KernelJAI(width, width, weights); } /** * Creates a new KernelJAI object with a rectangular configuraton. * An IllegalArgumentException will be thrown if width or height are less than 1. *

* This is equivalent to calling... *

* {@code createRectangle(width, height, Kernel.ValueType.BINARY, width/2, height/2, 1.0f) } * * @param width rectangle width * @param height rectangle height * * @return a new instance of KernelJAI */ public static KernelJAI createRectangle(int width, int height) { float [] weights = (new KernelFactoryHelper()).makeRect(width, height); return new KernelJAI(width, height, weights); } /** * Creates a new KernelJAI object with a rectangular configuration. *

* An IllegalArgumentException will be thrown if: *

    *
  • width or height are less than 1 *
  • keyX is not in the range 0:width-1 *
  • keyY is not in the range 0:height-1 *
* * @param width rectangle width * @param height rectangle height * * @param type one of * {@linkplain ValueType#BINARY}, * {@linkplain ValueType#DISTANCE} or * {@linkplain ValueType#INVERSE_DISTANCE} * * @param keyX x position of the key element * @param keyY y position of the key element (y coords increase downwards) * @param keyValue value of the key element * * @return a new instance of KernelJAI * */ public static KernelJAI createRectangle( int width, int height, ValueType type, int keyX, int keyY, float keyValue) { if (width < 1) { throw new IllegalArgumentException("width must be >= 1"); } if (height < 1) { throw new IllegalArgumentException("height must be >= 1"); } if (!(keyX >= 0 && keyX < width) || !(keyY >= 0 && keyY < height)) { throw new IllegalArgumentException("key element position " + keyX + "," + keyY + " is outside rectangle bounds"); } KernelFactoryHelper kh = new KernelFactoryHelper(); float weights[]; if (type == ValueType.BINARY) { weights = kh.makeRect(width, height); weights[keyX + keyY*width] = keyValue; return new KernelJAI(width, height, keyX, keyY, weights); } weights = new float[width*height]; float dist; int k = 0; Point2D p = new Point(keyX, keyY); for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++, k++) { dist = (float) p.distance(x, y); if (type == ValueType.DISTANCE) { weights[k] = dist; } else { weights[k] = 1.0f / dist; } } } weights[keyX + keyY*width] = keyValue; return new KernelJAI(width, height, keyX, keyY, weights); } /** * Create a new KernelJAI object by rasterizing a shape. The shape must be a closed * polygon. The rasterizing process checks whether the centre of each pixel is inside * the polygon. *

* This method can cope with arbitrary shape bounds, ie. there is no need to * set the bounding rectangle to have origin x=0, y=0. The values of keyX and keyY, * which specify the position of the kernel's key element, must be within the * bounds of the shape as passed to this method, but do not need to be inside the * shape itself. * * @param shape an object representing a closed polygon * @param transform an optional AffineTransform to relate shape coordinates to * kernel element coordinates. May be null. This is useful to scale and/or rotate * the shape. * * @param type one of * {@linkplain ValueType#BINARY}, * {@linkplain ValueType#DISTANCE} or * {@linkplain ValueType#INVERSE_DISTANCE} * * @param keyX the x coord of the key element * @param keyY the y coord of the key element * @param keyValue the value of the key element * * @return a new instance of KernelJAI */ public static KernelJAI createFromShape(Shape shape, AffineTransform transform, ValueType type, int keyX, int keyY, float keyValue) { /* * First we transform the shape to a JTS Polygon object * so we can take advantage of the JTS intersects method * and avoid rasterizing artefacts which can arise when * using Shape.contains */ PathIterator iter = shape.getPathIterator(transform, 0.05); float[] buf = new float[6]; List coords = CollectionFactory.list(); while (!iter.isDone()) { iter.currentSegment(buf); coords.add(new Coordinate(buf[0], buf[1])); iter.next(); } GeometryFactory gf = new GeometryFactory(); Coordinate[] coordsAr = coords.toArray(new Coordinate[coords.size()]); Geometry poly = gf.createPolygon(gf.createLinearRing(coordsAr), null); Envelope env = poly.getEnvelopeInternal(); int left = (int) Math.floor(env.getMinX()); int right = (int) Math.ceil(env.getMaxX()); int top = (int) Math.ceil(env.getMaxY()); int bottom = (int) Math.floor(env.getMinY()); int width = right - left + 1; int height = top - bottom + 1; float[] weights = new float[width * height]; int[] offset = new int[height]; for (int i = 0, o=0; i < offset.length; i++, o+=width) offset[i] = o; coords.clear(); double y = top; for (int iy = 0; iy < height; y--, iy++) { double x = left; for (int ix = 0; ix < width; x++, ix++) { coords.add(new Coordinate(x, y)); } } /* * Now we buffer the polygon by a small amount to avoid * rejection points that lie exactly on the boundary * and the points that intersect with the poly */ MultiPoint mp = gf.createMultiPoint(coords.toArray(new Coordinate[coords.size()])); Geometry inside = mp.intersection(poly.buffer(0.05, Math.max(width/2, 10))); /* * For each intersecting point we set a kernel element */ int n = inside.getNumGeometries(); for (int i = 0; i < n; i++) { Geometry g = inside.getGeometryN(i); Coordinate c = g.getCoordinate(); int index = (int) c.x - left + offset[(int) c.y - bottom]; if (type == ValueType.BINARY) { weights[index] = 1.0f; } else if (type == ValueType.DISTANCE) { weights[index] = (float) Point2D.distance(keyX, keyY, (int)c.x, (int)c.y); } else if (type == ValueType.INVERSE_DISTANCE) { weights[index] = 1.0f / (float) Point2D.distance(keyX, keyY, (int)c.x, (int)c.y); } } // set the key element to the requested value weights[keyX + offset[keyY]] = keyValue; return new KernelJAI(width, height, keyX, keyY, weights); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy