Skip to content

techishthoughts-org/object-detection

Repository files navigation

MTG Card Object Detection

Detect and identify Magic: The Gathering cards — train a model locally on your Mac or on a cloud GPU, then point your webcam at a card to see its name, oracle text, and price in real time.

The core model is a function: detect(image) -> [{class, bbox, confidence}, ...]. It takes an image and returns a list of detected regions, each with a class label, bounding box coordinates, and a confidence score. The full identification pipeline chains detection → OCR → Scryfall API lookup to go from a raw image to complete card data.

Best performance: 96.7% mAP50 / 77.7% mAP50-95 on Dataset v1 (Roboflow project version 8; 891 images) — the model correctly finds and identifies card regions ~97% of the time.

Dataset versions

Canonical lineage used throughout this repo:

  • Dataset v1 = Roboflow project version 8 export used in early baseline runs (891 images: train ~623 / valid ~178 / test ~90).
  • Dataset v2 = expanded export used by current setup script (4,065 images: train 3,761 / valid 223 / test 81).
Dataset version Image count Split sizes (train/valid/test) Date Training run docs
v1 891 ~623 / ~178 / ~90 2026-03-28 (documented) v1 baseline metrics
v2 4,065 3,761 / 223 / 81 2026-03-28 (documented) v2 experiment metrics

The 7 Detection Classes

A "class" is like an enum value — an integer ID (0-6) mapped to a string name:

ID Class What It Is Typical Size
0 art The illustration area Large
1 card The full card boundary Very large
2 description Rules text box Large
3 mana-cost Mana symbols (top right) Small
4 power Power/toughness (bottom right) Small
5 tags Type line (e.g., "Creature — Dragon") Medium
6 title Card name at the top Medium

Quick Start

Six commands from zero to card identification:

# 1. Install dependencies
uv sync

# 2. Download the dataset (get your free key at roboflow.com)
export ROBOFLOW_API_KEY="your_key_here"
uv run python scripts/01_setup_dataset.py

# 3. Explore the data (see what you're working with)
uv run python scripts/02_explore_dataset.py

# 4. Train the model (~28 hrs on M3 CPU, or ~29 min on RunPod — see Step 3)
uv run python scripts/03_train.py

# 5. Hold a card up to your webcam!
uv run python scripts/06_live_detect.py

# 6. Identify cards with full info (name, price, oracle text)
uv run python scripts/10_live_identify.py

Step-by-Step Guide

Step 1: Download Dataset

export ROBOFLOW_API_KEY="your_key_here"
uv run python scripts/01_setup_dataset.py

What happens: Downloads Dataset v2 from Roboflow (4,065 annotated MTG card images), pre-split into train (3,761), validation (223), and test (81) sets. Each image has a matching .txt label file with bounding box coordinates for every region. The three splits serve different purposes — train is where the model learns, validation monitors for overfitting during training, and test provides a final unbiased evaluation.

Output: data/mtg-detection/ directory with train/, valid/, test/ subdirectories.

Deep dive: docs/concepts.md#data-splits | docs/architecture.md#file-format-reference

Step 2: Explore Dataset

uv run python scripts/02_explore_dataset.py

What happens: Generates visual summaries of the dataset — a 3x3 grid of randomly sampled training images with bounding boxes drawn, a class distribution chart, and data quality checks (missing labels, empty files).

Output: outputs/exploration/sample_grid.png, outputs/exploration/class_distribution.png

Deep dive: docs/concepts.md#bounding-boxes-and-yolo-format

Step 3: Train the Model

You have two options: local training (free, slower) or cloud GPU training (fast, ~$1-6).

Option A: Local Training (CPU)

uv run python scripts/03_train.py

What happens: Loads yolo11n.pt (a model pretrained on 80 everyday object types) and fine-tunes it on the MTG card dataset. This is transfer learning — like forking a well-tested open-source library and customizing it for your use case. The model's 2.6 million float parameters are iteratively adjusted over up to 100 epochs (passes through the data) to minimize prediction errors. Training stops early if validation performance plateaus for 20 consecutive epochs. Data augmentation includes geometric transforms (rotation up to 15°, perspective, shear), multi-scale training (320-960px), color shifts, mosaic, and mixup — creating variations the model hasn't seen to handle real-world webcam conditions like tilted cards, varying distances, and background clutter.

