如何使用Cython包装Cuda版本的SparsePyrLKOpticalFlow?

时间:2018-04-14 18:31:21

标签: opencv cython wrapper opticalflow

我正在尝试使用Cython包装SparsePyrLKOpticalFlow OpenCV算法的Cuda版本。到目前为止,我的代码主要基于this post,并在其答案中进行了修改。我想我必须初始化SparsePyrLKOpticalFlow类的实例,然后使用该类继承自calc的成员函数SparseOpticalFlow

这是我到目前为止所拥有的:

Corner_Tracker.pxd

from libcpp cimport bool
from cpython.ref cimport PyObject
from libcpp.vector cimport vector

# References PyObject to OpenCV object conversion code borrowed from OpenCV's own conversion file, cv2.cpp
cdef extern from 'pyopencv_converter.cpp':
    cdef void import_array()
    cdef PyObject* pyopencv_from(const Mat& m)
    cdef bool pyopencv_to(PyObject* o, Mat& m)

cdef extern from 'opencv2/imgproc.hpp' namespace 'cv':
    cdef enum InterpolationFlags:
        INTER_NEAREST = 0 
    cdef enum ColorConversionCodes:
        COLOR_BGR2GRAY

cdef extern from 'opencv2/core/core.hpp':
    cdef int CV_8UC1
    cdef int CV_32FC1

cdef extern from 'opencv2/core/core.hpp' namespace 'cv':
    cdef cppclass Size_[T]:
        Size_() except +
        Size_(T width, T height) except +
        T width
        T height
    ctypedef Size_[int] Size2i
    ctypedef Size2i Size
    cdef cppclass Scalar[T]:
        Scalar() except +
        Scalar(T v0) except +

cdef extern from 'opencv2/core/core.hpp' namespace 'cv':
    cdef cppclass Mat:
        Mat() except +
        void create(int, int, int) except +
        void* data
        int rows
        int cols
    cdef cppclass Algorithm:
        Algorithm() except +

cdef extern from 'opencv2/core/cuda.hpp' namespace 'cv::cuda':
    cdef cppclass GpuMat:
        GpuMat() except +
        void upload(Mat arr) except +
        void download(Mat dst) const
    cdef cppclass Stream:
        Stream() except +

cdef extern from 'opencv2/core/cvstd.hpp' namespace 'cv':
    cdef cppclass Ptr[T]:
        Ptr() except +
        Ptr(Ptr*) except +
        T& operator* () except +

cdef extern from 'opencv2/cudaoptflow.hpp' namespace 'cv::cuda':
    cdef cppclass SparseOpticalFlow:
        void calc(GpuMat prevImg, GpuMat nextImg, GpuMat prevPts, GpuMat nextPts, GpuMat status) except +

cdef extern from 'opencv2/cudaoptflow.hpp' namespace 'cv::cuda::SparsePyrLKOpticalFlow':
    cdef cppclass SparsePyrLKOpticalFlow(SparseOpticalFlow):
        @staticmethod
        Ptr[SparsePyrLKOpticalFlow] create(Size winSize, int maxLevel) except +

我怀疑我没有正确输入calc参数类型,但我不确定还能做什么。

Corner_Tracker.pyx

import numpy as np
import cv2 
cimport numpy as np
from cython.operator cimport dereference

def cudaCalcFlowWrapper(
        np.ndarray[np.uint8_t, ndim=2] prevImg,
        np.ndarray[np.uint8_t, ndim=2] nextImg,
        np.ndarray[np.float32_t, ndim=2] prevPts):

    np.import_array()

    cdef Ptr[SparsePyrLKOpticalFlow] flow_tracker = SparsePyrLKOpticalFlow.create(Size(15,15), 2)  

    cdef Mat prevImgMat
    cdef GpuMat prevImgGpu
    pyopencv_to(<PyObject*> prevImg, prevImgMat)
    prevImgGpu.upload(prevImgMat)

    cdef Mat nextImgMat
    cdef GpuMat nextImgGpu
    pyopencv_to(<PyObject*> nextImg, nextImgMat)
    nextImgGpu.upload(nextImgMat)

    cdef Mat prevPtsMat = Mat()
    cdef GpuMat prevPtsGpu
    pyopencv_to(<PyObject*> prevPts, prevPtsMat)
    prevPtsGpu.upload(prevPtsMat)

    cdef Mat nextPtsMat
    cdef Mat statusMat
    cdef GpuMat nextPtsGpu
    cdef GpuMat statusGpu

    dereference(flow_tracker).calc(prevImgGpu, nextImgGpu, prevPtsGpu, nextPtsGpu, statusGpu)

    nextPtsGpu.download(nextPtsMat)
    statusGpu.download(statusMat)

    cdef np.ndarray nextPts = <np.ndarray> pyopencv_from(nextPtsMat)
    cdef np.ndarray status = <np.ndarray> pyopencv_from(statusMat)

    return nextPts, status

