NOTE: The Jupyter Notebook below is included in the Chimera SDK and can be run interactively by running the following CLI command:
$ quadric sdk notebook
From the Jupyter Notebook window in your browser, select the notebook named /quadric/sdk-cli/examples/models/segmentation/ffnet/ffnet.ipynb.
Fuss-Free Network (FFNet)
Abstract
Naively applying deep backbones designed for Image Classification to the task of Semantic Segmentation leads to sub-par results, owing to a much smaller effective receptive field of these backbones. Implicit among the various design choices put forth in works like HRNet, DDRNet, and FANet are networks with a large effective receptive field.
The proposed model demonstrates that a simple encoder-decoder architecture with a ResNet-like backbone and a small multi-scale head, performs on-par or better than complex semantic segmentation architectures such as HRNet, FANet and DDRNets.
Further information can be found in the following Paper.
- Dushyant Mehta, Andrii Skliar, Haitam Ben Yahia, Shubhankar Borse, Fatih Porikli, Amirhossein Habibian, Tijmen Blankevoort, "Simple and Efficient Architectures for Semantic Segmentation"
Environment Setup
from pathlib import Path
import onnx
import onnxsim
import urllib
import zipfile
from examples.models.zoo.zoo_utils import download_file, onnx_check_and_simplify
FFNet Github Clone
import os
basename = Path("FFNet")
## Install HRNet-Human-Pose-Estimation.
if basename.exists():
print(f"Existing {basename} is used.")
else:
url = "https://github.com/Qualcomm-AI-research/FFNet/archive/refs/heads/master.zip"
filename = Path(url).name
download_file(url, filename)
with zipfile.ZipFile(filename, "r") as fd:
fd.extractall()
os.rename(f"{basename}-{Path(url).stem}", basename)
print(f"{basename} is installed.")
## Make symbolic links for later experiment.
symlink_list = ["models"]
for sym_path in symlink_list:
if not Path(sym_path).is_symlink():
os.symlink(basename / sym_path, Path(sym_path).name)
print(f"{sym_path} is symbolic")
FFNet is installed.
models is symbolic
Download Pretrained Models
In the fithub, various sizes of models are provided. In this experiment, we are going to use ffnet54S. ffnet40S can be experimented as well.
## Define target model.
MODEL_NAME = "ffnet54S" # "ffnet40S", "ffnet54S"
import zipfile
url = "https://github.com/Qualcomm-AI-research/FFNet/releases/download/models/%s.zip"
model_path = Path("pretrained_models")
os.makedirs(str(model_path), exist_ok=True)
if not (model_path / Path(url % (MODEL_NAME)).name).exists():
download_file(url % (MODEL_NAME), f"{model_path / Path(url%(MODEL_NAME)).name}")
with zipfile.ZipFile(f"{model_path / Path(url%(MODEL_NAME)).name}") as modelfd:
modelfd.extractall(str(model_path))
print(f"{MODEL_NAME} downloaded and extracted.")
ffnet54S downloaded and extracted.
Write config.py
config_text = """
model_weights_base_path = "pretrained_models/"
"""
with open("config.py", mode="w") as fd:
fd.writelines(config_text)
Load Pretrained Model
from models.model_registry import register_model
from models.ffnet_S_mobile import (
segmentation_ffnet40S_BBB_mobile,
segmentation_ffnet54S_BBB_mobile,
)
model_list = {
"ffnet40S": segmentation_ffnet40S_BBB_mobile,
"ffnet54S": segmentation_ffnet54S_BBB_mobile,
}
net = register_model(model_list[MODEL_NAME])()
Loading pretrained model state dict from pretrained_models/ffnet54S/ffnet54S_BBB_cityscapes_state_dict_quarts.pth
Initializing ffnnet54S_BBB_mobile weights
Export ONNX
The input size can be either (512, 512) or (1024, 512) as width x height. Here, we are going to use (512, 512).
import torch
import tvm.contrib.epu.chimera_job.constants as sdk_constants
onnx_file = f"{MODEL_NAME}.onnx"
model_input_size = (512, 512) # (1024, 512)
dummy_data = torch.randn(1, 3, *model_input_size[::-1], requires_grad=False)
input_edges = ["inputs0"]
output_edges = ["outputs0"]
## Export the model
torch.onnx.export(
net, # model being run
dummy_data, # model input (or a tuple for multiple inputs)
onnx_file, # where to save the model (can be a file or file-like object)
export_params=True, # store the trained parameter weights inside the model file
do_constant_folding=True, # whether to execute constant folding for optimization
opset_version=sdk_constants.DEFAULT_ONNX_OPSET,
input_names=input_edges, # the model's input names
output_names=output_edges, # the model's output names
)
onnx_model = onnx_check_and_simplify(onnx.load(onnx_file))
onnx.save(onnx_model, onnx_file)
print(f"ONNX is exported and simplified as {onnx_file}")
del onnx_model, net, dummy_data
ONNX is exported and simplified as ffnet54S.onnx
Quantization
from torch.utils.data import Subset
from torchvision.transforms import Compose, Normalize, ToTensor
from sdk_cli.utils.transforms import ResizePad
from sdk_cli.lib.quantize import (
QuantizedONNXModel,
quantize_onnx_model,
)
from sdk_cli.utils.datasets import QuadricCalibration
dataset_mean, dataset_std = [0.485, 0.456, 0.406], [0.229, 0.224, 0.225]
transforms = Compose(
[
ResizePad(model_input_size),
ToTensor(),
Normalize(dataset_mean, dataset_std),
]
)
## Path to directory containing data to use for for numerical range calibration during quantization
## Data is used also used to compare accuracy of fp32 and int8 models
dataset = QuadricCalibration.Dataset(transform=transforms)
## NOTE: `coco-like` is the 0th index target for `QuadricCalibration.Dataset`
coco_like_data_indices = [index for index, target in enumerate(dataset.targets) if target == 0]
coco_like_subset_of_dataset = Subset(dataset, coco_like_data_indices)
quantized_onnx_model: QuantizedONNXModel = quantize_onnx_model(
onnx_file, coco_like_subset_of_dataset, asymmetric_activation=True
)
print(
f"quantized onnx: {quantized_onnx_model.model_path}, tranges file: {quantized_onnx_model.tensor_ranges_path}"
)
del coco_like_subset_of_dataset
2026-06-19 03:53 - INFO - sdk - quantize - ONNX model shapes inferred.
2026-06-19 03:54 - DEBUG - sdk - quantize - Forcing node types: []
2026-06-19 03:54 - DEBUG - sdk - quantize - ONNX Node types excluded from quantization: ['Softmax', 'Sigmoid', 'QuadricCustomOp']
2026-06-19 03:54 - DEBUG - sdk - quantize - ONNX Node names excluded from quantization: []
2026-06-19 03:54 - INFO - sdk - quantize - Starting quantization...
WARNING:root:Please use QuantFormat.QDQ for activation type QInt8 and weight type QInt8. Or it will lead to bad performance on x64.
2026-06-19 03:54 - INFO - sdk - quantize - Quantization completed! Quantized model saved to /quadric/sdk-cli/examples/models/segmentation/ffnet/ffnet54S_OpSet16_optimized_asym_int8_q.onnx
2026-06-19 03:54 - INFO - sdk - quantize - ONNX full precision model size: 68.83MB
2026-06-19 03:54 - INFO - sdk - quantize - ONNX quantized model size: 17.33MB
2026-06-19 03:54 - INFO - sdk - quantize - ONNX model shapes inferred.
2026-06-19 03:54 - INFO - sdk - quantize - ONNX Model with well-defined shapes has been saved at `/quadric/sdk-cli/examples/models/segmentation/ffnet/ffnet54S_OpSet16_optimized_asym_int8_q_shaped.onnx`.
2026-06-19 03:54 - DEBUG - sdk - quantize - Checking for FLOAT/FLOAT16 types...
2026-06-19 03:54 - INFO - sdk - quantize - Checking for remaining FLOAT/FLOAT16 types.
2026-06-19 03:54 - INFO - sdk - quantize - Model still has FLOAT/FLOAT16 types after quantization. Creating ranges for floating point tensors using calibration data...
2026-06-19 03:54 - INFO - sdk - quantize - Saved computed tensor ranges to /quadric/sdk-cli/examples/models/segmentation/ffnet/ffnet54S_OpSet16_optimized_asym_int8_q_shaped.tranges.
2026-06-19 03:54 - INFO - sdk - quantize -
╒════════════════════════════════════════════════════════════════════════════════════════════════════════╤═══════════════════════════════════════════════════════════════════════════════════════════════════════════╕
│ Quantized ONNX Model │ Tensor Ranges File │
╞════════════════════════════════════════════════════════════════════════════════════════════════════════╪═══════════════════════════════════════════════════════════════════════════════════════════════════════════╡
│ /quadric/sdk-cli/examples/models/segmentation/ffnet/ffnet54S_OpSet16_optimized_asym_int8_q_shaped.onnx │ /quadric/sdk-cli/examples/models/segmentation/ffnet/ffnet54S_OpSet16_optimized_asym_int8_q_shaped.tranges │
╘════════════════════════════════════════════════════════════════════════════════════════════════════════╧═══════════════════════════════════════════════════════════════════════════════════════════════════════════╛
quantized onnx: /quadric/sdk-cli/examples/models/segmentation/ffnet/ffnet54S_OpSet16_optimized_asym_int8_q_shaped.onnx, tranges file: /quadric/sdk-cli/examples/models/segmentation/ffnet/ffnet54S_OpSet16_optimized_asym_int8_q_shaped.tranges
Compilation
from tvm.contrib.epu.chimera_job.chimera_job import ChimeraJob
cgc_job = ChimeraJob(
model_p=str(quantized_onnx_model.model_path),
)
cgc_job.compile(quiet=True)
print(cgc_job)
╒═════════════════════╤════════════════════════════════════════════════════════════════════════════════════════════════════════╕
│ Module Name │ ffnet54S_OpSet16_optimized_asym_int8_q_shaped_QC_U_1d7_16MB_4kB_128GBps_128GBps_16_OFF_x1_x1 │
├─────────────────────┼────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ ONNX File │ /quadric/sdk-cli/examples/models/segmentation/ffnet/ffnet54S_OpSet16_optimized_asym_int8_q_shaped.onnx │
├─────────────────────┼────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ Product Target │ QC-U │
├─────────────────────┼────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ Number of Cores │ 1 │
├─────────────────────┼────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ ISS Clock Frequency │ 1.700 │
├─────────────────────┼────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ L2M Size │ 16MB │
├─────────────────────┼────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ LRM Size │ 4kB │
├─────────────────────┼────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ External Read BW │ 128GBps │
├─────────────────────┼────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ External Write BW │ 128GBps │
├─────────────────────┼────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ MACS per PE │ 16 │
├─────────────────────┼────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ Max L2M │ 8.615MB │
├─────────────────────┼────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ Max LRM │ 2.188kB │
├─────────────────────┼────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ Max Temp Ext Bytes │ 0.000MB │
├─────────────────────┼────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ Network GMACs │ 36.703 │
╘═════════════════════╧════════════════════════════════════════════════════════════════════════════════════════════════════════╛
╒════╤════════╤══════════╤═══════════════════╤══════════════════════════╤═══════╕
│ │ Type │ Name │ shape │ type │ mse │
╞════╪════════╪══════════╪═══════════════════╪══════════════════════════╪═══════╡
│ 0 │ Input │ inputs0 │ [1, 3, 512, 512] │ tensor[FixedPoint32<27>] │ n/a │
├────┼────────┼──────────┼───────────────────┼──────────────────────────┼───────┤
│ 1 │ Output │ outputs0 │ [1, 19, 128, 128] │ tensor[FixedPoint32<25>] │ n/a │
╘════╧════════╧══════════╧═══════════════════╧══════════════════════════╧═══════╛
Demo
Inference
import numpy as np
from PIL import Image
from sdk_cli.lib.inference import InferenceEngine, batch_inference
all_image_paths = [
"../../../common/calibration/coco-like/17790319373_bd19b24cfc_k.jpeg",
"../../../common/calibration/coco-like/19064748793_bb942deea1_k.jpeg",
"../../../common/calibration/coco-like/24274813513_0cfd2ce6d0_k.jpeg",
]
all_images = []
for image_path in all_image_paths:
image = Image.open(image_path)
transformed_image = transforms(image)
all_images.append(np.expand_dims(transformed_image.numpy(), axis=0).astype(np.float32))
engines = {
InferenceEngine.CHIMERA_ORT_INT8: cgc_job,
InferenceEngine.CHIMERA_ISS_INT8: cgc_job,
}
outputs_per_inference_engine = {}
THREADS = min(len(all_images), 6)
for inference_engine, engine in engines.items():
all_backbone_outputs = batch_inference(
inference_engine,
engine,
all_images,
threads=THREADS,
)
outputs_per_inference_engine[inference_engine] = all_backbone_outputs
2026-06-19 03:57 - WARNING - epu - chimera_job - ORT is not threadsafe -- forcing single threaded batch execution
100%|████████████████████████████████████████████| 3/3 [00:01<00:00, 1.59it/s]
Processing: 100%|████████████████████████████████| 3/3 [02:48<00:00, 56.01s/it]
Run Statistics
print(cgc_job)
cgc_job.plot_run_statistics()
╒═════════════════════╤════════════════════════════════════════════════════════════════════════════════════════════════════════╕
│ Module Name │ ffnet54S_OpSet16_optimized_asym_int8_q_shaped_QC_U_1d7_16MB_4kB_128GBps_128GBps_16_OFF_x1_x1 │
├─────────────────────┼────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ ONNX File │ /quadric/sdk-cli/examples/models/segmentation/ffnet/ffnet54S_OpSet16_optimized_asym_int8_q_shaped.onnx │
├─────────────────────┼────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ Product Target │ QC-U │
├─────────────────────┼────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ Number of Cores │ 1 │
├─────────────────────┼────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ ISS Clock Frequency │ 1.700 │
├─────────────────────┼────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ L2M Size │ 16MB │
├─────────────────────┼────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ LRM Size │ 4kB │
├─────────────────────┼────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ External Read BW │ 128GBps │
├─────────────────────┼────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ External Write BW │ 128GBps │
├─────────────────────┼────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ MACS per PE │ 16 │
├─────────────────────┼────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ Max L2M │ 8.615MB │
├─────────────────────┼────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ Max LRM │ 2.188kB │
├─────────────────────┼────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ Max Temp Ext Bytes │ 0.000MB │
├─────────────────────┼────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ Network GMACs │ 36.703 │
╘═════════════════════╧════════════════════════════════════════════════════════════════════════════════════════════════════════╛
╒════╤════════╤══════════╤═══════════════════╤══════════════════════════╤═══════╕
│ │ Type │ Name │ shape │ type │ mse │
╞════╪════════╪══════════╪═══════════════════╪══════════════════════════╪═══════╡
│ 0 │ Input │ inputs0 │ [1, 3, 512, 512] │ tensor[FixedPoint32<27>] │ n/a │
├────┼────────┼──────────┼───────────────────┼──────────────────────────┼───────┤
│ 1 │ Output │ outputs0 │ [1, 19, 128, 128] │ tensor[FixedPoint32<25>] │ 0.006 │
╘════╧════════╧══════════╧═══════════════════╧══════════════════════════╧═══════╛
Post-ISS Report 1.7 GHz ***
Fully placed-and-routed gate simulation:
╒══════════════════════════════════╤═════════╕
│ Latency (ms) │ 2.56 │
├──────────────────────────────────┼─────────┤
│ FPS │ 390.17 │
├──────────────────────────────────┼─────────┤
│ Average Power @ 3nm SSGNP (mW) │ 2429.72 │
├──────────────────────────────────┼─────────┤
│ FPS per Watt @ 3nm SSGNP (FPS/W) │ 160.58 │
├──────────────────────────────────┼─────────┤
│ Ext Rd Bytes (MB) │ 20.35 │
├──────────────────────────────────┼─────────┤
│ Ext Wr Bytes (MB) │ 1.19 │
├──────────────────────────────────┼─────────┤
│ Avg Ext Rd BW (GBps) │ 7.76 │
├──────────────────────────────────┼─────────┤
│ Avg Ext Wr BW (GBps) │ 0.45 │
├──────────────────────────────────┼─────────┤
│ MAC Utilization │ 51.42% │
╘══════════════════════════════════╧═════════╛
*** Data generated using 7nm SSGNP gatesim and scaled to 3nm
[SDK-CLI] : TotalCycles: 4,357,027
[SDK-CLI] : Executions/second: 390.17
compute : ▇▇▇▇▇▇▇▇ 482.485K
data_array : ▇▇▇▇▇▇ 358.17K
mac : ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 2.796M
data_external: ▇ 56.483K
data_ocm : ▇▇▇▇▇▇▇▇▇▇▇ 662.039K
for more information check run directory: /quadric/sdk-cli/examples/models/segmentation/ffnet/ccl_build/ffnet54S_OpSet16_optimized_asym_int8_q_shaped_QC_U_1d7_16MB_4kB_128GBps_128GBps_16_OFF_x1_x1/run/20260619_035752_da38ee
2026-06-19 04:00 - INFO - epu - chimera_job - Combined plots generated and saved to:
/quadric/sdk-cli/examples/models/segmentation/ffnet/ccl_build/ffnet54S_OpSet16_optimized_asym_int8_q_shaped_QC_U_1d7_16MB_4kB_128GBps_128GBps_16_OFF_x1_x1/run/20260619_035752_da38ee/data/ffnet54S_OpSet16_optimized_asym_int8_q_shaped_QC_U_1d7_16MB_4kB_128GBps_128GBps_16_OFF_x1_x1.combined.png
'/quadric/sdk-cli/examples/models/segmentation/ffnet/ccl_build/ffnet54S_OpSet16_optimized_asym_int8_q_shaped_QC_U_1d7_16MB_4kB_128GBps_128GBps_16_OFF_x1_x1/run/20260619_035752_da38ee/data'

