• 答题卡识别


    两个idea:

    1、霍夫变换找圆在复杂环境并不理想,可以找轮廓,然后限制外接矩形w和h来排除

    2、坐标排序时,可能同一行的x有些许误差,则可以这样排序(分两次排序):假设一行5个,

    先从上向下排序,则每5个即为一道题的选项,虽然是乱序的,但只要再对每5个从左向右排序即可

    主要判断论述:

    import cv2
    import numpy as np
    import math
    
    def show(img):
        cv2.imshow('name', img)
        cv2.waitKey()
        cv2.destroyAllWindows()
    
    # def equal(x, y): #如果相差在15以内,则说明相等
    #     if abs(x - y) <= 15:
    #         return 1
    #     return 0
    #
    # def cnt_sort(docCnt):
    #     for i in range(len(docCnt) - 1):
    #         for j in range(len(docCnt) - i - 1):
    #             if equal(docCnt[j][1], docCnt[j + 1][1]) == 1:
    #                 if(docCnt[j][0] > docCnt[j + 1][0]):
    #                     tmp = docCnt[j + 1].copy()
    #                     docCnt[j + 1] = docCnt[j]
    #                     docCnt[j] = tmp
    #             else:
    #                 if docCnt[j][1] > docCnt[j + 1][1]:
    #                     tmp = docCnt[j + 1].copy()
    #                     docCnt[j + 1] = docCnt[j]
    #                     docCnt[j] = tmp
    #     return docCnt
    
    
    def cnt_sort(docCnt):
    
        docCnt = sorted(docCnt, key = lambda x : x[1])
        ret = []
        for i in range(0, len(docCnt), 2):
            cnts = docCnt[i : i + 2]
            cnts = sorted(cnts, key = lambda x : x[0])
            ret += cnts
    
        return ret
    
    
    def four_point_transform(img, docCnt):
        # print(docCnt)
        docCnt = cnt_sort(docCnt)
        docCnt = np.array(docCnt, dtype = 'float32')
    
        coner1, coner2, coner3, coner4 = docCnt
        # print(docCnt)
        w = int(max(math.sqrt((coner1[0] - coner2[0]) ** 2 + (coner1[1] - coner2[1]) ** 2), math.sqrt((coner3[0] - coner4[0]) ** 2 + (coner3[1] - coner4[1]) ** 2)))
        h = int(max(math.sqrt((coner1[0] - coner3[0]) ** 2 + (coner1[1] - coner3[1]) ** 2), math.sqrt((coner2[0] - coner4[0]) ** 2 + (coner2[1] - coner4[1]) ** 2)))
        rightCnt = np.array([[0, 0], [w, 0], [0, h], [w, h]], dtype = 'float32')
        # print(docCnt, rightCnt)
        transform_matrix = cv2.getPerspectiveTransform(docCnt, rightCnt) #输入类型需要一样,都是float32就行
        img = cv2.warpPerspective(img, transform_matrix, (w, h)) #w和h需要是整数
        return img
    
    
    def questionCnt_sort(questionCnt, flag):
        # flag为1就是从上向下,0就是从左到右
        boundingbox = [cv2.boundingRect(cnt) for cnt in questionCnt]
        questionCnt, boundingbox = zip(*sorted(zip(questionCnt, boundingbox), key = lambda x : x[1][flag]))
        return questionCnt
    
    if __name__ == '__main__':
        img = cv2.imread('C:/Users/WTSRUVF/Downloads/card/answer sheet/images/test_01.png')
        img_gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
        img_gray = cv2.GaussianBlur(img_gray, (5, 5), 0)
        edged = cv2.Canny(img_gray, 75, 200)
        # show(edged)
        contours = cv2.findContours(edged, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[1]
        contours = sorted(contours, key = cv2.contourArea, reverse = True)
        docCnt = None
        for cnt in contours:
            peri = cv2.arcLength(cnt, True)
            approx = cv2.approxPolyDP(cnt, peri * 0.02, True)
            if len(approx) == 4:
                docCnt = approx
                break
        img_gray_copy = img_gray.copy()
        # img_copy = cv2.drawContours(img_copy, [docCnt], -1, (0, 0, 255), 2)
        img_s = four_point_transform(img_gray_copy, docCnt.reshape(4, 2))
        # show(img_s)
        thresh = cv2.threshold(img_s, 0, 255, cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU)[1]
        threshContours = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[1]
        questionCnt = []
        for cnt in threshContours:
            (x, y, w, h) = cv2.boundingRect(cnt)
            ar = w / float(h)
            if w >= 20 and h >= 20 and ar >= 0.9 and ar <= 1.1:
                questionCnt.append(cnt)
        questionCnt = questionCnt_sort(questionCnt, 1)
        #先从上向下排序,则每5个即为一道题的选项,虽然是乱序的,但只要再对每5个从左向右排序即可
        for k, i in enumerate(range(0, len(questionCnt), 5)):
            cnts = questionCnt_sort(questionCnt[i : i + 5], 0)
            answer = None
            for j, cnt in enumerate(cnts):
                mask = np.zeros(img_s.shape, dtype = 'uint8')
                mask = cv2.drawContours(mask, [cnt], -1, 255, -1) #-1为填充
                mask = cv2.bitwise_and(thresh, thresh, mask = mask)
                total = cv2.countNonZero(mask)
                if answer == None or answer[0] < total:
                    answer = (total, j + 1)
            print(answer[1])

     数据集和代码链接:https://pan.baidu.com/s/1SMJ76fL1sbFmWUGurAjoQQ
    提取码:qgz6

  • 相关阅读:
    sql server 镜像操作
    微信测试公众号的创见以及菜单创建
    linux安装redis步骤
    Mysql 查询表字段数量
    linux 链接mysql并覆盖数据
    linux (centos)增删改查用户命令
    CentOS修改用户密码方法
    https原理及其中所包含的对称加密、非对称加密、数字证书、数字签名
    com.mysql.cj.exceptions.InvalidConnectionAttributeException: The server time zone value 'Öйú±ê׼ʱ¼ä' is unrecognized or represents more than one time zone. 问题解决方法
    设计模式(三):模板方法模式
  • 原文地址:https://www.cnblogs.com/WTSRUVF/p/15825882.html
Copyright © 2020-2023  润新知