Sanjayraju30 commited on
Commit
a6294cd
·
verified ·
1 Parent(s): 3137c41

Update ocr_engine.py

Browse files
Files changed (1) hide show
  1. ocr_engine.py +113 -89
ocr_engine.py CHANGED
@@ -31,31 +31,34 @@ def estimate_brightness(img):
31
  gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
32
  return np.mean(gray)
33
 
34
- def preprocess_image(img):
35
  """Preprocess image for better OCR accuracy."""
 
 
 
36
  gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
37
  # Apply bilateral filter to preserve edges
38
- denoised = cv2.bilateralFilter(gray, 11, 17, 17)
39
- save_debug_image(denoised, "01_preprocess_bilateral")
40
  # Enhance contrast using CLAHE
41
- clahe = cv2.createCLAHE(clipLimit=2.5, tileGridSize=(8, 8))
42
  enhanced = clahe.apply(denoised)
43
- save_debug_image(enhanced, "02_preprocess_clahe")
44
  # Sharpen the image
45
  kernel_sharpening = np.array([[-1, -1, -1], [-1, 9, -1], [-1, -1, -1]])
46
  sharpened = cv2.filter2D(enhanced, -1, kernel_sharpening)
47
- save_debug_image(sharpened, "03_preprocess_sharpened")
48
  return sharpened
49
 
50
  def correct_rotation(img):
51
  """Correct image rotation using Hough Transform."""
52
  try:
53
- edges = cv2.Canny(cv2.cvtColor(img, cv2.COLOR_BGR2GRAY), 100, 200)
54
- lines = cv2.HoughLinesP(edges, 1, np.pi / 180, threshold=100, minLineLength=100, maxLineGap=10)
55
  if lines is not None:
56
  angles = [np.arctan2(line[0][3] - line[0][1], line[0][2] - line[0][0]) * 180 / np.pi for line in lines]
57
- angle = np.median(angles) # Use median for robustness
58
- if abs(angle) > 5:
59
  (h, w) = img.shape[:2]
