基于PythonOpenCv的视频图像处理

news2024/11/11 16:39:27

博主简介

博主是一名大二学生,主攻人工智能研究。感谢让我们在CSDN相遇,博主致力于在这里分享关于人工智能,c++,Python,爬虫等方面知识的分享。 如果有需要的小伙伴可以关注博主,博主会继续更新的,如果有错误之处,大家可以指正。

专栏简介:   本专栏主要研究python在人工智能方面的应用,涉及算法,案例实践。包括一些常用的数据处理算法,也会介绍很多的Python第三方库。如果需要,点击这里   订阅专栏

给大家分享一个我很喜欢的一句话:“每天多努力一点,不为别的,只为日后,能够多一些选择,选择舒心的日子,选择自己喜欢的人!”



初识OpenCV

OpenCV是一个开源的,跨平台的计算机视觉库,它采用优化的C/C++代码编写,能够充分利用多核处理器的优势,提供了Python,Ruby,MATPLOAB以及其他高级语言接口。

OpenCV的设计目标是执行速度尽量快,主要面向实时应用,是视频信号处理的主要工具之一,它封装了丰富的视频处理相关的工具包。视频信号是重要的视觉信息来源,其中包含的信息要远大于图像,对视频的分析也是计算机视觉领域的重要研究方向之一。视频在本质上由连锁的多帧图像构成,因此,视频信号处理最终仍归属图像处理范畴。但在视频中,其时间维度也包含了许多有用的信息。


视频读写处理

视频一般有两种来源,一种是从本地磁盘加载,另一种是从摄像头等设备实时获取。上述两种视频获取方式分别对应着OpenCV2的两个函数CaptureFromFile()和CaptureFromCAM().在OpenCV3中则统一为一个用于处理视频源载入的函数VideoCapture()。 

下示例代码展示了如何从本地载入一个视频文件,然后将其转化为灰度图像连续帧并播放:

import cv2
cap=cv2.VideoCapture('D:\Image\Funny.mp4')
while(cap.isOpened()):
    ret,frame=cap.read() #循环播放视频中每帧图像
    gray=cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY) #将原帧图像转化为灰度图像
    cv2.imshow('视频捕捉',gray) #显示处理后的图像
    if cv2.waitKey(1) & 0xFF==ord('q'): #按q键退出程序
        break
cap.release() #处理完成,释放视频捕捉
cv2.destroyAllWindows() #关闭窗口释放资源

 由于视频过长,原视频放在了主页。

下列代码则展示了如何从摄像头获取视频:

#摄像头获取视频
import cv2
cap=cv2.VideoCapture(1) #打开摄像头获取视频
while(True):
    ret,frame=cap.read() #循环播放视频中每帧图像
    gray=cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY)
    #显示结果
    cv2.imshow('摄像头拍照',gray)
    if cv2.waitKey(1) & 0xFF==ord('q'):
        break
cap.release()#处理完成,释放视频捕捉
cv2.destroyAllWindows()

下列代码展示了将摄像头获得的视频写入存储文件的过程。其中,VideoWriter_fourcc类用于定义视频文件的写入格式,其参数有多种格式可选,如下:

①.VideoWriter_fourcc('T','4','2','0'),该选项为一个未压缩的YUV颜色编码类型,是4:2:0色度子采样。改编码有着很好的兼容性,但是会产生较大的文件,文件拓展名为:“.avi”

②.VideoWriter_fourcc('P','T','M','1'):该选项为“MPEG-1”编码类型,文件拓展名为“.mpeg”

③.VideoWrite_fourcc('X','V','T','D'):该选项是“MPEG-4”编码类型,如果希望得到的视频大小为平均值,推荐使用该选项,文件拓展名为:“.mp4”

④.VideWriter_fourcc('T','H','E','O'):该选项是“Ogg Vorbis”编码类型,文件拓展名为“.ogv”。

⑤.VideWriter_foucc('F','L','V','T'):该选项是Flash编码类型,文件拓展名是“.flv”

定义好输出视频的格式后,用VideWriter类进行写入的时候,需要指定帧速率和帧大小,因此需要从另一个视频文件复制视频帧,这些属性可以通过VideoCapture类的get()函数得到。

通过OpenCV获取视频并写入文件的示例代码:

