Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -6,15 +6,14 @@ import io
|
|
6 |
import base64
|
7 |
from datetime import datetime
|
8 |
import pytz
|
9 |
-
from simple_salesforce import Salesforce
|
10 |
-
import logging
|
11 |
import numpy as np
|
|
|
12 |
import os
|
13 |
|
14 |
# Set up logging
|
15 |
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
16 |
|
17 |
-
# Configure Tesseract path
|
18 |
try:
|
19 |
pytesseract.pytesseract.tesseract_cmd = '/usr/bin/tesseract'
|
20 |
pytesseract.get_tesseract_version() # Test Tesseract availability
|
@@ -22,51 +21,12 @@ try:
|
|
22 |
except Exception as e:
|
23 |
logging.error(f"Tesseract not found or misconfigured: {str(e)}")
|
24 |
|
25 |
-
# Salesforce configuration (use environment variables in production)
|
26 |
-
SF_USERNAME = os.getenv("SF_USERNAME", "your_salesforce_username")
|
27 |
-
SF_PASSWORD = os.getenv("SF_PASSWORD", "your_salesforce_password")
|
28 |
-
SF_SECURITY_TOKEN = os.getenv("SF_SECURITY_TOKEN", "your_salesforce_security_token")
|
29 |
-
SF_DOMAIN = os.getenv("SF_DOMAIN", "login") # or "test" for sandbox
|
30 |
-
|
31 |
-
def connect_to_salesforce():
|
32 |
-
"""Connect to Salesforce with error handling."""
|
33 |
-
try:
|
34 |
-
sf = Salesforce(username=SF_USERNAME, password=SF_PASSWORD, security_token=SF_SECURITY_TOKEN, domain=SF_DOMAIN)
|
35 |
-
logging.info("Connected to Salesforce successfully")
|
36 |
-
return sf
|
37 |
-
except Exception as e:
|
38 |
-
logging.error(f"Salesforce connection failed: {str(e)}")
|
39 |
-
return None
|
40 |
-
|
41 |
-
def resize_image(img, max_size_mb=5):
|
42 |
-
"""Resize image to ensure size < 5MB while preserving quality."""
|
43 |
-
try:
|
44 |
-
img_bytes = io.BytesIO()
|
45 |
-
img.save(img_bytes, format="PNG")
|
46 |
-
size_mb = len(img_bytes.getvalue()) / (1024 * 1024)
|
47 |
-
if size_mb <= max_size_mb:
|
48 |
-
return img, img_bytes.getvalue()
|
49 |
-
|
50 |
-
scale = 0.9
|
51 |
-
while size_mb > max_size_mb:
|
52 |
-
w, h = img.size
|
53 |
-
img = img.resize((int(w * scale), int(h * scale)), Image.Resampling.LANCZOS)
|
54 |
-
img_bytes = io.BytesIO()
|
55 |
-
img.save(img_bytes, format="PNG")
|
56 |
-
size_mb = len(img_bytes.getvalue()) / (1024 * 1024)
|
57 |
-
scale *= 0.9
|
58 |
-
logging.info(f"Resized image to {size_mb:.2f} MB")
|
59 |
-
return img, img_bytes.getvalue()
|
60 |
-
except Exception as e:
|
61 |
-
logging.error(f"Image resizing failed: {str(e)}")
|
62 |
-
return img, None
|
63 |
-
|
64 |
def preprocess_image(img_cv):
|
65 |
"""Preprocess image for OCR: enhance contrast, reduce noise, and apply adaptive thresholding."""
|
66 |
try:
|
67 |
# Convert to grayscale
|
68 |
gray = cv2.cvtColor(img_cv, cv2.COLOR_BGR2GRAY)
|
69 |
-
# Enhance contrast with CLAHE
|
70 |
clahe = cv2.createCLAHE(clipLimit=3.0, tileGridSize=(8, 8))
|
71 |
contrast = clahe.apply(gray)
|
72 |
# Reduce noise with Gaussian blur
|
@@ -79,7 +39,7 @@ def preprocess_image(img_cv):
|
|
79 |
return sharpened
|
80 |
except Exception as e:
|
81 |
logging.error(f"Image preprocessing failed: {str(e)}")
|
82 |
-
return
|
83 |
|
84 |
def detect_roi(img_cv):
|
85 |
"""Detect the region of interest (ROI) containing the weight display."""
|
@@ -156,46 +116,13 @@ def process_image(img):
|
|
156 |
return "No image uploaded", None, None, None, gr.update(visible=False), gr.update(visible=False)
|
157 |
|
158 |
ist_time = datetime.now(pytz.timezone("Asia/Kolkata")).strftime("%d-%m-%Y %I:%M:%S %p")
|
159 |
-
img, img_bytes = resize_image(img)
|
160 |
-
if img_bytes is None:
|
161 |
-
logging.error("Image resizing failed")
|
162 |
-
return "Image processing failed", ist_time, img, None, gr.update(visible=False), gr.update(visible=False)
|
163 |
-
|
164 |
weight, confidence = extract_weight(img)
|
165 |
|
166 |
if weight == "Not detected" or confidence < 95.0:
|
167 |
logging.warning(f"Weight detection failed: {weight} (Confidence: {confidence:.2f}%)")
|
168 |
-
return f"{weight} (Confidence: {confidence:.2f}%)", ist_time,
|
169 |
|
170 |
-
|
171 |
-
img_base64 = base64.b64encode(img_buffer.getvalue()).decode()
|
172 |
-
logging.info(f"Weight detected successfully: {weight} kg")
|
173 |
-
return f"{weight} kg (Confidence: {confidence:.2f}%)", ist_time, img, img_base64, gr.update(visible=True), gr.update(visible=True)
|
174 |
-
|
175 |
-
def save_to_salesforce(weight_text, img_base64):
|
176 |
-
"""Save weight and image to Salesforce Weight_Log__c object."""
|
177 |
-
try:
|
178 |
-
sf = connect_to_salesforce()
|
179 |
-
if sf is None:
|
180 |
-
logging.error("Salesforce connection failed")
|
181 |
-
return "Failed to connect to Salesforce"
|
182 |
-
|
183 |
-
weight = float(weight_text.split(" ")[0])
|
184 |
-
ist_time = datetime.now(pytz.timezone("Asia/Kolkata")).strftime("%Y-%m-%d %H:%M:%S")
|
185 |
-
|
186 |
-
record = {
|
187 |
-
"Name": f"Weight_Log_{ist_time}",
|
188 |
-
"Captured_Weight__c": weight,
|
189 |
-
"Captured_At__c": ist_time,
|
190 |
-
"Snapshot_Image__c": img_base64,
|
191 |
-
"Status__c": "Confirmed"
|
192 |
-
}
|
193 |
-
result = sf.Weight_Log__c.create(record)
|
194 |
-
logging.info(f"Salesforce record created: {result}")
|
195 |
-
return "Successfully saved to Salesforce"
|
196 |
-
except Exception as e:
|
197 |
-
logging.error(f"Salesforce save failed: {str(e)}")
|
198 |
-
return f"Failed to save to Salesforce: {str(e)}"
|
199 |
|
200 |
# Gradio Interface
|
201 |
with gr.Blocks(title="βοΈ Auto Weight Logger") as demo:
|
@@ -210,29 +137,18 @@ with gr.Blocks(title="βοΈ Auto Weight Logger") as demo:
|
|
210 |
timestamp = gr.Textbox(label="π Captured At (IST)")
|
211 |
snapshot = gr.Image(label="πΈ Snapshot Image")
|
212 |
|
213 |
-
with gr.Row():
|
214 |
-
confirm_button = gr.Button("β
Confirm and Save to Salesforce", visible=False)
|
215 |
-
status = gr.Textbox(label="Save Status", visible=False)
|
216 |
-
|
217 |
submit = gr.Button("π Detect Weight")
|
218 |
submit.click(
|
219 |
fn=process_image,
|
220 |
inputs=image_input,
|
221 |
-
outputs=[output_weight, timestamp, snapshot
|
222 |
-
)
|
223 |
-
confirm_button.click(
|
224 |
-
fn=save_to_salesforce,
|
225 |
-
inputs=[output_weight, gr.State()],
|
226 |
-
outputs=status
|
227 |
)
|
228 |
|
229 |
gr.Markdown("""
|
230 |
### Instructions
|
231 |
- Upload a clear, well-lit image of a digital weight scale display (7-segment font preferred).
|
232 |
- Ensure the image is < 5MB (automatically resized if larger).
|
233 |
-
- Review the detected weight and
|
234 |
-
- Works on desktop and mobile browsers.
|
235 |
-
- If weight detection fails, check the image for glare, low contrast, or non-numeric characters and try again.
|
236 |
""")
|
237 |
|
238 |
if __name__ == "__main__":
|
|
|
6 |
import base64
|
7 |
from datetime import datetime
|
8 |
import pytz
|
|
|
|
|
9 |
import numpy as np
|
10 |
+
import logging
|
11 |
import os
|
12 |
|
13 |
# Set up logging
|
14 |
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
15 |
|
16 |
+
# Configure Tesseract path
|
17 |
try:
|
18 |
pytesseract.pytesseract.tesseract_cmd = '/usr/bin/tesseract'
|
19 |
pytesseract.get_tesseract_version() # Test Tesseract availability
|
|
|
21 |
except Exception as e:
|
22 |
logging.error(f"Tesseract not found or misconfigured: {str(e)}")
|
23 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
24 |
def preprocess_image(img_cv):
|
25 |
"""Preprocess image for OCR: enhance contrast, reduce noise, and apply adaptive thresholding."""
|
26 |
try:
|
27 |
# Convert to grayscale
|
28 |
gray = cv2.cvtColor(img_cv, cv2.COLOR_BGR2GRAY)
|
29 |
+
# Enhance contrast with CLAHE
|
30 |
clahe = cv2.createCLAHE(clipLimit=3.0, tileGridSize=(8, 8))
|
31 |
contrast = clahe.apply(gray)
|
32 |
# Reduce noise with Gaussian blur
|
|
|
39 |
return sharpened
|
40 |
except Exception as e:
|
41 |
logging.error(f"Image preprocessing failed: {str(e)}")
|
42 |
+
return img_cv
|
43 |
|
44 |
def detect_roi(img_cv):
|
45 |
"""Detect the region of interest (ROI) containing the weight display."""
|
|
|
116 |
return "No image uploaded", None, None, None, gr.update(visible=False), gr.update(visible=False)
|
117 |
|
118 |
ist_time = datetime.now(pytz.timezone("Asia/Kolkata")).strftime("%d-%m-%Y %I:%M:%S %p")
|
|
|
|
|
|
|
|
|
|
|
119 |
weight, confidence = extract_weight(img)
|
120 |
|
121 |
if weight == "Not detected" or confidence < 95.0:
|
122 |
logging.warning(f"Weight detection failed: {weight} (Confidence: {confidence:.2f}%)")
|
123 |
+
return f"{weight} (Confidence: {confidence:.2f}%)", ist_time, None, gr.update(visible=True), gr.update(visible=False)
|
124 |
|
125 |
+
return f"{weight} kg (Confidence: {confidence:.2f}%)", ist_time, None, gr.update(visible=True), gr.update(visible=True)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
126 |
|
127 |
# Gradio Interface
|
128 |
with gr.Blocks(title="βοΈ Auto Weight Logger") as demo:
|
|
|
137 |
timestamp = gr.Textbox(label="π Captured At (IST)")
|
138 |
snapshot = gr.Image(label="πΈ Snapshot Image")
|
139 |
|
|
|
|
|
|
|
|
|
140 |
submit = gr.Button("π Detect Weight")
|
141 |
submit.click(
|
142 |
fn=process_image,
|
143 |
inputs=image_input,
|
144 |
+
outputs=[output_weight, timestamp, snapshot]
|
|
|
|
|
|
|
|
|
|
|
145 |
)
|
146 |
|
147 |
gr.Markdown("""
|
148 |
### Instructions
|
149 |
- Upload a clear, well-lit image of a digital weight scale display (7-segment font preferred).
|
150 |
- Ensure the image is < 5MB (automatically resized if larger).
|
151 |
+
- Review the detected weight and try again if it's incorrect.
|
|
|
|
|
152 |
""")
|
153 |
|
154 |
if __name__ == "__main__":
|