| |
| """Edge/boundary detection solver — Laplacian Conv. |
| |
| v5.2: 0 matches in current task set (edge definition too strict). |
| Kept for future variants (per-color edges, interior-only edges, etc.). |
| """ |
|
|
| import numpy as np |
| from onnx import helper, numpy_helper |
| from ..onnx_helpers import mk, _make_int64_init, _build_pad_node |
| from ..data_loader import get_exs, fixed_shapes |
| from ..constants import GH, GW |
|
|
|
|
| def _has_edges(inp, out, edge_color, bg_color=0): |
| """Check if output is edge detection of input.""" |
| h, w = inp.shape |
| for r in range(h): |
| for c in range(w): |
| pix = inp[r, c] |
| is_edge = False |
| if pix != bg_color: |
| for dr, dc in [(-1, 0), (1, 0), (0, -1), (0, 1)]: |
| nr, nc = r + dr, c + dc |
| if 0 <= nr < h and 0 <= nc < w: |
| if inp[nr, nc] != pix: |
| is_edge = True |
| break |
| else: |
| is_edge = True |
| break |
| expected = edge_color if is_edge else bg_color |
| if out[r, c] != expected: |
| return False |
| return True |
|
|
|
|
| def _build_edge_model(IH, IW, edge_color, bg_color=0): |
| """Build ONNX model for edge detection via Laplacian conv.""" |
| pad_h, pad_w = GH - IH, GW - IW |
|
|
| ch_sel = np.zeros((1, 10, 1, 1), dtype=np.float32) |
| for c in range(10): |
| if c != bg_color: |
| ch_sel[0, c, 0, 0] = 1.0 |
|
|
| lap_k = np.array([[0, -1, 0], |
| [-1, 4, -1], |
| [0, -1, 0]], dtype=np.float32).reshape(1, 1, 3, 3) |
|
|
| edge_oh = np.zeros((1, 10, 1, 1), dtype=np.float32) |
| edge_oh[0, edge_color, 0, 0] = 1.0 |
| bg_oh = np.zeros((1, 10, 1, 1), dtype=np.float32) |
| bg_oh[0, bg_color, 0, 0] = 1.0 |
|
|
| inits = [ |
| _make_int64_init('sl_st', [0, 0, 0, 0]), |
| _make_int64_init('sl_en', [1, 10, IH, IW]), |
| numpy_helper.from_array(ch_sel, 'ch_sel'), |
| numpy_helper.from_array(lap_k, 'lap_k'), |
| numpy_helper.from_array(np.float32(0.5), 'thresh'), |
| numpy_helper.from_array(edge_oh, 'edge_oh'), |
| numpy_helper.from_array(bg_oh, 'bg_oh'), |
| ] |
|
|
| nodes = [ |
| helper.make_node('Slice', ['input', 'sl_st', 'sl_en'], ['cropped']), |
| helper.make_node('Conv', ['cropped', 'ch_sel'], ['occ'], kernel_shape=[1, 1]), |
| helper.make_node('Conv', ['occ', 'lap_k'], ['lap_out'], kernel_shape=[3, 3], pads=[1, 1, 1, 1]), |
| helper.make_node('Abs', ['lap_out'], ['lap_abs']), |
| helper.make_node('Greater', ['lap_abs', 'thresh'], ['is_edge_raw']), |
| helper.make_node('Greater', ['occ', 'thresh'], ['is_occ']), |
| helper.make_node('And', ['is_edge_raw', 'is_occ'], ['is_edge']), |
| helper.make_node('Where', ['is_edge', 'edge_oh', 'bg_oh'], ['result_small']), |
| ] |
| nodes.append(_build_pad_node('result_small', 'output', pad_h, pad_w, inits)) |
| return mk(nodes, inits) |
|
|
|
|
| def s_edge_detect(td): |
| """Edge detection solver: output = boundary pixels of input shapes.""" |
| exs = get_exs(td) |
| sp = fixed_shapes(td) |
| if sp is None: |
| return None |
| (IH, IW), (OH, OW) = sp |
| if (IH, IW) != (OH, OW): |
| return None |
|
|
| for bg_color in [0]: |
| out_colors = set() |
| for _, out in exs: |
| out_colors.update(out.flatten()) |
| for edge_color in out_colors: |
| if edge_color == bg_color: |
| continue |
| if all(_has_edges(inp, out, edge_color, bg_color) for inp, out in exs): |
| return _build_edge_model(IH, IW, edge_color, bg_color) |
| return None |
|
|