#通过OpenCv获取视频并写入
import cv2
cap2=cv2.VideoCapture('D:\Image\Funny.mp4')
cap=cv2.VideoCapture(1)#打开摄像头并获取视频
#对视频帧率fps进行赋值
fps=24
#过去视频帧的大小
size=(int(cap2.get(cv2.CAP_PROP_FRAME_WIDTH)),int(cap2.get(cv2.CAP_PROP_FRAME_HEIGHT)))
fourcc=cv2.VideoWriter_fourcc('X','V','I','D')  #MP4文件格式
out=cv2.VideoWriter('D:\Image\output.avi',fourcc,fps,size)#定义视频文件写入对象
while(cap.isOpened()):
    ret,frame=cap.read()
    if ret==True:
        '''
        获取帧图像并翻转,cv2.flip()的第二个参数表示翻转方式:0代表垂直翻转,1代表水平翻转,-1代表水平垂直翻转

        '''
        frame=cv2.flip(frame,1)
        out.write(frame)#将翻转后的帧图像写入文件
        cv2.imshow('帧图像处理',frame)
        if cv2.waitKey(1) & 0xFF==ord('q'):
            break
    else:
        break
cap.release()
out.release()
cv2.destroyAllWindows()

运动轨迹标记

运动捕捉(Motion Capture)技术可对运动物体或其特征点在三维空间中的运动轨迹进行实时,精确,定量地连续测量,跟踪和记录。运动轨迹则是从物体开始位置运动结束位置所经过的路线组成的空间特征。基于计算机视觉图像处理技术的运动捕捉方案在动画及游戏制作,仿真训练等领域有着广泛的应用。

光流(Optical Flow)是图像亮度的运动信息描述,是空间运动物体在观测成像面上的像素运动的瞬时速度,也是对视频中运动对象轨迹进行标记的一种常用方法。光流由场景中前景目标本身的运动,摄像机的运动,或者两者的共同运动产生。当人通过眼睛观察运动物体时,物体的景象在人眼的视网膜上形成一系列连续变化的图像,这一系列连续变化的信息不断“流过”视网膜,犹如光在平面中的流动,故称之为“光流”。光流的概念在20世纪40年代首次被提出,该方法利用图像序列中的像素在时域上的变化,相邻帧之间的相关性来找到前一帧与当前帧之间存在的对应关系,从而计算出相邻帧之间物体的运动信息。光流表达了图像的变化,由于它包含了目标运动的信息,因此可被观察者用于确定目标的运动情况。

在真实的三维空间中,描述物体运动状态的物理概念是运动场(Motion Field)。三维空间中的每一个点,经过某段时间的运动之后会到达一个新的位置,而这个位移过程可以用运动场来描述,运动场的实质上就是物体在三维真实世界的运动,而时光流畅(Optical Flow Field)是指图像中所有像素点构成的一种二维瞬时速度场它是一个二维矢量场。

三维空间运动到二维平面的投影所形成的光流,当描述部分像素时,称为稀疏光流,当描述全部像素时,则称为稠密光流。

OpenCV实现了不少光流算法,其中,Lucas-Kanade(L-K)是一种广泛使用的光流估计差分算法,它由布鲁斯·D.卢卡斯(Bruce D.Lucas)和金出武雄(Takeo Kanade)提出,L-K算法假设光流在像素点的领域是一个常数,然后使用最小二乘法对领域中的所有像素点求解基本的光流方程。通过结合几个邻近像素点的信息,L-K算法通常能够消除光流方程中的多义性。而且与逐点计算的方法比,L-K算法对图像噪声不敏感。对于L-K算法,低速度,亮度不变以及区域一致性都是较强的假设,但是这些条件并不容易满足。当运动速度过快时,这种假设不成立,使得最终求出的光流值有较大的误差。吉思——伊卡斯·布格(Jean—Yves Bouguent)提出了一种基于金字塔分层的算法,针对仿射变换的改进L-K算法,该算法最明显的优势在于,对于每一层的光流都会保持很小,但最终计算的光流可进行累积,从而可有效地跟踪特征点。L-K算法现已逐渐发展成为计算图像稀疏光流的重要方法。通过金字塔L-K算法计算稀疏光流的示例代码:



#L-K算法
import numpy as np
import cv2
#设置L-K算法参数
lk_params=dict(winSize=(15,15), #搜索窗口的大小
                        maxLevel=2,#最大金字塔层数
                        #迭代算法终止条件(迭代次数或迭代阈值)
                        criteria=(cv2.TERM_CRITERIA_EPS|cv2.TERM_CRITERIA_COUNT,10,0.03))
feature_params=dict(maxCorners=500,   #设置最多返回的关键点(角点)数
                    qualityLevel=0.3,  #角点阈值:反映一个像素点对强才算一个角点
                    minDistance=7, #角点之间的最小像素点(欧氏距离)
                    blockSize=7) #计算一个像素点是否为关键点时所取区域的大小
