APIMONSTER commited on
Commit
e20642b
Β·
verified Β·
1 Parent(s): 4c90346

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +78 -47
app.py CHANGED
@@ -1,125 +1,156 @@
1
  # app.py
 
2
  import numpy as np
3
  np.int = int # patch for PaddleOCR’s old np.int calls
4
 
5
- import cv2, json, tempfile, re
 
 
 
6
  import gradio as gr
7
  from ultralytics import YOLO
8
  from paddleocr import PaddleOCR
9
 
 
 
 
 
10
  # ─── 1) Load models ───────────────────────────────────────────────
 
11
  yolo = YOLO("models/best.pt")
12
 
13
- # Use recognition‐only mode (no detector), with angle‐cls to handle simple flips
14
  ocr = PaddleOCR(
15
- det=False, # skip built‐in detection
16
- rec=True, # enable recognition
17
  rec_model_dir="models/ocr_model",
18
- cls=True, # turn on angle classification
19
- use_angle_cls=True, # v2.x flag
20
- lang="en" # our char dict is basic Latin+digits
21
  )
 
 
 
22
 
23
  # ─── 2) Turkish plate formatter ────────────────────────────────────
24
  def format_turkish_plate(s: str) -> str:
 
25
  s = re.sub(r'[^A-Z0-9]', '', s.upper())
26
  m = re.match(r'^(\d{2})([A-Z]{1,3})(\d{2,4})$', s)
27
- return f"{m.group(1)} {m.group(2)} {m.group(3)}" if m else "Unknown"
 
 
 
28
 
29
  # ─── 3) Single‐image inference ─────────────────────────────────────
30
  def run_image(img, conf=0.25):
 
31
  bgr = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)
32
  res = yolo(bgr, conf=conf)[0]
33
  out = bgr.copy()
34
 
35
- for box, yolo_score in zip(res.boxes.xyxy.cpu().numpy(),
36
- res.boxes.conf.cpu().numpy()):
37
- x1,y1,x2,y2 = box.astype(int)
38
  crop = out[y1:y2, x1:x2]
39
  if crop.size == 0:
40
  continue
41
 
42
- plate_img = cv2.resize(crop, (128,32))
43
- # explicitly tell PaddleOCR: skip detection here
44
- recs = ocr.ocr(plate_img, det=False, cls=False)
 
45
 
 
46
  if recs and recs[0]:
47
- _, (raw, ocr_score) = recs[0]
48
  else:
49
- raw, ocr_score = "", 0.0
50
 
51
- plate = format_turkish_plate(raw)
52
  label = f"{plate} ({ocr_score:.2f})"
53
 
54
- cv2.rectangle(out, (x1,y1),(x2,y2), (0,255,0), 2)
55
- cv2.putText(out, label, (x1,y1-5),
56
- cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0,255,0), 2)
 
57
 
 
58
  return cv2.cvtColor(out, cv2.COLOR_BGR2RGB), f"{len(res.boxes)} plate(s) detected"
59
 
60
- # ─── 4) Video inference (same pattern) ───────────────────────────
 
61
  def run_video(video_file, conf=0.25):
62
  cap = cv2.VideoCapture(video_file)
63
  fps = cap.get(cv2.CAP_PROP_FPS)
64
- w,h = int(cap.get(3)), int(cap.get(4))
65
- outfp = tempfile.NamedTemporaryFile(suffix=".mp4", delete=False).name
66
- writer = cv2.VideoWriter(outfp, cv2.VideoWriter_fourcc(*"mp4v"), fps, (w,h))
67
- records, idx = [], 0
68
 
 
69
  while True:
70
  ret, frame = cap.read()
71
  if not ret:
72
  break
73
- idx += 1
74
- t = idx / fps
75
 
76
  res = yolo(frame, conf=conf)[0]
77
- for (x1,y1,x2,y2) in res.boxes.xyxy.cpu().numpy().astype(int):
78
  crop = frame[y1:y2, x1:x2]
79
  if crop.size == 0:
80
  continue
81
-
82
- plate_img = cv2.resize(crop, (128,32))
83
- recs = ocr.ocr(plate_img, det=False, cls=False)
84
 
85
  if recs and recs[0]:
86
- _, (raw, ocr_score) = recs[0]
87
  else:
88
- raw, ocr_score = "", 0.0
89
 
90
- plate = format_turkish_plate(raw)
91
  if plate != "Unknown":
92
- records.append({"time_s":round(t,2), "plate":plate, "conf":round(ocr_score,3)})
 
 
 
 
93
 
94
- cv2.rectangle(frame, (x1,y1),(x2,y2), (0,255,0), 2)
95
- cv2.putText(frame, plate, (x1,y1-5),
96
- cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0,255,0), 2)
97
 
98
  writer.write(frame)
99
 
100
  cap.release()
101
  writer.release()
102
- with open("output.json","w") as f:
 
 
103
  json.dump(records, f, indent=2)
104
- return outfp
 
 
105
 
106
  # ─── 5) Gradio UI ─────────────────────────────────────────────────
107
  with gr.Blocks() as demo:
108
- gr.Markdown("## πŸš— License Plate Detection + Recognition")
 
109
  with gr.Row():
110
  with gr.Column():
111
  img_in = gr.Image(type="numpy", label="Upload Image")
112
  vid_in = gr.File(label="Upload Video (.mp4)")
113
- conf = gr.Slider(0,1,0.25,0.01, label="YOLO Confidence")
114
- b1 = gr.Button("Run Image")
115
- b2 = gr.Button("Run Video")
116
  with gr.Column():
117
  img_out = gr.Image(type="numpy", label="Annotated Image")
118
  vid_out = gr.Video(label="Annotated Video")
119
  status = gr.Textbox(label="Status / JSON Path")
120
 
121
- b1.click(run_image, [img_in,conf], [img_out,status])
122
- b2.click(run_video, [vid_in,conf], [vid_out,status])
123
 
124
- if __name__=="__main__":
125
  demo.launch()
 
1
  # app.py
2
+
3
  import numpy as np
4
  np.int = int # patch for PaddleOCR’s old np.int calls
5
 
6
+ import cv2
7
+ import json
8
+ import tempfile
9
+ import re
10
  import gradio as gr
11
  from ultralytics import YOLO
12
  from paddleocr import PaddleOCR
13
 
14
+ # ─── 0) Character map ───────────────────────────────────────────────
15
+ # Your plate charset: digits + uppercase + space
16
+ MY_CHAR_LIST = list("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ ")
17
+
18
  # ─── 1) Load models ───────────────────────────────────────────────
19
+ # YOLO for detection
20
  yolo = YOLO("models/best.pt")
21
 
22
+ # PaddleOCR for recognition only, with angle‐classify turned on
23
  ocr = PaddleOCR(
24
+ det=False, # no detector
25
+ rec=True, # recognition only
26
  rec_model_dir="models/ocr_model",
27
+ cls=True, # turn on angle classifier
28
+ use_angle_cls=True, # v2.x flag
29
+ use_space_char=True # allow recognizing spaces
30
  )
31
+ # Override the internal character list so it matches your plate labels exactly
32
+ ocr.text_recognizer.character = MY_CHAR_LIST
33
+
34
 
35
  # ─── 2) Turkish plate formatter ────────────────────────────────────
36
  def format_turkish_plate(s: str) -> str:
37
+ # uppercase, strip any non-alnum, then regex into β€œAA 123 BB” style
38
  s = re.sub(r'[^A-Z0-9]', '', s.upper())
39
  m = re.match(r'^(\d{2})([A-Z]{1,3})(\d{2,4})$', s)
40
+ if m:
41
+ return f"{m.group(1)} {m.group(2)} {m.group(3)}"
42
+ return "Unknown"
43
+
44
 
45
  # ─── 3) Single‐image inference ─────────────────────────────────────
46
  def run_image(img, conf=0.25):
47
+ # convert and detect
48
  bgr = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)
49
  res = yolo(bgr, conf=conf)[0]
50
  out = bgr.copy()
51
 
52
+ for box, score in zip(res.boxes.xyxy.cpu().numpy(),
53
+ res.boxes.conf.cpu().numpy()):
54
+ x1, y1, x2, y2 = box.astype(int)
55
  crop = out[y1:y2, x1:x2]
