Skip to content

1. Backend /scan-frame — endpoint pour crops pré-warpés ​

Publié le 2026-05-12

Première page utilisant la nouvelle convention YYYY-MM-DD-NNN-slug.md + frontmatter daté/numéroté. Les anciennes pages gardent leur nom non-préfixé (rétro-fit séparé).

Contexte ​

Le chemin Path #5 « Frame » de poc-tcg-collector (voir Path #5 — alternatives considérées) sépare la détection de carte (côté client, OpenCV sur Android) de l'embedding (côté backend, DINOv2 + pgvector). Quand le client a réussi à isoler la carte et à la dérouler en un crop net via une transformation perspective, on n'a plus besoin que le serveur refasse une détection de bordure — il suffit de l'embedder tel quel.

C'est exactement ce que fait POST /scan-frame, livré dans tcg-collector-backend#148 (mergé le 2026-05-12).

Différence clé avec /scan-lab ​

/scan-lab accepte un flag ?crop=1 (désactivé par défaut) qui déclenche une détection de bordure serveur via detectCardBounds avant d'embedder. Cette détection est utile quand le client envoie une photo brute, mais elle est contre-productive quand le client envoie un crop déjà parfait : la heuristique mord dans la carte au lieu de la laisser intacte, et le vecteur produit ne ressemble plus à ce qu'on a au moment de l'indexation.

/scan-frame ne fait jamais de détection serveur. La contrat est explicite : « le client a déjà warpé, embed-le tel quel ».

/scan-lab/scan-frame
Crop serveuroptionnel (?crop=1)jamais
Pipeline embedDINOv2 → pgvector ANNDINOv2 → pgvector ANN
Re-rank TCG dominantouioui
?tcg= filterouioui
Réponse JSONmême formatmême format (1:1)
Authpublique (POST allowlisté)publique (POST allowlisté)

Pourquoi le même format de réponse ​

Le mobile fait un fan-out parallèle sur 4 cellules (Lab / Compat / Tormknd / Combined) depuis 2026-05-10. PR 4b sur poc-tcg-collector (à venir) ajoute une 5ème cellule « Frame » qui consomme exactement la même forme JSON. Garder un contrat 1:1 entre /scan-lab et /scan-frame rend l'A/B test trivial : on compare la similarity du même catalogue, même indexation, même DB — la seule variable est « crop client » vs « photo brute ».

Détails d'implémentation ​

  • Nouveau fichier src/api/src/scan/routeFrame.ts (~150 lignes). Pipeline complet réutilisé depuis routeLab.ts : queryLab, projectCardDetails, toPgVector, LabMatchRow sont maintenant exportés et partagés. Le SQL pgvector n'est donc pas dupliqué — la divergence (si elle vient un jour) sera contenue dans le handler envelope.
  • Wiré dans src/api/src/index.ts à côté de scanLabRoutes, derrière la même gate embeddingsConfig (l'endpoint n'apparaît que si le sidecar d'embedding est branché).
  • /scan-frame ajouté à PUBLIC_POST_PATHS dans src/api/src/auth.ts — explicit security review point, comme /scan et /scan-lab.
  • 186/186 tests existants verts, tsc --noEmit clean.

Limites connues ​

  • Pas de test e2e dédié dans ce PR — les fixtures locales sont des photos non-warpées (conçues pour /scan-lab), donc les passer à /scan-frame donnerait des sims artificiellement basses. Un vrai test e2e nécessite des fixtures de crops déjà warpés, qui tomberont du pipeline OpenCV mobile une fois PR 4b mergée.
  • Pas de CHANGELOG.md dans le PR — par convention du repo backend (CLAUDE.md), les branches feat ne modifient pas CHANGELOG ; l'entrée arrive sur une branche docs/… séparée.

Pour aller plus loin ​