Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pybind11 for C++ code with inner struct created via static factory method

I have my own C++ that I'm trying to generate python binding using Pybind11:

auto markerParams = MarkerDetector::Params::create(MarkerType::chessboard);
markerDetector.detect(image, markerParams);

I have problem of generating binding for MarkerDetector::Params struct since it's an inner struct constructed with a factory method that takes enum as parameter:

enum MarkerType { chessboard, tag };

typedef std::vector<cv::Point> ContourType;
typedef std::vector<cv::Point2d> ContourTyped;

//              contour full, contour approximate, area, corners
typedef std::tuple<ContourType, ContourType, double, ContourTyped> MarkerDescriptor;


class MarkerDetector {
    public:
        std::vector<MarkerDescriptor> detect(Mat image, const Params params);

        struct Params {
            int rows, cols;
            ColorRange borderColor;
            ShapeDetector::Params borderShape;
            cv::Size borderSize;
            cv::Size Size;


            static Params create(MarkerType markerType) {
                static Params markerTypes[] = {
                    { 3, 6, ColorRange::GREEN, ShapeDetector::Params::RECTANGLE, cv::Size(30,30), cv::Size(140, 140) }
                };
                return markerTypes[markerType];
            }
        };
    };

Does any one know how to handle this more advanced case?

like image 349
pzo Avatar asked Jan 21 '18 22:01

pzo


1 Answers

I've got a basic implementation of your code to run, using the inner struct as per your design. For brevity I've only included the relevant details for MarkerDetector and Params but it should match what you've done.

The c++ code:

#include <pybind11/pybind11.h>

namespace py = pybind11;

enum MarkerType { chessboard, tag };


class MarkerDetector {
    public:
    MarkerDetector() { }
    struct Params {
        int rows;
        int cols;
        static Params create(MarkerType markerType) {
            static Params markerTypes[] = {
                    { 1, 2 },
                    { 3, 4 }
            };
            return markerTypes[markerType];
        }
    };

};


PYBIND11_MODULE(example, m) {
    m.doc() = "pybind11 example"; // optional module docstring

    py::enum_<MarkerType>(m, "MarkerType")
         .value("chessboard", MarkerType::chessboard)
         .value("tag",        MarkerType::tag)
         .export_values();

    py::class_<MarkerDetector>(m, "MarkerDetector")
            .def(py::init<>())
            ;

    py::class_<MarkerDetector::Params>(m, "MarkerDetectorParams")
            .def(py::init<>())
            .def_readwrite("rows", &MarkerDetector::Params::rows)
            .def_readwrite("cols", &MarkerDetector::Params::cols)
            .def("create", (MarkerDetector::Params (*)(MarkerType)) &MarkerDetector::Params::create)
            ;
}

(If you're interested, the command line to compile the above:)

c++ -O3 -Wall -shared -std=c++11 -fPIC `python3 -m pybind11 --includes` example.cpp -o example`python3-config --extension-suffix` -L /usr/local/Cellar/python3/3.6.4/Frameworks/Python.framework/Versions/3.6/lib/ -lpython3.6

The python code:

import sys
sys.path.append('/Volumes/Programs/workspaces/workspace 1/Project CPP 2')

import example

mtype = example.MarkerType.chessboard
print (mtype)

x = example.MarkerDetectorParams.create(example.MarkerType.chessboard)
print (x)
print (x.rows)
print (x.cols)

y = example.MarkerDetectorParams.create(example.MarkerType.tag)
print (y)
print (y.rows)
print (y.cols)

This gives the following output, which appears correct as per the design:

MarkerType.chessboard
<example.MarkerDetectorParams object at 0x1043e35a8>
1
2
<example.MarkerDetectorParams object at 0x1043e3768>
3
4

I hope this gives you something to work from. Regards, AS

like image 102
AS Mackay Avatar answered Sep 21 '22 15:09

AS Mackay