基于OpenCV4.x + Python3.7的文件扫描件切边实践
之前用Matlab做图像较多,最近准备学习用opencv和python进行图像处理,并就网上的实际案例进行了练手,其中的一篇扫描件切边案例是:opencv之案例实战-扫描件切边。仅当作学习,不喜勿喷!
本文链接:基于OpenCV4.x + Python3.7的文件扫描件切边实践
测试图像两种:(1)边缘整齐+倾斜;(2)边缘添加干扰+倾斜。具体图像如下所示。
1.具体思路
(1)边缘检测后进行孔洞填充,得到初始文件区域(包含其他连通域);
(2)取最大的连通域作为文件区域,此时其他连通域已被过滤去除;
(3)霍夫直线检测,以kmeans方法挑选出4类直线;
(4)4条筛选出的直线求交点,并以此对原图做仿射变换,得到校正后的图像;
(5)用上面的4个交点求仿射变换后的4个新点坐标,并以此切边获得最终结果图像
2.实现过程
2.1 边缘检测+孔洞填充
采用canny边缘检测,该算子以及用法在此不用详述;孔洞填充,opencv和python好像还没有可以直接用的函数(可能我还没发现,可以补充),孔洞填充函数如下,其中用到了cv.floodFill自带函数。(fillimg()是借鉴别人的,链接不知道了,下次碰见附上链接):
import cv2 as cv
import numpy as npdef fillimg(img):img1 = img.copy()h,w = img.shape[:2]mask = np.zeros((h+2,w+2),np.uint8)cv.floodFill(img1,mask,(0,0),255)img2 = cv.bitwise_not(img1) #取反out = img|img2 # 或操作return out
该过程实现如下:
gray = cv.cvtColor(img,cv.COLOR_BGR2GRAY)
edge = cv.Canny(gray,th,2*th) #canny边缘检测ele = cv.getStructuringElement(cv.MORPH_RECT,(3,3))
bw = cv.morphologyEx(edge,cv.MORPH_DILATE,ele) #形态学处理:膨胀
bw = fillimg(bw) #孔洞填充
结果见下:
左边:边缘不整齐; 右边:边缘整齐
2.2 最大连通域获取
最大连通域,首先查找轮廓,然后遍历轮廓面积,得到最大面积区域的联通域进行填充,便是文件区域,其中用到的函数:cv.findContours,cv.contourArea() ,以及cv.fillConvexPoly()。
def maxAreaContour_ROI(bw):out = np.zeros_like(bw,np.uint8)contour,her = cv.findContours(bw,cv.RETR_EXTERNAL,cv.CHAIN_APPROX_SIMPLE) #找轮廓area=[]for i in range(len(contour)):area.append(cv.contourArea(contour[i])) #找最大面积轮廓max_ind = np.argmax(area)angl =( cv.minAreaRect(contour[max_ind])[2]) #z最小矩形分析得到偏传角度,后续有用处的cv.fillConvexPoly(out,contour[max_ind],255) #区域内填充白色return out,angl
处理后的结果如下:
左边:边缘不整齐; 右边:边缘整齐
2.3 霍夫直线检测求交点
霍夫直线求出的直线有很多条,我们需要进行分类,通过kmeans分成4类点。其中用到的函数有:cv.HoughLinesP(),cv.kmeans() ,
注意kmeans输入数据是N行2列。
实现过程如下:
def getLines(bw,N,th,lineLength,angle):h,w = bw.shape[:2]edge = cv.Canny(bw,th,2*th)lins = cv.HoughLinesP(edge,1,np.pi/180,10,minLineLength =lineLength,maxLineGap=1)lin =lins[:,0,:] #注意数据的顺序criteria = (cv.TERM_CRITERIA_EPS + cv.TERM_CRITERIA_MAX_ITER, 10, 1.0)#drawLine(img,lin)flags = cv.KMEANS_RANDOM_CENTERStemp = (lin[:,:2] - lin[:,2:-1]).astype(np.float32) #将4维转为2维,作为后续输入compactness,labels,centers = cv.kmeans(temp,4,None,criteria,10,flags) # Kmeans聚类points =[]for j in range(N):points.append(np.mean(lin[np.where(labels==j)[0],:],axis = 0))#每类直线求均值,4行4列 points = np.array(points)center = ((points[:,:2] + points[:,2:-1])/2).astype(np.float32) # 直线中点,4行2列points_up=[] #上points_down=[] #下points_left=[] #左points_right=[] #右for i in range(N):t =points[i]d = (np.arctan2((t[3]-t[1]),(t[2]-t[0]))/np.pi*180)d_angle = abs(d-angle)# 通过最小矩形求得的转转角度做参考,偏传20度内的是上下边缘,偏传70度内的是左右边缘# 并看中点横纵坐标与图像行列关系,具体确定上下左右边缘点(此方法可能不可靠,目前表现OK)if d_angle <20 and center[i][1]>h/2 :points_up.append([t[0],t[1]])points_up.append([t[2],t[3]])elif d_angle <20 and center[i][1] <h/2:points_down.append([t[0],t[1]])points_down.append([t[2],t[3]])elif d_angle >70 and center[i][0] <w/2:points_left.append([t[0],t[1]])points_left.append([t[2],t[3]])else:points_right.append([t[0],t[1]])points_right.append([t[2],t[3]])return points_up,points_down,points_left,points_right
2.4 拟合直线求交点,求仿射变换矩阵
上面已经得到了四类边缘点,此时只需要拟合直线再求交点;以四个交点的中心坐标和一条上或下边缘的角度作为仿射变换参数矩阵的中心点和旋转角度。其中,用到的函数有:cv.fitLine(),cv.getRotationMatrix2D()。
实现过程如下:
def getCrossPoint(points1,points2): #求直线交点points1 = np.array(points1)points2 = np.array(points2)out1 = cv.fitLine(points1,cv.DIST_L2,0,0.1,0.1) #拟合直线out2 = cv.fitLine(points2,cv.DIST_L2,0,0.1,0.1)k1 = out1[1]/out1[0] # 通过直线(k,b)求直线交点,注意k=0,以及k为无穷时候情况k2 = out2[1]/out2[0] # 本案例中未对k值进行讨论,注意,注意,注意 x3 =((out2[3] - k2*out2[2])-(out1[3] - k1*out1[2]))/(k1-k2)y3 = k1*(x3-out1[2])+out1[3]return x3,y3u_lx,u_ly = getCrossPoint(u,l) #左上点
u_rx,u_ry = getCrossPoint(u,r) #右上点
d_lx,d_ly = getCrossPoint(d,l) #左下点
d_rx,d_ry = getCrossPoint(d,r) #右下点p4 =[[u_lx,u_ly],[u_rx,u_ry],[d_lx,d_ly],[d_rx,d_ry ]] # 格式转换center0 = np.mean(p4,axis=0) # 旋转中心
angle0 = np.arctan2((u_ly-u_ry),(u_lx-u_rx))/np.pi*180-180 # 旋转角度rot = cv.getRotationMatrix2D((center0[0],center0[1]),angle0,1.0) # 变换矩阵
实现结果如下:
左边:边缘不整齐; 右边:边缘整齐
2.5 四个交点仿射变换
只需要将上面的四个交点按照仿射变换矩阵进行变换,会得到4个新的图像坐标,然后在仿射变换后的图像中裁剪出文件区域即可。这里我将4个点坐标用cv.warpAffine() 变换,企图得到新的坐标,可是这样做的结果坐标是错的,可能是我还没有弄清楚。这里自己定义了变换函数:Rote()。
该变换矩阵是:
def Rote(points,rot):out=[]for i in range(len(points)):x = rot[0][0]*points[i][0]+rot[0][1]*points[i][1]+rot[0][2] # xc =a*x+b*y+cy = rot[1][0]*points[i][0]+rot[1][1]*points[i][1]+rot[1][2]out.append([x,y])return out
Rote函数变换结果如下:
(1)边缘不整齐图片从倾斜状态下的4点(A)经过仿射变换后的新4点(B):
A ——> B
对原图进行仿射变换的结果如下:
outpoint = np.array(Rote(np.array(p4)[:,:,0],rot)).astype(np.int) #Rote函数变换dst = cv.warpAffine(img,rot,img.shape[:2],borderValue=(255,255,255)) # 仿射变换
2.6 切边
直接根据B矩阵的4点坐标进行切边,形如img[y0:y1,x0:x1]操作,实现如下:
min_x = np.min(outpoint[:,0]) # 最大最小横纵轴,矩形框范围
min_y = np.min(outpoint[:,1])max_x = np.max(outpoint[:,0])
max_y = np.max(outpoint[:,1])OUT = dst[min_y:max_y,min_x:max_x] #最终结果
实现结果如下:
3 结论
终于写完了,从实现到编辑为文档,好累了!我在边缘上加了一些橙色形成非整齐的边缘,以此与整齐的边缘图形对比,其结果显示采用特定的方法可以很好的做到非整齐边缘文件的检测,但还是存在一些边缘误差(上面B矩阵中的X轴坐标109和112,注意结果图中非整齐边缘的蓝色边不是切边后的边缘,实际切边是x=min(109,112))。若有不正确或者好注意的地方,希望进行交流。完整的程序代码可以自行编写,基本上重要的都已经说到。
基于OpenCV4.x + Python3.7的文件扫描件切边实践相关推荐
- 文件扫描件怎么弄?分享三个实用的方法
许多组织需要保存大量的纸质文件,例如合同.发票.报告等.通过扫描这些文件并将其转换为数字格式,可以将文档存储在计算机上,从而节省空间和时间,减少管理难度.此外,数字化的文档还可以更方便地进行备份.归档 ...
- Python3:基于Opencv4.2和wxPython4可视化界面的人脸考勤系统
前言 主题:基于Opencv4.2的人脸考勤系统 功能模块 采集人脸:收集需要录入的学生信息,开启摄像头收集截取对应的人脸图片: 训练人脸数据模型:根据收集和整合好的人脸图片数据训练成人脸数据模型: ...
- OpenCV基础(基于Opencv4.4+VS2019)
OpenCV基础(基于Opencv4.4+VS2019) 1.OpenCV介绍 OpenCV是计算机视觉开源库,主要算法涉及图像处理和机器学习相关方法. 是Intel公司贡献出来的,俄罗斯工程师贡献大 ...
- web目录文件扫描工具 dirmap 简介
知名的web目录文件扫描工具有很多,如:御剑1.5.DirBuster.Dirsearch.cansina. 其他开源的各种轮子,更是数不胜数. 这次我们不造轮子,我们需要造的是一辆车!open so ...
- web目录字典_Dirmap:一款高级Web目录文件扫描工具
前言 本人是一名立志安全开发的大学生,有一年安全测试经验,有时在刷src的时候,需要检查所有target的web业务系统是否泄露敏感目录.文件,工作量十分庞大,于是Dirmap诞生了~ 知名的web目 ...
- 基于macos M1 python3.8的tensorflow安装(简单方便几步完成)
基于macos M1 python3.8的tensorflow安装: 基于macos M1 ,ios12,anaconda3,python3.8 问题描述: 之前安装tensorflow走了tm的好多 ...
- centos7安装python3.7.4_基于centos7 安装python3.6.4出错的解决方法
基于centos7 安装python3.6.4出错的解决方法 错误:zipimport.ZipImportError: can't decompress data; zlib not availabl ...
- python实现文字识别软件_文字识别(OCR)CRNN(基于pytorch、python3) 实现不定长中文字符识别...
文字识别(OCR)CRNN(基于pytorch.python3) 实现不定长中文字符识别 发布时间:2018-09-26 19:40, 浏览次数:1265 , 标签: OCR CRNN pytorch ...
- 文件扫描-TWAIN,WIA,ISIS,SANE
文档扫描功能是软件开发者建立一个网站,内容管理系统,或办公自动化系统的重要组成部分. 市场上有几种不同的扫描驱动程序: TWAIN WIA ISIS SANE 当然,对于什么是最佳解决方案,您可能会感 ...
最新文章
- 如何写网站的robots.txt和meta name robots的配置
- java webservice接口开发_php开发webservice服务端接口(wsdl)
- 仓鼠体重年龄对照表_各年龄段血糖,血压,血脂,尿酸对照表,内容太值!
- Activiti邮件任务
- 前端开发浏览器兼容问题
- 字符流与字节流转换输出
- 【Python】Python处理图像五个有趣场景,很实用!
- C++:函数指针,回调函数
- 验证GridControl Gridview 单元格。
- Qt 串口类QSerialPort 学习笔记
- MATLAB入门教程之MATLAB的基本知识
- 洛克菲勒:一部西方石油工业的传奇史
- 23种设计模式——适配器模式
- 超越QQ Mail文件中转站---大文件上传设计思路和实践 原创 王泽宾
- md5加解密工具 java_java中常用工具类之字符串操作类和MD5加密解密类
- instagram下载的工具instaloader
- 自组织特征图(SOFM)详解
- 数字电子钟—VHDL 设计
- 【软件定义汽车】【场景篇】智能座舱
- 宇视科技2015应届生招聘笔试题