0
votes

I'm trying to write a simple python c-extension which includes some opencv code. Here is my c++ code:

#include "Python.h"
#include "numpy/arrayobject.h"

#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>


 /* .... C matrix utility functions ..................*/
double **pymatrix_to_Carrayptrs(PyArrayObject *arrayin);
double **ptrvector(long n);
void free_Carrayptrs(double **v);
int  not_doublematrix(PyArrayObject *mat);

static PyObject *simple_cv_ops(PyObject *self, PyObject *args)
{
    PyArrayObject *matin, *matout;
    double **cin, **cout;
    int n,m, dims[2];

    /* Parse tuples separately since args will differ between C fcns */
    if (!PyArg_ParseTuple(args, "O!", &PyArray_Type, &matin))  return NULL;
    if (NULL == matin)  return NULL;

    /* Check that object input is 'double' type and a matrix
       Not needed if python wrapper function checks before call to this routine */
    if (not_doublematrix(matin)) return NULL;

    /* Get the dimensions of the input */
    n=dims[0]=matin->dimensions[0];
    m=dims[1]=matin->dimensions[1];

    /* Make a new double matrix of same dims */
    matout=(PyArrayObject *) PyArray_FromDims(2,dims,NPY_DOUBLE);

    /* Change contiguous arrays into C ** arrays (Memory is Allocated!) */
    cin=pymatrix_to_Carrayptrs(matin);
    cout=pymatrix_to_Carrayptrs(matout);

    // _______ Program LOGIC HERE ________

    cv::Mat kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(10, 10), cv::Point(-1, -1));
    cv::Mat imgIn(n, m, CV_64F, cin);
    cv::Mat imgOut(n, m, CV_64F);

    cv::morphologyEx(imgIn, imgOut, cv::MORPH_CLOSE, kernel);
    cv::GaussianBlur(imgOut, imgOut, cv::Size(5, 5), 0, 0);
    cv::medianBlur(imgOut, imgOut, 5);
    cv::threshold(imgOut, imgOut, 127, 255, 0);

    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < m; j++)
        {
            cout[i][j] = imgOut.at<double>(i,j);

        }
    }   

// ____________________________________

    /* Free memory, close file and return */
    free_Carrayptrs(cin);
    free_Carrayptrs(cout);

    return PyArray_Return(matout);
}


/* ==== Set up the methods table ====================== */
static PyMethodDef simple_cv_methods[] = {
    {"simple_cv_ops", simple_cv_ops, METH_VARARGS, "Just a simple cv operation"},
    {NULL, NULL, 0, NULL}     /* Sentinel - marks the end of this structure */
};

static struct PyModuleDef simple_cv_modules = {
    PyModuleDef_HEAD_INIT,
    "simple_cv",
    "Python interface for the simple cv ops C library function",
    -1,
    simple_cv_methods
};

PyMODINIT_FUNC PyInit_simple_cv(void) {
    PyObject *pyob;

    pyob = PyModule_Create(&simple_cv_modules);
    import_array();

    return pyob;
}


/* ==== Create Carray from PyArray ======================
Assumes PyArray is contiguous in memory.
Memory is allocated!                                    */
double **pymatrix_to_Carrayptrs(PyArrayObject *arrayin)  {
    double **c, *a;
    int i,n,m;

    n=arrayin->dimensions[0];
    m=arrayin->dimensions[1];
    c=ptrvector(n);
    a=(double *) arrayin->data;  /* pointer to arrayin data as double */
    for ( i=0; i<n; i++)  {
        c[i]=a+i*m;  }
    return c;
}

/* ==== Allocate a double *vector (vec of pointers) ======================
Memory is Allocated!  See void free_Carray(double ** )                  */
double **ptrvector(long n)  {
    double **v;
    v=(double **)malloc((size_t) (n*sizeof(double)));
    if (!v)   {
        printf("In **ptrvector. Allocation of memory for double array failed.");
        exit(0);  }
    return v;
}

/* ==== Free a double *vector (vec of pointers) ========================== */
void free_Carrayptrs(double **v)  {
    free((char*) v);
}

/* ==== Check that PyArrayObject is a double (Float) type and a matrix ==============
return 1 if an error and raise exception */
int  not_doublematrix(PyArrayObject *mat)  {
    if (mat->descr->type_num != NPY_DOUBLE || mat->nd != 2)  {
        PyErr_SetString(PyExc_ValueError,
            "In not_doublematrix: array must be of type Float and 2 dimensional (n x m).");
        return 1;  }
    return 0;
}

To write the c-extension I used some codes from here which works perfectly except when I use opencv in my code.

Here I also post my setup.py code:

import numpy
from distutils.core import setup, Extension

def main():
    setup(name="simple_cv",
          version="1.0.0",
          description="Python interface for the simple cv C extension library function",
          author="My Name",
          author_email="[email protected]",
          ext_modules=[Extension("simple_cv", ["simple_cv.cpp"],
                  include_dirs=[numpy.get_include(), 'C:/opencv/build/include']),
                        ])

if __name__ == "__main__":
    main()

when I run my python setup I get the following errors:

simple_cv.obj : error LNK2001: unresolved external symbol "double __cdecl cv::threshold(class cv::_InputArray const &,class cv::_OutputArray const &,double,double,int)" (?threshold@cv@@YANAEBV_InputArray@1@AEBV_OutputArray@1@NNH@Z) simple_cv.obj : error LNK2001: unresolved external symbol "public: void __cdecl cv::Mat::deallocate(void)" (?deallocate@Mat@cv@@QEAAXXZ) simple_cv.obj : error LNK2001: unresolved external symbol "public: void __cdecl cv::Mat::create(int,int const *,int)" (?create@Mat@cv@@QEAAXHPEBHH@Z) simple_cv.obj : error LNK2001: unresolved external symbol "class cv::Mat __cdecl cv::getStructuringElement(int,class cv::Size_,class cv::Point_)" (?getStructuringElement@cv@@YA?AVMat@1@HV?$Size_@H@1@V?$Point_@H@1@@Z) simple_cv.obj : error LNK2001: unresolved external symbol "void __cdecl cv::GaussianBlur(class cv::_InputArray const &,class cv::OutputArray const &,class cv::Size,double,double,int)" (?GaussianBlur@cv@@YAXAEBV_InputArray@1@AEBV_OutputArray@1@V?$Size_@H@1@NNH@Z) simple_cv.obj : error LNK2001: unresolved external symbol "void __cdecl cv::fastFree(void *)" (?fastFree@cv@@YAXPEAX@Z) simple_cv.obj : error LNK2001: unresolved external symbol "private: void __cdecl cv::String::deallocate(void)" (?deallocate@String@cv@@AEAAXXZ) simple_cv.obj : error LNK2001: unresolved external symbol "private: char * __cdecl cv::String::allocate(unsigned __int64)" (?allocate@String@cv@@AEAAPEAD_K@Z) simple_cv.obj : error LNK2001: unresolved external symbol "void __cdecl cv::error(int,class cv::String const &,char const *,char const *,int)" (?error@cv@@YAXHAEBVString@1@PEBD1H@Z) simple_cv.obj : error LNK2001: unresolved external symbol "void __cdecl cv::morphologyEx(class cv::_InputArray const &,class cv::_OutputArray const &,int,class cv::InputArray const &,class cv::Point,int,int,class cv::Scalar_ const &)" (?morphologyEx@cv@@YAXAEBV_InputArray@1@AEBV_OutputArray@1@H0V?$Point_@H@1@HHAEBV?$Scalar_@N@1@@Z) simple_cv.obj : error LNK2001: unresolved external symbol "void __cdecl cv::medianBlur(class cv::_InputArray const &,class cv::_OutputArray const &,int)" (?medianBlur@cv@@YAXAEBV_InputArray@1@AEBV_OutputArray@1@H@Z) build\lib.win-amd64-3.6\simple_cv_c.cp36-win_amd64.pyd : fatal error LNK1120: 12 unresolved externals error: command 'C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\VC\Tools\MSVC\14.12.25827\bin\HostX86\x64\link.exe' failed with exit status 1120

Now I know that these are linker errors. I had this problem when coding in Visual Studio and I resolved it by adding the lib path to Additional Library Directories at Linker tab of my Visual Studios project settings. But here I dont know what to do.

By the way I'm using windows 10, Visual Studio 2017 and python 3.6.

Can someone help me please?

1

1 Answers

1
votes

I found the answer.

Looks like Pythons Extension class from distutils.core module hass two additional input arguments for libraries which are library_dirs and libraries.

So I just had to change my setup.py code as below:

import numpy
from distutils.core import setup, Extension

def main():
    setup(name="simple_cv",
          version="1.0.0",
          description="Python interface for the simple cv C extension library function",
          author="My Name",
          author_email="[email protected]",
          ext_modules=[Extension("simple_cv", ["simple_cv.cpp"],
                  include_dirs=[numpy.get_include(), 'C:/opencv/build/include']),
                  library_dirs = ['C:\\opencv\\build\\x64\\vc14\\lib'],
                  libraries = ['opencv_world330']])

if __name__ == "__main__":
    main()