ObiJuanCodenobi commited on
Commit
770c2ad
Β·
1 Parent(s): 3d46820
Files changed (2) hide show
  1. app.py +138 -4
  2. requirements.txt +6 -0
app.py CHANGED
@@ -1,7 +1,141 @@
1
  import gradio as gr
 
 
 
 
 
2
 
3
- def greet(name):
4
- return "Hello " + name + "!!"
 
 
 
 
5
 
6
- demo = gr.Interface(fn=greet, inputs="text", outputs="text")
7
- demo.launch()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import gradio as gr
2
+ import cv2
3
+ import pytesseract
4
+ import numpy as np
5
+ import re
6
+ import requests
7
 
8
+ # Align image using OpenCV
9
+ def align_form_from_image(image):
10
+ img = cv2.cvtColor(np.array(image), cv2.COLOR_RGB2BGR)
11
+ gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
12
+ blurred = cv2.GaussianBlur(gray, (5, 5), 0)
13
+ edged = cv2.Canny(blurred, 75, 200)
14
 
15
+ contours, _ = cv2.findContours(edged.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
16
+ contours = sorted(contours, key=cv2.contourArea, reverse=True)[:5]
17
+ doc_cnts = None
18
+ for c in contours:
19
+ peri = cv2.arcLength(c, True)
20
+ approx = cv2.approxPolyDP(c, 0.02 * peri, True)
21
+ if len(approx) == 4:
22
+ doc_cnts = approx
23
+ break
24
+
25
+ if doc_cnts is not None:
26
+ pts = doc_cnts.reshape(4, 2)
27
+ rect = order_points(pts)
28
+ dst = np.array([[0, 0], [800, 0], [800, 1000], [0, 1000]], dtype="float32")
29
+ M = cv2.getPerspectiveTransform(rect, dst)
30
+ aligned = cv2.warpPerspective(img, M, (800, 1000))
31
+ else:
32
+ aligned = img
33
+
34
+ rgb = cv2.cvtColor(aligned, cv2.COLOR_BGR2RGB)
35
+ text = pytesseract.image_to_string(rgb)
36
+ return rgb, text
37
+
38
+ def order_points(pts):
39
+ rect = np.zeros((4, 2), dtype="float32")
40
+ s = pts.sum(axis=1)
41
+ rect[0] = pts[np.argmin(s)]
42
+ rect[2] = pts[np.argmax(s)]
43
+ diff = np.diff(pts, axis=1)
44
+ rect[1] = pts[np.argmin(diff)]
45
+ rect[3] = pts[np.argmax(diff)]
46
+ return rect
47
+
48
+ # Extract fields from driver payout form
49
+ def parse_driver_payout_custom(text: str):
50
+ def find_checkbox(label_yes, label_no):
51
+ yes = re.search(label_yes + r"\s*[:]?[\s\S]{0,20}?βœ“", text)
52
+ no = re.search(label_no + r"\s*[:]?[\s\S]{0,20}?βœ“", text)
53
+ if yes and not no:
54
+ return "Yes"
55
+ elif no and not yes:
56
+ return "No"
57
+ elif yes and no:
58
+ return "Both Checked"
59
+ return "Unchecked"
60
+
61
+ data = {
62
+ "date": re.search(r"Date[:\s]*([\d/]+)", text),
63
+ "time": re.search(r"Time[:\s]*([\d:]+)", text),
64
+ "name": re.search(r"Name[:\s]*([A-Za-z ]+)", text),
65
+ "email": re.search(r"Email[:\s]*(\S+@\S+)?", text),
66
+ "phone": re.search(r"Phone Number[:\s]*(\d{3}[- ]\d{3}[- ]\d{4})", text),
67
+ "service_type": None,
68
+ "w9_filled_out": find_checkbox("Yes", "No"),
69
+ "payment_received": re.search(r"\$\s?([\d.]+)", text),
70
+ "payout": "Payout Now" if "Payout Now" in text and "βœ“" in text.split("Payout Now")[1][:10] else (
71
+ "Payout Later" if "Payout Later" in text and "βœ“" in text.split("Payout Later")[1][:10] else None),
72
+ "team_member": re.search(r"Team Member's Name[:\s]*([A-Za-z]+)", text),
73
+ "uploaded_to_drive": re.search(r"Uploaded to the Drive\?\s*(Yes|No)", text, re.IGNORECASE),
74
+ }
75
+
76
+ for service in ["Taxi", "Limo", "Uber", "Lyft", "Other"]:
77
+ if f"{service}" in text and "βœ“" in text.split(service)[1][:10]:
78
+ data["service_type"] = service
79
+
80
+ return {k: v.group(1).strip() if v else v for k, v in data.items()}
81
+
82
+ # Send to webhook
83
+ def send_to_webhook(webhook, *field_values):
84
+ data = {f"field_{i}": val for i, val in enumerate(field_values)}
85
+ try:
86
+ resp = requests.post(webhook, json=data)
87
+ return f"βœ… Sent! Status: {resp.status_code}"
88
+ except Exception as e:
89
+ return f"❌ Failed: {str(e)}"
90
+
91
+ # Launch Gradio app
92
+ with gr.Blocks() as demo:
93
+ gr.Markdown("# πŸ“„ Driver Payout Form OCR β†’ Webhook")
94
+
95
+ webhook_url = gr.State("https://example.com/webhook")
96
+
97
+ with gr.Row():
98
+ image_input = gr.Image(type="pil", label="Upload or Take Photo", source="upload")
99
+ aligned_image = gr.Image(type="numpy", label="Aligned Image")
100
+
101
+ form_type = gr.Radio(["Driver Payout"], label="Form Type", value="Driver Payout")
102
+ raw_text_output = gr.Textbox(label="OCR Text", lines=8)
103
+ parsed_json = gr.JSON(label="Parsed Fields")
104
+
105
+ editable_fields_group = gr.Group(visible=False)
106
+ editable_fields = []
107
+ for i in range(12): # max 12 fields
108
+ tb = gr.Textbox(label=f"Field {i+1}")
109
+ editable_fields.append(tb)
110
+ editable_fields_group.children = editable_fields
111
+
112
+ status_output = gr.Textbox(label="Webhook Response")
113
+
114
+ def process_image(image, form_type):
115
+ aligned, text = align_form_from_image(image)
116
+ parsed = parse_driver_payout_custom(text)
117
+ visible = gr.update(visible=True)
118
+ values = list(parsed.values()) + [""] * (12 - len(parsed))
119
+ return aligned, text, parsed, visible, values[:12]
120
+
121
+ process_btn = gr.Button("OCR + Parse")
122
+ process_btn.click(
123
+ fn=process_image,
124
+ inputs=[image_input, form_type],
125
+ outputs=[aligned_image, raw_text_output, parsed_json, editable_fields_group] + editable_fields
126
+ )
127
+
128
+ send_btn = gr.Button("Send to Webhook")
129
+ send_btn.click(
130
+ fn=send_to_webhook,
131
+ inputs=[webhook_url] + editable_fields,
132
+ outputs=status_output
133
+ )
134
+
135
+ with gr.Accordion("Admin Settings", open=False):
136
+ webhook_input = gr.Textbox(label="Set Webhook URL")
137
+ set_webhook_btn = gr.Button("Save Webhook")
138
+ set_webhook_btn.click(lambda url: url, inputs=webhook_input, outputs=webhook_url)
139
+
140
+ if __name__ == "__main__":
141
+ demo.launch()
requirements.txt ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ gradio>=4.0.0
2
+ opencv-python
3
+ pytesseract
4
+ numpy
5
+ requests
6
+ Pillow