1

I'm trying to test out the facial recognition API in OpenCV. I have imported the .jar provided and it loads the DLL's correctly. The imageLibIit() function will load the DLL. I also have the provided XML files in a directory:
src\main\resources\opencv

enter image description here

public boolean faceDetect(String inputFilename, String outputFilename){
    if(!loaded){
        if(!imageLibInit()){ // initializes and loads the dynamic libraries
            return false;
        }
    }
    //TODO @Austin fix image resource issues
    ClassLoader loader = Thread.currentThread().getContextClassLoader();
    String xmlResource = loader.getResource("opencv/haarcascade_frontalface_alt.xml").getPath();
    CascadeClassifier faceDetector = new CascadeClassifier(xmlResource);
    Mat inputImage = Imgcodecs.imread(inputFilename);
    ErrorUtils.println(xmlResource);
    if(inputImage.empty()){
        ErrorUtils.println("image is empty");
        return false;
    }
    MatOfRect facesDetected = new MatOfRect();
    faceDetector.detectMultiScale(inputImage, facesDetected);
    Rect[] detections = facesDetected.toArray();
    ErrorUtils.println("Faces detected in '" + inputFilename + "': " + detections.length);
    for(Rect detection : detections){
        Imgproc.rectangle(
                inputImage,
                new Point(detection.x, detection.y),
                new Point(detection.x + detection.width, detection.y + detection.height),
                new Scalar(0, 0, 255)
        );
    }
    Imgcodecs.imwrite(outputFilename, inputImage);
    return true;
}

I'm still getting an error:

OpenCV Error: Assertion failed (!empty()) in cv::CascadeClassifier::detectMultiScale, file C:\builds\master_PackSlaveAddon-win64-vc12-static\opencv\modules\objdetect\src\cascadedetect.cpp, line 1639

I have researched this error and each time the solution seems to be something with the resources. It's more than likely an incredibly simple issue, but I am stuck as of now.

Goodies
  • 4,439
  • 3
  • 31
  • 57
  • It looks like your classifier never gets initialized. What are the contents of `xmlResource` variable just after you load the resource and before you construct the cascade classifier? – Dan Mašek Apr 11 '16 at 19:32
  • 1
    @DanMašek The ErrorUtils class is a simple debug-related class, although you probably inferred that. Anyways, it prints out the path to the XML file: `"/D:/Programming/workspaces/github/project1/HomeServer/target/classes/opencv/haarcascade_frontalface_alt.xml"` – Goodies Apr 11 '16 at 20:14
  • Aha. I believe the problem is the first forward slash at the beginning of the path. Internally OpenCV uses `FileStorage` class to load the data. This class uses `fopen` to open the file. I just did a little test in Visual C++, and it fails to open files with a path like that. Removing the leading slash should solve the problem. – Dan Mašek Apr 11 '16 at 20:41
  • See this [answer](http://stackoverflow.com/a/15663303/3962537) on how to work around this in a platform independent manner. – Dan Mašek Apr 11 '16 at 20:50
  • Worked like a charm, my friend. I will post an answer unless you would like to. – Goodies Apr 11 '16 at 21:05
  • Ya, I'm working on a more complete answer right now. – Dan Mašek Apr 11 '16 at 21:06

1 Answers1

1

Inspecting the OpenCV source code, we can find the following:

  • Internally the CascadeClassifier implementation uses class FileStorage to load the data. [1]

  • Class FileStorage internally uses the function fopen(...) to open the data file. [2] [3]


Since the path returned by the resouce loader ("/D:/Programming/workspaces/github/project1/HomeServer/target/classes/opencv/ha‌​arcascade_frontalface_alt.xml") is a little odd, containing the leading slash, the first suspicion leads to some issue with opening the file.

I wrote a simple little test to check this with Visual C++:

#include <cstdio>
#include <iostream>

bool try_open(char const* filename)
{
    FILE* f;

    f = fopen(filename, "rb");
    if (f) {
        fclose(f);
        return true;
    }
    return false;
}

int main()
{
    char const* path_1("/e:/foo.txt");
    char const* path_2("e:/foo.txt");

    std::cout << "fopen(\"" << path_1 << "\") -> " << try_open(path_1) << "\n";
    std::cout << "fopen(\"" << path_2 << "\") -> " << try_open(path_2) << "\n";

    return 0;
}

Output:

fopen("/e:/foo.txt") -> 0
fopen("e:/foo.txt") -> 1

Therefore the path is a culprit. According to this answer, one platform independent way is to modify the code as follows to generate a valid path:

// ...
ClassLoader loader = Thread.currentThread().getContextClassLoader();
String xmlResource = loader.getResource("opencv/haarcascade_frontalface_alt.xml").getPath();
File file = new File(xmlResource);
xmlResource = file.getAbsolutePath());
// ...
CascadeClassifier faceDetector = new CascadeClassifier(xmlResource);
if(faceDetector.empty()){
    ErrorUtils.println("cascade classifier is empty");
    return false;
}
// ...
Community
  • 1
  • 1
Dan Mašek
  • 17,852
  • 6
  • 57
  • 85
  • This works fine. Also, I appreciate that you did this in C++ since I'm relatively new to Java :) `String xmlResourcePath = new File(loader.getResource("opencv/haarcascade_frontalface_alt.xml").getPath()).getAbsolutePath();` – Goodies Apr 11 '16 at 21:13
  • 1
    No problem, I haven't really written any Java for over a decade, so this was mostly looking up the Java docs, and then digging through the OpenCV source (since the error happened there) and narrowing it down to a minimal test case that could reproduce this. – Dan Mašek Apr 11 '16 at 21:26