setup.py

import subprocess
import os
import numpy as np
from distutils.core import setup, Extension
from Cython.Build import cythonize
from Cython.Distutils import build_ext

# Determine current directory of this setup file to find our module
CUR_DIR = os.path.dirname(__file__)

# Use pkg-config to determine library locations and include locations
opencv_libs_str = subprocess.check_output("pkg-config --libs opencv".split()).decode()
opencv_incs_str = subprocess.check_output("pkg-config --cflags opencv".split()).decode()

# Parse into usable format for Extension call
opencv_libs = [str(lib) for lib in opencv_libs_str.strip().split()]
opencv_incs = [str(inc) for inc in opencv_incs_str.strip().split()]

extensions = [ 
    Extension('Corner_Tracker',
              sources=[os.path.join(CUR_DIR, 'Corner_Tracker.pyx')],
              language='c++',
              include_dirs=[np.get_include()] + opencv_incs,
              extra_link_args=opencv_libs)
]

setup(
    cmdclass={'build_ext': build_ext},
    name="Corner_Tracker",
    ext_modules=cythonize(extensions)
)

当我运行python setup.py build_ext --inplace时,我明白了:

Compiling Corner_Tracker.pyx because it changed.
[1/1] Cythonizing Corner_Tracker.pyx
running build_ext
building 'Corner_Tracker' extension
x86_64-linux-gnu-gcc -pthread -DNDEBUG -g -fwrapv -O2 -Wall -Wstrict-prototypes -fno-strict-aliasing -Wdate-time -D_FORTIFY_SOURCE=2 -g -fstack-protector-strong -Wformat -Werror=format-security -fPIC -I/home/sccarey/.virtualenvs/cv/local/lib/python2.7/site-packages/numpy/core/include -I-I/usr/local/include/opencv -I-I/usr/local/include -I/usr/include/python2.7 -c Corner_Tracker.cpp -o build/temp.linux-x86_64-2.7/Corner_Tracker.o
cc1plus: warning: command line option ‘-Wstrict-prototypes’ is valid for C/ObjC but not for C++
In file included from /home/sccarey/.virtualenvs/cv/local/lib/python2.7/site-packages/numpy/core/include/numpy/ndarraytypes.h:1816:0,
                 from /home/sccarey/.virtualenvs/cv/local/lib/python2.7/site-packages/numpy/core/include/numpy/ndarrayobject.h:18,
                 from pyopencv_converter.cpp:2,
                 from Corner_Tracker.cpp:623:
/home/sccarey/.virtualenvs/cv/local/lib/python2.7/site-packages/numpy/core/include/numpy/npy_1_7_deprecated_api.h:15:2: warning: #warning "Using deprecated NumPy API, disable it by " "#defining NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION" [-Wcpp]
 #warning "Using deprecated NumPy API, disable it by " \
  ^
Corner_Tracker.cpp: In function ‘PyObject* __pyx_pf_14Corner_Tracker_cudaCalcFlowWrapper(PyObject*, PyArrayObject*, PyArrayObject*, PyArrayObject*)’:
Corner_Tracker.cpp:1843:67: error: type/value mismatch at argument 1 in template parameter list for ‘template<class T> struct cv::Ptr’
   cv::Ptr<cv::cuda::SparsePyrLKOpticalFlow::SparsePyrLKOpticalFlow>  __pyx_v_flow_tracker;
                                                                   ^
