3
3
import colorsys
4
4
import numpy as np
5
5
import tensorflow as tf
6
+ import pytesseract
6
7
from core .config import cfg
8
+ import re
9
+
10
+ # function to recognize license plate numbers using Tesseract OCR
11
+ def recognize_plate (img , coords ):
12
+ # separate coordinates from box
13
+ xmin , ymin , xmax , ymax = coords
14
+ # get the subimage that makes up the bounded region and take an additional 5 pixels on each side
15
+ box = img [int (ymin )- 5 :int (ymax )+ 5 , int (xmin )- 5 :int (xmax )+ 5 ]
16
+ # grayscale region within bounding box
17
+ gray = cv2 .cvtColor (box , cv2 .COLOR_RGB2GRAY )
18
+ # resize image to three times as large as original for better readability
19
+ gray = cv2 .resize (gray , None , fx = 3 , fy = 3 , interpolation = cv2 .INTER_CUBIC )
20
+ # perform gaussian blur to smoothen image
21
+ blur = cv2 .GaussianBlur (gray , (5 ,5 ), 0 )
22
+ #cv2.imshow("Gray", gray)
23
+ #cv2.waitKey(0)
24
+ # threshold the image using Otsus method to preprocess for tesseract
25
+ ret , thresh = cv2 .threshold (gray , 0 , 255 , cv2 .THRESH_OTSU | cv2 .THRESH_BINARY_INV )
26
+ #cv2.imshow("Otsu Threshold", thresh)
27
+ #cv2.waitKey(0)
28
+ # create rectangular kernel for dilation
29
+ rect_kern = cv2 .getStructuringElement (cv2 .MORPH_RECT , (5 ,5 ))
30
+ # apply dilation to make regions more clear
31
+ dilation = cv2 .dilate (thresh , rect_kern , iterations = 1 )
32
+ #cv2.imshow("Dilation", dilation)
33
+ #cv2.waitKey(0)
34
+ # find contours of regions of interest within license plate
35
+ contours , hierarchy = cv2 .findContours (dilation , cv2 .RETR_TREE , cv2 .CHAIN_APPROX_SIMPLE )
36
+ # sort contours left-to-right
37
+ sorted_contours = sorted (contours , key = lambda ctr : cv2 .boundingRect (ctr )[0 ])
38
+ # create copy of gray image
39
+ im2 = gray .copy ()
40
+ # create blank string to hold license plate number
41
+ plate_num = ""
42
+ # loop through contours and find individual letters and numbers in license plate
43
+ for cnt in sorted_contours :
44
+ x ,y ,w ,h = cv2 .boundingRect (cnt )
45
+ height , width = im2 .shape
46
+ # if height of box is not tall enough relative to total height then skip
47
+ if height / float (h ) > 6 : continue
48
+
49
+ ratio = h / float (w )
50
+ # if height to width ratio is less than 1.5 skip
51
+ if ratio < 1.5 : continue
52
+
53
+ # if width is not wide enough relative to total width then skip
54
+ if width / float (w ) > 15 : continue
55
+
56
+ area = h * w
57
+ # if area is less than 100 pixels skip
58
+ if area < 100 : continue
59
+
60
+ # draw the rectangle
61
+ rect = cv2 .rectangle (im2 , (x ,y ), (x + w , y + h ), (0 ,255 ,0 ),2 )
62
+ # grab character region of image
63
+ roi = thresh [y - 5 :y + h + 5 , x - 5 :x + w + 5 ]
64
+ # perfrom bitwise not to flip image to black text on white background
65
+ roi = cv2 .bitwise_not (roi )
66
+ # perform another blur on character region
67
+ roi = cv2 .medianBlur (roi , 5 )
68
+ try :
69
+ text = pytesseract .image_to_string (roi , config = '-c tessedit_char_whitelist=0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ --psm 8 --oem 3' )
70
+ # clean tesseract text by removing any unwanted blank spaces
71
+ clean_text = re .sub ('[\W_]+' , '' , text )
72
+ plate_num += clean_text
73
+ except :
74
+ text = None
75
+ if plate_num != None :
76
+ print ("License Plate #: " , plate_num )
77
+ #cv2.imshow("Character's Segmented", im2)
78
+ #cv2.waitKey(0)
79
+ return plate_num
7
80
8
81
def load_freeze_layer (model = 'yolov4' , tiny = False ):
9
82
if tiny :
@@ -103,7 +176,6 @@ def get_anchors(anchors_path, tiny=False):
103
176
return anchors .reshape (3 , 3 , 2 )
104
177
105
178
def image_preprocess (image , target_size , gt_boxes = None ):
106
-
107
179
ih , iw = target_size
108
180
h , w , _ = image .shape
109
181
@@ -134,7 +206,7 @@ def format_boxes(bboxes, image_height, image_width):
134
206
box [0 ], box [1 ], box [2 ], box [3 ] = xmin , ymin , xmax , ymax
135
207
return bboxes
136
208
137
- def draw_bbox (image , bboxes , info = False , counted_classes = None , show_label = True , allowed_classes = list (read_class_names (cfg .YOLO .CLASSES ).values ())):
209
+ def draw_bbox (image , bboxes , info = False , counted_classes = None , show_label = True , allowed_classes = list (read_class_names (cfg .YOLO .CLASSES ).values ()), read_plate = False ):
138
210
classes = read_class_names (cfg .YOLO .CLASSES )
139
211
num_classes = len (classes )
140
212
image_h , image_w , _ = image .shape
@@ -157,6 +229,13 @@ def draw_bbox(image, bboxes, info = False, counted_classes = None, show_label=Tr
157
229
if class_name not in allowed_classes :
158
230
continue
159
231
else :
232
+ if read_plate :
233
+ height_ratio = int (image_h / 25 )
234
+ plate_number = recognize_plate (image , coor )
235
+ if plate_number != None :
236
+ cv2 .putText (image , plate_number , (int (coor [0 ]), int (coor [1 ]- height_ratio )),
237
+ cv2 .FONT_HERSHEY_SIMPLEX , 1.25 , (255 ,255 ,0 ), 2 )
238
+
160
239
bbox_color = colors [class_ind ]
161
240
bbox_thick = int (0.6 * (image_h + image_w ) / 600 )
162
241
c1 , c2 = (coor [0 ], coor [1 ]), (coor [2 ], coor [3 ])
@@ -172,7 +251,7 @@ def draw_bbox(image, bboxes, info = False, counted_classes = None, show_label=Tr
172
251
cv2 .rectangle (image , c1 , (np .float32 (c3 [0 ]), np .float32 (c3 [1 ])), bbox_color , - 1 ) #filled
173
252
174
253
cv2 .putText (image , bbox_mess , (c1 [0 ], np .float32 (c1 [1 ] - 2 )), cv2 .FONT_HERSHEY_SIMPLEX ,
175
- fontScale , (0 , 0 , 0 ), bbox_thick // 2 , lineType = cv2 .LINE_AA )
254
+ fontScale , (0 , 0 , 0 ), bbox_thick // 2 , lineType = cv2 .LINE_AA )
176
255
177
256
if counted_classes != None :
178
257
height_ratio = int (image_h / 25 )
0 commit comments