001/*******************************************************************************
002 * This software is provided as a supplement to the authors' textbooks on digital
003 *  image processing published by Springer-Verlag in various languages and editions.
004 * Permission to use and distribute this software is granted under the BSD 2-Clause 
005 * "Simplified" License (see http://opensource.org/licenses/BSD-2-Clause). 
006 * Copyright (c) 2006-2016 Wilhelm Burger, Mark J. Burge. All rights reserved. 
007 * Visit http://imagingbook.com for additional details.
008 *******************************************************************************/
009package imagingbook.pub.regions.geometry;
010
011import java.awt.Point;
012import java.awt.geom.Point2D;
013
014import ij.process.ImageProcessor;
015import imagingbook.lib.math.Matrix;
016import imagingbook.pub.regions.RegionLabeling.BinaryRegion;
017
018
019
020public class AxisAlignedBoundingBox {
021        
022        private final double[][] boundingBox;
023        
024        public AxisAlignedBoundingBox(BinaryRegion r) {
025                boundingBox = makeBox(r);
026        }
027        
028        
029//      public double[][] getCorners() {
030//              return boundingBox;
031//      }
032        
033        public Point2D[] getCornerPoints() {
034                Point2D[] cpts = new Point2D[boundingBox.length];
035                for (int i = 0; i < boundingBox.length; i++) {
036                        cpts[i] = new Point2D.Double(boundingBox[i][0], boundingBox[i][1]);
037                }
038                return cpts;
039        }
040                
041        /**
042         * Calculates the major axis-aligned bounding box of 
043         * the supplied region, as a sequence of four point
044         * coordinates (A, B, C, D).
045         * @param r binary region
046         * @return the region's bounding box as a sequence of 4 coordinates,
047         * (A, B, C, D)
048         */
049        private double[][] makeBox(BinaryRegion r) {
050                double theta = getRegionOrientation(r);
051                double xa = Math.cos(theta);
052                double ya = Math.sin(theta);
053                double[] ea = {xa,  ya};
054                double[] eb = {ya, -xa};
055                
056                double amin = Double.POSITIVE_INFINITY;
057                double amax = Double.NEGATIVE_INFINITY;
058                double bmin = Double.POSITIVE_INFINITY;
059                double bmax = Double.NEGATIVE_INFINITY;
060                
061                for (Point p : r) {
062                        double a = p.x * xa + p.y * ya; // project (x,y) on the major axis vector
063                        double b = p.x * ya - p.y * xa; // project (x,y) on perpendicular vector
064                        amin = Math.min(a, amin);
065                        amax = Math.max(a, amax);
066                        bmin = Math.min(b, bmin);
067                        bmax = Math.max(b, bmax);
068                }
069                                        
070                double[] A = Matrix.add(Matrix.multiply(amin, ea), Matrix.multiply(bmin, eb));
071                double[] B = Matrix.add(Matrix.multiply(amin, ea), Matrix.multiply(bmax, eb));
072                double[] C = Matrix.add(Matrix.multiply(amax, ea), Matrix.multiply(bmax, eb));
073                double[] D = Matrix.add(Matrix.multiply(amax, ea), Matrix.multiply(bmin, eb));
074                
075                return new double[][] {A, B, C, D};
076        }
077        
078        /**
079         * Calculates the orientation of major axis.
080         * TODO: move this somewhere else (into class BinaryRegion)
081         * @param r binary region
082         * @return orientation of the major axis (angle in radians)
083         */
084        private double getRegionOrientation(BinaryRegion r) {
085                final double xc = r.getXc();
086                final double yc = r.getYc();
087                double mu11 = 0;
088                double mu20 = 0;
089                double mu02 = 0;
090                for (Point p : r) {
091                        double dx = (p.x - xc);
092                        double dy = (p.y - yc);
093                        mu11 = mu11 + dx * dy;
094                        mu20 = mu20 + dx * dx;
095                        mu02 = mu02 + dy * dy;
096                }
097                
098                return 0.5 * Math.atan2(2 * mu11, mu20 - mu02);
099        }
100        
101        // drawing --------------------------------------------
102        
103        public void draw(ImageProcessor ip) {
104                drawLine(ip, boundingBox[0], boundingBox[1]);
105                drawLine(ip, boundingBox[1], boundingBox[2]);
106                drawLine(ip, boundingBox[2], boundingBox[3]);
107                drawLine(ip, boundingBox[3], boundingBox[0]);
108        }
109        
110        private void drawLine(ImageProcessor ip, double[] p1, double[] p2) {
111                int u1 = (int) Math.round(p1[0]);
112                int v1 = (int) Math.round(p1[1]);
113                int u2 = (int) Math.round(p2[0]);
114                int v2 = (int) Math.round(p2[1]);
115                ip.drawLine(u1, v1, u2, v2);    
116        }
117
118}