class App:
    #构造方法,初始化一些参数和视频路径
    def __init__(self,video_src):
        self.track_len=10 #光流标记长度
        self.detect_interval=5#帧检测间隔
        #跟踪点几何初始化,self.tracks中值的格式时:(前一帧角点)
        self.tracks=[]
        self.cam=cv2.VideoCapture(video_src) #视频源
        self.frame_idx=0 #帧序列号初始化
    def run(self): #运行光流方法
        while True:
            ret,frame=self.cam.read()
            if ret ==True:
                frame_gray=cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY)
                vis=frame.copy()
                #检测到角点后光流跟踪
                if len(self.tracks)>0:
                    img0,img1=self.prev_gray,frame_gray
                    p0=np.float32([tr[-1] for tr in self.tracks]).reshape(-1,1,2)
                    #将前一帧的角点和当前帧的图像作为输入的角点在当前帧的位置
                    p1,st,err=cv2.calcOpticalFlowPyrLK(img0,img1,p0,None,**lk_params)
                    #将当前帧跟踪到的角点以及图像和前一帧的图像作为输入得到前一帧的角点检测
                    p0r,st,err=cv2.calcOpticalFlowPyrLK(img1,img0,p1,None,**lk_params)
                    #得到角点回溯与前一帧实际角点的位置变化系
                    d=abs(p0-p0r).reshape(-1,2).max(-1)
                    good=d<1 #判断d的值是否小于1
                    new_tracks=[]
                    #将跟踪的点列为成功跟踪点
                    for tr,(x,y),good_flag in zip(self.tracks,p1.reshape(-1,2),good):
                        if not good_flag:
                            continue
                        tr.append((x,y))
                        if (len(tr)>self.track_len):
                            del tr[0]
                        new_tracks.append(tr)
                        cv2.circle(vis,(x,y),2,(0,255,0),-1)
                    self.tracks=new_tracks
                    #以前一帧角点为初始点,以当前帧跟踪到的点为终点划线,开始轨迹标记
                    cv2.polylines(vis,[np.int32(tr) for tr in self.tracks],False,(0,255,0))
                    #每五帧检测一次
                if (self.frame_idx % self.detect_interval==0):
                    mask=np.zeros_like(frame_gray)#初始化和视频尺寸大小相同的图像
                    mask[:]=255 #计算全部图像的角点
                    for x,y in [np.int32(tr[-1]) for tr in self.tracks]:
                        cv2.circle(mask,(x,y),5,0,-1)
            #利用goodFeaturesToTrack进行角点检测
                    p=cv2.goodFeaturesToTrack(frame_gray,mask=mask,**feature_params)
                    if p is not None:
                        for x,y in np.float32(p).reshape(-1,2):
                            self.tracks.append([(x,y)]) #将检测到的角点放在预跟踪序列
                    self.frame_idx+=1
                    self.prev_gray=frame_gray
                    cv2.imshow('Lucas-Kanade光流算法',vis)
                    ch=0xFF & cv2.waitKey(1)
                    if ch==ord('q'):#按q键退出
                        break
def main():
    import sys    
    video_src='D:\Image\haha.mp4'    
    App(video_src).run() #运行主程序
    cv2.destroyAllWindows()
if __name__=='__main__':
    main() 

由于视频是本地文件,不能展示出来,这里就直接呈现代码;通过运行结果程序可知,稠密光流算法是一种对图像进行逐点匹配的图像配准算法。不同于稀疏光流算法只针对图像中若干个特征点,稠密光流算法计算图像上所有的点的偏移量,从而形成一个稠密的光流场。通过这个稠密的光流场,可以进行像素级别的图像配准,因此,其配准后的效果也明显优于稀疏光流算法配准的效果。但是其副作用也非常明显,由于要计算每个点的偏移量,其计算量也明显大于稀疏光流算法。

CalOpticalFlowFraneback()算法

在OpenCV中,CalOpticalFlowFraneback()函数利用Gunnar Farneback算法进行全局性稠密光流算法,其参数说明如下:

(1)prevImg:输入的8bit单通道前一帧图像。

(2)nextImg:输入的8bit单通道当前帧图像。

(3)pyr_scale:金字塔参数,0.5为经典参数,每一层是下一层尺度的一般。

(4)levels:金字塔的层数。

(5)winsize:窗口大小。

(6)iterations:迭代次数。

(7)poly_n:像素领域的大小,如果值比较大则表示图像整体比较平滑。

(8)poly_sigma:高斯标准差。

(9)flags:可以为这些组合——OPTFLOW_USE_INITIAL_FLOW,OPTFLOW_FARNEBACK_GAUSSIAN,返回值为每一个像素点的位移。

