参考代码:https://my.oschina.net/DDigimon/blog/1236344
https://www.pyimagesearch.com/2014/09/01/build-kick-ass-mobile-document-scanner-just-5-minutes/
import cv2
import matplotlib.pyplot as plt
import imutils
from imutils.perspective import four_point_transform
#读入图片
#print(__doc__)
image = cv2.imread('E:/picture/IMG_9375.JPG')
plt.figure(figsize = (20,20))
#plt.subplot(221)
#plt.imshow(image)
#plt.axis("off")#去除坐标轴
#plt.show()
#转换为灰度图像
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
#plt.subplot(222)
#plt.imshow(gray)
#plt.axis("off")#去除坐标轴
#plt.show()
#高斯滤波
blurred = cv2.GaussianBlur(gray, (3, 3), 0)
#自适应二值化方法
blurred=cv2.adaptiveThreshold(blurred,255,cv2.ADAPTIVE_THRESH_MEAN_C,cv2.THRESH_BINARY,51,2)
#plt.subplot(223)
#plt.imshow(blurred)
#plt.axis("off")#去除坐标轴
#plt.show()
'''
adaptiveThreshold函数:第一个参数src指原图像,原图像应该是灰度图。
第二个参数x指当像素值高于(有时是小于)阈值时应该被赋予的新的像素值
第三个参数adaptive_method 指: CV_ADAPTIVE_THRESH_MEAN_C 或 CV_ADAPTIVE_THRESH_GAUSSIAN_C
第四个参数threshold_type 指取阈值类型:必须是下者之一
• CV_THRESH_BINARY,
• CV_THRESH_BINARY_INV
第五个参数 block_size 指用来计算阈值的象素邻域大小: 3, 5, 7, ...
第六个参数param1 指与方法有关的参数。对方法CV_ADAPTIVE_THRESH_MEAN_C 和 CV_ADAPTIVE_THRESH_GAUSSIAN_C, 它是一个从均值或加权均值提取的常数, 尽管它可以是负数。
'''
#这一步可有可无,主要是增加一圈白框,以免刚好卷子边框压线后期边缘检测无果。好的样本图就不用考虑这种问题
blurred=cv2.copyMakeBorder(blurred,50,50,50,50,cv2.BORDER_CONSTANT,value=(255,255,255))
#plt.subplot(224)
plt.imshow(blurred)
plt.show()
edged = cv2.Canny(blurred, 10, 100)
# 从边缘图中寻找轮廓,然后初始化答题卡对应的轮廓
plt.figure(figsize = (20,20))
plt.subplot(221)
plt.imshow(edged)
plt.show()
'''
findContours
image -- 要查找轮廓的原图像
mode -- 轮廓的检索模式,它有四种模式:
cv2.RETR_EXTERNAL 表示只检测外轮廓
cv2.RETR_LIST 检测的轮廓不建立等级关系
cv2.RETR_CCOMP 建立两个等级的轮廓,上面的一层为外边界,里面的一层为内孔的边界信息。如果内孔内还有一个连通物体,
这个物体的边界也在顶层。
cv2.RETR_TREE 建立一个等级树结构的轮廓。
method -- 轮廓的近似办法:
cv2.CHAIN_APPROX_NONE 存储所有的轮廓点,相邻的两个点的像素位置差不超过1,即max (abs (x1 - x2), abs(y2 - y1) == 1
cv2.CHAIN_APPROX_SIMPLE压缩水平方向,垂直方向,对角线方向的元素,只保留该方向的终点坐标,例如一个矩形轮廓只需
4个点来保存轮廓信息
cv2.CHAIN_APPROX_TC89_L1,CV_CHAIN_APPROX_TC89_KCOS使用teh-Chinl chain 近似算法
'''
cnts = cv2.findContours(edged, cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if imutils.is_cv2() else cnts[1]
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
newimage=image.copy()
for i in docCnt:
#circle函数为在图像上作图,新建了一个图像用来演示四角选取
cv2.circle(newimage, (i[0][0],i[0][1]), 50, (255, 0, 0), -1)
print(docCnt)
paper = four_point_transform(image, docCnt.reshape(4, 2))
warped = four_point_transform(gray, docCnt.reshape(4, 2))
plt.figure(figsize = (20,20))
plt.imshow(warped)
plt.show()