Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d9a40952c4 |
+2
-14
@@ -246,22 +246,10 @@ def score_bitmap_rescored(
|
|||||||
return np.maximum(0.0, out).astype(np.float32)
|
return np.maximum(0.0, out).astype(np.float32)
|
||||||
|
|
||||||
|
|
||||||
_HAS_NP_BITCOUNT = hasattr(np, "bitwise_count")
|
|
||||||
|
|
||||||
|
|
||||||
def popcount_density(spread: np.ndarray) -> np.ndarray:
|
def popcount_density(spread: np.ndarray) -> np.ndarray:
|
||||||
"""Conta bit set per pixel.
|
|
||||||
|
|
||||||
Order:
|
|
||||||
1) Numba JIT parallel (preferito: piu veloce su 1080p, 0.5ms vs 1.6ms)
|
|
||||||
2) numpy.bitwise_count (NumPy 2.0+, SIMD ma single-thread)
|
|
||||||
3) Fallback numpy bit-shift puro
|
|
||||||
"""
|
|
||||||
spread_c = np.ascontiguousarray(spread, dtype=np.uint8)
|
|
||||||
if HAS_NUMBA:
|
if HAS_NUMBA:
|
||||||
return _jit_popcount_density(spread_c)
|
return _jit_popcount_density(np.ascontiguousarray(spread, dtype=np.uint8))
|
||||||
if _HAS_NP_BITCOUNT:
|
# Fallback
|
||||||
return np.bitwise_count(spread_c).astype(np.float32, copy=False)
|
|
||||||
H, W = spread.shape
|
H, W = spread.shape
|
||||||
out = np.zeros((H, W), dtype=np.float32)
|
out = np.zeros((H, W), dtype=np.float32)
|
||||||
for b in range(8):
|
for b in range(8):
|
||||||
|
|||||||
+5
-2
@@ -220,8 +220,11 @@ def auto_tune(template_bgr: np.ndarray, mask: np.ndarray | None = None) -> dict:
|
|||||||
else:
|
else:
|
||||||
min_score = 0.45
|
min_score = 0.45
|
||||||
|
|
||||||
# angle step: 5° default; se simmetria, mantengo step ma range ridotto
|
# angle step adattivo (Halcon-style): atan(2/max_side) deg, clampato.
|
||||||
angle_step = 5.0
|
# Template grande → step fine (rotazione minima visibile su perimetro).
|
||||||
|
# Template piccolo → step grosso (over-sampling = sprecato).
|
||||||
|
max_side = max(h, w)
|
||||||
|
angle_step = float(np.clip(np.degrees(np.arctan2(2.0, max_side)), 1.0, 8.0))
|
||||||
|
|
||||||
result = {
|
result = {
|
||||||
"backend": "line",
|
"backend": "line",
|
||||||
|
|||||||
+24
-5
@@ -197,12 +197,31 @@ class LineShapeMatcher:
|
|||||||
n = int(np.floor((s1 - s0) / self.scale_step)) + 1
|
n = int(np.floor((s1 - s0) / self.scale_step)) + 1
|
||||||
return [float(s0 + i * self.scale_step) for i in range(n)]
|
return [float(s0 + i * self.scale_step) for i in range(n)]
|
||||||
|
|
||||||
|
def _auto_angle_step(self) -> float:
|
||||||
|
"""Step angolare derivato da dimensione template (Halcon-style).
|
||||||
|
|
||||||
|
Formula: step ≈ atan(2 / max_side) gradi. Garantisce che la
|
||||||
|
rotazione minima produca uno spostamento di ≥2 px sul perimetro
|
||||||
|
del template (sotto sample il matching coarse perde candidati).
|
||||||
|
Clampato in [0.5°, 10°].
|
||||||
|
"""
|
||||||
|
max_side = max(self.template_size) if self.template_size != (0, 0) else 64
|
||||||
|
step = math.degrees(math.atan2(2.0, float(max_side)))
|
||||||
|
return float(np.clip(step, 0.5, 10.0))
|
||||||
|
|
||||||
|
def _effective_angle_step(self) -> float:
|
||||||
|
"""Risolve angle_step_deg gestendo modalità auto (<=0)."""
|
||||||
|
if self.angle_step_deg <= 0:
|
||||||
|
return self._auto_angle_step()
|
||||||
|
return self.angle_step_deg
|
||||||
|
|
||||||
def _angle_list(self) -> list[float]:
|
def _angle_list(self) -> list[float]:
|
||||||
a0, a1 = self.angle_range_deg
|
a0, a1 = self.angle_range_deg
|
||||||
if self.angle_step_deg <= 0 or a0 >= a1:
|
step = self._effective_angle_step()
|
||||||
|
if step <= 0 or a0 >= a1:
|
||||||
return [float(a0)]
|
return [float(a0)]
|
||||||
n = int(np.floor((a1 - a0) / self.angle_step_deg))
|
n = int(np.floor((a1 - a0) / step))
|
||||||
return [float(a0 + i * self.angle_step_deg) for i in range(n)]
|
return [float(a0 + i * step) for i in range(n)]
|
||||||
|
|
||||||
# --- Training ------------------------------------------------------
|
# --- Training ------------------------------------------------------
|
||||||
|
|
||||||
@@ -415,7 +434,7 @@ class LineShapeMatcher:
|
|||||||
if original_score is not None and original_score >= 0.99:
|
if original_score is not None and original_score >= 0.99:
|
||||||
return (angle_deg, original_score, cx, cy)
|
return (angle_deg, original_score, cx, cy)
|
||||||
if search_radius is None:
|
if search_radius is None:
|
||||||
search_radius = self.angle_step_deg / 2.0
|
search_radius = self._effective_angle_step() / 2.0
|
||||||
|
|
||||||
h, w = template_gray.shape
|
h, w = template_gray.shape
|
||||||
sw = max(16, int(round(w * scale)))
|
sw = max(16, int(round(w * scale)))
|
||||||
@@ -802,7 +821,7 @@ class LineShapeMatcher:
|
|||||||
ang_f, score_f, cx_f, cy_f = self._refine_angle(
|
ang_f, score_f, cx_f, cy_f = self._refine_angle(
|
||||||
spread0, bit_active_full, self.template_gray, cx_f, cy_f,
|
spread0, bit_active_full, self.template_gray, cx_f, cy_f,
|
||||||
var.angle_deg, var.scale, mask_full,
|
var.angle_deg, var.scale, mask_full,
|
||||||
search_radius=self.angle_step_deg / 2.0,
|
search_radius=self._effective_angle_step() / 2.0,
|
||||||
original_score=score,
|
original_score=score,
|
||||||
)
|
)
|
||||||
if verify_ncc:
|
if verify_ncc:
|
||||||
|
|||||||
Reference in New Issue
Block a user