基于稠密光流算法的运动轨迹标记的代码:

#基于稠密光流算法轨迹标记
from numpy import *
import cv2
#定义光流跟踪标记函数
def draw_flow(im,flow,step=16):
    h,w=im.shape[:2]
    y,x=mgrid[step/2:h:step,step/2:w:step].reshape(2,-1)
    fx,fy=flow[y.astype(int),x.astype(int)].T
    #创建标记线条端点
    lines=vstack([x,y,x+fx,y+fy]).T.reshape(-1,2,2)
    lines=int32(lines)
    #创建图像和进行线条标记
    vis=cv2.cvtColor(im,cv2.COLOR_GRAY2BGR)
    for (x1,y1),(x2,y2) in lines:
        cv2.line(vis,(x1,y1),(x2,y2),(0,255,0),1)
        cv2.circle(vis,(x1,y1),1,(0,255,0),-1)#画圆
    return vis
cap=cv2.VideoCapture(1)#开启摄像头
#读取视频帧
ret,im=cap.read()
#转化为灰度图像
prev_gray=cv2.cvtColor(im,cv2.COLOR_BGR2GRAY)
while True:
    #读取视频帧
    ret,im=cap.read()
    #转换为灰度图像
    gray=cv2.cvtColor(im,cv2.COLOR_BGR2GRAY)
    #光流计算
    flow=cv2.calcOpticalFlowFarneback(prev_gray,gray,None,0.5,3,15,3,5,1.2,0)
    prev_gray=gray
    #绘制光流轨迹
    cv2.imshow('稠密光流算法',draw_flow(gray,flow))
    if cv2.waitKey(1)&0xFF==ord('q'): #按q键结束
        break
im.release()
cv2.destroyAllWindows()

        

运动检测

运动检测(Motion Detection)是计算机视觉和视频处理中常用的预处理步骤,是指从视频中识别发生变化或移动的区域。运动检测最常见的应用场景是运动目标检测,也就是对摄像头记录的视频移动目标进行定位到过程,有着非常广泛的应用。实时目标检测是许多计算机视觉的重要任务,例如安全监控,增强现实应用,基于对象的视频压缩,基于感知的用户界面及辅助驾驶等。

运动目标检测算法根据目标与摄像机之间的关系可以分为静态背景下的运动目标检测和动态背景下的运动目标检测。静态背景下的运动目标检测,就是从序列图像中将实际的的变化区域与背景分开。在背景静止的大前提下进行运动目标检测的方法有很多,大多侧重于背景扰动小噪声的消除,如背景差分法(Background Difference Nethod ,BDM),帧间差分法(Inter-Frame Difference Method,IFDM),光流法,高斯混合模型(Gaussion Mixed Model,GMM),码本(Codebook),自组织背景减除(Self-Organizing Background Subtraction,SOBS),视觉背景提取(Visual Background Extractor,VIBE)以及这些方法的变种,如三帧差分法,五帧差分法,或者这些方法的结合。动态背景下的运动目标检测,相对于静态背景而言,算法的思路有所不同,一般更侧重于匹配,需要进行图像的全局运动估计与补偿,因为在目标和背景同时运动的情况下,无法简单的根据运动来判断。动态背景下的运动目标检测算法也有很多,例如块匹配(Block Matching,BM)和光流估计(Optical Flow Estimation,OFE)等。

帧间差分法是一种常用的运动目标检测算法,其基本原理是观测视频图像相邻帧之间的细微变化来判断物体是否在运动。摄像机采集的视频序列具有连续性,如果场景内没有运动目标,则连续帧的变化很小;如果存在运动目标,由于场景中的目标的运动,目标的影像在不同图像帧之间的位置会不同,从而导致连续帧之间有显著变化。该算法通过对时间上连续的两帧或三帧图像进行差分运算,对不同帧对应的的像素点灰度值想减,来判断灰度值的绝对值,当绝对值超过一定阈值时,则可判断其为运动目标,从而实现目标的检测功能。

(二帧差分原理图)

(三帧差分原理图) 

 在OpebCV中用absdiff()函数实现。

三帧差分法的运算过程如上图所示。记视频序列中第n+1帧、第n帧和第n-1帧的图像分别为fn+1、fn和fn-1,三帧对应像素点的灰度值记为fn+1(x,y)、fn(x,y)和fn-1(x,y),分别得到差分图像Dn和Dn+1,对差分图像Dn和Dn+1按照下式进行计算,得到图像Dn',然后再进行阈值处理、连通性分析,最终提取出运动目标。

