opencv答题卡识别项目

代码所用图片:
opencv答题卡识别项目

直接上代码:
datika.py

# @time: 2022/2/17 18:20
# @Author: wangshubo
# @File: datika.py
# @description:
# @author_email: '971490321@qq.com'
import cv2
import numpy as np
from myutils import sort_contours

from utilsW.utils import cvSHow, CalDistance

# 正确答案: BEADB
ANSWER_KEY = {0: 1, 1: 4, 2: 0, 3: 3, 4: 1}

def four_point_transform(image, pts):
    # rect = order_points(pts)
    rect = pts.squeeze().astype(np.float32)
    (tl, tr, bl, br) = rect[1], rect[2], rect[0], rect[3],
    # 计算输入的w和h值
    widthA = CalDistance(tl, tr)
    widthB = CalDistance(bl, br)
    maxWidth = max(int(widthA), int(widthB))

    heightA = CalDistance(tl, bl)
    heightB = CalDistance(tr, br)
    maxHeight = max(int(heightA), int(heightB))

    # 变换后对应坐标位置
    dst = np.array([
        [0, 0],
        [maxWidth - 1, 0],
        [maxWidth - 1, maxHeight - 1],
        [0, maxHeight - 1]], dtype="float32")
    src_points = np.array([bl, br, tr, tl])
    # 计算变换矩阵
    M = cv2.getPerspectiveTransform(src_points, dst)
    warped = cv2.warpPerspective(image, M, (maxWidth, maxHeight))

    # 返回变换后结果
    return warped

if __name__ == '__main__':
    src = cv2.imread("D:/images/datika.png")
    cvSHow("src", src)
    gray = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY)
    blur_img = cv2.GaussianBlur(gray, (5, 5), 0)
    cvSHow("blur_img", blur_img)
    edge_img = cv2.Canny(blur_img, 75, 200)
    cvSHow("edge_img", edge_img)

    cnts = cv2.findContours(edge_img.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[0]
    cv2.drawContours(src, cnts, -1, (0, 0, 255), 2)
    cvSHow("draw", src)
    docCnt = None

    if len(cnts) > 0:
        cnts = sorted(cnts, key=cv2.contourArea, reverse=True)  # reverse为TRUE则为降序,FALSE则为升序

        for c in cnts:
            peri = cv2.arcLength(c, True)
            approx = cv2.approxPolyDP(c, 0.02 * peri, True)  # 主要功能是把一个连续光滑曲线折线化

            if len(approx) == 4:
                docCnt = approx
                break

    #透视变换
    warped = four_point_transform(gray, docCnt.reshape(4, 2))
    cvSHow("warped", warped)

    thresh = cv2.threshold(warped, 0, 255, cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU)[1]
    cvSHow("thresh", thresh)

    thresh_Contours = thresh.copy()

    cnts = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[0]
    cv2.drawContours(warped, cnts, -1, (0,0,255),2)
    cvSHow("thresh_Contours", warped)
    questionCnts = []

    for c in cnts:
        (x, y, w, h) = cv2.boundingRect(c)
        ar = w / float(h)

        if w >=20 and h >= 20 and ar >=0.9 and ar <= 1.1:
            questionCnts.append(c)

    questionCnts = sort_contours(questionCnts, method="top-to-bottom")[0]
    correct = 0

    for (q, i) in enumerate(np.arange(0, len(questionCnts), 5)):
        # 排序
        cnts = sort_contours(questionCnts[i:i + 5])[0]
        bubbled = None

        # 遍历每一个结果
        for (j, c) in enumerate(cnts):
            # 使用mask来判断结果
            mask = np.zeros(thresh.shape, dtype="uint8")
            cv2.drawContours(mask, [c], -1, 255, -1)  # -1表示填充
            cvSHow('mask', mask)
            # 通过计算非零点数量来算是否选择这个答案
            mask = cv2.bitwise_and(thresh, thresh, mask=mask)
            cvSHow("mask1",mask)
            total = cv2.countNonZero(mask)

            # 通过阈值判断
            if bubbled is None or total > bubbled[0]:
                bubbled = (total, j)

        # 对比正确答案
        color = (0, 0, 255)
        k = ANSWER_KEY[q]

        # 判断正确
        if k == bubbled[1]:
            color = (0, 255, 0)
            correct += 1  

        # 绘图
        cv2.drawContours(warped, [cnts[k]], -1, color, 3)

    score = (correct / 5.0) * 100
    print("[INFO] score: {:.2f}%".format(score))
    cv2.putText(warped, "{:.2f}%".format(score), (10, 30),
                cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 0, 255), 2)
    cv2.imshow("Original", src)
    cv2.imshow("Exam", warped)
    cv2.waitKey(0)

引用文件
myutils.py

# @time: 2022/1/17 16:22
# @Author: wangshubo
# @File: myutils.py
# @description:
# @author_email: '971490321@qq.com'


import cv2

def sort_contours(cnts, method="left-to-right"):
    reverse = False
    i = 0

    if method == "right-to-left" or method == "bottom-to-top":
        reverse = True

    if method == "top-to-bottom" or method == "bottom-to-top":
        i = 1
    boundingBoxes = [cv2.boundingRect(c) for c in cnts] #用一个最小的矩形,把找到的形状包起来x,y,h,w
    (cnts, boundingBoxes) = zip(*sorted(zip(cnts, boundingBoxes),
                                        key=lambda b: b[1][i], reverse=reverse))

    return cnts, boundingBoxes
def resize(image, width=None, height=None, inter=cv2.INTER_AREA):
    dim = None
    (h, w) = image.shape[:2]
    if width is None and height is None:
        return image
    if width is None:
        r = height / float(h)
        dim = (int(w * r), height)
    else:
        r = width / float(w)
        dim = (width, int(h * r))
    resized = cv2.resize(image, dim, interpolation=inter)
    return resized

utils.py

# @time: 2022/1/6 11:06
# @Author: wangshubo
# @File: utilsW.py
# @description: 封装的工具函数
# @author_email: '971490321@qq.com'
import cv2 as cv
import numpy as np


def cvSHow(name, img):
    cv.imshow(name, img)
    cv.waitKey(0)
    cv.destroyAllWindows()

#计算两点距离之和
def CalDistance(pt1, pt2):
    x1, y1, x2, y2 = pt1[0], pt1[1], pt2[0], pt2[1]
    distance = np.sqrt(((y2 - y1) ** 2) + ((x2 - x1) ** 2))
    return distance

# 计算列表中元素之和
def listSum(list):
    total = 0
    ele = 0
    while (ele < len(list)):
        total = total + list[ele]
        ele += 1
    return total
上一篇:VC6.0编写在windows7下以管理员权限运行的程序


下一篇:Git 子模块