STM32 export#
Install requirements#
Ensure that the Aidge modules are properly installed in the current environment. If it is the case, the following setup steps can be skipped.
Note: This notebook is not intended to be executed on Binder, as it relies on Docker, which cannot be run inside the Binder environment.
[ ]:
%pip install aidge-core \
aidge-backend-cpu \
aidge-export-arm-cortexm \
aidge-onnx
%pip install requests \
numpy \
ipywidgets \
ipycanvas
Download the model#
[ ]:
import aidge_core
file_url = "https://huggingface.co/EclipseAidge/LeNet/resolve/main/lenet_mnist.onnx?download=true"
file_path = "lenet_mnist.onnx"
aidge_core.utils.download_file(file_path, file_url)
Load the model in Aidge and manipulate it#
[ ]:
import aidge_backend_cpu
import aidge_onnx
import aidge_export_arm_cortexm
[ ]:
model = aidge_onnx.load_onnx("lenet_mnist.onnx")
[ ]:
# Remove Flatten node, useless in the CPP export
aidge_core.remove_flatten(model)
# Configuration for the model + forward dimensions
model.compile("cpu", aidge_core.dtype.float32, dims=[[1, 1, 28, 28]])
[ ]:
# Generate scheduling of the model
scheduler = aidge_core.SequentialScheduler(model)
scheduler.generate_scheduling()
Export the model#
[ ]:
print(aidge_export_arm_cortexm.ExportLibAidgeARM._name)
[ ]:
# Adapt the graph to the NHWC format
model.set_dataformat(aidge_core.dformat.nhwc)
model.set_backend(aidge_export_arm_cortexm.ExportLibAidgeARM._name)
aidge_core.adapt_to_backend(model)
aidge_core.adapt_fc_params_format(model)
model.forward_dims()
scheduler.reset_scheduling()
scheduler.generate_scheduling()
[ ]:
aidge_core.export_utils.scheduler_export(
scheduler,
"lenet_export_fp32",
aidge_export_arm_cortexm.ExportLibAidgeARM,
memory_manager=aidge_core.mem_info.generate_optimized_memory_info,
memory_manager_args={"stats_folder": "lenet_export_fp32/stats", "wrapping": False }
)
Now that we have generated the dnn folder, we can generate the utils files that will help with compilation:
[ ]:
aidge_export_arm_cortexm.gen_board_files("lenet_export_fp32", "stm32h7")
Draw your own number#
[ ]:
from ipywidgets import HBox, VBox, Button, Layout
from ipycanvas import RoughCanvas, hold_canvas
img_name = "my_number.png"
canvas = RoughCanvas(width=28, height=28, sync_image_data=True)
button_gen = Button(description="Generate PNG")
button_clear = Button(description="Clear")
drawing = False
position = None
shape = []
def on_erase_button_clicked(b):
canvas.clear()
def on_generate_button_clicked(b):
try:
canvas.to_file(img_name)
print(f"Image generated to {img_name} !")
except:
print("Draw a number before generating the image.")
button_clear.on_click(on_erase_button_clicked)
button_gen.on_click(on_generate_button_clicked)
def on_mouse_down(x, y):
global drawing
global position
global shape
drawing = True
position = (x, y)
shape = [position]
def on_mouse_move(x, y):
global drawing
global position
global shape
if not drawing:
return
with hold_canvas():
canvas.stroke_line(position[0], position[1], x, y)
position = (x, y)
shape.append(position)
def on_mouse_up(x, y):
global drawing
global position
global shape
drawing = False
with hold_canvas():
canvas.stroke_line(position[0], position[1], x, y)
shape = []
canvas.on_mouse_down(on_mouse_down)
canvas.on_mouse_move(on_mouse_move)
canvas.on_mouse_up(on_mouse_up)
canvas.stroke_style = "#000000"
VBox((canvas, HBox((button_gen, button_clear))),
layout=Layout(height='auto', width="300px"))
Generate inputs for testing the model from your drawing#
[ ]:
try:
number_np = canvas.get_image_data()
# We got a numpy array with the shape of (28,28,4)
# Transform it to (28,28)
x = number_np[:, :, 3].astype("float32")
# Convert from [0, 255] to [0, 1] and export it
aidge_core.export_utils.generate_input_file(export_folder="lenet_export_fp32", array_name="feature_feature_0_Conv_input_0", tensor=aidge_core.Tensor(x / 255))
except:
print("Please draw a number in the previous cell before running this one.")
Compile the export and test it#
[ ]:
!cd lenet_export_fp32 && make build_image_docker && make build_export_docker