和我一起入门OpenCV with Python Day7

二、语法介绍

(三)视频处理

此处通过实战训练视频防抖学习
原文指路:
https://mp.weixin.qq.com/s/MUNeV2Uj5Y0vFpOD4dgP5A

1. 设置输入和输出视频

import numpy as np
import cv2

cap=cv2.VideoCapture('C:/Users/LEGION/Videos/dou.mp4')

n_frames=int(cap.get(cv2.CAP_PROP_FRAME_COUNT))

w=int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
h=int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))

fourcc=cv2.VideoWriter_fourcc(*'MJPG')

out=cv2.VideoWriter('C:/Users/LEGION/Videos/dou.mp4',fourcc,fps,(w,h))

2. 读取第一帧并转换为灰度图


_,prev=cap.read()

prev_gray=cv2.cvtColor(prev,cv2.COLOR_BGR2GRAY)

3. 寻找帧之间的移动

3.1 可用于跟踪帧的优质特征

光滑的区域不利于跟踪,而有很多角的纹理区域则比较好。幸运的是,OpenCV有一个快速的特征检测器(goodFeaturesToTrack),可以检测最适合跟踪的特性。

corners=cv.goodFeaturesToTrack(image, maxCorners, qualityLevel, minDistance[, corners[, mask[, blockSize[, useHarrisDetector[, k]]]]])
corners=cv.goodFeaturesToTrack(image, maxCorners, qualityLevel, minDistance, mask, blockSize, gradientSize[, corners[, useHarrisDetector[, k]]])

  • image:输入灰度图像,float32类型;
  • maxCorners:返回角点的最大数目,值为0表示没有设置最大值限制,返回所有检测到的角点;
  • qualityLevel:质量系数(小于1.0的正数,一般在0.01-0.1之间),表示可接受角点的最低质量水平。该系数乘以最好的角点分数(也就是上面较小的那个特征值),作为可接受的最小分数;例如,如果最好的角点分数值为1500且质量系数为0.01,那么所有质量分数小于15的角都将被忽略;
  • minDistance:角之间最小欧式距离,忽略小于此距离的点;
  • corners:输出角点坐标;
  • mask:可选的感兴趣区域,指定想要检测角点的区域;
  • blockSize:默认为3,角点检测的邻域大小(窗口尺寸);
  • useHarrisDetector:用于指定角点检测的方法,如果是true则使用Harris角点检测,false则使用Shi Tomasi算法。默认为False;
  • k:默认为0.04,Harris角点检测时使用。
 设定好这些参数,函数就能在图像上找到角点。所有低于质量水平的角点都会被忽略,然后再把合格角点按角点质量进行降序排列。

 然后保留质量最高的一个角点,将它附近(最小距离之内)的角点都删掉(类似于非极大值抑制),按这样的方式最后得到 N 个最佳角点。
3.2 Lucas-Kanade光流

一旦我们在前一帧中找到好的特征,我们就可以使用Lucas-Kanade光流算法在下一帧中跟踪它们。
它是利用OpenCV中的 calcOpticalFlowPyrLK函数实现的。LK代表Lucas-Kanade,而Pyr代表金字塔。
由于各种原因,calcOpticalFlowPyrLK可能 无法计算出所有点的运动。例如,当前帧的特征点可能会被下一帧的另一个对象遮挡。幸运的是,calcOpticalFlowPyrLK中的 状态标志可以用来 过滤掉这些值。
3.3 估计运动
已知:特征在当前帧中的位置+特征在前一帧中的位置。
所以我们可以使用这两组点来找到映射前一个坐标系到当前坐标系的 刚性(欧几里德) 变换。这是使用函数 estimateRigidTransform完成的。
一旦我们估计了运动,我们可以把它分解成 x和y的平移和旋转(角度)。我们将这些值存储在一个 数组中,这样就可以平稳地更改它们。

from tkinter import Frame
import numpy as np
import cv2

