Commit in CDMS/src/CDMS/Partridge/ImageComparison on MAIN
Cluster.java+129added 1.1
CompareImages.java+176added 1.1
ImageDescriptor.java+180added 1.1
NearestNeighborClustering.java+14-41.1 -> 1.2
PixelArray.java+161.1 -> 1.2
+515-4
3 added + 2 modified, total 5 files
Updates and improvements to the automated inspection infrastructure

CDMS/src/CDMS/Partridge/ImageComparison
Cluster.java added at 1.1
diff -N Cluster.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ Cluster.java	5 Sep 2010 20:20:10 -0000	1.1
@@ -0,0 +1,129 @@
+/*
+ * Cluster.java
+ */
+
+package CDMS.Partridge.ImageComparison;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+/**
+ * Cluster class to hold image pixels that are clustered together and calculate
+ * cluster properties.
+ *
+ * @author Richard Partridge
+ */
+public class Cluster {
+
+    private Map<Long, Double> _pixmap;
+    private PixelArray _pixarray;
+
+    /**
+     * Construct a cluster on a specified pixel array.  The pixel array is required
+     * to define the position of the pixels in the cluster.
+     *
+     * @param pixarray pixel array
+     */
+    public Cluster(PixelArray pixarray) {
+        _pixarray = pixarray;
+        _pixmap = new HashMap<Long, Double>();
+    }
+
+    /**
+     * Add a pixel to the cluster with a specified deviation from the reference
+     * pixel.
+     *
+     * @param pixel pixel to be added
+     * @param deviation deviation in intensity from what is expected
+     */
+    public void addPixel(Long pixel, double deviation) {
+        _pixmap.put(pixel, deviation);
+    }
+
+    /**
+     * Return the set of pixels in this cluster.
+     *
+     * @return set of pixels
+     */
+    public Set<Long> getPixelList() {
+        return _pixmap.keySet();
+    }
+
+    /**
+     * Return a map linking the pixels in the cluster to their deviations.
+     *
+     * @return map of pixels to deviations
+     */
+    public Map<Long, Double> getPixelMap() {
+        return _pixmap;
+    }
+
+    /**
+     * Return the pixel array for this cluster.
+     *
+     * @return pixel array
+     */
+    public PixelArray getPixelArray() {
+        return _pixarray;
+    }
+
+    /**
+     * Return the center of gravity for the cluster weighted by the absolute
+     * value of the deviation.
+     *
+     * @return center of gravity coordinates (0 = x, 1 = y)
+     */
+    public double[] getCenterOfGravity() {
+
+        //  Initialize the COG sums
+        double xc = 0.;
+        double yc = 0.;
+        double devsum = 0.;
+
+        //  Loop over all pixels and calculate position weighted by |deviation|
+        for (Entry<Long, Double> entry : _pixmap.entrySet()) {
+            long pixel = entry.getKey();
+            double deviation = Math.abs(entry.getValue());
+            double x = _pixarray.getX(pixel);
+            double y = _pixarray.getY(pixel);
+            xc += x * deviation;
+            yc += y * deviation;
+            devsum += deviation;
+        }
+
+        //  Normalize the weighted position to get the COG
+        if (devsum != 0.) {
+            xc /= devsum;
+            yc /= devsum;
+        }
+
+        //  Return the COG as a pair of doubles
+        double[] pos = {xc, yc};
+        return pos;
+    }
+
+    /**
+     * Return the number of pixels in the cluster.
+     *
+     * @return number of pixels
+     */
+    public int getClusterSize() {
+        return _pixmap.size();
+    }
+
+    /**
+     * Return the sum of the absolute value of the deviation for the pixels
+     * in the cluster.
+     *
+     * @return total deviation
+     */
+    public double getTotalDeviation() {
+        double devsum = 0.;
+        for (double dev : _pixmap.values()) {
+            devsum += Math.abs(dev);
+        }
+        return devsum;
+    }
+}

