Skip to content

Repository files navigation

Card Front Grader (Next.js + OpenCV.js)

Front-only trading card photo grading assistant that:

  • detects the card, perspective-rectifies it
  • estimates front centering (L/R and T/B) and applies the PSA-style centering caps from your rubric
  • runs heuristic flaw detection (the additive flaw-point model is project-specific, not an official PSA formula) and converts measurements into the severity/points table you provided
  • renders a visual overlay (inner border box, border bands, midlines, flaw summary text)
  • batch-processes multiple images in one session and exports results.json and results.csv

This is an assistant tool, not an official PSA grader. Photo quality heavily affects outputs.


Requirements

  • Node.js 18+

Install

npm install

Run (dev)

npm run dev

Open the local dev server in your browser.

Optional OpenAI review

The app can run a server-side AI review after the deterministic local grade completes. This does not replace the centering math or grade-cap logic; it only flags obvious missed defects, likely false positives, image-quality issues, and manual-review needs.

Configure the server-side key in .env.local:

OPENAI_API_KEY="sk-..."
OPENAI_GRADER_MODEL="gpt-5-nano"

The browser calls POST /api/grade-ai; the key stays server-side. The default model is gpt-5-nano for lowest cost, and the route sends the rectified image at high detail plus the source image at low detail for context.

How it works

1) Card detection + rectification

  • Canny edges + contour search for the largest 4-point polygon
  • Perspective warp to a fixed size close to a standard card (default 640x890) for consistent pixel-to-cm conversion

2) Centering

  • On the rectified image, compute edge-energy per row/column
  • Find prominent inner edges (border → artwork boundary) to estimate an inner content rectangle
  • Borders in pixels:
    • leftPx = innerLeft
    • rightPx = width - innerRight
    • topPx = innerTop
    • bottomPx = height - innerBottom
  • Convert to percentages:
    • leftPct = leftPx / (leftPx + rightPx) and similarly for others
  • Report L/R ratio and T/B ratio
  • Use the worse of the two as the front centering score, then apply the rubric caps:
    • worst side <= 55% → PSA 10 cap
    • <= 65% → PSA 9 cap
    • <= 70% → PSA 8 cap
    • <= 75% → PSA 7 cap
    • <= 80% → PSA 6 cap
    • <= 85% → PSA 5/4 cap (your text repeats 85/15 for both)
    • <= 90% → PSA 3/2/1.5 cap

3) Flaws (heuristics)

Measured in the rectified image using px→cm conversion (6.35cm x 8.89cm assumed (standard 2.5 x 3.5 in card)).

  • Scratch: Hough line segments in the interior; sum lengths → severity by your cm cutoffs
  • Scuffing: local texture anomaly area (|gray - blur|) → cm² thresholds
  • Edgewear: edge energy within a perimeter strip → approximate perimeter wear length
  • Indentation: small high-contrast blobs (Laplacian) → mm² thresholds
  • Grime: low-saturation + dark blobs in perimeter strip → mm² / cm² thresholds
  • Bend: long crease-like lines (Hough, large minLineLength) → cm thresholds
  • Corner rounding: corner patch distance transform → average corner radius (px) thresholds

Total points are mapped to a condition and a grade cap. Front-image estimate = worse of:

  • centering cap
  • visible-flaw cap

Image quality changes confidence/scoreability, not card condition. Severe quality failures return UNSCORABLE instead of forcing an artificially low grade.

4) UNSCORABLE

The app returns UNSCORABLE with reasons and a lower confidence when:

  • card cannot be found
  • border/inner content boundary cannot be detected
  • image is too blurry, too glary, or too skewed

Tuning

All thresholds live in lib/grader.ts under export const TUNING = { ... }. This includes:

  • Canny thresholds
  • blur/glare cutoffs
  • border detection parameters
  • flaw severities and measurement thresholds
  • card physical size used for px→cm conversion

Optional server persistence

By default, everything runs locally in the browser. If you want to persist results to a temp folder on the Node server (dev/Node runtime), POST to:

  • POST /api/save
  • GET /api/results
  • GET /api/results/:id

Files go to:

  • /tmp/card-grader-results/<id>/

Limitations / known gaps

  • Cannot reliably detect: micro-scratches, gloss loss, subtle print registration issues, extremely faint stains.
  • Border-based centering assumes there is a detectable border-to-art boundary; borderless designs will often be UNSCORABLE.
  • Glare and strong shadows can create false positives for scuffs/grime.

License

MIT (you can replace this).

About

Calculates the centering of a trading card in order to ensure a high grade when sent to be authenticated and graded.

Resources

Stars

1 star

Watchers

1 watching

Forks

Used by

Contributors

Languages