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