整张答题卡识别原理( 答题卡处理2:倾斜校正)

2023-10-24  本文已影响0人  大龙10

书名:计算机视觉40例从入门到深度学习:OpenCV-Python
作者:李立宗
出版社:电子工业出版社
出版时间:2022-07-01
ISBN:9787121436857


第9章 答题卡识别

9.2 整张答题卡识别原理

9.2.2 答题卡处理

1、问题2:如何对答题卡进行倾斜校正、裁剪掉扫描的边缘


2、透视变换


3、倾斜校正

综上所述,使用透视变换将扫描得到的不规则四边形的答题卡映射为矩形的具体步骤如下。

  需要注意的一个关键问题是,用于构造变换矩阵使用的原始图像的四个顶点和目标图像的四个顶点的位置必须是匹配的。也就是说,要将左上、右上、左下、右下四个顶点按照相同的顺序排列。

  通过轮廓查找,确定轮廓的逼近多边形,找到答题卡(待校正的不规则四边形)的四个顶点。由于并不知道这四个顶点分别是左上、右上、左下、右下四个顶点中的哪个顶点,因此需要在函数内先确定好这四个顶点分别对应左上、右上、左下、右下四个顶点中的哪个顶点。然后将这四个顶点和目标图像的四个顶点,按照一致的排列方式传递给函数getPerspectiveTransform获取变换矩阵。最后根据变换矩阵,使用函数warpPerspective完成倾斜校正。


  根据逼近多边形中各个点的坐标,进一步确定了每一个顶点分别对应左上、右上、左下、右下四个顶点中的哪个顶点。

4、程序

# -*- coding: utf-8 -*-

import cv2
import numpy as np
from scipy.spatial import distance as dist  # 用于计算距离

# 自定义透视函数
# step 1: 参数 pts 是要进行倾斜校正的轮廓的逼近多边形(本例中的答题卡)的四个顶点

def myWarpPerspective(image,pts):    
    #确定四个顶点分别对应左上、右上、右下、左下四个顶点中的哪个顶点
    
    # step 1.1:根据轴坐标值对四个顶点进行排序
    xSorted = pts[np.argsort(pts[:,0]), :]
    
    # step 1.2:将四个顶点划分为左侧两个、右侧两个
    left  = xSorted[:2, :]
    right = xSorted[2:, :]
    
    # step 1.3:在左侧寻找左上顶点、左下顶点
    # 根据 y 轴坐标值排序
    left = left[np.argsort(left[:, 1]),:]

    # 排在前面的是左上角顶点 (tl:top-left)、排在后面的是左下角顶点 (bl:bottom-left)
    (tl,bl) = left
    
    #step 1.4: 根据右侧两个顶点与左上角顶点的距离判断右侧两个顶点的位置
    # 计算右侧两个顶点距离左上角顶点的距离
    D = dist.cdist(tl[np.newaxis], right,"euclidean")[0]
    # 形状大致如下
    # 左上角顶点(t1)            右上角顶点(tr)
    #                 页面中心
    # 左下角顶点(bl)            右下角顶点(br)
    # 右侧两个顶点中,距离左上角顶点远的点是右下角顶点 (br),近的点是右上角顶点(tr)
    # br:bottom-right/tr:top-right
    (br, tr) = right[np.argsort(D)[::-1], :]
    
    # step 1.5:确定 pts 的四个顶点分别属于左上、左下、右上、右下顶点中的哪个
    # src 是根据左上、左下、右上、右下顶点对 pts 的四个顶点进行排序的结果
    src = np.array([tl, tr, br, bl], dtype="float32")

    #========以下5行是测试语句,显示计算的顶点是否正确=========
    srcx = np.array([tl, tr, br, bl], dtype="int32")
    print("看看各个顶点在哪: \n",src)               # 测试语句,查看顶点   
    test=image.copy()                             # 复制 image 图像,处理用
    cv2.polylines(test,[srcx],True,(255,0,0),8)  # 在 test 内绘制得到的点
    cv2.imshow("Test",test)  #显示绘制线条结果

    # ======= Step2:根据 pts的四个顶点,计算校正后图像的宽度和高度==    
    # 校正后图像的大小计算比较随意,根据需要选用合适值即可
    # 这里选用较长的宽度和高度作为最终宽度和高度
    # 计算方式:由于图像是倾斜的,所以将算得的X轴方向、Y轴方向的差值的平方根作为实际长度

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

    # 根据 (左上,左下)和 (石上,石下)的最大值,获取高度
    heightA = np.sqrt(((tr[0] - br[0]) ** 2) + ((tr[1] - br[1]) ** 2))
    heightB = np.sqrt(((tl[0] - bl[0]) ** 2) + ((tl[1] - bl[1]) ** 2))
    maxHeight = max(int (heightA), int(heightB))

    #根据宽度、高度,构造新图像 dst 对应的四个顶点
    dst = np.array([[0,0], [maxWidth - 1,0], [maxWidth - 1,maxHeight - 1], [0,maxHeight - 1]], dtype="float32")
    # print("看看目标如何: n,dst)  # 测试语句
    # 构造从 src到dst的变换矩阵
    M = cv2.getPerspectiveTransform(src, dst)
    # 完成从 src到dst的透视变换
    warped = cv2.warpPerspective(image, M, (maxWidth,maxHeight)) # 返回透视变换的结果
    return warped

# ======主程序======
# ====读取原始图像====
img =  cv2.imread('d:\\OpenCVpic\\TestPaper.jpg')  
cv2.imshow('img', img)

gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) 
# 高斯滤波
gaussian = cv2.GaussianBlur(gray,(5,5), 0)
edged =cv2.Canny(gaussian, 50, 200)
cts, hierarchy = cv2.findContours(edged.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)  
# cv2.drawContours(img,cts,-1,(0,0,255),3)

list=sorted(cts,key=cv2.contourArea,reverse=True)
print("寻找轮廓的个数:",len(cts))
# cv2.imshow("draw contours",img)
rightSum = 0
# 可能只能找到一个轮廓,该轮廓就是答题卡的轮廓
# 由于噪声等影响,也可能找到很多轮廓
# 使用 for 循环,遍历每一个轮廓,找到答题卡的轮廓
# 对答题卡进行倾斜校正处理

for c in list:
    peri=0.01*cv2.arcLength(c,True)
    approx=cv2.approxPolyDP(c,peri,True)
    print("顶点个数:",len(approx))
    # 四个顶点的轮廓是矩形(或者是扫描造成的矩形失真为梯形)
    if len(approx)==4:
        # 对外轮廓进行倾斜校正,将其构造成一个矩形
        # 处理后,只保留答题卡部分,答题卡外面的边界被删除#原始图像的倾斜校正用于后续标注
        # print(approx)
        print(approx.reshape(4,2))
        paper = myWarpPerspective(img,approx.reshape(4,2))

cv2.imshow("result",paper)  # 打印识别结果
cv2.waitKey()
cv2.destroyAllWindows()

运行结果 输出数据

5、分析

从结果可以看到,该程序在对左侧图像进行了倾斜校正、裁边后得到了右侧的处理结果。

在实际对答题卡进行识别时,要得到两个倾斜校正结果,分别如下:

上一篇 下一篇

猜你喜欢

热点阅读