Download fold_depth_log.py from WaveMatrix/Yolo26-Depth: direct link, hf CLI and curl.
- Browser
- Download file 3.47 kB
-
https://huggingface.co/WaveMatrix/Yolo26-Depth/resolve/main/fold_depth_log.py
- Command line
-
hf download hf://WaveMatrix/Yolo26-Depth/fold_depth_log.py
-
curl -L -o fold_depth_log.py https://huggingface.co/WaveMatrix/Yolo26-Depth/resolve/main/fold_depth_log.py
3.47 kB
| #!/usr/bin/env python3 | |
| """Remove the unsupported Log branch from this YOLO depth ONNX export. | |
| For the depth head exported by this project: | |
| Log(Clip(Exp(Clip(x, lo, hi)), floor, +inf)) | |
| is equivalent to Clip(x, max(lo, log(floor)), hi). The generated AX637 | |
| model then contains no AxQuantizedLog CPU fallback. | |
| """ | |
| from __future__ import annotations | |
| import argparse | |
| import math | |
| from pathlib import Path | |
| import numpy as np | |
| import onnx | |
| from onnx import numpy_helper | |
| def scalar_initializer(model: onnx.ModelProto, name: str) -> float: | |
| """Return a scalar initializer and reject models with a different topology.""" | |
| for initializer in model.graph.initializer: | |
| if initializer.name == name: | |
| value = numpy_helper.to_array(initializer) | |
| if value.size != 1: | |
| raise ValueError(f"{name} must be scalar, got shape {value.shape}") | |
| return float(value.reshape(-1)[0]) | |
| raise ValueError(f"missing initializer: {name}") | |
| def fold_depth_log(model: onnx.ModelProto) -> None: | |
| """Fold the Clip -> Exp -> Clip -> Log sequence used by the depth head.""" | |
| nodes = list(model.graph.node) | |
| producers = {output: node for node in nodes for output in node.output if output} | |
| log_node = next( | |
| (node for node in nodes if node.name == "/model.23/Log" and node.op_type == "Log"), | |
| None, | |
| ) | |
| if log_node is None or len(log_node.input) != 1 or len(log_node.output) != 1: | |
| raise ValueError("expected a single-input /model.23/Log node") | |
| outer_clip = producers.get(log_node.input[0]) | |
| exp_node = producers.get(outer_clip.input[0]) if outer_clip is not None and outer_clip.input else None | |
| inner_clip = producers.get(exp_node.input[0]) if exp_node is not None and exp_node.input else None | |
| if ( | |
| outer_clip is None | |
| or outer_clip.op_type != "Clip" | |
| or exp_node is None | |
| or exp_node.op_type != "Exp" | |
| or inner_clip is None | |
| or inner_clip.op_type != "Clip" | |
| or len(inner_clip.input) != 3 | |
| or len(outer_clip.input) < 2 | |
| ): | |
| raise ValueError("unexpected depth calibration topology around /model.23/Log") | |
| inner_min = scalar_initializer(model, inner_clip.input[1]) | |
| floor = scalar_initializer(model, outer_clip.input[1]) | |
| if floor <= 0.0: | |
| raise ValueError(f"Clip floor must be positive, got {floor}") | |
| # Do not mutate a potentially shared initializer from elsewhere in the graph. | |
| folded_min = max(inner_min, math.log(floor)) | |
| min_name = "depth_log_folded_min" | |
| model.graph.initializer.append(numpy_helper.from_array(np.array(folded_min, dtype=np.float32), min_name)) | |
| inner_clip.input[1] = min_name | |
| inner_clip.name = "/model.23/Clip_log_folded" | |
| inner_clip.output[0] = log_node.output[0] | |
| remove_ids = {id(outer_clip), id(exp_node), id(log_node)} | |
| del model.graph.node[:] | |
| model.graph.node.extend(node for node in nodes if id(node) not in remove_ids) | |
| def main() -> None: | |
| parser = argparse.ArgumentParser(description=__doc__) | |
| parser.add_argument("input", type=Path, help="source ONNX model") | |
| parser.add_argument("output", type=Path, help="rewritten ONNX model") | |
| args = parser.parse_args() | |
| model = onnx.load(args.input) | |
| fold_depth_log(model) | |
| onnx.checker.check_model(model) | |
| args.output.parent.mkdir(parents=True, exist_ok=True) | |
| onnx.save(model, args.output) | |
| print(f"wrote {args.output}") | |
| if __name__ == "__main__": | |
| main() | |