cap=cv2.VideoCapture('C:/Users/LEGION/Videos/dou.mp4')

n_frames=int(cap.get(cv2.CAP_PROP_FRAME_COUNT))

w=int(cap.get(cv2.CAP_PROP_FRAMW_WIDTH))
h=int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))

fourcc=cv2.VideoWriter_fourcc(*'MJPG')

_,prev=cap.read()

prev_gray=cv2.cvtColor(prev,cv2.COLOR_BGR2GRAY)

transforms=np.zeros(((n_frames-1),3),np.float32)
for i in range(n_frames-2):

    prev_pts=cv2.goodFeaturesToTrack(prev_gray,maxCorners=200,qualityLevel=0.01,minDistance=30,blockSize=3)

while ret:
    ret,curr=cv2.read()

curr_gray=cv2.cvtColor(curr,cv2.COLOR_BGR2GRAY)

curr_pts,status,err=cv2.calcOpticalFlowPyrLK(prev_gray,curr_gray,prev_pts,None)

assert prev_pts.shape==curr_pts.shape

idx=np.where(status==1)[0]
prev_pts=prev_pts[idx]
curr_pts=curr_pts[idx]

m=cv2.estimateRigidTransform(prev_pts,curr_pts,fullAffine=False)

dx=m[0,2]
dy=m[1,2]

da=np.arctan2(m[1,0],m[0,0])

transforms[i]=[dx,dy,da]

prev_gray=curr_gray

print("Frame:"+str(i)+"/"+str(n_frames)+"- Tracked points :"+str(len(prev_pts)))

4. 计算帧之间的平滑运动

4.1 轨迹计算

增加运动之间的帧来计算轨迹


trajectory=np.cumsum(transforms,axis=0)
4.2 计算平滑轨迹

平滑任何曲线最简单的方法是使用 移动平均滤波器(moving average filter)。顾名思义,移动平均过滤器将函数在某一 上的值替换为 由窗口定义的其相邻函数的平均值。

  • 应用卷积方法
np.convolve(a,v,'mode')

相关:

和我一起入门OpenCV with Python Day7
图片来源文章https://www.cjavapy.com/article/834/
4.3 计算平滑变换

使用平滑的轨迹来获得平滑的变换(通过找到平滑轨迹和原始轨迹之间的 差异,并将这些差异加回到原始的变换中),可以应用到视频的帧来稳定它。

part4代码


trajectory=np.cumsum(transforms,axis=0)

def movingAverage(curve,radius):
    window_size=2*radius+1

    f=np.ones(window_size)/window_size

    curve_pad=np.lib.pad(curve,(radius,radius),'edge')

    curve_smoothed=np.convolve(curve_pad,f,mode='same')

    curve_smoothed=curve_smoothed[radius:-radius]

    return curve_smoothed

def smooth(trajectory):
    smoothed_trajectory=np.copy(trajectory)
    '接受轨迹并对三个部分平滑处理'
    for i in range(3):
        smoothed_trajectory[:,i]=movingAverage(trajectory[:,i],radius=SMOOTHING_RADIUS)

    return smoothed_trajectory

difference=smoothed_trajectory-trajectory

transforms_smooth=transforms+difference

5. 将平滑的摄像机运动应用到帧中

和我一起入门OpenCV with Python Day7
5.1 修复边界伪影

为了稳定视频,帧可能不得不缩小大小,因此可能会看到一些黑色的边界。
我们可以通过将视频的中心缩小一小部分(例如4%)来缓解这个问题。
下面的fixBorder函数显示了实现。我们使用getRotationMatrix2D,因为它在不移动图像中心的情况下缩放和旋转图像。我们所需要做的就是调用这个函数时,旋转为0,缩放为1.04(也就是提升4%)。


def fixBorder(frame):
  s = frame.shape

  T = cv2.getRotationMatrix2D((s[1]/2, s[0]/2), 0, 1.04)
  frame = cv2.warpAffine(frame, T, (s[1], s[0]))
  return frame