在帧间差分法中,阈值T的选择非常重要。如果阈值T选取的值太小,则无法抑制差分图像中的噪声;如果阈值T选取的值太大,又有可能掩盖差分图像中目标的部分信息;而且固定的阈值T无法适应场景中光线变化等情况。为此,有人提出了在判决条件中加入对整体光照敏感的添加项的方法,将判决条件修改为:

其中,NA为待检测区域中像素的总数目,λ为光照的抑制系数,A可设为整帧图像。红框中的添加项表达了整帧图像中光照的变化情况。如果场景中的光照变化较小,则该项的值趋向于零;如果场景中的光照变化明显,则该项的值明显增大,导致上式右侧判决条件自适应地增大,最终的判决结果为没有运动目标,这样就有效地抑制了光线变化对运动目标检测结果的影响。

下图中左图是采用帧间差分法进行运动目标检测的实验结果,(b)图是采用两帧差分法的检测结果,(c)图是采用三帧差分法的检测结果。视频序列中的目标运动较快,在这种情况下,运动目标在不同图像帧内的位置明显不同,采用两帧差分法检测出的目标会出现“重影”的现象,采用三帧差分法,可以检测出较为完整的运动目标

综上所述,帧间差分法的原理简单,计算量小,能够快速检测出场景中的运动目标。但由实验结果可以看出,帧间差分法不能提取出对象的完整区域,只能提取出边界。同时依赖于选择的帧间时间间隔,对快速运动的物体,需要选择较小的时间间隔,如果选择不合适,当物体在前后两帧中没有重叠时,会被检测为两个分开的物体,而对慢速运动的物体,应该选择较大的时

间差,如果时间选择不适当,当物体在前后两帧中几乎完全重叠时,则检测不到物体。

因此帧间差分法通常不单独用在目标检测中,往往与其它的检测算法结合使用。常见的是结合背景差分法和帧间差分法的优缺点,使它们优势互补,从而克服相互的弱点,提高运动检测的效果。例如在实际的场景中,即便是室内环境,也存在光线等各种变化造成的干扰,或者人为造成的开灯等光线的强烈变化。所以在背景差分法的实现中,它的固定背景不能一成不变。如果不进行重新初始化,错误的检测结果将随时间不断累计,造成恶性循环,从而造成监控失效

利用帧间差分法示例代码:


#利用帧间差分法进行目标检测
import time
import cv2
import argparse #用于解析参数
import datetime #用于时间和日期相关处理
import imutils #用于图像相关处理
import argparse
#创建参数解析器并解析参数
ap=argparse.ArgumentParser()
ap.add_argument('-v','--video',help='path to the video file')
ap.add_argument('-a','--min-area',type=int,default=500,help='minimum area size')
args=vars(ap.parse_args(args=[])) #这里有两种表达式,JUPYTER只能使用这种,表示默认参数,pycharme可以用另一种
#如果video参数为None,那么我们从摄像头读取数据
if args.get('video',None) is None:
        camera=cv2.VideoCapture(1)
        if camera is None:
            print("请检查摄像头连接")
            exit()
            time.sleep(0.25)
        else:
        #读取一个视频
            camera=cv2.VideoCapture('D:\Image\haha.mp4')
'''
初始化视频流的第一帧。一般情况下,视频第一帧不会包含运动而仅仅是背景
''' 
firstFrame=None
#遍历视频的每一帧
while True:
    '''
    获取当前视帧并初始化显示文本,调用camera.read()将返回一个二元组,元组的第一个值是True和False,表明是否成功从缓冲中读取帧图像,元组的第二个值就是获取的当前帧图像的值。
    '''
    (grabbed,frame)=camera.read()
    text='No Motion Detected'
    #如果不能获取到帧,说明到了视频的结尾
    if not grabbed:
        break
    frame=imutils.resize(frame,width=500) #调整帧图像大小
    gray=cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY) #转换为灰度图像
    gray=cv2.GaussianBlur(gray,(21,21),0)#高斯模糊处理
#如果第一帧是None,对其进行初始化
    if firstFrame is None:
        firstFrame=gray
        continue
#将当前帧和第一帧图像对应的像素点的灰度值相减并求绝对值来计算两帧的不同
    frameDelta=cv2.absdiff(firstFrame,gray)
#对差分图像进行阈值处理来显示图像中像素点的灰度值有所变化的区域
    thresh=cv2.threshold(frameDelta,25,255,cv2.THRESH_BINARY)[1]
