Appearance
Inventaire des algorithmes de scan
Dernière mise à jour : 2026-05-23.
Cette page relie les choix visibles dans le scanner mobile aux vrais moteurs de reconnaissance derrière. Elle clarifie aussi où se trouvent les photos de fixture du POC et si elles participent au matching de production.
Résumé Exécutif
L'app a actuellement quatre familles de scan visibles :
| Source mobile | Type d'objet | Path runtime | Rôle |
|---|---|---|---|
SKN | Card | fan-out lab, compat, tormknd, combined, frame | Comparer tous les paths carte côte à côte. |
SKN | Slab | mêmes paths que Card | Comportement temporaire. Il n'y a pas encore de reconnaissance slab dédiée. |
SKN | Sealed | sealed seulement | Reconnaissance générique de scellés dans le backend SKN. |
Booster Lab | Booster | tcgapp2-booster seulement | Scanner booster façon Tormknd, isolé du path scellé SKN. |
La prise de photo et l'import d'image sont seulement des sources d'entrée. Une fois l'image choisie, les deux passent dans le même plan de scan actif pour le type d'objet sélectionné.
Algorithmes Actuels
Lab
- Mode mobile :
lab - Endpoint :
POST /scan-labsur l'API Node/Fastify SKN, port8080 - Objet : photo brute de carte TCG
- Pré-traitement : décodage JPEG côté serveur, détection OpenCV du contour de carte, crop/warp si une zone carte est trouvée
- Embedding : DINOv2 small, 384 dimensions, via le sidecar
embeddings - Index : PostgreSQL
pgvector, tablecard_embeddings_lab, index HNSW cosine - Catalogue : Pokémon, One Piece, Magic, Lorcana, Flesh and Blood
- Décision : signal visuel unique par embedding
- Force : path carte SKN rapide, large couverture catalogue et filtres SQL avant recherche de voisins
- Faiblesse : peut faire des faux positifs sur des cartes visuellement proches, car il ne confirme pas avec OCR ou pHash
C'est le path carte principal de SKN. Il teste proprement notre stack DINOv2 plus pgvector.
Compat
- Mode mobile :
compat - Endpoint :
POST /scan-compatsur le sidecar Pythontcg-vision, port8000 - Objet : photo brute de carte TCG
- Pré-traitement : correction perspective OpenCV façon Tormknd
- Embedding : DINOv2 small, 384 dimensions
- Index : FAISS
IndexFlatIPen mémoire, avec unfaiss_index.binpréconstruit - Catalogue : catalogue embarqué dans le sidecar, surtout Pokémon, One Piece et un peu de Lorcana
- Décision : signal visuel unique par embedding
- Force : path rollback/A-B utile, sans dépendance à pgvector
- Faiblesse : couverture plus étroite, artefact FAISS figé, filtrage plus faible que SQL
Compat est notre version forkée et intégrée du path tgc-app de ton ami. Il adapte la réponse au contrat mobile SKN et peut enrichir les résultats avec nos métadonnées.
Tormknd / tgc-app
- Mode mobile :
tormknd - Endpoint :
POST /scansur le sidecar Tormknd non modifié, port8002 - Objet : photo brute de carte TCG
- Pré-traitement : correction perspective OpenCV upstream
tgc-app - Embedding : DINOv2 small, 384 dimensions
- Index : FAISS
IndexFlatIPet mapping upstream - Décision : signal visuel unique par embedding
- Force : référence immuable pour comparer notre fork et nos changements backend
- Faiblesse : réponse non enrichie pour la production SKN, catalogue plus étroit, désactivé par défaut derrière le profil Docker
tormknd
Dans nos discussions on l'appelle parfois tcg-app, mais le repo local de référence est tgc-app. On le traite comme repo de référence et on ne le modifie pas pour le comportement SKN.
Combined
- Mode mobile :
combined - Endpoint :
POST /scansur l'API Node/Fastify SKN, port8080 - Objet : photo brute de carte TCG
- Pré-traitement : crop serveur, puis plusieurs signaux indépendants
- Signaux : pHash, OCR, embedding DINOv2
- Index embedding : même table pgvector
card_embeddings_labqueLab - Décision : cascade multi-signal via
decideMatch - Force : meilleur path debug pour comprendre pourquoi une photo est acceptée ou refusée
- Faiblesse : plus strict et plus lent que
Lab; peut rejeter une image acceptée par un path embedding-only
Combined est l'ancien path production. Il peut matcher via pHash quasi-duplicate, identifiants imprimés lus par OCR, ou rescue par embedding. Il est très utile quand l'app doit expliquer une ambiguïté.
Frame
- Mode mobile :
frame - Endpoint :
POST /scan-framesur l'API Node/Fastify SKN, port8080 - Objet : photo de carte après rectification côté client
- Pré-traitement : OpenCV natif Android
detectQuadpuiswarpQuad, puis upload du crop redressé - Embedding : DINOv2 small, 384 dimensions
- Index : même table pgvector
card_embeddings_labqueLab - Décision : signal visuel unique par embedding
- Force : isole l'impact du crop/warp fait sur le téléphone
- Faiblesse : lent ou fragile si le détecteur quadrilatère côté client rate la carte ou croppe la mauvaise zone
Frame est le Path #5. C'est un path lab pour mesurer si le téléphone peut produire un meilleur crop canonique que le backend.
Sealed
- Mode mobile :
sealed - Endpoint :
POST /scan-sealed-labsur l'API Node/Fastify SKN, port8080 - Objet : scellés Pokémon génériques : ETB, display, booster bundle, collection box, blister, booster pack
- Pré-traitement : image brute par défaut; crop scellé optionnel quand l'app a un indice de forme booster
- Embedding : DINOv2 base, 768 dimensions, via le sidecar dédié
sealed-embeddings - Index : PostgreSQL
pgvector, tablesealed_product_embeddings_vitb_lab - Sources de référence : images internes
ptcg-assetspar défaut ; les photos de référence scellées SKN-owned sont en pause pour les scans normaux et doivent être demandées explicitement - Signaux en plus : OCR optionnel, filtres type produit/langue, reranking des candidats
- Détecteur optionnel : segmentation YOLO si
models/yolo/booster_seg.ptexiste; sinon fallback OpenCV foreground crop - Force : la couverture scellée large de
ptcg-assetspeut être interrogée dans le même backend que les cartes, tandis que les références SKN-owned restent disponibles pour le debug et les benchmarks contrôlés - Faiblesse : l'accuracy terrain dépend encore de la couverture des références, qualité photo, variantes produit, variantes de langue et qualité du détecteur
Les scans scellés génériques ne forcent volontairement pas booster_pack, parce qu'un booster visible dans une box peut sinon dominer le match et masquer le vrai produit.
tcg-app2 Booster
- Mode mobile :
tcgapp2-booster - Endpoint :
POST /scan/boostersur le service FastAPI localtcg-app2, port8003 - Objet : booster packs
- Pré-traitement : contrôle de flou, segmentation booster YOLOv11-Seg si un modèle est présent, fallback contour OpenCV sinon
- Embedding : DINOv2 ViT-B/14, 768 dimensions, entrée 518 px
- OCR : EasyOCR, concentré sur les métadonnées utiles de booster : langue, éditeur, édition, année et indice TCG
- Vector store : Qdrant, en stockage local embarqué ou mode serveur
- Source de référence :
tcg-app2/data/boosters_metadata.jsonplus images boosters indexées parscripts/build_booster_index.py - Décision : recherche vectorielle avec filtres metadata issus de l'OCR
- Force : plus proche de l'architecture booster spécifique de ton ami, utile pour les A-B tests booster-only
- Faiblesse : stack séparée, index séparé, scope booster-only, qualité très dépendante du modèle de segmentation booster et du catalogue indexé
Ce path est isolé volontairement du scellé générique SKN. C'est un comparateur lab, pas un remplacement de tous les scellés.
Fallback on-device
- Runtime : app React Native, sans backend
- Entrées :
src/scan/cardIndex.jsonlocal et stratégies scanner - Signaux : pHash, histogramme couleur, OCR/MLKit local quand disponible, ensemble de stratégies debug
- Force : fonctionne quand les endpoints backend échouent
- Faiblesse : moins précis et moins complet que la reconnaissance backend
C'est une couche fallback et diagnostic, pas le scanner de production principal.
Reconnaissance slab
- État actuel : l'app a un type
Slab, mais il utilise actuellement les cinq mêmes paths queCard - Ce qui manque : détection slab, crop du label, reconnaissance de l'organisme de grading, OCR de la note, OCR du numéro de certification, politique de vérification dédiée
Aujourd'hui une photo de slab peut donc identifier la carte visible si le crop est assez bon, mais elle ne reconnaît pas vraiment PSA 10, BGS, CGC ou le cert number.
Ce Qui Est Entraîné vs Indexé
| Composant | Entraîné par nous aujourd'hui ? | Ce qui change quand on l'améliore |
|---|---|---|
| Embeddings cartes DINOv2 | Non. Modèle pré-entraîné figé. | Rebuild des embeddings cartes si le modèle, le crop ou la géométrie d'entrée change. |
| Embeddings scellés DINOv2 | Non. Modèle pré-entraîné figé. | Rebuild des embeddings scellés si le modèle, le crop ou la géométrie d'entrée change. |
| FAISS / pgvector / Qdrant | Non. Ce sont des index vectoriels, pas des modèles de reconnaissance. | Réindexer les vecteurs et les métadonnées. |
| Détecteur YOLO scellé/booster | Oui, dès qu'on fournit des poids. | Meilleur crop objet avant embedding, surtout sur vraies photos téléphone. |
| OCR | Non. OCR off-the-shelf plus règles. | Améliorer régions texte, dictionnaires, filtres et reranking. |
ptcg-assets / images SKN-owned | Pas des modèles. Ce sont des références. Les scans normaux utilisent ptcg-assets par défaut ; SKN-owned est en pause sauf demande explicite. | Ajouter une couverture référence plus propre, licenciée et mieux labellisée. |
Où Interviennent les Photos du POC
Il y a deux concepts différents à ne pas mélanger : les fixtures de test du POC et les références scellées backend.
Dans poc-tcg-collector
Le repo POC contient des images de fixture pour les tests et le fallback local :
| Path | Nombre observé | Rôle |
|---|---|---|
__fixtures__/cards/real/*.jpg | 5 | Fixtures cartes réelles pour les tests d'intégration autour de demoScanDb. |
__fixtures__/tcgdex/images/*.jpg | 13 | Fixtures de reproductibilité pour les anciens tests d'ingest TCGdex. |
__fixtures__/cards/card-a-stripes.jpg, card-b-checker.jpg, card-c-gradient.jpg | 3 | Fixtures synthétiques pour déterminisme pHash et histogramme. |
src/scan/demoScanDb.ts | lignes générées | Petite DB démo legacy utilisée par les tests. Le scanner écran production ne la lit plus directement. |
src/scan/cardIndex.json | catalogue JSON | Catalogue fallback/debug bundlé par Metro, généré depuis les données backend. |
fichiers racine Screenshot_*.jpg | non suivis | Screenshots de debug visuel seulement. Ce ne sont pas des références. |
Ces images du POC ne nourrissent pas /scan-lab, /scan-compat, /scan, /scan-frame, /scan-sealed-lab ou le matching production tcg-app2 au runtime. Elles servent surtout aux tests, à la reproductibilité et au fallback local de diagnostic.
Les "10 photos" du dernier travail scellé
Les 10 photos scellées possédées par SKN sont côté backend, pas dans le POC :
text
tcg-collector-backend/src/api/tests/fixtures/sealed-samples/images/labeled/Elles sont listées dans :
text
tcg-collector-backend/src/api/tests/fixtures/sealed-samples/manifest.jsonElles interviennent à deux endroits :
- Échantillons benchmark :
npm run benchmark:sealedles poste vers/scan-sealed-labet mesure l'accuracy exact/top-K. - Références privées SKN-owned : l'ingest Docker monte
sealed-samplesviaSKN_SEALED_REFERENCES_SOURCE, mais depuis le 2026-05-23 le défaut compose estSEALED_INCLUDE_SKN_REFERENCES=false. Si elles sont activées manuellement, elles peuvent être embed danssealed_product_embeddings_vitb_labcomme référencesskn-owned:*.
Ce deuxième point est utile, mais il faut l'interpréter correctement : matcher ces mêmes 10 photos après les avoir indexées valide le câblage et le rappel de doublons. Ça ne prouve pas que le scanner généralise à de nouvelles photos téléphone, angles, lumières, langues ou variantes jamais vues.
Les requêtes /scan-sealed-lab normales utilisent maintenant source=ptcg-assets par défaut, même si d'anciennes lignes skn-owned existent encore dans Postgres. Utiliser source=skn-owned uniquement pour debugger les références bootstrap possédées, ou source=all pour des comparaisons A/B contrôlées.
Pour valider le terrain avec des images jamais ingérées, il faut utiliser le dossier séparé :
text
tcg-collector-backend/src/api/tests/fixtures/sealed-eval/Ces images ne doivent pas être ingérées comme références. Elles servent à mesurer la généralisation.
Guide de Lecture Pratique
- Si
Labest bon etCombinedrefuse : le voisin visuel est probablement bon, mais OCR/pHash ne confirment pas. - Si
Frameest pire queLab: le crop client est probablement faux ou trop lent. - Si
CompatouTormkndest meilleur queLab: comparer pré-traitement et couverture d'index avant de changer les seuils. - Si
tcg-app2 Boosterest meilleur queSealedsur boosters : sa segmentation booster/Qdrant/OCR aide vraiment. - Si
Sealedretourne de mauvais produits proches : inspecter top-K, filtres OCR, type produit, langue, présence de la référence attendue, et vérifier si une requête debug a explicitement activésource=skn-ownedousource=all. - Si une photo a été prise depuis un écran PC : attendre une accuracy plus faible, les reflets et le moiré peuvent dominer l'embedding.