Opencv Q&A_10
2022/03/20
AR(增强现实)对特定图片进行特征识别并替换图片或视频帧
具体算法:通过对样本图和测试图的特征计算进行对比(match),当匹配度超过一定的值则判定匹配正确,进行替换
代码
import cv2 as cv import numpy as np mode = 'video' #样本图是否为视频,若不是video则默认图片 cap = cv.VideoCapture(1) video = cv.VideoCapture('D:\work\\automation\Skill\Python\pythonWORK\cv\material\\water_Trim.mp4') img = cv.imread('D:\work\\automation\Skill\Python\pythonWORK\cv\material\\book_big.jpg') img_na = cv.imread('D:\work\\automation\Skill\Python\pythonWORK\cv\material\\nanami.jpg') img = cv.resize(img,(0,0),None,0.4,0.4) h,w,c = img.shape img_na = cv.resize(img_na,(w,h)) orb = cv.ORB_create(nfeatures=2000) #参数为特征点数 kp1,des1 = orb.detectAndCompute(img,None) #计算出样本图片的关键点坐标和特征向量 while True: oringal_flag = 'on' #初始化为on if mode == 'video': #样本图为视频帧 flag,vid = video.read() if vid is not None: img_change = cv.resize(vid, (w, h)) else: img_change = img_na.copy() flag,frame = cap.read() kp2,des2 = orb.detectAndCompute(frame,None) #计算出检测图的关键点坐标和特征向量 if des2 is not None: #当计算出来时 bf = cv.BFMatcher() #二维特征值匹配 matches = bf.knnMatch(des1,des2,k=2) #找出样本图和检测图的最佳匹配,k值为每一个点要找出多少个匹配点,输出query样本下标,train测试下标,distance差距 if len(matches[0]) == 2: #有时匹配点数不满足要求的数量,所以需要进行判断 good = [] for m,n in matches: if m.distance < 0.75 * n.distance: #猜想是误差距离没办法绝对衡量,只能通过最优的两个匹配值进行相对衡量,通过相对衡量反映出绝对衡量的可信度 good.append(m) #加入最优匹配m(所以n就是个工具人) imgFeat = cv.drawMatches(img,kp1,frame,kp2,good,None,flags=2) #画出匹配映射图 if len(good) > 40: srcPts = np.float32([kp1[m.queryIdx].pt for m in good]).reshape(-1,2,1) #通过下标找出特征点坐标集合 dstPts = np.float32([kp2[m.trainIdx].pt for m in good]).reshape(-1,2,1) matrix,mask = cv.findHomography(srcPts,dstPts,cv.RANSAC,5) #根据两个特征点计算出的透视变换矩阵,输入三维数组 if matrix is not None: #有时会得到None的变换矩阵 pts = np.float32([[0,0],[0,h],[w,h],[w,0]]).reshape(-1,1,2) #没搞明白为什么不能改成(-1,2,1) dst = cv.perspectiveTransform(pts,matrix) #由正视图角度转变成实时角度,适合只有一组坐标的时候使用 ### 创建一个选中部分黑色(0,0,0),其余部分白色(255,255,255)的掩罩 mask_black = np.zeros((frame.shape[0],frame.shape[1]),np.uint8) cv.fillPoly(mask_black,[np.int32(dst)],(255,255,255)) mask_inv = cv.bitwise_not(mask_black) mask_inv = cv.cvtColor(mask_inv, cv.COLOR_GRAY2BGR) #单通道转三通道 frame_mask = cv.bitwise_and(frame,mask_inv) #通过通道转换可以不使用掩罩方式的bitwise_and # frame_mask = cv.bitwise_and(frame,frame,mask = mask_inv) #单通道只能使用掩罩方式的bitwise_and img_change = cv.warpPerspective(img_change,matrix,(frame.shape[1],frame.shape[0])) #替换图片透视转换,适合多组坐标的时候使用 img_or = cv.bitwise_or(img_change,frame_mask) oringal_flag = 'off' #关闭原图显示 cv.imshow('img',img_or) if oringal_flag == 'on': #如果为on则原图显示 cv.imshow('img',frame) cv.waitKey(1) # cv.imshow('img2', imgFeat) # cv.imshow('img1',frame) # cv.imshow('img',img) # cv.waitKey(1)
运行效果
图一 被识别物体
图二 识别成功并替换
遇到的问题
Q1:运行过程容易中断报错
A1:读取替换视频帧和计算测试帧特征值时可能会返回None,需要提前进行判断
Q2:findHomography(),perspectiveTransform(),warpPerspective(),getPerspectiveTransform()几个函数的区别
A2:getPerspectiveTransform()和findHomography()两个都是获得透视变换矩阵的函数。getPerspectiveTransform()输入图片四个点(非共线)的坐标集;findHomography()输入图片的两个特征点(3D)
perspectiveTransform()和warpPerspective()两个都是进行透视变换的函数。perspectiveTransform()适合输入一组坐标的时候使用;warpPerspective()适合输入多组坐标的时候使用
Q3:bitwise_and()的两种用法
A3:如下代码知,方式选择由图片间的通道数决定,如通道数不同,需用掩罩的方式
frame_mask = cv.bitwise_and(frame,mask_inv) #通过通道转换可以不使用掩罩方式的bitwise_and frame_mask = cv.bitwise_and(frame,frame,mask = mask_inv) #单通道只能使用掩罩方式的bitwise_and