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.processing.morphology;
31  
32  import java.util.Arrays;
33  import java.util.HashSet;
34  import java.util.Set;
35  
36  import org.openimaj.image.pixel.Pixel;
37  
38  /**
39   * Morphological structuring element
40   * 
41   * The central element is the pixel 0,0. Other s.e. pixels are relative to this.
42   * 
43   * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
44   * 
45   */
46  public class StructuringElement {
47  	/**
48  	 * Standard 3x3 box structuring element
49  	 */
50  	public final static StructuringElement BOX;
51  
52  	/**
53  	 * Standard 3x3 cross structuring element
54  	 */
55  	public final static StructuringElement CROSS;
56  
57  	/**
58  	 * Standard horizontal pit structuring element [x . x]
59  	 */
60  	public final static StructuringElement HPIT;
61  
62  	// build the elements
63  	static {
64  		BOX = new StructuringElement();
65  		BOX.positive.add(new Pixel(-1, -1));
66  		BOX.positive.add(new Pixel(0, -1));
67  		BOX.positive.add(new Pixel(1, -1));
68  		BOX.positive.add(new Pixel(-1, 0));
69  		BOX.positive.add(new Pixel(0, 0));
70  		BOX.positive.add(new Pixel(1, 0));
71  		BOX.positive.add(new Pixel(-1, 1));
72  		BOX.positive.add(new Pixel(0, 1));
73  		BOX.positive.add(new Pixel(1, 1));
74  
75  		CROSS = new StructuringElement();
76  		CROSS.positive.add(new Pixel(0, -1));
77  		CROSS.positive.add(new Pixel(-1, 0));
78  		CROSS.positive.add(new Pixel(0, 0));
79  		CROSS.positive.add(new Pixel(1, 0));
80  		CROSS.positive.add(new Pixel(0, 1));
81  
82  		HPIT = new StructuringElement();
83  		HPIT.positive.add(new Pixel(-1, 0));
84  		HPIT.positive.add(new Pixel(1, 0));
85  	}
86  
87  	/**
88  	 * Set of positive pixels in the structuring element
89  	 */
90  	public Set<Pixel> positive = new HashSet<Pixel>();
91  
92  	/**
93  	 * Set of negative pixels in the structuring element
94  	 */
95  	public Set<Pixel> negative = new HashSet<Pixel>();
96  
97  	/**
98  	 * Construct an empty structuring element
99  	 */
100 	public StructuringElement() {
101 
102 	}
103 
104 	/**
105 	 * Construct a structuring element with the given positive and negative
106 	 * pixels
107 	 * 
108 	 * @param positive
109 	 *            the positive pixels
110 	 * @param negative
111 	 *            the negative pixels
112 	 */
113 	public StructuringElement(Set<Pixel> positive, Set<Pixel> negative) {
114 		if (positive != null)
115 			this.positive.addAll(positive);
116 		if (negative != null)
117 			this.negative.addAll(negative);
118 	}
119 
120 	/**
121 	 * Construct a structuring element with the given positive and negative
122 	 * pixels
123 	 * 
124 	 * @param positive
125 	 *            the positive pixels
126 	 * @param negative
127 	 *            the negative pixels
128 	 */
129 	public StructuringElement(Pixel[] positive, Pixel[] negative) {
130 		if (positive != null)
131 			this.positive.addAll(Arrays.asList(positive));
132 		if (negative != null)
133 			this.negative.addAll(Arrays.asList(negative));
134 	}
135 
136 	/**
137 	 * Get the size of the structuring element in the form [width, height, x, y]
138 	 * 
139 	 * @return the size of the structuring element
140 	 */
141 	public int[] size() {
142 		int xmin = Integer.MAX_VALUE;
143 		int xmax = -Integer.MAX_VALUE;
144 		int ymin = Integer.MAX_VALUE;
145 		int ymax = -Integer.MAX_VALUE;
146 
147 		for (final Pixel p : positive) {
148 			if (p.x < xmin)
149 				xmin = p.x;
150 			if (p.x > xmax)
151 				xmax = p.x;
152 			if (p.y < ymin)
153 				ymin = p.y;
154 			if (p.y > ymax)
155 				ymax = p.y;
156 		}
157 		for (final Pixel p : negative) {
158 			if (p.x < xmin)
159 				xmin = p.x;
160 			if (p.x > xmax)
161 				xmax = p.x;
162 			if (p.y < ymin)
163 				ymin = p.y;
164 			if (p.y > ymax)
165 				ymax = p.y;
166 		}
167 
168 		return new int[] { 1 + xmax - xmin, 1 + ymax - ymin, xmin, ymin };
169 	}
170 
171 	/**
172 	 * Construct a structuring element from a @link{String} of the form produced
173 	 * by @link{#toString()}.
174 	 * 
175 	 * @see #toString()
176 	 * 
177 	 * @param ele
178 	 *            the string defining the element
179 	 * @param cx
180 	 *            the top-left x-coordinate
181 	 * @param cy
182 	 *            the top-left y-coordinate
183 	 * @return a new structuring element
184 	 */
185 	public static StructuringElement parseElement(String ele, int cx, int cy) {
186 		final String[] lines = ele.split("\\n");
187 		final int height = lines.length;
188 		final int width = lines[0].length();
189 
190 		final StructuringElement se = new StructuringElement();
191 
192 		for (int j = 0; j < height; j++) {
193 			for (int i = 0; i < width; i++) {
194 				final char c = lines[j].charAt(i);
195 
196 				if (c == '*') {
197 					se.positive.add(new Pixel(i - cx, j - cy));
198 				} else if (c == 'o') {
199 					se.negative.add(new Pixel(i - cx, j - cy));
200 				}
201 			}
202 		}
203 
204 		return se;
205 	}
206 
207 	@Override
208 	public String toString() {
209 		final int[] sz = size();
210 		String s = "";
211 
212 		for (int j = 0; j < sz[1]; j++) {
213 			for (int i = 0; i < sz[0]; i++) {
214 				final Pixel p = new Pixel(i + sz[2], j + sz[3]);
215 
216 				if (positive.contains(p))
217 					s += "*";
218 				else if (negative.contains(p))
219 					s += "o";
220 				else
221 					s += ".";
222 			}
223 			s += "\n";
224 		}
225 
226 		return s;
227 	}
228 
229 	/**
230 	 * Determine if this structuring element is completely contained in the
231 	 * pixels centered at p.
232 	 * 
233 	 * @param p
234 	 *            the centre
235 	 * @param pixels
236 	 *            the pixels
237 	 * @return true if completely contained, false otherwise
238 	 */
239 	public boolean matches(Pixel p, Set<Pixel> pixels) {
240 		// is the s.e completely contained in the pixels centered at p?
241 		return (intersect(p, pixels).size() == countActive());
242 	}
243 
244 	Set<Pixel> intersect(Pixel p, Set<Pixel> pixels) {
245 		final Set<Pixel> intersect = new HashSet<Pixel>();
246 
247 		// positive
248 		for (final Pixel sep : positive) {
249 			final Pixel imp = new Pixel(p.x + sep.x, p.y + sep.y);
250 
251 			if (pixels.contains(imp))
252 				intersect.add(imp);
253 		}
254 
255 		// negative
256 		for (final Pixel sep : negative) {
257 			final Pixel imp = new Pixel(p.x + sep.x, p.y + sep.y);
258 
259 			if (!pixels.contains(imp))
260 				intersect.add(imp);
261 		}
262 
263 		return intersect;
264 	}
265 
266 	/**
267 	 * Count the total (positive and negative) number of pixels in this
268 	 * structuring element
269 	 * 
270 	 * @return the total number of pixels
271 	 */
272 	public int countActive() {
273 		return positive.size() + negative.size();
274 	}
275 
276 	/**
277 	 * Build a disk shaped structuring element with the given radius.
278 	 * 
279 	 * @param radius
280 	 *            the disk radius
281 	 * @return the disk shaped S.E.
282 	 */
283 	public static StructuringElement disk(int radius) {
284 		final StructuringElement se = new StructuringElement();
285 		final int r2 = radius * radius;
286 
287 		for (int j = -radius; j <= radius; j++) {
288 			final int j2 = j * j;
289 			for (int i = -radius; i <= radius; i++) {
290 				if ((i * i + j2) <= r2) {
291 					se.positive.add(new Pixel(i, j));
292 				}
293 			}
294 		}
295 
296 		return se;
297 	}
298 }