Training uses CPU (not GPU) because PyTorch 2.10 MPS has known bugs on macOS 26. CPU training on Apple Silicon is still fast thanks to high-bandwidth unified memory.

Output: models/mtg-detect-best.pt (the trained model), runs/mtg-detect/ (training logs)

Option B: Cloud GPU Training (RunPod)

For higher accuracy, train on a cloud GPU with larger models and higher resolution. Three scripts are available, each representing an iteration of the training approach:

Script Model Resolution Epochs Est. Cost Est. Time
train_cloud.py yolo11n 640 100 ~$0.73 ~29 min
train_cloud_v2.py yolo11n/s/m 1280 150-200 $0.90-$3.50 1.5-6 hrs
train_cloud_v3.py yolo11m 1280 250 ~$5.60 ~6 hrs

Prerequisites:

  • A RunPod account with balance (~$5-15)
  • runpodctl CLI installed: brew install runpod/runpodctl/runpodctl
  • API key configured: runpodctl doctor (prompts for key and saves it)

Step-by-step:

# 1. Create a GPU pod (RTX 4090 recommended)
runpodctl pod create \
  --name "mtg-training" \
  --gpu-id "NVIDIA GeForce RTX 4090" \
  --template-id runpod-torch-v240 \
  --volume-in-gb 20 \
  --cloud-type SECURE

# 2. Get SSH connection info (wait ~1 min for pod to start)
runpodctl ssh info <pod-id>

# 3. Upload the training script
scp -i ~/.runpod/ssh/RunPod-Key-Go -P <port> \
  -o StrictHostKeyChecking=no \
  scripts/train_cloud_v3.py root@<pod-ip>:/workspace/

# 4. Install dependencies on the pod
ssh -i ~/.runpod/ssh/RunPod-Key-Go -p <port> \
  -o StrictHostKeyChecking=no root@<pod-ip> \
  "pip install ultralytics roboflow"

# 5. Start training (runs in background so SSH disconnect is safe)
ssh -i ~/.runpod/ssh/RunPod-Key-Go -p <port> \
  -o StrictHostKeyChecking=no root@<pod-ip> \
  "cd /workspace && ROBOFLOW_API_KEY=your_key \
   nohup python train_cloud_v3.py > training.log 2>&1 &"

# 6. Check progress
ssh -i ~/.runpod/ssh/RunPod-Key-Go -p <port> \
  -o StrictHostKeyChecking=no root@<pod-ip> \
  "tail -5 /workspace/training.log"

# 7. Download trained model when complete
scp -i ~/.runpod/ssh/RunPod-Key-Go -P <port> \
  -o StrictHostKeyChecking=no \
  root@<pod-ip>:/workspace/runs/detect/v3-final/weights/best.pt \
  models/mtg-detect-v3.pt

# 8. Stop and remove the pod to stop billing
runpodctl pod stop <pod-id>
runpodctl pod remove <pod-id>

The <pod-id>, <port>, and <pod-ip> values come from the output of steps 1 and 2.

Live dashboard: In a separate terminal, run:

uv run tensorboard --logdir runs/

Open http://localhost:6006 to watch training metrics update in real time — loss curves, precision/recall, mAP, and learning rate schedules.

Option C: Full Cloud Pipeline (RunPod)

Run the entire pipeline (train + validate + predict + identify) in a single script on a cloud GPU:

# 1. Create pod and SCP the script (same pod setup as Option B)
scp -i ~/.runpod/ssh/RunPod-Key-Go -P <port> \
  -o StrictHostKeyChecking=no \
  scripts/run_cloud.py root@<pod-ip>:/workspace/

# 2. Install dependencies
ssh -i ~/.runpod/ssh/RunPod-Key-Go -p <port> \
  -o StrictHostKeyChecking=no root@<pod-ip> \
  "pip install ultralytics roboflow rapidocr-onnxruntime opencv-python-headless matplotlib"

# 3. Run the full pipeline
ssh -i ~/.runpod/ssh/RunPod-Key-Go -p <port> \
  -o StrictHostKeyChecking=no root@<pod-ip> \
  "cd /workspace && ROBOFLOW_API_KEY=your_key \
   nohup python run_cloud.py --all > pipeline.log 2>&1 &"

# 4. Download all outputs when complete
scp -i ~/.runpod/ssh/RunPod-Key-Go -P <port> -r \
  -o StrictHostKeyChecking=no \
  root@<pod-ip>:/workspace/outputs/ ./outputs/

