Commit in CDMS/src/CDMS/Partridge/ImageUtils on MAIN
ImageData.java+244added 1.1
ImageDescriptor.java+180added 1.1
InterpolatePixelIntensity.java+275added 1.1
PixelArray.java+402added 1.1
TransformedPixelArray.java+162added 1.1
+1263
5 added files
Put image utilities in its own package

CDMS/src/CDMS/Partridge/ImageUtils
ImageData.java added at 1.1
diff -N ImageData.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ ImageData.java	4 Oct 2010 22:49:31 -0000	1.1
@@ -0,0 +1,244 @@
+/*
+ * ImageData.java
+ */
+package CDMS.Partridge.ImageUtils;
+
+import java.awt.image.BufferedImage;
+
+/**
+ * Encapsulate the RGB color information associated with an image.
+ *
+ * @author Richard Partridge
+ */
+public class ImageData {
+
+    private PixelArray _pixarray;
+    private BufferedImage _image;
+    private int[][] _RGB;
+
+    /**
+     * Construct an empty image.
+     *
+     * @param pixarray pixel array descriptor
+     */
+    public ImageData(PixelArray pixarray) {
+        _pixarray = pixarray;
+        _RGB = new int[pixarray.getNRow()][pixarray.getNCol()];
+    }
+
+    /**
+     * Construct an image using the RGB data contained in a BufferedImage
+     *
+     * @param pixarray pixel array descriptor
+     * @param image image
+     */
+    public ImageData(PixelArray pixarray, BufferedImage image) {
+        _pixarray = pixarray;
+        _image = image;
+
+        //  Check that the image sizes match
+        if (_pixarray.getNRow() != image.getHeight() || _pixarray.getNCol() != image.getWidth())
+            throw new RuntimeException("Image size does not match pixel array descriptor");
+    }
+
+    /**
+     * Return the pixel array descriptor
+     *
+     * @return pixel array descriptor
+     */
+    public PixelArray getPixelArray() {
+        return _pixarray;
+    }
+
+    /**
+     * Return a BufferedImage associated with this image.
+     * 
+     * @return image
+     */
+    public BufferedImage getBufferedImage() {
+
+        //  Check if we already have an associated BufferedImage
+        if (_image == null) {
+
+            //  Create a new BufferedImage
+            int nrow = _pixarray.getNRow();
+            int ncol = _pixarray.getNCol();
+            _image = new BufferedImage(ncol, nrow, BufferedImage.TYPE_3BYTE_BGR);
+
+            //  Copy the image data
+            for (int row=0; row<nrow; row++) {
+                for (int col=0; col<ncol; col++) {
+                    _image.setRGB(col, row, _RGB[row][col]);
+                }
+            }
+        }
+
+        return _image;
+    }
+
+    /**
+     * Return the blue color data
+     *
+     * @param RGB packed color data
+     * @return blue color data
+     */
+    public int getBlue(int RGB) {
+        return RGB & 0xff;
+    }
+
+    /**
+     * Return the green color data
+     *
+     * @param RGB packed color data
+     * @return green color data
+     */
+    public int getGreen(int RGB) {
+        return (RGB & 0xff00) >> 8;
+    }
+
+    /**
+     * Return the red color data
+     *
+     * @param RGB packed color data
+     * @return red color data
+     */
+    public int getRed(int RGB) {
+        return (RGB & 0xff0000) >> 16;
+    }
+
+    /**
+     * Return the intensity from the color data
+     *
+     * @param red red color data
+     * @param green green color data
+     * @param blue blue color data
+     * @return intensity
+     */
+    public double getIntensity(int red, int green, int blue) {
+        return Math.sqrt((red * red + green * green + blue * blue) / 3);
+    }
+
+    /**
+     * Return the intensity from the packed color data
+     *
+     * @param RGB packed color data
+     * @return intensity
+     */
+    public double getIntensity(int RGB) {
+        return getIntensity(getRed(RGB), getGreen(RGB), getBlue(RGB));
+    }
+
+    /**
+     * Return the intensity for a given row/col in the pixel array
+     * @param row image row
+     * @param col image col
+     * @return intensity
+     */
+    public double getIntensity(int row, int col) {
+        return getIntensity(getRGB(row, col));
+    }
+
+    /**
+     * Set the packed color data for a pixel
+     * @param row image row
+     * @param col image column
+     * @param RGB packed color data
+     */
+    public void setRGB(int row, int col, int RGB) {
+        if (_image != null) throw new RuntimeException("Attempt to modify static image");
+        _RGB[row][col] = RGB;
+    }
+
+    /**
+     * Set the packed color data for a pixel
+     * @param row image row
+     * @param col image column
+     * @param red red color data
+     * @param green green color data
+     * @param blue blue color data
+     */
+    public void setRGB(int row, int col, int red, int green, int blue) {
+        setRGB(row, col, makeRGB(red, green, blue));
+    }
+
+    /**
+     * Set the packed color data for a pixel
+     *
+     * @param pixel pixel number as specified by the pixel array descriptor
+     * @param RGB packed color data
+     */
+    public void setRGB(long pixel, int RGB) {
+        setRGB(_pixarray.getRow(pixel), _pixarray.getCol(pixel), RGB);
+    }
+
+    /**
+     * Set the packed color data for a pixel
+     *
+     * @param pixel pixel number as specified by the pixel array descriptor
+     * @param red red color data
+     * @param green green color data
+     * @param blue blue color data
+     */
+    public void setRGB(long pixel, int red, int green, int blue) {
+        setRGB(pixel, makeRGB(red, green, blue));
+    }
+
+    /**
+     * Return the packed color data for a given element in the image
+     *
+     * @param row image row
+     * @param col image column
+     * @return packed color data
+     */
+    public int getRGB(int row, int col) {
+        if (_image == null) return _RGB[row][col];
+        int RGB = _image.getRGB(col, row);
+        return translateBufferedImageRGB(RGB);
+    }
+
+    /**
+     * Return the packed color data for a pixel
+     *
+     * @param pixel pixel number as specified by the pixel array descriptor
+     * @return packed color data
+     */
+    public int getRGB(long pixel) {
+        return getRGB(_pixarray.getRow(pixel), _pixarray.getCol(pixel));
+    }
+
+    /**
+     * Translate from the BufferedImage color scheme to the one used here.  At
+     * present, it is believe these schemes are the same based on trial and
+     * error techniques.  Since it is not entirely clear what format is used
+     * for RGB data in a BufferedImage, keep this simple method as a place
+     * holder for dealing with possible future complications and for now
+     * just mask off the upper 8 bits.
+     *
+     * @param RGB color data for a BufferedImage pixel
+     * @return packed color data
+     */
+    private int translateBufferedImageRGB(int RGB) {
+        //  Since the pixel RGB scheme is believed to match the BufferedImage convention,
+        //  skip decoding the BufferedImage convention
+        // int blue = pixel & 0x000000ff;
+        // int green = (pixel & 0x0000ff00) >> 8;
+        // int red = (pixel & 0x00ff0000) >> 16;
+        // return makePixel(red, green, blue);
+        return RGB & 0xffffff;
+    }
+
+    /**
+     * Return the packed color given the color components
+     *
+     * @param red red color data
+     * @param green green color data
+     * @param blue blue color data
+     * @return packed color data
+     */
+    private int makeRGB(int red, int green, int blue) {
+        if (red < 0 || red > 0xff) throw new RuntimeException("Invalid RGB data for red: "+red);
+        if (green < 0 || green > 0xff) throw new RuntimeException("Invalid RGB data for green: "+green);
+        if (blue < 0 || blue > 0xff) throw new RuntimeException("Invalid RGB data for blue: "+blue);
+        return (red << 16) + (green << 8) + blue;
+    }
+}

