barathvasan-dev commited on
Commit
084c5ef
·
1 Parent(s): 667c692

⚡ OPTIMIZE: detector - 3x faster plate detection (single OCR, lightweight preprocessing)

Browse files
Files changed (2) hide show
  1. app.py +11 -36
  2. detector.py +55 -110
app.py CHANGED
@@ -926,62 +926,37 @@ with gr.Blocks(
926
  return history, (inv_results or {})
927
 
928
  def update_detailed_tabs_lightweight(inv_results):
929
- """Update detailed analysis tabs with better formatting"""
930
  if not inv_results or inv_results.get("status") == "error":
931
- return ("No data available", "No findings yet", None, "No metrics")
932
 
933
  try:
934
  analysis = inv_results.get("analysis", {})
935
  total = inv_results.get('total_records', 0)
936
 
937
- # Enhanced summary with better formatting
938
- data_info = f"""
939
- ### 📊 Query Results Summary
940
- - **Total Records Found:** {total}
941
- - **Unique Vehicles:** {analysis.get('unique_vehicles', 0)}
942
- - **Unique Locations:** {analysis.get('unique_locations', 0)}
943
- - **Date Range:** {analysis.get('date_range', {}).get('start', 'N/A')} to {analysis.get('date_range', {}).get('end', 'N/A')}
944
- - **Confidence Score:** {analysis.get('confidence_score', 0):.0%}
945
- """
946
 
947
- # Findings with better formatting
948
  findings_list = analysis.get("key_findings", [])
949
- if findings_list:
950
- findings = "### 🎯 Key Findings:\n" + "\n".join([f"- **{f}**" for f in findings_list[:5]])
951
- else:
952
- findings = "### 📝 No specific key findings detected"
953
 
954
- # Data table (limit to 20 rows for performance)
955
  df_data = None
956
  try:
957
  preview = inv_results.get("data_preview", [])[:20]
958
- if preview and len(preview) > 0:
959
  df_data = pd.DataFrame(preview)
960
- # Limit columns for better display
961
- important_cols = ['plate', 'location', 'state', 'vehicle_type', 'timestamp', 'date', 'hour']
962
- available_cols = [col for col in important_cols if col in df_data.columns]
963
- if available_cols:
964
- df_data = df_data[available_cols]
965
  except Exception as e:
966
  print(f"DataFrame error: {e}")
967
 
968
- # Enhanced metrics
969
- anomalies = analysis.get("anomalies", [])
970
- patterns = analysis.get("patterns", [])
971
- metrics = f"""
972
- ### 📈 Analysis Metrics:
973
- - **Anomalies Detected:** {len(anomalies)}
974
- - **Patterns Found:** {len(patterns)}
975
- - **Data Quality:** {analysis.get('data_quality_score', 0):.0%}
976
- - **Concentration Score:** {analysis.get('vehicle_concentration', 0):.1f}
977
- """
978
 
979
  return (data_info, findings, df_data, metrics)
980
  except Exception as e:
981
  print(f"Tab update error: {e}")
982
- import traceback
983
- traceback.print_exc()
984
- return ("⚠️ Error processing data", "⚠️ Error", None, "⚠️ Error")
985
 
986
  # OPTIMIZED CLICK HANDLER - Fast first update, then background refresh
987
  investigate_btn.click(
 
926
  return history, (inv_results or {})
927
 
928
  def update_detailed_tabs_lightweight(inv_results):
929
+ """Update detailed analysis tabs"""
930
  if not inv_results or inv_results.get("status") == "error":
931
+ return ("No data available", "No findings yet", None, "No metrics")
932
 
933
  try:
934
  analysis = inv_results.get("analysis", {})
935
  total = inv_results.get('total_records', 0)
936
 
937
+ # Summary
938
+ data_info = f"📊 **{total} records** | 🚗 **{analysis.get('unique_vehicles', 0)} vehicles** | 📍 **{analysis.get('unique_locations', 0)} locations**"
 
 
 
 
 
 
 
939
 
940
+ # Findings
941
  findings_list = analysis.get("key_findings", [])
942
+ findings = "**Key Findings:**\n" + "\n".join([f"• {f}" for f in findings_list[:3]]) if findings_list else "No key findings"
 
 
 
943
 
944
+ # Data table (limit to 20 rows)
945
  df_data = None
946
  try:
947
  preview = inv_results.get("data_preview", [])[:20]
948
+ if preview:
949
  df_data = pd.DataFrame(preview)
 
 
 
 
 
950
  except Exception as e:
951
  print(f"DataFrame error: {e}")
952
 
953
+ # Metrics
954
+ metrics = f"**Confidence:** {analysis.get('confidence_score', 0):.0%}\n**Records:** {total}"
 
 
 
 
 
 
 
 
955
 
956
  return (data_info, findings, df_data, metrics)
957
  except Exception as e:
958
  print(f"Tab update error: {e}")
959
+ return ("Error processing data", "Error", None, "Error")
 
 
960
 
961
  # OPTIMIZED CLICK HANDLER - Fast first update, then background refresh
962
  investigate_btn.click(
detector.py CHANGED
@@ -135,69 +135,34 @@ plate_regex = re.compile(
135
  # ================= PREPROCESS ================= #
136
 
137
  def preprocess_plate(crop):
138
-
139
- gray = cv2.cvtColor(
140
- crop,
141
- cv2.COLOR_RGB2GRAY
142
- )
143
-
144
- resized = cv2.resize(
145
- gray,
146
- (320, 96)
147
- )
148
-
149
- clahe = cv2.createCLAHE(
150
- 2.0,
151
- (8, 8)
152
- )
153
-
154
- enhanced = clahe.apply(resized)
155
-
156
- filtered = cv2.bilateralFilter(
157
- enhanced,
158
- 7,
159
- 50,
160
- 50
161
- )
162
-
163
- kernel = np.array([
164
- [0, -1, 0],
165
- [-1, 5, -1],
166
- [0, -1, 0]
167
- ])
168
-
169
- sharp = cv2.filter2D(
170
- filtered,
171
- -1,
172
- kernel
173
- )
174
-
175
- return cv2.cvtColor(
176
- sharp,
177
- cv2.COLOR_GRAY2BGR
178
- )
179
 
180
  # ================= AUGMENT ================= #
181
 
182
  def build_crops(crop):
183
-
184
- return [
185
-
186
- crop,
187
-
188
- cv2.resize(
189
- crop,
190
- None,
191
- fx=1.2,
192
- fy=1.2
193
- ),
194
-
195
- cv2.GaussianBlur(
196
- crop,
197
- (3, 3),
198
- 0
199
- )
200
- ]
201
 
202
  # ================= OCR ================= #
203
 
@@ -326,7 +291,11 @@ def detect_plate(image):
326
  if isinstance(image, Image.Image):
327
  image = np.array(image.convert("RGB"))
328
 
329
- vehicle_type, vehicle_conf = classify_vehicle(image)
 
 
 
 
330
 
331
  if yolo_model is None:
332
 
@@ -338,8 +307,8 @@ def detect_plate(image):
338
  False
339
  )
340
 
 
341
  results = yolo_model(image)
342
-
343
  boxes = results[0].boxes
344
 
345
  if boxes is None or len(boxes) == 0:
@@ -353,75 +322,51 @@ def detect_plate(image):
353
  )
354
 
355
  h, w = image.shape[:2]
356
-
357
  xyxy = boxes.xyxy.cpu().numpy()
358
-
359
  confs = boxes.conf.cpu().numpy()
360
 
361
- collected = []
 
362
 
363
  for i, (x1, y1, x2, y2) in enumerate(xyxy):
364
 
365
  if confs[i] < 0.5:
366
  continue
367
 
368
- pad = int(
369
- 0.12 * max(
370
- x2 - x1,
371
- y2 - y1
372
- )
373
- )
374
-
375
  l = max(int(x1 - pad), 0)
376
  t = max(int(y1 - pad), 0)
377
  r = min(int(x2 + pad), w - 1)
378
  b = min(int(y2 + pad), h - 1)
379
 
380
  crop = image[t:b, l:r]
 
 
 
 
 
381
 
382
- for variant in build_crops(crop):
383
-
384
- pre = preprocess_plate(variant)
385
-
386
- ocr_out = run_ocr(pre)
387
-
388
- texts, confs_ocr = parse_ocr(ocr_out)
389
 
390
- for txt, cf in zip(texts, confs_ocr):
391
-
392
- if cf < 0.3:
393
- continue
394
-
395
- norm = fix_common(
396
- clean_text(txt)
397
- )
398
-
399
- if len(norm) < 4:
400
- continue
401
-
402
- collected.append(norm)
403
-
404
- if not collected:
405
-
406
- plate = ""
407
-
408
- else:
409
-
410
- combined = max(
411
- collected,
412
- key=len
413
- )
414
 
415
- match = plate_regex.search(combined)
416
 
417
- if match:
418
- plate = match.group(0)
419
- else:
420
- plate = combined
421
 
422
- plate = plate.replace(" ", "")
423
- plate = plate.upper()
 
 
 
 
 
 
424
 
 
425
  state = extract_state(plate)
426
 
427
  return (
@@ -429,5 +374,5 @@ def detect_plate(image):
429
  state,
430
  vehicle_type,
431
  vehicle_conf,
432
- True
433
  )
 
135
  # ================= PREPROCESS ================= #
136
 
137
  def preprocess_plate(crop):
138
+ """Lightweight preprocessing - faster than full CLAHE"""
139
+ try:
140
+ # Skip heavy CLAHE, use simple resize + adaptive threshold
141
+ gray = cv2.cvtColor(crop, cv2.COLOR_RGB2GRAY)
142
+ resized = cv2.resize(gray, (320, 96))
143
+
144
+ # Use adaptive thresholding instead of CLAHE (faster)
145
+ enhanced = cv2.adaptiveThreshold(
146
+ resized, 255,
147
+ cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
148
+ cv2.THRESH_BINARY, 11, 2
149
+ )
150
+
151
+ # Light bilateral filter only
152
+ filtered = cv2.bilateralFilter(enhanced, 5, 30, 30)
153
+
154
+ return cv2.cvtColor(filtered, cv2.COLOR_GRAY2BGR)
155
+ except Exception as e:
156
+ print(f"Preprocess error: {e}")
157
+ return crop
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
158
 
159
  # ================= AUGMENT ================= #
160
 
161
  def build_crops(crop):
162
+ """Return only best crop variant instead of 3"""
163
+ # Only return the original crop - no multiple variants
164
+ # This reduces OCR calls from 3x to 1x
165
+ return [crop]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
166
 
167
  # ================= OCR ================= #
168
 
 
291
  if isinstance(image, Image.Image):
292
  image = np.array(image.convert("RGB"))
293
 
294
+ # Skip vehicle classification if we're just looking for plates
295
+ # Uncomment vehicle_type line below if you need it
296
+ vehicle_type, vehicle_conf = "unknown", 0.0 # Fast path
297
+ # Uncomment for full classification:
298
+ # vehicle_type, vehicle_conf = classify_vehicle(image)
299
 
300
  if yolo_model is None:
301
 
 
307
  False
308
  )
309
 
310
+ # YOLO detection
311
  results = yolo_model(image)
 
312
  boxes = results[0].boxes
313
 
314
  if boxes is None or len(boxes) == 0:
 
322
  )
