Spaces:
Running on Zero
Running on Zero
Download src/spatial_jev/point_probability_map.py from hugging-apps/mjev-spatial: direct link, hf CLI and curl.
- Browser
- Download file 5.93 kB
-
https://huggingface.co/spaces/hugging-apps/mjev-spatial/resolve/main/src/spatial_jev/point_probability_map.py
- Command line
-
hf download hf://spaces/hugging-apps/mjev-spatial/src/spatial_jev/point_probability_map.py
-
curl -L -o point_probability_map.py https://huggingface.co/spaces/hugging-apps/mjev-spatial/resolve/main/src/spatial_jev/point_probability_map.py
5.93 kB
| """Nine-way conditional trees and connected-component multi-point readout.""" | |
| import numpy as np | |
| from .hierarchy import POINT_RC | |
| def softmax9(logits): | |
| values = np.asarray(logits, dtype=np.float64) | |
| if values.shape != (9,) or not np.isfinite(values).all(): | |
| raise ValueError("Expected nine finite logits") | |
| probabilities = np.exp(values - values.max()) | |
| return probabilities / probabilities.sum() | |
| def grid_location(path): | |
| row = col = 0 | |
| for choice in path: | |
| dr, dc = POINT_RC[choice] | |
| row, col = row * 3 + dr, col * 3 + dc | |
| return row, col | |
| def branch_spec(builder, parent, choice): | |
| # Copy mutable histories, but share immutable PIL images and input metadata. | |
| child = dict(parent) | |
| for key in ("images", "image_keys", "counts", "mapping", "path"): | |
| child[key] = list(parent[key]) | |
| if 'roi_boxes' in parent: | |
| child['roi_boxes'] = list(parent['roi_boxes']) | |
| child["path"].append(choice) | |
| builder.advance(child, choice) | |
| return child | |
| def probability_map(builder, request, evaluate_nodes, mode="full", root_relative_cutoff=0.01): | |
| """Three levels, 729 leaves; labels/masks/counts are not accepted as arguments. | |
| root_pruned uses only the first decision to omit low-mass root subtrees. | |
| Unvisited leaves remain explicitly unknown, with their missing mass reported. | |
| No probability renormalization is applied after pruning. | |
| """ | |
| if mode not in ("full", "root_pruned"): | |
| raise ValueError("Unknown tree policy") | |
| if not 0 <= root_relative_cutoff <= 1: | |
| raise ValueError("Invalid root cutoff") | |
| if request["input"]["answer_space"]["kind"] != "point": | |
| raise ValueError("Point input required") | |
| grid = np.zeros((27, 27), dtype=np.float64) | |
| known = np.zeros((27, 27), dtype=bool) | |
| nodes, pruned = [], [] | |
| frontier = [(builder.initial_spec(request["input"], request["id"]), 1.0)] | |
| for depth in range(3): | |
| logits = evaluate_nodes([state for state, _ in frontier]) | |
| if len(logits) != len(frontier): | |
| raise ValueError("Node evaluator returned a different batch size") | |
| following = [] | |
| for (state, mass), scores in zip(frontier, logits): | |
| probabilities = softmax9(scores) | |
| path = list(state["path"]) | |
| nodes.append({"path": path, "mass": mass, "logits": list(map(float, scores)), | |
| "probabilities": probabilities.tolist()}) | |
| cutoff = probabilities.max() * root_relative_cutoff | |
| for choice, probability in enumerate(probabilities): | |
| child_path = [*path, choice] | |
| child_mass = mass * float(probability) | |
| if depth == 2: | |
| row, col = grid_location(child_path) | |
| if known[row, col]: | |
| raise ValueError("Duplicate leaf location") | |
| grid[row, col], known[row, col] = child_mass, True | |
| elif depth == 0 and mode == "root_pruned" and probability < cutoff: | |
| pruned.append({"path": child_path, "mass": child_mass}) | |
| else: | |
| following.append((branch_spec(builder, state, choice), child_mass)) | |
| frontier = following | |
| retained, omitted = float(grid.sum()), sum(r["mass"] for r in pruned) | |
| if not np.isclose(retained + omitted, 1, atol=1e-12, rtol=0): | |
| raise ValueError("Probability mass was lost or duplicated") | |
| return {"probabilities": grid, "known": known, "nodes": nodes, | |
| "evaluated_nodes": len(nodes), "evaluated_leaves": int(known.sum()), | |
| "retained_mass": retained, "pruned_mass": omitted, "pruned_branches": pruned} | |
| def component_points(probabilities, known=None, relative_threshold=0.1, connectivity=8): | |
| """Pick each superlevel component's maximum cell, with deterministic tie breaks.""" | |
| grid = np.asarray(probabilities, dtype=np.float64) | |
| if grid.ndim != 2 or not np.isfinite(grid).all() or (grid < 0).any(): | |
| raise ValueError("Invalid spatial probability grid") | |
| if not 0 < relative_threshold <= 1 or connectivity not in (4, 8): | |
| raise ValueError("Invalid connected-component settings") | |
| known = np.ones_like(grid, dtype=bool) if known is None else np.asarray(known, dtype=bool) | |
| if known.shape != grid.shape: | |
| raise ValueError("Known-cell mask shape mismatch") | |
| peak = float(grid[known].max()) if known.any() else 0.0 | |
| threshold = peak * relative_threshold | |
| mask = known & (grid >= threshold) & (grid > 0) | |
| visited = np.zeros_like(mask) | |
| neighbors = [(-1, 0), (1, 0), (0, -1), (0, 1)] | |
| if connectivity == 8: | |
| neighbors += [(-1, -1), (-1, 1), (1, -1), (1, 1)] | |
| height, width = grid.shape | |
| components, points = [], [] | |
| for row, col in zip(*np.where(mask)): | |
| if visited[row, col]: | |
| continue | |
| visited[row, col] = True | |
| stack, cells = [(int(row), int(col))], [] | |
| while stack: | |
| y, x = stack.pop() | |
| cells.append((y, x)) | |
| for dy, dx in neighbors: | |
| yy, xx = y + dy, x + dx | |
| if 0 <= yy < height and 0 <= xx < width and mask[yy, xx] and not visited[yy, xx]: | |
| visited[yy, xx] = True | |
| stack.append((yy, xx)) | |
| # Prefer the highest probability, then topmost/leftmost on exact ties. | |
| y, x = min(cells, key=lambda cell: (-grid[cell], cell[0], cell[1])) | |
| point = [(x + 0.5) / width, (y + 0.5) / height] | |
| points.append(point) | |
| components.append({"cells": [list(cell) for cell in cells], "peak_cell": [y, x], | |
| "peak_probability": float(grid[y, x]), | |
| "mass": sum(float(grid[cell]) for cell in cells)}) | |
| return {"points": points, "components": components, "threshold": threshold, | |
| "relative_threshold": relative_threshold, "connectivity": connectivity} | |