Spaces:
Sleeping
Sleeping
github-actions[bot] commited on
Commit ·
484609a
1
Parent(s): 6ed4e84
deploy: backend bundle from 27fd3a957d41862b7f48c17d609eb808d9c5c3f2
Browse files
.gitattributes
CHANGED
|
@@ -10,3 +10,10 @@
|
|
| 10 |
Code/Frontend/public/icon-32.png -filter -diff -merge -text
|
| 11 |
Code/Frontend/public/icon-192.png -filter -diff -merge -text
|
| 12 |
Code/Frontend/public/logo-128.png -filter -diff -merge -text
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 10 |
Code/Frontend/public/icon-32.png -filter -diff -merge -text
|
| 11 |
Code/Frontend/public/icon-192.png -filter -diff -merge -text
|
| 12 |
Code/Frontend/public/logo-128.png -filter -diff -merge -text
|
| 13 |
+
|
| 14 |
+
# Exclude model weight files from LFS (tracked via .gitignore instead)
|
| 15 |
+
*.pth -filter -diff -merge -text
|
| 16 |
+
*.pt -filter -diff -merge -text
|
| 17 |
+
|
| 18 |
+
# Exclude sample/test images from LFS (small files used for testing)
|
| 19 |
+
Datasets/Samples/** -filter -diff -merge -text
|
Code/Backend/api_server.py
CHANGED
|
@@ -191,7 +191,7 @@ import time
|
|
| 191 |
|
| 192 |
from src.dl.prediction_pipeline import create_dl_pipeline
|
| 193 |
from src.preprocessing.image_utils import check_image_quality
|
| 194 |
-
from src.preprocessing.card_detector import detect_card_boundary_strict
|
| 195 |
|
| 196 |
# Import validators
|
| 197 |
import sys
|
|
@@ -754,7 +754,7 @@ async def authenticate_card(request: AuthenticateRequest):
|
|
| 754 |
# Layer 1: Stricter geometric validation for Pokemon cards
|
| 755 |
# Strategy: Reject cards with incorrect aspect ratio or low saturation
|
| 756 |
# Pokemon cards have aspect ratio ~0.716 (63mm x 88mm)
|
| 757 |
-
front_corners =
|
| 758 |
front_img,
|
| 759 |
min_area_ratio=0.001, # Very low - card can be small in frame
|
| 760 |
max_area_ratio=0.999, # Almost entire image is OK for webcam shots
|
|
@@ -762,7 +762,7 @@ async def authenticate_card(request: AuthenticateRequest):
|
|
| 762 |
solidity_threshold=0.60, # Lower solidity for cards with background
|
| 763 |
fill_ratio_threshold=0.40 # Lower fill ratio to be more permissive
|
| 764 |
)
|
| 765 |
-
back_corners =
|
| 766 |
back_img,
|
| 767 |
min_area_ratio=0.001,
|
| 768 |
max_area_ratio=0.999, # Almost entire image is OK for webcam shots
|
|
|
|
| 191 |
|
| 192 |
from src.dl.prediction_pipeline import create_dl_pipeline
|
| 193 |
from src.preprocessing.image_utils import check_image_quality
|
| 194 |
+
from src.preprocessing.card_detector import detect_card_boundary_strict, detect_card_boundary_with_hand
|
| 195 |
|
| 196 |
# Import validators
|
| 197 |
import sys
|
|
|
|
| 754 |
# Layer 1: Stricter geometric validation for Pokemon cards
|
| 755 |
# Strategy: Reject cards with incorrect aspect ratio or low saturation
|
| 756 |
# Pokemon cards have aspect ratio ~0.716 (63mm x 88mm)
|
| 757 |
+
front_corners = detect_card_boundary_with_hand(
|
| 758 |
front_img,
|
| 759 |
min_area_ratio=0.001, # Very low - card can be small in frame
|
| 760 |
max_area_ratio=0.999, # Almost entire image is OK for webcam shots
|
|
|
|
| 762 |
solidity_threshold=0.60, # Lower solidity for cards with background
|
| 763 |
fill_ratio_threshold=0.40 # Lower fill ratio to be more permissive
|
| 764 |
)
|
| 765 |
+
back_corners = detect_card_boundary_with_hand(
|
| 766 |
back_img,
|
| 767 |
min_area_ratio=0.001,
|
| 768 |
max_area_ratio=0.999, # Almost entire image is OK for webcam shots
|
Code/Model/src/dl/prediction_pipeline.py
CHANGED
|
@@ -20,6 +20,7 @@ from .model import CardAuthModel
|
|
| 20 |
from .transforms import get_eval_transforms
|
| 21 |
from ..preprocessing import (
|
| 22 |
detect_card_boundary_strict,
|
|
|
|
| 23 |
crop_to_card,
|
| 24 |
resize_image,
|
| 25 |
check_image_quality,
|
|
|
|
| 20 |
from .transforms import get_eval_transforms
|
| 21 |
from ..preprocessing import (
|
| 22 |
detect_card_boundary_strict,
|
| 23 |
+
detect_card_boundary_with_hand,
|
| 24 |
crop_to_card,
|
| 25 |
resize_image,
|
| 26 |
check_image_quality,
|
Code/Model/src/preprocessing/__init__.py
CHANGED
|
@@ -2,7 +2,14 @@
|
|
| 2 |
Preprocessing module for card image processing
|
| 3 |
"""
|
| 4 |
|
| 5 |
-
from .card_detector import
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 6 |
from .image_utils import (
|
| 7 |
resize_image,
|
| 8 |
normalize_pixels,
|
|
@@ -31,6 +38,8 @@ __all__ = [
|
|
| 31 |
# Card detection
|
| 32 |
"detect_card_boundary",
|
| 33 |
"detect_card_boundary_strict",
|
|
|
|
|
|
|
| 34 |
"crop_to_card",
|
| 35 |
"validate_card_detection",
|
| 36 |
# Image utilities
|
|
|
|
| 2 |
Preprocessing module for card image processing
|
| 3 |
"""
|
| 4 |
|
| 5 |
+
from .card_detector import (
|
| 6 |
+
detect_card_boundary,
|
| 7 |
+
detect_card_boundary_strict,
|
| 8 |
+
detect_skin_mask,
|
| 9 |
+
detect_card_boundary_with_hand,
|
| 10 |
+
crop_to_card,
|
| 11 |
+
validate_card_detection,
|
| 12 |
+
)
|
| 13 |
from .image_utils import (
|
| 14 |
resize_image,
|
| 15 |
normalize_pixels,
|
|
|
|
| 38 |
# Card detection
|
| 39 |
"detect_card_boundary",
|
| 40 |
"detect_card_boundary_strict",
|
| 41 |
+
"detect_skin_mask",
|
| 42 |
+
"detect_card_boundary_with_hand",
|
| 43 |
"crop_to_card",
|
| 44 |
"validate_card_detection",
|
| 45 |
# Image utilities
|
Code/Model/src/preprocessing/card_detector.py
CHANGED
|
@@ -160,6 +160,65 @@ def detect_card_boundary_strict(
|
|
| 160 |
return best_corners
|
| 161 |
|
| 162 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 163 |
def detect_card_boundary(
|
| 164 |
image: np.ndarray,
|
| 165 |
debug: bool = False,
|
|
|
|
| 160 |
return best_corners
|
| 161 |
|
| 162 |
|
| 163 |
+
def detect_skin_mask(image: np.ndarray) -> np.ndarray:
|
| 164 |
+
"""Return a dilated binary mask of skin-coloured pixels using YCrCb thresholding."""
|
| 165 |
+
ycrcb = cv2.cvtColor(image, cv2.COLOR_BGR2YCrCb)
|
| 166 |
+
mask = cv2.inRange(ycrcb,
|
| 167 |
+
np.array([0, 133, 77]),
|
| 168 |
+
np.array([255, 173, 127]))
|
| 169 |
+
kernel = np.ones((15, 15), np.uint8)
|
| 170 |
+
return cv2.dilate(mask, kernel, iterations=2)
|
| 171 |
+
|
| 172 |
+
|
| 173 |
+
def detect_card_boundary_with_hand(image: np.ndarray, **kwargs) -> Optional[np.ndarray]:
|
| 174 |
+
"""Strict detection first; if that fails, retry with skin pixels neutralised to grey.
|
| 175 |
+
|
| 176 |
+
For hand-held cards the fingers/skin tones (and warm-background surfaces that share
|
| 177 |
+
YCrCb values with skin) confuse Canny edge detection. Three-tier strategy:
|
| 178 |
+
|
| 179 |
+
Tier 1 — normal strict path (flat card on table: always succeeds here).
|
| 180 |
+
Tier 2 — skin removal + same strict params.
|
| 181 |
+
Tier 3 — skin removal + relaxed params (hand distorts the bounding contour AR).
|
| 182 |
+
|
| 183 |
+
Tiers 2-3 are only entered when a non-zero skin mask is found, so flat-card
|
| 184 |
+
images where no skin is present always exit at Tier 1 or return None.
|
| 185 |
+
|
| 186 |
+
Args:
|
| 187 |
+
image: Input image (BGR format).
|
| 188 |
+
**kwargs: Forwarded to :func:`detect_card_boundary_strict` for Tiers 1 & 2.
|
| 189 |
+
Tier 3 overrides aspect_ratio_range, solidity_threshold, and
|
| 190 |
+
fill_ratio_threshold with hand-tolerant values.
|
| 191 |
+
|
| 192 |
+
Returns:
|
| 193 |
+
Array of 4 corner points or None if no card found after all tiers.
|
| 194 |
+
"""
|
| 195 |
+
# Tier 1: normal strict path
|
| 196 |
+
corners = detect_card_boundary_strict(image, **kwargs)
|
| 197 |
+
if corners is not None:
|
| 198 |
+
return corners # Flat-card path unchanged
|
| 199 |
+
|
| 200 |
+
skin_mask = detect_skin_mask(image)
|
| 201 |
+
if skin_mask.sum() == 0:
|
| 202 |
+
return None # No skin present; detection simply failed
|
| 203 |
+
|
| 204 |
+
masked = image.copy()
|
| 205 |
+
masked[skin_mask > 0] = [128, 128, 128] # Neutral grey removes skin/background edges
|
| 206 |
+
|
| 207 |
+
# Tier 2: skin removal + caller's strict params
|
| 208 |
+
corners = detect_card_boundary_strict(masked, **kwargs)
|
| 209 |
+
if corners is not None:
|
| 210 |
+
return corners
|
| 211 |
+
|
| 212 |
+
# Tier 3: skin removal + relaxed params
|
| 213 |
+
# Holding the card in hand makes the detected bounding contour wider/shorter than
|
| 214 |
+
# the bare card, pushing AR below 0.65. Relax all three geometry thresholds.
|
| 215 |
+
relaxed = dict(kwargs)
|
| 216 |
+
relaxed['aspect_ratio_range'] = (0.40, 0.95)
|
| 217 |
+
relaxed['solidity_threshold'] = 0.40
|
| 218 |
+
relaxed['fill_ratio_threshold'] = 0.20
|
| 219 |
+
return detect_card_boundary_strict(masked, **relaxed)
|
| 220 |
+
|
| 221 |
+
|
| 222 |
def detect_card_boundary(
|
| 223 |
image: np.ndarray,
|
| 224 |
debug: bool = False,
|