Spaces:
Running on Zero
Running on Zero
Upload grid_seeded.py
Browse filestilt angle tolerance increased to 30 degree
- grid_seeded.py +59 -2
grid_seeded.py
CHANGED
|
@@ -108,7 +108,19 @@ WIN_PERIODS = 6.2 # window width in ruling periods: 4 for the block,
|
|
| 108 |
# the rest for phase search and the angle crop
|
| 109 |
PER_WORK = WORK / WIN_PERIODS # a period is ALWAYS this many work px
|
| 110 |
HP_W = int(0.25 * PER_WORK) | 1
|
| 111 |
-
ANG_RANGE =
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 112 |
BOOT_FRAC = 0.55 # v3c's fixed window -- used only to seed the fixed point
|
| 113 |
MAX_ITER = 4
|
| 114 |
REPEAT_TOL = 0.03 # period change below this ends the fixed point
|
|
@@ -686,10 +698,31 @@ def _finish(g, s1, verify):
|
|
| 686 |
}
|
| 687 |
|
| 688 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 689 |
def detect_from_seed(image, seed_xy, verify=True, period_hint=None,
|
| 690 |
-
lock=False, want_debug=False
|
|
|
|
| 691 |
"""One tap -> the four corners of the surrounding 4x4 counting block.
|
| 692 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 693 |
Parameters
|
| 694 |
----------
|
| 695 |
image : ndarray, HxW grey or HxWx3 RGB. Use the FULL-resolution image;
|
|
@@ -705,6 +738,30 @@ def detect_from_seed(image, seed_xy, verify=True, period_hint=None,
|
|
| 705 |
|
| 706 |
Returns a JSON-safe dict. Always check ``ok`` before using ``corners``.
|
| 707 |
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 708 |
g = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY) if image.ndim == 3 else image
|
| 709 |
g = np.ascontiguousarray(g)
|
| 710 |
H, W = g.shape[:2]
|
|
|
|
| 108 |
# the rest for phase search and the angle crop
|
| 109 |
PER_WORK = WORK / WIN_PERIODS # a period is ALWAYS this many work px
|
| 110 |
HP_W = int(0.25 * PER_WORK) | 1
|
| 111 |
+
ANG_RANGE = 30.0 # deg, rotation half-range. Widened from 14: phone-through-
|
| 112 |
+
# eyepiece photos are routinely tilted 15-17 deg (measured on
|
| 113 |
+
# the 20260901 cos7 set), which sat AT/OUTSIDE the old +/-14 box.
|
| 114 |
+
# The coarse tilt search then pinned at the boundary or locked
|
| 115 |
+
# onto a spurious negative-angle autocorrelation peak, and every
|
| 116 |
+
# downstream period and line offset came out wrong -- the fit was
|
| 117 |
+
# then (correctly) rejected by QC, so the tap "failed". Set to 30
|
| 118 |
+
# for headroom against steeper future tilts. Low-tilt photos are
|
| 119 |
+
# unaffected: their autocorrelation peak is in the same place, and
|
| 120 |
+
# widening only scans extra angles that score lower.
|
| 121 |
+
# HARD CEILING: keep this < 45. A square grid repeats every 90 deg,
|
| 122 |
+
# so a tilt past 45 aliases into the perpendicular axis (the two
|
| 123 |
+
# ruling directions swap) and the reported angle becomes ambiguous.
|
| 124 |
BOOT_FRAC = 0.55 # v3c's fixed window -- used only to seed the fixed point
|
| 125 |
MAX_ITER = 4
|
| 126 |
REPEAT_TOL = 0.03 # period change below this ends the fixed point
|
|
|
|
| 698 |
}
|
| 699 |
|
| 700 |
|
| 701 |
+
# ---- multi-start retry --------------------------------------------------
|
| 702 |
+
# One tap fits ONE analysis window. On a faint, cluttered or tilted grid that
|
| 703 |
+
# window can land on a patch where the period fixed point wanders and the fit
|
| 704 |
+
# is (correctly) rejected by QC -- while the SAME block, seeded a little to one
|
| 705 |
+
# side, fits cleanly. Measured on the 20260901 cos7 set: a single centre tap
|
| 706 |
+
# failed on 2 of 4 quadrants, yet every quadrant fit at some nearby tap. A user
|
| 707 |
+
# re-tapping is doing exactly this by hand. So on failure we retry from a small
|
| 708 |
+
# ring of nearby seeds and keep the first that PASSES THE SAME QC gate -- this
|
| 709 |
+
# never loosens acceptance, it only gives the fitter more starting points.
|
| 710 |
+
RETRY_RING = 8 # seeds tried on failure, evenly spaced on a ring
|
| 711 |
+
RETRY_RADIUS_FRAC = 0.08 # ring radius as a fraction of the short image side.
|
| 712 |
+
# ~0.5-0.8 of a ruling period on these photos, so the
|
| 713 |
+
# retries stay well inside the tapped block and cannot
|
| 714 |
+
# jump to a neighbouring one (block ~4 periods across).
|
| 715 |
+
|
| 716 |
+
|
| 717 |
def detect_from_seed(image, seed_xy, verify=True, period_hint=None,
|
| 718 |
+
lock=False, want_debug=False,
|
| 719 |
+
retries=RETRY_RING, retry_radius_frac=RETRY_RADIUS_FRAC):
|
| 720 |
"""One tap -> the four corners of the surrounding 4x4 counting block.
|
| 721 |
|
| 722 |
+
Fits at the tap; if that is rejected, retries from a ring of nearby seeds
|
| 723 |
+
(see RETRY_RING above) and returns the first fit that passes QC, otherwise
|
| 724 |
+
the original rejection. Set retries=0 for the old single-shot behaviour.
|
| 725 |
+
|
| 726 |
Parameters
|
| 727 |
----------
|
| 728 |
image : ndarray, HxW grey or HxWx3 RGB. Use the FULL-resolution image;
|
|
|
|
| 738 |
|
| 739 |
Returns a JSON-safe dict. Always check ``ok`` before using ``corners``.
|
| 740 |
"""
|
| 741 |
+
import math
|
| 742 |
+
out = _detect_from_seed_once(image, seed_xy, verify, period_hint,
|
| 743 |
+
lock, want_debug)
|
| 744 |
+
if out.get("ok") or retries <= 0:
|
| 745 |
+
return out
|
| 746 |
+
H, W = image.shape[:2]
|
| 747 |
+
r = retry_radius_frac * min(H, W)
|
| 748 |
+
sx0, sy0 = float(seed_xy[0]), float(seed_xy[1])
|
| 749 |
+
for k in range(int(retries)):
|
| 750 |
+
a = 2.0 * math.pi * k / int(retries)
|
| 751 |
+
s2 = (sx0 + r * math.cos(a), sy0 + r * math.sin(a))
|
| 752 |
+
if not (0 <= s2[0] < W and 0 <= s2[1] < H):
|
| 753 |
+
continue
|
| 754 |
+
alt = _detect_from_seed_once(image, s2, verify, period_hint,
|
| 755 |
+
lock, want_debug)
|
| 756 |
+
if alt.get("ok"):
|
| 757 |
+
alt["retry_seed"] = [int(s2[0]), int(s2[1])]
|
| 758 |
+
return alt
|
| 759 |
+
return out
|
| 760 |
+
|
| 761 |
+
|
| 762 |
+
def _detect_from_seed_once(image, seed_xy, verify=True, period_hint=None,
|
| 763 |
+
lock=False, want_debug=False):
|
| 764 |
+
"""Single-window fit at exactly `seed_xy`. See detect_from_seed."""
|
| 765 |
g = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY) if image.ndim == 3 else image
|
| 766 |
g = np.ascontiguousarray(g)
|
| 767 |
H, W = g.shape[:2]
|