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.feature.dense.binarypattern;
31
32 import org.openimaj.citation.annotation.Reference;
33 import org.openimaj.citation.annotation.ReferenceType;
34 import org.openimaj.image.FImage;
35 import org.openimaj.image.analyser.ImageAnalyser;
36 import org.openimaj.image.pixel.Pixel;
37
38 /**
39 * Implementation of a Local Ternary Pattern.
40 *
41 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
42 */
43 @Reference(
44 type = ReferenceType.Article,
45 author = { "Tan, Xiaoyang", "Triggs, Bill" },
46 title = "Enhanced local texture feature sets for face recognition under difficult lighting conditions",
47 year = "2010",
48 journal = "Trans. Img. Proc.",
49 pages = { "1635", "", "1650" },
50 url = "http://dx.doi.org/10.1109/TIP.2010.2042645",
51 month = "June",
52 number = "6",
53 publisher = "IEEE Press",
54 volume = "19"
55 )
56 public class LocalTernaryPattern implements ImageAnalyser<FImage> {
57 protected int[][] positiveBinaryPattern;
58 protected int[][] negativeBinaryPattern;
59 protected int[][] ternaryPattern;
60
61 protected float radius;
62 protected int samples;
63 protected float threshold;
64
65 /**
66 * Construct an LTP extractor with the given parameters.
67 * @param radius the radius of the sampling circle
68 * @param samples the number of samples around the circle
69 * @param threshold the threshold
70 */
71 public LocalTernaryPattern(float radius, int samples, float threshold) {
72 checkParams(radius, samples);
73 this.radius = radius;
74 this.samples = samples;
75 this.threshold = threshold;
76 }
77
78 /**
79 * Calculate the LBP for every pixel in the image. The returned
80 * array of LBP codes hase the same dimensions as the image.
81 *
82 * Samples taken from outside the image bounds are assumed to have
83 * the value 0.
84 *
85 * @param image the image
86 * @param radius the radius of the sampling circle
87 * @param samples the number of samples around the circle
88 * @param threshold the threshold
89 * @return three 2d-arrays of the positive and negative binary LTP codes and the ternary code for every pixel
90 */
91 public static int[][][] calculateLTP(FImage image, float radius, int samples, float threshold) {
92 checkParams(radius, samples);
93
94 int [][][] pattern = new int[3][image.height][image.width];
95
96 for (int y=0; y<image.height; y++) {
97 for (int x=0; x<image.width; x++) {
98 int [] pn = calculateLTP(image, radius, samples, threshold, x, y);
99 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 }