I'm currently working on a university project using OpenIMAJ. However, I have problems with correctly using the face recognition API. Face detection always seems to work correctly, but recognition causes an exception at the second call to faceEngine.recogniseBest(). I already tried to normalise the images and scale them to a common resolution but with no success. Even calls with the exact same image twice fail.
Inspired by a Stackoverflow post I put together a small demo to illustrate the problem:
packagetest;importorg.openimaj.feature.DoubleFVComparison;importorg.openimaj.image.FImage;importorg.openimaj.image.ImageUtilities;importorg.openimaj.image.processing.face.alignment.RotateScaleAligner;importorg.openimaj.image.processing.face.detection.HaarCascadeDetector;importorg.openimaj.image.processing.face.detection.keypoints.FKEFaceDetector;importorg.openimaj.image.processing.face.detection.keypoints.KEDetectedFace;importorg.openimaj.image.processing.face.recognition.EigenFaceRecogniser;importorg.openimaj.image.processing.face.recognition.FaceRecognitionEngine;importorg.openimaj.ml.annotation.ScoredAnnotation;importorg.openimaj.util.pair.IndependentPair;importjava.io.File;importjava.io.IOException;importjava.util.List;publicclassFaceRecognitionTest{privatestaticFKEFaceDetectorfaceDetector=newFKEFaceDetector(newHaarCascadeDetector());privatestaticEigenFaceRecogniser<KEDetectedFace,String>faceRecognizer=EigenFaceRecogniser.create(20,newRotateScaleAligner(),1,DoubleFVComparison.CORRELATION,0.9f);privatestaticFaceRecognitionEngine<KEDetectedFace,String>faceEngine=FaceRecognitionEngine.create(faceDetector,faceRecognizer);privatestaticintid=0;publicstaticvoidmain(String[]args)throwsIOException{recognizeFace("lenna.png");recognizeFace("lenna.png");}publicstaticvoidrecognizeFace(Stringpath)throwsIOException{FImagefimg=ImageUtilities.readF(newFile(path));List<KEDetectedFace>faces=faceEngine.getDetector().detectFaces(fimg);for(KEDetectedFaceface:faces){Stringperson=null;try{List<IndependentPair<KEDetectedFace,ScoredAnnotation<String>>>rfaces=faceEngine.recogniseBest(face.getFacePatch());//crasheshereScoredAnnotation<String>score=rfaces.get(0).getSecondObject();if(score!=null)person=score.annotation;}catch(Exceptione){}if(person==null){person=newString(String.valueOf(id++));System.out.println("Identified new person: "+person);faceEngine.train(person,face.getFacePatch());//necessary?}else{System.out.println("Identified existing person: "+person);}}}}
Identified new person: 0
Exception in thread "main" java.lang.Error: dsaupd ERRNO = -2, see http://www.caam.rice.edu/software/ARPACK/UG/node136.html
at ch.akuhn.matrix.eigenvalues.FewEigenvalues.run(FewEigenvalues.java:212)
at ch.akuhn.matrix.eigenvalues.SingularValues.decompose(SingularValues.java:51)
at org.openimaj.math.matrix.ThinSingularValueDecomposition.<init>(ThinSingularValueDecomposition.java:107)
at org.openimaj.math.matrix.ThinSingularValueDecomposition.<init>(ThinSingularValueDecomposition.java:68)
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 test.FaceRecognitionTest.recognizeFace(FaceRecognitionTest.java:40)
at test.FaceRecognitionTest.main(FaceRecognitionTest.java:30)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
Process finished with exit code 1
I am using a gradle build with the following dependency: compile('org.openimaj:faces:1.3.1')
Of course I am aware that a single image as training input is insufficient for a good recognition rate, but in the example I use the exact same image twice. Anyway, even if the person is not recognized correctly, I would expect the call to not crash but just yield an empty result.
By the way, is the explicit call to faceEngine.train() necessary? It seems faceEngine.recogniseBest() is training automatically (as seen in the stack trace). If I remove that call the program finishes successfully, but the face is not recognized to be the same (also not if I lower the threshold).
Am I missing something? Any idea how to fix this?
Daniel
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
The problem is that you can't use an EigenFaceRecogniser with a single training image - it needs a suitably large number of samples in order to learn the PCA basis before recognition can proceed (they can't all be the same image either as the input matrix would have rank-1).
As for calling faceEngine.train(), yes that's necessary to tell the faceRecogniser that you want it to learn a new example. The reason that you see a call to train (a different train method btw) in the stacktrace is that the EigenFaceRecogniser is a LazyRecogniser and doesn't actually do the training internally until you call one of the recognise methods for the first time (it will re-learn as necessary, so if you call train with new faces, it will internally redo the training on the next call to recognise).
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Thanks a lot for your fast and informative reply.
As far as I can see there is no way to check whether the PCA basis has already been learned. I replaced "Exception" by "Throwable" to pass the error.
Daniel
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
After ignoring all previous assert-failures and out-of-bounds exception for 5-7 different images I get another exception:
java.lang.IllegalArgumentException: Vectors have differing lengths
at org.openimaj.feature.DoubleFVComparison$3.compare(DoubleFVComparison.java:98)
at org.openimaj.feature.DoubleFVComparison.compare(DoubleFVComparison.java:355)
at org.openimaj.feature.DoubleFVComparison$3.compare(DoubleFVComparison.java:94)
at org.openimaj.knn.ObjectNearestNeighbours.distanceFunc(ObjectNearestNeighbours.java:76)
at org.openimaj.knn.ObjectNearestNeighboursExact.search(ObjectNearestNeighboursExact.java:230)
at org.openimaj.knn.ObjectNearestNeighboursExact.searchKNN(ObjectNearestNeighboursExact.java:175)
at org.openimaj.knn.ObjectNearestNeighboursExact.searchKNN(ObjectNearestNeighboursExact.java:49)
at org.openimaj.ml.annotation.basic.KNNAnnotator.annotate(KNNAnnotator.java:320)
at org.openimaj.image.processing.face.recognition.AnnotatorFaceRecogniser.annotate(AnnotatorFaceRecogniser.java:139)
at org.openimaj.image.processing.face.recognition.LazyFaceRecogniser.annotate(LazyFaceRecogniser.java:154)
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)
...
Where h1.length is 2 and increases with each trained image and h2.length keeps 1. I tried to resample all images to an equal size but with the same result. Can you help me out?
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
It looks like the faces you're training/testing with have different sizes. It's important for EigenFaces that the face images are exactly the same height and width. In your code above, the face patch returned by face.getFacePatch() would need to be resampled to a standardised size.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
View and moderate all "General Discussion" comments posted by this user
Mark all as spam, and block user from posting to "Discussion"
Hi!
I'm currently working on a university project using OpenIMAJ. However, I have problems with correctly using the face recognition API. Face detection always seems to work correctly, but recognition causes an exception at the second call to faceEngine.recogniseBest(). I already tried to normalise the images and scale them to a common resolution but with no success. Even calls with the exact same image twice fail.
Inspired by a Stackoverflow post I put together a small demo to illustrate the problem:
I used the following image as input:
http://tutorial.simplecv.org/en/latest/_images/lenna.png
Output:
I am using a gradle build with the following dependency:
compile('org.openimaj:faces:1.3.1')
Of course I am aware that a single image as training input is insufficient for a good recognition rate, but in the example I use the exact same image twice. Anyway, even if the person is not recognized correctly, I would expect the call to not crash but just yield an empty result.
By the way, is the explicit call to faceEngine.train() necessary? It seems faceEngine.recogniseBest() is training automatically (as seen in the stack trace). If I remove that call the program finishes successfully, but the face is not recognized to be the same (also not if I lower the threshold).
Am I missing something? Any idea how to fix this?
Daniel
The problem is that you can't use an EigenFaceRecogniser with a single training image - it needs a suitably large number of samples in order to learn the PCA basis before recognition can proceed (they can't all be the same image either as the input matrix would have rank-1).
As for calling
faceEngine.train()
, yes that's necessary to tell the faceRecogniser that you want it to learn a new example. The reason that you see a call totrain
(a different train method btw) in the stacktrace is that theEigenFaceRecogniser
is aLazyRecogniser
and doesn't actually do the training internally until you call one of the recognise methods for the first time (it will re-learn as necessary, so if you calltrain
with new faces, it will internally redo the training on the next call to recognise).View and moderate all "General Discussion" comments posted by this user
Mark all as spam, and block user from posting to "Discussion"
Hi Jonathon!
Thanks a lot for your fast and informative reply.
As far as I can see there is no way to check whether the PCA basis has already been learned. I replaced "Exception" by "Throwable" to pass the error.
Daniel
View and moderate all "General Discussion" comments posted by this user
Mark all as spam, and block user from posting to "Discussion"
After ignoring all previous assert-failures and out-of-bounds exception for 5-7 different images I get another exception:
Where h1.length is 2 and increases with each trained image and h2.length keeps 1. I tried to resample all images to an equal size but with the same result. Can you help me out?
It looks like the faces you're training/testing with have different sizes. It's important for EigenFaces that the face images are exactly the same height and width. In your code above, the face patch returned by
face.getFacePatch()
would need to be resampled to a standardised size.View and moderate all "General Discussion" comments posted by this user
Mark all as spam, and block user from posting to "Discussion"
Got it working, thanks a lot for your support :)
View and moderate all "General Discussion" comments posted by this user
Mark all as spam, and block user from posting to "Discussion"
Would you please share your working code.