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.feature.dense.binarypattern;
031
032import org.openimaj.citation.annotation.Reference;
033import org.openimaj.citation.annotation.ReferenceType;
034import org.openimaj.image.FImage;
035import org.openimaj.image.analyser.ImageAnalyser;
036import org.openimaj.image.pixel.Pixel;
037
038/**
039 * Implementation of a Local Ternary Pattern.
040 * 
041 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
042 */
043@Reference(
044                type = ReferenceType.Article,
045                author = { "Tan, Xiaoyang", "Triggs, Bill" },
046                title = "Enhanced local texture feature sets for face recognition under difficult lighting conditions",
047                year = "2010",
048                journal = "Trans. Img. Proc.",
049                pages = { "1635", "", "1650" },
050                url = "http://dx.doi.org/10.1109/TIP.2010.2042645",
051                month = "June",
052                number = "6",
053                publisher = "IEEE Press",
054                volume = "19"
055        )
056public class LocalTernaryPattern implements ImageAnalyser<FImage> {
057        protected int[][] positiveBinaryPattern;
058        protected int[][] negativeBinaryPattern;
059        protected int[][] ternaryPattern;
060        
061        protected float radius;
062        protected int samples;
063        protected float threshold;
064        
065        /**
066         * Construct an LTP extractor with the given parameters.
067         * @param radius the radius of the sampling circle
068         * @param samples the number of samples around the circle
069         * @param threshold the threshold
070         */
071        public LocalTernaryPattern(float radius, int samples, float threshold) {
072                checkParams(radius, samples);
073                this.radius = radius;
074                this.samples = samples;
075                this.threshold = threshold;
076        }
077        
078        /**
079         * Calculate the LBP for every pixel in the image. The returned
080         * array of LBP codes hase the same dimensions as the image. 
081         * 
082         * Samples taken from outside the image bounds are assumed to have 
083         * the value 0.
084         * 
085         * @param image the image
086         * @param radius the radius of the sampling circle
087         * @param samples the number of samples around the circle
088         * @param threshold the threshold
089         * @return three 2d-arrays of the positive and negative binary LTP codes and the ternary code for every pixel
090         */
091        public static int[][][] calculateLTP(FImage image, float radius, int samples, float threshold) {
092                checkParams(radius, samples);
093                
094                int [][][] pattern = new int[3][image.height][image.width];
095                
096                for (int y=0; y<image.height; y++) {
097                        for (int x=0; x<image.width; x++) {
098                                int [] pn = calculateLTP(image, radius, samples, threshold, x, y);
099                                pattern[0][y][x] = pn[0];
100                                pattern[1][y][x] = pn[1];
101                                pattern[2][y][x] = pn[2];
102                        }
103                }
104                
105                return pattern;
106        }
107        
108        /**
109         * Calculate the LTP for a single point. The
110         * point must be within the image.
111         * 
112         * @param image the image
113         * @param radius the radius of the sampling circle
114         * @param samples the number of samples around the circle
115         * @param threshold the threshold
116         * @param x the x-coordinate of the point
117         * @param y the y-coordinate of the point
118         * @return the LTP code (positive and negative binary code and ternary code)
119         */
120        public static int[] calculateLTP(FImage image, float radius, int samples, float threshold, int x, int y) {
121                float centre = image.pixels[y][x];
122                int pattern[] = new int[3];
123                
124                for (int i=0; i<samples; i++) {
125                        double xx = -radius * Math.sin(2 * Math.PI * i / samples);
126                        double yy = radius * Math.cos(2 * Math.PI * i / samples);
127                        
128                        float pix = image.getPixelInterp(x+xx, y+yy);
129                        
130                        float d = pix - centre;
131                        
132                        if (d >= threshold) {
133                                pattern[0] += Math.pow(2, i);
134                                pattern[2] += Math.pow(3, i);
135                        }
136                        if (d <= threshold) {
137                                pattern[1] += Math.pow(2, i);
138                                pattern[2] += 2 * Math.pow(3, i);
139                        }
140                }
141                
142                return pattern;
143        }
144
145        /**
146         * Calculate the LTP for a single point. The
147         * point must be within the image.
148         * 
149         * @param image the image
150         * @param radius the radius of the sampling circle
151         * @param threshold the threshold
152         * @param samples the number of samples around the circle
153         * @param point the point
154         * @return the LTP code (positive and negative binary code and ternary code)
155         */
156        public static int [] calculateLTP(FImage image, float radius, int samples, float threshold, Pixel point) {
157                return calculateLTP(image, radius, samples, threshold, point.x, point.y);
158        }
159
160        private static void checkParams(float radius, int samples) {
161                if (radius <= 0) {
162                        throw new IllegalArgumentException("radius must be greater than 0");
163                }
164                if (samples <= 1 || samples > 31) {
165                        throw new IllegalArgumentException("samples cannot be less than one or more than 31");
166                }
167        }
168
169        /* (non-Javadoc)
170         * @see org.openimaj.image.analyser.ImageAnalyser#analyseImage(org.openimaj.image.Image)
171         */
172        @Override
173        public void analyseImage(FImage image) {
174                int [][][] patterns = calculateLTP(image, radius, samples, threshold);
175                
176                positiveBinaryPattern = patterns[0];
177                negativeBinaryPattern = patterns[1];
178                ternaryPattern = patterns[2];
179        }
180        
181        /**
182         * Get the positive pattern created during the last call to
183         * {@link #analyseImage(FImage)}.
184         * 
185         * @return the pattern
186         */
187        public int[][] getPositivePattern() {
188                return positiveBinaryPattern;
189        }
190        
191        /**
192         * Get the negative pattern created during the last call to
193         * {@link #analyseImage(FImage)}.
194         * 
195         * @return the pattern
196         */
197        public int[][] getNegativePattern() {
198                return negativeBinaryPattern;
199        }
200        
201        /**
202         * Get the ternary pattern created during the last call to
203         * {@link #analyseImage(FImage)}.
204         * 
205         * @return the pattern
206         */
207        public int[][] getTernaryPattern() {
208                return ternaryPattern;
209        }
210}