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}