56
  if crop.size == 0:
57
  continue
58
 
59
+ # resize to your OCR’s input
60
+ plate_img = cv2.resize(crop, (128, 32))
61
+ # run recognition + angle correction
62
+ recs = ocr.ocr(plate_img, cls=True)
63
 
64
+ # unpack or default
65
  if recs and recs[0]:
66
+ raw_text, ocr_score = recs[0][1][0], recs[0][1][1]
67
  else:
68
+ raw_text, ocr_score = "", 0.0
69
 
70
+ plate = format_turkish_plate(raw_text)
71
  label = f"{plate} ({ocr_score:.2f})"
72
 
73
+ # draw
74
+ cv2.rectangle(out, (x1, y1), (x2, y2), (0, 255, 0), 2)
75
+ cv2.putText(out, label, (x1, y1 - 5),
76
+ cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2)
77
 
78
+ # back to RGB for Gradio
79
  return cv2.cvtColor(out, cv2.COLOR_BGR2RGB), f"{len(res.boxes)} plate(s) detected"
80
 
81
+
82
+ # ─── 4) Video inference ───────────────────────────────────────────
83
  def run_video(video_file, conf=0.25):
84
  cap = cv2.VideoCapture(video_file)
85
  fps = cap.get(cv2.CAP_PROP_FPS)
86
+ w, h = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)), int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
87
+ tmp_out = tempfile.NamedTemporaryFile(suffix=".mp4", delete=False).name
88
+ writer = cv2.VideoWriter(tmp_out, cv2.VideoWriter_fourcc(*"mp4v"), fps, (w, h))
89
+ records = []
90
 
91
+ frame_idx = 0
92
  while True:
93
  ret, frame = cap.read()
94
  if not ret:
95
  break
96
+ frame_idx += 1
97
+ t = frame_idx / fps
98
 
99
  res = yolo(frame, conf=conf)[0]
100
+ for (x1, y1, x2, y2) in res.boxes.xyxy.cpu().numpy().astype(int):
101
  crop = frame[y1:y2, x1:x2]
102
  if crop.size == 0:
103
  continue
104
+ plate_img = cv2.resize(crop, (128, 32))
105
+ recs = ocr.ocr(plate_img, cls=True)
 
106
 
107
  if recs and recs[0]:
108
+ raw_text, ocr_score = recs[0][1][0], recs[0][1][1]
109
  else:
110
+ raw_text, ocr_score = "", 0.0
111
 
112
+ plate = format_turkish_plate(raw_text)
113
  if plate != "Unknown":
114
+ records.append({
115
+ "time_s": round(t, 2),
116
+ "plate": plate,
117
+ "conf": round(ocr_score, 3)
118
+ })
119
 
120
+ cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 2)
121
+ cv2.putText(frame, plate, (x1, y1 - 5),
122
+ cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2)
123
 
124
  writer.write(frame)
125
 
126
  cap.release()
127
  writer.release()
128
+
129
+ # save JSON log
130
+ with open("output.json", "w") as f:
131
  json.dump(records, f, indent=2)
132
+
133
+ return tmp_out, "Done"
134
+
135
 
136
  # ─── 5) Gradio UI ─────────────────────────────────────────────────
137
  with gr.Blocks() as demo:
138
+ gr.Markdown("## πŸš— License Plate Detection & Recognition")
139
+
140
  with gr.Row():
141
  with gr.Column():
142
  img_in = gr.Image(type="numpy", label="Upload Image")
143
  vid_in = gr.File(label="Upload Video (.mp4)")
144
+ conf = gr.Slider(0, 1, 0.25, 0.01, label="YOLO Confidence")
145
+ btn_i = gr.Button("Run Image")
146
+ btn_v = gr.Button("Run Video")
147
  with gr.Column():
148
  img_out = gr.Image(type="numpy", label="Annotated Image")
149
  vid_out = gr.Video(label="Annotated Video")
150
  status = gr.Textbox(label="Status / JSON Path")
151
 
152
+ btn_i.click(run_image, [img_in, conf], [img_out, status])
153
+ btn_v.click(run_video, [vid_in, conf], [vid_out, status])
154
 
155
+ if __name__ == "__main__":
156
  demo.launch()