CDMS/src/CDMS/Partridge/ImageComparison
CompareImages.java added at 1.1
diff -N CompareImages.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ CompareImages.java	5 Sep 2010 20:20:10 -0000	1.1
@@ -0,0 +1,176 @@
+/*
+ * CompareImages.java
+ */
+
+package CDMS.Partridge.ImageComparison;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+/**
+ * Compare two sets of images by finding significant deviations in the image and
+ * clustering the regions containing these differences.
+ *
+ * @author Richard Partridge
+ */
+public class CompareImages {
+
+    private double _seed_threshold;
+    private double _neighbor_threshold;
+
+    /**
+     * Constructor for the CompareImages class.  The constructor has two
+     * parameters that control the clustering.  The seed threshold is the
+     * minimum intensity deviation required to form a new cluster, while
+     * the neighbor threshold is the minimum deviation required to be added
+     * to a cluster.
+     *
+     * @param seed_threshold seed threshold
+     * @param neighbor_threshold neighbor threshold
+     */
+    public CompareImages(double seed_threshold, double neighbor_threshold) {
+        _seed_threshold = seed_threshold;
+        _neighbor_threshold = neighbor_threshold;
+    }
+
+    /**
+     * Finds clusters of significant intensity deviations between two sets
+     * of images.  The user provides a map relating pairs of image descriptors
+     * for the images to be compared.  The image descriptor serving as the key
+     * is the image being examined, while the image descriptor serving as the
+     * value is the image descriptor for the reference image.
+     *
+     * @param imagemap map giving set of image pairs to be compared
+     * @return list of clusters found
+     */
+    public List<Cluster> FindClusters(Map<ImageDescriptor, ImageDescriptor> imagemap) {
+
+        //  Create the list of clusters
+        List<Cluster> clusterlist = new ArrayList<Cluster>();
+
+        //  If no images to check, we are done
+        if (imagemap.size() == 0) return clusterlist;
+
+        //  Initialize the image bounds and minimum pixel size
+        double xmin = 9999.;
+        double xmax = -9999.;
+        double ymin = 99999.;
+        double ymax = -9999.;
+        double dxmin = 9999.;
+        double dymin = 9999.;
+
+        //  Loop over all pairs of test and reference image descriptors
+        for (Entry<ImageDescriptor, ImageDescriptor> entry : imagemap.entrySet()) {
+     
+            //  Check that the pixel arrays for the reference and test images are identical
+            PixelArray testpix = entry.getKey().getPixelArray();
+            PixelArray refpix = entry.getValue().getPixelArray();
+            if (!testpix.equals(refpix))
+                throw new RuntimeException("Test and Reference pixel arrays are not consistent");
+
+            //  Accumulate bounds on the images and the smallest pixel size
+            xmin = Math.min(xmin, refpix.getXMin());
+            xmax = Math.max(xmax, refpix.getXMax());
+            ymin = Math.min(ymin, refpix.getYMin());
+            ymax = Math.max(ymax, refpix.getYMax());
+            dxmin = Math.min(dxmin, refpix.getXSize());
+            dymin = Math.min(dymin, refpix.getYSize());
+        }
+
+        //  Create a mother pixelarray to cover the extent given of the images
+        int ncol = (int) Math.floor(2. * Math.max(xmax, -xmin) / dxmin + 0.5);
+        int nrow = (int) Math.floor(2. * Math.max(ymax, -ymin) / dymin + 0.5);
+        PixelArray mother = new PixelArray(nrow, ncol, 0., 0., dxmin, dymin);
+
+        //  Add subarrays to the mother array for each image
+        for (ImageDescriptor descriptor : imagemap.keySet()) {
+
+            //  Get the pixel array for this image
+            PixelArray pixarray = descriptor.getPixelArray();
+            
+            //  Get the image ID and check that it is unique
+            int id = descriptor.getIdentifer();
+            if (mother.getSubArray(id)!= null)
+                throw new RuntimeException("Duplicate imaged descriptor ID: "+id);
+
+            //  Create a subarray in the mother array for this image
+            mother.addSubArray(id, pixarray);
+        }
+
+        //  Create a map relating mother pixels to deviations above the neighbor cut
+        Map<Long, Double> devmap = new HashMap<Long, Double>();
+
+        //  Loop over images and find significant deviations
+        for (Entry<ImageDescriptor, ImageDescriptor> entry : imagemap.entrySet()) {
+            ImageDescriptor test = entry.getKey();
+            ImageDescriptor ref = entry.getValue();
+            ImageData testimage = test.getImageData();
+            ImageData refimage = ref.getImageData();
+            PixelArray pixarray = test.getPixelArray();
+            for (int row=0; row<pixarray.getNRow(); row++) {
+                for (int col=0; col<pixarray.getNCol(); col++) {
+                    int refrgb = refimage.getRGB(row, col);
+                    int testrgb = testimage.getRGB(row, col);
+                    int dblue = refimage.getBlue(refrgb) - testimage.getBlue(testrgb);
+                    int dgreen = refimage.getGreen(refrgb) - testimage.getGreen(testrgb);
+                    int dred = refimage.getRed(refrgb) - testimage.getGreen(testrgb);
+                    double deviation = Math.sqrt((dblue*dblue + dgreen*dgreen + dred*dred) / 3.);
+                    if (Math.abs(deviation) > _neighbor_threshold) {
+                        long pixel = pixarray.getPixel(row, col);
+                        double x = pixarray.getX(pixel);
+                        double y = pixarray.getY(pixel);
+                        long mpixel = mother.getPixel(x, y);
+                        devmap.put(pixel, deviation);
+                    }
+                }
+            }
+        }
+
+        //  Cluster the deviations
+        NearestNeighborClustering clusterer = new NearestNeighborClustering();
+        clusterer.setNeighborThreshold(_neighbor_threshold);
+        clusterer.setSeedThreshold(_seed_threshold);
+        List<Cluster> clusters = clusterer.findClusters(mother, devmap);
+
+        return clusters;
+    }
+
+    /**
+     * Return the neighbor threshold.
+     *
+     * @return neighbor threshold
+     */
+    public double getNeighbor_threshold() {
+        return _neighbor_threshold;
+    }
+
+    /**
+     * Set the neighbor threshold.
+     *
+     * @param neighbor_threshold neighbor threshold
+     */
+    public void setNeighbor_threshold(double neighbor_threshold) {
+        _neighbor_threshold = neighbor_threshold;
+    }
+
+    /**
+     * Return the seed threshold.
+     *
+     * @return seed threshold
+     */
+    public double getSeed_threshold() {
+        return _seed_threshold;
+    }
+
+    /**
+     * Set the seed threshold.
+     *
+     * @param seed_threshold seed threshold
+     */
+    public void setSeed_threshold(double seed_threshold) {
+        _seed_threshold = seed_threshold;
+    }
+}

