36 lines
1.6 KiB
Python
36 lines
1.6 KiB
Python
from photoflow.core.models import ClusterMember
|
|
from app.clustering import _epoch, _median
|
|
|
|
|
|
def coverage_members(candidate, assets_by_id, *, outlier_factor: float = 8.0) -> list:
|
|
member_ids = [i for i in candidate.member_ids if i in assets_by_id]
|
|
members = sorted((assets_by_id[i] for i in member_ids), key=lambda a: a.taken_at)
|
|
epochs = [_epoch(a.taken_at) for a in members]
|
|
gaps = [b - a for a, b in zip(epochs, epochs[1:])]
|
|
base = _median(gaps) if gaps else 0.0
|
|
|
|
out = []
|
|
for idx, a in enumerate(members):
|
|
is_outlier = False
|
|
if candidate.seed_tag and base > 0:
|
|
left = epochs[idx] - epochs[idx - 1] if idx > 0 else 0
|
|
right = epochs[idx + 1] - epochs[idx] if idx < len(members) - 1 else 0
|
|
nearest = min([g for g in (left, right) if g > 0], default=0)
|
|
if nearest > outlier_factor * base:
|
|
is_outlier = True
|
|
out.append(ClusterMember(
|
|
cluster_id=0, immich_id=a.immich_id,
|
|
member_confidence=candidate.confidence, is_outlier=is_outlier,
|
|
included=True, flagged_coverage=False))
|
|
|
|
if candidate.seed_tag and candidate.start_at and candidate.end_at:
|
|
mset = set(member_ids)
|
|
for a in sorted(assets_by_id.values(), key=lambda x: x.taken_at):
|
|
if a.immich_id in mset:
|
|
continue
|
|
if candidate.start_at <= a.taken_at <= candidate.end_at:
|
|
out.append(ClusterMember(
|
|
cluster_id=0, immich_id=a.immich_id, member_confidence=0.0,
|
|
is_outlier=False, included=False, flagged_coverage=True))
|
|
return out
|