def __init__(self, vlogger = None): self.vlogger = vlogger self.pre= None self.edged= None self.warp= ImageBlobWarping() self.p= Pool(processes = 6) self.bnight= False
def __init__(self): self.vlogger = None self.pre= None self.edged= None self.warp= ImageBlobWarping() self.bnight= False self.ocr_engine = Ocr('spa', logger)
class PlateDetector(object): def __init__(self, vlogger = None): self.vlogger = vlogger self.pre = None self.edged = None self.warp = ImageBlobWarping() self.p = Pool(processes = 6) def find(self, img): blobs = self.findBlobs(img) for b in blobs: plate = self.checkBlob(b) if plate: return plate return None def find2(self, img): lastp = None blobs = self.findBlobs(img) for b in blobs: bb=np.int0(cv2.boxPoints(b)) lastp = cv2.boundingRect(bb) plate = self.checkBlob(b) if plate: return plate, lastp return None, lastp def findContours2(self, img): height = img.shape[0] width = img.shape[1] kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3,3)) # matrix of ones cnts = [] for gray in cv2.split(img):# [cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)]:# dilated = cv2.dilate(src = gray, kernel = kernel, anchor = (-1,-1)) blured = cv2.medianBlur(dilated, 7) small = cv2.pyrDown(blured, dstsize = (width / 2, height / 2)) oversized = cv2.pyrUp(small, dstsize = (width, height)) for thrs in xrange(0, 255, 26): if thrs == 0: edges = cv2.Canny(oversized, threshold1 = 0, threshold2 = 50, apertureSize = 3) next = cv2.dilate(src = edges, kernel = kernel, anchor = (-1,-1)) else: retval, next = cv2.threshold(gray, thrs, 255, cv2.THRESH_BINARY) _, contours, hierarchy = cv2.findContours(next, mode = cv2.RETR_LIST, method = cv2.CHAIN_APPROX_SIMPLE) cnts.extend(contours) self.prepare(img) return cnts def findContours(self, img): i = self.prepare(img) _, cnts, hie = cv2.findContours(i, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_SIMPLE) return cnts def findBlobs(self, img): rects = [] cnts = self.findContours(img) for c in cnts: c = c.reshape(-1, 2) if len(c) < 4: continue arcl = cv2.arcLength(c, True) approx = cv2.approxPolyDP(c, 0.02 * arcl, True) approx = approx.reshape(-1, 2) if len(approx) == 4 and cv2.contourArea(approx) > 3410: max_cos = np.max([self.angle_cos(approx[i], approx[(i+1) % 4], approx[(i+2) % 4]) for i in xrange(4)]) if max_cos < 0.25: rect = cv2.minAreaRect(approx) w, h = rect[1] ratio = float(w) / h if w>h else float(h) / w if 2.4 < ratio < 4.2: rects.append(rect) return rects def checkBlob(self, rect): ang = rect[2] w = rect[1][0] h = rect[1][1] if ang<-45: ang = ang + 90 w = h h = rect[1][0] box = cv2.boxPoints(rect) box = np.int0(box) box = self.warp.order_points(box) roic = self.warp.transform(self.edged, box) roi =self.warp.transform(self.pre, box) (roich,roicw) = roic.shape[:2] nh = 143 if roich>200: nw = (roicw * nh)/roich roi = cv2.resize(roi,(nw, nh), interpolation = cv2.INTER_LINEAR) roic = cv2.resize(roic,(nw, nh), interpolation = cv2.INTER_LINEAR) letters = [] i, cnts, _ = cv2.findContours(roic, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) h = roic.shape[0] if not cnts or len(cnts) < 5: return None for b in cnts: c = b.reshape(-1,2) if len(b)<3: continue r = cv2.boundingRect(c) ratio = float(r[3]) / r[2] if not 1.5 <= ratio <= 2.5 or r[3] < 0.5*h or r[0]==1: continue letters.append(r) return self.findChars(self.prepare2(roi), letters) def findChars(self, img, blobs): i = 0 letters = [(img,p[1], p[0]) for p in enumerate(sorted(blobs, key=lambda b:b[0]))] plate = self.p.map(get_start, letters) return ''.join(filter(None,plate)) def prepare(self, img, scale=True): gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) self.pre = cv2.GaussianBlur(gray, (5, 5), 0) self.edged = cv2.Canny(self.pre, 500, 1605, apertureSize=5) if self.vlogger: self.vlogger.debug(VisualRecord("prepare", [self.pre, self.edged], fmt = "jpg")) return self.edged def prepare2(self, img, scale=True): kern = np.ones((3,5),np.uint8) th1 = cv2.erode(img/2, kern, iterations = 1) ret,th = cv2.threshold(th1, 77, 255, cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU) if self.vlogger: self.vlogger.debug(VisualRecord("prepare2", [img, th1, th], fmt = "jpg")) return th def angle_cos(self, p0, p1, p2): d1, d2 = (p0-p1).astype('float'), (p2-p1).astype('float') return abs( np.dot(d1, d2) / np.sqrt( np.dot(d1, d1)*np.dot(d2, d2) ) )
class PlateDetector(object): def __init__(self, vlogger = None): self.vlogger = vlogger self.pre= None self.edged= None self.warp= ImageBlobWarping() self.p= Pool(processes = 6) self.bnight= False def set_logger(self, logger): self.vlogger = logger def bestocr(self, ocrlst): ocr_overall_acc_lst= [] imax= -1 if len(ocrlst) == 0: return imax ocr_acc= 0 #~ print ocrlst for ocritm in ocrlst: #~ print ocritm for det in ocritm: if det is not None and det[1] != None: try: ocr_acc = ocr_acc + det[1]**2 except: pass if len(ocritm) > 0: ocr_acc /= len(ocritm) ocr_acc= ocr_acc**0.5 print "ocr_acc: %.3f %%"%ocr_acc ocr_overall_acc_lst.append(round(ocr_acc,3)) imax= max(ocr_overall_acc_lst) return ocr_overall_acc_lst.index(imax) """ Return best text recognized """ def first(self, img): bbox= None code= None cnt= None blobs= self.findBlobs( img ) ocrlst= [] bboxlst= [] cntslst= [] print "original image shape:", img.shape for orig_rot_blob in blobs: bb= np.int0(cv2.boxPoints( orig_rot_blob )) bbox= cv2.boundingRect( bb ) w= bbox[2] h= bbox[3] if (w > 2*h) and (w > 80) and (w < 200): # this should be relative to image dimensions code,cnts= self.ocr( orig_rot_blob ) if code: ocrlst.append( code ) bboxlst.append( bbox ) cntslst.append( cnts ) if len(code) == 6: break # hardcoded -- width should not be higher than img.width / 8 if (w > 2*h) and (w > 80) and (w < 400): # second stage without max size constraints code,cnts= self.ocr( orig_rot_blob ) if code: ocrlst.append( code ) bboxlst.append( bbox ) cntslst.append( cnts ) if len(code) == 6: break if len( ocrlst ) > 0: ocr_best_index= self.bestocr( ocrlst ) if ocr_best_index != -1: code= ocrlst[ ocr_best_index ] bbox= bboxlst[ ocr_best_index ] cnt= cntslst[ ocr_best_index ] else: print "none" return code, bbox def findBlobs(self, img): rects= [] cnts= self.findContours(img) for c in cnts: c= c.reshape(-1, 2) if len(c) < 4: continue arcl= cv2.arcLength(c, True) approx= cv2.approxPolyDP(c, 0.02 * arcl, True) approx= approx.reshape(-1, 2) rect= cv2.minAreaRect(approx) w, h= rect[1] if len(approx) >= 4: if (h > 0) and (w > h): ratio = float(w) / h if 2.4 < ratio < 4.2: rects.append(rect) return rects def ocr(self, rect): ang= rect[2] w,h= rect[1] if ang < -45: ang= ang + 90 w= h h= rect[1][0] box= cv2.boxPoints(rect) box= np.int0(box) box= self.warp.order_points(box) letters= [] code= [] try: roic= self.warp.transform(self.edged, box) roi= self.warp.transform(self.pre, box) roi_orig= self.warp.transform(self.original_image, box) except: pass print "some error" return code (roich, roicw)= roic.shape[:2] nh= 143 if roich > 200: nw= (roicw * nh)/roich roi= cv2.resize(roi,(nw, nh), interpolation= cv2.INTER_LINEAR) roic= cv2.resize(roic,(nw, nh), interpolation= cv2.INTER_LINEAR) #~ self.do_skeleton(roi) image_rect= self.prepare_for_ocr(roi) image_rect2= image_rect.copy() if self.vlogger: self.vlogger.debug(VisualRecord("candidate", [image_rect], fmt = "jpg")) i, cnts, hie_letters= cv2.findContours(image_rect, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) print "candidate shape", image_rect.shape if self.vlogger: self.vlogger.debug(VisualRecord("candidate after contours", [cv2.drawContours(roi_orig,cnts,-1,(0,255,0),1)], fmt = "jpg")) h= roic.shape[0] filtered_cnts= [] for i,b in enumerate(cnts): hie_let= hie_letters[0][i] # [next, previous, first_child, parent] if hie_let[3] == -1: # if contour has no parent then continue with next continue c = b.reshape(-1,2) if len(b) < 3: # ?? continue r= cv2.boundingRect(c) # pantentes.txt - las letras miden 3.2cm y la patente completa 29.4cm if r[2] < (image_rect.shape[1] / 10): continue ratio= float(r[3]) / r[2] if not 1.5 <= ratio <= 2.5: continue letters.append(r) filtered_cnts.append(b) if len(letters) >= 4: for p in enumerate(sorted(letters, key= lambda b:b[0])): detected_letter= get_start((image_rect2, p[1], p[0])) code.append(detected_letter) print "letter detected: ",detected_letter if self.vlogger: self.vlogger.debug(VisualRecord("LETTER DETECTION", [cv2.drawContours(image_rect2,filtered_cnts,-1,(0,255,0),1)], fmt = "jpg")) return code, cnts def findContours(self, img): imgcopy= img.copy() if self.bnight: i= self.prepare_night(img) else: i= self.prepare_day(img) _,cnts, hie = cv2.findContours(i, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) if self.vlogger: if self.bnight: self.vlogger.debug(VisualRecord("contours", [cv2.drawContours(imgcopy,cnts,-1, (80,255,80),2),i], fmt = "jpg")) else: self.vlogger.debug(VisualRecord("contours", [cv2.drawContours(imgcopy,cnts,-1, (255,120,120),2),i], fmt = "jpg")) return cnts #################################################################################################### def prepare_night(self, img): tinit= timer() self.original_image= img gray= cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) gauss_gray= cv2.GaussianBlur(gray, (5, 5), 0) max_gray= np.max(gray) std_gray= np.std(gray) saturated_night= np.uint8(( gray > ( max_gray - 2 * std_gray )) * 255) # argentina self.pre= gauss_gray self.edged= cv2.Canny(saturated_night, 10, 200, apertureSize= 5) if self.vlogger: self.vlogger.debug(VisualRecord("thresholding > (max - 2 * std)", [saturated_night], fmt = "jpg")) print "e:%.3f"%(timer()-tinit) return self.edged #################################################################################################### def prepare_day(self, img): self.original_image= img gray= cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) gauss_gray= cv2.GaussianBlur(gray, (5, 5), 0) self.pre= gauss_gray self.edged= cv2.Canny(gauss_gray, 1000, 1700, apertureSize= 5) if self.vlogger: self.vlogger.debug(VisualRecord("day prepare", [self.pre, self.edged], fmt = "jpg")) return self.edged #################################################################################################### def do_skeleton(self, img): size= np.size(img) skel= np.zeros(img.shape,np.uint8) ret,img = cv2.threshold(img,127,255,0) element = cv2.getStructuringElement(cv2.MORPH_CROSS,(3,3)) done = False while( not done): eroded= cv2.erode(img,element) temp= cv2.dilate(eroded,element) temp= cv2.subtract(img,temp) skel= cv2.bitwise_or(skel,temp) img= eroded.copy() zeros= size- cv2.countNonZero(img) if zeros == size: done= True if self.vlogger: self.vlogger.debug(VisualRecord("pancro", [img, skel], fmt = "jpg")) #################################################################################################### def angle_cos(self, p0, p1, p2): d1, d2 = (p0-p1).astype('float'), (p2-p1).astype('float') return abs( np.dot(d1, d2) / np.sqrt( np.dot(d1, d1)*np.dot(d2, d2) ) ) def prepare_for_ocr(self, img, scale=True): #~ print "shape", img.shape kern= cv2.getStructuringElement(cv2.MORPH_RECT,(3,3)) # http://docs.opencv.org/master/d5/daf/tutorial_py_histogram_equalization.html#gsc.tab=0 clahe= cv2.createCLAHE(clipLimit=2.0, tileGridSize=(5,5)) ims= clahe.apply(img) ret,th= cv2.threshold(ims, 150, 255, cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU) th1= cv2.morphologyEx(th, cv2.MORPH_CLOSE, kern) th2= self.create_rect(th1) if self.vlogger: self.vlogger.debug(VisualRecord("prepare_for_ocr", [img, ims, th], fmt = "jpg")) self.calc_stats(img) return th2 def calc_stats(self, img): uniques= np.unique(np.ravel(img)) hist, bins= np.histogram(img, bins= uniques) mxcount= np.max(hist) #~ print hist #~ print bins print "min-",np.min(uniques),"max-",np.max(uniques) print "unique values-", len(uniques) print "mxcount-", mxcount, "index-", np.where(hist == mxcount)[0][0], "DN-", bins[np.where(hist == mxcount)][0] def create_rect(self, img): dims= img.shape imgcop= img.copy() imgcop[0:4,0:dims[1]]= 255 imgcop[dims[0]-2:dims[0],0:dims[1]]= 255 if self.vlogger: self.vlogger.debug(VisualRecord("CREATE RECT", [imgcop], fmt = "jpg")) return imgcop