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.math.matrix.MatrixUtils; 055 056import Jama.Matrix; 057 058/** 059 * Check whether a patch looks like a face by applying an SVM classifier 060 * 061 * @author Jason Mora Saragih 062 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk) 063 */ 064public class FCheck { 065 static { Tracker.init(); } 066 067 /** < Piecewise affine warp */ 068 PAW _paw; 069 070 /** < SVM bias */ 071 double _b; 072 073 /** < SVM gain */ 074 Matrix _w; 075 076 private Matrix vec_; 077 private FImage crop_; 078 079 /** 080 * Construct 081 * 082 * @param b 083 * @param w 084 * @param paw 085 */ 086 FCheck(double b, Matrix w, PAW paw) { 087 assert ((w.getRowDimension() == paw._nPix)); 088 089 _b = b; 090 _w = w.copy(); 091 _paw = paw; 092 093 crop_ = new FImage(_paw._mask.width, _paw._mask.height); 094 vec_ = new Matrix(_paw._nPix, 1); 095 } 096 097 protected FCheck() { 098 } 099 100 static FCheck load(final String fname) throws FileNotFoundException { 101 BufferedReader br = null; 102 try { 103 br = new BufferedReader(new FileReader(fname)); 104 Scanner sc = new Scanner(br); 105 return read(sc, true); 106 } finally { 107 try { 108 br.close(); 109 } catch (IOException e) { 110 } 111 } 112 } 113 114 void save(final String fname) throws IOException { 115 BufferedWriter bw = null; 116 try { 117 bw = new BufferedWriter(new FileWriter(fname)); 118 119 write(bw); 120 } finally { 121 try { 122 if (bw != null) 123 bw.close(); 124 } catch (IOException e) { 125 } 126 } 127 } 128 129 void write(BufferedWriter s) throws IOException { 130 s.write(IO.Types.FCHECK.ordinal() + " " + _b + " "); 131 IO.writeMat(s, _w); 132 _paw.write(s); 133 } 134 135 static FCheck read(Scanner s, boolean readType) { 136 if (readType) { 137 int type = s.nextInt(); 138 assert (type == IO.Types.FCHECK.ordinal()); 139 } 140 141 FCheck fcheck = new FCheck(); 142 143 fcheck._b = s.nextDouble(); 144 fcheck._w = IO.readMat(s); 145 fcheck._paw = PAW.read(s, true); 146 fcheck.crop_ = new FImage(fcheck._paw._mask.width, 147 fcheck._paw._mask.height); 148 fcheck.vec_ = new Matrix(fcheck._paw._nPix, 1); 149 150 return fcheck; 151 } 152 153 boolean check(FImage im, Matrix s) { 154 assert ((s.getRowDimension() / 2 == _paw.nPoints()) && (s 155 .getColumnDimension() == 1)); 156 157 _paw.crop(im, crop_, s); 158 159 if ((vec_.getRowDimension() != _paw._nPix) 160 || (vec_.getColumnDimension() != 1)) 161 vec_ = new Matrix(_paw._nPix, 1); 162 163 final int w = crop_.width; 164 final int h = crop_.height; 165 166 final double[][] vp = vec_.getArray(); 167 final float[][] cp = crop_.pixels; 168 final float[][] mp = _paw._mask.pixels; 169 170 for (int i = 0, k = 0; i < h; i++) { 171 for (int j = 0; j < w; j++) { 172 if (mp[i][j] != 0) { 173 vp[k][0] = cp[i][j]; 174 k++; 175 } 176 } 177 } 178 179 double mean = MatrixUtils.sum(vec_) / vec_.getRowDimension(); 180 MatrixUtils.minus(vec_, mean); 181 182 double var = 0; 183 for (int i = 0; i < _paw._nPix; i++) 184 var += vec_.get(i, 0) * vec_.get(i, 0); 185 186 if (var < 1.0e-10) 187 MatrixUtils.fill(vec_, 0); 188 else 189 vec_ = vec_.times(1 / Math.sqrt(var)); // FIXME inline 190 191 double wdv = 0; 192 for (int i = 0; i < _paw._nPix; i++) 193 wdv += _w.get(i, 0) * vec_.get(i, 0); 194 195 if ((wdv + _b) > 0) 196 return true; 197 198 return false; 199 } 200}