def preprocess(dirUuid, gray, algoFunc): # 1. Sobel算子,x方向求梯度 #sobel = cv2.Sobel(gray, cv2.CV_8U, 1, 0, ksize = 3) #获取二值化阈值 thr = bz.myThreshold() #threshold = thr.get1DMaxEntropyThreshold(gray) threshold = getattr(thr, algoFunc)(gray) if threshold <= 0: raise Exception("获取二值化阈值失败") # 2. 二值化 ret, binary = cv2.threshold(gray, threshold, 255, cv2.THRESH_BINARY) # func.showImg(binary, 'binary') func.storePic(dirUuid, 'binary', binary) #获取核大小 calculateElement(gray) # 3. 膨胀和腐蚀操作的核函数 element1 = cv2.getStructuringElement(cv2.MORPH_RECT, (2, 2)) element2 = cv2.getStructuringElement(cv2.MORPH_RECT, calculateElement(gray)) #微处理去掉小的噪点 dilation = cv2.dilate(binary, element1, iterations=1) binary = cv2.erode(dilation, element1, iterations=1) #文字膨胀与腐蚀使其连成一个整体 erosion = cv2.erode(binary, element2, iterations=1) dilation = cv2.dilate(erosion, element1, iterations=1) # 7. 存储中间图片 # cv2.namedWindow("binary", cv2.WINDOW_NORMAL) # cv2.imshow("binary", binary) # cv2.waitKey(0) # # cv2.namedWindow("dilation2", cv2.WINDOW_NORMAL) # cv2.imshow("dilation2", erosion) # cv2.waitKey(0) # # cv2.namedWindow("dilation2", cv2.WINDOW_NORMAL) # cv2.imshow("dilation2", dilation) # cv2.waitKey(0) cv2.destroyAllWindows() #sys.exit(0) return dilation
def getChineseChar(dirUuid, img, kenalRect): """ 分析汉字区域,并识别提取 :return: """ global CARD_NAME global CARD_SEX global CARD_ETHNIC global CARD_YEAR global CARD_MON global CARD_DAY global CARD_ADDR global CARD_NUM CARD_NAME = CARD_SEX = CARD_ETHNIC = CARD_YEAR = CARD_MON = CARD_DAY = CARD_ADDR = '' # 图片大小比例缩小处理,为了加快性能 h, w, _ = img.shape min_w = 200 scale = 1 #min(1., w * 1. / min_w) h_proc = int(h * 1. / scale) w_proc = int(w * 1. / scale) im_dis = cv2.resize(img, (w_proc, h_proc)) # 灰度处理 gray = cv2.cvtColor(im_dis, cv2.COLOR_BGR2GRAY) # 2. 形态学变换的预处理,得到可以查找矩形的图片 mybz = bz.myThreshold() algos = mybz.getAlgos() for i in algos: #获取阈值 thr = getattr(mybz, algos[i])(gray) #thr = mybz.getMinimumThreshold(gray) #func.showImg(gray, 'gray') # 膨胀和腐蚀 ret, binary = cv2.threshold(gray, thr, 255, cv2.THRESH_BINARY) #获取行起始坐标 boundaryCoors = func.horizontalProjection(binary) if not boundaryCoors: continue #垂直投影对行内字符进行切割 erosion = cb = copy.copy(binary) #func.showImg(binary, 'binary') coors = {} #信息所对应的坐标 textLine = 0 #有效文本行序号 for LineNum, boundaryCoor in enumerate(boundaryCoors): if textLine == 2: kenal1 = cv2.getStructuringElement(cv2.MORPH_RECT, (1, 1)) kenal2 = cv2.getStructuringElement(cv2.MORPH_RECT, (2, 2)) dilation = cv2.dilate(cb, kenal1, iterations=1) erosion = cv2.erode(dilation, kenal2, iterations=1) # func.showImg(erosion, 'erosion') func.storePic(dirUuid, 'erosion', erosion) # cv2.imwrite('E:\\Test\\0.png', erosion) # vertiCoors, text = func.verticalProjection(erosion, boundaryCoor, textLine, img) vertiCoors, text = func.verticalProjection(erosion, boundaryCoor, textLine, erosion) if len(vertiCoors) == 0: continue if textLine == 0: CARD_NAME = text elif textLine == 1: if text[0] != '男' and text[0] != '女': CARD_SEX = func.getSexByCardNum(CARD_NUM) else: CARD_SEX = text[0] CARD_ETHNIC = text[1] elif textLine == 2: #为了获取更加精准的值,通过身份证号码规则直接取得出生年月 # CARD_YEAR = text[0] # CARD_MON = text[1] # CARD_DAY = text[2] pass else: CARD_ADDR += text if DEBUG: fator = 2 for verticoo in vertiCoors: box = [ [ verticoo[0] * scale - fator, boundaryCoor[0] * scale - fator ], [ verticoo[1] * scale + fator, boundaryCoor[0] * scale - fator ], [ verticoo[1] * scale + fator, boundaryCoor[1] * scale + fator ], [ verticoo[0] * scale - fator, boundaryCoor[1] * scale + fator ], ] cv2.drawContours(img, [np.int0(box)], 0, (0, 255, 0), 2) textLine += 1 return return False
def detect(dirUuid, img): global CARD_NUM CARD_NUM = '' notFound = True # 1. 转化成灰度图 gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # func.showImg(gray, 'gray') func.storePic(dirUuid, 'gray', gray) # 2. 遍历二值化阈值算法 algos = bz.myThreshold().getAlgos() # for i in algos: # # #形态学变换的预处理,得到可以查找矩形的图片 # dilation = preprocess(gray, algos[i]) # # # 3. 查找和筛选文字区域 # region = findTextRegion(dilation) # # # 4. 用绿线画出这些找到的轮廓 # angle = 0 # for rect in region: # # angle = rect[2] # # #识别身份证号码 # a, b = rect[1] # if a > b: # width = a # hight = b # pts2 = np.float32([[0, hight], [0, 0], [width, 0], [width, hight]]) # else: # width = b # hight = a # angle = 90 + angle # pts2 = np.float32([[width, hight], [0, hight], [0, 0], [width, 0]]) # # #透视变换 # box = cv2.cv.BoxPoints(rect) # pts1 = np.float32(box) # M = cv2.getPerspectiveTransform(pts1, pts2) # cropImg = cv2.warpPerspective(img, M, (int(width), int(hight))) # # # 计算核大小 # kenalx = kenaly = int(math.ceil((hight / 100.0))) # CARD_NUM = getCardNum(cropImg, (kenalx, kenaly)) # if CARD_NUM: # notFound = False # #找到身份证号码,然后根据号码区域的倾斜角度,对原图进行旋转变换 # # if abs(angle) > 10: # sp = img.shape # H = sp[0] # W = sp[1] # M = cv2.getRotationMatrix2D((W/2, H/2), angle, 1) # cropImg = cv2.warpAffine(img, M, (W, H)) # # cv2.namedWindow("倾斜矫正", cv2.WINDOW_NORMAL) # # cv2.imshow("倾斜矫正", cropImg) # # cv2.waitKey(0) # #矫正图片地址 # global curpath # path = 'tilt_correction.jpg' # newFile = os.path.join(curpath, path) # cv2.imwrite(newFile, cropImg) # # return True, '', newFile # # # 画图 # if DEBUG: # cv2.drawContours(img, [np.int0(box)], 0, (0, 255, 0), 3) # # # 寻找汉字区域 # # 裁剪后的图片 # box = cv2.cv.BoxPoints(rect) # box = np.int0(box) # cropImg, point, width, hight = func.cropImgByBox(img, box) # box = findChineseCharArea(point, width, hight) # #cv2.drawContours(img, [box], 0, (0, 255, 0), 3) # # chiCharArea, point, width, hight = func.cropImgByBox(img, box) # getChineseChar(chiCharArea, (kenalx, kenaly)) # # # winname = "身份证号码: %s" % (CARD_NUM) # # cv2.namedWindow(winname, cv2.WINDOW_NORMAL) # # cv2.imshow(winname, cropImg) # # cv2.waitKey(0) # # break # # if notFound: # continue # else: # break i = 1 #形态学变换的预处理,得到可以查找矩形的图片 dilation = preprocess(dirUuid, gray, algos[i]) # func.showImg(dilation, 'dilation') func.storePic(dirUuid, 'dilation', dilation) # 3. 查找和筛选文字区域 region = findTextRegion(dilation) # 4. 用绿线画出这些找到的轮廓 angle = 0 num = 0 for rect in region: angle = rect[2] #识别身份证号码 a, b = rect[1] if a > b: width = a hight = b pts2 = np.float32([[0, hight], [0, 0], [width, 0], [width, hight]]) else: width = b hight = a angle = 90 + angle pts2 = np.float32([[width, hight], [0, hight], [0, 0], [width, 0]]) #透视变换 # box = cv2.cv.BoxPoints(rect) box = cv2.boxPoints(rect) pts1 = np.float32(box) M = cv2.getPerspectiveTransform(pts1, pts2) cropImg = cv2.warpPerspective(img, M, (int(width), int(hight))) # 计算核大小 kenalx = kenaly = int(math.ceil((hight / 100.0))) CARD_NUM = getCardNum(num, dirUuid, cropImg, (kenalx, kenaly)) num = num + 1 if CARD_NUM: notFound = False #找到身份证号码,然后根据号码区域的倾斜角度,对原图进行旋转变换 if abs(angle) > 10: sp = img.shape H = sp[0] W = sp[1] M = cv2.getRotationMatrix2D((W / 2, H / 2), angle, 1) cropImg = cv2.warpAffine(img, M, (W, H)) # cv2.namedWindow("倾斜矫正", cv2.WINDOW_NORMAL) # cv2.imshow("倾斜矫正", cropImg) # cv2.waitKey(0) #矫正图片地址 global curpath path = 'tilt_correction.jpg' newFile = os.path.join(curpath, path) cv2.imwrite(newFile, cropImg) return True, '', newFile, '' # 画图 if DEBUG: cv2.drawContours(img, [np.int0(box)], 0, (0, 255, 0), 3) # 寻找汉字区域 # 裁剪后的图片5 box = cv2.boxPoints(rect) box = np.int0(box) # 寻找人脸区域 faceBox = cv2.boxPoints(rect) faceBox = np.int0(faceBox) cropImg, point, width, hight = func.cropImgByBox(img, box) box = findChineseCharArea(point, width, hight) faceBox = findFaceArea(point, width, hight) #cv2.drawContours(img, [box], 0, (0, 255, 0), 3) chiCharArea, point, width, hight = func.cropImgByBox(img, box) # func.showImg(chiCharArea, 'chiCharArea') getChineseChar(dirUuid, chiCharArea, (kenalx, kenaly)) faceArea, _, _, _ = func.cropImgByBox(img, faceBox) # func.showImg(faceArea, 'faceArea') func.storePic(dirUuid, 'faceArea', faceArea) # winname = "身份证号码: %s" % (CARD_NUM) # cv2.namedWindow(winname, cv2.WINDOW_NORMAL) # cv2.imshow(winname, cropImg) # cv2.waitKey(0) break if notFound: continue else: break if notFound: #win32api.MessageBox(0, "无法识别,请换一个分辨率高点的照片~", "错误提示") return False, '无法识别,请换一个分辨率高点的照片~", "错误提示', '', '' # 带轮廓的图片 if DEBUG: cv2.namedWindow("img", cv2.WINDOW_NORMAL) cv2.imshow("img", img) key = cv2.waitKey(0) cv2.destroyAllWindows() if key != 32: sys.exit() if CARD_NUM != '': # 为了获取更加精准的值,通过身份证号码规则直接取得出生年月 CARD_YEAR, CARD_MON, CARD_DAY = func.getBirthByCardNum(CARD_NUM) info = """ 姓名:%s 性别:%s 民族:%s 出生:%s 年 %s 月 %s 日 住址:%s 公民身份号码:%s """ % (CARD_NAME, CARD_SEX, CARD_ETHNIC, CARD_YEAR, CARD_MON, CARD_DAY, CARD_ADDR, CARD_NUM) ret = [ CARD_NAME, CARD_SEX, CARD_ETHNIC, CARD_YEAR, CARD_MON, CARD_DAY, CARD_ADDR, CARD_NUM ] if DEBUG: print info return True, ret, '', faceArea else: return True, '', '', ''