org.openimaj.image.processing.face.alignment.CLMAligner Maven / Gradle / Ivy
/**
* Copyright (c) 2011, The University of Southampton and the individual contributors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the University of Southampton nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.openimaj.image.processing.face.alignment;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.openimaj.image.FImage;
import org.openimaj.image.processing.face.detection.CLMDetectedFace;
import org.openimaj.image.processing.face.detection.CLMFaceDetector.Configuration;
import org.openimaj.image.processing.face.tracking.clm.CLMFaceTracker;
import org.openimaj.image.processing.transform.PiecewiseMeshWarp;
import org.openimaj.io.IOUtils;
import org.openimaj.math.geometry.shape.Shape;
import org.openimaj.math.geometry.shape.Triangle;
import org.openimaj.util.pair.Pair;
/**
*
* An aligner that warps a {@link CLMDetectedFace} to the neutral pose
* (reference shape) of the {@link Configuration}.
*
*
* Implementors of subclasses of this should note that if the triangles of the
* configuration are changed that the reference triangles must be recomputed.
*
*
* @author Jonathon Hare ([email protected])
*/
public class CLMAligner implements FaceAligner {
protected Configuration config;
protected int size = 100;
protected transient List referenceTriangles;
protected transient FImage mask;
/**
* Construct a new {@link CLMAligner} using the default
* {@link Configuration} and default size of 100 pixels.
*/
public CLMAligner() {
config = new Configuration();
loadReference();
}
/**
* Construct a new {@link CLMAligner} using the default
* {@link Configuration} and given size for the aligned output image.
*
* @param size
* the output facial patch size
*/
public CLMAligner(int size) {
this.size = size;
config = new Configuration();
loadReference();
}
/**
* Construct a new {@link CLMAligner} using the provided
* {@link Configuration} and default size of 100 pixels.
*
* @param size
* the output facial patch size
* @param config
* the configuration
*/
public CLMAligner(int size, Configuration config) {
this.size = size;
this.config = config;
loadReference();
}
protected void loadReference() {
referenceTriangles = CLMFaceTracker.getTriangles(config.referenceShape, null, this.config.triangles);
mask = new FImage(size, size);
for (final Triangle t : referenceTriangles) {
// magic numbers chosen to scale and centre the face
// with a small border
t.scale(0.3f * size);
t.translate(0.5f * size, 0.45f * size);
mask.drawShapeFilled(t, 1f);
}
}
@Override
public void readBinary(DataInput in) throws IOException {
config = IOUtils.read(in);
loadReference();
}
@Override
public byte[] binaryHeader() {
return this.getClass().getName().getBytes();
}
@Override
public void writeBinary(DataOutput out) throws IOException {
IOUtils.write(config, out);
}
@Override
public FImage align(CLMDetectedFace face) {
if (face == null)
return null;
final List triangles = CLMFaceTracker.getTriangles(
face.getShapeMatrix(), face.getVisibility(), this.config.triangles);
final List> matches = computeMatches(triangles);
final PiecewiseMeshWarp pmw = new PiecewiseMeshWarp(matches);
return pmw.transform(face.getFacePatch(), size, size);
}
@Override
public FImage getMask() {
return mask;
}
private List> computeMatches(List triangles) {
final List> mtris = new ArrayList>();
for (int i = 0; i < triangles.size(); i++) {
final Triangle t1 = triangles.get(i);
final Triangle t2 = referenceTriangles.get(i);
if (t1 != null && t2 != null) {
mtris.add(new Pair(t1, t2));
}
}
return mtris;
}
}