barathvasan-dev commited on
Commit
fadf3f5
·
1 Parent(s): c47aa92

Improve OCR and set python 3.10

Browse files
Files changed (2) hide show
  1. README.md +1 -1
  2. app.py +83 -13
README.md CHANGED
@@ -5,7 +5,7 @@ colorFrom: gray
5
  colorTo: gray
6
  sdk: gradio
7
  sdk_version: 6.14.0
8
- python_version: '3.13'
9
  app_file: app.py
10
  pinned: false
11
  license: mit
 
5
  colorTo: gray
6
  sdk: gradio
7
  sdk_version: 6.14.0
8
+ python_version: '3.10'
9
  app_file: app.py
10
  pinned: false
11
  license: mit
app.py CHANGED
@@ -25,7 +25,9 @@ def run_ocr(image_bgr):
25
  return ocr.ocr(image_bgr, cls=True)
26
  except TypeError:
27
  return ocr.ocr(image_bgr)
28
- plate_regex = re.compile(r"[A-Z]{2}[0-9]{2}[A-Z]{1,2}[0-9]{4}")
 
 
29
 
30
 
31
  def preprocess_plate(crop_rgb):
@@ -40,6 +42,29 @@ def preprocess_plate(crop_rgb):
40
  return cv2.cvtColor(filtered, cv2.COLOR_GRAY2BGR)
41
 
42
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
  def clean_text(text):
44
  return re.sub(r"[^A-Z0-9]", "", text.upper())
45
 
@@ -54,6 +79,33 @@ def fix_common(text):
54
  )
55
 
56
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
  def select_best(plates):
58
  if not plates:
59
  return "", None
@@ -78,7 +130,12 @@ def detect(image):
78
 
79
  height, width = image.shape[:2]
80
  plates = []
81
- for idx, (x1, y1, x2, y2) in enumerate(boxes.xyxy.cpu().numpy(), start=1):
 
 
 
 
 
82
  pad = int(0.03 * max(x2 - x1, y2 - y1))
83
  left = max(int(x1) - pad, 0)
84
  top = max(int(y1) - pad, 0)
@@ -88,10 +145,28 @@ def detect(image):
88
  continue
89
 
90
  crop = image[top:bottom, left:right]
91
- prepped = preprocess_plate(crop)
92
- ocr_out = run_ocr(prepped)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
93
 
94
- if not ocr_out or not ocr_out[0]:
95
  plates.append(
96
  {
97
  "box": idx,
@@ -103,17 +178,12 @@ def detect(image):
103
  )
104
  continue
105
 
106
- best = max(ocr_out[0], key=lambda item: item[1][1])
107
- raw_text = best[1][0]
108
- confidence = float(best[1][1])
109
- normalized = fix_common(clean_text(raw_text))
110
-
111
  plates.append(
112
  {
113
  "box": idx,
114
- "raw": raw_text,
115
- "normalized": normalized,
116
- "confidence": confidence,
117
  "bbox": [left, top, right, bottom],
118
  }
119
  )
 
25
  return ocr.ocr(image_bgr, cls=True)
26
  except TypeError:
27
  return ocr.ocr(image_bgr)
28
+
29
+
30
+ plate_regex = re.compile(r"[A-Z]{2}\d{1,2}[A-Z]{1,3}\d{3,4}")
31
 
32
 
33
  def preprocess_plate(crop_rgb):
 
42
  return cv2.cvtColor(filtered, cv2.COLOR_GRAY2BGR)
43
 
44
 
45
+ def deskew(image_bgr):
46
+ gray = cv2.cvtColor(image_bgr, cv2.COLOR_BGR2GRAY)
47
+ coords = np.column_stack(np.where(gray > 0))
48
+ if len(coords) < 10:
49
+ return image_bgr
50
+
51
+ angle = cv2.minAreaRect(coords)[-1]
52
+ if angle < -45:
53
+ angle = -(90 + angle)
54
+ else:
55
+ angle = -angle
56
+
57
+ h, w = image_bgr.shape[:2]
58
+ matrix = cv2.getRotationMatrix2D((w // 2, h // 2), angle, 1.0)
59
+ return cv2.warpAffine(
60
+ image_bgr,
61
+ matrix,
62
+ (w, h),
63
+ flags=cv2.INTER_CUBIC,
64
+ borderMode=cv2.BORDER_REPLICATE,
65
+ )
66
+
67
+
68
  def clean_text(text):
69
  return re.sub(r"[^A-Z0-9]", "", text.upper())
70
 
 
79
  )
80
 
81
 
82
+ def smart_fix(text):
83
+ if len(text) >= 10:
84
+ chars = list(text)
85
+ chars[2] = chars[2].replace("O", "0")
86
+ chars[3] = chars[3].replace("O", "0")
87
+ return "".join(chars)
88
+ return text
89
+
90
+
91
+ def build_crops(crop_bgr):
92
+ scaled = cv2.resize(crop_bgr, None, fx=1.2, fy=1.2, interpolation=cv2.INTER_CUBIC)
93
+ blurred = cv2.GaussianBlur(crop_bgr, (3, 3), 0)
94
+ return [crop_bgr, scaled, blurred]
95
+
96
+
97
+ def combine_ocr_text(ocr_out):
98
+ if not ocr_out or not ocr_out[0]:
99
+ return "", 0.0
100
+ texts = [item[1][0] for item in ocr_out[0] if item and item[1]]
101
+ confidences = [float(item[1][1]) for item in ocr_out[0] if item and item[1]]
102
+ if not texts or not confidences:
103
+ return "", 0.0
104
+ full_text = "".join(texts)
105
+ avg_conf = sum(confidences) / len(confidences)
106
+ return full_text, avg_conf
107
+
108
+
109
  def select_best(plates):
110
  if not plates:
111
  return "", None
 
130
 
131
  height, width = image.shape[:2]
132
  plates = []
133
+ xyxy = boxes.xyxy.cpu().numpy()
134
+ confs = boxes.conf.cpu().numpy()
135
+
136
+ for idx, (x1, y1, x2, y2) in enumerate(xyxy, start=1):
137
+ if float(confs[idx - 1]) < 0.5:
138
+ continue
139
  pad = int(0.03 * max(x2 - x1, y2 - y1))
140
  left = max(int(x1) - pad, 0)
141
  top = max(int(y1) - pad, 0)
 
145
  continue
146
 
147
  crop = image[top:bottom, left:right]
148
+ best_candidate = None
149
+
150
+ for variant in build_crops(crop):
151
+ prepped = preprocess_plate(variant)
152
+ prepped = deskew(prepped)
153
+ ocr_out = run_ocr(prepped)
154
+ raw_text, confidence = combine_ocr_text(ocr_out)
155
+ if not raw_text:
156
+ continue
157
+
158
+ normalized = smart_fix(fix_common(clean_text(raw_text)))
159
+ if len(normalized) < 8 or confidence < 0.5:
160
+ continue
161
+
162
+ if best_candidate is None or confidence > best_candidate["confidence"]:
163
+ best_candidate = {
164
+ "raw": raw_text,
165
+ "normalized": normalized,
166
+ "confidence": confidence,
167
+ }
168
 
169
+ if best_candidate is None:
170
  plates.append(
171
  {
172
  "box": idx,
 
178
  )
179
  continue
180
 
 
 
 
 
 
181
  plates.append(
182
  {
183
  "box": idx,
184
+ "raw": best_candidate["raw"],
185
+ "normalized": best_candidate["normalized"],
186
+ "confidence": best_candidate["confidence"],
187
  "bbox": [left, top, right, bottom],
188
  }
189
  )