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.algorithm;
31  
32  import java.util.Set;
33  
34  import org.openimaj.image.FImage;
35  import org.openimaj.image.analyser.ImageAnalyser;
36  import org.openimaj.image.pixel.Pixel;
37  
38  /**
39   * Analyser that computes for every pixel the minimum and maximum of its
40   * neighbours. This is equivalent to greyscale morphological erosion and
41   * dilation.
42   * 
43   * @see MinFilter
44   * @see MaxFilter
45   * 
46   * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
47   * 
48   */
49  public class MinMaxAnalyser implements ImageAnalyser<FImage> {
50  	private Set<Pixel> support;
51  	private int blockWidth = -1;
52  	private int blockHeight = -1;
53  
54  	/**
55  	 * The min filtered image computed from the last call to
56  	 * {@link #analyseImage(FImage)}
57  	 */
58  	public FImage min;
59  
60  	/**
61  	 * The max filtered image computed from the last call to
62  	 * {@link #analyseImage(FImage)}
63  	 */
64  	public FImage max;
65  
66  	/**
67  	 * Construct with the given support region for selecting pixels to take the
68  	 * median from. The support mask is a set of <code>n</code> relative x, y
69  	 * offsets from the pixel currently being processed, and can be created
70  	 * using the methods or constants in the {@link FilterSupport} class.
71  	 * 
72  	 * @param support
73  	 *            the support coordinates
74  	 */
75  	public MinMaxAnalyser(Set<Pixel> support) {
76  		this.support = support;
77  
78  		if (FilterSupport.isBlockSupport(support)) {
79  			blockWidth = FilterSupport.getSupportWidth(support);
80  			blockHeight = FilterSupport.getSupportHeight(support);
81  		}
82  	}
83  
84  	@Override
85  	public void analyseImage(FImage image) {
86  		min = new FImage(image.width, image.height);
87  		max = new FImage(image.width, image.height);
88  
89  		if (blockHeight >= 1 && blockWidth >= 1) {
90  			processBlock(image, blockWidth, blockHeight);
91  		} else {
92  			for (int y = 0; y < image.height; y++) {
93  				for (int x = 0; x < image.width; x++) {
94  					float minv = Float.MAX_VALUE;
95  					float maxv = -Float.MAX_VALUE;
96  
97  					for (final Pixel sp : support) {
98  						final int xx = x + sp.x;
99  						final int yy = y + sp.y;
100 
101 						if (xx >= 0 && xx < image.width - 1 && yy >= 0 && yy < image.height -
102 								1)
103 						{
104 							minv = Math.min(minv, image.pixels[yy][xx]);
105 							maxv = Math.max(maxv, image.pixels[yy][xx]);
106 						}
107 					}
108 
109 					min.pixels[y][x] = minv;
110 					max.pixels[y][x] = maxv;
111 				}
112 			}
113 		}
114 	}
115 
116 	/**
117 	 * Efficient processing of block support regions through separable passes
118 	 * over the data.
119 	 * 
120 	 * @param image
121 	 *            the image
122 	 * @param width
123 	 *            the width
124 	 * @param height
125 	 *            the height
126 	 */
127 	private void processBlock(FImage image, int width, int height) {
128 		final int halfWidth = width / 2;
129 		final float buffer[] = new float[image.width + width];
130 
131 		for (int r = 0; r < image.height; r++) {
132 			for (int i = 0; i < halfWidth; i++)
133 				buffer[i] = image.pixels[r][0];
134 			for (int i = 0; i < image.width; i++)
135 				buffer[halfWidth + i] = image.pixels[r][i];
136 			for (int i = 0; i < halfWidth; i++)
137 				buffer[halfWidth + image.width + i] = image.pixels[r][image.width - 1];
138 
139 			final int l = buffer.length - width;
140 			for (int i = 0; i < l; i++) {
141 				float min = Float.MAX_VALUE;
142 				float max = -Float.MAX_VALUE;
143 
144 				for (int j = 0; j < width; j++) {
145 					min = Math.min(buffer[i + j], min);
146 					max = Math.max(buffer[i + j], max);
147 				}
148 
149 				this.min.pixels[r][i] = min;
150 				this.max.pixels[r][i] = max;
151 			}
152 		}
153 
154 		final int halfHeight = height / 2;
155 
156 		final float minbuffer[] = new float[min.height + height];
157 		final float maxbuffer[] = new float[max.height + height];
158 
159 		for (int c = 0; c < min.width; c++) {
160 			for (int i = 0; i < halfHeight; i++) {
161 				minbuffer[i] = min.pixels[0][c];
162 				maxbuffer[i] = max.pixels[0][c];
163 			}
164 			for (int i = 0; i < min.height; i++) {
165 				minbuffer[halfHeight + i] = min.pixels[i][c];
166 				maxbuffer[halfHeight + i] = max.pixels[i][c];
167 			}
168 			for (int i = 0; i < halfHeight; i++) {
169 				minbuffer[halfHeight + min.height + i] = min.pixels[min.height - 1][c];
170 				maxbuffer[halfHeight + min.height + i] = max.pixels[max.height - 1][c];
171 			}
172 
173 			final int l = minbuffer.length - height;
174 			for (int i = 0; i < l; i++) {
175 				float minv = Float.MAX_VALUE;
176 				float maxv = -Float.MAX_VALUE;
177 
178 				for (int j = 0; j < height; j++) {
179 					minv = Math.min(minbuffer[i + j], minv);
180 					maxv = Math.max(maxbuffer[i + j], maxv);
181 				}
182 
183 				minbuffer[i] = minv;
184 				maxbuffer[i] = maxv;
185 			}
186 
187 			for (int r = 0; r < min.height; r++) {
188 				min.pixels[r][c] = minbuffer[r];
189 				max.pixels[r][c] = maxbuffer[r];
190 			}
191 		}
192 	}
193 }