merge: hysteresis edge linking
This commit is contained in:
+38
-2
@@ -241,13 +241,49 @@ class LineShapeMatcher:
|
|||||||
bins = np.clip(bins, 0, N_BINS - 1)
|
bins = np.clip(bins, 0, N_BINS - 1)
|
||||||
return mag, bins
|
return mag, bins
|
||||||
|
|
||||||
|
def _hysteresis_mask(self, mag: np.ndarray) -> np.ndarray:
|
||||||
|
"""Edge mask con hysteresis (Halcon Contrast='auto' two-threshold).
|
||||||
|
|
||||||
|
Procedura:
|
||||||
|
1. seed = pixel con mag >= strong_grad (edge nitidi)
|
||||||
|
2. weak = pixel con mag >= weak_grad (edge candidati)
|
||||||
|
3. Espande seed dentro weak via componenti connesse 8-vicini
|
||||||
|
|
||||||
|
Risultato: edge debole connesso a edge forte viene PROMOSSO a
|
||||||
|
feature valida; edge debole isolato (rumore) viene SCARTATO.
|
||||||
|
|
||||||
|
Riduce sia falsi-positivi (rumore puro) sia falsi-negativi
|
||||||
|
(continuita' interrotta su edge sottili a basso contrasto).
|
||||||
|
"""
|
||||||
|
weak = (mag >= self.weak_grad).astype(np.uint8)
|
||||||
|
strong = (mag >= self.strong_grad).astype(np.uint8)
|
||||||
|
# connectedComponentsWithStats su weak: per ogni componente,
|
||||||
|
# se contiene almeno un pixel strong → tutto componente accettato
|
||||||
|
n_lab, labels = cv2.connectedComponents(weak, connectivity=8)
|
||||||
|
if n_lab <= 1:
|
||||||
|
return strong.astype(bool)
|
||||||
|
# Label dei pixel strong: marker per componenti da accettare
|
||||||
|
strong_labels = np.unique(labels[strong > 0])
|
||||||
|
strong_labels = strong_labels[strong_labels > 0] # 0 = bg
|
||||||
|
if len(strong_labels) == 0:
|
||||||
|
return strong.astype(bool)
|
||||||
|
# Mask = appartiene a label di componente "promosso"
|
||||||
|
keep = np.isin(labels, strong_labels)
|
||||||
|
return keep
|
||||||
|
|
||||||
def _extract_features(
|
def _extract_features(
|
||||||
self, mag: np.ndarray, bins: np.ndarray, mask: np.ndarray | None,
|
self, mag: np.ndarray, bins: np.ndarray, mask: np.ndarray | None,
|
||||||
) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
|
) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
|
||||||
if mask is not None:
|
if mask is not None:
|
||||||
mag = np.where(mask > 0, mag, 0)
|
mag = np.where(mask > 0, mag, 0)
|
||||||
strong = mag >= self.strong_grad
|
# Halcon-style edge selection: hysteresis tra weak_grad e strong_grad.
|
||||||
ys, xs = np.where(strong)
|
# Edge weak connessi a edge strong sono inclusi (continuita' bordi).
|
||||||
|
# Se weak_grad >= strong_grad → fallback a soglia singola strong.
|
||||||
|
if self.weak_grad < self.strong_grad:
|
||||||
|
edge = self._hysteresis_mask(mag)
|
||||||
|
else:
|
||||||
|
edge = mag >= self.strong_grad
|
||||||
|
ys, xs = np.where(edge)
|
||||||
if len(xs) == 0:
|
if len(xs) == 0:
|
||||||
return (np.zeros(0, np.int32),) * 3
|
return (np.zeros(0, np.int32),) * 3
|
||||||
vals = mag[ys, xs]
|
vals = mag[ys, xs]
|
||||||
|
|||||||
Reference in New Issue
Block a user