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 = detect_card_boundary_strict(
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 = detect_card_boundary_strict(
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 detect_card_boundary, detect_card_boundary_strict, crop_to_card, validate_card_detection
 
 
 
 
 
 
 
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,