You can also run individual steps or customize training:

# Just train and validate with a larger model
python run_cloud.py --setup --train --validate --model-size m --imgsz 1280 --epochs 200 --batch 4

# Validate an existing model
python run_cloud.py --setup --validate --model /workspace/runs/detect/train/weights/best.pt

# Download test images and run identification
python run_cloud.py --setup --download-test-images --identify

Available flags: --setup, --explore, --train, --validate, --predict, --download-test-images, --identify, --all. Training flags: --model-size (n/s/m), --imgsz, --epochs, --batch, --patience, --resume.

Deep dive: docs/concepts.md#what-is-training | docs/concepts.md#transfer-learning | docs/parameters.md

Step 4: Validate

# Default: validate with the baseline model
uv run python scripts/04_validate.py

# Validate a specific model at its training resolution
uv run python scripts/04_validate.py --model models/mtg-detect-v3.pt --imgsz 1280

# With test-time augmentation (flips + scales for extra accuracy)
uv run python scripts/04_validate.py --model models/mtg-detect-v3.pt --imgsz 1280 --tta

What happens: Runs the trained model on the validation set (images it never saw during training) and computes standard detection metrics. Produces a per-class metrics table, confusion matrix, and precision-recall curves.

Options:

  • --model: Path to model weights (default: models/mtg-detect-best.pt)
  • --imgsz: Validation image size — should match training resolution (default: 640)
  • --tta: Enable test-time augmentation for +0.5-1% mAP gain at the cost of 3-5x slower inference

Output: outputs/validation/metrics.json (or outputs/validation-1280/, outputs/validation-1280-tta/ for non-default settings), plus 6 diagnostic plots (confusion matrices, PR/F1/P/R curves)

Deep dive: docs/metrics-guide.md

Step 5: Predict on Images

# On test split images
uv run python scripts/05_predict.py

# On your own image
uv run python scripts/05_predict.py --source path/to/card.jpg

# Adjust detection sensitivity
uv run python scripts/05_predict.py --conf 0.3 --iou 0.5

What happens: Runs inference — the trained model processes each image in a single forward pass, producing ~8,400 candidate bounding boxes. These are filtered by confidence threshold (--conf, default 0.25) and de-duplicated via Non-Maximum Suppression (--iou, default 0.45), leaving ~5-15 final detections per card.

Output: outputs/predictions/run/ with annotated images and prediction labels

Deep dive: docs/concepts.md#how-inference-works | docs/parameters.md#inference-parameters

Step 6: Live Detection

uv run python scripts/06_live_detect.py

What happens: Opens your webcam and runs the model on every frame in real time (~15-30 FPS). Hold a card in front of the camera and watch bounding boxes appear around detected regions. When a card title is detected, OCR reads the name and fetches the English reference image from Scryfall, displaying it in the top-right corner for visual comparison.

Controls: q quit | s screenshot | c toggle confidence labels | r toggle reference card

Troubleshooting camera issues: docs/troubleshooting.md#step-6-live-detection

Step 7: Download Multilingual Test Images

# Default: 10 creature cards per language, all 11 languages
uv run python scripts/07_download_test_images.py

# Customize
uv run python scripts/07_download_test_images.py --per-language 20 --languages en ja de fr

What happens: Downloads creature card images from the Scryfall API (free, no authentication) across 11 languages. These are completely independent images the model has never seen — ideal for testing how well it generalizes.

Output: test_images/{lang}/ directories with card images, plus test_images/manifest.json with metadata (card name, language, set).

Step 8: Fix Misclassifications

# Export model predictions in Label Studio format
uv run python scripts/08_export_for_correction.py

# Then fix annotations in Label Studio
pip install label-studio && label-studio start

What happens: Runs the trained model on your test images and exports predictions as YOLO-format annotations that Label Studio can import. You fix any mistakes in Label Studio's visual editor, export the corrections, and merge them back into training data for retraining.

Output: outputs/correction/ with images, labels, and Label Studio config files.

Full workflow: docs/annotation-correction.md

Step 9: Identify Cards

# Identify a single card image
uv run python scripts/09_identify_card.py test_images/en/card_001.jpg

# Process a directory of card images
uv run python scripts/09_identify_card.py test_images/en/ --save

# Adjust detection sensitivity
uv run python scripts/09_identify_card.py card.jpg --conf 0.3