Corner_Tracker.cpp:1843:67: note:   expected a type, got ‘cv::cuda::SparsePyrLKOpticalFlow::SparsePyrLKOpticalFlow’
Corner_Tracker.cpp:1866:67: error: type/value mismatch at argument 1 in template parameter list for ‘template<class T> struct cv::Ptr’
   cv::Ptr<cv::cuda::SparsePyrLKOpticalFlow::SparsePyrLKOpticalFlow>  __pyx_t_3;
                                                                   ^
Corner_Tracker.cpp:1866:67: note:   expected a type, got ‘cv::cuda::SparsePyrLKOpticalFlow::SparsePyrLKOpticalFlow’
Corner_Tracker.cpp:1868:3: error: ‘cv::cuda::SparsePyrLKOpticalFlow::SparsePyrLKOpticalFlow’ names the constructor, not the type
   cv::cuda::SparsePyrLKOpticalFlow::SparsePyrLKOpticalFlow __pyx_t_5;
   ^
Corner_Tracker.cpp:1868:60: error: expected ‘;’ before ‘__pyx_t_5’
   cv::cuda::SparsePyrLKOpticalFlow::SparsePyrLKOpticalFlow __pyx_t_5;
                                                            ^
Corner_Tracker.cpp:1868:69: error: statement cannot resolve address of overloaded function
   cv::cuda::SparsePyrLKOpticalFlow::SparsePyrLKOpticalFlow __pyx_t_5;
                                                                     ^
Corner_Tracker.cpp:1923:81: error: invalid user-defined conversion from ‘cv::Ptr<cv::cuda::SparsePyrLKOpticalFlow>’ to ‘int’ [-fpermissive]
     __pyx_t_3 = cv::cuda::SparsePyrLKOpticalFlow::SparsePyrLKOpticalFlow::create(__pyx_t_2, 2);
                                                                                 ^
In file included from /usr/local/include/opencv2/core/cvstd.hpp:1067:0,
                 from /usr/local/include/opencv2/core/base.hpp:56,
                 from /usr/local/include/opencv2/core.hpp:54,
                 from /usr/local/include/opencv2/core/core.hpp:48,
                 from pyopencv_converter.cpp:3,
                 from Corner_Tracker.cpp:623:
/usr/local/include/opencv2/core/ptr.inl.hpp:222:1: note: candidate is: cv::Ptr<T>::operator T*() const [with T = cv::cuda::SparsePyrLKOpticalFlow] <near match>
 Ptr<T>::operator T* () const
 ^
/usr/local/include/opencv2/core/ptr.inl.hpp:222:1: note:   no known conversion from ‘cv::cuda::SparsePyrLKOpticalFlow*’ to ‘int’
Corner_Tracker.cpp:2022:5: error: ‘__pyx_t_5’ was not declared in this scope
     __pyx_t_5 = * __pyx_v_flow_tracker;
     ^
Corner_Tracker.cpp:2022:19: error: invalid type argument of unary ‘*’ (have ‘int’)
     __pyx_t_5 = * __pyx_v_flow_tracker;
                   ^
Corner_Tracker.cpp:2028:5: error: ‘__pyx_t_5’ was not declared in this scope
     __pyx_t_5.calc(__pyx_v_prevImgGpu, __pyx_v_nextImgGpu, __pyx_v_prevPtsGpu, __pyx_v_nextPtsGpu, __pyx_v_statusGpu);
     ^
error: command 'x86_64-linux-gnu-gcc' failed with exit status 1

我已经玩了几天,但我开始认为我只是在我的头上。 有人可以帮助我吗?

编辑:我通过用numpy数组替换包装器参数中的memoryviews来修复了一个不相关的错误。

编辑:应用@DavidW建议的更改后,我收到了这个新错误:

Corner_Tracker.cpp: In function ‘PyObject* __pyx_pf_14Corner_Tracker_cudaCalcFlowWrapper(PyObject*, PyArrayObject*, PyArrayObject*, PyArrayObject*)’:
Corner_Tracker.cpp:1868:36: error: cannot declare variable ‘__pyx_t_5’ to be of abstract type ‘cv::cuda::SparsePyrLKOpticalFlow’
   cv::cuda::SparsePyrLKOpticalFlow __pyx_t_5;
                                    ^
