答题卡识别

两个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

上一篇:专业3 学生信息添加(原声数据进行添加+文件上传进行验证)


下一篇:android解析httpresponce内容