What happens: Combines all three stages of the full identification pipeline:

  1. YOLO detection — finds the 7 card regions (title, art, mana-cost, etc.)
  2. OCR — crops the title region and reads the card name using RapidOCR
  3. Scryfall lookup — queries the Scryfall API with the OCR'd name to fetch full card data

Prints complete card information: name, mana cost, type line, oracle text, power/toughness, rarity, set, price, and Scryfall link.

Output: Console output with card details. With --save, annotated images in outputs/identified/.

Step 10: Live Card Identification

uv run python scripts/10_live_identify.py

What happens: Real-time webcam card identification with a side info panel. Hold a card in front of the camera and the system detects regions, OCRs the title, looks up the card on Scryfall, and displays a detailed panel showing the card's reference image, name, mana cost, type line, oracle text, P/T, rarity, set, and price — all in real time.

OCR runs every ~1 second to avoid performance impact. Results are cached so re-scanning the same card is instant.

Controls: q quit | s screenshot | c toggle confidence labels | i toggle info panel

Step 11: Deploy Model to Roboflow

uv run python -c "
from roboflow import Roboflow
import os, shutil

# Prepare directory structure Roboflow expects
os.makedirs('models/deploy/weights', exist_ok=True)
shutil.copy('models/mtg-detect-best.pt', 'models/deploy/weights/best.pt')

rf = Roboflow(api_key=os.environ['ROBOFLOW_API_KEY'])
project = rf.workspace('magic-the-gathering').project('mtg-detection-cixf6')
version = project.version(8)
version.deploy('yolov11', 'models/deploy')

shutil.rmtree('models/deploy')
print('Model deployed to Roboflow!')
"

What happens: Uploads your trained model weights to Roboflow's hosted inference API. Once deployed, you can run inference via the Roboflow API without needing local compute.

Step 12: Web Application

ROBOFLOW_API_KEY=your_key uv run uvicorn web.app:app --reload --host 0.0.0.0 --port 8000

What happens: Launches a web UI at http://localhost:8000 with two modes:

  • Upload mode — drop, paste, or select an image for full 4-stage identification (detect → OCR → Scryfall → DINOv2 art matching)
  • Live camera mode — point your webcam at a card for real-time identification with detection overlay

The web app uses Roboflow's hosted inference API (requires a deployed model from Step 11) and adds DINOv2 art matching to identify the exact printing — not just the card name, but which set and collector number.

Full details: docs/solution.md

Results

Training Experiments

Experiment Model Resolution mAP50 mAP50-95 Cost Notes
Local CPU yolo11n 640 95.1% 71.3% Free Dataset v1 (~28 hrs on M3 CPU)
v1 (RunPod) yolo11n 640 96.7% 77.7% $0.73 Dataset v1; best overall balance
v2-quick yolo11n 1280 96.2% 74.8% ~$0.90 Dataset v2; card class collapsed
v3-final yolo11m 1280 96.1% 77.2% ~$5.60 Dataset v2; better small objects, worse card

Best Model (v1 — yolo11n @ 640, Dataset v1, reported 2026-03-28)

Class Precision Recall mAP50 mAP50-95
art 0.963 0.972 0.982 0.918
card 0.967 0.969 0.959 0.880
description 0.955 0.961 0.974 0.851
mana-cost 0.954 0.847 0.959 0.710
power 0.824 0.919 0.937 0.703
tags 0.984 0.964 0.983 0.651
title 0.986 0.949 0.974 0.727
ALL 0.948 0.940 0.967 0.777

Reading these numbers (Dataset v1, reported 2026-03-28):

  • Precision (0.948): 94.8% of detections are correct (low false alarm rate)
  • Recall (0.940): 94% of real regions are found (few misses)
  • mAP50 (0.967): Overall detection quality at 50% overlap — 96.7% is excellent
  • mAP50-95 (0.777): Stricter overlap requirement — 77.7% reflects that small regions (mana-cost, power) are harder to localize with pixel precision

Large regions (art, card, description) score highest because small positioning errors barely affect their IoU. Small regions (mana-cost, power) score lower on strict metrics because even tiny pixel errors cause proportionally large IoU drops.

