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 gnu.trove.list.array.TIntArrayList; 033import gnu.trove.map.hash.TIntObjectHashMap; 034 035import java.util.ArrayList; 036import java.util.Arrays; 037import java.util.List; 038 039import org.openimaj.citation.annotation.Reference; 040import org.openimaj.citation.annotation.ReferenceType; 041import org.openimaj.image.FImage; 042import org.openimaj.image.pixel.Pixel; 043 044/** 045 * Class for determining whether specific binary patterns are "uniform". Uniform 046 * patterns have less than one 01 transition and one 01 transition when viewed 047 * as a circular buffer. 048 * <p> 049 * The class caches lookup tables of uniform patterns on demand, with the 050 * exception of the commonly used 8-bit patterns which are cached on 051 * initialization. 052 * 053 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk) 054 * 055 */ 056@Reference( 057 type = ReferenceType.Article, 058 author = { "Ojala, T.", "Pietikainen, M.", "Maenpaa, T." }, 059 title = "Multiresolution gray-scale and rotation invariant texture classification with local binary patterns", 060 year = "2002", 061 journal = "Pattern Analysis and Machine Intelligence, IEEE Transactions on", 062 pages = { "971 ", "987" }, 063 month = "jul", 064 number = "7", 065 volume = "24", 066 customData = { 067 "doi", "10.1109/TPAMI.2002.1017623", 068 "ISSN", "0162-8828" 069 }) 070public class UniformBinaryPattern { 071 protected static TIntObjectHashMap<TIntArrayList> lut = new TIntObjectHashMap<TIntArrayList>(); 072 073 static { 074 // pre-cache the table for 8-bit patterns as it is common 075 lut.put(8, calculateUniformPatterns(8)); 076 // other patterns will be cached on demand 077 } 078 079 protected static TIntArrayList calculateUniformPatterns(int nbits) { 080 final TIntArrayList result = new TIntArrayList(); 081 082 final boolean[] bits = new boolean[nbits]; 083 084 for (int i = 0; i < Math.pow(2, nbits); i++) { 085 Arrays.fill(bits, false); 086 087 for (int temp = i, j = 1; j <= nbits; j++) { 088 final int pow = (int) Math.pow(2, (nbits - j)); 089 090 if (temp / pow > 0) { 091 bits[j - 1] = true; 092 } 093 temp = temp % pow; 094 } 095 096 if (isUniform(bits)) { 097 result.add(i); 098 } 099 } 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}