diff --git a/hyperlpr_pip_pkg/demo.py b/hyperlpr_pip_pkg/demo.py index 862d248..3a98249 100644 --- a/hyperlpr_pip_pkg/demo.py +++ b/hyperlpr_pip_pkg/demo.py @@ -1,4 +1,5 @@ -from hyperlpr import * import cv2 +from hyperlpr import * + image = cv2.imread("./test_images/test_db2.jpg") -print(HyperLPR_plate_recognition(image,16,charSelectionDeskew=False,DB=True)) +print(HyperLPR_plate_recognition(image, 16, charSelectionDeskew=False, DB=True)) diff --git a/hyperlpr_pip_pkg/hyperlpr/__init__.py b/hyperlpr_pip_pkg/hyperlpr/__init__.py index e999c48..c11a550 100644 --- a/hyperlpr_pip_pkg/hyperlpr/__init__.py +++ b/hyperlpr_pip_pkg/hyperlpr/__init__.py @@ -1,8 +1,8 @@ -name = "hyperlpr_python_pkg" -import sys -from .hyperlpr import LPR import os +from .hyperlpr import LPR + +PR = LPR(os.path.join(os.path.split(os.path.realpath(__file__))[0], "models")) + -PR = LPR(os.path.join(os.path.split(os.path.realpath(__file__))[0],"models")) -def HyperLPR_plate_recognition(Input_BGR,minSize=30,charSelectionDeskew=True , region = "CH",DB=True): - return PR.plate_recognition(Input_BGR,minSize,charSelectionDeskew,DB) +def HyperLPR_plate_recognition(Input_BGR, minSize=30, charSelectionDeskew=True, region="CH", DB=True): + return PR.plate_recognition(Input_BGR, minSize, charSelectionDeskew, DB) diff --git a/hyperlpr_pip_pkg/hyperlpr/hyperlpr.py b/hyperlpr_pip_pkg/hyperlpr/hyperlpr.py index 5ff0970..ca1abe3 100644 --- a/hyperlpr_pip_pkg/hyperlpr/hyperlpr.py +++ b/hyperlpr_pip_pkg/hyperlpr/hyperlpr.py @@ -1,42 +1,42 @@ -#coding=utf-8 +import os import cv2 import numpy as np -import os from .table_chs import chars +class LPR: - -class LPR(): - def __init__(self,folder): + def __init__(self, folder): """ Init the recognition instance. - - :param model_detection: opencv cascade model which detecting license plate. - :param model_finemapping: finemapping model which deskew the license plate - :param model_rec: CNN based sequence recognition model trained with CTC loss. + :param model_detection: opencv cascade model which detecting license plate. + :param model_finemapping: finemapping model which deskew the license plate + :param model_rec: CNN based sequence recognition model trained with CTC loss. """ - - charLocPath= os.path.join(folder,"cascade/char/char_single.xml") - detectorPath = os.path.join(folder,"cascade/detector/detector_ch.xml") - detectorPathDB = os.path.join(folder,"cascade/detector/cascade_double.xml") - modelRecognitionPath = [os.path.join(folder,"dnn/SegmenationFree-Inception.prototxt"),os.path.join(folder,"dnn/SegmenationFree-Inception.caffemodel")] - modelFineMappingPath= [os.path.join(folder,"dnn/HorizonalFinemapping.prototxt"),os.path.join(folder,"dnn/HorizonalFinemapping.caffemodel")] - mini_ssd_path= [os.path.join(folder,"dnn/mininet_ssd_v1.prototxt"),os.path.join(folder,"dnn/mininet_ssd_v1.caffemodel")] - refine_net_path = [os.path.join(folder,"dnn/refinenet.prototxt"),os.path.join(folder,"dnn/refinenet.caffemodel")] + charLocPath = os.path.join(folder, "cascade/char/char_single.xml") + detectorPath = os.path.join(folder, "cascade/detector/detector_ch.xml") + detectorPathDB = os.path.join(folder, "cascade/detector/cascade_double.xml") + modelRecognitionPath = [os.path.join(folder, "dnn/SegmenationFree-Inception.prototxt"), + os.path.join(folder, "dnn/SegmenationFree-Inception.caffemodel")] + modelFineMappingPath = [os.path.join(folder, "dnn/HorizonalFinemapping.prototxt"), + os.path.join(folder, "dnn/HorizonalFinemapping.caffemodel")] + mini_ssd_path = [os.path.join(folder, "dnn/mininet_ssd_v1.prototxt"), + os.path.join(folder, "dnn/mininet_ssd_v1.caffemodel")] + refine_net_path = [os.path.join(folder, "dnn/refinenet.prototxt"), + os.path.join(folder, "dnn/refinenet.caffemodel")] self.detector = cv2.CascadeClassifier(detectorPath) self.detectorDB = cv2.CascadeClassifier(detectorPathDB) self.charLoc = cv2.CascadeClassifier(charLocPath) self.modelRecognition = cv2.dnn.readNetFromCaffe(*modelRecognitionPath) self.ssd_detection = cv2.dnn.readNetFromCaffe(*mini_ssd_path) - self.refine_net = cv2.dnn.readNetFromCaffe(*refine_net_path) + self.refine_net = cv2.dnn.readNetFromCaffe(*refine_net_path) - def detect_ssd(self,im): + def detect_ssd(self, im): """ Detect the approximate location of plate via single shot detector based on modified mobilenet. - :param im: input image (BGR) . - :return: [[cropped,x1,y2,x2,y2] ,... ] + :param im: input image (BGR) . + :return: [[cropped,x1,y2,x2,y2] ,... ] """ _im = im.copy() pixel_means = [0.406, 0.456, 0.485] @@ -48,7 +48,7 @@ class LPR(): for i in range(3): im_tensor[0, i, :, :] = (im[:, :, 2 - i] / pixel_scale - pixel_means[2 - i]) / pixel_stds[2 - i] self.ssd_detection.setInput(im_tensor) - print(im_tensor.shape) + # print(im_tensor.shape) cropped_images = [] cvOut = self.ssd_detection.forward() for detection in cvOut[0, 0, :, :]: @@ -58,22 +58,22 @@ class LPR(): y1 = int(detection[4] * rows) x2 = int(detection[5] * cols) y2 = int(detection[6] * rows) - x1 = max(x1,0) - y1 = max(y1,0) - x2 = min(x2,im.shape[1]-1) - y2 = min(y2,im.shape[0]-1) - cropped = _im[y1:y2,x1:x2] - cropped_images.append([cropped ,[x1,y1,x2,y2]]) + x1 = max(x1, 0) + y1 = max(y1, 0) + x2 = min(x2, im.shape[1]-1) + y2 = min(y2, im.shape[0]-1) + cropped = _im[y1:y2, x1:x2] + cropped_images.append([cropped, [x1, y1, x2, y2]]) return cropped_images - def detect_traditional(self,image_gray,resize_h = 720,en_scale =1.1,minSize = 30,DB=True): + def detect_traditional(self, image_gray, resize_h=720, en_scale=1.1, minSize=30, DB=True): """ Detect the approximate location of plate via opencv build-in cascade detection. - :param image_gray: input single channel image (gray) . - :param resize_h: adjust input image size to a fixed size. - :param en_scale: the ratio of image between every scale of images in cascade detection. - :param minSize: minSize of plate increase this parameter can increase the speed of detection. - :return: the results. + :param image_gray: input single channel image (gray) . + :param resize_h: adjust input image size to a fixed size. + :param en_scale: the ratio of image between every scale of images in cascade detection. + :param minSize: minSize of plate increase this parameter can increase the speed of detection. + :return: the results. """ if DB: watches = self.detectorDB.detectMultiScale(image_gray, en_scale, 3, minSize=(minSize*4, minSize)) @@ -89,23 +89,22 @@ class LPR(): y1 = int(y) x2 = int(x+w) y2 = int(y+h) - x1 = max(x1,0) - y1 = max(y1,0) - x2 = min(x2,image_gray.shape[1]-1) - y2 = min(y2,image_gray.shape[0]-1) - cropped = image_gray[y1:y2,x1:x2] - cropped_images.append([cropped ,[x1,y1,x2,y2]]) + x1 = max(x1, 0) + y1 = max(y1, 0) + x2 = min(x2, image_gray.shape[1]-1) + y2 = min(y2, image_gray.shape[0]-1) + cropped = image_gray[y1:y2, x1:x2] + cropped_images.append([cropped, [x1, y1, x2, y2]]) return cropped_images - - def loose_crop(self,image, box, aspect_ratio, padding_ratio=1.7): + def loose_crop(self, image, box, aspect_ratio, padding_ratio=1.7): """ Crop the image with an extend rectangle. - :param image: input image (BGR). - :param box: origin bounding box. - :param aspect_ratio: the aspect ratio that need to keep. - :param padding_ratio: padding ratio of origin rectangle. - :return: the cropped image + :param image: input image (BGR). + :param box: origin bounding box. + :param aspect_ratio: the aspect ratio that need to keep. + :param padding_ratio: padding ratio of origin rectangle. + :return: the cropped image """ x1, y1, x2, y2 = box cx, cy = ((x2 + x1) // 2, (y2 + y1) // 2) @@ -121,11 +120,10 @@ class LPR(): cropped = image[y1:y2, x1:x2] return cropped - - def decode_ctc(self,y_pred): + def decode_ctc(self, y_pred): """ - Decode the results from the last layer of recognition model. - :param y_pred: the feature map output last feature map. + Decode the results from the last layer of recognition model. + :param y_pred: the feature map output last feature map. :return: decode results. """ results = "" @@ -133,14 +131,14 @@ class LPR(): y_pred = y_pred.T table_pred = y_pred res = table_pred.argmax(axis=1) - for i,one in enumerate(res): - if one 0.7 and bdbox[3] * bdbox[2] > 100 and bdbox[3] * bdbox[2] < 1200) or ( - bdbox[3] / float(bdbox[2]) > 3 and bdbox[3] * bdbox[2] < 100)): + threshold1 = bdbox[3] / float(bdbox[2]) + threshold2 = bdbox[3] * bdbox[2] + if (threshold1 > 0.7 and 100 < threshold2 < 1200) or (threshold1 > 3 and threshold2 < 100): line_upper.append([bdbox[0], bdbox[1]]) line_lower.append([bdbox[0] + bdbox[2], bdbox[1] + bdbox[3]]) line_experiment.append([bdbox[0], bdbox[1]]) @@ -192,17 +190,14 @@ class LPR(): image = cv2.warpPerspective(rgb, mat, (136, 36), flags=cv2.INTER_CUBIC) return image - - def fine_mapping_by_selecting(self,image_rgb,line_upper,line_lower ): + def fine_mapping_by_selecting(self, image_rgb, line_upper, line_lower): """ - fit plate upper and lower with detecting character boundingbox + fit plate upper and lower with detecting character bounding box :param image_rgb: input image :param line_upper: padding of upper :param line_lower: padding of lower :return: fined image. """ - - rgb = cv2.copyMakeBorder(image_rgb, 30, 30, 0, 0, cv2.BORDER_REPLICATE) leftyA, rightyA = self.fit_ransac(np.array(line_lower), 3) leftyB, rightyB = self.fit_ransac(np.array(line_upper), -3) @@ -213,10 +208,10 @@ class LPR(): image = cv2.warpPerspective(rgb, mat, (136, 36), flags=cv2.INTER_CUBIC) return image - def to_refine(self,image, pts, scale=3.0): + def to_refine(self, image, pts, scale=3.0): """ refine the image by input points. - :param image_rgb: input image + :param image: input image :param pts: points """ x1, y1, x2, y2, x3, y3, x4, y4 = pts.ravel() @@ -233,7 +228,10 @@ class LPR(): ty4 = cy + ch // 2 target_pts = np.array([[tx1, ty1], [tx2, ty2], [tx3, ty3], [tx4, ty4]]).astype(np.float32) * scale org_pts = np.array([[x1, y1], [x2, y2], [x3, y3], [x4, y4]]).astype(np.float32) - mat_ = cv2.estimateRigidTransform(org_pts, target_pts, True) + if cv2.__version__[0] == "4": + mat_, _ = cv2.estimateAffine2D(org_pts, target_pts) + else: + mat_ = cv2.estimateRigidTransform(org_pts, target_pts, True) dsize = (int(120 * scale), int(48 * scale)) warped = cv2.warpAffine(image, mat_, dsize) return warped @@ -241,7 +239,7 @@ class LPR(): def affine_crop(self, image, pts): """ crop a image by affine transform. - :param image_rgb: input image + :param image: input image :param pts: points """ x1, y1, x2, y2, x3, y3, x4, y4 = pts.ravel() @@ -252,13 +250,12 @@ class LPR(): warped = cv2.warpPerspective(image, mat, dsize) return warped - def finetune(self,image_, stage=2): + def finetune(self, image_, stage=2): """ cascade fine tune a image by regress four corner of plate. - :param image_rgb: input image + :param image_: input image :param stages: cascade stage """ - tof = image_.copy() image = cv2.resize(tof, (120, 48)) blob = cv2.dnn.blobFromImage(image, size=(120, 48), swapRB=False, mean=(127.5, 127.5, 127.5), scalefactor=0.0078125, crop=False) @@ -273,22 +270,20 @@ class LPR(): cropped = self.affine_crop(g, pts) return cropped - - def segmentation_free_recognition(self,src): + def segmentation_free_recognition(self, src): """ return: ctc decode results """ - temp = cv2.resize(src,( 160,40)) + temp = cv2.resize(src, (160, 40)) temp = temp.transpose(1, 0, 2) - blob = cv2.dnn.blobFromImage(temp, 1/255.0, (40, 160), (0,0,0), False, False) + blob = cv2.dnn.blobFromImage(temp, 1/255.0, (40, 160), (0, 0, 0), False, False) self.modelRecognition.setInput(blob) y_pred = self.modelRecognition.forward()[0] - y_pred = y_pred[:,2:,:] + y_pred = y_pred[:, 2:, :] y_pred = np.squeeze(y_pred) return self.decode_ctc(y_pred) - - def plate_recognition(self,image,minSize=30,charSelectionDeskew=True,DB = True, mode='ssd'): + def plate_recognition(self, image, minSize=30, charSelectionDeskew=True, DB=True, mode='ssd'): """ the simple pipline consists of detection . deskew , fine mapping alignment, recognition. :param image: the input BGR image from imread used by opencv @@ -307,31 +302,27 @@ class LPR(): print(pr.plateRecognition(image)) """ if DB: - image_gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY) + image_gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) images = self.detect_traditional(image_gray) else: images = self.detect_ssd(image) res_set = [] - for j,plate in enumerate(images): - plate,[left,top,right,bottom] = plate - print(left, top, right, bottom) + for j, plate in enumerate(images): + plate, [left, top, right, bottom] = plate + # print(left, top, right, bottom) if DB: w, h = right - left, bottom - top - plate = image[top:bottom,left:right,:] - crop_up = plate[int(h * 0.05):int((h) * 0.4), int(w * 0.2):int(w * 0.75)] - crop_down = plate[int((h) * 0.4):int(h), int(w * 0.05):w] + plate = image[top:bottom, left:right, :] + crop_up = plate[int(h * 0.05):int(h * 0.4), int(w * 0.2):int(w * 0.75)] + crop_down = plate[int(h * 0.4):int(h), int(w * 0.05):w] crop_up = cv2.resize(crop_up, (64, 40)) crop_down = cv2.resize(crop_down, (96, 40)) cropped_finetuned = np.concatenate([crop_up, crop_down], 1) # cv2.imshow("crop",plate) # cv2.waitKey(0) else: - cropped = self.loose_crop(image, [left, top, right, bottom], 120 / 48) cropped_finetuned = self.finetune(cropped) res, confidence = self.segmentation_free_recognition(cropped_finetuned) - res_set.append([res,confidence,[left,top,right,bottom ]]) + res_set.append([res, confidence, [left, top, right, bottom]]) return res_set - - -# diff --git a/hyperlpr_pip_pkg/hyperlpr/table_chs.py b/hyperlpr_pip_pkg/hyperlpr/table_chs.py index e7aa39c..8cef91e 100644 --- a/hyperlpr_pip_pkg/hyperlpr/table_chs.py +++ b/hyperlpr_pip_pkg/hyperlpr/table_chs.py @@ -1,5 +1,7 @@ -chars = [u"京", u"沪", u"津", u"渝", u"冀", u"晋", u"蒙", u"辽", u"吉", u"黑", u"苏", u"浙", u"皖", u"闽", u"赣", u"鲁", u"豫", u"鄂", u"湘", u"粤", u"桂", - u"琼", u"川", u"贵", u"云", u"藏", u"陕", u"甘", u"青", u"宁", u"新", u"0", u"1", u"2", u"3", u"4", u"5", u"6", u"7", u"8", u"9", u"A", - u"B", u"C", u"D", u"E", u"F", u"G", u"H", u"J", u"K", u"L", u"M", u"N", u"P", u"Q", u"R", u"S", u"T", u"U", u"V", u"W", u"X", - u"Y", u"Z",u"港",u"学",u"使",u"警",u"澳",u"挂",u"军",u"北",u"南",u"广",u"沈",u"兰",u"成",u"济",u"海",u"民",u"航",u"空" - ] +chars = [ + u"京", u"沪", u"津", u"渝", u"冀", u"晋", u"蒙", u"辽", u"吉", u"黑", u"苏", u"浙", u"皖", u"闽", u"赣", u"鲁", + u"豫", u"鄂", u"湘", u"粤", u"桂", u"琼", u"川", u"贵", u"云", u"藏", u"陕", u"甘", u"青", u"宁", u"新", u"0", + u"1", u"2", u"3", u"4", u"5", u"6", u"7", u"8", u"9", u"A", u"B", u"C", u"D", u"E", u"F", u"G", u"H", u"J", u"K", + u"L", u"M", u"N", u"P", u"Q", u"R", u"S", u"T", u"U", u"V", u"W", u"X", u"Y", u"Z", u"港", u"学", u"使", u"警", + u"澳", u"挂", u"军", u"北", u"南", u"广", u"沈", u"兰", u"成", u"济", u"海", u"民", u"航", u"空" +] diff --git a/hyperlpr_pip_pkg/setup.py b/hyperlpr_pip_pkg/setup.py index fd2e605..8ecb46c 100644 --- a/hyperlpr_pip_pkg/setup.py +++ b/hyperlpr_pip_pkg/setup.py @@ -29,5 +29,4 @@ setuptools.setup( "models/dnn/*.*" ] }, - )