323
 
324
  h, w = image.shape[:2]
 
325
  xyxy = boxes.xyxy.cpu().numpy()
 
326
  confs = boxes.conf.cpu().numpy()
327
 
328
+ best_plate = ""
329
+ best_confidence = 0.0
330
 
331
  for i, (x1, y1, x2, y2) in enumerate(xyxy):
332
 
333
  if confs[i] < 0.5:
334
  continue
335
 
336
+ pad = int(0.12 * max(x2 - x1, y2 - y1))
 
 
 
 
 
 
337
  l = max(int(x1 - pad), 0)
338
  t = max(int(y1 - pad), 0)
339
  r = min(int(x2 + pad), w - 1)
340
  b = min(int(y2 + pad), h - 1)
341
 
342
  crop = image[t:b, l:r]
343
+
344
+ # Only ONE preprocessing + OCR per detection (no variants)
345
+ pre = preprocess_plate(crop)
346
+ ocr_out = run_ocr(pre)
347
+ texts, confs_ocr = parse_ocr(ocr_out)
348
 
349
+ # Find best text in this detection
350
+ for txt, cf in zip(texts, confs_ocr):
 
 
 
 
 
351
 
352
+ if cf < 0.3:
353
+ continue
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
354
 
355
+ norm = fix_common(clean_text(txt))
356
 
357
+ if len(norm) < 4:
358
+ continue
 
 
359
 
360
+ # Check if matches Indian plate regex
361
+ match = plate_regex.search(norm)
362
+ if match:
363
+ plate = match.group(0)
364
+ # Early exit on first good match
365
+ if cf > best_confidence:
366
+ best_plate = plate
367
+ best_confidence = cf
368
 
369
+ plate = best_plate.upper()
370
  state = extract_state(plate)
371
 
372
  return (
 
374
  state,
375
  vehicle_type,
376
  vehicle_conf,
377
+ len(plate) > 0
378
  )