CDMS/src/CDMS/Partridge/ImageUtils
ImageDescriptor.java added at 1.1
diff -N ImageDescriptor.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ ImageDescriptor.java	4 Oct 2010 22:49:31 -0000	1.1
@@ -0,0 +1,180 @@
+/*
+ * ImageDescriptor.java
+ */
+
+package CDMS.Partridge.ImageUtils;
+
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.IOException;
+import javax.imageio.ImageIO;
+
+/**
+ * Encapsulate the meta-data associated with an image file
+ *
+ * @author Richard Partrdge
+ */
+public class ImageDescriptor {
+
+   private String _filename;
+   private int _id;
+   private double _xc;
+   private double _yc;
+   private double _dx;
+   private double _dy;
+   private int _nrow;
+   private int _ncol;
+
+   /**
+    * Fully qualified constructor.
+    *
+    * @param filename name of image file
+    * @param id unique image identifier
+    * @param xc x coordinate of image center
+    * @param yc y coordinate of image center
+    * @param dx x pixel size
+    * @param dy y pixel size
+    * @param nrow number of rows for image
+    * @param ncol number of columns for image
+    */
+    public ImageDescriptor(String filename, int id, double xc, double yc, double dx,
+            double dy, int nrow, int ncol) {
+        _filename = filename;
+        _id = id;
+        _xc = xc;
+        _yc = yc;
+        _dx = dx;
+        _dy = dy;
+        _nrow = nrow;
+        _ncol = ncol;
+    }
+
+    /**
+    * Constructor that gets image size from the image itself
+    * 
+    * @param filename name of image file
+    * @param id unique image identifier
+    * @param xc x coordinate of image center
+    * @param yc y coordinate of image center
+    * @param dx x pixel size
+    * @param dy y pixel size
+    */
+    public ImageDescriptor(String filename, int id, double xc, double yc, double dx, double dy) {
+        _filename = filename;
+        _id = id;
+        _xc = xc;
+        _yc = yc;
+        _dx = dx;
+        _dy = dy;
+        BufferedImage bufimage = getBufferedImage();
+        _nrow = bufimage.getHeight();
+        _ncol = bufimage.getWidth();
+    }
+
+    /**
+     * Return the BufferedImage associated with this image descriptor
+     *
+     * @return image
+     */
+    public BufferedImage getBufferedImage() {
+        File file = new File(_filename);
+        BufferedImage bufimage = null;
+        try {
+            bufimage = ImageIO.read(file);
+        } catch (IOException ex) {
+            System.out.println("Image file not found: "+_filename);
+        }
+        return bufimage;
+    }
+
+    /**
+     * Return the PixelArray associated with this image descriptor
+     *
+     * @return pixel array descriptor
+     */
+    public PixelArray getPixelArray() {
+        return new PixelArray(_nrow, _ncol, _xc, _yc, _dx, _dy);
+    }
+
+    /**
+     * Return the ImageData associated with this image descriptor
+     *
+     * @return image
+     */
+    public ImageData getImageData() {
+        return new ImageData(getPixelArray(), getBufferedImage());
+    }
+
+    /**
+     * Return image identifer
+     * 
+     * @return
+     */
+    public int getIdentifer() {
+        return _id;
+    }
+
+    /**
+     * Retrun the x pixel size
+     *
+     * @return x pixel size
+     */
+    public double getXSize() {
+        return _dx;
+    }
+
+    /**
+     * Return the y pixel size
+     *
+     * @return y pixel size
+     */
+    public double getYSize() {
+        return _dy;
+    }
+
+    /**
+     * Return the file name containing the image
+     *
+     * @return image file name
+     */
+    public String getFilename() {
+        return _filename;
+    }
+
+    /**
+     * Return the number of columns for this image
+     *
+     * @return number of columns
+     */
+    public int getNCol() {
+        return _ncol;
+    }
+
+    /**
+     * Return the number of rows for this image
+     *
+     * @return number of rows
+     */
+    public int getNRow() {
+        return _nrow;
+    }
+
+    /**
+     * Return the x coordinate of the image center
+     *
+     * @return x coordinate
+     */
+    public double getXC() {
+        return _xc;
+    }
+
+    /**
+     * Return the y coordinate of the image center
+     *
+     * @return y coordinate
+     */
+    public double getYC() {
+        return _yc;
+    }
+
+}

