How do Python C++ bindings work?

I tried to debug-trace ‘cv2.imshow()’ and it was just executed. Then I learned about Python C++ bindings.

My question here is, how do they work?
More particularly, as an example, what happens when I execute ‘cv2.imshow()’? Where is ‘cv2.imshow()’ linked to?
It looks like it is linked with a compiled object, which is not debug-traceable.

I also want to know if and how can I have access to such bindings.

Note: I googled my question (of the topic) --using various wordings-- before I posted this question here. I found hundreds of references on the subject of Python C++ bindings but not a single one on how exactly does it work.

If you have installed the bindings from pip then the process should be similar to what I describe below. The details may not be 100% correct but I think this high level overview should be enough to answer your question.

When you call cv2.imshow() you are calling the pyopencv_cv_imshow() function “inside the python bindings”, where the bindings are the PATH_TO_PYTHON_DIST/site-packages/cv2/cv2.pyd shared library containing the entry points (e.g. pyopencv_cv_imshow) and the OpenCV C++ library of functions (e.g. cv::imshow).

These bindings act as a wrapper around the C++ library allowing you to call it from python. In the extract below for pyopencv_cv_imshow you can see how the python objects are converted to their OpenCV counterparts and passed directly to the cv::imshow function.

Extract from OPENCV_BUILD_DIR\modules\python_bindings_generator\pyopencv_generated_funcs.h
static PyObject* pyopencv_cv_imshow(PyObject* , PyObject* py_args, PyObject* kw)
{
    using namespace cv;

    pyPrepareArgumentConversionErrorsStorage(3);

    {
    PyObject* pyobj_winname = NULL;
    String winname;
    PyObject* pyobj_mat = NULL;
    Mat mat;

    const char* keywords[] = { "winname", "mat", NULL };
    if( PyArg_ParseTupleAndKeywords(py_args, kw, "OO:imshow", (char**)keywords, &pyobj_winname, &pyobj_mat) &&
        pyopencv_to_safe(pyobj_winname, winname, ArgInfo("winname", 0)) &&
        pyopencv_to_safe(pyobj_mat, mat, ArgInfo("mat", 0)) )
    {
        ERRWRAP2(cv::imshow(winname, mat));
        Py_RETURN_NONE;
    }


        pyPopulateArgumentConversionErrors();
    }
    

    {
    PyObject* pyobj_winname = NULL;
    String winname;
    PyObject* pyobj_mat = NULL;
    cuda::GpuMat mat;

    const char* keywords[] = { "winname", "mat", NULL };
    if( PyArg_ParseTupleAndKeywords(py_args, kw, "OO:imshow", (char**)keywords, &pyobj_winname, &pyobj_mat) &&
        pyopencv_to_safe(pyobj_winname, winname, ArgInfo("winname", 0)) &&
        pyopencv_to_safe(pyobj_mat, mat, ArgInfo("mat", 0)) )
    {
        ERRWRAP2(cv::imshow(winname, mat));
        Py_RETURN_NONE;
    }


        pyPopulateArgumentConversionErrors();
    }
    

    {
    PyObject* pyobj_winname = NULL;
    String winname;
    PyObject* pyobj_mat = NULL;
    UMat mat;

    const char* keywords[] = { "winname", "mat", NULL };
    if( PyArg_ParseTupleAndKeywords(py_args, kw, "OO:imshow", (char**)keywords, &pyobj_winname, &pyobj_mat) &&
        pyopencv_to_safe(pyobj_winname, winname, ArgInfo("winname", 0)) &&
        pyopencv_to_safe(pyobj_mat, mat, ArgInfo("mat", 0)) )
    {
        ERRWRAP2(cv::imshow(winname, mat));
        Py_RETURN_NONE;
    }


        pyPopulateArgumentConversionErrors();
    }
    pyRaiseCVOverloadException("imshow");

    return NULL;
}

In windows with Visual Studio you can build a Debug version of OpenCV from source with the python bindings and step through. You will need a debug version of the python lib or you will have to manually alter PATH_TO_PYTHON_DIST/include/pyconfig.h

1 Like

Thank you @cudawarped for your detailed and extensive reply. Things are much clearer to me now, on this complicated subject.