Easy-to-manage touch screens
Free, open-source touch screens. Guided USB setup, a drag-and-drop editor and local control. Made for Home Assistant.
Aponte a câmera para uma carta Pokémon TCG e descubra qual é, com preço de referência e links para Liga e MYP.
Translation pending
LifestyleTypeScript · PythonGitHub @gabrielcraveiroOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
Loading the full README. The saved excerpt is shown below.
Scanner de cartas Pokémon TCG pela câmera do celular, direto no navegador. Aponte a câmera para a carta e o site diz qual é, com preço de referência e links de busca para a Liga Pokémon e a MYP Cards. Pokémon TCG card scanner for the phone browser. Point the camera at a card to identify it.Read the full README ↗
Scanner de cartas Pokémon TCG pela câmera do celular, direto no navegador. Aponte a câmera para a carta e o site diz qual é, com preço de referência e links de busca para a Liga Pokémon e a MYP Cards.
Pokémon TCG card scanner for the phone browser. Point the camera at a card to identify it.
Cartas em japonês ou coreano são identificadas pela arte e caem na versão em inglês equivalente. O servidor publicado também tem algumas coleções japonesas inteiras (ex.: Storm Emeralda), com a imagem da carta japonesa.
câmera → retícula viva (scanic, segue os 4 cantos da carta)
→ foto nítida → YOLO acha os 4 cantos → corte com correção de perspectiva
→ POST /recognize (servidor)
DINOv2-S @336 → similaridade de cosseno contra ~20 mil cartas
→ menos a penalidade das cartas "ímã" (as que parecem com qualquer foto)
→ desempate por pHash quando dois prints têm a mesma arte
→ só quando o match é ambíguo, OCR (RapidOCR): o nome no topo da carta e, se ainda faltar, o número no rodapé em alta resolução
→ resultado + preço de referência + links de busca
O reconhecimento roda num servidor, não no celular. Rodar o modelo no navegador travava a tela e levava segundos por carta. No servidor quente, um scan leva ~0,5s.
O site publicado usa o nosso servidor. Para usar o reconhecimento no seu projeto, veja Usar o reconhecimento no seu projeto.
Decisões de arquitetura e o porquê de cada uma: docs/arquitetura.md.
npm install
cp .env.example .env # ajuste VITE_RECOGNIZE_URL se usar outro servidor
npm run dev # http://localhost:5173
A câmera exige HTTPS, mas localhost é exceção. Para testar no celular, publique o build (veja abaixo) ou use um túnel HTTPS.
npm run typecheck
npm run build # gera dist/, um site estático
O dist/ é estático e roda em qualquer host (Vercel, Netlify, GitHub Pages, Cloudflare Pages). Configure as variáveis no build:
| Variável | Uso |
|---|---|
VITE_RECOGNIZE_URL | URL do servidor de reconhecimento. Vazio = o servidor publicado (https://cartinhas-recognize.fly.dev). |
VITE_FEEDBACK_URL | Opcional. Link do botão "Mandar feedback". |
VITE_TELEMETRY_URL | Opcional. Endpoint que recebe a telemetria anônima (POST, corpo JSON em texto puro). Vazio = sem telemetria. |
Não existe um modelo treinado por nós. O modelo é o DINOv2-small público (Meta, Apache-2.0), sem ajuste. O que faz o reconhecimento funcionar é o que está em volta dele:
Tudo isso está no código de server/, com os scripts para gerar o modelo e o índice. Há duas formas de usar:
Siga o server/README.md. O atalho é baixar da última release o modelo, o índice e a penalidade prontos, e pular direto para rodar. Do zero:
VITE_RECOGNIZE_URL.Diferenças para o servidor publicado:
Um build sem VITE_RECOGNIZE_URL já usa o nosso servidor (https://cartinhas-recognize.fly.dev), que aceita chamadas de qualquer site. Serve para testar e para uso pessoal, com estes limites:
Para um site com tráfego de verdade, rode o seu servidor (opção 1).
POST {VITE_RECOGNIZE_URL}/recognize, multipart/form-data:
file: JPEG da carta (lado maior até 900px).pre: 1 quando a imagem já é só a carta recortada. Sem ele, o servidor detecta a carta na foto.footer (opcional, só com pre=1): JPEG dos 28% de baixo da mesma carta, em resolução cheia (até 1200px de largura). O servidor lê o número da carta nesta imagem. No file reduzido, o número tem ~12px e o OCR não lê.Resposta:
{
"card": { "api_id": "sv07-158", "name": "Lapras ex", "number": "158", "set_id": "sv07",
"set_name": "Stellar Crown", "printed_total": 142,
"image_url": "https://assets.tcgdex.net/en/sv/sv07/158/low.webp", "cos": 0.91 },
"confident": true,
"not_card": false,
"ocr": { "number": "158", "total": "142", "match": true, "name": "Lapras ex 220", "name_match": true },
"candidates": [ "… até 5 cartas no mesmo formato de card …" ]
}
confident: true quando o número lido confere, quando a semelhança é alta com folga sobre a 2ª candidata, ou quando o nome lido confere e a semelhança é alta com folga sobre as outras cartas desse nome. Com false, mostre as candidatas para a pessoa escolher.not_card: true quando a foto não parece carta nenhuma (mão, mesa, tecido). O card continua vindo, mas não deve ser mostrado.candidates: a carta escolhida primeiro, depois a ordem do ranking do servidor, que desconta as cartas "ímã" e põe na frente as cartas do nome lido. Por isso o cos nem sempre aparece em ordem decrescente.ocr: o que o servidor leu na carta. name é o texto lido no topo e name_match diz se ele casou com o nome de alguma carta. Os dois vêm vazios quando o OCR não rodou (match decisivo ou quadro sem carta).400 quando o file não é uma imagem; 413 quando a imagem tem mais de 16 milhões de pixels.api_id é o id do TCGdex, na maioria das cartas: https://api.tcgdex.net/v2/en/cards/{api_id} traz o resto dos dados. As exceções são as coleções que só o servidor publicado tem (ex.: m6-jp-87).GET /health acorda o servidor. O site chama essa rota ao abrir, porque a máquina suspende quando fica ociosa e o primeiro scan depois disso pode levar ~20s.
Uma foto da carta, de preferência só a carta (sem pre, o servidor procura a carta na foto). Troque SERVER pelo seu servidor (veja Usar o reconhecimento no seu projeto).
curl
curl -F "[email protected]" https://cartinhas-recognize.fly.dev/recognize
JavaScript (navegador ou Node 18+)
const SERVER = "https://cartinhas-recognize.fly.dev";
// `foto` é um Blob/File: um <input type="file">, ou canvas.toBlob(cb, "image/jpeg", 0.8).
async function reconhecer(foto) {
const form = new FormData();
form.append("file", foto, "carta.jpg");
const res = await fetch(`${SERVER}/recognize`, { method: "POST", body: form });
const r = await res.json();
if (r.not_card) return null;
return { carta: r.card, certeza: r.confident, candidatas: r.candidates };
}
Python
import requests
SERVER = "https://cartinhas-recognize.fly.dev"
with open("carta.jpg", "rb") as f:
r = requests.post(f"{SERVER}/recognize", files={"file": ("carta.jpg", f, "image/jpeg")}, timeout=60).json()
if r["not_card"]:
print("Não é uma carta")
else:
c = r["card"]
print(c["name"], c["set_name"], c["number"], "(certeza)" if r["confident"] else "(confira)")
for alt in r["candidates"]:
print(" -", alt["api_id"], alt["cos"])
Saída com a foto do Altaria de Surging Sparks:
Altaria Surging Sparks 134 (certeza)
- sv08-134 0.916
- swsh3.5-49 0.732
- swsh12-143 0.733
- swsh7-106 0.716
- sv03-160 0.746
A primeira chamada com a máquina dormindo leva até ~20s. Use um timeout de pelo menos 40s, ou chame GET /health antes.
A foto da carta vai para o servidor só para ser identificada. Ela fica no máximo ~6 horas em disco para depuração e depois é apagada. O site não tem conta nem cookie. A lista de cartas fica só no seu aparelho.
A versão publicada coleta telemetria anônima para medir o acerto do scanner: um ID aleatório por aparelho (gerado no localStorage), o país (vindo da CDN), a origem do acesso, os eventos de scan (cartas candidatas, confiança, tempo, "não é essa") e os dados técnicos da câmera que o navegador informa (nome da lente, modo e distância de foco, resolução). Não vai foto, IP nem nada que identifique a pessoa. O código está em src/telemetry.ts. Ele só envia quando o build define VITE_TELEMETRY_URL, então um build seu não manda nada.