Create a custom .hef for stereo depht

Hi everyone,

I’m looking for a stereo depth model that can run in real-time (100–200 ms) on a Raspberry Pi 5 with a Hailo-8*accelerator.

I tried the stereo model from the hailo_model_zoo, but inference takes about 800 ms which is too slow for my use case.

So I started testing other stereo depth models like FastACVNet, CREStereo, and HitNet using the following conversion code:

import os
import onnx
from onnxsim import simplify
import numpy as np
from hailo_sdk_client import ClientRunner
from PIL import Image

def parse_stereo_onnx(onnx_path: str, left_dir: str, right_dir: str, net_name: str, hw_arch: str, target_size=(640, 480)):
    # -------------------------------
    # Step 1. Simplify the ONNX model
    # -------------------------------
    model = onnx.load(onnx_path)
    model_simp, check = simplify(model)
    if not check:
        raise RuntimeError("Simplified ONNX model validation failed.")
        
    simplified_onnx_path = os.path.splitext(onnx_path)[0] + "_simplified.onnx"
    onnx.save(model_simp, simplified_onnx_path)
    print(f"[✓] Simplified ONNX model saved to: {simplified_onnx_path}")

    # -----------------------------------------------------
    # Step 2. Translate the simplified ONNX model to Hailo format
    # -----------------------------------------------------
    runner = ClientRunner(hw_arch=hw_arch)

    # Précise les noms des entrées et sorties comme dans le YAML
    input_names = ["left_image", "right_image"]
    output_names = ["output"]

    hn, params = runner.translate_onnx_model(simplified_onnx_path, net_name,
                                             input_names=input_names,
                                             outputs_names=output_names)
    print("[✓] Model translation to Hailo format completed.")

    # -----------------------------------------------------
    # Step 3. Load stereo calibration dataset
    # -----------------------------------------------------
    calib_dataset = load_stereo_calib_dataset(left_dir, right_dir, target_size)
    print("[✓] Calibration dataset prepared.")

    # -----------------------------------------------------
    # Step 4. Optimize the model (quantization)
    # -----------------------------------------------------
    runner.optimize(calib_dataset)
    print("[✓] Model quantization complete.")

    # -----------------------------------------------------
    # Step 5. Save HAR file
    # -----------------------------------------------------
    har_file = f"{net_name}_quantized.har"
    runner.save_har(har_file)
    print(f"[✓] HAR file saved to: {har_file}")

def load_stereo_calib_dataset(left_dir, right_dir, target_size=(640, 480)):
    """
    Charge et redimensionne les images gauche/droite pour la calibration.
    Retourne un dict: { "left_image": [...], "right_image": [...] }
    """
    left_imgs = sorted([f for f in os.listdir(left_dir) if f.lower().endswith(('.jpg', '.png'))])
    right_imgs = sorted([f for f in os.listdir(right_dir) if f.lower().endswith(('.jpg', '.png'))])

    assert len(left_imgs) == len(right_imgs), "Nombre d'images gauche/droite différent"
    images_left = []
    images_right = []

    for l_img, r_img in zip(left_imgs, right_imgs):
        l_path = os.path.join(left_dir, l_img)
        r_path = os.path.join(right_dir, r_img)

        left = Image.open(l_path).convert("RGB").resize(target_size, Image.BILINEAR)
        right = Image.open(r_path).convert("RGB").resize(target_size, Image.BILINEAR)

        images_left.append(np.array(left, dtype=np.float32))
        images_right.append(np.array(right, dtype=np.float32))

    return {
        "left_image": np.stack(images_left),
        "right_image": np.stack(images_right)
    }

def main():
    onnx_path = "./model/crestereo_combined_iter10_480x640.onnx"
    calib_left = "./calibration/left"
    calib_right = "./calibration/right"
    net_name = "stereo_depth_net"
    hw_arch = "hailo8l"

    parse_stereo_onnx(onnx_path, calib_left, calib_right, net_name, hw_arch)

if __name__ == "__main__":
    main()

and i have some issue like

[warning] This model has non-default (reflective/edge) padding layers which are not supported currently, and were replaced with zero padding.

or

ValueError: axes don't match array

This seems to occur during model translation. Has anyone encountered and resolved this when working with stereo ONNX models on Hailo?

And when i execute in the dockers

hailo tutorial

i get

[C 2025-06-11 10:26:16.209 ServerApp] Running as root is not recommended. Use --allow-root to bypass.

After this, the Jupyter server crashes. Is there a clean way to launch the tutorial environment inside Docker?

Any suggestions, tips, or shared experiences would be greatly appreciated!

Thanks in advance!

Just looking at the repositories, I can immediately spot some of the issues.

This is the reason why you are getting a warning. You either accept the warning or retrain Fast-ACV with zero padding instead.

Same padding issue with CREStereo. But also, you will not be able to compile CREStereo for at least one reason: sin/cos is not supported. CREStereo/nets/attention/position_encoding.py at master · megvii-research/CREStereo · GitHub

HITNet might work. I don’t see any glaring issues from the PyTorch implementation, so long as the grid_sample is always the same. PyTorch-HITNet-Hierarchical-Iterative-Tile-Refinement-Network-for-Real-time-Stereo-Matching/models/submodules.py at main · MJITG/PyTorch-HITNet-Hierarchical-Iterative-Tile-Refinement-Network-for-Real-time-Stereo-Matching · GitHub That doesn’t mean the onnx export will be correct though. To enforce no GridSample onnx op, you must export with opset 15 or lower.

Thank you for your detailed response.
I now understand that certain models can’t be compiled to a .hef format. Following your advice, I selected the FastNet model (opset 11, without grid sampling) and tested it using the same code as before.
However, I encountered some errors during execution.