In file included from Corner_Tracker.cpp:628:0:
/usr/local/include/opencv2/cudaoptflow.hpp:160:18: note:   because the following virtual functions are pure within ‘cv::cuda::SparsePyrLKOpticalFlow’:
 class CV_EXPORTS SparsePyrLKOpticalFlow : public SparseOpticalFlow
                  ^
/usr/local/include/opencv2/cudaoptflow.hpp:99:18: note:         virtual void cv::cuda::SparseOpticalFlow::calc(cv::InputArray, cv::InputArray, cv::InputArray, cv::InputOutputArray, cv::OutputArray, cv::OutputArray, cv::cuda::Stream&)
     virtual void calc(InputArray prevImg, InputArray nextImg,
                  ^
/usr/local/include/opencv2/cudaoptflow.hpp:163:18: note:        virtual cv::Size cv::cuda::SparsePyrLKOpticalFlow::getWinSize() const
     virtual Size getWinSize() const = 0;
                  ^
/usr/local/include/opencv2/cudaoptflow.hpp:164:18: note:        virtual void cv::cuda::SparsePyrLKOpticalFlow::setWinSize(cv::Size)
     virtual void setWinSize(Size winSize) = 0;
                  ^
/usr/local/include/opencv2/cudaoptflow.hpp:166:17: note:        virtual int cv::cuda::SparsePyrLKOpticalFlow::getMaxLevel() const
     virtual int getMaxLevel() const = 0;
                 ^
/usr/local/include/opencv2/cudaoptflow.hpp:167:18: note:        virtual void cv::cuda::SparsePyrLKOpticalFlow::setMaxLevel(int)
     virtual void setMaxLevel(int maxLevel) = 0;
                  ^
/usr/local/include/opencv2/cudaoptflow.hpp:169:17: note:        virtual int cv::cuda::SparsePyrLKOpticalFlow::getNumIters() const
     virtual int getNumIters() const = 0;
                 ^
/usr/local/include/opencv2/cudaoptflow.hpp:170:18: note:        virtual void cv::cuda::SparsePyrLKOpticalFlow::setNumIters(int)
     virtual void setNumIters(int iters) = 0;
                  ^
/usr/local/include/opencv2/cudaoptflow.hpp:172:18: note:        virtual bool cv::cuda::SparsePyrLKOpticalFlow::getUseInitialFlow() const
     virtual bool getUseInitialFlow() const = 0;
                  ^
/usr/local/include/opencv2/cudaoptflow.hpp:173:18: note:        virtual void cv::cuda::SparsePyrLKOpticalFlow::setUseInitialFlow(bool)
     virtual void setUseInitialFlow(bool useInitialFlow) = 0;
                  ^
error: command 'x86_64-linux-gnu-gcc' failed with exit status 1

1 个答案:

答案 0 :(得分:0)

根据错误消息,您的主要问题是命名空间声明

cdef extern from 'opencv2/cudaoptflow.hpp' namespace 'cv::cuda::SparsePyrLKOpticalFlow':
    cdef cppclass SparsePyrLKOpticalFlow(SparseOpticalFlow):
    # ...

编译器输出报告

  

错误:'cv :: cuda :: SparsePyrLKOpticalFlow :: SparsePyrLKOpticalFlow'命名构造函数,而不是类型

因此,您希望将此类的命名空间更改为'cv::cuda'

这不是错误,但如果您将pyopencv_from/to声明为

cdef object pyopencv_from(const Mat& m)
cdef bool pyopencv_to(object, Mat& m)

然后您不必转发到PyObject*

您使用抽象变量的问题来自

dereference(flow_tracker).calc(prevImgGpu, nextImgGpu, prevPtsGpu, nextPtsGpu, statusGpu)

这是因为它试图(粗略地):

SparsePyrLKOptical tmp_variable
try {
   tmp_variable = dereference(flow_tracker)
}
catch(...) {
   // handle errors
}
tmp_variable.calc(...)

由于SparsePyrLKOptical是抽象的,你只能创建它的派生类,但实际上不能直接创建它。

如果从行except +中删除T& operator* () except +,则Cython不会创建错误检查代码,因此不需要临时代码。我怀疑这是正确的,operator*实际上不会抛出异常,但如果可以,它有可能停止你的程序。