CDMS/src/CDMS/Partridge/ImageUtils
InterpolatePixelIntensity.java added at 1.1
diff -N InterpolatePixelIntensity.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ InterpolatePixelIntensity.java	4 Oct 2010 22:49:31 -0000	1.1
@@ -0,0 +1,275 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package CDMS.Partridge.ImageUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ *
+ * @author richp
+ */
+public class InterpolatePixelIntensity {
+
+    private ImageData _ref;
+    private TransformedPixelArray _refpix;
+    private TransformedPixelArray _testpix;
+    private boolean _rotated;
+    private double _eps = 1.e-10;
+    private int _drow;
+    private int _dcol;
+    private double[] _wrow = new double[3];
+    private double[] _wcol = new double[3];
+
+    public InterpolatePixelIntensity(ImageData ref, TransformedPixelArray refpix, TransformedPixelArray testpix) {
+        _ref = ref;
+        _refpix = refpix;
+        _testpix = testpix;
+        _rotated = refpix.isRotated() || testpix.isRotated();
+        if (!_rotated) {
+            double dx = (testpix.getXC() - refpix.getXC()) / testpix.getSize();
+            double dy = (testpix.getYC() - refpix.getYC()) / testpix.getSize();
+            int dcol = (int) Math.round(dx);
+            int drow = (int) Math.round(dy);
+//            System.out.println("dx: " + dx + " dy: " + dy + " dcol: " + dcol + " drow: " + drow);
+            dx -= dcol;
+            dy -= drow;
+            if (dx < 0.) {
+                _wcol[0] = -dx;
+            }
+            if (dx > 0.) {
+                _wcol[2] = dx;
+            }
+            _wcol[1] = 1. - _wcol[0] - _wcol[2];
+            if (dy < 0.) {
+                _wrow[0] = -dy;
+            }
+            if (dy > 0.) {
+                _wrow[2] = dy;
+            }
+            _wrow[1] = 1. - _wrow[0] - _wrow[2];
+//            System.out.println("wrow: " + _wrow[0] + " , " + _wrow[1] + " , " + _wrow[2]);
+//            System.out.println("wcol: " + _wcol[0] + " , " + _wcol[1] + " , " + _wcol[2]);
+
+        }
+    }
+
+    public double Interpolate(int pixel) {
+
+        //  Find the center of the current test pixel
+        double[] testcenter = _testpix.getCenter(pixel);
+
+        //  Find the reference pixel containing the center of the test pixel
+        int refcenter = _refpix.getPixel(testcenter[0], testcenter[1]);
+
+//        double[] center = _refpix.getCenter(refcenter);
+//        if (refcenter == 640 * 40 + 40) {
+//            System.out.println("test: " + testcenter[0] + " , " + testcenter[1]);
+//            System.out.println("ref: " + center[0] + " , " + center[1]);
+//            System.out.println("nbrs: " + _refpix.getNeighbors(refcenter).size());
+//        }
+        //  Get the list of reference pixels to check for overlaps
+        List<Integer> refnbrs = _refpix.getNeighbors(refcenter);
+
+        //  Initialize the pixel area and weighted intensity sums
+        double totarea = 0.;
+        double intensity = 0.;
+
+        if (!_rotated) {
+            int row0 = _refpix.getRow(refcenter);
+            int col0 = _refpix.getCol(refcenter);
+            for (int nbr : refnbrs) {
+                int row = _refpix.getRow(nbr);
+                int col = _refpix.getCol(nbr);
+                double wgt = _wrow[row - row0 + 1] * _wcol[col - col0 + 1];
+                if (wgt > 0.) {
+                    intensity += wgt * _ref.getIntensity(row, col);
+                }
+                totarea += Math.abs(wgt);
+//                if (refcenter == 640 * 40 + 40) {
+//                    double[] rcenter = _refpix.getCenter(nbr);
+//                    System.out.println("ref: " + rcenter[0] + " , " + rcenter[1]);
+//                    System.out.println("wrow: " + _wrow[row - row0 + 1] + " wcol: " + _wcol[col - col0 + 1]);
+//                    System.out.println("wgt: " + wgt + " intensity: " + _ref.getIntensity(row, col));
+//                }
+            }
+            if (Math.abs(totarea - 1) > _eps) {
+                throw new RuntimeException("Area integral failure");
+            }
+            return intensity;
+        }
+
+        //  Loop over the list of reference pixels
+        for (Integer nbr : refnbrs) {
+
+            //  Create a list for vertices of the overlap region between this ref pixel and the test pixel
+            List<double[]> vertices = new ArrayList<double[]>();
+
+            //  Loop over the reference pixel vertices and see if the reference vertex
+            //  is inside the test pixel.  If so add it to the list of vertices (don't
+            //  worry about duplicate vertices - they contribute no additional polygon area
+            for (int i = 0; i < 4; i++) {
+                double[] vtx = _refpix.getVertex(i, nbr);
+                if (_testpix.Inside(pixel, vtx)) {
+                    vertices.add(vtx);
+                }
+            }
+
+            //  Now loop over the test pixel vertices
+            for (int i = 0; i < 4; i++) {
+                double[] vtx = _testpix.getVertex(i, pixel);
+                if (_refpix.Inside(nbr, vtx)) {
+                    vertices.add(vtx);
+                }
+            }
+
+            //  Finally, find any vertices produced by intersections of pixel edges
+            for (int i1 = 0; i1 < 4; i1++) {
+                int i2 = (i1 + 1) % 4;
+                double[] vt1 = _testpix.getVertex(i1, pixel);
+                double[] vt2 = _testpix.getVertex(i2, pixel);
+                for (int j1 = 0; j1 < 4; j1++) {
+                    int j2 = (j1 + 1) % 4;
+                    double[] vr1 = _refpix.getVertex(j1, nbr);
+                    double[] vr2 = _refpix.getVertex(j2, nbr);
+                    double[] vtx = findIntersection(vt1, vt2, vr1, vr2);
+                    if (vtx != null) {
+                        vertices.add(vtx);
+                    }
+                }
+            }
+
+            //  If there are no vertices, the test pixel and this reference pixel don't overlap
+            if (vertices.size() == 0) {
+                continue;
+            }
+
+            //  Find the area of overlap between the test pixel and this reference neighbor pixel
+            double area = getArea(vertices);
+            totarea += area;
+
+            //  Take the interpolated intensity to be the sum of the reference neighbor intensities
+            //  weighted by the overlap area
+            int refrow = _refpix.getRow(nbr);
+            int refcol = _refpix.getCol(nbr);
+            intensity += area * _ref.getIntensity(refrow, refcol);
+        }
+
+        //  Check to make sure the total pixel area is unity
+        if (Math.abs(totarea - 1.) > Math.sqrt(_eps)) {
+            System.out.println(" ***** Grand total area: " + totarea + " *****");
+            throw new RuntimeException("area sum error");
+        }
+
+        return intensity;
+    }
+
+    public double getNormalizionWeight() {
+        double winv = 1.0;
+        for (int row=0; row<2; row++) {
+            for (int col=0; col<2; col++) {
+                winv += Math.pow(_wrow[row]*_wcol[col], 2);
+            }
+        }
+        return 1.0 / winv;
+    }
+
+    private double[] findIntersection(double[] vt1, double[] vt2, double[] vr1, double[] vr2) {
+
+        //  Find the variables defining the line between vt1 and vt2
+        double a1 = vt2[1] - vt1[1];
+        double b1 = vt1[0] - vt2[0];
+        double c1 = a1 * vt1[0] + b1 * vt1[1];
+
+        //  Find the variables defining the line between vr1 and vr2
+        double a2 = vr2[1] - vr1[1];
+        double b2 = vr1[0] - vr2[0];
+        double c2 = a2 * vr1[0] + b2 * vr1[1];
+
+        //  Calculate the determinant, check for parallel lines
+        double det = a1 * b2 - a2 * b1;
+        if (Math.abs(det) < _eps) {
+            return null;
+        }
+
+        //  Find the intersection using the determinant method
+        double x = (b2 * c1 - b1 * c2) / det;
+        double y = (a1 * c2 - a2 * c1) / det;
+
+        //  Check that the x coordinate of the intersection is on the line segment
+        if (x < Math.min(vt1[0], vt2[0]) - _eps || x > Math.max(vt1[0], vt2[0]) + _eps) {
+            return null;
+        }
+        if (x < Math.min(vr1[0], vr2[0]) - _eps || x > Math.max(vr1[0], vr2[0]) + _eps) {
+            return null;
+        }
+
+        //  Also check y in case the line is vertical
+        if (y < Math.min(vt1[1], vt2[1]) - _eps || y > Math.max(vt1[1], vt2[1]) + _eps) {
+            return null;
+        }
+        if (y < Math.min(vr1[1], vr2[1]) - _eps || y > Math.max(vr1[1], vr2[1]) + _eps) {
+            return null;
+        }
+
+        double[] cross = {x, y};
+        return cross;
+    }
+
+    private double getArea(List<double[]> polygon) {
+
+        //  Check that we have at least 3 polygon vertices
+        int nv = polygon.size();
+        if (nv < 3) {
+            return 0.;
+        }
+
+        //  Order the vertices so that adjacent vertices describe a line segment in the
+        //  polygon
+        OrderVertices(polygon);
+
+        //  Find the area of the polygon (see, for example, http://local.wasp.uwa.edu.au/~pbourke/geometry/polyarea/)
+        double area = 0.;
+        for (int i = 0; i < nv; i++) {
+            int j = (i + 1) % nv;
+            double[] p0 = polygon.get(i);
+            double[] p1 = polygon.get(j);
+            double darea = 0.5 * (p0[0] * p1[1] - p1[0] * p0[1]);
+            area += darea;
+        }
+        return Math.abs(area);
+    }
+
+    private double[] PseudoCentroid(List<double[]> polygon) {
+
+        // Find a point within the convex polygon by averaging the coordinates of all vertices
+        double[] pcent = {0., 0.};
+        int nv = polygon.size();
+        for (double[] point : polygon) {
+            pcent[0] += point[0] / nv;
+            pcent[1] += point[1] / nv;
+        }
+        return pcent;
+    }
+
+    private void OrderVertices(List<double[]> polygon) {
+
+        //  Take as an origin a point within the polygon and order the polygon vertices according to their azimuthal angle
+        double[] pcent = PseudoCentroid(polygon);
+        int nv = polygon.size();
+        for (int i = 0; i < nv - 1; i++) {
+            for (int j = i + 1; j < nv; j++) {
+                //  phi1 calcuation must stay inside loop because of possible re-ordering
+                double phi1 = Math.atan2(polygon.get(i)[1] - pcent[1], polygon.get(i)[0] - pcent[0]);
+                double phi2 = Math.atan2(polygon.get(j)[1] - pcent[1], polygon.get(j)[0] - pcent[0]);
+                if (phi1 > phi2) {
+                    double[] temp = polygon.get(j);
+                    polygon.set(j, polygon.get(i));
+                    polygon.set(i, temp);
+                }
+            }
+        }
+    }
+}

