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

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +70 -88
app.py CHANGED
@@ -1,5 +1,7 @@
1
  # app.py
2
- import re, json, tempfile
 
 
3
  import cv2
4
  import numpy as np
5
  import gradio as gr
@@ -15,145 +17,125 @@ CHAR_LIST = list("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ ")
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})([A-Z0-9]{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
- # YOLO’dan gelen tüm kutuları al
66
  boxes = res.boxes.xyxy.cpu().numpy().astype(int)
67
- if len(boxes):
68
- # genişlik*yükseklik’e göre en büyüğü seç
69
- w = boxes[:,2] - boxes[:,0]
70
- h = boxes[:,3] - boxes[:,1]
71
- idx = int(np.argmax(w * h))
72
- x1,y1,x2,y2 = boxes[idx]
73
  crop = out[y1:y2, x1:x2]
74
-
75
-
76
- if crop.size == 0:
77
- continue
78
-
79
- # resize to OCR input
80
- plate_img = cv2.resize(crop, (128,32))
81
- # safe OCR
82
- try:
83
- recs = ocr.ocr(plate_img, det=False, cls=True)
84
- except Exception:
85
- recs = []
86
- txt, score = normalize_ocr(recs)
87
- plate = format_plate(txt)
88
- label = f"{plate} ({score:.2f})"
89
-
90
- # draw
91
- cv2.rectangle(out, (x1,y1),(x2,y2), (0,255,0), 2)
92
- cv2.putText(out, label, (x1, y1-8),
93
- cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0,255,0), 2)
94
-
95
- return cv2.cvtColor(out, cv2.COLOR_BGR2RGB), f"{len(res.boxes)} plate(s) detected"
96
-
97
- # ─── 6) Video inference
98
  def run_video(video_file, conf=0.25):
99
  cap = cv2.VideoCapture(video_file)
100
  fps = cap.get(cv2.CAP_PROP_FPS) or 30
101
  w = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
102
  h = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
103
-
104
  out_path = tempfile.NamedTemporaryFile(suffix=".mp4", delete=False).name
105
  writer = cv2.VideoWriter(out_path, cv2.VideoWriter_fourcc(*"mp4v"), fps, (w,h))
106
- records, idx = [], 0
107
 
 
 
108
  while True:
109
  ret, frame = cap.read()
110
  if not ret: break
111
- idx += 1; t = idx/fps
112
 
113
  res = yolo(frame, conf=conf)[0]
114
- # YOLO’dan gelen tüm kutuları al
115
  boxes = res.boxes.xyxy.cpu().numpy().astype(int)
116
- if len(boxes):
117
- # genişlik*yükseklik’e göre en büyüğü seç
118
- w = boxes[:,2] - boxes[:,0]
119
- h = boxes[:,3] - boxes[:,1]
120
- idx = int(np.argmax(w * h))
121
- x1,y1,x2,y2 = boxes[idx]
122
- crop = out[y1:y2, x1:x2]
123
-
124
- if crop.size == 0: continue
125
-
126
- plate_img = cv2.resize(crop, (128,32))
127
- try:
128
- recs = ocr.ocr(plate_img, det=False, cls=True)
129
- except:
130
- recs = []
131
- txt, score = normalize_ocr(recs)
132
- plate = format_plate(txt)
133
-
134
- if plate != "Unknown":
135
- records.append({"time_s":round(t,2),"plate":plate,"conf":round(score,3)})
136
-
137
- cv2.rectangle(frame, (x1,y1),(x2,y2), (0,255,0), 2)
138
- cv2.putText(frame, plate, (x1, y1-8),
139
- cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0,255,0), 2)
140
 
141
  writer.write(frame)
142
 
143
- cap.release(); writer.release()
 
144
  with open("output.json","w") as f:
145
  json.dump(records, f, indent=2)
146
  return out_path, "Done"
147
 
148
- # ─── 7) Gradio UI
149
  with gr.Blocks() as demo:
150
  gr.Markdown("## 🚗 License Plate Detection + Recognition")
151
-
152
  with gr.Row():
153
  with gr.Column():
154
  img_in = gr.Image(type="numpy", label="Upload Image")
155
  vid_in = gr.File(label="Upload Video (.mp4)")
156
- conf = gr.Slider(0.0,1.0,0.25,0.01, label="YOLO Confidence")
157
  btn_i = gr.Button("Run Image")
158
  btn_v = gr.Button("Run Video")
159
  with gr.Column():
@@ -161,8 +143,8 @@ with gr.Blocks() as demo:
161
  vid_out = gr.Video(label="Annotated Video")
162
  status = gr.Textbox(label="Status / JSON Path")
163
 
164
- btn_i.click(run_image, [img_in,conf], [img_out,status])
165
- btn_v.click(run_video, [vid_in,conf], [vid_out,status])
166
-
167
  if __name__ == "__main__":
168
  demo.launch()
 
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
  # ─── 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
  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()