公司的惠普M126nw一体机,因为是黑白一体机,所以不能通过扫描-打印-再扫描的方式获取彩色扫描图片,虽然去网上可以找到相类似的工具,基本都是在线的,即需要上传自己的证件,这其实有信息泄漏的风险.另外类似的软件都有一个使用次数限制.比如一个月用10次.超过之后就需要缴费等限制.
故而我通过搜索,学习.码出了一个python脚本.实现了将2张证件扫描图片,合并到1张图片的功能.这样就不需要手动剪切复制编辑图片了(以前就是这么干的...).
先看最终效果图
可以看到自动对齐了正面,反面在一页A4纸上,且图片中基本无其他噪点(因为是在一个白纸上的基础上,将部分区域替换成了扫描到的证件)
环境如下:
OS:ubuntu 18.04
python:3.7.9
opencv:3.4.2
numpy:1.19.2
可以看到一体机扫描出来的图片如下图,一次只能扫描一面(我知道有证件扫描模式,但是最终只能得到黑白扫描件,故而不谈).
下图是扫描身份证正面的扫描结果,我特意没有把身份证与扫描边缘对齐,因为这个才是最常见的情况.像素大小: 1700*2338
可以看到扫描出的图片中有些许噪点.这个在处理时需要过滤掉.
下图是身份证背面的扫描结果.
直接上代码
import numpy as np #导入numpy
import cv2 #导入cv2库
主体部分
# 声明全白画布
canvas = np.ones((2338, 1700, 3),np.uint8)*255
y,x = canvas.shape[:2] #注意是y,x.不是x,y
#定义函数处理图片,pic为扫描图片文件名
def crop(pic):
img = cv2.imread(pic)
imgGray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 转换为gray灰度图
imgBlur = cv2.GaussianBlur(imgGray,(5,5),1) #高斯模糊
imgCanny = cv2.Canny(imgBlur,30,30) #边缘检测
kernel = np.ones((5,5))
imgDial = cv2.dilate(imgCanny,kernel,iterations=2) #膨胀处理,迭代2次
imgThres = cv2.erode(imgDial,kernel,iterations=1) #腐蚀处理,迭代1次
contours, hier = cv2.findContours(imgThres, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[-2:] # 寻找轮廓
for cidx,cnt in enumerate(contours): #遍历轮廓
area = cv2.contourArea(cnt) #获取轮廓的面积
if area>500: #面积大于500才处理
minAreaRect = cv2.minAreaRect(cnt) #获取最小外接矩形
((cx, cy), (w, h), theta) = minAreaRect #中心点坐标,长宽,旋转角度[-90,0),当矩形水平或竖直时均返回-90
# 转换为整数点集坐标
rectCnt = np.int64(cv2.boxPoints(minAreaRect))
width,height=int(w),int(h) #转换宽,高为整数类型
src_pts = rectCnt.astype("float32") #转换数字类型
# dst_pts 矩形顶点顺序分别为左下,左上,右上,右下
if w > h:
dst_pts = np.array([[0, height-1],
[0, 0],
[width-1, 0],
[width-1, height-1]], dtype="float32")
# the perspective transformation matrix
M = cv2.getPerspectiveTransform(src_pts, dst_pts)
# directly warp the rotated rectangle to get the straightened rectangle
croped = cv2.warpPerspective(img, M, (width, height))
elif w < h:
dst_pts = np.array([[height-1, width-1],
[0, width -1],
[0, 0],
[height-1, 0]], dtype="float32")
# the perspective transformation matrix
M = cv2.getPerspectiveTransform(src_pts, dst_pts)
# directly warp the rotated rectangle to get the straightened rectangle
croped = cv2.warpPerspective(img, M, (height,width))
return croped
def warp(imread,bias=0): # 定义覆盖函数
roiHeiht,roiWidth = imread.shape[:2] #roi的高,宽
warpY,warpX=int((x - roiWidth)//2),int(0.1*y)+bias #crop图片插入的元点
canvas[warpX:warpX + roiHeiht,warpY:warpY + roiWidth] = imread #直接替换制定区域为imread图片
然后是具体的操作部分
cropFront = crop('front.jpg') #获取front的crop
cropBack = crop('back.jpg') #获取back的crop
warp(cropFront) #替换front_roi
warp(cropBack,bias=int(0.8*y-cropBack.shape[0])) #替换back_roi
cv2.imwrite('Final.jpg',canvas)
以上就是全部代码,用户只需要修改 上面的 front.jpg为正面扫描件,back.jpg为背面扫描件.Final.jpg为最后合成的文件名即可.后期看情况做个GUI版.预计包含opencv,numpy的打包文件可能比较大.