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}