CDMS/src/CDMS/Partridge/ImageComparison
ImageDescriptor.java added at 1.1
diff -N ImageDescriptor.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ ImageDescriptor.java	5 Sep 2010 20:20:10 -0000	1.1
@@ -0,0 +1,180 @@
+/*
+ * ImageDescriptor.java
+ */
+
+package CDMS.Partridge.ImageComparison;
+
+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/ImageComparison
NearestNeighborClustering.java 1.1 -> 1.2
diff -u -r1.1 -r1.2
--- NearestNeighborClustering.java	4 Sep 2010 01:25:37 -0000	1.1
+++ NearestNeighborClustering.java	5 Sep 2010 20:20:10 -0000	1.2
@@ -47,6 +47,16 @@
     }
 
     /**
+     * Minimal constructor - you must set the seed and neighbor thresholds before
+     * calling the findClusters method.
+     */
+    public NearestNeighborClustering() {
+
+        _seed_threshold = -2;
+        _neighbor_threshold = -1;
+    }
+
+    /**
      * Set the seed threshold.
      *
      * @param seed_threshold seed threshold
@@ -90,7 +100,7 @@
      * @param pixmap map containing deviations for pixels selected for clustering
      * @return list of clusters, with a cluster being a list of pixels
      */
-    public List<List<Long>> findClusters(PixelArray pixarray, Map<Long, Double> pixmap) {
+    public List<Cluster> findClusters(PixelArray pixarray, Map<Long, Double> pixmap) {
 
         //  Check that the seed threshold is at least as large as  the neighbor threshold
         if (_seed_threshold < _neighbor_threshold)
@@ -119,7 +129,7 @@
         }
 
         //  Create a list of clusters
-        List<List<Long>> clusterlist = new ArrayList<List<Long>>();
+        List<Cluster> clusterlist = new ArrayList<Cluster>();
 
         //  Loop over the cluster seeds
         for (Long seed_pixel : cluster_seeds) {
@@ -128,7 +138,7 @@
             if (!clusterable.get(seed_pixel)) continue;
 
             //  Create a new cluster
-            List<Long> cluster = new ArrayList<Long>();
+            Cluster cluster = new Cluster(pixarray);
 
             //  Create a queue to hold channels whose neighbors need to be checked for inclusion
             LinkedList<Long> unchecked = new LinkedList<Long>();
@@ -142,7 +152,7 @@
 
                 //  Pull the next pixel off the queue and add it to the cluster
                 long clustered_pixel = unchecked.removeFirst();
-                cluster.add(clustered_pixel);
+                cluster.addPixel(clustered_pixel, pixmap.get(clustered_pixel));
 
                 //  Get the neigbor channels
                 Set<Long> neighbor_pixels = pixarray.getNeighbors(clustered_pixel);

CDMS/src/CDMS/Partridge/ImageComparison
PixelArray.java 1.1 -> 1.2
diff -u -r1.1 -r1.2
--- PixelArray.java	4 Sep 2010 01:25:38 -0000	1.1
+++ PixelArray.java	5 Sep 2010 20:20:10 -0000	1.2
@@ -383,4 +383,20 @@
         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;
+    }
 }
CVSspam 0.2.8