Skip to content

Ask opencv mat numpy array conversion  #538

Closed
@edmBernard

Description

@edmBernard

I try to add an Openv Numpy interface with type_caster
I start from @jampekka comments here

I ended to this :

#include <pybind11/numpy.h>
#include <opencv2/core/core.hpp>
#include <stdexcept>

namespace pybind11 { namespace detail {

template <> struct type_caster<cv::Mat> {
    public:
        /**
         * This macro establishes the name 'inty' in
         * function signatures and declares a local variable
         * 'value' of type inty
         */
        PYBIND11_TYPE_CASTER(cv::Mat, _("numpy.ndarray"));

        /**
         * Conversion part 1 (Python->C++): convert a PyObject into a inty
         * instance or return false upon failure. The second argument
         * indicates whether implicit conversions should be applied.
         */
        bool load(handle src, bool) 
        {
            array b(src, true);
            buffer_info info = b.request();
            int ndims = info.ndim;

            decltype(CV_32F) dtype;
            size_t elemsize; 
            if (info.format == format_descriptor<float>::format()) { 
                dtype = CV_32F;
                elemsize = sizeof(float);
            } else if (info.format == format_descriptor<double>::format()) {
                dtype = CV_64F;
                elemsize = sizeof(double);
            } else if (info.format == format_descriptor<unsigned char>::format()) { 
                dtype = CV_8U;
                elemsize = sizeof(unsigned char);
            } else if (info.format == format_descriptor<unsigned char>::format()) { 
                if (ndims == 3) {
                    dtype = CV_8UC3;
                } else {
                    dtype = CV_8U;
                }
                elemsize = sizeof(unsigned char);
            } else { 
                throw std::logic_error("Unsupported type");
                return false;
            }

            std::vector<int> shape = {info.shape[0], info.shape[1]};
            std::vector<size_t> strides = {(size_t) elemsize * info.strides[0], (size_t) elemsize * info.strides[1], (size_t) elemsize * info.strides[2]};

            value = cv::Mat(2,
                    &shape[0],
                    dtype,
                    info.ptr,
                    &strides[0]);
            return true;
        }

        /**
         * Conversion part 2 (C++ -> Python): convert an inty instance into
         * a Python object. The second and third arguments are used to
         * indicate the return value policy and parent object (for
         * ``return_value_policy::reference_internal``) and are generally
         * ignored by implicit casters.
         */
        static handle cast(const cv::Mat &m, return_value_policy, handle defval) 
        {
            std::cout << "m.cols : " << m.cols << std::endl;
            std::cout << "m.rows : " << m.rows << std::endl;
            std::string format = format_descriptor<unsigned char>::format();
            size_t elemsize = sizeof(unsigned char);
            int dim; 
            switch(m.type()) {
                case CV_8U: 
                    format = format_descriptor<unsigned char>::format();
                    elemsize = sizeof(unsigned char);
                    dim = 2;
                    break; 
                case CV_8UC3: 
                    format = format_descriptor<unsigned char>::format();
                    elemsize = sizeof(unsigned char);
                    dim = 3;
                    break; 
                case CV_32F: 
                    format = format_descriptor<float>::format(); 
                    elemsize = sizeof(float);
                    dim = 2;
                    break;
                case CV_64F: 
                    format = format_descriptor<double>::format();
                    elemsize = sizeof(double);
                    dim = 2;
                    break;
                default: 
                    throw std::logic_error("Unsupported type");
            }

            std::vector<size_t> bufferdim;
            std::vector<size_t> strides;
            if (dim == 2) {
                bufferdim = {(size_t) m.rows, (size_t) m.cols};
                strides = {elemsize * (size_t) m.cols, elemsize};
            } else if (dim == 3) {
                bufferdim = {(size_t) m.rows, (size_t) m.cols, (size_t) 3};
                strides = {(size_t) elemsize * m.cols * 3, (size_t) elemsize * 3, (size_t) elemsize};
            }
            return array(buffer_info(
                m.data,         /* Pointer to buffer */
                elemsize,       /* Size of one scalar */
                format,         /* Python struct-style format descriptor */
                dim,            /* Number of dimensions */
                bufferdim,      /* Buffer dimensions */
                strides         /* Strides (in bytes) for each index */
                )).release();
        }

    };
}} // namespace pybind11::detail

The cast part work, i'm able to transfert cv::Mat from C++ to numpy array in python.
but the load part is completely broken there is buffer issues. it seem strides were bad but I don't find how to fixe them.
have you an idea to fixe that ? thx

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions