Skip to content

fix(camera): blur CLI/self-verify calls undefined edge_detect -> NameError on NPU - #2

Open
Vyacheslav-Tomashevskiy wants to merge 2 commits into
Scottcjn:mainfrom
Vyacheslav-Tomashevskiy:fix/blur-cli-edge-detect-nameerror
Open

fix(camera): blur CLI/self-verify calls undefined edge_detect -> NameError on NPU#2
Vyacheslav-Tomashevskiy wants to merge 2 commits into
Scottcjn:mainfrom
Vyacheslav-Tomashevskiy:fix/blur-cli-edge-detect-nameerror

Conversation

@Vyacheslav-Tomashevskiy

Copy link
Copy Markdown

Problem

camera/blur_pipeline.py defines its NPU design as blur (def blur(...), and both camera/npu_camera_daemon.py and camera/test_blur.py do from blur_pipeline import blur). But the module's own runtime paths were still calling edge_detect — a leftover name from AMD's edge_detect IRON example this file was adapted from. edge_detect is never defined or imported anywhere in blur_pipeline.py:

  • _run_and_verify()edge_detect(in_t, b_t, out_t, **_compile_kwargs(opts))
  • main()run_design_cli(edge_detect, ...)

Why CI stays green but real hardware crashes

blur_pipeline.py imports aie.iron at the top, so on a host without the NPU toolchain the module fails at that import first — which is exactly the boundary ci.yml's import loop expects, so CI is green. On a real NPU box the import succeeds, python camera/blur_pipeline.py reaches main(), and it dies with:

NameError: name 'edge_detect' is not defined

before running the blur design or its self-verify. The module's CLI/self-verify is unusable on the one platform it's meant for.

Fix

Rename the two runtime references from edge_detect to blur (the function that actually exists and that the daemon/test already import). Two-line change; no behavior change on the happy path.

Regression test

Added camera/test_blur_pipeline_cli_symbol.py — a pure-AST test (no aie/numpy, runs anywhere) asserting the design symbol the CLI hands to the NPU is one the module actually defines. Mutation-guarded: it fails on the pre-fix code (AssertionError: ... references undefined edge_detect ...) and passes after the fix. python -m compileall -q camera stays clean.


Verified bug — claimed under the RustChain verified-bug channel (rustchain-bounties#71).
RTC: RTCd1554f0f35576faf01d386a6be1c947f560dd0b7

…Error on NPU

blur_pipeline.py defines the design function , but _run_and_verify()
and main() still referenced  (a leftover from the AMD example
this was adapted from), which is never defined or imported in the module.

The module imports aie.iron at the top, so a host without the NPU toolchain
fails at that import first and CI's import-boundary check stays green. But on
a real NPU box, `python camera/blur_pipeline.py` reaches main() and crashes
with `NameError: name 'edge_detect' is not defined` instead of running the
blur design or its self-verify.

Rename the two runtime references to `blur` (the function the daemon and
test_blur.py already import). Add a pure-AST regression test (no aie/numpy
needed, runs anywhere) asserting the design symbol the CLI hands to the NPU
is one the module actually defines.
…edge-detect

Scottcjn#2 fixed the CLI/self-verify NameError (edge_detect -> blur) but left the
golden reference itself wrong: _run_and_verify() still built its expected
output with _edge_detect_ref() -- a Laplacian edge-detect + threshold +
highlight pipeline that shares no math with the Gaussian blur the design
actually computes. On real NPU hardware --verify would go from 'crashes'
to 'runs and reports a correct blur as wrong', since a blur output never
matches an edge map.

Add _blur_ref(), a numpy reference mirroring blur()'s actual pipeline:
rgba2gray -> 3x3 Gaussian blur (the unity-gain [[1,2,1],[2,4,2],[1,2,1]]/16
kernel filter_kernel_buff actually holds, per the module's own 'GAIN FIX'
docstring, not the Laplacian used for edge-detect) -> gray2rgba, combined
via add_weighted with alpha=1 (register 16384 = Q14 unity) and beta=0
(register 0 -- unlike edge_detect's highlighted-edges-over-original
composite, the original RGBA is not blended back in). Route
_run_and_verify() through it instead of _edge_detect_ref().

Tests (camera/test_blur_pipeline_golden_ref.py, no aie/NPU toolchain
needed -- minimal import-time stand-ins only, real design/verify code
never executed):
  - AST guard that _run_and_verify calls _blur_ref, not _edge_detect_ref
  - impulse-response test pinning the exact kernel weights + normalization
  - flat-field test covering BORDER_REPLICATE at the corners
  - checkerboard smoothing test mirroring test_blur.py's real-hardware
    smoke assertion, entirely in numpy, which also catches a beta!=0
    regression (blending the original color back in would fail it)
  - sanity check that _blur_ref and _edge_detect_ref actually differ

Mutation-checked: reverting the _run_and_verify call fails the AST guard;
corrupting the kernel weights fails the impulse-response test. Existing
camera/*.py CI convention preserved -- blur_pipeline.py still fails only
at the aie import boundary, both new/existing camera test files run to
completion.
@Vyacheslav-Tomashevskiy

Copy link
Copy Markdown
Author

Good catch, thank you -- pushed a revision. Added _blur_ref(), a numpy reference that actually mirrors what blur() computes: rgba2gray -> 3x3 Gaussian blur (the unity-gain [[1,2,1],[2,4,2],[1,2,1]]/16 kernel filter_kernel_buff holds, per the module's own "GAIN FIX" docstring) -> gray2rgba, combined via add_weighted with alpha=1/beta=0 (register values 16384/0 -- the original RGBA is not blended back in, unlike edge_detect's highlighted-edges-over-original composite). _run_and_verify() now calls _blur_ref instead of _edge_detect_ref.

Added camera/test_blur_pipeline_golden_ref.py: an AST guard on the _run_and_verify call target, an impulse-response test pinning the exact kernel weights and normalization shift, a flat-field test for BORDER_REPLICATE at the corners, and a checkerboard-smoothing test mirroring test_blur.py's real-hardware smoke assertion (which also catches a beta!=0 regression). No aie/NPU toolchain needed -- minimal import-time stand-ins only, the real design/verify code is never executed, and blur_pipeline.py still fails only at the aie import boundary per this repo's CI convention. Mutation-checked: reverting the _run_and_verify call target fails the AST guard, corrupting the kernel weights fails the impulse-response test.

@Scottcjn

Copy link
Copy Markdown
Owner

Reviewed properly, not bulk-triaged. The core diagnosis is confirmed on main: blur_pipeline.py has no def edge_detect and no import of it, yet _run_and_verify() (line 320) and main() (line 342) both reference it, so python blur_pipeline.py on a real NPU dies with NameError before it ever reaches the design. I also confirmed the second half of your claim by reading the design body: only two workers are wired (rgba2gray_fn, gray2rgba_add_weight_fn around the filter_fn), threshold_line_kernel and of_intermediates[2] are never used, filter_kernel_buff really is [[256,512,256],[512,1024,512],[256,512,256]], and alpha, beta, gamma = 16384, 0, 0 — so _blur_ref’s _add_weighted_cv_ref(mask_rgba, rgba_uint8, 1, 0, 0) is the right shape and _edge_detect_ref was genuinely the wrong golden. I ran pytest camera/test_blur_pipeline_cli_symbol.py camera/test_blur_pipeline_golden_ref.py — 6 passed.

Two things before this goes in.

  1. The header comment of test_blur_pipeline_golden_ref.py contradicts its own PR:
# _run_and_verify() used to compare the blur() design's output against
# _edge_detect_ref() -- ...
# #2 fixed the CLI NameError crash (edge_detect -> blur) but left
# that mismatch in place, so on real NPU hardware --verify would go from
# "crashes" to "runs and reports the blur output as wrong"

This is #2, and it fixes both in the same diff — _run_and_verify gets blur(...) and _blur_ref(...) in one hunk. gh pr list --state all shows only #1 (the path/CI fix) and this one, so there is no earlier PR the note can be pointing at. As written it sends a future reader hunting for a #3 that closed a gap that was never actually left open. Please reword to describe the single change.

  1. Not a defect, just the limit of what I could check: I have no NPU box, so every assertion I ran was against the pure-numpy _..._ref() helpers. What I could not exercise is the thing this PR changes the meaning of — whether _blur_ref matches what the AIE filter2d kernel actually emits, given the fixed-point path (int16 coeffs >>8, then >>4 for SRS_SHIFT-8) versus _gaussian_blur3x3_ref’s float np.round before the _EPSILON = 2.0 comparison. Since --verify currently cannot run at all, this cannot regress anything, but it does mean nobody has yet seen it pass. A single python camera/blur_pipeline.py --verify on the NPU machine would settle it, and would be worth noting in the PR body so the claim rests on a run rather than on inspection.

Thanks — this is a good catch on a path CI structurally cannot reach (the aie.iron import fails first on hosts without the toolchain, which is exactly why it stayed green while being broken). Happy to merge once (1) is reworded.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants