|
| 1 | +#define PY_SSIZE_T_CLEAN |
| 2 | +#include <Python.h> |
| 3 | + |
| 4 | +#include <iostream> |
| 5 | +#include <stdexcept> |
| 6 | +#include <vector> |
| 7 | + |
| 8 | +#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION |
| 9 | +#include "numpy/arrayobject.h" |
| 10 | + |
| 11 | +#include <opencv2/opencv.hpp> |
| 12 | + |
| 13 | +extern "C"; |
| 14 | + |
| 15 | +// Simple addition test function (return = in1 + in2) |
| 16 | +static PyObject* test_func(PyObject* self, PyObject* args) { |
| 17 | + int input1, input2; |
| 18 | + if (!PyArg_ParseTuple(args, "ii", &input1, &input2)) { // Arguments are 2 integer values ("ii") |
| 19 | + return nullptr; |
| 20 | + } |
| 21 | + PyObject *return_value = PyLong_FromLong(input1 + input2); // Create object to return |
| 22 | + return return_value; |
| 23 | +} |
| 24 | + |
| 25 | +// Simple image processing function (Inverse image color) |
| 26 | +static PyObject* test_image_processing(PyObject* self, PyObject* args) { |
| 27 | + PyArrayObject* input_image; |
| 28 | + PyObject* output_image; |
| 29 | + if(!PyArg_ParseTuple(args, "O!", &PyArray_Type, &input_image)) { // Argument is an Numpy array object |
| 30 | + return nullptr; |
| 31 | + } |
| 32 | + output_image = PyArray_NewLikeArray(input_image, NPY_ANYORDER, NULL, 0); // Create object to return |
| 33 | + |
| 34 | + int ndim = PyArray_NDIM(input_image); // Number of dimensions of the input Numpy array |
| 35 | + npy_intp *shape; |
| 36 | + shape = PyArray_SHAPE(input_image); // Shape of the input Numpy array (npy_intp[]) |
| 37 | + |
| 38 | + // Obtain data buffer pointers |
| 39 | + char* in_buf = static_cast<char*>(PyArray_DATA(input_image)); // PyArray_DATA() will return void* |
| 40 | + char* out_buf = static_cast<char*>(PyArray_DATA(reinterpret_cast<PyArrayObject*>(output_image))); |
| 41 | + |
| 42 | + // Image processing |
| 43 | + size_t C = shape[2]; |
| 44 | + size_t W = shape[1]; |
| 45 | + size_t H = shape[0]; |
| 46 | + for(size_t y=0; y<H; y++) { |
| 47 | + for(size_t x=0; x<W; x++){ |
| 48 | + for(size_t c=0; c<C; c++) { |
| 49 | + out_buf[y*W*C + x*C + c] = 255 - in_buf[y*W*C + x*C + c]; |
| 50 | + } |
| 51 | + } |
| 52 | + } |
| 53 | + |
| 54 | + return output_image; // Returned object must exist after exiting of this C++ function |
| 55 | +} |
| 56 | + |
| 57 | +// Simple image processing function using OpenCV (Canny edge detection) |
| 58 | +static PyObject* test_image_processing_OCV(PyObject* self, PyObject* args) { |
| 59 | + PyArrayObject* input_image; |
| 60 | + int th1, th2; // Threshold value for Canny edge detection |
| 61 | + if(!PyArg_ParseTuple(args, "O!ii", &PyArray_Type, &input_image, &th1, &th2)) { // 3 arguments (Numpy obj, int, int) |
| 62 | + return nullptr; |
| 63 | + } |
| 64 | + |
| 65 | + shape = PyArray_SHAPE(input_image); |
| 66 | + |
| 67 | + char* in_buf = static_cast<char*>(PyArray_DATA(input_image)); // PyArray_DATA() will return void* |
| 68 | + |
| 69 | + // Image processing with OpenCV |
| 70 | + cv::Mat in_mat(shape[0] /*Width(rows)*/, shape[1] /*Height(cols)*/, CV_8UC3, in_buf); // Input cv::Mat generated from input Numpy object |
| 71 | + cv::Mat out_mat; // Mat object to store final image |
| 72 | + cv::Mat channels[3]; // Mat object for channel extraction |
| 73 | + cv::cvtColor(in_mat, in_mat, cv::COLOR_BGR2GRAY); // Color -> Gray scale |
| 74 | + cv::split(in_mat, channels); // Channel extraction. packed BGR -> [B, G, R] |
| 75 | + cv::Canny(channels[0], out_mat, th1, th2); // Canny edge detection |
| 76 | + |
| 77 | + npy_intp out_shape[] = { out_mat.rows, out_mat.cols, out_mat.channels() }; |
| 78 | + PyObject* result = PyArray_SimpleNew(3, out_shape, NPY_UINT8); // Create a Numpy object to return |
| 79 | + char* out_buf = static_cast<char*>(PyArray_DATA(reinterpret_cast<PyArrayObject*>(result))); // Obtain data buffer pointer of the final image |
| 80 | + memcpy(out_buf, out_mat.data, out_shape[0] * out_shape[1] * out_shape[2]); // Copy data buffer |
| 81 | + |
| 82 | + return result; // Return created Numpy object |
| 83 | +} |
| 84 | + |
| 85 | +// Numpy array attribute extraction test function |
| 86 | +static PyObject* npy_array_test(PyObject* self, PyObject* args) { |
| 87 | + PyArrayObject* input_image; |
| 88 | + |
| 89 | + if(!PyArg_ParseTuple(args, "O!", &PyArray_Type, &input_image)) { // Argument is a Numpy object |
| 90 | + return nullptr; |
| 91 | + } |
| 92 | + |
| 93 | + size_t ndims = PyArray_NDIM(input_image); // Number of dimensions |
| 94 | + std::cout << "#Dims: " << PyArray_NDIM(input_image) << std::endl; |
| 95 | + |
| 96 | + npy_intp* shape = PyArray_SHAPE(input_image); // Shape |
| 97 | + for(size_t i=0; i<ndims; i++) { |
| 98 | + std::cout << "Dim" << i << ": " << shape[i] << std::endl; |
| 99 | + } |
| 100 | + std::cout << "NpyType: " << PyArray_TYPE(input_image) << std::endl; // Numpy data type |
| 101 | + std::cout << "ItemSize: " << PyArray_ITEMSIZE(input_image) << std::endl; // Size of item (element) |
| 102 | + std::cout << "TotalSize: " << PyArray_SIZE(input_image) << std::endl; // Total size (in size of item) |
| 103 | + std::cout << "TotalBytes: " << PyArray_NBYTES(input_image) << std::endl; // Total bytes |
| 104 | + uint8_t* buffer = static_cast<uint8_t*>(PyArray_DATA(input_image)); // PyArray_DATA() will return void* |
| 105 | + |
| 106 | + // Show first 10 items of the Numpy array |
| 107 | + int bytes = PyArray_NBYTES(input_image); |
| 108 | + int disp_size = bytes<10 ? bytes : 10; |
| 109 | + for(size_t i=0; i<disp_size; i++) { |
| 110 | + std::cout << (int)buffer[i] << " "; |
| 111 | + } |
| 112 | + std::cout << std::endl; |
| 113 | + |
| 114 | + return PyLong_FromLong(0); |
| 115 | +} |
| 116 | + |
| 117 | +// Function definition table to export to Python |
| 118 | +PyMethodDef method_table[] = { |
| 119 | + {"test_func", static_cast<PyCFunction>(test_func), METH_VARARGS, "test method function of test module"}, |
| 120 | + {"test_image_processing", static_cast<PyCFunction>(test_image_processing), METH_VARARGS, "test image processing function"}, |
| 121 | + {"test_image_processing_OCV", static_cast<PyCFunction>(test_image_processing_OCV), METH_VARARGS, "test image processing function"}, |
| 122 | + {"npy_array_test", static_cast<PyCFunction>(npy_array_test), METH_VARARGS, "test numpy array handling test"}, |
| 123 | + {NULL, NULL, 0, NULL} |
| 124 | +}; |
| 125 | + |
| 126 | +// Module definition table |
| 127 | +PyModuleDef test_module = { |
| 128 | + PyModuleDef_HEAD_INIT, |
| 129 | + "python_cpp_module", |
| 130 | + "test module", |
| 131 | + -1, |
| 132 | + method_table |
| 133 | +}; |
| 134 | + |
| 135 | +// Initialize and register module function |
| 136 | +// Function name must be 'PyInit_'+module name |
| 137 | +// This function must be the only *non-static* function in the source code |
| 138 | +PyMODINIT_FUNC PyInit_python_cpp_module(void) { |
| 139 | + import_array(); // Required to receive Numpy object as arguments |
| 140 | + if (PyErr_Occurred()) { |
| 141 | + return nullptr; |
| 142 | + } |
| 143 | + return PyModule_Create(&test_module); |
| 144 | +} |
0 commit comments