身份证ID号识别

def img_show(name, img):
    cv2.imshow(name, img)  # 图像显示,第一个变量为窗口名称
    cv2.waitKey(0)  # 等待时间,0表示任意键终止
    cv2.destroyAllWindows()  # 关闭所有窗口


class IDNumber(object):
    def __init__(self, tem_file_name):
        self.template = self.get_tem_nums(tem_file_name)
        ID = self.get_img_num('E:/RL/ID_identify/3.jpg')
        ID_number = self.identify(ID)
        print(ID_number)

    # 规定w宽度,保证原比例不变放缩图像
    def change_size(self, img, aim_w=512):
        h, w = img.shape[:2]
        ratio = h/w
        aim_h = int(aim_w * h / w)
        new_img = cv2.resize(img, (aim_w, aim_h))
        return new_img

    # 计算轮廓最小外界矩形面积
    def contourArea(self, cnt):
        rect = cv2.minAreaRect(cnt)
        box = cv2.boxPoints(rect)
        box = np.int64(box)
        return cv2.contourArea(box)

    # 获得身份证ID图像
    def get_img_num(self, img_file):
        kernel_3 = np.ones((3, 3))
        kernel_5 = np.ones((5, 5))

        img = cv2.imread(img_file)
        img = self.change_size(img, aim_w=800)
        gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

        # 提取身份证位置
        sobel = self.gradient(gray_img)  # 图像梯度计算
        sobel = cv2.threshold(sobel, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
        sobel = cv2.morphologyEx(sobel, cv2.MORPH_CLOSE, kernel_3, iterations=2)
        sobel = cv2.morphologyEx(sobel, cv2.MORPH_CLOSE, kernel_5, iterations=2)

        contours, hierarchy = cv2.findContours(sobel, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)  # 轮廓检测
        contour = sorted(contours, key=self.contourArea, reverse=True)[0]  # 寻找最大面积的轮廓即身份证

        # 轮廓近似为四边形
        epsilon = 0.02 * cv2.arcLength(contour, True)  # 通常间隔阈值是通过周长的百分比进行比较
        approx = cv2.approxPolyDP(contour, epsilon, True)  # 轮廓近似
        aim_img = self.transform(img, approx)  # 透视变换,将身份证图片放正

        aim_img = self.change_size(aim_img, aim_w=500)
        aim_gray_img = cv2.cvtColor(aim_img, cv2.COLOR_BGR2GRAY)
        img_show('a', aim_gray_img)
        aim_gray_img = cv2.medianBlur(aim_gray_img, 3)

        # 找到身份证号码轮廓
        aim_gray_img = cv2.morphologyEx(aim_gray_img, cv2.MORPH_BLACKHAT, kernel_5, iterations=2)
        aim_gray_img = cv2.threshold(aim_gray_img, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
        aim_gray_img = cv2.morphologyEx(aim_gray_img, cv2.MORPH_CLOSE, kernel_5, iterations=2)
        aim_gray_img = cv2.dilate(aim_gray_img, kernel_3, iterations=3)
        contours, hierarchy = cv2.findContours(aim_gray_img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

        # 根据身份证号码长宽比确定位置轮廓
        area = []
        loc = []
        loc_contours = []
        for cnt in contours:
            x, y, w, h = cv2.boundingRect(cnt)
            if 6 <= (w / h) <= 13:
                loc_contours.append(cnt)
                area.append(self.contourArea(cnt))
                loc.append([x, y, w, h])
        [x, y, w, h] = loc[np.argmax(area)]
        contour = loc_contours[np.argmax(area)]

        # 利用最小外界矩形,加透视变换,将身份证号码放正
        rect = cv2.minAreaRect(contour)
        box = cv2.boxPoints(rect)
        box = np.int0(box)
        box = [[box_loc] for box_loc in box]
        ID_img = self.transform(aim_img, box)

        # 提取身份证号码位置图像
        ID_img = self.change_size(ID_img, aim_w=400)
        ID_gray_img = cv2.cvtColor(ID_img, cv2.COLOR_BGR2GRAY)
        ID_gray_img = cv2.threshold(ID_gray_img, 0, 255, cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU)[1]
        img_show('a', ID_gray_img)

        contours, hierarchy = cv2.findContours(ID_gray_img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
        # res = cv2.drawContours(ID_img.copy(), contours, -1, (0, 0, 255), 2)
        # img_show('a', res)
        ID = []
        location_x = []
        for cnt in contours:
            x, y, w, h = cv2.boundingRect(cnt)
            if w * h > 25:
                location_x.append(x)
                loc = ID_img[y:y + h, x:x + w]
                loc = cv2.resize(loc, (100, 150), interpolation=cv2.INTER_LINEAR)
                loc = cv2.cvtColor(loc, cv2.COLOR_BGR2GRAY)
                loc = cv2.threshold(loc, 0, 255, cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU)[1]
                ID.append(loc)
        ID = np.array(ID)
        ID = ID[np.argsort(location_x)]  # 根据数字x坐标排序身份证图像
        return ID

    # 图像梯度计算
    def gradient(self, gray_img, weight_x=0.5, weight_y=0.5):

        if (weight_x+weight_y) != 1:
            weight_x = weight_x / (weight_x + weight_y)
            weight_y = weight_y / (weight_x + weight_y)

        sobelx = cv2.Scharr(gray_img, cv2.CV_64F, 1, 0)  # X方向上的梯度,此时数值存在正负
        sobelx = cv2.convertScaleAbs(sobelx)  # 对其进行绝对值操作,否则只能显示出白减黑的梯度
        (minval, maxval) = (np.min(sobelx), np.max(sobelx))
        sobelx = (255 * ((sobelx - minval) / (maxval - minval)))
        sobelx = sobelx.astype('uint8')

        sobely = cv2.Scharr(gray_img, cv2.CV_64F, 0, 1)  # y方向上的梯度,此时数值存在正负
        sobely = cv2.convertScaleAbs(sobely)  # 对其进行绝对值操作,否则只能显示出白减黑的梯度
        (minval, maxval) = (np.min(sobely), np.max(sobely))
        sobely = (255 * ((sobely - minval) / (maxval - minval)))
        sobely = sobely.astype('uint8')

        sobel = cv2.addWeighted(sobelx, weight_x, sobely, weight_y, 0)
        return sobel

    # 获得0-X模板图像
    def get_tem_nums(self, file_name):
        template_img = cv2.imread(file_name)
        template_img_gray = cv2.cvtColor(template_img, cv2.COLOR_BGR2GRAY)
        template_img_gray = cv2.threshold(template_img_gray, 128, 255, cv2.THRESH_BINARY_INV)[1]  # 获得二值化标签图像

        contours, hierarchy = cv2.findContours(template_img_gray, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)  # 获得轮廓信息
        contours = sorted(contours, key=self.contourArea, reverse=True)[:11]  # 获取面积前11的轮廓,即0-X

        template_nums = np.array([cv2.boundingRect(cnt) for cnt in contours])  # 0-X的(x,y,w,h)
        template_nums_x = [template_num[0] for template_num in template_nums]  # 各个数字的X坐标值
        template_nums = template_nums[np.argsort(template_nums_x)]  # 将0-X按顺序排序

        template_num_img = []  # 将0-X标签图像存入
        for [x, y, w, h] in template_nums:
            loc = template_img_gray[y:y+h, x:x+w]
            loc = cv2.resize(loc, (100, 150))
            template_num_img.append(loc)
        return template_num_img

    # 将数字图像与模板进行匹配
    def identify(self, ID_imgs):
        ID = str()  # 身份证数字
        for id_img in ID_imgs:
            scores = []
            # 匹配各个模板,取得分最高的一项
            for tem in self.template:
                result = cv2.matchTemplate(id_img, tem, cv2.TM_CCOEFF)
                _, score, _, _ = cv2.minMaxLoc(result)
                scores.append(score)
            num = np.argmax(scores)
            if num == 10:
                num = 'X'
            else:
                num = str(num)
            ID += num
        return ID

    # 透视变换,将图片根据四坐标点摆正
    def transform(self, img, points):
        new_points = []
        new_points_y = []
        new_points_x = []
        for point in points:
            new_points.append(point[0].tolist())
            new_points_x.append(point[0][0].tolist())
            new_points_y.append(point[0][1].tolist())

        top_points = [new_points[i] for i in np.argsort(new_points_y)[:2]]
        left_points = [new_points[i] for i in np.argsort(new_points_x)[:2]]
        for top_point in top_points:
            if top_point in left_points:
                tl = top_point
                new_points.remove(tl)
                top_points.remove(tl)
                tr = top_points[0]
                new_points.remove(tr)
                left_points.remove(tl)
                bl = left_points[0]
                new_points.remove(bl)
                br = new_points[0]

        widthA = np.sqrt(((tr[0] - tl[0]) ** 2) + ((tr[1] - tl[1]) ** 2))
        widthB = np.sqrt(((br[0] - bl[0]) ** 2) + ((br[1] - bl[1]) ** 2))
        maxWidth = max(int(widthA), int(widthB))

        heightA = np.sqrt(((tl[0] - bl[0]) ** 2) + ((tl[1] - bl[1]) ** 2))
        heightB = np.sqrt(((tr[0] - br[0]) ** 2) + ((tr[1] - br[1]) ** 2))
        maxHeight = max(int(heightA), int(heightB))

        points = np.array([tl, tr, br, bl], dtype='float32')

        changed_points = np.array([[0, 0],
                                   [maxWidth - 1, 0],
                                   [maxWidth - 1, maxHeight - 1],
                                   [0, maxHeight - 1]], dtype='float32')

        M = cv2.getPerspectiveTransform(points, changed_points)  # 计算变换矩阵
        warped = cv2.warpPerspective(img, M, (maxWidth, maxHeight))  # 将图片摆正
        return warped


a = IDNumber('E:/RL/ID_identify/number_ref.jpg')

参考视频:唐宇迪opencv P36-P34

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值