pythonc++opencvdarknet

OpenCV(4.8.0) Error: The function/feature is not implemented () in cv::debug_build_guard::_OutputArray::assign


I'm trying to translate some Python code to C++, and I run into this code:

self.net = cv.dnn.readNetFromDarknet(cfg_file, weights_file)
self.net.setPreferableBackend(cv.dnn.DNN_BACKEND_OPENCV)
self.ln = self.net.getLayerNames()
self.ln = [self.ln[i-1] for i in self.net.getUnconnectedOutLayers()]
     ...
blob = cv.dnn.blobFromImage(img, 1/255.0, (416, 416), swapRB=True, crop=False)
self.net.setInput(blob)
outputs = self.net.forward(self.ln)
outputs = np.vstack(outputs)

As you can see, the code is taking the output from net forward from the unconnected layer names that are specified in self.ln. Now here is my C++ code:

static Mat blobFromImg;
bool swapRB = true;
blobFromImage(img, blobFromImg, 1/255.0, Size(416, 416), Scalar(), swapRB, false);
network.setInput(blobFromImg);
Mat outMat;
network.forward(outMat , network.getUnconnectedOutLayersNames());
vconcat(outMat, outMat);

As you can see, I am using cv::vconcat in place of np.vstack instead of making a new std::vector<<cv::String>> to pass to network.forward(). I use getUnconnectedOutLayersNames(). Yet I get this error:

OpenCV(4.8.0) Error: The function/feature is not implemented () in cv::debug_build_guard::_OutputArray::assign, file D:\vcpkg\buildtrees\opencv4\src\4.8.0-2bf495557d.clean\modules\core\src\matrix_wrap.cpp, line 2052

The line where the exception occurs is network.forward(). It is because of this method that network.getUnconnectedOutLayersNames() doesn't give the exception by itself.

If I do:

std::vector<cv::String> ln;
auto layers = network.getLayerNames();
for (auto i : network.getUnconnectedOutLayers()){
   ln.push_back(layers[i-1]);
}
   ...
network.setInput(blobFromImg);
Mat outMat;
network.forward(outMat , ln);
vconcat(outMat, outMat);

I get the same result.

Why is the function not implemented? Why does Python have this implementation.

Note: I am using darknet/yolo.


Solution

  • Based on the context, we're dealing with the following overload of cv::dnn::Net::forward, the documentation of which says:

    void cv::dnn::Net::forward(OutputArrayOfArrays outputBlobs, const std::vector<String>& outBlobNames)

    Parameters:

    • outputBlobs contains blobs for first outputs of specified layers.
    • outBlobNames names for layers which outputs are needed to get

    The problem is with the first parameter outputBlobs. The documentation tells us about cv::OutputArrayOfArrays that it's an alias for cv::_OutputArray :

    typedef OutputArray OutputArrayOfArrays;

    Other than that, there's not much to be found on that page. For the more useful information, we need to look at the documentation of cv::_InputArray that it derives from:

    ... can be constructed from ..., std::vector<std::vector<T>>, std::vector<Mat>, std::vector<Mat_<T>>

    ...

    There is another related type, InputArrayOfArrays, which is currently defined as a synonym for InputArray:

    typedef InputArray InputArrayOfArrays;

    It denotes function arguments that are either vectors of vectors or vectors of matrices. A separate synonym is needed to generate Python/Java etc. wrappers properly. At the function implementation level their use is similar, but _InputArray::getMat(idx) should be used to get header for the idx-th component of the outer vector and _InputArray::size().area() should be used to find the number of components (vectors/matrices) of the outer vector.

    And suddenly it becomes clear. We're calling the function with a sequence a layer names, we should be getting back a sequence of blobs. We know that a blob can be represented as a 4D cv::Mat. A sequence of those in C++ would be a vector of Mats: std::vector<cv::Mat>. So, change the first parameter of forward to be that type. And as you say "that worked perfectly".