001/** 002 * FaceTracker Licence 003 * ------------------- 004 * (Academic, non-commercial, not-for-profit licence) 005 * 006 * Copyright (c) 2010 Jason Mora Saragih 007 * All rights reserved. 008 * 009 * Redistribution and use in source and binary forms, with or without 010 * modification, are permitted provided that the following conditions are met: 011 * 012 * * The software is provided under the terms of this licence stricly for 013 * academic, non-commercial, not-for-profit purposes. 014 * * Redistributions of source code must retain the above copyright notice, 015 * this list of conditions (licence) and the following disclaimer. 016 * * Redistributions in binary form must reproduce the above copyright 017 * notice, this list of conditions (licence) and the following disclaimer 018 * in the documentation and/or other materials provided with the 019 * distribution. 020 * * The name of the author may not be used to endorse or promote products 021 * derived from this software without specific prior written permission. 022 * * As this software depends on other libraries, the user must adhere to and 023 * keep in place any licencing terms of those libraries. 024 * * Any publications arising from the use of this software, including but 025 * not limited to academic journal and conference publications, technical 026 * reports and manuals, must cite the following work: 027 * 028 * J. M. Saragih, S. Lucey, and J. F. Cohn. Face Alignment through Subspace 029 * Constrained Mean-Shifts. International Journal of Computer Vision 030 * (ICCV), September, 2009. 031 * 032 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED 033 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 034 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 035 * EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 036 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 037 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 038 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 039 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 040 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 041 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 042 */ 043package com.jsaragih; 044 045import java.io.BufferedReader; 046import java.io.BufferedWriter; 047import java.io.FileNotFoundException; 048import java.io.FileReader; 049import java.io.FileWriter; 050import java.io.IOException; 051import java.util.Scanner; 052 053import org.openimaj.image.FImage; 054import org.openimaj.image.analysis.algorithm.TemplateMatcher; 055import org.openimaj.image.analysis.algorithm.TemplateMatcher.Mode; 056 057/** 058 * A patch on a face 059 * 060 * @author Jason Mora Saragih 061 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk) 062 */ 063public class Patch { 064 static { Tracker.init(); } 065 066 /** Type of patch (0=raw, 1=grad, 2=lbp) */ 067 public int _t; 068 069 /** scaling */ 070 public double _a; 071 072 /** bias */ 073 public double _b; 074 075 /** Gain */ 076 public FImage _W; 077 078 protected FImage im_ = new FImage(0, 0); 079 protected TemplateMatcher matcher; 080 081 FImage Grad(FImage im) { 082 FImage grad = new FImage(im.width, im.height); 083 084 for (int y = 1; y < im.height - 1; y++) { 085 for (int x = 1; x < im.width - 1; x++) { 086 float vx = im.pixels[y][x + 1] - im.pixels[y][x - 1]; 087 float vy = im.pixels[y + 1][x] - im.pixels[y - 1][x]; 088 grad.pixels[y][x] = vx * vx + vy * vy; 089 } 090 } 091 return grad; 092 } 093 094 final float SGN(float x) { 095 return (x < 0) ? 0 : 1; 096 } 097 098 FImage LBP(FImage im) { 099 FImage lp = new FImage(im.width, im.height); 100 101 // float [] v = new float[9]; 102 // for(int y = 1; y < im.height-1; y++) { 103 // for(int x = 1; x < im.width-1; x++) { 104 // v[4] = im.pixels[y][x-1]; 105 // v[0] = im.pixels[y][x]; 106 // v[5] = im.pixels[y][x+1]; 107 // v[1] = im.pixels[y-1][x-1]; 108 // v[2] = im.pixels[y-1][x]; 109 // v[3] = im.pixels[y-1][x+1]; 110 // v[6] = im.pixels[y+1][x-1]; 111 // v[7] = im.pixels[y+1][x]; 112 // v[8] = im.pixels[y+1][x+1]; 113 // 114 // lp.pixels[y][x] = 115 // SGN(v[0]-v[1])*2 + SGN(v[0]-v[2])*4 + 116 // SGN(v[0]-v[3])*8 + SGN(v[0]-v[4])*16 + 117 // SGN(v[0]-v[5])*32 + SGN(v[0]-v[6])*64 + 118 // SGN(v[0]-v[7])*128 + SGN(v[0]-v[8])*256 ; 119 // } 120 // } 121 122 return lp; 123 } 124 125 void load(final String fname) throws FileNotFoundException { 126 BufferedReader br = null; 127 try { 128 br = new BufferedReader(new FileReader(fname)); 129 Scanner sc = new Scanner(br); 130 read(sc, true); 131 } finally { 132 try { 133 br.close(); 134 } catch (IOException e) { 135 } 136 } 137 } 138 139 void save(final String fname) throws IOException { 140 BufferedWriter bw = null; 141 try { 142 bw = new BufferedWriter(new FileWriter(fname)); 143 144 write(bw); 145 } finally { 146 try { 147 if (bw != null) 148 bw.close(); 149 } catch (IOException e) { 150 } 151 } 152 } 153 154 void write(BufferedWriter s) throws IOException { 155 s.write(IO.Types.PATCH.ordinal() + " " + _t + " " + _a + " " + _b + " "); 156 IO.writeImg(s, _W); 157 } 158 159 static Patch read(Scanner s, boolean readType) { 160 if (readType) { 161 int type = s.nextInt(); 162 assert (type == IO.Types.PATCH.ordinal()); 163 } 164 165 Patch p = new Patch(); // s.nextInt(), s.nextDouble(), s.nextDouble(), 166 // IO.ReadImg(s) ); 167 p._t = s.nextInt(); 168 p._a = s.nextDouble(); 169 p._b = s.nextDouble(); 170 p._W = IO.readImg(s); 171 p.matcher = new TemplateMatcher(p._W.clone(), 172 Mode.NORM_CORRELATION_COEFFICIENT); 173 174 return p; 175 } 176 177 Patch() { 178 } 179 180 /** 181 * @param t 182 * @param a 183 * @param b 184 * @param W 185 */ 186 public Patch(int t, double a, double b, FImage W) { 187 _t = t; 188 _a = a; 189 _b = b; 190 _W = W; 191 matcher = new TemplateMatcher(W.clone(), 192 Mode.NORM_CORRELATION_COEFFICIENT); 193 } 194 195 void response(FImage im, FImage resp) { 196 assert ((im.height >= _W.height) && (im.width >= _W.width)); 197 198 int h = im.height - _W.height + 1; 199 int w = im.width - _W.width + 1; 200 201 if (resp.height != h || resp.width != w) 202 resp.internalAssign(new FImage(w, h)); 203 204 FImage I; 205 if (_t == 0) { 206 I = im; 207 } else { 208 if (im_.height == im.height && im_.width == im.width) { 209 I = im_; 210 } else if (im_.height >= im.height && im_.width >= im.width) { 211 I = im_.extractROI(0, 0, im.width, im.height); 212 } else { 213 im_ = new FImage(im.width, im.height); 214 I = im_; 215 } 216 217 if (_t == 1) { 218 I = Grad(im); 219 } else if (_t == 2) { 220 I = LBP(im); 221 } else { 222 throw new RuntimeException("ERROR: Unsupported patch type!\n"); 223 } 224 } 225 226 matcher.analyseImage(I); 227 FImage res = matcher.getResponseMap(); 228 229 for (int y = 0; y < resp.height; y++) 230 for (int x = 0; x < resp.width; x++) 231 resp.pixels[y][x] = (float) (1.0 / (1.0 + Math 232 .exp(res.pixels[y][x] * _a + _b))); 233 } 234 235 /** 236 * Returns a copy of this patch 237 * 238 * @return a copy of this patch. 239 */ 240 public Patch copy() { 241 return new Patch(_t, _a, _b, _W); 242 } 243}