Display Segmentation
import matplotlib.pyplot as plt
from sdk_cli.node_builtins.outputs.segmentation_visualizer import (
draw_segmentation,
SegmentationVariant,
)
from sdk_cli.utils.datasets.Cityscapes import CITYSCAPES19CLASSES
%matplotlib inline
pic_len = len(engines) + 1
for i in range(len(all_images)):
ax, idx = {}, 1
fig = plt.figure(figsize=(4 * pic_len, 4), tight_layout=True)
ax[idx] = fig.add_subplot(int("1%s%s" % (pic_len, idx)))
ax[idx].imshow(np.array(Image.open(all_image_paths[i])))
ax[idx].set_title("Original Image")
ax[idx].axis("off")
for inference_engine, all_outputs in outputs_per_inference_engine.items():
idx += 1
ax[idx] = fig.add_subplot(int("1%s%s" % (pic_len, idx)))
detected_frame = draw_segmentation(
SegmentationVariant.SemanticSegmentation,
np.array(Image.open(all_image_paths[i])),
all_outputs[i][0].argmax(1),
classes=CITYSCAPES19CLASSES,
alpha=0.8,
random_seed=11,
)
ax[idx].imshow(detected_frame)
ax[idx].set_title(f"{MODEL_NAME}: {str(inference_engine).upper()}")
ax[idx].axis("off")
fig.show()



Citation
@inproceedings{mehta2022simple,
title={Simple and Efficient Architectures for Semantic Segmentation},
author={Mehta, Dushyant and Skliar, Andrii and Ben Yahia, Haitam and Borse, Shubhankar and Porikli, Fatih and Habibian, Amirhossein and Blankevoort, Tijmen},
booktitle={Proceedings of the IEEE/CVF Conference on Computer Vision and Pattern Recognition},
pages={2628--2636},
year={2022}
}