Lessons Learned (Dataset v1 vs v2 experiments, reported 2026-03-28)

  1. Resolution increase helps small objects — mana-cost and power gained +5-11 points at 1280px
  2. Larger models don't always win — yolo11m at 1280 destabilized the card class (88% → 48.5% mAP50-95), likely due to copy_paste augmentation and training dynamics
  3. The nano model is surprisingly competitive — yolo11n at 640 remains the best balanced model at a fraction of the cost
  4. Annotation quality is the ceiling — the 19-point gap between mAP50 (96.7%) and mAP50-95 (77.7%) indicates annotation noise limits further gains

Full experiment log: docs/training-v2-status.md | Metrics analysis: docs/metrics-guide.md

Project Structure

object-detection/
├── scripts/
│   ├── 01_setup_dataset.py         # Download dataset from Roboflow API
│   ├── 02_explore_dataset.py       # Visualize data & check quality
│   ├── 03_train.py                 # Train YOLOv11n locally (transfer learning)
│   ├── 04_validate.py              # Evaluate with full metrics (+TTA support)
│   ├── 05_predict.py               # Run inference on images
│   ├── 06_live_detect.py           # Real-time webcam detection
│   ├── 07_download_test_images.py  # Download multilingual test images
│   ├── 08_export_for_correction.py # Export predictions for Label Studio
│   ├── 09_identify_card.py         # Identify cards: YOLO + OCR + Scryfall
│   ├── 10_live_identify.py         # Live webcam card identification
│   ├── run_cloud.py                # Unified cloud pipeline (all steps)
│   ├── train_cloud.py              # Cloud GPU training v1 (yolo11n @ 640)
│   ├── train_cloud_v2.py           # Cloud GPU training v2 (preset experiments)
│   └── train_cloud_v3.py           # Cloud GPU training v3 (yolo11m @ 1280)
├── data/                     # Dataset (gitignored)
│   └── mtg-detection/
│       ├── data.yaml         # Class names + split paths
│       ├── train/            # 3,761 images + labels
│       ├── valid/            # 223 images + labels
│       └── test/             # 81 images + labels
├── models/                   # Trained weights (gitignored)
│   ├── mtg-detect-best.pt   # Best model — v1 yolo11n (~5.2 MB)
│   ├── mtg-detect-v2-balanced.pt # v2-balanced yolo11s (~19 MB)
│   └── mtg-detect-v3.pt     # v3 yolo11m (~41 MB)
├── runs/                     # Training logs (gitignored, created by training)
│   └── detect/               # YOLO validation/training outputs
├── outputs/                  # Generated results (gitignored)
│   ├── exploration/          # Dataset visualization plots
│   ├── validation/           # v1 metrics + diagnostic plots
│   ├── validation-1280/      # v3 1280px validation results
│   ├── validation-1280-tta/  # v3 1280px with TTA results
│   ├── predictions/          # Annotated prediction images
│   ├── correction/           # Label Studio export for fixing
│   └── screenshots/          # Webcam screenshots
├── web/                      # Web application
│   ├── app.py                # FastAPI server (4-stage pipeline)
│   ├── services/
│   │   ├── detection.py      # Roboflow hosted inference client
│   │   ├── ocr.py            # RapidOCR title extraction
│   │   ├── scryfall.py       # Card lookup + printings
│   │   └── image_match.py    # DINOv2 art matching
│   └── static/               # Frontend (vanilla JS)
│       ├── index.html        # Upload + live camera tabs
│       ├── css/style.css
│       └── js/               # upload.js, live.js, card-panel.js
├── docs/                     # Deep-dive documentation
│   ├── solution.md           # Complete solution overview
│   ├── concepts.md           # Every ML concept via SWE analogies
│   ├── architecture.md       # Pipeline data flow & file formats
│   ├── parameters.md         # Every parameter explained
│   ├── metrics-guide.md      # How to read your results
│   ├── troubleshooting.md    # Error messages & fixes
│   ├── annotation-correction.md # Fix mistakes & retrain guide
│   ├── training-v2-status.md # Cloud training experiment log
│   ├── training-strategies.md # Augmentation & hyperparameter deep-dive
│   ├── training-cost-analysis.md # GPU cost comparison & estimates
│   └── manual-vs-roboflow.md # Annotation workflow comparison
├── test_images/              # Multilingual test card images
├── pyproject.toml            # Dependencies
└── README.md                 # You are here

Full architecture diagram: docs/architecture.md

