Last active
April 20, 2023 09:33
-
-
Save eneriz-daniel/3e6ff7e855795d8d463f83771f252db9 to your computer and use it in GitHub Desktop.
ndarrays2Carray.py - Exporting Numpy ndarrays to C arrays declared inside C files
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
npy2Carray.py - A np.ndarray to C array converter | |
This script converts a numpy array to a C array and saves it in a file. | |
Author: Daniel Enériz | |
Version: 1.0 | |
License: GPL-3.0 | |
Example of use: | |
If you declare the following array in Python: | |
>>> import numpy as np | |
>>> A = np.arange(24).reshape((2, 3, 4)) | |
>>> A | |
array([[[ 0, 1, 2, 3], | |
[ 4, 5, 6, 7], | |
[ 8, 9, 10, 11]], | |
[[12, 13, 14, 15], | |
[16, 17, 18, 19], | |
[20, 21, 22, 23]]]) | |
You can save it as a C array in a file with the following code: | |
>>> npy2Carray(A, 'A', mode='w') | |
There will be a file named 'arrays.c' in the current directory with the following content: | |
const int A[2][3][4] = { | |
{ | |
{0, 1, 2, 3}, | |
{4, 5, 6, 7}, | |
{8, 9, 10, 11} | |
}, | |
{ | |
{12, 13, 14, 15}, | |
{16, 17, 18, 19}, | |
{20, 21, 22, 23} | |
} | |
}; | |
""" | |
import numpy as np | |
def _write_array_values(array : np.ndarray, f, fmt : str = '{}', ident : int = 0): | |
"""Writes the values of a numpy array to a file. This auxiliary function is | |
used by npy2Carray. Recursively calls itself to write the | |
values of multidimensional arrays. | |
Args: | |
array (np.ndarray): Numpy array to write. | |
f (file): File to write to. | |
fmt (str, optional): Format to write the values. Defaults to '{}'. | |
ident (int, optional): Number of identation levels. Defaults to 0. | |
""" | |
# If shape is 1D, write the values in a single line | |
if len(array.shape) == 1: | |
# Indent the line | |
for i in range(ident): | |
f.write(' ') | |
# Write the opening brace | |
f.write('{') | |
for i in range(array.shape[0]): | |
f.write(fmt.format(array[i])) | |
if i < array.shape[0] - 1: | |
f.write(', ') | |
else: | |
f.write('}') | |
# If shape has more than 1 dimension, call this function a number of times | |
# equal to the first dimension | |
else: | |
for i in range(ident): | |
f.write(' ') | |
f.write('{\n') | |
for i in range(array.shape[0]): | |
_write_array_values(array[i], f, fmt, ident + 4) | |
if i < array.shape[0] - 1: | |
f.write(',\n') | |
else: | |
f.write('\n') | |
for i in range(ident): | |
f.write(' ') | |
f.write('}') | |
default_header = "// This file was automatically generated by npy2Carrays.py,\n" \ | |
+ "// a script by Daniel Enériz that creates C arrays from numpy\n" \ | |
+ "// arrays and saves them on a file." \ | |
# Save data as a C header file defining a float array | |
def npy2Carray(array : np.ndarray, name : 'str', | |
file_path : str = 'arrays.c', mode : str = 'a', fmt : str = '{}', | |
ifndef : bool = True, header : str = default_header): | |
"""Saves a numpy array as a C array in a file | |
Args: | |
array (np.ndarray): Numpy array to save | |
name (str): Name of the array. Must be a valid C identifier. | |
file_path (str, optional): Path to the file. Defaults to 'arrays.c'. | |
mode (str, optional): Mode to open the file, must be 'w' or 'a'. | |
Defaults to 'a'. | |
fmt (str, optional): Format to write the values. Defaults to '{}'. | |
Must be a valid format for the type of the array. | |
ifndef (bool, optional): If True, the file will be wrapped in an #ifndef | |
statement to avoid multiple definitions. Defaults to True. | |
header (str, optional): Header to add to the file. Defaults to | |
`default_header`. | |
""" | |
# Check if array is a np.ndarray | |
if not isinstance(array, np.ndarray): | |
raise TypeError('array must be a numpy array') | |
# Check if name is a string and is a valid C identifier | |
if not isinstance(name, str): | |
raise TypeError('name must be a string') | |
# Check if file_path is a string and is a valid file | |
if not isinstance(file_path, str): | |
raise TypeError('file_path must be a string') | |
# Check if mode is a string and is valid | |
if not isinstance(mode, str): | |
raise TypeError('mode must be a string') | |
if mode not in ['w', 'a']: | |
raise ValueError('mode must be \'w\' or \'a\'') | |
# Open the file | |
f = open(file_path, mode) | |
# Determine the type of the array from the dtype | |
if 'float' in array.dtype.name: | |
c_type = 'float' | |
elif 'int' in array.dtype.name: | |
c_type = 'int' | |
else: | |
raise TypeError('array must be an array of floats or integers') | |
# Add the header | |
f.write(header) | |
# Add the #ifndef statement if required | |
if ifndef: | |
f.write('\n\n#ifndef {}\n#define {}\n'.format(name.upper(), name.upper())) | |
# Write the array declaration | |
f.write('\nconst {} {}'.format(c_type, name)) | |
# Iterate over the dimensions of the array to add the shape | |
for dim in array.shape: | |
f.write('[{}]'.format(dim)) | |
# Write the equal sign and the opening brace | |
f.write(' = ') | |
# Iterate over the dimensions of the array and start writing the values | |
_write_array_values(array, f, '{}') | |
# Add a semicolon and a new line | |
f.write(';\n\n') | |
# Write the number of dimensions of the array | |
f.write('const unsigned int {}_ndim = {};\n'.format(name, array.ndim)) | |
# Write the shape of the array in ndim unsigned ints | |
for i, dim in enumerate(array.shape): | |
f.write('const unsigned int {}_shape_{} = {};\n'.format(name, i, dim)) | |
# Add the #endif statement if required | |
if ifndef: | |
f.write('\n#endif') |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment