Skip to content

Instantly share code, notes, and snippets.

@eneriz-daniel
Last active April 20, 2023 09:33
Show Gist options
  • Save eneriz-daniel/3e6ff7e855795d8d463f83771f252db9 to your computer and use it in GitHub Desktop.
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
"""
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