#扩展阈值图像填充空洞,然后找到阈值图像中的轮廓
    thresh=cv2.dilate(thresh,None,iterations=2)
    '''
cv2.findContours()函数返回3个值,第一个是所处理的图像,第二个是轮廓,第三个是每个轮廓对应的属性
'''
    contours,hierrarchy=cv2.findContours(thresh.copy(),cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE) #老版返回3个接受参数,新版返回两个;
    #遍历轮廓
    for c in contours:
    #过滤小的,不相关的轮廓,如果轮廓面积大于min_area,则在前景和移动区域画边框
        if cv2.contourArea(c)<args['min_area']:
            continue
    #计算轮廓的边界框,在当前帧中画出该框并更新相应文本
        (x,y,w,h)=cv2.boundingRect(c)
        cv2.rectangle(frame,(x,y),(x+w,y+h),(0,255,0),2)
        text='Motion Detected'
#在当前帧上写文本及时间戳
    cv2.putText(frame,'Room Status:{}'.format(text),(10,20),cv2.FONT_HERSHEY_SIMPLEX,0.5,(0,0,255),2)
    cv2.putText(frame,datetime.datetime.now().strftime('%A %%d %B %Y %I:%M:%S%p'),(10,frame.shape[0]-10),cv2.FONT_HERSHEY_SIMPLEX,0.35,(0,0,255),1)
    #显示当前帧并记录用户是否按了键
    cv2.imshow('视频监控演示',frame)
    cv2.imshow('阈值轮廓图像',thresh)
    cv2.imshow('帧差分图像',frameDelta)
    key=cv2.waitKey(1)
    if key==ord('q'):
        break #跳出循环
#释放摄像机资源并关闭打开的窗口
camera.release()
cv2.destroyAllWindows()

由于视频不太容易上传,这里就使用截屏作为结果展示。

运动方向检测

在某些应用场合,检测出运动的物体之后,我们还需知道物体的运动方向,判断其是否进入或离开检测区域。对于运动方向的检测,一般通过检测图像的光流场估算图像的运动场来实现。根据传统估算方法,需要对图像中的每一个像素进行计算,算出图像每一点的运动场,然后得到整幅图像的运动场。

检测物体的运动方向,理论上也可以使在帧间差分法的基础上通过计算帧图像轮廓中点的变化来实现,但因每次检测出的轮廓数量不稳定,所以该方式会使得误差不可控。不过,OpenCv中的goodFeaturesToTrack()函数可用于获取图像最大特征只的角点。我们可以此为契机重新设计物体运动方向检测算法,步骤如下:

(1) 对相邻两帧图像所有像素点通过absdiff()函数进行差分运算得到差分图像。

(2) 将差分图像转化成灰度图像并进行二值化处理。 

(3) 利用goodFeaturesToTrack()函数获得最大特征值的角点。

(4) 计算角点的平均特征值,写入队列。

(5) 维护一个长度为10的队列,队列满时计算队列中元素的增减情况,并以此来确定目标的运动方向。

利用上述改进算法进行物体运动方向检测的示例代码:





#运动方向检测
import cv2
import numpy as np
import queue  #导入库主要用于队列处理
camera=cv2.VideoCapture(1)
if camera is None:
    print('请检查摄像头链接')
    exit()
width=int(camera.get(3))
height=int(camera.get(4))

#参数初始化
firstFrame=None
lastDec=None
firstThresh=None
feature_params=dict(maxCorners=100, #设置最多返回的关键点数
                    qualityLevel=0.3, #角点阈值:响应最大值
                    minDistance=7, #角点之间最少像素点(欧氏距离)
                    blockSize=7) #计算一个像素点是否为关键点时所取区域
#Lucas-Kanade 光流算法参数设置
lk_params=dict(winSize=(15,15),#搜索窗口的大小
                maxLevel=2,#最大金字塔层数
                criteria=(cv2.TermCriteria_EPS|cv2.TERM_CRITERIA_COUNT,10,0.03))
