PSA (Professional Sports Authenticator) grades trading cards on a 1-10 scale. The difference between a PSA 9 and PSA 10
can be thousands of dollars – but to the naked eye, they look
identical.
Even more confusing: people regularly resubmit PSA 9s and get
10s on the second attempt. The grading process has inherent
subjectivity.
This makes it perfect for machine learning – finding patterns
in noisy, subjective data.
What PSA Graders Look For
Professional graders examine four aspects:
- Surface: Scratches, print lines, dots
- Corners: Whitening, bending, softness
- Edges: Chipping, wear, roughness
- Centering: Front/back alignment measured in percentages
At $30+ per card submission, guessing wrong gets expensive
fast.
The Approach: Ensemble Learning > Single Model
My first attempt was a single CNN trained on thousands of
graded cards. Accuracy was disappointing.
The problem: one network trying to learn everything at once
doesn't work well. So I built specialist networks:
class CardGradingEnsemble:
def init(self):
self.surface_model = SurfaceDefectDetector()
self.corner_model = CornerAnalyzer()
self.edge_model = EdgeWearClassifier()
self.centering_model = CenteringMeasurer()
Each model focuses on ONE aspect. Combined predictions with
weighted scoring.
Transfer Learning Beat Custom Architecture
I spent weeks building custom CNNs. Results were decent but
not great.
Then I tried EfficientNet with transfer learning:
backbone = models.efficientnet_b0(weights='IMAGENET1K_V1')
# Freeze early layers - they already know edges/textures
for param in backbone.features.parameters():
param.requires_grad = False
# Only train the final layers for card-specific patterns
backbone.classifier = nn.Sequential(
nn.Linear(num_features, 512),
nn.ReLU(),
nn.Linear(512, 2) # Binary: submit or don't
)
Accuracy improved significantly overnight.
Current Performance
The system catches most PSA 10s while avoiding obvious 8s. It
occasionally suggests grading some 9s, which isn't terrible
given how often 9s regrade as 10s anyway.
Not perfect, but useful for bulk submissions.
The Hardest Part: Edge Detection
Cards have different borders – black, yellow, holographic. My
edge model kept confusing card design with damage.
Solution:
def isolate_edge_wear(image, card_mask):
# Don't analyze the printed border design
# Just the physical card edge
physical_edge = extract_card_outline(card_mask)
edge_region = dilate(physical_edge, 3) - physical_edge
return image * edge_region
Simple morphological operations fixed weeks of poor
performance.
Production Challenges
Problem 1: Training data had perfect white backgrounds. Real
photos have kitchen tables, hands, random surfaces.
Solution: Aggressive augmentation + background removal
preprocessing.
Problem 2: High-res photos lose critical details when
resized. Surface scratches disappear at lower resolutions.
Solution: Smart cropping that analyzes regions at different
scales:
def multi_scale_analysis(image):
# Analyze corners at high resolution for whitening
corners = extract_corners_highres(image)
# Center can be lower res (centering measurement)
center = extract_center_lowres(image)
# Surface needs medium res to catch scratches
surface_patches = extract_surface_patches(image)
return combine_analyses(corners, center, surface_patches)
Real-World Application
We integrated this into https://pokeinvest.io where
collectors track their portfolios. The pre-grading
recommendations help users decide which cards are worth the
submission cost.
What's Next
Current limitations:
- Training on pristine photos – Need more real-world, poorly-lit, handheld photos
- Limited fine-tuning data – Getting verified PSA grades for training is expensive
- Clean backgrounds in training set – Real submissions have messy backgrounds that confuse the model
- Calibration drift – Model trained on one grader's standards, but PSA has many graders
Exploring:
- Data augmentation to simulate poor photo conditions
- Active learning to identify which cards would most improve the model
- Semi-supervised learning using ungraded cards
Key Takeaways
- Ensemble specialist models > One model for everything
- Transfer learning > Custom architectures (usually)
- Domain-specific preprocessing matters more than model complexity
- Even moderate accuracy can be commercially valuable
Working on similar computer vision problems? I'm curious
about other niche applications – especially in
collectibles/grading. Drop a comment.
The pre-grading tool is available at https://pokeinvest.io
for those tracking card portfolios.
Top comments (0)