60
  center = (w // 2, h // 2)
61
  M = cv2.getRotationMatrix2D(center, angle, 1.0)
@@ -70,87 +73,96 @@ def correct_rotation(img):
70
  def detect_roi(img):
71
  """Detect and crop the region of interest (likely the digital display)."""
72
  try:
73
- save_debug_image(img, "04_original")
74
- preprocessed = preprocess_image(img)
75
  brightness_map = cv2.GaussianBlur(cv2.cvtColor(img, cv2.COLOR_BGR2GRAY), (15, 15), 0)
76
 
77
- # Dynamic adaptive thresholding
78
- block_size = max(11, min(31, int(img.shape[0] / 20) * 2 + 1))
79
- thresh = cv2.adaptiveThreshold(preprocessed, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
80
- cv2.THRESH_BINARY_INV, block_size, 5)
81
- _, otsu_thresh = cv2.threshold(preprocessed, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
82
- combined_thresh = cv2.bitwise_and(thresh, otsu_thresh)
83
- save_debug_image(combined_thresh, "05_roi_combined_threshold")
84
-
85
- # Morphological operations to connect digits
86
- kernel = np.ones((5, 5), np.uint8)
87
- dilated = cv2.dilate(combined_thresh, kernel, iterations=2)
88
- eroded = cv2.erode(dilated, kernel, iterations=1)
89
- save_debug_image(eroded, "06_roi_morphological")
90
-
91
- contours, _ = cv2.findContours(eroded, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
92
-
93
- if contours:
94
- img_area = img.shape[0] * img.shape[1]
95
- valid_contours = []
96
- for c in contours:
97
- area = cv2.contourArea(c)
98
- x, y, w, h = cv2.boundingRect(c)
99
- roi_brightness = np.mean(brightness_map[y:y+h, x:x+w])
100
- aspect_ratio = w / h
101
- if (500 < area < (img_area * 0.9) and
102
- 0.8 <= aspect_ratio <= 12.0 and w > 60 and h > 30 and roi_brightness > 80):
103
- valid_contours.append((c, roi_brightness))
104
- logging.debug(f"Contour: Area={area}, Aspect={aspect_ratio:.2f}, Brightness={roi_brightness:.2f}")
105
 
106
- if valid_contours:
107
- contour, _ = max(valid_contours, key=lambda x: x[1]) # Max brightness
108
- x, y, w, h = cv2.boundingRect(contour)
109
- padding = 100
110
- x, y = max(0, x - padding), max(0, y - padding)
111
- w, h = min(w + 2 * padding, img.shape[1] - x), min(h + 2 * padding, img.shape[0] - y)
112
- roi_img = img[y:y+h, x:x+w]
113
- save_debug_image(roi_img, "07_detected_roi")
114
- logging.info(f"Detected ROI with dimensions: ({x}, {y}, {w}, {h})")
115
- return roi_img, (x, y, w, h)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
116
 
117
  logging.info("No suitable ROI found, attempting fallback criteria.")
118
  # Fallback with relaxed criteria
119
- valid_contours = [c for c in contours if 300 < cv2.contourArea(c) < (img_area * 0.95) and
120
- 0.5 <= cv2.boundingRect(c)[2]/cv2.boundingRect(c)[3] <= 15.0]
 
 
 
 
 
121
  if valid_contours:
122
  contour = max(valid_contours, key=cv2.contourArea)
123
  x, y, w, h = cv2.boundingRect(contour)
124
- padding = 100
125
  x, y = max(0, x - padding), max(0, y - padding)
126
  w, h = min(w + 2 * padding, img.shape[1] - x), min(h + 2 * padding, img.shape[0] - y)
127
  roi_img = img[y:y+h, x:x+w]
128
- save_debug_image(roi_img, "07_detected_roi_fallback")
129
  logging.info(f"Detected fallback ROI with dimensions: ({x}, {y}, {w}, {h})")
130
  return roi_img, (x, y, w, h)
131
 
132
  logging.info("No suitable ROI found, returning original image.")
133
- save_debug_image(img, "07_no_roi_original_fallback")
134
  return img, None
135
  except Exception as e:
136
  logging.error(f"ROI detection failed: {str(e)}")
137
- save_debug_image(img, "07_roi_detection_error_fallback")
138
  return img, None
139
 
140
  def detect_segments(digit_img, brightness):
141
  """Detect seven-segment patterns in a digit image."""
142
  h, w = digit_img.shape
143
- if h < 15 or w < 10:
144
  return None
145
 
146
  segments = {
147
- 'top': (int(w*0.15), int(w*0.85), 0, int(h*0.2)),
148
- 'middle': (int(w*0.15), int(w*0.85), int(h*0.45), int(h*0.55)),
149
- 'bottom': (int(w*0.15), int(w*0.85), int(h*0.8), h),
150
- 'left_top': (0, int(w*0.25), int(h*0.15), int(h*0.5)),
151
- 'left_bottom': (0, int(w*0.25), int(h*0.5), int(h*0.85)),
152
- 'right_top': (int(w*0.75), w, int(h*0.15), int(h*0.5)),
153
- 'right_bottom': (int(w*0.75), w, int(h*0.5), int(h*0.85))
154
  }
155
 
156
  segment_presence = {}
@@ -163,7 +175,7 @@ def detect_segments(digit_img, brightness):
163
  continue
164
  pixel_count = np.sum(region == 255)
165
  total_pixels = region.size
166
- segment_presence[name] = pixel_count / total_pixels > (0.25 if brightness < 100 else 0.45)
167
 
168
  digit_patterns = {
169
  '0': ('top', 'bottom', 'left_top', 'left_bottom', 'right_top', 'right_bottom'),
@@ -184,7 +196,7 @@ def detect_segments(digit_img, brightness):
184
  matches = sum(1 for segment in pattern if segment_presence.get(segment, False))
185
  non_matches_penalty = sum(1 for segment in segment_presence if segment not in pattern and segment_presence[segment])
186
  score = matches - 0.2 * non_matches_penalty
187
- if matches >= len(pattern) * 0.75:
188
  score += 1.0
189
  if score > max_score:
190
  max_score = score
@@ -198,20 +210,20 @@ def custom_seven_segment_ocr(img, roi_bbox):
198
  try:
199
  preprocessed = preprocess_image(img)
200
  brightness = estimate_brightness(img)
201
- thresh_value = 100 if brightness < 100 else 0
202
  _, thresh = cv2.threshold(preprocessed, thresh_value, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
203
- save_debug_image(thresh, "08_roi_thresh_for_digits")
204
 
205
- # Morphological operations to enhance digit segments
206
  kernel = np.ones((3, 3), np.uint8)
207
  thresh = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel, iterations=2)
208
- save_debug_image(thresh, "09_morph_closed")
209
 
210
  batch_size = max(4, min(16, int(img.shape[0] * img.shape[1] / 100000)))
211
  results = easyocr_reader.readtext(thresh, detail=1, paragraph=False,
212
- contrast_ths=0.3, adjust_contrast=1.0,
213
- text_threshold=0.6, mag_ratio=3.0,
214
- allowlist='0123456789.', batch_size=batch_size, y_ths=0.2)
215
 
216
  logging.info(f"EasyOCR results: {results}")
217
  if not results:
@@ -222,7 +234,7 @@ def custom_seven_segment_ocr(img, roi_bbox):
222
  for (bbox, text, conf) in results:
223
  (x1, y1), (x2, y2), (x3, y3), (x4, y4) = bbox
224
  h_bbox = max(y1, y2, y3, y4) - min(y1, y2, y3, y4)
225
- if len(text) == 1 and (text.isdigit() or text == '.') and h_bbox > 8:
226
  x_min, x_max = int(min(x1, x4)), int(max(x2, x3))
227
  y_min, y_max = int(min(y1, y2)), int(max(y3, y4))
228
  digits_info.append((x_min, x_max, y_min, y_max, text, conf))
@@ -235,8 +247,8 @@ def custom_seven_segment_ocr(img, roi_bbox):
235
  if x_max <= x_min or y_max <= y_min:
236
  continue
237
  digit_img_crop = thresh[y_min:y_max, x_min:x_max]
238
- save_debug_image(digit_img_crop, f"10_digit_crop_{idx}_{easyocr_char}")
239
- if easyocr_conf > 0.95 or easyocr_char == '.':
240
  recognized_text += easyocr_char
241
  else:
242
  digit_from_segments = detect_segments(digit_img_crop, brightness)
@@ -268,18 +280,18 @@ def extract_weight_from_image(pil_img):
268
  img = correct_rotation(img)
269
 
270
  brightness = estimate_brightness(img)
271
- conf_threshold = 0.7 if brightness > 150 else (0.6 if brightness > 80 else 0.4)
272
 
273
  roi_img, roi_bbox = detect_roi(img)
274
  if roi_bbox:
275
  roi_area = roi_bbox[2] * roi_bbox[3]
276
- conf_threshold *= 1.2 if roi_area > (img.shape[0] * img.shape[1] * 0.5) else 1.0
277
 
278
  custom_result = custom_seven_segment_ocr(roi_img, roi_bbox)
279
  if custom_result:
280
  try:
281
  weight = float(custom_result)
282
- if 0.001 <= weight <= 1000:
283
  logging.info(f"Custom OCR result: {custom_result}, Confidence: 95.0%")
284
  return custom_result, 95.0
285
  else:
@@ -292,14 +304,24 @@ def extract_weight_from_image(pil_img):
292
  block_size = max(11, min(31, int(roi_img.shape[0] / 20) * 2 + 1))
293
  final_roi = cv2.adaptiveThreshold(preprocessed_roi, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
294
  cv2.THRESH_BINARY_INV, block_size, 8)
295
- save_debug_image(final_roi, "11_fallback_adaptive_thresh")
296
 
297
  batch_size = max(4, min(16, int(roi_img.shape[0] * roi_img.shape[1] / 100000)))
298
  results = easyocr_reader.readtext(final_roi, detail=1, paragraph=False,
299
- contrast_ths=0.4, adjust_contrast=1.2,
300
- text_threshold=0.5, mag_ratio=4.0,
301
- allowlist='0123456789. kglb', batch_size=batch_size, y_ths=0.2)
302
 
 
 
 
 
 
 
 
 
 
 
303
  best_weight = None
304
  best_conf = 0.0
305
  best_score = 0.0
@@ -325,9 +347,9 @@ def extract_weight_from_image(pil_img):
325
  weight /= 1000 # Convert grams to kilograms
326
  elif unit == 'lb':
327
  weight *= 0.453592 # Convert pounds to kilograms
328
- range_score = 1.5 if 0.001 <= weight <= 1000 else 0.8
329
  digit_count = len(text.replace('.', ''))
330
- digit_score = 1.3 if 2 <= digit_count <= 7 else 0.9
331
  score = conf * range_score * digit_score
332
  if roi_bbox:
333
  (x_roi, y_roi, w_roi, h_roi) = roi_bbox
@@ -335,8 +357,8 @@ def extract_weight_from_image(pil_img):
335
  x_min, y_min = int(min(b[0] for b in bbox)), int(min(b[1] for b in bbox))
336
  x_max, y_max = int(max(b[0] for b in bbox)), int(max(b[1] for b in bbox))
337
  bbox_area = (x_max - x_min) * (y_max - y_min)
338
- if roi_area > 0 and bbox_area / roi_area < 0.05:
339
- score *= 0.6
340
  if score > best_score and conf > conf_threshold:
341
  best_weight = text
342
  best_conf = conf
@@ -361,8 +383,10 @@ def extract_weight_from_image(pil_img):
361
 
362
  try:
363
  final_weight = float(best_weight)
364
- if final_weight < 0.001 or final_weight > 1000:
365
- best_conf *= 0.7
 
 
366
  except ValueError:
367
  pass
368
 
 
31
  gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
32
  return np.mean(gray)
33
 
34
+ def preprocess_image(img, scale=1.0):
35
  """Preprocess image for better OCR accuracy."""
36
+ if scale != 1.0:
37
+ img = cv2.resize(img, None, fx=scale, fy=scale, interpolation=cv2.INTER_CUBIC)
38
+ save_debug_image(img, f"01_preprocess_scaled_{scale}")
39
  gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
40
  # Apply bilateral filter to preserve edges
41
+ denoised = cv2.bilateralFilter(gray, 9, 15, 15)
42
+ save_debug_image(denoised, "02_preprocess_bilateral")
43
  # Enhance contrast using CLAHE
44
+ clahe = cv2.createCLAHE(clipLimit=3.0, tileGridSize=(8, 8))
45
  enhanced = clahe.apply(denoised)
46
+ save_debug_image(enhanced, "03_preprocess_clahe")
47
  # Sharpen the image
48
  kernel_sharpening = np.array([[-1, -1, -1], [-1, 9, -1], [-1, -1, -1]])
49
  sharpened = cv2.filter2D(enhanced, -1, kernel_sharpening)
50
+ save_debug_image(sharpened, "04_preprocess_sharpened")
51
  return sharpened
52
 
53
  def correct_rotation(img):
54
  """Correct image rotation using Hough Transform."""
55
  try:
56
+ edges = cv2.Canny(cv2.cvtColor(img, cv2.COLOR_BGR2GRAY), 50, 150)
57
+ lines = cv2.HoughLinesP(edges, 1, np.pi / 180, threshold=80, minLineLength=50, maxLineGap=10)
58
  if lines is not None:
59
  angles = [np.arctan2(line[0][3] - line[0][1], line[0][2] - line[0][0]) * 180 / np.pi for line in lines]
60
+ angle = np.median(angles)
61
+ if abs(angle) > 3:
62
  (h, w) = img.shape[:2]
63
  center = (w // 2, h // 2)
64
  M = cv2.getRotationMatrix2D(center, angle, 1.0)
 
73
  def detect_roi(img):
74
  """Detect and crop the region of interest (likely the digital display)."""
75
  try:
76
+ save_debug_image(img, "05_original")
 
77
  brightness_map = cv2.GaussianBlur(cv2.cvtColor(img, cv2.COLOR_BGR2GRAY), (15, 15), 0)
78
 
79
+ # Try multiple scales for preprocessing
80
+ scales = [1.0, 1.5, 0.75]
81
+ for scale in scales:
82
+ preprocessed = preprocess_image(img, scale)
83
+ block_size = max(11, min(31, int(img.shape[0] / 20) * 2 + 1))
84
+ thresh = cv2.adaptiveThreshold(preprocessed, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
85
+ cv2.THRESH_BINARY_INV, block_size, 5)
86
+ _, otsu_thresh = cv2.threshold(preprocessed, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
87
+ combined_thresh = cv2.bitwise_and(thresh, otsu_thresh)
88
+ save_debug_image(combined_thresh, f"06_roi_combined_threshold_scale_{scale}")
89
+
90
+ # Morphological operations
91
+ kernel = np.ones((5, 5), np.uint8)
92
+ dilated = cv2.dilate(combined_thresh, kernel, iterations=2)
93
+ eroded = cv2.erode(dilated, kernel, iterations=1)
94
+ save_debug_image(eroded, f"07_roi_morphological_scale_{scale}")
95
+
96
+ contours, _ = cv2.findContours(eroded, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
 
 
 
 
 
 
 
 
 
 
97
 
98
+ if contours:
99
+ img_area = img.shape[0] * img.shape[1]
100
+ valid_contours = []
101
+ for c in contours:
102
+ area = cv2.contourArea(c)
103
+ x, y, w, h = cv2.boundingRect(c)
104
+ roi_brightness = np.mean(brightness_map[y:y+h, x:x+w] if scale == 1.0 else cv2.resize(brightness_map, (img.shape[1], img.shape[0])))
105
+ aspect_ratio = w / h
106
+ if (200 < area < (img_area * 0.95) and
107
+ 0.5 <= aspect_ratio <= 15.0 and w > 50 and h > 20 and roi_brightness > 60):
108
+ valid_contours.append((c, roi_brightness))
109
+ logging.debug(f"Contour: Scale={scale}, Area={area}, Aspect={aspect_ratio:.2f}, Brightness={roi_brightness:.2f}")
110
+
111
+ if valid_contours:
112
+ contour, _ = max(valid_contours, key=lambda x: x[1])
113
+ x, y, w, h = cv2.boundingRect(contour)
114
+ if scale != 1.0:
115
+ x, y, w, h = [int(v / scale) for v in (x, y, w, h)]
116
+ padding = 120
117
+ x, y = max(0, x - padding), max(0, y - padding)
118
+ w, h = min(w + 2 * padding, img.shape[1] - x), min(h + 2 * padding, img.shape[0] - y)
119
+ roi_img = img[y:y+h, x:x+w]
120
+ save_debug_image(roi_img, f"08_detected_roi_scale_{scale}")
121
+ logging.info(f"Detected ROI with dimensions: ({x}, {y}, {w}, {h}) at scale {scale}")
122
+ return roi_img, (x, y, w, h)
123
 
124
  logging.info("No suitable ROI found, attempting fallback criteria.")
125
  # Fallback with relaxed criteria
126
+ preprocessed = preprocess_image(img)
127
+ thresh = cv2.adaptiveThreshold(preprocessed, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
128
+ cv2.THRESH_BINARY_INV, block_size, 8)
129
+ save_debug_image(thresh, "06_roi_fallback_threshold")
130
+ contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
131
+ valid_contours = [c for c in contours if 100 < cv2.contourArea(c) < (img.shape[0] * img.shape[1] * 0.95) and
132
+ 0.3 <= cv2.boundingRect(c)[2]/cv2.boundingRect(c)[3] <= 20.0]
133
  if valid_contours:
134
  contour = max(valid_contours, key=cv2.contourArea)
135
  x, y, w, h = cv2.boundingRect(contour)
136
+ padding = 120
137
  x, y = max(0, x - padding), max(0, y - padding)
138
  w, h = min(w + 2 * padding, img.shape[1] - x), min(h + 2 * padding, img.shape[0] - y)
139
  roi_img = img[y:y+h, x:x+w]
140
+ save_debug_image(roi_img, "08_detected_roi_fallback")
141
  logging.info(f"Detected fallback ROI with dimensions: ({x}, {y}, {w}, {h})")
142
  return roi_img, (x, y, w, h)
143
 
144
  logging.info("No suitable ROI found, returning original image.")
145
+ save_debug_image(img, "08_no_roi_original_fallback")
146
  return img, None
147
  except Exception as e:
148
  logging.error(f"ROI detection failed: {str(e)}")
149
+ save_debug_image(img, "08_roi_detection_error_fallback")
150
  return img, None
151
 
152
  def detect_segments(digit_img, brightness):
153
  """Detect seven-segment patterns in a digit image."""
154
  h, w = digit_img.shape
155
+ if h < 10 or w < 8:
156
  return None
157
 
158
  segments = {
159
+ 'top': (int(w*0.1), int(w*0.9), 0, int(h*0.25)),
160
+ 'middle': (int(w*0.1), int(w*0.9), int(h*0.45), int(h*0.55)),
161
+ 'bottom': (int(w*0.1), int看到的: int(w*0.9), int(h*0.75), h),
162
+ 'left_top': (0, int(w*0.3), int(h*0.1), int(h*0.5)),
163
+ 'left_bottom': (0, int(w*0.3), int(h*0.5), int(h*0.9)),
164
+ 'right_top': (int(w*0.7), w, int(h*0.1), int(h*0.5)),
165
+ 'right_bottom': (int(w*0.7), w, int(h*0.5), int(h*0.9))
166
  }
167
 
168
  segment_presence = {}
 
175
  continue
176
  pixel_count = np.sum(region == 255)
177
  total_pixels = region.size
178
+ segment_presence[name] = pixel_count / total_pixels > (0.2 if brightness < 80 else 0.4)
179
 
180
  digit_patterns = {
181
  '0': ('top', 'bottom', 'left_top', 'left_bottom', 'right_top', 'right_bottom'),
 
196
  matches = sum(1 for segment in pattern if segment_presence.get(segment, False))
197
  non_matches_penalty = sum(1 for segment in segment_presence if segment not in pattern and segment_presence[segment])
198
  score = matches - 0.2 * non_matches_penalty
199
+ if matches >= len(pattern) * 0.7:
200
  score += 1.0
201
  if score > max_score:
202
  max_score = score
 
210
  try:
211
  preprocessed = preprocess_image(img)
212
  brightness = estimate_brightness(img)
213
+ thresh_value = 80 if brightness < 80 else 0
214
  _, thresh = cv2.threshold(preprocessed, thresh_value, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
215
+ save_debug_image(thresh, "09_roi_thresh_for_digits")
216
 
217
+ # Morphological operations
218
  kernel = np.ones((3, 3), np.uint8)
219
  thresh = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel, iterations=2)
220
+ save_debug_image(thresh, "10_morph_closed")
221
 
222
  batch_size = max(4, min(16, int(img.shape[0] * img.shape[1] / 100000)))
223
  results = easyocr_reader.readtext(thresh, detail=1, paragraph=False,
224
+ contrast_ths=0.2, adjust_contrast=1.2,
225
+ text_threshold=0.5, mag_ratio=4.0,
226
+ allowlist='0123456789.', batch_size=batch_size, y_ths=0.3)
227
 
228
  logging.info(f"EasyOCR results: {results}")
229
  if not results:
 
234
  for (bbox, text, conf) in results:
235
  (x1, y1), (x2, y2), (x3, y3), (x4, y4) = bbox
236
  h_bbox = max(y1, y2, y3, y4) - min(y1, y2, y3, y4)
237
+ if (text.isdigit() or text == '.') and h_bbox > 6:
238
  x_min, x_max = int(min(x1, x4)), int(max(x2, x3))
239
  y_min, y_max = int(min(y1, y2)), int(max(y3, y4))
240
  digits_info.append((x_min, x_max, y_min, y_max, text, conf))
 
247
  if x_max <= x_min or y_max <= y_min:
248
  continue
249
  digit_img_crop = thresh[y_min:y_max, x_min:x_max]
250
+ save_debug_image(digit_img_crop, f"11_digit_crop_{idx}_{easyocr_char}")
251
+ if easyocr_conf > 0.9 or easyocr_char == '.':
252
  recognized_text += easyocr_char
253
  else:
254
  digit_from_segments = detect_segments(digit_img_crop, brightness)
 
280
  img = correct_rotation(img)
281
 
282
  brightness = estimate_brightness(img)
283
+ conf_threshold = 0.7 if brightness > 150 else (0.5 if brightness > 80 else 0.3)
284
 
285
  roi_img, roi_bbox = detect_roi(img)
286
  if roi_bbox:
287
  roi_area = roi_bbox[2] * roi_bbox[3]
288
+ conf_threshold *= 1.1 if roi_area > (img.shape[0] * img.shape[1] * 0.5) else 1.0
289
 
290
  custom_result = custom_seven_segment_ocr(roi_img, roi_bbox)
291
  if custom_result:
292
  try:
293
  weight = float(custom_result)
294
+ if 0.001 <= weight <= 2000:
295
  logging.info(f"Custom OCR result: {custom_result}, Confidence: 95.0%")
296
  return custom_result, 95.0
297
  else:
 
304
  block_size = max(11, min(31, int(roi_img.shape[0] / 20) * 2 + 1))
305
  final_roi = cv2.adaptiveThreshold(preprocessed_roi, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
306
  cv2.THRESH_BINARY_INV, block_size, 8)
307
+ save_debug_image(final_roi, "12_fallback_adaptive_thresh")
308
 
309
  batch_size = max(4, min(16, int(roi_img.shape[0] * roi_img.shape[1] / 100000)))
310
  results = easyocr_reader.readtext(final_roi, detail=1, paragraph=False,
311
+ contrast_ths=0.3, adjust_contrast=1.2,
312
+ text_threshold=0.4, mag_ratio=5.0,
313
+ allowlist='0123456789. kglb', batch_size=batch_size, y_ths=0.3)
314
 
315
+ # Secondary EasyOCR pass with different parameters
316
+ if not results:
317
+ logging.info("First EasyOCR pass failed, trying with relaxed parameters.")
318
+ results = easyocr_reader.readtext(final_roi, detail=1, paragraph=False,
319
+ contrast_ths=0.2, adjust_contrast=1.5,
320
+ text_threshold=0.3, mag_ratio=6.0,
321
+ allowlist='0123456789. kglb', batch_size=batch_size, y_ths=0.4)
322
+ save_debug_image(final_roi, "12_fallback_adaptive_thresh_relaxed")
323
+
324
+ logging.info(f"EasyOCR results: {results}")
325
  best_weight = None
326
  best_conf = 0.0
327
  best_score = 0.0
 
347
  weight /= 1000 # Convert grams to kilograms
348
  elif unit == 'lb':
349
  weight *= 0.453592 # Convert pounds to kilograms
350
+ range_score = 1.5 if 0.001 <= weight <= 2000 else 0.7
351
  digit_count = len(text.replace('.', ''))
352
+ digit_score = 1.3 if 1 <= digit_count <= 8 else 0.8
353
  score = conf * range_score * digit_score
354
  if roi_bbox:
355
  (x_roi, y_roi, w_roi, h_roi) = roi_bbox
 
357
  x_min, y_min = int(min(b[0] for b in bbox)), int(min(b[1] for b in bbox))
358
  x_max, y_max = int(max(b[0] for b in bbox)), int(max(b[1] for b in bbox))
359
  bbox_area = (x_max - x_min) * (y_max - y_min)
360
+ if roi_area > 0 and bbox_area / roi_area < 0.03:
361
+ score *= 0.5
362
  if score > best_score and conf > conf_threshold:
363
  best_weight = text
364
  best_conf = conf
 
383
 
384
  try:
385
  final_weight = float(best_weight)
386
+ if final_weight < 0.001 or final_weight > 2000:
387
+ best_conf *= 0.6
388
+ elif final_weight == 0 and best_conf < 0.9:
389
+ best_conf *= 0.7 # Penalize zero weights with low confidence
390
  except ValueError:
391
  pass
392