完整代码:

from imp import source_from_cache
from sre_constants import SUCCESS
from tkinter import Frame
import numpy as np
import cv2

cap=cv2.VideoCapture('C:/Users/LEGION/Videos/ge.mp4')

n_frames=int(cap.get(cv2.CAP_PROP_FRAME_COUNT))

w=int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
h=int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))

fourcc=cv2.VideoWriter_fourcc(*'MJPG')

out=cv2.VideoWriter('C:/Users/LEGION/Videos/dou.mp4',fourcc,20.0,(w,h))

_,prev=cap.read()

prev_gray=cv2.cvtColor(prev,cv2.COLOR_BGR2GRAY)

transforms=np.zeros(((n_frames-1),3),np.float32)
for i in range(n_frames-2):

    prev_pts=cv2.goodFeaturesToTrack(prev_gray,maxCorners=200,qualityLevel=0.01,minDistance=30,blockSize=3)

ret,curr=cap.read()
while ret:
    ret,curr=cap.read()

curr_gray=cv2.cvtColor(curr,cv2.COLOR_BGR2GRAY)

curr_pts,status,err=cv2.calcOpticalFlowPyrLK(prev_gray,curr_gray,prev_pts,None)

assert prev_pts.shape==curr_pts.shape

idx=np.where(status==1)[0]
prev_pts=prev_pts[idx]
curr_pts=curr_pts[idx]

m=cv2.estimateRigidTransform(prev_pts,curr_pts,fullAffine=False)

dx=m[0,2]
dy=m[1,2]

da=np.arctan2(m[1,0],m[0,0])

transforms[i]=[dx,dy,da]

prev_gray=curr_gray

print("Frame:"+str(i)+"/"+str(n_frames)+"- Tracked points :"+str(len(prev_pts)))

trajectory=np.cumsum(transforms,axis=0)

def movingAverage(curve,radius):
    window_size=2*radius+1

    f=np.ones(window_size)/window_size

    curve_pad=np.lib.pad(curve,(radius,radius),'edge')

    curve_smoothed=np.convolve(curve_pad,f,mode='same')

    curve_smoothed=curve_smoothed[radius:-radius]

    return curve_smoothed

def smooth(trajectory):
    smoothed_trajectory=np.copy(trajectory)
    '接受轨迹并对三个部分平滑处理'
    for i in range(3):
        smoothed_trajectory[:,i]=movingAverage(trajectory[:,i],radius=30)

    return smoothed_trajectory

difference=smooth(trajectory)-trajectory

transforms_smooth=transforms+difference

cap.set(cv2.CAP_PROP_POS_FRAMES,0)

for i in range(n_frames-2):

    success,frame=cap.read()
    if not success:
        break

dx=transforms_smooth[i,0]
dy=transforms_smooth[i,1]
da=transforms_smooth[i,2]

m=np.zeros((2,3),np.float32)
m[0,0]=np.cos(da)
m[0,1]=-np.sin(da)
m[1,0]=np.sin(da)
m[1,1]=np.cos(da)
m[0,2]=dx
m[1,2]=dy

frame_stabilized=cv2.warpAffine(frame,m,(w,h))

frame_out=cv2.hconcat([frame,frame_stabilized])

if(frame_out.shape[1]):
    frame_out=cv2.resize(frame_out,(frame_out.shape[1]/2,frame_out.shape[0]/2));

def fixBorder(frame):
    s=frame.shape

    T=cv2.getRotationMatrix2D((s[1]/2,s[0]/2),0,1.04)
    frame=cv2.warpAffine(frame,T,(s[1],s[0]))
    return frame

cv2.imshow("Before and After",frame_out)
cv2.waitKey(10)
out.write(frame_out)

Original: https://blog.csdn.net/weixin_63336179/article/details/126201057
Author: 孜然星星星星
Title: 和我一起入门OpenCV with Python Day7

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/703014/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球