File size: 4,214 Bytes
2bc6272
0d59618
 
 
 
 
 
 
 
 
 
2bc6272
 
 
0d59618
2bc6272
0d59618
 
 
 
 
 
 
 
 
 
 
 
2bc6272
 
0d59618
2bc6272
0d59618
 
2bc6272
0d59618
 
2bc6272
0d59618
 
2bc6272
0d59618
 
 
 
2bc6272
 
0d59618
2bc6272
 
 
0d59618
2bc6272
 
0d59618
2bc6272
0d59618
2bc6272
 
0d59618
 
 
 
2bc6272
0d59618
2bc6272
0d59618
 
2bc6272
0d59618
 
 
 
 
2bc6272
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0d59618
 
 
 
 
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
import easyocr
import numpy as np
import cv2
import re
from PIL import Image
import logging
import sys

# Set up logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s', handlers=[logging.StreamHandler(sys.stdout)])

# Initialize EasyOCR reader (once at module level for efficiency)
reader = easyocr.Reader(['en'], gpu=False)  # GPU=False for CPU-only environments like Hugging Face Spaces

def preprocess_image(img):
    """Preprocess image for robust OCR with EasyOCR, optimized for weight displays."""
    try:
        # Convert PIL to OpenCV format
        img = np.array(img)
        img = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)
        
        # Convert to grayscale
        gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
        
        # Enhance contrast for diverse lighting conditions
        clahe = cv2.createCLAHE(clipLimit=4.0, tileGridSize=(8, 8))
        enhanced = clahe.apply(gray)
        
        # Apply adaptive thresholding
        block_size = max(11, min(31, int(img.shape[0] / 15) * 2 + 1))
        thresh = cv2.adaptiveThreshold(
            enhanced, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV, block_size, 2
        )
        
        return thresh  # EasyOCR handles further processing
    except Exception as e:
        logging.error(f"Preprocessing failed: {str(e)}")
        return gray

def extract_weight_from_image(pil_img):
    """Extract weight and unit from a digital scale image using EasyOCR."""
    try:
        # Preprocess image
        thresh = preprocess_image(pil_img)
        
        # Convert to numpy array for EasyOCR
        img_np = np.array(thresh)
        
        # Use EasyOCR to detect text
        results = reader.readtext(img_np, detail=1, paragraph=False)
        logging.info(f"EasyOCR raw output: {results}")
        
        # Extract weight and unit from detected text
        text = " ".join([result[1] for result in results])  # Combine all detected text
        text = text.strip().lower()
        text = re.sub(r'\s+', ' ', text)  # Normalize spaces
        
        # Extract weight and unit, prioritizing common formats
        match = re.search(r'(-?\d*\.?\d+)([kgkg]?)', text)
        if match:
            weight_str = match.group(1)
            unit = match.group(2) if match.group(2) else "g"  # Default to grams if no unit
            weight_str = weight_str.replace(',', '.')  # Handle decimal formats (e.g., 68,0)
            if re.fullmatch(r'^-?\d*\.?\d+$', weight_str):
                weight_str = weight_str.lstrip('0') or '0'
                confidence = min([result[2] for result in results if result[1]] or [0.0]) * 100  # Convert EasyOCR confidence (0-1) to percentage
                try:
                    weight = float(weight_str)
                    if -5000 <= weight <= 5000:
                        logging.info(f"Detected weight: {weight} {unit}, Confidence: {confidence:.2f}%")
                        return weight_str, confidence, unit
                except ValueError:
                    logging.warning(f"Invalid weight format: {weight_str}")
        
        # Fallback to detect numbers without units if no match
        match_no_unit = re.search(r'(-?\d*\.?\d+)', text)
        if match_no_unit and not match:
            weight_str = match_no_unit.group(1)
            weight_str = weight_str.replace(',', '.')
            if re.fullmatch(r'^-?\d*\.?\d+$', weight_str):
                weight_str = weight_str.lstrip('0') or '0'
                confidence = min([result[2] for result in results if result[1]] or [0.0]) * 100
                try:
                    weight = float(weight_str)
                    if -5000 <= weight <= 5000:
                        logging.info(f"Detected weight (no unit): {weight} g, Confidence: {confidence:.2f}%")
                        return weight_str, confidence, "g"
                except ValueError:
                    logging.warning(f"Invalid weight format: {weight_str}")
        
        logging.info("No valid weight detected.")
        return "Not detected", 0.0, ""
    except Exception as e:
        logging.error(f"Weight extraction failed: {str(e)}")
        return "Not detected", 0.0, ""