Extracting unsigned char from array of numpy.uint8

2024/9/28 15:33:27

I have code to extract a numeric value from a python sequence, and it works well in most cases, but not for a numpy array.

When I try to extract an unsigned char, I do the following

unsigned char val = boost::python::extract<unsigned char>(sequence[n]);

where sequence is any python sequence and n is the index.I get the following error:

TypeError: No registered converter was able to produce a C++ rvalue of type 
unsigned char from this Python object of type numpy.uint8

How can I successfully extract an unsigned char in C++? Do I have to write/register special converters for numpy types? I would rather use the same code that I use for other python sequences, and not have to write special code that uses the PyArrayObject*.

Answer

One can register a custom from-python converter with Boost.Python that handles conversions from NumPy array scalars, such as numpy.uint8, to C++ scalars, such as unsigned char. A custom from-python converter registration has three parts:

  • A function that checks if a PyObject is convertible. A return of NULL indicates that the PyObject cannot use the registered converter.
  • A construct function that constructs the C++ type from a PyObject. This function will only be called if converter(PyObject) does not return NULL.
  • The C++ type that will be constructed.

Extracting the value from the NumPy array scalar requires a few NumPy C API calls:

  • import_array() must be called within the initialization of an extension module that is going to use the NumPy C API. Depending on how the extension(s) are using the NumPy C API, other requirements for importing may need to occur.
  • PyArray_CheckScalar() checks if a PyObject is a NumPy array scalar.
  • PyArray_DescrFromScalar() gets the data-type-descriptor object for an array scalar. The data-type-descriptor object contains information about how to interpret the underlying bytes. For example, its type_num data member contains an enum value that corresponds to a C-type.
  • PyArray_ScalarAsCtype() can be used to extract the C-type value from a NumPy array scalar.

Here is a complete example demonstrating using a helper class, enable_numpy_scalar_converter, to register specific NumPy array scalars to their corresponding C++ types.

#include <boost/cstdint.hpp>
#include <boost/python.hpp>
#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
#include <numpy/arrayobject.h>// Mockup functions./// @brief Mockup function that will explicitly extract a uint8_t
///        from the Boost.Python object.
boost::uint8_t test_generic_uint8(boost::python::object object)
{return boost::python::extract<boost::uint8_t>(object)();
}/// @brief Mockup function that uses automatic conversions for uint8_t.
boost::uint8_t test_specific_uint8(boost::uint8_t value) { return value; }/// @brief Mokcup function that uses automatic conversions for int32_t.
boost::int32_t test_specific_int32(boost::int32_t value) { return value; }/// @brief Converter type that enables automatic conversions between NumPy
///        scalars and C++ types.
template <typename T, NPY_TYPES NumPyScalarType>
struct enable_numpy_scalar_converter
{enable_numpy_scalar_converter(){// Required NumPy call in order to use the NumPy C API within another// extension module.import_array();boost::python::converter::registry::push_back(&convertible,&construct,boost::python::type_id<T>());}static void* convertible(PyObject* object){// The object is convertible if all of the following are true:// - is a valid object.// - is a numpy array scalar.// - its descriptor type matches the type for this converter.return (object &&                                                    // ValidPyArray_CheckScalar(object) &&                               // ScalarPyArray_DescrFromScalar(object)->type_num == NumPyScalarType // Match)? object // The Python object can be converted.: NULL;}static void construct(PyObject* object,boost::python::converter::rvalue_from_python_stage1_data* data){// Obtain a handle to the memory block that the converter has allocated// for the C++ type.namespace python = boost::python;typedef python::converter::rvalue_from_python_storage<T> storage_type;void* storage = reinterpret_cast<storage_type*>(data)->storage.bytes;// Extract the array scalar type directly into the storage.PyArray_ScalarAsCtype(object, storage);// Set convertible to indicate success. data->convertible = storage;}
};BOOST_PYTHON_MODULE(example)
{namespace python = boost::python;// Enable numpy scalar conversions.enable_numpy_scalar_converter<boost::uint8_t, NPY_UBYTE>();enable_numpy_scalar_converter<boost::int32_t, NPY_INT>();// Expose test functions.python::def("test_generic_uint8",  &test_generic_uint8);python::def("test_specific_uint8", &test_specific_uint8);python::def("test_specific_int32", &test_specific_int32);
}

Interactive usage:

>>> import numpy
>>> import example
>>> assert(42 == example.test_generic_uint8(42))
>>> assert(42 == example.test_generic_uint8(numpy.uint8(42)))
>>> assert(42 == example.test_specific_uint8(42))
>>> assert(42 == example.test_specific_uint8(numpy.uint8(42)))
>>> assert(42 == example.test_specific_int32(numpy.int32(42)))
>>> example.test_specific_int32(numpy.int8(42))
Traceback (most recent call last):File "<stdin>", line 1, in <module>
Boost.Python.ArgumentError: Python argument types inexample.test_specific_int32(numpy.int8)
did not match C++ signature:test_specific_int32(int)
>>> example.test_generic_uint8(numpy.int8(42))
Traceback (most recent call last):File "<stdin>", line 1, in <module>
TypeError: No registered converter was able to produce a C++ rvalue of typeunsigned char from this Python object of type numpy.int8

A few things to note from the interactive usage:

  • Boost.Python was able to extract boost::uint8_t from both numpy.uint8 and int Python objects.
  • The enable_numpy_scalar_converter does not support promotions. For instance, it should be safe for test_specific_int32() to accept a numpy.int8 object that is promoted to a larger scalar type, such as int. If one wishes to perform promotions:
    • convertible() will need to check for compatible NPY_TYPES
    • construct() should use PyArray_CastScalarToCtype() to cast the extracted array scalar value to the desired C++ type.
https://en.xdnf.cn/q/71328.html

Related Q&A

How to hold keys down with pynput?

Im using pynput and I would like to be able to hold keys down, specifically wasd but when I try and run this code it only presses the key and doesnt hold it for 2 seconds. If anyone knows what Im doing…

How well does your language support unicode in practice?

Im looking into new languages, kind of craving for one where I no longer need to worry about charset problems amongst inordinate amounts of other niggles I have with PHP for a new project.I tend to fin…

analogy to scipy.interpolate.griddata?

I want to interpolate a given 3D point cloud:I had a look at scipy.interpolate.griddata and the result is exactly what I need, but as I understand, I need to input "griddata" which means some…

Mongodb TTL expires documents early

I am trying insert a document into a Mongo database and have it automatically expire itself after a predetermine time. So far, my document get inserted but always get deleted from the database from 0 -…

sqlalchemy, hybrid property case statement

This is the query Im trying to produce through sqlalchemySELECT "order".id AS "id", "order".created_at AS "created_at", "order".updated_at AS "u…

Whats the newest way to develop gnome panel applets (using python)

Today Ive switched to GNOME (from XFCE) and found some of the cool stuff missing and I would like to (try to) do them on my own. I tried to find information on how to develop Gnome applets (items you p…

How to install opencv-python in python 3.8

Im having problem during the installation of opencv-python in pycharm. After opening pycharm I click on settings and then project interpreter, I click on + and search for the correct module, I started …

python augmented assignment for boolean operators

Does Python have augmented assignment statements corresponding to its boolean operators?For example I can write this:x = x + 1or this:x += 1Is there something I can write in place of this:x = x and yT…

Change a pandas DataFrame column value based on another column value

I have a dataframe with two columns each of which represents an organism. They are called ORG1 and ORG2 I want to move the values of ORG2 into ORG1 for the corresponding index value.So, if ORG1 is A a…

How to lock a sqlite3 database in Python?

Is there a way to explicitly acquire a lock on a sqlite3 database in Python?