CDMS/src/CDMS/Partridge/ImageUtils
PixelArray.java added at 1.1
diff -N PixelArray.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ PixelArray.java	4 Oct 2010 22:49:32 -0000	1.1
@@ -0,0 +1,402 @@
+/*
+ * PixelArray.java
+ */
+
+package CDMS.Partridge.ImageUtils;
+
+import java.awt.image.BufferedImage;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+/**
+ * This class encapsulates a description of a pixel array associated with a physical
+ * object.  Sufficient information is available to provide convert between a pixel
+ * identifier, x/y coordinates, and row/column of the image.
+ * 
+ * The pixel array may have associated with it non-overlapping subarrays.  This
+ * allows pixel array descriptions to be composed of smaller pixel arrays stitched
+ * together to form a much larger pixel array descriptor.
+ *
+ * @author Richard Partridge
+ */
+public class PixelArray {
+
+    private int _nrow;
+    private int _ncol;
+    private double _xc;
+    private double _yc;
+    private double _dx;
+    private double _dy;
+    private Map<Integer, PixelArray> _subarraymap;
+    private double _eps = 1.e-4;  // set tolerance to 0.1 um
+
+    /**
+     * Fully qualified constructor
+     *
+     * @param nrow number of rows for this pixel array
+     * @param ncol number of columns for this pixel array
+     * @param xc x coordinate of the center of the pixel array
+     * @param yc y coordinate of the center of the pixel array
+     * @param dx x pixel size
+     * @param dy y pixel size
+     */
+    public PixelArray(int nrow, int ncol, double xc, double yc, double dx, double dy) {
+        _nrow = nrow;
+        _ncol = ncol;
+        _xc = xc;
+        _yc = yc;
+        _dx = dx;
+        _dy = dy;
+        if (_nrow < 0 || _ncol < 0) throw new RuntimeException("Negative array size - nrow: "+nrow+" ncol: "+ncol);
+        if (_dx <= 0. || _dy <= 0.) throw new RuntimeException("Negative pixel size - dx: "+_dx+" dy: "+dy);
+        _subarraymap = new HashMap<Integer, PixelArray>();
+     }
+
+    /**
+     * Constructor that uses a Buffered Image to specify the number of row/columns in
+     * the pixel array
+     *
+     * @param bufimage BufferedImage represented by this pixel array descriptor
+     * @param xc x coordinate of the center of the pixel array
+     * @param yc y coordinate of the center of the pixel array
+     * @param dx x pixel size
+     * @param dy y pixel size
+     */
+    public PixelArray(BufferedImage bufimage, double xc, double yc, double dx, double dy) {
+        this(bufimage.getHeight(), bufimage.getWidth(), xc, yc, dx, dy);
+     }
+
+    /**
+     * Return the row number for a given pixel
+     *
+     * @param pixel pixel identifier
+     * @return row number
+     */
+    public int getRow(long pixel) {
+        int row = (int) ((pixel >> 32) & 0xffffffffL);
+        if (row < 0 || row >= _nrow) throw new RuntimeException("Invalid pixel number"+pixel);
+        return row;
+    }
+
+    /**
+     * Return the row number for a given y coordinate
+     *
+     * @param y y coordinate
+     * @return row number
+     */
+    public int getRow(double y) {
+        int ipix = (int) Math.floor((y - _yc) / _dy + _nrow / 2.);
+        if (ipix < 0 || ipix >= _nrow) throw new RuntimeException("Row outside pixel array for y = "+y);
+        return ipix;
+    }
+
+    /**
+     * Return the column number for a given pixel
+     * @param pixel pixel identifier
+     * @return column number
+     */
+    public int getCol(long pixel) {
+        int col = (int) (pixel & 0xffffffffL);
+        if (col < 0 || col >= _ncol) throw new RuntimeException("Invalid pixel number"+pixel);
+        return col;
+    }
+
+    /**
+     * Return the column number for a given x coordinate
+     *
+     * @param x x coordinate
+     * @return column number
+     */
+    public int getCol(double x) {
+        int ipix = (int) Math.floor((x - _xc) / _dx + _ncol / 2.);
+        if (ipix < 0 || ipix >= _ncol) throw new RuntimeException("Col outside pixel array for x = "+x);
+        return ipix;
+    }
+
+    /**
+     * Return the pixel identifier for a given row and column
+     * @param row row number
+     * @param col column number
+     * @return pixel identifier
+     */
+    public long getPixel(int row, int col) {
+        if (row < 0 || row >= _nrow) throw new RuntimeException("Invalid row number"+row);
+        if (col < 0 || col >= _ncol) throw new RuntimeException("Invalid col number"+col);
+        long lrow = row;
+        long lcol = col;
+        return (lrow << 32) + lcol;
+    }
+
+    /**
+     * Return the pixel identifier for a given x,y coordinate
+     *
+     * @param x x coordinate
+     * @param y y coordinate
+     * @return pixel identifier
+     */
+    public long getPixel(double x, double y) {
+        return getPixel(getRow(y), getCol(x));
+    }
+
+    /**
+     * Return the number of rows associated with this pixel array
+     *
+     * @return number of rows
+     */
+    public int getNRow() {
+        return _nrow;
+    }
+
+    /**
+     * Return the number of columns associated with this pixel array
+     *
+     * @return number of columns
+     */
+    public int getNCol() {
+        return _ncol;
+    }
+
+    /**
+     * Return the x coordinate of a given pixel
+     *
+     * @param pixel pixel identifier
+     * @return x coordinate
+     */
+    public double getX(long pixel) {
+        return _xc + (getCol(pixel) - (_ncol - 1.) / 2.) * _dx;
+    }
+
+    /**
+     * Return the y coordinate of a given pixel
+     *
+     * @param pixel pixel identifier
+     * @return y coordinate
+     */
+    public double getY(long pixel) {
+        return _yc + (getRow(pixel) - (_nrow - 1.) / 2.) * _dy;
+    }
+
+    /**
+     * Return the x coordinate of the center of the pixel array
+     *
+     * @return x center coordinate
+     */
+    public double getXC() {
+        return _xc;
+    }
+
+    /**
+     * Return the y coordinate of the center of the pixel array
+     *
+     * @return y center coordinate
+     */
+    public double getYC() {
+        return _yc;
+    }
+
+    /**
+     * Return the width in x of a pixel
+     *
+     * @return x pixel size
+     */
+    public double getXSize() {
+        return _dx;
+    }
+
+    /**
+     * Return the height in y of a pixel
+     *
+     * @return y pixel size
+     */
+    public double getYSize() {
+        return _dy;
+    }
+
+    /**
+     * Return the minimum x coordinate for the pixel array
+     *
+     * @return minimum x coordinate
+     */
+    public double getXMin() {
+        return _xc - 0.5 * _ncol * _dx;
+    }
+
+    /**
+     * Return the maximum x coordinate for the pixel array
+     *
+     * @return maximum x coordinate
+     */
+    public double getXMax() {
+        return _xc + 0.5 * _ncol * _dx;
+    }
+
+    /**
+     * Return the minimum y coordinate of the pixel array
+     *
+     * @return minimum y coordinate
+     */
+    public double getYMin() {
+        return _yc - 0.5 * _nrow * _dy;
+    }
+
+    /**
+     * Return the maximum y coordinate of the pixel array
+     *
+     * @return maximum y coordinate
+     */
+    public double getYMax() {
+        return _yc + 0.5 * _nrow * _dy;
+    }
+
+    /**
+     * Check whether a given x coordinate is within the pixel array
+     *
+     * @param x x coordinate
+     * @return true if inside
+     */
+    public boolean xInside(double x) {
+        return Math.abs(x -_xc) < 0.5 * _ncol * _dx + _eps;
+    }
+
+    /**
+     * Check whether a given y coordinate is within the pixel array
+     *
+     * @param y y coordinate
+     * @return true if inside
+     */
+    public boolean yInside(double y) {
+        return Math.abs(y -_yc) < 0.5 * _nrow * _dy + _eps;
+    }
+
+    /**
+     * Check whether a given x,y coordinate is within the pixel array
+     *
+     * @param x x coordinate
+     * @param y y coordinate
+     * @return true if inside
+     */
+    public boolean Inside(double x, double y) {
+        return xInside(x) && yInside(y);
+    }
+
+    /**
+     * Check whether a given pixel identifier describes a pixel within the pixel array
+     *
+     * @param pixel pixel identifier
+     * @return true if inside
+     */
+    public boolean Inside(long pixel) {
+        return Inside(getX(pixel), getY(pixel));
+    }
+
+    /**
+     * Add a subarray to this pixel array.  The subarray must be fully contained
+     * within the pixel array and not overlap any other subarrays that are present.
+     *
+     * @param id identifier for this subarray (must be >=0)
+     * @param sub subarray
+     */
+    public void addSubArray(int id, PixelArray sub) {
+
+        //  Check that we have a positive subarray identifer (negative values will be used to
+        //  identify error conditions)
+        if (id < 0) throw new RuntimeException("Subarray ID must be non-negative - ID: "+id);
+
+        //  Make sure that the identifier is unique
+        if (_subarraymap.containsKey(id)) throw new RuntimeException("Duplicate subarray identifier");
+
+        //  Make sure subarray is entirely inside this pixel array
+        if (!Inside(sub.getXMin(), sub.getYMin()) || !Inside(sub.getXMax(), sub.getYMax()))
+                throw new RuntimeException("Subarray is not contained within this pixel array");
+
+        //  Loop over the other subarrays and make sure that we don't overlap any of them
+        for (PixelArray check : _subarraymap.values()) {
+            long ll = getPixel(0, 0);
+            long lr = getPixel(0, check.getNCol() -1);
+            long ul = getPixel(check.getNRow() - 1, 0);
+            long ur = getPixel(check.getNRow() - 1, check.getNCol() - 1);
+            if (Inside(ll) || Inside(lr) || Inside(ul) || Inside(ur))
+                throw new RuntimeException("New subarray overlaps an existing subarray");
+        }
+
+        //  Put the subarray into the map that contains the subarrays and their identifiers
+        _subarraymap.put(id, sub);
+    }
+
+    /**
+     * Get the subarray identifier associated with a given x,y position. Returns
+     * a value of -1 if there is no subarray containing these coordinates.
+     *
+     * @param x x coordinate
+     * @param y y coordinate
+     * @return subarray identifier
+     */
+    public int getSubArrayID(double x, double y) {
+        for (Entry<Integer, PixelArray> sub : _subarraymap.entrySet()) {
+            if (sub.getValue().Inside(x, y)) return sub.getKey();
+        }
+        return -1;
+    }
+
+    /**
+     * Return the subarray descriptor associated with a given x,y position
+     *
+     * @param x x coordinate
+     * @param y y coordinate
+     * @return subarray
+     */
+    public PixelArray getSubArray(double x, double y) {
+        int id = getSubArrayID(x, y);
+        return _subarraymap.get(id);
+    }
+
+    /**
+     * Return the subarray descriptor for a given ID
+     *
+     * @param id subarray identifier
+     * @return subarray
+     */
+    public PixelArray getSubArray(int id) {
+        return _subarraymap.get(id);
+    }
+
+    /**
+     * Return a list of neighbors to a given pixel
+     *
+     * @param pixel pixel identifier
+     * @return set of neighbor pixels
+     */
+    public Set<Long> getNeighbors(long pixel) {
+        Set<Long> nbrs = new HashSet<Long>();
+        int row = getRow(pixel);
+        int col = getCol(pixel);
+        for (int irow=row-1; irow<=row+1; irow++) {
+            if (irow < 0 || irow >= _nrow) continue;
+            for (int icol=col-1; icol<=col+1; icol++) {
+                if (icol < 0 || icol >= _ncol) continue;
+                nbrs.add(getPixel(irow, icol));
+            }
+        }
+        return nbrs;
+    }
+
+    public boolean equals(PixelArray test) {
+        if (_ncol != test.getNCol()) return false;
+        if (_nrow != test.getNRow()) return false;
+        if (Math.abs(_xc - test.getXC()) > _eps) return false;
+        if (Math.abs(_yc - test.getYC()) > _eps) return false;
+        if (Math.abs(_dx - test.getXSize()) > _eps / _ncol) return false;
+        if (Math.abs(_dy - test.getYSize()) > _eps / _nrow) return false;
+        for (Entry<Integer, PixelArray> entry : _subarraymap.entrySet()) {
+            int id = entry.getKey();
+            PixelArray subarray = entry.getValue();
+            PixelArray testsub = test._subarraymap.get(id);
+            if (testsub == null) return false;
+            if (!subarray.equals(testsub)) return false;
+        }
+        return true;
+    }
+}

CDMS/src/CDMS/Partridge/ImageUtils
TransformedPixelArray.java added at 1.1
diff -N TransformedPixelArray.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ TransformedPixelArray.java	4 Oct 2010 22:49:32 -0000	1.1
@@ -0,0 +1,162 @@
+/*
+ * TranformedPixelArray.java
+ */
+
+package CDMS.Partridge.ImageUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ *
+ * @author Richard Partridge
+ */
+public class TransformedPixelArray {
+
+    private double _x0;
+    private double _y0;
+    private double _rotang;
+    private double _size;
+    private int _nx;
+    private int _ny;
+    private double _xc;
+    private double _yc;
+    private double _u[] = new double[2];
+    private double _v[] = new double[2];
+    private double[] _xv = new double[4];
+    private double[] _yv = new double[4];
+    private double _eps = 1.e-6;
+
+    public TransformedPixelArray(double x0, double y0, double rotang, double size, int nx, int ny) {
+
+        //  Save the parameters that define the pixel array
+        _x0 = x0;
+        _y0 = y0;
+        _rotang = rotang;
+        _size = size;
+        _nx = nx;
+        _ny = ny;
+
+        //  Get unit vectors along the col (u) and row (v) directions
+        double cphi = Math.cos(rotang);
+        double sphi = Math.sin(rotang);
+        _u[0] = cphi;
+        _u[1] = sphi;
+        _v[0] = -sphi;
+        _v[1] = cphi;
+
+        //  Find the coordinates of the LL pixel given offsets x0, y0 specified for
+        //  the center pixel
+        double xll = _x0 + 0.5 * _size * (_nx * (1 - _u[0]) - _ny * _v[0]);
+        double yll = _y0 + 0.5 * _size * (_ny * (1 - _v[1]) - _nx * _u[1]);
+
+        //  Find the vertices of pixel 0 whose LL corner is at the origin
+        //  Vertices are stored in a clockwise direction
+        _xv[0] = xll;
+        _yv[0] = yll;
+        _xv[1] = xll + _size * _v[0];
+        _yv[1] = yll + _size * _v[1];
+        _xv[2] = xll + _size * (_u[0] + _v[0]);
+        _yv[2] = yll + _size * (_u[1] + _v[1]);
+        _xv[3] = xll + _size * _u[0];
+        _yv[3] = yll + _size * _u[1];
+
+        //  Find the center
+        _xc = (_xv[0] + _xv[2]) / 2.;
+        _yc = (_yv[0] + _yv[2]) / 2.;
+    }
+
+    public double[] getCenter(int pixel) {
+        int row = getRow(pixel);
+        int col = getCol(pixel);
+        double[] center = new double[2];
+        center[0] = _xc + _size * (col * _u[0] + row * _v[0]);
+        center[1] = _yc + _size * (col * _u[1] + row * _v[1]);
+        return center;
+    }
+
+    public boolean Inside(int pixel, double[] vtx0) {
+        for (int i=0; i<4; i++) {
+            int j = (i+1) % 4;
+            double[] vtx1 = getVertex(i, pixel);
+            double[] vtx2 = getVertex(j, pixel);
+            if (twicearea(vtx1[0], vtx1[1], vtx2[0], vtx2[1], vtx0[0], vtx0[1]) < 0.) return false;
+        }
+        return true;
+    }
+
+    public double[] getVertex(int ivtx, int pixel) {
+        int row = getRow(pixel);
+        int col = getCol(pixel);
+        double[] vtx = new double[2];
+        vtx[0] = _xv[ivtx] + _size * (col * _u[0] + row * _v[0]);
+        vtx[1] = _yv[ivtx] + _size * (col * _u[1] + row * _v[1]);
+        return vtx;
+    }
+
+    public int getRow(int pixel) {
+        int row = pixel / _nx;
+        if (row < 0 || row >= _ny) throw new RuntimeException("Invalid pixel number");
+        return row;
+    }
+
+    public int getCol(int pixel) {
+        int col = pixel % _nx;
+        if (col < 0 || col >= _nx) throw new RuntimeException("Invalid pixel number");
+        return col;
+    }
+
+    public int getPixel(int row, int col) {
+        if (row < 0 || row >= _ny) throw new RuntimeException("Invalid row number");
+        if (col < 0 || col >= _nx) throw new RuntimeException("Invalid col number");
+        return row * _nx + col;
+    }
+
+    public int getPixel(double x, double y) {
+        int col = (int) Math.round(((x-_xc)*_u[0] + (y-_yc)*_u[1]) / _size);
+        int row = (int) Math.round(((x-_xc)*_v[0] + (y-_yc)*_v[1]) / _size);
+        return getPixel(row, col);
+    }
+
+    public int getNRow() {
+        return _ny;
+    }
+
+    public int getNCol() {
+        return _nx;
+    }
+
+    public List<Integer> getNeighbors(int pixel) {
+        List<Integer> nbrs = new ArrayList<Integer>();
+        int row = getRow(pixel);
+        int col = getCol(pixel);
+        for (int irow=row-1; irow<row+2; irow++) {
+            if (irow < 0 || irow >= _ny) continue;
+            for (int icol=col-1; icol<col+2; icol++) {
+                if (icol < 0 || icol > _nx) continue;
+                nbrs.add(getPixel(irow, icol));
+            }
+        }
+        return nbrs;
+    }
+
+    public boolean isRotated() {
+        return Math.abs(_rotang) > _eps;
+    }
+
+    public double getXC() {
+        return _xc;
+    }
+
+    public double getYC() {
+        return _yc;
+    }
+
+    public double getSize() {
+        return _size;
+    }
+
+    private double twicearea(double x1, double y1, double x2, double y2, double x3, double y3) {
+        return (x3*y2 - x2*y3) - (x3*y1 - x1*y3) + (x2*y1 - x1*y2);
+    }
+}
CVSspam 0.2.8