Spaces:
Sleeping
Sleeping
import cv2 | |
import numpy as np | |
import easyocr | |
import re | |
from PIL import Image, ImageDraw | |
import pytz | |
from datetime import datetime | |
from skimage import filters | |
class WeightDetector: | |
def __init__(self): | |
"""OCR optimized for 7-segment displays""" | |
self.reader = easyocr.Reader( | |
['en'], | |
gpu=True, | |
model_storage_directory='model', | |
download_enabled=True, | |
recog_network='english_g2' # Better for digital displays | |
) | |
self.ist = pytz.timezone('Asia/Kolkata') | |
def get_current_ist(self) -> str: | |
"""Get current IST time""" | |
return datetime.now(self.ist).strftime('%Y-%m-%d %H:%M:%S IST') | |
def is_blurry(self, image: np.ndarray, threshold=100) -> bool: | |
"""Check if image is blurry using Laplacian variance""" | |
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) | |
variance = cv2.Laplacian(gray, cv2.CV_64F).var() | |
return variance < threshold | |
def preprocess_7segment(self, image: np.ndarray) -> np.ndarray: | |
"""Optimized preprocessing for 7-segment displays""" | |
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) | |
# Adaptive thresholding for digital displays | |
thresh = cv2.adaptiveThreshold( | |
gray, 255, | |
cv2.ADAPTIVE_THRESH_GAUSSIAN_C, | |
cv2.THRESH_BINARY_INV, 11, 2 | |
) | |
# Remove small noise | |
kernel = np.ones((2, 2), np.uint8) | |
cleaned = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel) | |
return cleaned | |
def extract_weight(self, text: str) -> Optional[float]: | |
"""Extract weight value (handles decimals, units like g/kg)""" | |
text = text.replace(" ", "").replace(",", ".") | |
# Patterns for digital scales (e.g., "0.000g", "12.34 kg") | |
patterns = [ | |
r'(\d+\.\d+)\s*[gGkK]', # 12.34g or 12.34kg | |
r'(\d+)\s*[gGkK]', # 123g or 123kg | |
r'(\d+\.\d+)', # Decimal only | |
r'(\d+)' # Whole number | |
] | |
for pattern in patterns: | |
match = re.search(pattern, text) | |
if match: | |
try: | |
value = float(match.group(1)) | |
if 'k' in text.lower(): # Convert kg to g | |
return value * 1000 | |
return value | |
except ValueError: | |
continue | |
return None | |
def detect_weight(self, image_path: str) -> dict: | |
"""Detect weight from image with error checks""" | |
try: | |
img = Image.open(image_path).convert("RGB") | |
img_cv = cv2.cvtColor(np.array(img), cv2.COLOR_RGB2BGR) | |
# Check for blur | |
if self.is_blurry(img_cv): | |
return { | |
"weight": None, | |
"message": "⚠️ Image is blurry! Ensure clear focus.", | |
"image": img, | |
"time": self.get_current_ist() | |
} | |
# Preprocess for 7-segment digits | |
processed = self.preprocess_7segment(img_cv) | |
# OCR with 7-segment optimization | |
results = self.reader.readtext( | |
processed, | |
allowlist='0123456789.gkGKlL', | |
paragraph=False, | |
text_threshold=0.7, | |
width_ths=1.5 | |
) | |
# Extract and validate weights | |
detected_weights = [] | |
for (bbox, text, prob) in results: | |
weight = self.extract_weight(text) | |
if weight and prob > 0.5: # Minimum confidence | |
detected_weights.append({ | |
"weight": weight, | |
"text": text, | |
"probability": prob, | |
"bbox": bbox | |
}) | |
# Select best match (highest confidence + largest area) | |
if detected_weights: | |
detected_weights.sort( | |
key=lambda x: (x["probability"], | |
(x["bbox"][2][0] - x["bbox"][0][0]) * # Width | |
(x["bbox"][2][1] - x["bbox"][0][1])), # Height | |
reverse=True | |
) | |
best_match = detected_weights[0] | |
# Draw annotations | |
draw = ImageDraw.Draw(img) | |
for item in detected_weights: | |
bbox = item["bbox"] | |
polygon = [(int(x), int(y)) for [x, y] in bbox] | |
color = "green" if item == best_match else "red" | |
draw.polygon(polygon, outline=color, width=2) | |
label = f"{item['weight']}g (Conf: {item['probability']:.2f})" | |
draw.text((polygon[0][0], polygon[0][1] - 15), label, fill=color) | |
# Add timestamp | |
draw.text((10, 10), f"Time: {self.get_current_ist()}", fill="blue") | |
return { | |
"weight": best_match["weight"], | |
"message": f"✅ Detected: {best_match['weight']}g (Conf: {best_match['probability']:.2f})", | |
"image": img, | |
"time": self.get_current_ist() | |
} | |
return { | |
"weight": None, | |
"message": "❌ No weight detected. Ensure clear 7-segment digits.", | |
"image": img, | |
"time": self.get_current_ist() | |
} | |
except Exception as e: | |
return { | |
"weight": None, | |
"message": f"⚠️ Error: {str(e)}", | |
"image": None, | |
"time": self.get_current_ist() | |
} |