Spaces:
Running
Running
File size: 7,802 Bytes
975f9c6 5234a64 0bb13f0 5234a64 0f29b7c 975f9c6 2b694be 5234a64 0f29b7c 2b694be 0f29b7c 2b694be 0f29b7c 2b694be 0f29b7c 2b694be fcdea18 2b694be 0f29b7c 2b694be 0f29b7c 2b694be 0f29b7c 2b694be 0f29b7c fcdea18 0f29b7c fcdea18 2b694be 5234a64 0f29b7c 2b694be 5234a64 2b694be 5234a64 2b694be fcdea18 975f9c6 5234a64 0f29b7c 975f9c6 2b694be 0f29b7c 2b694be 975f9c6 8ccdb60 2b694be 0f29b7c 2b694be 0f29b7c 2b694be 975f9c6 8ccdb60 5234a64 385a153 975f9c6 2154cf1 975f9c6 5234a64 975f9c6 2b694be 975f9c6 385a153 975f9c6 5234a64 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 |
import easyocr
import numpy as np
import cv2
import re
import logging
# Set up logging for debugging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
# Initialize EasyOCR
easyocr_reader = easyocr.Reader(['en'], gpu=False)
def estimate_brightness(img):
"""Estimate image brightness to detect illuminated displays"""
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
return np.mean(gray)
def detect_roi(img):
"""Detect and crop the region of interest (likely the digital display)"""
try:
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# Threshold to isolate bright areas (like illuminated displays)
brightness = estimate_brightness(img)
thresh_value = 200 if brightness > 100 else 150 # Adjust based on brightness
_, thresh = cv2.threshold(gray, thresh_value, 255, cv2.THRESH_BINARY)
# Dilate to connect digits
kernel = np.ones((7, 7), np.uint8)
dilated = cv2.dilate(thresh, kernel, iterations=2)
# Find contours
contours, _ = cv2.findContours(dilated, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
if contours:
# Get the largest contour with reasonable size
valid_contours = [c for c in contours if cv2.contourArea(c) > 500]
if valid_contours:
largest_contour = max(valid_contours, key=cv2.contourArea)
x, y, w, h = cv2.boundingRect(largest_contour)
# Add more padding and ensure bounds
x, y = max(0, x-30), max(0, y-30)
w, h = min(w+60, img.shape[1]-x), min(h+60, img.shape[0]-y)
if w > 50 and h > 30:
return img[y:y+h, x:x+w]
return img
except Exception as e:
logging.error(f"ROI detection failed: {str(e)}")
return img
def enhance_image(img, mode="standard"):
"""Enhance image with different modes for multi-scale processing"""
try:
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
if mode == "seven_segment":
# Gentle preprocessing for seven-segment displays
denoised = cv2.GaussianBlur(gray, (5, 5), 0)
_, thresh = cv2.threshold(denoised, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
elif mode == "high_contrast":
denoised = cv2.bilateralFilter(gray, d=11, sigmaColor=100, sigmaSpace=100)
clahe = cv2.createCLAHE(clipLimit=3.0, tileGridSize=(8, 8))
thresh = clahe.apply(denoised)
elif mode == "low_noise":
denoised = cv2.bilateralFilter(gray, d=7, sigmaColor=50, sigmaSpace=50)
clahe = cv2.createCLAHE(clipLimit=1.5, tileGridSize=(8, 8))
thresh = clahe.apply(denoised)
else:
denoised = cv2.bilateralFilter(gray, d=9, sigmaColor=75, sigmaSpace=75)
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))
thresh = clahe.apply(denoised)
if mode != "seven_segment":
thresh = cv2.adaptiveThreshold(thresh, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
cv2.THRESH_BINARY, 11, 2)
# Morphological operations
kernel = np.ones((3, 3), np.uint8)
morphed = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel, iterations=1)
# Reduced sharpening for seven-segment displays
brightness = estimate_brightness(img)
sharpen_strength = 3 if mode == "seven_segment" or brightness > 100 else 5
sharpen_kernel = np.array([[0, -1, 0], [-1, sharpen_strength, -1], [0, -1, 0]])
sharpened = cv2.filter2D(morphed, -1, sharpen_kernel)
# Dynamic resizing
h, w = sharpened.shape
target_size = 800
scale_factor = min(target_size / max(h, w), 2.0) if max(h, w) < 300 else min(target_size / max(h, w), 1.0)
if scale_factor != 1.0:
sharpened = cv2.resize(sharpened, None, fx=scale_factor, fy=scale_factor,
interpolation=cv2.INTER_CUBIC if scale_factor > 1 else cv2.INTER_AREA)
return sharpened
except Exception as e:
logging.error(f"Image enhancement failed (mode={mode}): {str(e)}")
return img
def extract_weight_from_image(pil_img):
try:
img = np.array(pil_img)
img = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)
# Estimate brightness for adaptive thresholding
brightness = estimate_brightness(img)
conf_threshold = 0.5 if brightness > 100 else 0.4 # Stricter for bright displays
# Detect ROI
roi_img = detect_roi(img)
# Process multiple image versions
images_to_process = [
("seven_segment", enhance_image(roi_img, mode="seven_segment"), {'contrast_ths': 0.3, 'allowlist': '0123456789.'}),
("standard", enhance_image(roi_img, mode="standard"), {'contrast_ths': 0.1}),
("high_contrast", enhance_image(roi_img, mode="high_contrast"), {'contrast_ths': 0.1}),
("low_noise", enhance_image(roi_img, mode="low_noise"), {'contrast_ths': 0.1}),
("original", roi_img, {'contrast_ths': 0.3, 'allowlist': '0123456789.'})
]
best_weight = None
best_conf = 0.0
best_score = 0.0
for mode, proc_img, ocr_params in images_to_process:
# EasyOCR detection
results = easyocr_reader.readtext(proc_img, detail=1, paragraph=False, **ocr_params)
for (bbox, text, conf) in results:
original_text = text
text = text.lower().strip()
# Fix common OCR errors
text = text.replace(",", ".").replace(";", ".")
text = text.replace("o", "0").replace("O", "0")
text = text.replace("s", "5").replace("S", "5")
text = text.replace("g", "9").replace("G", "6")
text = text.replace("l", "1").replace("I", "1")
text = text.replace("b", "8").replace("B", "8")
text = text.replace("z", "2").replace("Z", "2")
text = text.replace("q", "9").replace("Q", "9")
text = text.replace("6", "2").replace("9", "2") # Specific correction for seven-segment
text = text.replace("kgs", "").replace("kg", "").replace("k", "")
text = re.sub(r"[^\d\.]", "", text)
# Regex for weight (0.0 to 9999.999)
if re.fullmatch(r"\d{1,4}(\.\d{0,3})?", text):
try:
weight = float(text)
# Score based on realistic weight range (0.1–500 kg)
range_score = 1.0 if 0.1 <= weight <= 500 else 0.3
# Prefer two-digit weights for scales
digit_score = 1.1 if 10 <= weight < 100 else 1.0
score = conf * range_score * digit_score
if score > best_score and conf > conf_threshold:
best_weight = text
best_conf = conf
best_score = score
except ValueError:
continue
if not best_weight:
logging.info("No valid weight detected")
return "Not detected", 0.0
# Format output
if "." in best_weight:
int_part, dec_part = best_weight.split(".")
int_part = int_part.lstrip("0") or "0"
best_weight = f"{int_part}.{dec_part.rstrip('0')}"
else:
best_weight = best_weight.lstrip('0') or "0"
return best_weight, round(best_conf * 100, 2)
except Exception as e:
logging.error(f"Weight extraction failed: {str(e)}")
return "Not detected", 0.0 |