View Javadoc

1   /**
2    * Copyright (c) 2011, The University of Southampton and the individual contributors.
3    * All rights reserved.
4    *
5    * Redistribution and use in source and binary forms, with or without modification,
6    * are permitted provided that the following conditions are met:
7    *
8    *   * 	Redistributions of source code must retain the above copyright notice,
9    * 	this list of conditions and the following disclaimer.
10   *
11   *   *	Redistributions in binary form must reproduce the above copyright notice,
12   * 	this list of conditions and the following disclaimer in the documentation
13   * 	and/or other materials provided with the distribution.
14   *
15   *   *	Neither the name of the University of Southampton nor the names of its
16   * 	contributors may be used to endorse or promote products derived from this
17   * 	software without specific prior written permission.
18   *
19   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
20   * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21   * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22   * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
23   * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24   * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25   * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
26   * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28   * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29   */
30  package org.openimaj.image.connectedcomponent.proc;
31  
32  import java.util.List;
33  
34  import org.openimaj.feature.DoubleFV;
35  import org.openimaj.feature.FeatureVectorProvider;
36  import org.openimaj.image.pixel.ConnectedComponent;
37  import org.openimaj.image.pixel.ConnectedComponent.ConnectMode;
38  import org.openimaj.image.pixel.Pixel;
39  import org.openimaj.image.processor.connectedcomponent.ConnectedComponentProcessor;
40  import org.openimaj.math.util.Interpolation;
41  
42  /**
43   * Distance-from-centroid descriptor for convex shapes. Sweeps the 
44   * edge of the shape over all angles in 0..360 and records the distance
45   * from the centroid.
46   * 
47   * Scale invariance is optionally achieved by normalising the
48   * resultant vector to sum to 1.
49   * 
50   * Rotation invariance is optionally achieved by measuring angles
51   * from the dominant orientation of the connected component.
52   * 
53   * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
54   *
55   */
56  public class BoundaryDistanceDescriptor implements ConnectedComponentProcessor, FeatureVectorProvider<DoubleFV> {
57  	/**
58  	 * The number of samples
59  	 */
60  	public final static int DESCRIPTOR_LENGTH = 360;
61  	
62  	/**
63  	 * The descriptor vector, measusring distance from centroid per degree
64  	 */
65  	public double [] descriptor = new double[DESCRIPTOR_LENGTH];
66  	protected boolean normaliseScale;
67  	protected boolean normaliseAngle;
68  
69  	/**
70  	 * Construct the BoundaryDistanceDescriptor with both scale and
71  	 * orientation normalisation enabled
72  	 */
73  	public BoundaryDistanceDescriptor() {
74  		this(true, true);
75  	}
76  	
77  	/**
78  	 * Construct the BoundaryDistanceDescriptor with optional scale and
79  	 * orientation invariance.
80  	 * @param normaliseDistance enable scale invariance
81  	 * @param normaliseAngle enable rotation invariance
82  	 */
83  	public BoundaryDistanceDescriptor(boolean normaliseDistance, boolean normaliseAngle) {
84  		this.normaliseScale = normaliseDistance;
85  		this.normaliseAngle = normaliseAngle;
86  	}
87  
88  	@Override
89  	public void process(ConnectedComponent cc) {
90  		cc = new ConnectedComponent(cc.calculateConvexHull()); //make shape convex
91  		
92  		List<Pixel> bound = cc.getInnerBoundary(ConnectMode.CONNECT_8);
93  		double [] centroid = cc.calculateCentroid();
94  		double direction = cc.calculateDirection();
95  
96  		float[] distances = new float[bound.size()];
97  		float[] angle = new float[bound.size()];
98  		int count = 0;
99  
100 		for (int i=0; i<bound.size(); i++) {
101 			Pixel p = bound.get(i);
102 			double o = p.y - centroid[1];
103 			double a = p.x - centroid[0];
104 
105 			float dist = (float) Math.sqrt((a*a) + (o*o));
106 			distances[i] = dist;
107 
108 			if (normaliseAngle) {
109 				angle[i] = (float) (direction - Math.atan2(o, a));
110 			} else {
111 				angle[i] = (float) (Math.atan2(o, a));
112 			}
113 					
114 			angle[i] = (float) ((angle[i] %= 2.0*Math.PI) >= 0 ? angle[i] : (angle[i] + 2.0*Math.PI));			
115 			angle[i] = (float) (360.0 * angle[i] / (2.0*Math.PI));
116 		}
117 
118 		for (int i=0; i<DESCRIPTOR_LENGTH; i++) {
119 			int index1 = -1;
120 			int index2 = -1;
121 
122 			for (int j=0; j<angle.length; j++) {
123 				int n = ((j+1 == angle.length) ? 0 : j+1);
124 
125 				float aj = angle[j];
126 				float an = angle[n];
127 
128 				if (an > 350 && aj < 10) if (i<10) an-=360; else aj+=360;
129 				if (aj > 350 && an < 10) if (i<10) aj-=360; else an+=360;
130 				
131 				if (aj==i) {
132 					index1 = j;
133 					index2 = j;
134 					break;
135 				} else if (aj<an) {
136 					if (i <= an && i > aj) {
137 						index1 = j;
138 						index2 = n;
139 						break;
140 					}
141 				} else {
142 					if (i <= aj && i > an) {
143 						index1 = j;
144 						index2 = n;
145 						break;
146 					}
147 				}
148 			}
149 
150 			
151 			descriptor[i] = Interpolation.lerp(i, angle[index1], distances[index1], angle[index2], distances[index2]);
152 			count += descriptor[i];
153 		}
154 		
155 		if (normaliseScale) {
156 			for (int i=0; i<DESCRIPTOR_LENGTH; i++)
157 				descriptor[i] /= count;
158 		}
159 	}
160 
161 	/**
162 	 * Get the feature vector as a double array
163 	 * @return the feature vector
164 	 */
165 	public double[] getFeatureVectorArray() {
166 		return descriptor;
167 	}
168 	
169 	@Override
170 	public DoubleFV getFeatureVector() {
171 		return new DoubleFV(getFeatureVectorArray());
172 	}
173 }