Sanjayraju30 commited on
Commit
781a117
·
verified ·
1 Parent(s): 4c95d04

Update ocr_engine.py

Browse files
Files changed (1) hide show
  1. ocr_engine.py +226 -84
ocr_engine.py CHANGED
@@ -8,6 +8,8 @@ import logging
8
  logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
9
 
10
  # Initialize EasyOCR
 
 
11
  easyocr_reader = easyocr.Reader(['en'], gpu=False)
12
 
13
  def estimate_brightness(img):
@@ -20,21 +22,38 @@ def detect_roi(img):
20
  try:
21
  gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
22
  brightness = estimate_brightness(img)
23
- thresh_value = 230 if brightness > 100 else 190
 
 
 
 
24
  _, thresh = cv2.threshold(gray, thresh_value, 255, cv2.THRESH_BINARY)
25
- kernel = np.ones((9, 9), np.uint8)
26
- dilated = cv2.dilate(thresh, kernel, iterations=3)
 
 
 
27
  contours, _ = cv2.findContours(dilated, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
 
28
  if contours:
29
- valid_contours = [c for c in contours if cv2.contourArea(c) > 500]
 
 
30
  if valid_contours:
 
31
  for contour in sorted(valid_contours, key=cv2.contourArea, reverse=True):
32
  x, y, w, h = cv2.boundingRect(contour)
33
  aspect_ratio = w / h
34
- if 1.5 <= aspect_ratio <= 4.0 and w > 50 and h > 30:
35
- x, y = max(0, x-40), max(0, y-40)
36
- w, h = min(w+80, img.shape[1]-x), min(h+80, img.shape[0]-y)
 
 
 
 
37
  return img[y:y+h, x:x+w], (x, y, w, h)
 
 
38
  return img, None
39
  except Exception as e:
40
  logging.error(f"ROI detection failed: {str(e)}")
@@ -43,32 +62,41 @@ def detect_roi(img):
43
  def detect_segments(digit_img):
44
  """Detect seven-segment patterns in a digit image"""
45
  h, w = digit_img.shape
46
- if h < 10 or w < 10:
47
  return None
48
 
49
  # Define segment regions (top, middle, bottom, left-top, left-bottom, right-top, right-bottom)
 
50
  segments = {
51
- 'top': (0, w, 0, h//5),
52
- 'middle': (0, w, 2*h//5, 3*h//5),
53
- 'bottom': (0, w, 4*h//5, h),
54
- 'left_top': (0, w//5, 0, h//2),
55
- 'left_bottom': (0, w//5, h//2, h),
56
- 'right_top': (4*w//5, w, 0, h//2),
57
- 'right_bottom': (4*w//5, w, h//2, h)
58
  }
59
 
60
  segment_presence = {}
61
  for name, (x1, x2, y1, y2) in segments.items():
 
 
 
 
62
  region = digit_img[y1:y2, x1:x2]
63
  if region.size == 0:
64
- return None
 
 
65
  # Count white pixels in the region
66
  pixel_count = np.sum(region == 255)
67
  total_pixels = region.size
68
- # Segment is present if more than 50% of the region is white
69
- segment_presence[name] = pixel_count > total_pixels * 0.5
 
 
70
 
71
- # Seven-segment digit patterns
72
  digit_patterns = {
73
  '0': ('top', 'bottom', 'left_top', 'left_bottom', 'right_top', 'right_bottom'),
74
  '1': ('right_top', 'right_bottom'),
@@ -83,58 +111,105 @@ def detect_segments(digit_img):
83
  }
84
 
85
  best_match = None
86
- max_matches = 0
 
87
  for digit, pattern in digit_patterns.items():
88
  matches = sum(1 for segment in pattern if segment_presence.get(segment, False))
89
- non_matches = sum(1 for segment in segment_presence if segment not in pattern and segment_presence[segment])
90
- score = matches - non_matches
91
- if score > max_matches:
92
- max_matches = score
 
 
 
 
 
 
 
 
 
93
  best_match = digit
 
 
 
 
 
 
 
94
 
95
  return best_match
96
 
 
97
  def custom_seven_segment_ocr(img, roi_bbox):
98
  """Perform custom OCR for seven-segment displays"""
99
  try:
100
  gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
101
- _, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
102
-
 
 
 
 
 
 
 
103
  # Use EasyOCR to get bounding boxes for digits
 
 
104
  results = easyocr_reader.readtext(thresh, detail=1, paragraph=False,
105
- contrast_ths=0.1, adjust_contrast=0.7,
106
- text_threshold=0.9, mag_ratio=1.5,
107
  allowlist='0123456789.')
108
 
109
  if not results:
 
110
  return None
111
 
112
  # Sort bounding boxes left to right
113
- digits = []
114
- for (bbox, _, _) in results:
115
- (x1, y1), (x2, y2), (x3, y3), (x4, y4) = bbox
116
- x_min, x_max = min(x1, x4), max(x2, x3)
117
- y_min, y_max = min(y1, y2), max(y3, y4)
118
- digits.append((x_min, x_max, y_min, y_max))
 
 
119
 
120
- digits.sort(key=lambda x: x[0]) # Sort by x_min (left to right)
 
121
 
122
- # Extract and recognize each digit
123
  recognized_text = ""
124
- for x_min, x_max, y_min, y_max in digits:
125
- x_min, y_min = max(0, int(x_min)), max(0, int(y_min))
126
- x_max, y_max = min(thresh.shape[1], int(x_max)), min(thresh.shape[0], int(y_max))
 
127
  if x_max <= x_min or y_max <= y_min:
128
  continue
129
- digit_img = thresh[y_min:y_max, x_min:x_max]
130
- digit = detect_segments(digit_img)
131
- if digit:
132
- recognized_text += digit
133
-
 
 
 
 
 
 
 
 
 
 
134
  # Validate the recognized text
135
  text = recognized_text
136
- text = re.sub(r"[^\d\.]", "", text)
137
- if re.fullmatch(r"\d{1,4}(\.\d{0,3})?", text):
 
 
 
 
 
 
138
  return text
139
  return None
140
  except Exception as e:
@@ -147,74 +222,141 @@ def extract_weight_from_image(pil_img):
147
  img = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)
148
 
149
  brightness = estimate_brightness(img)
150
- conf_threshold = 0.9 if brightness > 100 else 0.7
 
151
 
152
  # Detect ROI
153
  roi_img, roi_bbox = detect_roi(img)
 
 
 
154
 
155
  # Try custom seven-segment OCR first
156
  custom_result = custom_seven_segment_ocr(roi_img, roi_bbox)
157
  if custom_result:
158
- # Format the custom result
159
  if "." in custom_result:
160
  int_part, dec_part = custom_result.split(".")
161
  int_part = int_part.lstrip("0") or "0"
162
  custom_result = f"{int_part}.{dec_part.rstrip('0')}"
163
  else:
164
  custom_result = custom_result.lstrip('0') or "0"
 
 
 
 
 
 
165
  return custom_result, 100.0 # High confidence for custom OCR
166
 
167
  # Fallback to EasyOCR if custom OCR fails
168
- images_to_process = [
169
- ("raw", roi_img, {'contrast_ths': 0.1, 'adjust_contrast': 0.7, 'text_threshold': 0.9, 'mag_ratio': 1.5, 'allowlist': '0123456789.'}),
170
- ]
 
 
 
 
 
 
 
 
 
 
 
 
171
 
 
 
 
 
 
 
 
 
172
  best_weight = None
173
  best_conf = 0.0
174
  best_score = 0.0
175
 
176
- for mode, proc_img, ocr_params in images_to_process:
177
- if mode == "raw":
178
- proc_img = cv2.cvtColor(proc_img, cv2.COLOR_BGR2GRAY)
179
- results = easyocr_reader.readtext(proc_img, detail=1, paragraph=False, **ocr_params)
180
 
181
- for (bbox, text, conf) in results:
182
- text = text.lower().strip()
183
- text = text.replace(",", ".").replace(";", ".")
184
- text = text.replace("o", "0").replace("O", "0")
185
- text = text.replace("s", "5").replace("S", "5")
186
- text = text.replace("g", "9").replace("G", "6")
187
- text = text.replace("l", "1").replace("I", "1")
188
- text = text.replace("b", "8").replace("B", "8")
189
- text = text.replace("z", "2").replace("Z", "2")
190
- text = text.replace("q", "9").replace("Q", "9")
191
- text = text.replace("kgs", "").replace("kg", "").replace("k", "")
192
- text = re.sub(r"[^\d\.]", "", text)
193
-
194
- if re.fullmatch(r"\d{1,4}(\.\d{0,3})?", text):
195
- try:
196
- weight = float(text)
197
- range_score = 1.0 if 0.1 <= weight <= 500 else 0.3
198
- digit_score = 1.5 if 10 <= weight < 100 else 1.0
199
- score = conf * range_score * digit_score
200
- if score > best_score and conf > conf_threshold:
201
- best_weight = text
202
- best_conf = conf
203
- best_score = score
204
- except ValueError:
205
- continue
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
206
 
207
  if not best_weight:
208
- logging.info("No valid weight detected")
209
  return "Not detected", 0.0
210
 
 
211
  if "." in best_weight:
212
  int_part, dec_part = best_weight.split(".")
213
- int_part = int_part.lstrip("0") or "0"
214
- best_weight = f"{int_part}.{dec_part.rstrip('0')}"
 
 
 
 
 
 
215
  else:
216
- best_weight = best_weight.lstrip('0') or "0"
217
 
 
218
  return best_weight, round(best_conf * 100, 2)
219
 
220
  except Exception as e:
 
8
  logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
9
 
10
  # Initialize EasyOCR
11
+ # Consider using 'en' and potentially 'ch_sim' or other relevant languages if your scales have non-English characters.
12
+ # gpu=True can speed up processing if a compatible GPU is available.
13
  easyocr_reader = easyocr.Reader(['en'], gpu=False)
14
 
15
  def estimate_brightness(img):
 
22
  try:
23
  gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
24
  brightness = estimate_brightness(img)
25
+
26
+ # Adaptive thresholding based on brightness
27
+ # For darker images, a lower threshold might be needed.
28
+ # For very bright images, a higher threshold.
29
+ thresh_value = 230 if brightness > 180 else (190 if brightness > 100 else 150)
30
  _, thresh = cv2.threshold(gray, thresh_value, 255, cv2.THRESH_BINARY)
31
+
32
+ # Increased kernel size for dilation to better connect segments of digits
33
+ kernel = np.ones((11, 11), np.uint8)
34
+ dilated = cv2.dilate(thresh, kernel, iterations=4) # Increased iterations
35
+
36
  contours, _ = cv2.findContours(dilated, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
37
+
38
  if contours:
39
+ # Filter contours by a more robust area range
40
+ valid_contours = [c for c in contours if 1000 < cv2.contourArea(c) < (img.shape[0] * img.shape[1] * 0.8)] # Added max area limit
41
+
42
  if valid_contours:
43
+ # Sort by area descending and iterate
44
  for contour in sorted(valid_contours, key=cv2.contourArea, reverse=True):
45
  x, y, w, h = cv2.boundingRect(contour)
46
  aspect_ratio = w / h
47
+
48
+ # Tighter aspect ratio and size constraints for typical digital displays
49
+ if 1.8 <= aspect_ratio <= 5.0 and w > 80 and h > 40: # Adjusted min w and h
50
+ # Expand ROI to ensure full digits are captured
51
+ padding = 30 # Increased padding
52
+ x, y = max(0, x - padding), max(0, y - padding)
53
+ w, h = min(w + 2 * padding, img.shape[1] - x), min(h + 2 * padding, img.shape[0] - y)
54
  return img[y:y+h, x:x+w], (x, y, w, h)
55
+
56
+ logging.info("No suitable ROI found, returning original image.")
57
  return img, None
58
  except Exception as e:
59
  logging.error(f"ROI detection failed: {str(e)}")
 
62
  def detect_segments(digit_img):
63
  """Detect seven-segment patterns in a digit image"""
64
  h, w = digit_img.shape
65
+ if h < 15 or w < 10: # Increased minimum dimensions for a digit
66
  return None
67
 
68
  # Define segment regions (top, middle, bottom, left-top, left-bottom, right-top, right-bottom)
69
+ # Adjusted segment proportions for better robustness
70
  segments = {
71
+ 'top': (int(w*0.1), int(w*0.9), 0, int(h*0.2)),
72
+ 'middle': (int(w*0.1), int(w*0.9), int(h*0.4), int(h*0.6)),
73
+ 'bottom': (int(w*0.1), int(w*0.9), int(h*0.8), h),
74
+ 'left_top': (0, int(w*0.2), int(h*0.05), int(h*0.5)),
75
+ 'left_bottom': (0, int(w*0.2), int(h*0.5), int(h*0.95)),
76
+ 'right_top': (int(w*0.8), w, int(h*0.05), int(h*0.5)),
77
+ 'right_bottom': (int(w*0.8), w, int(h*0.5), int(h*0.95))
78
  }
79
 
80
  segment_presence = {}
81
  for name, (x1, x2, y1, y2) in segments.items():
82
+ # Ensure coordinates are within bounds
83
+ x1, y1 = max(0, x1), max(0, y1)
84
+ x2, y2 = min(w, x2), min(h, y2)
85
+
86
  region = digit_img[y1:y2, x1:x2]
87
  if region.size == 0:
88
+ segment_presence[name] = False
89
+ continue
90
+
91
  # Count white pixels in the region
92
  pixel_count = np.sum(region == 255)
93
  total_pixels = region.size
94
+
95
+ # Segment is present if a significant portion of the region is white
96
+ # Adjusted threshold for segment presence
97
+ segment_presence[name] = pixel_count / total_pixels > 0.4 # Increased sensitivity
98
 
99
+ # Seven-segment digit patterns - remain the same
100
  digit_patterns = {
101
  '0': ('top', 'bottom', 'left_top', 'left_bottom', 'right_top', 'right_bottom'),
102
  '1': ('right_top', 'right_bottom'),
 
111
  }
112
 
113
  best_match = None
114
+ max_score = -1 # Initialize with a lower value
115
+
116
  for digit, pattern in digit_patterns.items():
117
  matches = sum(1 for segment in pattern if segment_presence.get(segment, False))
118
+
119
+ # Penalize for segments that should NOT be present but are
120
+ non_matches_penalty = sum(1 for segment in segment_presence if segment not in pattern and segment_presence[segment])
121
+
122
+ # Prioritize digits with more matched segments and fewer incorrect segments
123
+ current_score = matches - non_matches_penalty
124
+
125
+ # Add a small bonus for matching exactly all required segments for the digit
126
+ if all(segment_presence.get(s, False) for s in pattern):
127
+ current_score += 0.5
128
+
129
+ if current_score > max_score:
130
+ max_score = current_score
131
  best_match = digit
132
+ elif current_score == max_score and best_match is not None:
133
+ # Tie-breaking: prefer digits with fewer "extra" segments when scores are equal
134
+ current_digit_non_matches = sum(1 for segment in segment_presence if segment not in pattern and segment_presence[segment])
135
+ best_digit_pattern = digit_patterns[best_match]
136
+ best_digit_non_matches = sum(1 for segment in segment_presence if segment not in best_digit_pattern and segment_presence[segment])
137
+ if current_digit_non_matches < best_digit_non_matches:
138
+ best_match = digit
139
 
140
  return best_match
141
 
142
+
143
  def custom_seven_segment_ocr(img, roi_bbox):
144
  """Perform custom OCR for seven-segment displays"""
145
  try:
146
  gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
147
+
148
+ # Adaptive thresholding for digits within ROI
149
+ # Using OTSU for automatic thresholding or a fixed value depending on brightness
150
+ brightness = estimate_brightness(img)
151
+ if brightness > 150:
152
+ _, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
153
+ else:
154
+ _, thresh = cv2.threshold(gray, 120, 255, cv2.THRESH_BINARY) # Adjust threshold for darker displays
155
+
156
  # Use EasyOCR to get bounding boxes for digits
157
+ # Increased text_threshold for more confident digit detection
158
+ # Adjusted mag_ratio for better handling of digit sizes
159
  results = easyocr_reader.readtext(thresh, detail=1, paragraph=False,
160
+ contrast_ths=0.2, adjust_contrast=0.8, # Slightly more contrast adjustment
161
+ text_threshold=0.85, mag_ratio=1.2, # Reduced mag_ratio for potentially closer digits
162
  allowlist='0123456789.')
163
 
164
  if not results:
165
+ logging.info("EasyOCR found no digits for custom seven-segment OCR.")
166
  return None
167
 
168
  # Sort bounding boxes left to right
169
+ digits_info = []
170
+ for (bbox, text, conf) in results:
171
+ # Ensure the text found by EasyOCR is a single digit or a decimal point
172
+ if len(text) == 1 and (text.isdigit() or text == '.'):
173
+ (x1, y1), (x2, y2), (x3, y3), (x4, y4) = bbox
174
+ x_min, x_max = int(min(x1, x4)), int(max(x2, x3))
175
+ y_min, y_max = int(min(y1, y2)), int(max(y3, y4))
176
+ digits_info.append((x_min, x_max, y_min, y_max, text, conf))
177
 
178
+ # Sort by x_min (left to right)
179
+ digits_info.sort(key=lambda x: x[0])
180
 
 
181
  recognized_text = ""
182
+ for x_min, x_max, y_min, y_max, easyocr_char, easyocr_conf in digits_info:
183
+ x_min, y_min = max(0, x_min), max(0, y_min)
184
+ x_max, y_max = min(thresh.shape[1], x_max), min(thresh.shape[0], y_max)
185
+
186
  if x_max <= x_min or y_max <= y_min:
187
  continue
188
+
189
+ digit_img_crop = thresh[y_min:y_max, x_min:x_max]
190
+
191
+ # If EasyOCR is very confident about a digit or it's a decimal, use its result directly
192
+ if easyocr_conf > 0.95 or easyocr_char == '.':
193
+ recognized_text += easyocr_char
194
+ else:
195
+ # Otherwise, try the segment detection
196
+ digit_from_segments = detect_segments(digit_img_crop)
197
+ if digit_from_segments:
198
+ recognized_text += digit_from_segments
199
+ else:
200
+ # If segment detection also fails, fall back to EasyOCR's less confident result
201
+ recognized_text += easyocr_char
202
+
203
  # Validate the recognized text
204
  text = recognized_text
205
+ text = re.sub(r"[^\d\.]", "", text) # Remove any non-digit/non-dot characters
206
+
207
+ # Ensure there's at most one decimal point
208
+ if text.count('.') > 1:
209
+ text = text.replace('.', '', text.count('.') - 1) # Remove extra decimal points
210
+
211
+ # Basic validation for common weight formats
212
+ if re.fullmatch(r"^\d+(\.\d+)?$", text) and len(text) > 0: # Ensures it starts with digit and has optional decimal
213
  return text
214
  return None
215
  except Exception as e:
 
222
  img = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)
223
 
224
  brightness = estimate_brightness(img)
225
+ # Adjust confidence threshold more dynamically
226
+ conf_threshold = 0.9 if brightness > 150 else (0.75 if brightness > 80 else 0.6)
227
 
228
  # Detect ROI
229
  roi_img, roi_bbox = detect_roi(img)
230
+
231
+ # Convert ROI to RGB for display purposes if needed later
232
+ # roi_img_rgb = cv2.cvtColor(roi_img, cv2.COLOR_BGR2RGB) # For debugging or display
233
 
234
  # Try custom seven-segment OCR first
235
  custom_result = custom_seven_segment_ocr(roi_img, roi_bbox)
236
  if custom_result:
237
+ # Format the custom result: remove leading zeros (unless it's "0" or "0.x") and trailing zeros after decimal
238
  if "." in custom_result:
239
  int_part, dec_part = custom_result.split(".")
240
  int_part = int_part.lstrip("0") or "0"
241
  custom_result = f"{int_part}.{dec_part.rstrip('0')}"
242
  else:
243
  custom_result = custom_result.lstrip('0') or "0"
244
+
245
+ # Additional validation for custom result
246
+ if custom_result == "0." or custom_result == ".": # Handle cases like "0." or just "."
247
+ return "Not detected", 0.0
248
+
249
+ logging.info(f"Custom OCR result: {custom_result}, Confidence: 100.0%")
250
  return custom_result, 100.0 # High confidence for custom OCR
251
 
252
  # Fallback to EasyOCR if custom OCR fails
253
+ logging.info("Custom OCR failed, falling back to general EasyOCR.")
254
+
255
+ # Apply more aggressive image processing for EasyOCR if custom OCR failed
256
+ # This could involve different thresholds or contrast adjustments
257
+ processed_roi_img_gray = cv2.cvtColor(roi_img, cv2.COLOR_BGR2GRAY)
258
+
259
+ # Sharpening
260
+ kernel_sharpening = np.array([[-1,-1,-1],
261
+ [-1,9,-1],
262
+ [-1,-1,-1]])
263
+ sharpened_roi = cv2.filter2D(processed_roi_img_gray, -1, kernel_sharpening)
264
+
265
+ # Apply adaptive thresholding to the sharpened image for better digit isolation
266
+ processed_roi_img_final = cv2.adaptiveThreshold(sharpened_roi, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
267
+ cv2.THRESH_BINARY, 11, 2)
268
 
269
+ # EasyOCR parameters for general text
270
+ # Adjusted parameters for better digit recognition
271
+ # added batch_size for potentially better performance on multiple texts
272
+ results = easyocr_reader.readtext(processed_roi_img_final, detail=1, paragraph=False,
273
+ contrast_ths=0.3, adjust_contrast=0.9,
274
+ text_threshold=0.7, mag_ratio=1.8, # Increased mag_ratio for potentially larger digits
275
+ allowlist='0123456789.', batch_size=4) # Added batch_size
276
+
277
  best_weight = None
278
  best_conf = 0.0
279
  best_score = 0.0
280
 
281
+ for (bbox, text, conf) in results:
282
+ text = text.lower().strip()
 
 
283
 
284
+ # More robust character replacements
285
+ text = text.replace(",", ".").replace(";", ".").replace(":", ".")
286
+ text = text.replace("o", "0").replace("O", "0").replace("q", "0") # 'q' can look like 0
287
+ text = text.replace("s", "5").replace("S", "5")
288
+ text = text.replace("g", "9").replace("G", "6") # Be careful with G to 6 conversion
289
+ text = text.replace("l", "1").replace("I", "1").replace("|", "1") # Added | to 1
290
+ text = text.replace("b", "8").replace("B", "8")
291
+ text = text.replace("z", "2").replace("Z", "2")
292
+ text = text.replace("a", "4").replace("A", "4") # 'a' can look like 4
293
+ text = text.replace("e", "3") # 'e' can look like 3
294
+
295
+ # Remove common weight units and other non-numeric characters
296
+ text = re.sub(r"(kgs|kg|k|lb|g|gr|pounds)\b", "", text) # Use word boundary \b
297
+ text = re.sub(r"[^\d\.]", "", text)
298
+
299
+ # Handle multiple decimal points (keep only the first one)
300
+ if text.count('.') > 1:
301
+ parts = text.split('.')
302
+ text = parts[0] + '.' + ''.join(parts[1:])
303
+
304
+ # Validate the final text format
305
+ if re.fullmatch(r"^\d{1,4}(\.\d{0,3})?$", text): # Adjusted regex for more flexible digits
306
+ try:
307
+ weight = float(text)
308
+ # Refined scoring for weights within a reasonable range
309
+ range_score = 1.0
310
+ if 0.01 <= weight <= 300: # Typical personal scale range
311
+ range_score = 1.2
312
+ elif weight > 300 and weight <= 1000: # Larger scales
313
+ range_score = 1.1
314
+ else: # Very small or very large weights
315
+ range_score = 0.8
316
+
317
+ digit_count = len(text.replace('.', ''))
318
+ digit_score = 1.0
319
+ if digit_count >= 3 and digit_count <= 5: # Prefer weights with 3-5 digits (e.g., 50.5, 123.4)
320
+ digit_score = 1.3
321
+
322
+ score = conf * range_score * digit_score
323
+
324
+ # Also consider area of the bounding box relative to ROI for confidence
325
+ bbox_area = (bbox[1][0] - bbox[0][0]) * (bbox[2][1] - bbox[1][1])
326
+ if roi_bbox:
327
+ roi_area = roi_bbox[2] * roi_bbox[3]
328
+ if roi_area > 0 and bbox_area / roi_area < 0.05: # Small bounding boxes might be noise
329
+ score *= 0.5
330
+
331
+ if score > best_score and conf > conf_threshold:
332
+ best_weight = text
333
+ best_conf = conf
334
+ best_score = score
335
+ logging.info(f"Candidate EasyOCR weight: {text}, Conf: {conf}, Score: {score}")
336
+
337
+ except ValueError:
338
+ logging.warning(f"Could not convert '{text}' to float.")
339
+ continue
340
 
341
  if not best_weight:
342
+ logging.info("No valid weight detected after all attempts.")
343
  return "Not detected", 0.0
344
 
345
+ # Final formatting of the best detected weight
346
  if "." in best_weight:
347
  int_part, dec_part = best_weight.split(".")
348
+ int_part = int_part.lstrip("0") or "0" # Remove leading zeros, keep "0" for 0.x
349
+ dec_part = dec_part.rstrip('0') # Remove trailing zeros after decimal
350
+ if not dec_part and int_part != "0": # If decimal part is empty (e.g., "50."), remove the dot
351
+ best_weight = int_part
352
+ elif not dec_part and int_part == "0": # if it's "0." keep it as "0"
353
+ best_weight = "0"
354
+ else:
355
+ best_weight = f"{int_part}.{dec_part}"
356
  else:
357
+ best_weight = best_weight.lstrip('0') or "0" # Remove leading zeros, keep "0"
358
 
359
+ logging.info(f"Final detected weight: {best_weight}, Confidence: {round(best_conf * 100, 2)}%")
360
  return best_weight, round(best_conf * 100, 2)
361
 
362
  except Exception as e: