APIMONSTER commited on
Commit
aec9462
·
verified ·
1 Parent(s): 028c92b

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +75 -72
app.py CHANGED
@@ -1,7 +1,5 @@
1
  # app.py
2
- import re
3
- import json
4
- import tempfile
5
  import cv2
6
  import numpy as np
7
  import gradio as gr
@@ -17,125 +15,130 @@ CHAR_LIST = list("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ ")
17
  # ─── 2) Load YOLOv8 detector
18
  yolo = YOLO("models/best.pt")
19
 
20
- # ─── 3) Init PaddleOCR recognition-only
21
  ocr = PaddleOCR(
22
- det=False, # sadece rec
23
- rec=True,
24
  rec_model_dir="models/ocr_model",
25
- rec_image_shape="3,32,128", # eğitimde kullandığın input boyutu
26
- cls=True,
27
  use_angle_cls=True,
28
  use_space_char=True
29
  )
30
- ocr.text_recognizer.character = CHAR_LIST # kendi karakter kümen
 
31
 
32
- # ─── 4) OCR çıktısını normalize eden fonksiyon
33
  def normalize_ocr(recs):
 
 
 
 
 
 
 
 
34
  if not recs:
35
  return "", 0.0
36
  first = recs[0]
37
- # ["TEXT", score]
38
  if isinstance(first, (list,tuple)) and len(first)==2 and isinstance(first[0], str):
39
  return first[0], float(first[1])
40
- # [box, ("TEXT",score)] veya [box, ["TEXT",score]]
41
  if isinstance(first, (list,tuple)) and len(first)==2 and isinstance(first[1], (list,tuple)):
42
  return first[1][0], float(first[1][1])
43
  return "", 0.0
44
 
45
- # ─── 5) Türk plaka formatlayıcı (gevşetilmiş son grup)
46
  def format_plate(s: str) -> str:
 
47
  s = re.sub(r'[^A-Z0-9]', '', s.upper())
48
- # son grup artık harf veya rakamı da kabul ediyor
49
- m = re.match(r'^(\d{2})([A-Z]{1,3})([A-Z0-9]{2,4})$', s)
50
  return f"{m.group(1)} {m.group(2)} {m.group(3)}" if m else "Unknown"
51
 
52
- # ─── 6) Tek resim için inference
53
  def run_image(img, conf=0.25):
 
54
  bgr = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)
55
  res = yolo(bgr, conf=conf)[0]
56
  out = bgr.copy()
57
 
58
- # YOLO'nun bulduğu tüm kutuları al, en büyüğünü seç
59
- boxes = res.boxes.xyxy.cpu().numpy().astype(int)
60
- if boxes.shape[0] > 0:
61
- areas = (boxes[:,2] - boxes[:,0]) * (boxes[:,3] - boxes[:,1])
62
- i = int(np.argmax(areas))
63
- x1,y1,x2,y2 = boxes[i]
64
  crop = out[y1:y2, x1:x2]
65
-
66
- if crop.size > 0:
67
- plate_img = cv2.resize(crop, (128,32))
68
- try:
69
- recs = ocr.ocr(plate_img, det=False, cls=True)
70
- except Exception:
71
- recs = []
72
- txt, score = normalize_ocr(recs)
73
- plate = format_plate(txt)
74
- label = f"{plate} ({score:.2f})"
75
-
76
- cv2.rectangle(out, (x1,y1),(x2,y2), (0,255,0), 2)
77
- cv2.putText(out, label, (x1, y1-8),
78
- cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0,255,0), 2)
79
-
80
- return cv2.cvtColor(out, cv2.COLOR_BGR2RGB), f"{len(boxes)} plate(s) detected"
81
-
82
- # ─── 7) Video için inference (aynı “en büyük kutu” mantığı)
 
 
 
 
83
  def run_video(video_file, conf=0.25):
84
  cap = cv2.VideoCapture(video_file)
85
  fps = cap.get(cv2.CAP_PROP_FPS) or 30
86
  w = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
87
  h = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
 
88
  out_path = tempfile.NamedTemporaryFile(suffix=".mp4", delete=False).name
89
  writer = cv2.VideoWriter(out_path, cv2.VideoWriter_fourcc(*"mp4v"), fps, (w,h))
 
90
 
91
- records = []
92
- idx = 0
93
  while True:
94
  ret, frame = cap.read()
95
  if not ret: break
96
- idx += 1; t = idx / fps
97
 
98
  res = yolo(frame, conf=conf)[0]
99
- boxes = res.boxes.xyxy.cpu().numpy().astype(int)
100
- if boxes.shape[0] > 0:
101
- areas = (boxes[:,2] - boxes[:,0]) * (boxes[:,3] - boxes[:,1])
102
- i = int(np.argmax(areas))
103
- x1,y1,x2,y2 = boxes[i]
104
  crop = frame[y1:y2, x1:x2]
105
- if crop.size > 0:
106
- plate_img = cv2.resize(crop, (128,32))
107
- try:
108
- recs = ocr.ocr(plate_img, det=False, cls=True)
109
- except:
110
- recs = []
111
- txt, score = normalize_ocr(recs)
112
- plate = format_plate(txt)
113
- if plate != "Unknown":
114
- records.append({
115
- "time_s": round(t,2),
116
- "plate": plate,
117
- "conf": round(score,3)
118
- })
119
- cv2.rectangle(frame, (x1,y1),(x2,y2), (0,255,0), 2)
120
- cv2.putText(frame, plate, (x1, y1-8),
121
- cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0,255,0), 2)
122
 
123
  writer.write(frame)
124
 
125
- cap.release()
126
- writer.release()
127
  with open("output.json","w") as f:
128
  json.dump(records, f, indent=2)
129
  return out_path, "Done"
130
 
131
- # ─── 8) Gradio UI ────────────────────────────────────────────────
132
  with gr.Blocks() as demo:
133
  gr.Markdown("## 🚗 License Plate Detection + Recognition")
 
134
  with gr.Row():
135
  with gr.Column():
136
  img_in = gr.Image(type="numpy", label="Upload Image")
137
  vid_in = gr.File(label="Upload Video (.mp4)")
138
- conf = gr.Slider(0.0, 1.0, 0.25, 0.01, label="YOLO Confidence")
139
  btn_i = gr.Button("Run Image")
140
  btn_v = gr.Button("Run Video")
141
  with gr.Column():
@@ -143,8 +146,8 @@ with gr.Blocks() as demo:
143
  vid_out = gr.Video(label="Annotated Video")
144
  status = gr.Textbox(label="Status / JSON Path")
145
 
146
- btn_i.click(run_image, [img_in, conf], [img_out, status])
147
- btn_v.click(run_video, [vid_in, conf], [vid_out, status])
148
-
149
  if __name__ == "__main__":
150
  demo.launch()
 
1
  # app.py
2
+ import re, json, tempfile
 
 
3
  import cv2
4
  import numpy as np
5
  import gradio as gr
 
15
  # ─── 2) Load YOLOv8 detector
16
  yolo = YOLO("models/best.pt")
17
 
18
+ # ─── 3) Init PaddleOCR recognition-only, override ALL params in-code
19
  ocr = PaddleOCR(
20
+ det=False, # disable det on plate crops
21
+ rec=True, # recognition-only
22
  rec_model_dir="models/ocr_model",
23
+ rec_image_shape="3,32,128", # must match your training
24
+ cls=True, # angle classifier
25
  use_angle_cls=True,
26
  use_space_char=True
27
  )
28
+ # Force our exact char map (no dict file needed)
29
+ ocr.text_recognizer.character = CHAR_LIST
30
 
31
+ # ─── 4) Normalize & format OCR output
32
  def normalize_ocr(recs):
33
+ """
34
+ recs might be:
35
+ - [] → no read
36
+ - [["ABC123", 0.82]] → default det=False
37
+ - [["ABC123", 0.82], ...] → (unlikely here)
38
+ - [[box,…], ("ABC123",0.82)] → old det=True style
39
+ return text:str, score:float
40
+ """
41
  if not recs:
42
  return "", 0.0
43
  first = recs[0]
44
+ # case: ["TXT",score]
45
  if isinstance(first, (list,tuple)) and len(first)==2 and isinstance(first[0], str):
46
  return first[0], float(first[1])
47
+ # case: [<box>, (<txt>,score)] or [<box>, [txt,score]]
48
  if isinstance(first, (list,tuple)) and len(first)==2 and isinstance(first[1], (list,tuple)):
49
  return first[1][0], float(first[1][1])
50
  return "", 0.0
51
 
 
52
  def format_plate(s: str) -> str:
53
+ """‘DD AAA DDDD’ veya Unknown"""
54
  s = re.sub(r'[^A-Z0-9]', '', s.upper())
55
+ m = re.match(r'^(\d{2})([A-Z]{1,3})(\d{2,4})$', s)
 
56
  return f"{m.group(1)} {m.group(2)} {m.group(3)}" if m else "Unknown"
57
 
58
+ # ─── 5) Single-image inference
59
  def run_image(img, conf=0.25):
60
+ # YOLO wants BGR
61
  bgr = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)
62
  res = yolo(bgr, conf=conf)[0]
63
  out = bgr.copy()
64
 
65
+ for box in res.boxes.xyxy.cpu().numpy().astype(int):
66
+ x1,y1,x2,y2 = box
 
 
 
 
67
  crop = out[y1:y2, x1:x2]
68
+ if crop.size == 0:
69
+ continue
70
+
71
+ # resize to OCR input
72
+ plate_img = cv2.resize(crop, (128,32))
73
+ # safe OCR
74
+ try:
75
+ recs = ocr.ocr(plate_img, det=False, cls=True)
76
+ except Exception:
77
+ recs = []
78
+ txt, score = normalize_ocr(recs)
79
+ plate = format_plate(txt)
80
+ label = f"{plate} ({score:.2f})"
81
+
82
+ # draw
83
+ cv2.rectangle(out, (x1,y1),(x2,y2), (0,255,0), 2)
84
+ cv2.putText(out, label, (x1, y1-8),
85
+ cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0,255,0), 2)
86
+
87
+ return cv2.cvtColor(out, cv2.COLOR_BGR2RGB), f"{len(res.boxes)} plate(s) detected"
88
+
89
+ # ─── 6) Video inference
90
  def run_video(video_file, conf=0.25):
91
  cap = cv2.VideoCapture(video_file)
92
  fps = cap.get(cv2.CAP_PROP_FPS) or 30
93
  w = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
94
  h = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
95
+
96
  out_path = tempfile.NamedTemporaryFile(suffix=".mp4", delete=False).name
97
  writer = cv2.VideoWriter(out_path, cv2.VideoWriter_fourcc(*"mp4v"), fps, (w,h))
98
+ records, idx = [], 0
99
 
 
 
100
  while True:
101
  ret, frame = cap.read()
102
  if not ret: break
103
+ idx += 1; t = idx/fps
104
 
105
  res = yolo(frame, conf=conf)[0]
106
+ for box in res.boxes.xyxy.cpu().numpy().astype(int):
107
+ x1,y1,x2,y2 = box
 
 
 
108
  crop = frame[y1:y2, x1:x2]
109
+ if crop.size == 0: continue
110
+
111
+ plate_img = cv2.resize(crop, (128,32))
112
+ try:
113
+ recs = ocr.ocr(plate_img, det=False, cls=True)
114
+ except:
115
+ recs = []
116
+ txt, score = normalize_ocr(recs)
117
+ plate = format_plate(txt)
118
+
119
+ if plate != "Unknown":
120
+ records.append({"time_s":round(t,2),"plate":plate,"conf":round(score,3)})
121
+
122
+ cv2.rectangle(frame, (x1,y1),(x2,y2), (0,255,0), 2)
123
+ cv2.putText(frame, plate, (x1, y1-8),
124
+ cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0,255,0), 2)
 
125
 
126
  writer.write(frame)
127
 
128
+ cap.release(); writer.release()
 
129
  with open("output.json","w") as f:
130
  json.dump(records, f, indent=2)
131
  return out_path, "Done"
132
 
133
+ # ─── 7) Gradio UI
134
  with gr.Blocks() as demo:
135
  gr.Markdown("## 🚗 License Plate Detection + Recognition")
136
+
137
  with gr.Row():
138
  with gr.Column():
139
  img_in = gr.Image(type="numpy", label="Upload Image")
140
  vid_in = gr.File(label="Upload Video (.mp4)")
141
+ conf = gr.Slider(0.0,1.0,0.25,0.01, label="YOLO Confidence")
142
  btn_i = gr.Button("Run Image")
143
  btn_v = gr.Button("Run Video")
144
  with gr.Column():
 
146
  vid_out = gr.Video(label="Annotated Video")
147
  status = gr.Textbox(label="Status / JSON Path")
148
 
149
+ btn_i.click(run_image, [img_in,conf], [img_out,status])
150
+ btn_v.click(run_video, [vid_in,conf], [vid_out,status])
151
+
152
  if __name__ == "__main__":
153
  demo.launch()