Traceback (most recent call last):
  File "/home/hailo/shared/.venv/lib/python3.10/site-packages/hailo_sdk_client/sdk_backend/parser/parser.py", line 239, in translate_onnx_model
    parsing_results = self._parse_onnx_model_to_hn(
  File "/home/hailo/shared/.venv/lib/python3.10/site-packages/hailo_sdk_client/sdk_backend/parser/parser.py", line 320, in _parse_onnx_model_to_hn
    return self.parse_model_to_hn(
  File "/home/hailo/shared/.venv/lib/python3.10/site-packages/hailo_sdk_client/sdk_backend/parser/parser.py", line 371, in parse_model_to_hn
    fuser = HailoNNFuser(converter.convert_model(), net_name, converter.end_node_names)
  File "/home/hailo/shared/.venv/lib/python3.10/site-packages/hailo_sdk_client/model_translator/translator.py", line 83, in convert_model
    self._create_layers()
  File "/home/hailo/shared/.venv/lib/python3.10/site-packages/hailo_sdk_client/model_translator/edge_nn_translator.py", line 39, in _create_layers
    self._add_direct_layers()
  File "/home/hailo/shared/.venv/lib/python3.10/site-packages/hailo_sdk_client/model_translator/edge_nn_translator.py", line 121, in _add_direct_layers
    self._layer_callback_from_vertex(vertex)
  File "/home/hailo/shared/.venv/lib/python3.10/site-packages/hailo_sdk_client/model_translator/onnx_translator/onnx_translator.py", line 393, in _layer_callback_from_vertex
    consumed_vertices = self._create_convolutional_layer(vertex)
  File "/home/hailo/shared/.venv/lib/python3.10/site-packages/hailo_sdk_client/model_translator/onnx_translator/onnx_translator.py", line 806, in _create_convolutional_layer
    kernel = np.transpose(vertex_kernel, [2, 3, 0, 1])  # [k_w, k_h, f_in, f_out] (onnx repr)
  File "<__array_function__ internals>", line 180, in transpose
  File "/home/hailo/shared/.venv/lib/python3.10/site-packages/numpy/core/fromnumeric.py", line 660, in transpose
    return _wrapfunc(a, 'transpose', axes)
  File "/home/hailo/shared/.venv/lib/python3.10/site-packages/numpy/core/fromnumeric.py", line 57, in _wrapfunc
    return bound(*args, **kwds)
ValueError: axes don't match array

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/hailo/shared/onnx_to_hef.py", line 90, in <module>
    main()
  File "/home/hailo/shared/onnx_to_hef.py", line 87, in main
    parse_stereo_onnx(onnx_path, calib_left, calib_right, net_name, hw_arch)
  File "/home/hailo/shared/onnx_to_hef.py", line 35, in parse_stereo_onnx
    hn, params = runner.translate_onnx_model(simplified_onnx_path, net_name)
  File "/home/hailo/shared/.venv/lib/python3.10/site-packages/hailo_sdk_common/states/states.py", line 16, in wrapped_func
    return func(self, *args, **kwargs)
  File "/home/hailo/shared/.venv/lib/python3.10/site-packages/hailo_sdk_client/runner/client_runner.py", line 1187, in translate_onnx_model
    parser.translate_onnx_model(
  File "/home/hailo/shared/.venv/lib/python3.10/site-packages/hailo_sdk_client/sdk_backend/parser/parser.py", line 280, in translate_onnx_model
    parsing_results = self._parse_onnx_model_to_hn(
  File "/home/hailo/shared/.venv/lib/python3.10/site-packages/hailo_sdk_client/sdk_backend/parser/parser.py", line 320, in _parse_onnx_model_to_hn
    return self.parse_model_to_hn(
  File "/home/hailo/shared/.venv/lib/python3.10/site-packages/hailo_sdk_client/sdk_backend/parser/parser.py", line 371, in parse_model_to_hn
    fuser = HailoNNFuser(converter.convert_model(), net_name, converter.end_node_names)
  File "/home/hailo/shared/.venv/lib/python3.10/site-packages/hailo_sdk_client/model_translator/translator.py", line 83, in convert_model
    self._create_layers()
  File "/home/hailo/shared/.venv/lib/python3.10/site-packages/hailo_sdk_client/model_translator/edge_nn_translator.py", line 39, in _create_layers
    self._add_direct_layers()
  File "/home/hailo/shared/.venv/lib/python3.10/site-packages/hailo_sdk_client/model_translator/edge_nn_translator.py", line 121, in _add_direct_layers
    self._layer_callback_from_vertex(vertex)
  File "/home/hailo/shared/.venv/lib/python3.10/site-packages/hailo_sdk_client/model_translator/onnx_translator/onnx_translator.py", line 393, in _layer_callback_from_vertex
    consumed_vertices = self._create_convolutional_layer(vertex)
  File "/home/hailo/shared/.venv/lib/python3.10/site-packages/hailo_sdk_client/model_translator/onnx_translator/onnx_translator.py", line 806, in _create_convolutional_layer
    kernel = np.transpose(vertex_kernel, [2, 3, 0, 1])  # [k_w, k_h, f_in, f_out] (onnx repr)
  File "<__array_function__ internals>", line 180, in transpose
  File "/home/hailo/shared/.venv/lib/python3.10/site-packages/numpy/core/fromnumeric.py", line 660, in transpose
    return _wrapfunc(a, 'transpose', axes)
  File "/home/hailo/shared/.venv/lib/python3.10/site-packages/numpy/core/fromnumeric.py", line 57, in _wrapfunc
    return bound(*args, **kwds)
ValueError: axes don't match array

Do you have any idea what might be causing this issue?

Are you running this straight form the hailo suite docker? you should have been a standard user not root, when inside the docker. Have you launched the docker container as root?