View Javadoc

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 gnu.trove.list.array.TIntArrayList;
33  import gnu.trove.map.hash.TIntObjectHashMap;
34  
35  import java.util.ArrayList;
36  import java.util.Arrays;
37  import java.util.List;
38  
39  import org.openimaj.citation.annotation.Reference;
40  import org.openimaj.citation.annotation.ReferenceType;
41  import org.openimaj.image.FImage;
42  import org.openimaj.image.pixel.Pixel;
43  
44  /**
45   * Class for determining whether specific binary patterns are "uniform". Uniform
46   * patterns have less than one 01 transition and one 01 transition when viewed
47   * as a circular buffer.
48   * <p>
49   * The class caches lookup tables of uniform patterns on demand, with the
50   * exception of the commonly used 8-bit patterns which are cached on
51   * initialization.
52   * 
53   * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
54   * 
55   */
56  @Reference(
57  		type = ReferenceType.Article,
58  		author = { "Ojala, T.", "Pietikainen, M.", "Maenpaa, T." },
59  		title = "Multiresolution gray-scale and rotation invariant texture classification with local binary patterns",
60  		year = "2002",
61  		journal = "Pattern Analysis and Machine Intelligence, IEEE Transactions on",
62  		pages = { "971 ", "987" },
63  		month = "jul",
64  		number = "7",
65  		volume = "24",
66  		customData = {
67  				"doi", "10.1109/TPAMI.2002.1017623",
68  				"ISSN", "0162-8828"
69  		})
70  public class UniformBinaryPattern {
71  	protected static TIntObjectHashMap<TIntArrayList> lut = new TIntObjectHashMap<TIntArrayList>();
72  
73  	static {
74  		// pre-cache the table for 8-bit patterns as it is common
75  		lut.put(8, calculateUniformPatterns(8));
76  		// other patterns will be cached on demand
77  	}
78  
79  	protected static TIntArrayList calculateUniformPatterns(int nbits) {
80  		final TIntArrayList result = new TIntArrayList();
81  
82  		final boolean[] bits = new boolean[nbits];
83  
84  		for (int i = 0; i < Math.pow(2, nbits); i++) {
85  			Arrays.fill(bits, false);
86  
87  			for (int temp = i, j = 1; j <= nbits; j++) {
88  				final int pow = (int) Math.pow(2, (nbits - j));
89  
90  				if (temp / pow > 0) {
91  					bits[j - 1] = true;
92  				}
93  				temp = temp % pow;
94  			}
95  
96  			if (isUniform(bits)) {
97  				result.add(i);
98  			}
99  		}
100 
101 		return result;
102 	}
103 
104 	protected static boolean isUniform(boolean[] pattern) {
105 		int count = 0;
106 
107 		for (int i = 0; i < pattern.length - 1; i++) {
108 			if (pattern[i] != pattern[i + 1]) {
109 				count++;
110 			}
111 		}
112 
113 		return count <= 2;
114 	}
115 
116 	/**
117 	 * Get a list of all the binary patterns of a given length that are
118 	 * "uniform". Uniform patterns have less than one 01 transition and one 01
119 	 * transition when viewed as a circular buffer.
120 	 * 
121 	 * The length must be between 1 and 32 bits.
122 	 * 
123 	 * @param nbits
124 	 *            pattern length
125 	 * @return set of patterns encoded as integers
126 	 */
127 	public static TIntArrayList getUniformPatterns(int nbits) {
128 		if (nbits < 1 || nbits > 32)
129 			throw new IllegalArgumentException("Only patterns with lengths between 1 and 32 bits are supported");
130 
131 		TIntArrayList patterns = lut.get(nbits);
132 
133 		if (patterns == null) {
134 			patterns = calculateUniformPatterns(nbits);
135 			lut.put(nbits, patterns);
136 		}
137 
138 		return patterns;
139 	}
140 
141 	/**
142 	 * Check whether the given nbits pattern is uniform.
143 	 * 
144 	 * @param pattern
145 	 *            the pattern
146 	 * @param nbits
147 	 *            the pattern length
148 	 * @return true if uniform; false otherwise.
149 	 */
150 	public static boolean isPatternUniform(int pattern, int nbits) {
151 		return getUniformPatterns(nbits).contains(pattern);
152 	}
153 
154 	/**
155 	 * Compute a binary map showing the locations of the specified pattern code.
156 	 * 
157 	 * @param patternImage
158 	 *            the pattern data
159 	 * @param code
160 	 *            the code to extract
161 	 * @return a binary {@link FImage} depicting the locations of the given code
162 	 */
163 	public static FImage extractPatternImage(int[][] patternImage, int code) {
164 		final FImage image = new FImage(patternImage[0].length, patternImage.length);
165 
166 		for (int y = 0; y < image.height; y++) {
167 			for (int x = 0; x < image.width; x++) {
168 				if (patternImage[y][x] == code) {
169 					image.pixels[y][x] = 1;
170 				}
171 			}
172 		}
173 
174 		return image;
175 	}
176 
177 	/**
178 	 * Compute all binary maps for each possible pattern code given the number
179 	 * of bits used to encode patterns.
180 	 * 
181 	 * @param patternImage
182 	 *            the pattern data
183 	 * @param nbits
184 	 *            the number of bits for the patterns
185 	 * @return an array of binary maps corresponding to each pattern
186 	 */
187 	public static FImage[] extractPatternImages(int[][] patternImage, int nbits) {
188 		final TIntArrayList uniformPatterns = getUniformPatterns(nbits);
189 
190 		final FImage[] images = new FImage[uniformPatterns.size() + 1];
191 		final int width = patternImage[0].length;
192 		final int height = patternImage.length;
193 
194 		for (int y = 0; y < height; y++) {
195 			for (int x = 0; x < width; x++) {
196 				final int idx = uniformPatterns.indexOf(patternImage[y][x]);
197 
198 				if (images[idx + 1] == null) {
199 					images[idx + 1] = new FImage(width, height);
200 				}
201 
202 				images[idx + 1].pixels[y][x] = 1;
203 			}
204 		}
205 
206 		return images;
207 	}
208 
209 	/**
210 	 * Compute all binary maps for each possible pattern code given the number
211 	 * of bits used to encode patterns.
212 	 * 
213 	 * @param patternImage
214 	 *            the pattern data
215 	 * @param nbits
216 	 *            the number of bits for the patterns
217 	 * @return an array of binary maps corresponding to each pattern
218 	 */
219 	public static boolean[][][] extractPatternMaps(int[][] patternImage, int nbits) {
220 		final TIntArrayList uniformPatterns = getUniformPatterns(nbits);
221 
222 		final int width = patternImage[0].length;
223 		final int height = patternImage.length;
224 		final boolean[][][] maps = new boolean[uniformPatterns.size() + 1][height][width];
225 
226 		for (int y = 0; y < height; y++) {
227 			for (int x = 0; x < width; x++) {
228 				final int idx = uniformPatterns.indexOf(patternImage[y][x]);
229 
230 				maps[idx + 1][y][x] = true;
231 			}
232 		}
233 
234 		return maps;
235 	}
236 
237 	/**
238 	 * Compute all pixels matching each possible pattern code given the number
239 	 * of bits used to encode patterns.
240 	 * 
241 	 * @param patternImage
242 	 *            the pattern data
243 	 * @param nbits
244 	 *            the number of bits for the patterns
245 	 * @return an array of binary maps corresponding to each pattern
246 	 */
247 	public static List<List<Pixel>> extractPatternPixels(int[][] patternImage, int nbits) {
248 		final TIntArrayList uniformPatterns = getUniformPatterns(nbits);
249 
250 		final List<List<Pixel>> images = new ArrayList<List<Pixel>>(uniformPatterns.size() + 1);
251 		final int width = patternImage[0].length;
252 		final int height = patternImage.length;
253 
254 		for (int i = 0; i < uniformPatterns.size() + 1; i++)
255 			images.add(null);
256 
257 		for (int y = 0; y < height; y++) {
258 			for (int x = 0; x < width; x++) {
259 				final int idx = uniformPatterns.indexOf(patternImage[y][x]);
260 
261 				if (images.get(idx + 1) == null) {
262 					images.set(idx + 1, new ArrayList<Pixel>());
263 				}
264 
265 				images.get(idx + 1).add(new Pixel(x, y));
266 			}
267 		}
268 
269 		return images;
270 	}
271 }