javaout-of-memoryface-detectionopenimaj

OutOfMemoryError after Facededetion


package facerec;


import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.List;

import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;

import org.openimaj.feature.DoubleFVComparison;
import org.openimaj.image.FImage;
import org.openimaj.image.MBFImage;
import org.openimaj.image.colour.RGBColour;
import org.openimaj.image.processing.face.alignment.RotateScaleAligner;
import org.openimaj.image.processing.face.detection.HaarCascadeDetector;
import org.openimaj.image.processing.face.detection.keypoints.FKEFaceDetector;
import org.openimaj.image.processing.face.detection.keypoints.KEDetectedFace;
import org.openimaj.image.processing.face.recognition.EigenFaceRecogniser;
import org.openimaj.image.processing.face.recognition.FaceRecognitionEngine;
import org.openimaj.image.typography.hershey.HersheyFont;
import org.openimaj.math.geometry.point.Point2d;
import org.openimaj.ml.annotation.Annotated;
import org.openimaj.ml.annotation.AnnotatedObject;
import org.openimaj.ml.annotation.ScoredAnnotation;
import org.openimaj.util.pair.IndependentPair;
import org.openimaj.video.VideoDisplay;
import org.openimaj.video.VideoDisplayListener;
import org.openimaj.video.capture.VideoCapture;


public class NewFaceRegister extends KeyAdapter implements VideoDisplayListener<MBFImage> {
    private VideoCapture capture;
    private VideoDisplay<MBFImage> videoFrame;

    FKEFaceDetector faceDetector = new FKEFaceDetector(new HaarCascadeDetector());
    private EigenFaceRecogniser<KEDetectedFace, String> recogniser = EigenFaceRecogniser.create(20, new RotateScaleAligner(), 1, DoubleFVComparison.CORRELATION, 0.9f);
    FaceRecognitionEngine<KEDetectedFace, String> engine = FaceRecognitionEngine.create(faceDetector, recogniser);
    Annotated<KEDetectedFace, String> faceobj;
    private FImage currentFrame;

    public NewFaceRegister() throws Exception {
        capture = new VideoCapture(940, 720);
        //engine = new CLMFaceTracker();
        //engine.fpd = 120;

        videoFrame = VideoDisplay.createVideoDisplay(capture);
        videoFrame.addVideoListener(this);
        SwingUtilities.getRoot(videoFrame.getScreen()).addKeyListener(this);
}

    @Override
    public synchronized void keyPressed(KeyEvent key) {
        if (key.getKeyCode() == KeyEvent.VK_SPACE) {
            this.videoFrame.togglePause();
        } else if (key.getKeyChar() == 'c') {
            // if (!this.videoFrame.isPaused())
            // this.videoFrame.togglePause();

            final String person = JOptionPane.showInputDialog(this.videoFrame.getScreen(), "Name der Person eingeben", "",
                    JOptionPane.QUESTION_MESSAGE);

            final List<KEDetectedFace> faces = detectFaces();
            if (faces.size() == 1) {
                engine.train(faces.get(0), person);
                //TODO Datenbankmethode aufrufen, welches das AnnotatedObject (faceObj) speichert.
            } else {
                System.out.println("Zu viele/wenige Gesichter im Bild");
            }

            //this.videoFrame.close();
        } else 
            System.out.println("Wrong key");
    }

    private List<KEDetectedFace> detectFaces() {
        return engine.getDetector().detectFaces(currentFrame);
    }

    @Override
    public void afterUpdate(VideoDisplay<MBFImage> display) {
        // do nothing
    }

    @Override
    public synchronized void beforeUpdate(MBFImage frame) {
        this.currentFrame = frame.flatten();
        /*engine.track(frame);
        engine.drawModel(frame, true, true, true, true, true);*/
        final List<KEDetectedFace> faces = detectFaces();
        for (KEDetectedFace face : faces) {
            frame.drawShape(face.getBounds(), RGBColour.RED);
        }

        if (recogniser != null && recogniser.listPeople().size() >= 1) {
            for (KEDetectedFace face : faces) {
                List<IndependentPair<KEDetectedFace, ScoredAnnotation<String>>> name = engine.recogniseBest(face.getFacePatch());

                if (name.size() > 0) {
                    final Point2d r = face.getBounds().getTopLeft();
                    frame.drawText(name.get(0).getSecondObject().toString(), r, HersheyFont.ROMAN_SIMPLEX, 15, RGBColour.GREEN);
                }
            }
        }
    }

