# pylint: disable=invalid-name, redefined-outer-name, missing-docstring, non-parent-init-called, trailing-whitespace, line-too-long from os.path import splitext import cv2 import numpy as np from keras.models import load_model import os os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' class Label: def __init__(self, cl=-1, tl=np.array([0., 0.]), br=np.array([0., 0.]), prob=None): self.__tl = tl self.__br = br self.__cl = cl self.__prob = prob def __str__(self): return 'Class: %d, top left(x: %f, y: %f), bottom right(x: %f, y: %f)' % ( self.__cl, self.__tl[0], self.__tl[1], self.__br[0], self.__br[1]) def copy(self): return Label(self.__cl, self.__tl, self.__br) def wh(self): return self.__br - self.__tl def cc(self): return self.__tl + self.wh() / 2 def tl(self): return self.__tl def br(self): return self.__br def tr(self): return np.array([self.__br[0], self.__tl[1]]) def bl(self): return np.array([self.__tl[0], self.__br[1]]) def cl(self): return self.__cl def area(self): return np.prod(self.wh()) def prob(self): return self.__prob def set_class(self, cl): self.__cl = cl def set_tl(self, tl): self.__tl = tl def set_br(self, br): self.__br = br def set_wh(self, wh): cc = self.cc() self.__tl = cc - .5 * wh self.__br = cc + .5 * wh def set_prob(self, prob): self.__prob = prob class DLabel(Label): def __init__(self, cl, pts, prob): self.pts = pts tl = np.amin(pts, axis=1) br = np.amax(pts, axis=1) Label.__init__(self, cl, tl, br, prob) # Hàm normalize ảnh def im2single(Image): return Image.astype('float32') / 255 def getWH(shape): return np.array(shape[1::-1]).astype(float) def IOU(tl1, br1, tl2, br2): wh1, wh2 = br1-tl1, br2-tl2 assert((wh1 >= 0).all() and (wh2 >= 0).all()) intersection_wh = np.maximum(np.minimum(br1, br2) - np.maximum(tl1, tl2), 0) intersection_area = np.prod(intersection_wh) area1, area2 = (np.prod(wh1), np.prod(wh2)) union_area = area1 + area2 - intersection_area return intersection_area/union_area def IOU_labels(l1, l2): return IOU(l1.tl(), l1.br(), l2.tl(), l2.br()) def nms(Labels, iou_threshold=0.5): SelectedLabels = [] Labels.sort(key=lambda l: l.prob(), reverse=True) for label in Labels: non_overlap = True for sel_label in SelectedLabels: if IOU_labels(label, sel_label) > iou_threshold: non_overlap = False break if non_overlap: SelectedLabels.append(label) return SelectedLabels def load_model_wpod(path): model = load_model(path) return model def find_T_matrix(pts, t_pts): A = np.zeros((8, 9)) for i in range(0, 4): xi = pts[:, i] xil = t_pts[:, i] xi = xi.T A[i*2, 3:6] = -xil[2]*xi A[i*2, 6:] = xil[1]*xi A[i*2+1, :3] = xil[2]*xi A[i*2+1, 6:] = -xil[0]*xi [U, S, V] = np.linalg.svd(A) H = V[-1, :].reshape((3, 3)) return H def getRectPts(a, b): return np.array([[0,0], [a, 0], [a, b],[0,b]],np.float32) def normal(pts, side, mn, MN): pts_MN_center_mn = pts * side pts_MN = pts_MN_center_mn + mn.reshape((2, 1)) pts_prop = pts_MN / MN.reshape((2, 1)) return pts_prop def get_bound(x,y): bound =[] for i in range(0,len(x)): point =[x[i],y[i]] bound.append(point) return bound def calculate_ratio(bound): def distance(point1,point2): x1=point1[0] y1=point1[1] x2=point2[0] y2=point2[1] distance = np.sqrt((x2 - x1)**2 + (y2 - y1)**2) return distance box = bound dis1= distance(box[0],box[1]) dis2 = distance(box[1],box[2]) dis3 = distance(box[2],box[3]) dis4 = distance(box[3],box[0]) width = (dis1+dis3)/2 height= (dis2+dis4)/2 ratio = height/width if ratio>0.55: return 2 return 1 # Hàm tái tạo từ predict value thành biến số, cắt từ ảnh chính ra biển số, nhãn... def reconstruct(I, Iresized, Yr, lp_threshold): bounds=[] # 4 max-pooling layers, stride = 2 net_stride = 2**4 side = ((208 + 40)/2)/net_stride # one line and two lines license plate size one_line = (100, 23) two_lines = (64, 46) Probs = Yr[..., 0] Affines = Yr[..., 2:] xx, yy = np.where(Probs > lp_threshold) # CNN input image size WH = getWH(Iresized.shape) # output feature map size MN = WH/net_stride vxx = vyy = 0.5 #alpha base = lambda vx, vy: np.matrix([[-vx, -vy, 1], [vx, -vy, 1], [vx, vy, 1], [-vx, vy, 1]]).T labels = [] labels_frontal = [] for i in range(len(xx)): x, y = xx[i], yy[i] affine = Affines[x, y] prob = Probs[x, y] mn = np.array([float(y) + 0.5, float(x) + 0.5]) # affine transformation matrix A = np.reshape(affine, (2, 3)) A[0, 0] = max(A[0, 0], 0) A[1, 1] = max(A[1, 1], 0) # identity transformation B = np.zeros((2, 3)) B[0, 0] = max(A[0, 0], 0) B[1, 1] = max(A[1, 1], 0) pts = np.array(A*base(vxx, vyy)) pts_frontal = np.array(B*base(vxx, vyy)) pts_prop = normal(pts, side, mn, MN) frontal = normal(pts_frontal, side, mn, MN) labels.append(DLabel(0, pts_prop, prob)) labels_frontal.append(DLabel(0, frontal, prob)) final_labels = nms(labels, 0.1) final_labels_frontal = nms(labels_frontal, 0.1) if (len(final_labels_frontal)>0): # LP size and type #out_size, lp_type = (two_lines, 2) if ((final_labels_frontal[0].wh()[1] / final_labels_frontal[0].wh()[1]) >0.49) else (one_line, 1) lp_type=0 TLp = [] if len(final_labels): final_labels.sort(key=lambda x: x.prob(), reverse=True) for _, label in enumerate(final_labels): ptsh = np.concatenate((label.pts * getWH(I.shape).reshape((2, 1)), np.ones((1, 4)))) bound = get_bound(ptsh[0],ptsh[1]) pts=np.array(bound,dtype=np.float32) bounds.append(bound) lp_type = calculate_ratio(bound) if lp_type==2: out_size=two_lines else: out_size=one_line t_ptsh = getRectPts(out_size[0], out_size[1]) H=cv2.getPerspectiveTransform(pts,t_ptsh) Ilp = cv2.warpPerspective(I,H, (int(out_size[0]),int(out_size[1]))) TLp.append(Ilp) return final_labels, TLp, lp_type,bounds else: return None,[], None,None def detect_lp(model, I, lp_threshold): Dmax = 608 Dmin = 288 # Lấy tỷ lệ giữa W và H của ảnh và tìm ra chiều nhỏ nhất ratio = float(max(I.shape[:2])) / min(I.shape[:2]) side = int(ratio * Dmin) max_dim = min(side, Dmax) I=im2single(I) # Tính factor resize ảnh min_dim_img = min(I.shape[:2]) factor = float(max_dim) / min_dim_img # Tính W và H mới sau khi resize w, h = (np.array(I.shape[1::-1], dtype=float) * factor).astype(int).tolist() # Tiến hành resize ảnh Iresized = cv2.resize(I, (w, h)) T = Iresized.copy() # Chuyển thành Tensor T = T.reshape((1, T.shape[0], T.shape[1], T.shape[2])) # Tiến hành detect biển số bằng Wpod-net pretrain Yr = model.predict(T,verbose=0) # Remove các chiều =1 của Yr Yr = np.squeeze(Yr) # Tái tạo và trả về các biến gồm: Nhãn, Ảnh biến số, Loại biển số (1: dài: 2 vuông) L, TLp, lp_type,bounds = reconstruct(I, Iresized, Yr, lp_threshold) return L, TLp, lp_type,bounds