File size: 5,884 Bytes
ee68036
 
 
 
 
cc6391c
 
523ceab
ee68036
 
 
523ceab
cc6391c
 
 
 
523ceab
 
cc6391c
 
523ceab
cc6391c
523ceab
 
 
 
 
 
 
 
 
 
 
cc6391c
ee68036
523ceab
 
 
 
 
 
cc6391c
523ceab
 
 
ee68036
523ceab
 
 
 
 
cc6391c
523ceab
ee68036
523ceab
 
 
 
ee68036
 
 
cc6391c
ee68036
 
cc6391c
523ceab
 
cc6391c
ee68036
 
 
523ceab
 
 
ee68036
 
 
 
523ceab
 
 
 
 
 
 
 
cc6391c
523ceab
 
 
 
cc6391c
 
523ceab
cc6391c
523ceab
 
cc6391c
ee68036
523ceab
ee68036
 
523ceab
 
ee68036
523ceab
 
 
 
ee68036
 
523ceab
ee68036
cc6391c
523ceab
 
 
cc6391c
 
ee68036
 
523ceab
 
ee68036
523ceab
ee68036
cc6391c
 
523ceab
cc6391c
ee68036
523ceab
 
ee68036
523ceab
 
 
 
 
 
cc6391c
523ceab
 
 
 
 
 
ee68036
 
523ceab
 
 
 
 
 
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
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()
            }