当前位置:网站首页>opencv学习笔记八--答题卡识别
opencv学习笔记八--答题卡识别
2022-07-06 07:30:00 【Cloudy_to_sunny】
opencv学习笔记八--答题卡识别
导入工具包
#导入工具包
import numpy as np
import argparse
import imutils
import cv2
import matplotlib.pyplot as plt#Matplotlib是RGB
# 正确答案
ANSWER_KEY = {
0: 1, 1: 4, 2: 0, 3: 3, 4: 1}
定义函数
def order_points(pts):
# 一共4个坐标点
rect = np.zeros((4, 2), dtype = "float32")
# 按顺序找到对应坐标0123分别是 左上,右上,右下,左下
# 计算左上,右下
s = pts.sum(axis = 1)
rect[0] = pts[np.argmin(s)]
rect[2] = pts[np.argmax(s)]
# 计算右上和左下
diff = np.diff(pts, axis = 1)
rect[1] = pts[np.argmin(diff)]
rect[3] = pts[np.argmax(diff)]
return rect
def four_point_transform(image, pts):
# 获取输入坐标点
rect = order_points(pts)
(tl, tr, br, bl) = rect
# 计算输入的w和h值
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 = np.array([
[0, 0],
[maxWidth - 1, 0],
[maxWidth - 1, maxHeight - 1],
[0, maxHeight - 1]], dtype = "float32")
# 计算变换矩阵
M = cv2.getPerspectiveTransform(rect, dst)
warped = cv2.warpPerspective(image, M, (maxWidth, maxHeight))
# 返回变换后结果
return warped
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]
(cnts, boundingBoxes) = zip(*sorted(zip(cnts, boundingBoxes),
key=lambda b: b[1][i], reverse=reverse))
return cnts, boundingBoxes
#显示函数
def cv_show(name,img):
b,g,r = cv2.split(img)
img_rgb = cv2.merge((r,g,b))
plt.imshow(img_rgb)
plt.show()
def cv_show1(name,img):
plt.imshow(img)
plt.show()
cv2.imshow(name,img)
cv2.waitKey()
cv2.destroyAllWindows()
扫描
# 预处理
image = cv2.imread("./images/test_01.png")
contours_img = image.copy()
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blurred = cv2.GaussianBlur(gray, (5, 5), 0) #高斯滤波
cv_show1('blurred',blurred)
edged = cv2.Canny(blurred, 75, 200) #边缘检测
cv_show1('edged',edged)
# 轮廓检测
cnts = cv2.findContours(edged.copy(), cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE)[1]
cv2.drawContours(contours_img,cnts,-1,(0,0,255),3)
cv_show('contours_img',contours_img)
docCnt = None
# 确保检测到了
if len(cnts) > 0:
# 根据轮廓大小进行排序
cnts = sorted(cnts, key=cv2.contourArea, reverse=True)
# 遍历每一个轮廓
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))
cv_show1('warped',warped)
自适应阈值处理
# Otsu's 阈值处理
thresh = cv2.threshold(warped, 0, 255,
cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU)[1]
thresh_Contours = thresh.copy()
cv_show1('thresh_Contours',thresh_Contours)
检测每一个选项的轮廓
# 找到每一个圆圈轮廓
cnts = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE)[1]
cv2.drawContours(thresh_Contours,cnts,-1,(0,0,255),3) #因为是二值化的图像,所以只要不是255,255,255都会变成黑色
print(len(cnts))
cv_show1('thresh_Contours',thresh_Contours)
questionCnts = []
82
# 遍历
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
# 每排有5个选项
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表示填充
# 通过计算非零点数量来算是否选择这个答案
mask = cv2.bitwise_and(thresh, thresh, mask=mask)
total = cv2.countNonZero(mask)
cv_show1('mask',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)
cv_show("Original", image)
cv_show1("Exam", warped)
[INFO] score: 80.00%
参考
边栏推荐
- Bugku CTF daily question: do you want seeds? Blackmailed
- Sharing of source code anti disclosure scheme under burning scenario
- When the Jericho development board is powered on, you can open the NRF app with your mobile phone [article]
- The way to learn go (II) basic types, variables and constants
- 杰理之普通透传测试---做数传搭配 APP 通信【篇】
- Sélectionnez toutes les lignes avec un symbole dans Word et changez - les en titre
- Set picture annotation in markdown
- Memory error during variable parameter overload
- ORACLE列转行--某字段按指定分隔符转多行
- 可变参数重载时的内存错误
猜你喜欢
Seriously recommend several machine learning official account
TypeScript接口与泛型的使用
The ECU of 21 Audi q5l 45tfsi brushes is upgraded to master special adjustment, and the horsepower is safely and stably increased to 305 horsepower
Ble of Jerry [chapter]
Comparison of usage scenarios and implementations of extensions, equal, and like in TS type Gymnastics
Crawling exercise: Notice of crawling Henan Agricultural University
Markdown 中设置图片图注
If Jerry needs to send a large package, he needs to modify the MTU on the mobile terminal [article]
Detailed explanation | detailed explanation of internal mechanism of industrial robot
Set picture annotation in markdown
随机推荐
Three no resumes in the software testing industry. What does the enterprise use to recruit you? Shichendahai's resume
[window] when the Microsoft Store is deleted locally, how to reinstall it in three steps
Redis builds clusters
TypeScript 函数定义
Word delete the contents in brackets
烧录场景下的源代码防泄密方案分享
Résumé de la structure du modèle synthétisable
Project GFS data download
leecode-C语言实现-15. 三数之和------思路待改进版
超级浏览器是指纹浏览器吗?怎样选择一款好的超级浏览器?
If Jerry's Bluetooth device wants to send data to the mobile phone, the mobile phone needs to open the notify channel first [article]
navicat如何导入MySQL脚本
杰理之AD 系列 MIDI 功能说明【篇】
C # display the list control, select the file to obtain the file path and filter the file extension, and RichTextBox displays the data
Ble of Jerry [chapter]
Mise en œuvre du langage leecode - C - 15. Somme des trois chiffres - - - - - idées à améliorer
Typescript void base type
OpenJudge NOI 2.1 1749:数字方格
TypeScript接口与泛型的使用
2022年Instagram运营小技巧简单讲解