001/**
002 * Copyright (c) 2011, The University of Southampton and the individual contributors.
003 * All rights reserved.
004 *
005 * Redistribution and use in source and binary forms, with or without modification,
006 * are permitted provided that the following conditions are met:
007 *
008 *   *  Redistributions of source code must retain the above copyright notice,
009 *      this list of conditions and the following disclaimer.
010 *
011 *   *  Redistributions in binary form must reproduce the above copyright notice,
012 *      this list of conditions and the following disclaimer in the documentation
013 *      and/or other materials provided with the distribution.
014 *
015 *   *  Neither the name of the University of Southampton nor the names of its
016 *      contributors may be used to endorse or promote products derived from this
017 *      software without specific prior written permission.
018 *
019 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
020 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
021 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
022 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
023 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
024 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
025 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
026 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
027 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
028 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
029 */
030package org.openimaj.image.processing.algorithm;
031
032import java.util.Set;
033
034import org.openimaj.image.FImage;
035import org.openimaj.image.analyser.ImageAnalyser;
036import org.openimaj.image.pixel.Pixel;
037
038/**
039 * Analyser that computes for every pixel the minimum and maximum of its
040 * neighbours. This is equivalent to greyscale morphological erosion and
041 * dilation.
042 * 
043 * @see MinFilter
044 * @see MaxFilter
045 * 
046 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
047 * 
048 */
049public class MinMaxAnalyser implements ImageAnalyser<FImage> {
050        private Set<Pixel> support;
051        private int blockWidth = -1;
052        private int blockHeight = -1;
053
054        /**
055         * The min filtered image computed from the last call to
056         * {@link #analyseImage(FImage)}
057         */
058        public FImage min;
059
060        /**
061         * The max filtered image computed from the last call to
062         * {@link #analyseImage(FImage)}
063         */
064        public FImage max;
065
066        /**
067         * Construct with the given support region for selecting pixels to take the
068         * median from. The support mask is a set of <code>n</code> relative x, y
069         * offsets from the pixel currently being processed, and can be created
070         * using the methods or constants in the {@link FilterSupport} class.
071         * 
072         * @param support
073         *            the support coordinates
074         */
075        public MinMaxAnalyser(Set<Pixel> support) {
076                this.support = support;
077
078                if (FilterSupport.isBlockSupport(support)) {
079                        blockWidth = FilterSupport.getSupportWidth(support);
080                        blockHeight = FilterSupport.getSupportHeight(support);
081                }
082        }
083
084        @Override
085        public void analyseImage(FImage image) {
086                min = new FImage(image.width, image.height);
087                max = new FImage(image.width, image.height);
088
089                if (blockHeight >= 1 && blockWidth >= 1) {
090                        processBlock(image, blockWidth, blockHeight);
091                } else {
092                        for (int y = 0; y < image.height; y++) {
093                                for (int x = 0; x < image.width; x++) {
094                                        float minv = Float.MAX_VALUE;
095                                        float maxv = -Float.MAX_VALUE;
096
097                                        for (final Pixel sp : support) {
098                                                final int xx = x + sp.x;
099                                                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}