color=np.random.randint(0,255,(100,3))
num=0
#队列初始化
q_x=queue.Queue(maxsize=10)
q_y=queue.Queue(maxsize=10)
while True:
    #获取视频帧并转化为灰度图像
    (grabbed,frame)=camera.read()
    gray=cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY)
    gray=cv2.GaussianBlur(gray,(21,21),0)
    if firstFrame is None:
        firstFrame=gray
        continue
    frameDelta=cv2.absdiff(firstFrame,gray)
    #对图像进行阈值二值化
    thresh=cv2.threshold(frameDelta,25,255,cv2.THRESH_BINARY)[1]

    p0=cv2.goodFeaturesToTrack(thresh,mask=None,**feature_params)
    if p0 is not None:
        x_sum=0
        y_sum=0
        for i,old in enumerate(p0):
            x,y=old.ravel()
            x_sum+=x
            y_sum+=y
        x_avg=x_sum/len(p0)
        y_avg=y_sum/len(p0)
        if q_x.full():
            qx_list=list(q_x.queue)
            key=0
            diffx_sum=0
            for item_x in qx_list:
                key+=1
                if key<10:
                    diff_x=item_x-qx_list[key]
                    diffx_sum+=diff_x
                if diffx_sum<0 and x_avg<500:#表明队列在增加
                    print('Left')
                    cv2.putText(frame,'Left Motion Detected',(100,100),0,0.5,(0,0,255),2)
                else:
                     print("Right")
                     cv2.putText(frame,"Right Motion Detected",(300,100),0,0.5,(255,0,255),2)
            q_x.get()
            q_x.put(x_avg)
            cv2.putText(frame,str(x_avg),(300,100),0,0.5,(0,0,255),2)
            frame=cv2.circle(frame,(int(x_avg),int(y_avg)),5,color[i].tolist(),-1)
        cv2.imshow('运动方向检测',frame)
        firstFrame=gray.copy()
        key=cv2.waitKey(1)&0xFF
        if key==ord('q'):
            break
camera.release()
cv2.destroyAllWindows()

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/177288.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【教程】虚拟环境与Pytorch安装保姆级教学

【教程】虚拟环境与Pytorch安装保姆级教学NVIDIA驱动安装虚拟环境创建激活/退出相关库的安装Pycharm内设置虚拟环境Pytorch安装安装地址可能遇到的问题处理报错安装卡顿测试是否安装完成参考NVIDIA驱动安装 NVIDIA驱动可在官网进行安装&#xff1a;NVIDIA驱动官网 命令行输入…

一文上手决策树:从理论到实战

一、基础概念 决策树是一类极为常用的机器学习方法&#xff0c;尤其是在分类场景。决策树通过树形结构来递归地将样本分割到不同的叶子结点中去&#xff0c;并根据每个叶子结点中的样本构成对该结点中的样本进行分类。 我们可以从两个视角来理解决策树模型。 第一种视角是将…

Python副业技术总结,手把手教你用宝塔面板部署Django程序

前言 最近写了几个Django项目&#xff0c;写完以后怎么让对方测试成了问题&#xff0c;因为之前都是自己在本地写的练习项目&#xff0c;对于部署这一块很陌生&#xff0c;不知道怎么操作&#xff0c;内心很忐忑。没办法&#xff0c;只能硬着头皮上&#xff0c;一边百度&#…

17种编程语言实现排序算法-插入排序

开源地址 https://gitee.com/lblbc/simple-works/tree/master/sort/ 覆盖语言&#xff1a;C、C、C#、Java、Kotlin、Dart、Go、JavaScript(JS)、TypeScript(TS)、ArkTS、swift、PHP。 覆盖平台&#xff1a;安卓(Java、Kotlin)、iOS(SwiftUI)、Flutter(Dart)、Window桌面(C#)、…

ARP渗透与攻防(四)之WireShark截获用户数据

ARP-WireShark截获用户数据 系列文章 ARP渗透与攻防(一)之ARP原理 ARP渗透与攻防(二)之断网攻击 ARP渗透与攻防(三)之流量分析 1.WireShark工具介绍 wireshark的官方下载网站&#xff1a; WireShark wireshark是非常流行的网络封包分析软件&#xff0c;功能十分强大。可以…

PowerShell 学习笔记:压缩、解压缩文件

在自动构建的时候&#xff0c;最常用的就是压缩备份项目的源文件&#xff0c;PowerShell提供了相关命令。Compress-Archive&#xff08;压缩文件&#xff09;Compress-Archive[-Path] <String[]>[-DestinationPath] <String>[-CompressionLevel <String>][-P…

Crossplane - 比 Terraform 更先进的云基础架构管理平台?

&#x1f449;️URL: https://crossplane.io/ &#x1f4dd;Description: 将云基础架构和服务组成自定义平台 API 简介 在 11 月的 KCD 上海现场&#xff0c;听了一场阿里云的工程师关于他们自己的多云基础架构管理工具的介绍&#xff0c;前边的引言部分有介绍到 Terraform&am…

连续系统的数字PID控制仿真-3

在连续系统的数字PID控制仿真-2的基础上&#xff0c;利用S函数实现PID离散控制器的Simulink仿真。在S函数中&#xff0c;采用初始化函数、更新函数和输出函数&#xff0c;即 mdlInitializeSizes函数、mdIUpdates函数和mdlOutputs函数。在初始化中采用sizes 结构&#xff0c;选择…

什么是GRACE CPU --- Grace CPU架构详解

