PaulMartrenchar commited on
Commit
799d6a7
·
1 Parent(s): 74244be

First version

Browse files
Files changed (2) hide show
  1. app.py +87 -0
  2. requirements.txt +4 -0
app.py ADDED
@@ -0,0 +1,87 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import cv2
2
+ import numpy as np
3
+ import pytesseract
4
+ import re
5
+ import gradio as gr
6
+
7
+ # Regex pattern for allowed card values
8
+ VALID_PATTERN = re.compile(r"-?\d+")
9
+
10
+ def extract_card_value(card_img):
11
+ """Extract numeric value from a card using OCR + regex"""
12
+ gray = cv2.cvtColor(card_img, cv2.COLOR_BGR2GRAY)
13
+ gray = cv2.GaussianBlur(gray, (3,3), 0)
14
+ thresh = cv2.threshold(gray, 120, 255, cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)[1]
15
+
16
+ # OCR: digits + minus sign allowed
17
+ raw_text = pytesseract.image_to_string(
18
+ thresh,
19
+ config="--psm 6 -c tessedit_char_whitelist=-0123456789"
20
+ )
21
+
22
+ matches = VALID_PATTERN.findall(raw_text)
23
+ for m in matches:
24
+ try:
25
+ return int(m)
26
+ except ValueError:
27
+ continue
28
+ return None
29
+
30
+ def detect_cards_and_sum(image):
31
+ """Main pipeline to detect cards, extract values, and return results."""
32
+ try:
33
+ if image is None:
34
+ return 0, 0, "No image provided."
35
+
36
+ # Convert PIL/NumPy image to OpenCV BGR
37
+ if not isinstance(image, np.ndarray):
38
+ image = np.array(image.convert("RGB"))
39
+ img = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
40
+
41
+ gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
42
+ blur = cv2.GaussianBlur(gray, (5,5), 0)
43
+ thresh = cv2.threshold(blur, 120, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)[1]
44
+
45
+ contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
46
+
47
+ total = 0
48
+ detected_values = []
49
+
50
+ for cnt in contours:
51
+ peri = cv2.arcLength(cnt, True)
52
+ approx = cv2.approxPolyDP(cnt, 0.02 * peri, True)
53
+
54
+ if len(approx) == 4: # rectangle → card
55
+ pts = np.float32(approx.reshape(4, 2))
56
+ dst = np.array([[0,0],[200,0],[200,300],[0,300]], dtype="float32")
57
+ M = cv2.getPerspectiveTransform(pts, dst)
58
+ warp = cv2.warpPerspective(img, M, (200,300))
59
+
60
+ value = extract_card_value(warp)
61
+ if value is not None:
62
+ detected_values.append(value)
63
+ total += value
64
+
65
+ if not detected_values:
66
+ return 0, 0, "No valid card values detected. Try clearer lighting or adjusting the photo."
67
+
68
+ return len(detected_values), total, None
69
+
70
+ except Exception as e:
71
+ return 0, 0, f"Error: {str(e)}"
72
+
73
+ # Gradio UI
74
+ demo = gr.Interface(
75
+ fn=detect_cards_and_sum,
76
+ inputs=gr.Image(type="pil", label="Upload a photo of your cards"),
77
+ outputs=[
78
+ gr.Number(label="Detected Cards"),
79
+ gr.Number(label="Total Sum"),
80
+ gr.Textbox(label="Error Message", type="text")
81
+ ],
82
+ title="Card Value Detector (Skyjo / Flip7)",
83
+ description="Upload a photo of multiple cards. The system will detect the numbers and return their sum."
84
+ )
85
+
86
+ if __name__ == "__main__":
87
+ demo.launch()
requirements.txt ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ opencv-python
2
+ numpy
3
+ pytesseract
4
+ gradio