uk.bl.wa.tika.parser.imagefeatures.FaceDetectionParser.java Source code

Java tutorial

Introduction

Here is the source code for uk.bl.wa.tika.parser.imagefeatures.FaceDetectionParser.java

Source

/**
 * 
 */
package uk.bl.wa.tika.parser.imagefeatures;

/*
 * #%L
 * digipres-tika
 * %%
 * Copyright (C) 2013 - 2018 The webarchive-discovery project contributors
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as
 * published by the Free Software Foundation, either version 2 of the
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public
 * License along with this program.  If not, see
 * <http://www.gnu.org/licenses/gpl-2.0.html>.
 * #L%
 */

import java.awt.Color;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.tika.exception.TikaException;
import org.apache.tika.metadata.Metadata;
import org.apache.tika.metadata.Property;
import org.apache.tika.mime.MediaType;
import org.apache.tika.parser.AbstractParser;
import org.apache.tika.parser.ParseContext;
import org.openimaj.image.FImage;
import org.openimaj.image.ImageUtilities;
import org.openimaj.image.MBFImage;
import org.openimaj.image.colour.Transforms;
import org.openimaj.image.pixel.statistics.HistogramModel;
import org.openimaj.image.processing.face.detection.DetectedFace;
import org.openimaj.image.processing.face.detection.FaceDetector;
import org.openimaj.image.processing.face.detection.HaarCascadeDetector;
import org.openimaj.image.processing.face.detection.HaarCascadeDetector.BuiltInCascade;
import org.openimaj.image.processing.face.detection.keypoints.FKEFaceDetector;
import org.openimaj.image.processing.face.detection.keypoints.KEDetectedFace;
import org.openimaj.math.geometry.shape.Rectangle;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;

import com.typesafe.config.Config;

/**
 * @author Andrew Jackson <Andrew.Jackson@bl.uk>
 *
 */
public class FaceDetectionParser extends AbstractParser {
    private static Log log = LogFactory.getLog(FaceDetectionParser.class);

    /** */
    private static final long serialVersionUID = -773080986108106790L;

    private static final Set<MediaType> SUPPORTED_TYPES = Collections
            .unmodifiableSet(new HashSet<MediaType>(Arrays.asList(MediaType.image("jpg"))));

    public static final String FACE_FRAGMENT_ID = "DETECTED_FACES";
    public static final String DOM_COL = "DOMCOL";
    public static final String DOM_COLS = "DOMCOLS";
    public static final Property IMAGE_HEIGHT = Property.externalInteger("IMAGE_HEIGHT");
    public static final Property IMAGE_WIDTH = Property.externalInteger("IMAGE_WIDTH");
    public static final Property IMAGE_SIZE = Property.externalInteger("IMAGE_SIZE");

    /** */
    private boolean detectFaces;

    private boolean useFKEFaceAlgorithm = false;

    private FaceDetector<DetectedFace, FImage> fd;

    private FaceDetector<DetectedFace, FImage> catfd;
    /** */
    private boolean extractDominantColours;

    private boolean stepThruPixels = false;

    private ColourMatcher cm = new ColourMatcher();

    /** */
    //private ColourExtractor cep = new ColourExtractor();

    /**
     * @param conf
     */
    public FaceDetectionParser(Config conf) {
        this.detectFaces = conf.getBoolean("warc.index.extract.content.images.detectFaces");
        if (this.detectFaces)
            log.info("Face detection enabled.");
        this.extractDominantColours = conf.getBoolean("warc.index.extract.content.images.dominantColours");
        if (this.extractDominantColours)
            log.info("Dominant colour extraction enabled.");
        // Setup human face detector:
        fd = new HaarCascadeDetector(BuiltInCascade.frontalface_alt.classFile(), 20);
        // Also setup a cat-face detector:
        catfd = new HaarCascadeDetector("/opencv/haarcascades/haarcascade_frontalcatface.xml", 20);

    }

    /* (non-Javadoc)
     * @see org.apache.tika.parser.Parser#getSupportedTypes(org.apache.tika.parser.ParseContext)
     */
    @Override
    public Set<MediaType> getSupportedTypes(ParseContext context) {
        return SUPPORTED_TYPES;
    }

    /* (non-Javadoc)
     * @see org.apache.tika.parser.Parser#parse(java.io.InputStream, org.xml.sax.ContentHandler, org.apache.tika.metadata.Metadata, org.apache.tika.parser.ParseContext)
     */
    @Override
    public void parse(InputStream stream, ContentHandler handler, Metadata metadata, ParseContext context)
            throws IOException, SAXException, TikaException {
        // Parse the image:
        MBFImage image = ImageUtilities.readMBF(stream);

        // Get basic metadata:
        metadata.set(IMAGE_HEIGHT, image.getHeight());
        metadata.set(IMAGE_WIDTH, image.getWidth());
        metadata.set(IMAGE_SIZE, image.getWidth() * image.getHeight());

        // Pull out dominant colour:
        if (this.extractDominantColours) {
            Color dc = this.extractDominantColour(image);
            metadata.add(DOM_COL, cm.getMatch(dc).getName());
        }

        // Detect faces:
        if (this.detectFaces) {
            if (this.useFKEFaceAlgorithm) {
                this.useFKEFaceDetector(image, metadata);
            } else {
                this.useHaarCascadeDetector(image, metadata);
            }
        }
    }

    private void useFKEFaceDetector(MBFImage image, Metadata metadata) {
        FaceDetector<KEDetectedFace, FImage> fd = new FKEFaceDetector(20);
        FImage fim = Transforms.calculateIntensity(image);
        List<KEDetectedFace> faces = fd.detectFaces(fim);
        for (KEDetectedFace face : faces) {
            //for( FacialKeypoint kp : face.getKeypoints() ) {
            //    kp.position.translate(face.getBounds().getTopLeft());
            //image.drawPoint(kp.position, RGBColour.GRAY, 3);
            //}
            this.addFaceRectangle(face.getBounds(), metadata, "human");
            //image.drawShape(b, RGBColour.RED);
            //image.drawShape(b, ArrayUtils.toObject(dc.getColorComponents(null)) );
            // Output in standard form: http://www.w3.org/2008/WebVideo/Fragments/WD-media-fragments-spec/#naming-space
        }
        //DisplayUtilities.display(image);
    }

    private void useHaarCascadeDetector(MBFImage image, Metadata metadata) {
        FImage fim = Transforms.calculateIntensity(image);
        // Detect human faces:
        List<DetectedFace> faces = fd.detectFaces(fim);
        for (DetectedFace face : faces) {
            this.addFaceRectangle(face.getBounds(), metadata, "human");
        }
        // Detect cat faces:
        faces = catfd.detectFaces(fim);
        for (DetectedFace face : faces) {
            this.addFaceRectangle(face.getBounds(), metadata, "cat");
        }
    }

    // Output in standard form: http://www.w3.org/2008/WebVideo/Fragments/WD-media-fragments-spec/#naming-space
    private void addFaceRectangle(Rectangle b, Metadata metadata, String kind) {
        String xywh = kind + "@xywh=" + (int) b.x + "," + (int) b.y + "," + (int) b.width + "," + (int) b.height;
        metadata.add(FACE_FRAGMENT_ID, xywh);
        log.info("Found face: " + xywh);
    }

    /**
     * 
     * @param image
     * @return
     */
    private Color extractDominantColour(MBFImage image) {
        // Calculate image histogram:
        int res = 64;
        HistogramModel model = new HistogramModel(res, res, res);
        model.estimateModel(image);
        double max = 0.0;
        int max_i = 0;
        double[] vec = model.getFeatureVector().asDoubleVector();
        for (int i = 0; i < vec.length; i++) {
            if (vec[i] > max) {
                max = vec[i];
                max_i = i;
            }
        }
        Color dc = new Color((int) (255 * model.colourAverage(max_i)[0]),
                (int) (255 * model.colourAverage(max_i)[1]), (int) (255 * model.colourAverage(max_i)[2]));
        //System.out.println("Got Color: " + dc );
        //System.out.println("Got colour: " + cm.getMatch(dc).getName() );

        /*
        for( int i = 0; i < res; i++ ) {
        for( int j = 0; j < res; j++ ) {
            for( int k = 0; k < res; k++ ) {
                //System.out.println("item: "+i+","+j+","+k+" "+model.histogram.get(i,j,k));
            
            }
        }
        }
         */

        //
        if (this.stepThruPixels) {
            int pixelStep = 8;
            Map<Color, Integer> color2counter = new HashMap<Color, Integer>();
            for (int x = 0; x < image.getWidth(); x += pixelStep) {
                for (int y = 0; y < image.getHeight(); y += pixelStep) {
                    Float[] fc = image.getPixel(x, y);
                    Color color = new Color(fc[0], fc[1], fc[2]);
                    color = maxBrightness(color);
                    Integer occurrences = color2counter.get(color);
                    if (occurrences == null)
                        occurrences = 0;
                    color2counter.put(color, occurrences + 1);
                }
            }
            int fcmax = 0;
            Color fcmaxc = null;
            for (Color c : color2counter.keySet()) {
                if (color2counter.get(c) > fcmax) {
                    fcmax = color2counter.get(c);
                    fcmaxc = c;
                }
            }
        }
        //System.out.println("Got colour: "+cm.getMatch(fcmaxc).getName());
        //return fcmaxc;

        return dc;
    }

    private static Color maxBrightness(Color c) {
        float[] hsv = new float[3];
        Color.RGBtoHSB(c.getRed(), c.getGreen(), c.getBlue(), hsv);
        return new Color(Color.HSBtoRGB(hsv[0], hsv[1], 1.0f));
    }

}