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}