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.pixel.sampling;
031
032import org.openimaj.image.FImage;
033import org.openimaj.math.geometry.line.Line2d;
034import org.openimaj.math.geometry.point.Point2d;
035
036/**
037 * Methods for sampling along a line in an {@link FImage}. 
038 * 
039 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
040 */
041public enum FLineSampler implements LineSampler<FImage, float[]> {
042        /**
043         * Sample at 1-pixel intervals about the centre of the
044         * line using nearest-neighbour interpolation. The 
045         * overall length of the line is ignored. Values
046         * outside the image are considered to be 0.
047         * 
048         * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
049         */
050        PIXELSTEP_NEAREST_NEIGHBOUR {
051                @Override
052                public float[] extractSamples(Line2d line, FImage image, int numSamples) {
053                        Line2d scaled = getSampleLine(line, image, numSamples);
054                        
055                        return NEAREST_NEIGHBOUR.extractSamples(scaled, image, numSamples);
056                }
057                
058                @Override
059                public Line2d getSampleLine(Line2d line, FImage image, int numSamples) {
060                        Line2d scaled = line.clone();
061                        scaled.scaleCentroid((float) (numSamples / line.calculateLength()));
062                        return scaled;
063                }
064        },
065        /**
066         * Sample the pixel values at regular intervals along the full length
067         * of the line taking the sample value from the nearest pixel. Pixels 
068         * outside the image have a value of 0.
069         * 
070         * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
071         */
072        NEAREST_NEIGHBOUR {
073                @Override
074                public float[] extractSamples(Line2d line, FImage image, int numSamples) {
075                        float[] samples = new float[numSamples];
076                        
077                        final Point2d p1 = line.getBeginPoint();
078                        final Point2d p2 = line.getEndPoint();
079                        float x = p1.getX();
080                        float y = p1.getY();
081                        final float dxStep = (p2.getX() - x) / (numSamples-1);
082                        final float dyStep = (p2.getY() - y) / (numSamples-1);
083                        
084                        final int width = image.width;
085                        final int height = image.height;
086                        
087                        for (int i=0; i<numSamples; i++) {
088                                int ix = Math.round(x);
089                                int iy = Math.round(y);
090                                
091                                if (ix < 0 || ix >= width || iy < 0 || iy >= height)
092                                        samples[i] = 0;
093                                else
094                                        samples[i] = image.getPixelNative(ix, iy);
095                                
096                                x += dxStep;
097                                y += dyStep;
098                        }
099                        
100                        return samples;
101                }
102        },
103        /**
104         * Sample at 1-pixel intervals about the centre of the
105         * line using bilinear interpolation to estimate sub-pixel
106         * values. The overall length of the line is ignored. Values
107         * outside the image are considered to be 0.
108         * 
109         * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
110         */
111        PIXELSTEP_INTERPOLATED {
112                @Override
113                public float[] extractSamples(Line2d line, FImage image, int numSamples) {
114                        Line2d scaled = getSampleLine(line, image, numSamples);
115                        
116                        return INTERPOLATED.extractSamples(scaled, image, numSamples);
117                }
118                
119                @Override
120                public Line2d getSampleLine(Line2d line, FImage image, int numSamples) {
121                        Line2d scaled = line.clone();
122                        scaled.scaleCentroid((float) (numSamples / line.calculateLength()));
123                        return scaled;
124                }
125        },
126        /**
127         * Sample the pixel values at regular intervals along the full
128         * length of the line using bilinear interpolation to get 
129         * sub-pixel values.
130         * 
131         * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
132         */
133        INTERPOLATED {
134                @Override
135                public float[] extractSamples(Line2d line, FImage image, int numSamples) {
136                        float[] samples = new float[numSamples];
137                        
138                        Point2d p1 = line.getBeginPoint();
139                        Point2d p2 = line.getEndPoint();
140                        float x = p1.getX();
141                        float y = p1.getY();
142                        float dxStep = (p2.getX() - x) / (numSamples-1);
143                        float dyStep = (p2.getY() - y) / (numSamples-1);
144                        
145                        for (int i=0; i<numSamples; i++) {
146                                samples[i] = image.getPixelInterpNative(x, y, 0);
147                                
148                                x += dxStep;
149                                y += dyStep;
150                        }
151                        
152                        return samples;
153                }
154        },
155        /**
156         * Sample at 1-pixel intervals about the centre of the
157         * line using nearest neighbour interpolation to estimate values
158         * and then compute the derivative using [1 0 -1] filter. The number of 
159         * regular samples is two bigger than the requested number so 
160         * the number of derivative samples is as requested. 
161         * The overall length of the line is ignored. Values
162         * outside the image are considered to be 0.
163         * 
164         * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
165         */
166        PIXELSTEP_NEAREST_NEIGHBOUR_DERIVATIVE {
167                @Override
168                public float[] extractSamples(Line2d line, FImage image, int numSamples) {
169                        Line2d scaled = getSampleLine(line, image, numSamples+2);
170                        
171                        return NEAREST_NEIGHBOUR_DERIVATIVE.extractSamples(scaled, image, numSamples);
172                }
173                
174                @Override
175                public Line2d getSampleLine(Line2d line, FImage image, int numSamples) {
176                        Line2d scaled = line.clone();
177                        scaled.scaleCentroid((float) (numSamples / line.calculateLength()));
178                        return scaled;
179                }
180        },
181        /**
182         * Sample the pixel values at regular intervals along the full
183         * length of the line using nearest-neighbour pixels and then 
184         * compute the derivative using [1 0 -1] filter. The number of 
185         * regular samples is two bigger than the requested number so 
186         * the number of derivative samples is as requested.
187         * 
188         * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
189         */
190        NEAREST_NEIGHBOUR_DERIVATIVE {
191                @Override
192                public float[] extractSamples(Line2d line, FImage image, int numSamples) {
193                        float[] samples = NEAREST_NEIGHBOUR.extractSamples(line, image, numSamples+2);
194                        return computeDerivative(samples);
195                }
196        },
197        /**
198         * Sample at 1-pixel intervals about the centre of the
199         * line using bilinear interpolation to estimate sub-pixel values
200         * and then compute the derivative using [1 0 -1] filter. The number of 
201         * regular samples is two bigger than the requested number so 
202         * the number of derivative samples is as requested. 
203         * The overall length of the line is ignored. Values
204         * outside the image are considered to be 0.
205         * 
206         * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
207         */
208        PIXELSTEP_INTERPOLATED_DERIVATIVE {
209                @Override
210                public float[] extractSamples(Line2d line, FImage image, int numSamples) {
211                        Line2d scaled = getSampleLine(line, image, numSamples+2);
212                        
213                        return NEAREST_NEIGHBOUR_DERIVATIVE.extractSamples(scaled, image, numSamples);
214                }
215                
216                @Override
217                public Line2d getSampleLine(Line2d line, FImage image, int numSamples) {
218                        Line2d scaled = line.clone();
219                        scaled.scaleCentroid((float) (numSamples / line.calculateLength()));
220                        return scaled;
221                }
222        },
223        /**
224         * Sample the pixel values at regular intervals along the full
225         * length of the line using bilinear interpolation and then 
226         * compute the derivative using [1 0 -1] filter. The number 
227         * of regular samples is two bigger than the requested number 
228         * so the number of derivative samples is as requested.
229         * 
230         * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
231         */
232        INTERPOLATED_DERIVATIVE {
233                @Override
234                public float[] extractSamples(Line2d line, FImage image, int numSamples) {
235                        float[] samples = INTERPOLATED.extractSamples(line, image, numSamples+2);
236                        return computeDerivative(samples);
237                }
238        }
239        ;
240        
241        /**
242         * Compute the derivative with a [1 0 -1] filter. The returned
243         * derivatives will have a length of samples.length-2.
244         * @param samples the samples to differentiate
245         * @return differentiated samples
246         */
247        private static float[] computeDerivative(float[] samples) {
248                final int numSamples = samples.length - 2;
249                final float[] dsamples = new float[numSamples];
250                
251                for (int i=0; i<numSamples; i++) {
252                        dsamples[i] = samples[i] - samples[i+2];
253                }
254                
255                return dsamples;
256        }
257        
258        
259        @Override
260        public Line2d getSampleLine(Line2d line, FImage image, int numSamples) {
261                //most implementations sample the full length of the line
262                return line;
263        }
264        
265        @Override
266        public abstract float[] extractSamples(Line2d line, FImage image, int numSamples);
267}