Requirements

  • Python: 3.12 (auto-installed by uv if needed)
  • uv: Install with curl -LsSf https://astral.sh/uv/install.sh | sh
  • Roboflow account: Free at roboflow.com — needed for API key
  • Webcam: Built-in or external (for step 6)

For local training

  • Mac: Apple Silicon (M1/M2/M3/M4) with 8 GB+ RAM (16 GB+ recommended)

For cloud training

  • RunPod account: runpod.io with ~$5-15 balance
  • runpodctl: brew install runpod/runpodctl/runpodctl, then runpodctl doctor to set up API key

Troubleshooting

Problem Likely Cause Quick Fix
ModuleNotFoundError Not using uv run Use uv run python scripts/...
Camera not working macOS permissions System Settings > Privacy > Camera > enable your terminal
Training NaN loss MPS device bug Ensure device="cpu" in 03_train.py
No detections Confidence too high Try --conf 0.1 to see all predictions
Low mAP Too few epochs Increase patience or try cloud training
Scryfall 429 error Rate limited Script auto-retries; just re-run (skips existing)
Reference card not showing Missing OCR dependency Run uv sync to install rapidocr-onnxruntime
RunPod "no resources" GPU sold out Try --cloud-type SECURE or a different GPU
OCR reads wrong name Low-res title region Hold card closer to camera; try --conf 0.3
Info panel blank No title detected yet Hold card steady for 1-2 seconds
RunPod host key error New pod IP Add -o StrictHostKeyChecking=no to SSH/SCP

Full guide: docs/troubleshooting.md

Going Further

  • Your own photos: Drop card images into test_images/ and run step 5
  • Cloud training: Use train_cloud_v2.py --preset balanced for a middle-ground experiment
  • Custom classes: Annotate your own dataset on Roboflow with whatever regions you want
  • Card identification: Already built — run 09_identify_card.py or 10_live_identify.py to detect, OCR, and look up cards on Scryfall
  • Web app: Run the FastAPI web app for browser-based detection with DINOv2 art matching — see Step 12
  • Solution overview: Read docs/solution.md for the complete system documented end-to-end
  • Annotation refinement: The path to 90%+ mAP50-95 requires tighter bounding boxes on small objects (mana-cost, power, tags) — see docs/training-v2-status.md

Glossary

Term Meaning Deep Dive
Model Function with 2.6M configurable float parameters concepts.md#what-is-a-model
Training Optimization loop adjusting weights to minimize errors concepts.md#what-is-training
Transfer learning Starting from pretrained weights instead of scratch concepts.md#transfer-learning
Epoch One complete pass through all training data concepts.md#epochs-batches-and-learning-rate
Batch size Images processed together per weight update concepts.md#epochs-batches-and-learning-rate
Learning rate Step size for weight updates concepts.md#epochs-batches-and-learning-rate
Early stopping Stop training when performance plateaus concepts.md#early-stopping
Overfitting Model memorizes training data instead of learning concepts.md#data-splits
Data augmentation Random image modifications during training concepts.md#data-augmentation
Inference Running the trained model on new images concepts.md#how-inference-works
Confidence Model's certainty score (0-1) for each detection concepts.md#how-inference-works
NMS Removing duplicate overlapping detections concepts.md#how-inference-works
IoU Overlap ratio between two boxes (intersection/union) concepts.md#how-inference-works
TTA Test-time augmentation — running inference with flips/scales for better accuracy concepts.md#data-augmentation
Bounding box Rectangle marking an object's location concepts.md#bounding-boxes-and-yolo-format
Precision Of all detections, what % are correct metrics-guide.md#precision
Recall Of all real objects, what % were found metrics-guide.md#recall
mAP50 Overall detection quality metric metrics-guide.md#map50
Loss Scoring function: 0 = perfect, higher = worse concepts.md#what-is-training
Gradient How much each weight contributed to the error concepts.md#what-is-training
Weights The float numbers that define model behavior concepts.md#what-is-a-model

Tech Stack

Component Tool
Model YOLOv11 (Ultralytics) — nano, small, or medium
Dataset Roboflow Universe
Local training CPU (Apple Silicon)
Cloud training RunPod (RTX 4090)
Inference MPS (Metal Performance Shaders)
Model hosting Roboflow Deploy
Python 3.12 via uv
OCR RapidOCR (ONNX Runtime)
Card data Scryfall API
Visualization OpenCV + Matplotlib

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors