一、图像透视转换
对该发票图像进行处理,只识别这个发票
1.函数设置
python
import numpy as np
import cv2
def cv_show(name,img):
cv2.imshow(name,img)
cv2.waitKey(0)
将给定的4个点(通常是从图像中检测到的四边形角点)按固定顺序排列,这里为什么不直接用x和y的值对比是因为当图片不是平行于xy的时候可能会出现排序的其他情况,这里使用x和y的相加和相减,适用于不是很严重的扭曲的四边形,而不知服务于这一张图片。
x+y左上是最小的,y-x右上是最小的,x+y右下是最大的,y-x左下是最大的
python
def order_points(pts):
rect = np.zeros((4, 2), dtype = "float32")#用来存储排序之后的坐标位置
#按顺序找到对应坐标0123分别是左上,右上,右下,左下
s = pts.sum(axis=1)#对pts矩阵的每一行进行求和操作,(x+y)
rect[0] = pts[np.argmin(s)]
rect[2] = pts[np.argmax(s)]
diff=np.diff(pts,axis=1)#对pts矩阵的每一行进行求差操作。(y-x)
rect[1] = pts[np.argmin(diff)]
rect[3] = pts[np.argmax(diff)]
return rect
图像透视变换 cv2.getPerspectiveTransform(src,dst[,solveMethod]) MP获得转换之间的关系
src:变换前图像四边形顶点坐标
dst:变换后图像四边形顶点坐标
cv2.warpPerspective(src, MP, dsizel, dst[, flags[, borderNode[, borderValue]]]]) dst
参数说明:
- src:原图
- MP:透视变换矩阵,3行3列
- dsize:输出图像的大小,二元元组(width,height)
python
def four_point_transform(image, pts):
# 获取输入坐标点,把四个点进行排序左上,右上,右下,左下
rect = order_points(pts)
#计算原始四边形的宽和高(也许不是矩形,所以宽高两边不一定相等)
(tl, tr, br, bl) = rect
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
将图像中倾斜、扭曲的四边形区域(如倾斜拍摄的文档、车牌、票据等)转换为正面的矩形视图
python
def resize(image,width=None,height=None,inter=cv2.INTER_AREA):
dim=None
(h,w)=image.shape[:2]
if width is None and height is None:
return image
if width is None:
r=height/float(h)
dim=(int(w*r),height)
else:
r=width/float(w)
dim=(width,int(h*r))
resized=cv2.resize(image,dim,interpolation=inter) #默认为cv2.INTER_AREA,即面积插值,适用于缩放图像。
return resized
python
#读取输入
image=cv2.imread('fapiao.jpg')
# cv_show('image',image)
#图片过大,进行缩小处理
radio=image.shape[0]/500#计算缩小比率
orig=image.copy()
image=resize(orig,height=500)
# cv_show('1',image)
python
#轮廓检测
print('step1:轮廓检测')
gray=cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
edged=cv2.threshold(gray,0,255,cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]#自动寻找阈值二值化
cnts=cv2.findContours(edged.copy(),cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE)[-2]
image_contours=cv2.drawContours(image.copy(),cnts,-1,(0,0,255),1)
# cv_show('image_contours',image_contours)
print('step2:最大轮廓获取')
screencnt=sorted(cnts,key=cv2.contourArea,reverse=True)[0]#获取面积最大的轮廓
print(screencnt.shape)
peri=cv2.arcLength(screencnt,True)#计算轮廓周长
screencnt=cv2.approxPolyDP(screencnt,0.05*peri,True)#轮廓近似
print(screencnt.shape)
image_contour=cv2.drawContours(image.copy(),[screencnt],-1,(0,255,0),2)
# cv2.imshow('image_contour',image_contour)
# cv2.waitKey(0)
cv2.namedWindow( 'xx2',cv2.WINDOW_NORMAL),能让窗口能放大拉小
python
warped = four_point_transform(orig, screencnt.reshape(4, 2) * radio)
cv2.namedWindow( 'xx2',cv2.WINDOW_NORMAL)
# cv2.imshow("xx1", warped3)
cv2.imshow("xx2", warped)
cv2.waitKey(0)
输出结果:

想要更清晰的结果,可以对该图片继续处理,二值化,腐蚀等......,也可以用图片旋转把他旋转正(这里我们需要注意,如果我们想让文字变粗,该图片二值化之后是白底黑字,用的是腐蚀,因为腐蚀是让黑色区域变大,如果我们二值化的时候就把黑白颠倒,使得图片是黑底白字,就要用膨胀,因为膨胀是让白色区域更大)
二、角点检测
角点指图像中局部区域与周围区域有较大灰度变化的点或像素。不仅限于上下有较大变化,左右也有很大变化的时候才是角点。
eg:左上图上下时候有明显改变,但左右平移的时候颜色就没有明显改变的,无角点
左下图,左右有明显改变,但上下没有,所以无角点
右图中1到3有明显改变,1到2也有明显改变,那么1中有角点

cornerHarris(img, blockSize, ksize, k[, dst[, borderType]]) -> dst
- img:输入图像。
- blockSize:角点检测中要考虑的领域大小。
- ksize:Sobel求导中使用的窗口大小。
- k:Harris角点检测方程中的自由参数,取值参数为[0.04,0.06]。
- dst:返回numpy.ndarray对象,大小和src相同,值越大,对应像素点是角的概率越高
python
import cv2
img=cv2.imread(r"D:\project\whitex.jpg")
gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
dst=cv2.cornerHarris(gray,4,3,0.04)#blocksize,ksize,k
#标记检测到的角点
#这里通过对角点响应进行阈值处理,标记出检测到的角点。
#0.05*dst.max()是一个阈值,大于这个值的像素点会被标记为红色。
img[dst>0.01*dst.max()]=[0,255,255]#超过最大值的百分之五就被划为角点
cv2.namedWindow( 'img',cv2.WINDOW_NORMAL)
cv2.imshow('img',img)
cv2.waitKey(0)

三、sift特征检测
检测图像中的关键点
- cv2.SIFT_create()
- cv2.xfeatures2d.SIFT_create()创建一个sift特征的提取对象
- sift.detect(img)在图像中查找关键点
kp:
- kp.pt:关键点的(x,y) 坐标。
- kp.size:关键点的大小(尺度).
- kp.angle:关键点的方向.
- kp.response:关键点的响应值。
- kp.0ctave:关键点所在的金字塔层级.
查找关键点
- drawkeypoints(image, keypoints, outImage, color=None, flags=None)
- image:原始图片 keypoints:从原图中获得的关键点,这也是画图时所用到的数据
- outpytimag:输出图像,可以是原始图片,也可以是None color:颜色设置,通过修改(b,9,r)的值,更改画笔的颜色,b=蓝色,g=绿色,r=红色。
- flags:绘图功能的标识设置,绘制富有信息的关键点。
python
import cv2
import numpy as np
img=cv2.imread("qqqqss.jpg")
gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
sift=cv2.SIFT_create()#sift对象
kp=sift.detect(gray)
gray=cv2.drawKeypoints(img,kp,None,flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
cv2.imshow('gray',gray)
cv2.waitKey(0)
#使用sift.compute()计算关键点描述符,方便后期的特征匹配
kp,des=sift.compute(img,kp)
print(np.array(kp).shape,des.shape)
#输出关键点的形状和描述符的形状。
# np.array(kp).shape表示关键点的数量和属性。
#des.shape表示描述符的数量和属性。

四、指纹识别
python
import cv2
def cv_show(name,img):
cv2.imshow(name,img)
cv2.waitKey(0)
python
def verification(src,model):
sift=cv2.SIFT_create()#创建sift特征提取器
# 检测关键点和计算描述符(特征向量)源图像
kp1,des1=sift.detectAndCompute(src,None)
# 检测关键点和计算描述符(特征向量)模板图像
kp2,des2=sift.detectAndCompute(model,None)
#创建flann匹配器
flann=cv2.FlannBasedMatcher()
#使用k近邻匹配(des1中的每个描述符与des2中的最近链各个描述符进行匹配
matches=flann.knnMatch(des1,des2,k=2)
#distance:匹配的特征点描述符的欧式距离,数值越小也就越说明两个特征点越相近
#queryidx:测试图像的特征点描述符的下标(第几个特征点描述符),同时也是描述对应特征点的下标
#trainidx:样本图像的特征点描述符下标,同时也是描述符对应特征点的下标
#进行比较筛选
ok=[]
for m,n in matches:
#根据lowe比率测试:选择最佳匹配
if m.distance<0.4*n.distance:#值越小越严格
ok.append((m,n))
num=len(ok)#统计通过筛选匹配的数量
if num >=50:#共同点设置标准,多少为成功,是可以调整的,要根据上面数值进行设置
result='认证通过'
else:
result='认证失败'
return result
python
if __name__=='__main__':
src1=cv2.imread('zhiwen1.bmp')
src2=cv2.imread('zhiwen2.bmp')
model = cv2.imread('zhiwen_model.bmp')
cv2.imshow('src1',src1)
cv2.imshow('src2',src2)
cv2.imshow('model',model)
cv2.waitKey(0)
reselt1=verification(src1,model)
reselt2=verification(src2,model)
print('src1的结果为:',reselt1)
print('src2的结果为:',reselt2)

这里能进行识别,但是没有展示出指纹那些点匹配上了,如果要展示哪些点匹配上了我们需要修改函数verification
python
def verification(src,model,show_matchs=False):
sift=cv2.SIFT_create()#创建sift特征提取器
kp1,des1=sift.detectAndCompute(src,None)
kp2,des2=sift.detectAndCompute(model,None)
flann=cv2.FlannBasedMatcher()
matches=flann.knnMatch(des1,des2,k=2)
ok=[]
src_matches=[]
model_matches=[]
for m,n in matches:#m是最佳匹配,n为次家匹配
if m.distance<0.4*n.distance:
ok.append((m,n))
src_matches.append(kp1[m.queryIdx].pt)#测试图像中匹配点的坐标
model_matches.append(kp2[m.trainIdx].pt)#模版图像中的匹配点坐标
num=len(ok)
if num >=50:
result='认证通过'
else:
result='认证失败'
if show_matchs and len(src_matches)>0:#复制图像,避免修改原图
src_with_matches=src.copy()
model_with_matches=model.copy()
for (x,y) in src_matches[:100]:#在测试图像上绘制匹配点
cv2.circle(src_with_matches,(int(x),int(y)),3,(0,0,255),-1)
for (x,y) in model_matches[:100]:#在模版图上绘制匹配点
cv2.circle(model_with_matches,(int(x),int(y)),3,(0,0,255),-1)
cv2.imshow('src_matches',src_with_matches)
cv2.imshow('model_matches',model_with_matches)
cv2.waitKey(0)
return result,num,src_matches,model_matches
最后主函数中修改:
python
reselt1,num1,src1_matches,model1_matches=verification(src1,model,show_matchs=True)
reselt2,num2,src2_matches,model2_matches=verification(src2,model,show_matchs=True)
print('src1的结果为:',reselt1,'匹配点数:{}'.format(num1))
print('src2的结果为:',reselt2,'匹配点数:{}'.format(num2))
