Download src/position.py from rishavk77/ShelfEye: direct link, hf CLI and curl.
- Browser
- Download file 2.61 kB
-
https://huggingface.co/spaces/rishavk77/ShelfEye/resolve/main/src/position.py
- Command line
-
hf download hf://spaces/rishavk77/ShelfEye/src/position.py
-
curl -L -o position.py https://huggingface.co/spaces/rishavk77/ShelfEye/resolve/main/src/position.py
2.61 kB
| """Assign row and column positions to detected products based on bounding box layout.""" | |
| import numpy as np | |
| def _cluster_rows(y_centers: list[float], min_gap_ratio: float = 0.4) -> list[int]: | |
| """Cluster y-center values into shelf rows using gap-based splitting. | |
| Sorts detections by y-center, then splits into a new row whenever | |
| the gap between consecutive detections exceeds min_gap_ratio * median_box_height. | |
| """ | |
| if not y_centers: | |
| return [] | |
| indices = np.argsort(y_centers) | |
| sorted_y = np.array(y_centers)[indices] | |
| if len(sorted_y) == 1: | |
| row_labels = np.array([1]) | |
| result = np.empty_like(row_labels) | |
| result[indices] = row_labels | |
| return result.tolist() | |
| gaps = np.diff(sorted_y) | |
| median_gap = np.median(gaps) if len(gaps) > 0 else 1.0 | |
| threshold = max(median_gap * 1.5, 20) | |
| row_labels = np.ones(len(sorted_y), dtype=int) | |
| current_row = 1 | |
| for i in range(1, len(sorted_y)): | |
| if gaps[i - 1] > threshold: | |
| current_row += 1 | |
| row_labels[i] = current_row | |
| result = np.empty_like(row_labels) | |
| result[indices] = row_labels | |
| return result.tolist() | |
| def assign_positions(detections: list[dict]) -> list[dict]: | |
| """Assign row and column positions to each detection. | |
| Rows are numbered top-to-bottom (row 1 = top shelf). | |
| Columns are numbered left-to-right within each row. | |
| """ | |
| if not detections: | |
| return detections | |
| y_centers = [] | |
| for det in detections: | |
| x1, y1, x2, y2 = det["bbox"] | |
| y_centers.append((y1 + y2) / 2) | |
| rows = _cluster_rows(y_centers) | |
| for det, row in zip(detections, rows): | |
| det["row"] = row | |
| max_row = max(rows) | |
| for r in range(1, max_row + 1): | |
| row_dets = [(i, det) for i, det in enumerate(detections) if det["row"] == r] | |
| row_dets.sort(key=lambda x: x[1]["bbox"][0]) | |
| for col, (i, det) in enumerate(row_dets, start=1): | |
| detections[i]["column"] = col | |
| row_counts = {} | |
| for r in rows: | |
| row_counts[r] = row_counts.get(r, 0) + 1 | |
| print(f"[position] {len(detections)} detections -> {max_row} rows: {dict(sorted(row_counts.items()))}") | |
| return detections | |
| if __name__ == "__main__": | |
| import sys | |
| import json | |
| sys.path.insert(0, "src") | |
| from detector import detect_products | |
| path = sys.argv[1] if len(sys.argv) > 1 else "shelf_images/shelf_01.jpg" | |
| dets = detect_products(path) | |
| dets = assign_positions(dets) | |
| for d in dets: | |
| print(f" row={d['row']} col={d['column']} bbox={d['bbox']} conf={d['confidence']:.4f}") | |