    public static void main(String[] args) throws Exception {
        new NewFaceRegister();
    }
}

Why do I get an OutOfMemoryError? I tryed it with an other Dedector and there it works?! I also looked in some other Questions for an Answer and i found one Solution and i excactly worked with it, but it also didn't worked. It's my first time working with Openimaj.

Exception in thread "Thread-4" java.lang.OutOfMemoryError: Java heap space
at no.uib.cipr.matrix.AbstractDenseMatrix.<init>(AbstractDenseMatrix.java:47)
at no.uib.cipr.matrix.DenseMatrix.<init>(DenseMatrix.java:167)
at no.uib.cipr.matrix.SVD.<init>(SVD.java:98)
at no.uib.cipr.matrix.SVD.<init>(SVD.java:75)
at no.uib.cipr.matrix.SVD.factorize(SVD.java:146)
at org.openimaj.math.matrix.ThinSingularValueDecomposition.<init>(ThinSingularValueDecomposition.java:84)
at org.openimaj.math.matrix.ThinSingularValueDecomposition.<init>(ThinSingularValueDecomposition.java:69)
at org.openimaj.math.matrix.algorithm.pca.ThinSvdPrincipalComponentAnalysis.learnBasisNorm(ThinSvdPrincipalComponentAnalysis.java:56)
at org.openimaj.math.matrix.algorithm.pca.PrincipalComponentAnalysis.learnBasis(PrincipalComponentAnalysis.java:183)
at org.openimaj.math.matrix.algorithm.pca.PrincipalComponentAnalysis.learnBasis(PrincipalComponentAnalysis.java:170)
at org.openimaj.ml.pca.FeatureVectorPCA.learnBasis(FeatureVectorPCA.java:113)
at org.openimaj.image.model.EigenImages.train(EigenImages.java:125)
at org.openimaj.image.processing.face.feature.EigenFaceFeature$Extractor.train(EigenFaceFeature.java:167)
at org.openimaj.image.processing.face.recognition.EigenFaceRecogniser.beforeBatchTrain(EigenFaceRecogniser.java:159)
at org.openimaj.image.processing.face.recognition.LazyFaceRecogniser.retrain(LazyFaceRecogniser.java:139)
at org.openimaj.image.processing.face.recognition.LazyFaceRecogniser.annotate(LazyFaceRecogniser.java:153)
at org.openimaj.image.processing.face.recognition.EigenFaceRecogniser.annotate(EigenFaceRecogniser.java:55)
at org.openimaj.image.processing.face.recognition.FaceRecogniser.annotateBest(FaceRecogniser.java:115)
at org.openimaj.image.processing.face.recognition.FaceRecognitionEngine.recogniseBest(FaceRecognitionEngine.java:260)
at facerec.NewFaceRegister.beforeUpdate(NewFaceRegister.java:97)
at facerec.NewFaceRegister.beforeUpdate(NewFaceRegister.java:1)
at org.openimaj.video.VideoDisplay.fireBeforeUpdate(VideoDisplay.java:785)
at org.openimaj.video.VideoDisplay.run(VideoDisplay.java:522)
at java.lang.Thread.run(Unknown Source)

Solution

  • The reason you got failed is because of image processing algorithm used. I'm not sure what openimaj uses, but there are two workarounds possible for this:

    1. Increase heap size, so that your application has more memory available for image processing. See How can I increase the JVM memory?

    2. Decrease image size, so that your application will use less memory for processing.

    Based on my own experience with face detection on mobile devices (limited memory as well), 940x720 seems to be more than enough for face detection. Feel free to resize into 640x480 (or similar), results should not be affected.

    Remember that you can copy your initial image, resize it with any aspect ratio (i.g. 1.5), detect face on new resized image and return initial image with detected face coordinates multiplied on your aspect ratio.