深入详解GRACE CPU架构 NVIDIA Grace CPU 是 NVIDIA 开发的第一款数据中心 CPU。 通过将 NVIDIA 专业知识与 Arm 处理器、片上结构、片上系统 (SoC) 设计和弹性高带宽低功耗内存技术相结合&#xff0c;NVIDIA Grace CPU 从头开始构建&#xff0c;以创建世界上第一个超级芯片 用…

Spring控制反转(IoC)和依赖注入(DI)

Spring官网&#xff1a;spring.io1.spring 2.SprinMVC 3.Maven高级 4.SpringBoot 5.MyBatisPlus为什么要学Spring?简化开发&#xff0c;降低企业级开发的复杂度框架整合&#xff0c;高效整合其他技术&#xff0c;提高企业级应用开发与运行效率Spring 系统架构IOC&#xff08;I…

分享132个ASP源码,总有一款适合您

ASP源码 分享132个ASP源码&#xff0c;总有一款适合您 下面是文件的名字&#xff0c;我放了一些图片&#xff0c;文章里不是所有的图主要是放不下...&#xff0c; 132个ASP源码下载链接&#xff1a;https://pan.baidu.com/s/1bk2hftqR5NTdUIT2zvmbiw?pwdke5x 提取码&#x…

离散数学与组合数学-04图论

文章目录离散数学与组合数学-04图论4.1 图的引入4.1.1 图的示例4.1.2 无序对和无序积4.1.3 图的定义4.2 图的表示4.2.1 集合表示和图形表示4.2.2 矩阵表示法4.2.3 邻接点与邻接边4.3 图的分类4.3.1 按边的方向分类4.3.2 按平行边分类4.3.3 按权值分类4.3.4 综合分类方法4.4 图论…

干货 | 移动互联网应用程序(APP)个人信息安全自我评测工具

以下内容整理自清华大学《数智安全与标准化》课程大作业期末报告同学的汇报内容。第一部分&#xff1a;研究背景概述截止今年6月&#xff0c;我国已经有APP 232万款&#xff0c;手机网民达到10.47亿&#xff0c;在APP中大规模的个人信息收集和使用成为常态&#xff0c;个人信息…

【算法题】1828. 统计一个圆中点的数目

插&#xff1a; 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站。 坚持不懈&#xff0c;越努力越幸运&#xff0c;大家一起学习鸭~~~ 题目&#xff1a; 给你一个数组 points &#xff0c;…

Kubernetes:基于命令行终端UI的管理工具 K9s

写在前面 K9s 是一个基于终端UI的 K8S 管理工具博文内容为 k9s 在 windows、Linux 以及docker 安装Demo简单的 热键使用。理解不足小伙伴帮忙指正 我所渴求的&#xff0c;無非是將心中脫穎語出的本性付諸生活&#xff0c;為何竟如此艱難呢 ------赫尔曼黑塞《德米安》 K9s 是一…

客快物流大数据项目(一百零八):Spring Cloud 技术栈

文章目录 Spring Cloud 技术栈 ​​​​前言 一、微服务技术栈

如果物理学真的不存在?

最近过年&#xff0c;看「三体」电视剧。开始看剧情&#xff0c;觉得代入感挺不好的&#xff0c;特别林子健演的那个作战中心的长官&#xff0c;镜头从远处拉过去&#xff0c;看着他昂首挺胸慢慢走过去的样子。之后&#xff0c;讲到了火鸡和农场主的故事&#xff0c;这个时候再…

C++STL剖析(二)—— vector的概念和使用

文章目录1. vector的介绍2. vector的常见构造3. vector的遍历方式&#x1f351; [ ] 下标&#x1f351; 迭代器&#x1f351; 范围for4. vector 迭代器使用&#x1f351; begin 和 end&#x1f351; rbegin 和 rend5. vector 空间增长问题&#x1f351; size&#x1f351; cap…

37.Isaac教程--自由空间分割(道路分割)

自由空间分割 ISAAC教程合集地址: https://blog.csdn.net/kunhe0512/category_12163211.html 文章目录自由空间分割快速开始推理训练数据模拟数据设置与模拟器的通信来自公共数据集的真实数据具有自主数据收集的自由空间分割的真实数据自主数据收集通过地图规划路径监测机器人位…

JavaEE 突击 5 - Spring 更简单的读取和存储对象(2)

Spring 更简单的读取和存储对象 - 2三 . 获取 Bean 对象3.1 属性注入3.1.1 原理3.1.2 相关问题能在启动类里面调用 [Autowired ](/Autowired ) 注解吗[Autowired ](/Autowired ) 能使用多次吗Autowired 修饰的私有方法名字可以是其他的吗3.1.3 属性注入的优点和缺点3.2 Setter …