jjzjj

Python-OpenCV视频帧间差分、高斯混合建模、背景差分提取前景目标标示轮廓、KCF目标跟踪、Meanshift算法跟踪

手撕鸡翅 2023-04-03 原文

本人只是想很简单的入门了解opencv,目前相关原理和知识了解的不多,可能存在有些地方写的不对,仅供参考。

1.帧间差分

帧间差分法是一种通过对视频图像序列的连续两帧图像做差分运算获取运动目标轮廓的方法。当监控场景中出现异常目标运动时,相邻两帧图像之间会出现较为明显的差别,两帧相减,求得图像对应位置像素值差的绝对值,判断其是否大于某一阈值,进而分析视频或图像序列的物体运动特性

原理: 当视频中存在移动物体的时候,相邻帧之间在灰度上会有差别,求取两帧图像灰度差的绝对值,则静止的物体在差值图像上表现出来全是0,而移动物体特别是移动物体的轮廓处由于存在灰度变化为非0。

实现: 相邻帧间差分法直接对相邻的两帧图像做差分运算,并取差分运算的绝对值构成移动物体。
更多可以了解 运动目标检测(2)—帧间差分法

class demo1():

    def run(self, Videopath='./video.avi'):
        frames = self.Video_to_image(Videopath)
        self.absdiff_(frames)

    def Video_to_image(self, Videopath):
        capture = cv2.VideoCapture(Videopath)
        # 得到整个视频的帧数
        framesNum = capture.get(cv2.CAP_PROP_FRAME_COUNT)
        print("frames=", framesNum)
        frames = []

        for i in range(int(framesNum) - 1):
            ret, frame = capture.read()
            frames.append(frame)
        return frames

    def absdiff_(self, frames):
        c_frames = []
        for i in range(len(frames) - 2):
            frame_front = frames[i]
            frame_later = frames[i + 1]
            # 帧间做差
            d_frame = cv2.absdiff(frame_front, frame_later)
            c_frames.append(d_frame)
            cv2.imshow('d_frame', d_frame)
            cv2.waitKey()

        return c_frames

2.高斯混合建模、背景差分提取前景目标,将轮廓信息标示

混合高斯模型:在进行前景检测前,先对背景进行训练,对图像中每个背景采用一个混合高斯模型进行模拟,每个背景的混合高斯的个数可以自适应。然后在测试阶段,对新来的像素进行GMM匹配,如果该像素值能够匹配其中一个高斯,则认为是背景,否则认为是前景。
更多可以了解 [Python - opencv(十一)背景建模]

class demo2():
    def __init__(self, Videopath='./counting_test.avi'):
        self.capture = cv2.VideoCapture(Videopath)

    def Gaussian(self, drawContours=False, drawRectangle=True):
        cap = self.capture
        # 创建形态学操作时需要使用的核
        kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3))
        # 创建混合高斯模型
        fgbg = cv2.createBackgroundSubtractorMOG2()
        # 将行人在视频中实时标记出
        while (True):
            ret, frame = cap.read()
            fgmask = fgbg.apply(frame)
            # 形态学开运算去噪点
            fgmask = cv2.morphologyEx(fgmask, cv2.MORPH_OPEN, kernel)
            
            # 寻找视频中的轮廓
            im, contours, hierarchy = cv2.findContours(fgmask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
            
            if drawContours: #背景差分提取前景目标,将轮廓信息标示
                n = len(contours)
                for i in range(n):
                    temp = np.zeros(frame.shape, np.uint8)
                    temp = cv2.drawContours(temp, contours, i, (255, 255, 255), 2)
                    cv2.imshow('frame', frame)
                    cv2.imshow("contours", temp)
                cv2.waitKey()

            if drawRectangle:
                for c in contours:
                    # 计算各轮廓的周长
                    perimeter = cv2.arcLength(c, True)
                    if perimeter > 188:
                        # 找到一个直矩形(不会旋转)
                        x, y, w, h = cv2.boundingRect(c)
                        # 画出这个矩形
                        cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)

                cv2.imshow('frame', frame)
                cv2.imshow('fgmask', fgmask)
                k = cv2.waitKey()
                if k == 27:
                    break

        cap.release()
        cv2.destroyAllWindows()


3.实现KCF目标跟踪

什么是目标跟踪?

简单理解就是在一个视频的连续帧中定位目标,称之为跟踪。

KCF原理:目标跟踪系列–KCF算法

代码建议参考 python调用opencv库中的KCF等跟踪算法(有中文注释),我这边只是根据它的大体思路简化了一下,存在不足。

总共有七种不同的跟踪器:BOOSTING,MIL,KCF,TLD,MEDIANFLOW,GOTURN和MOSSE。

想简单了解更多关于目标跟踪的定义可以参考 Object Tracking using OpenCV (C++/Python)(使用OpenCV进行目标跟踪),其实它的代码才是元祖。

def demo3():
    # 初始化视频捕获设备
    # gVideoDevice = cv2.VideoCapture("./video.avi")
    gVideoDevice = cv2.VideoCapture(0)
    if not gVideoDevice.isOpened():
        print('open video failed')
        return
    else:
        print('open video succeeded')

    # 选择 框选帧
    print("按 enter 选择当前帧,否则继续下一帧")
    while True:
        gCapStatus, gFrame = gVideoDevice.read()
        cv2.imshow("pick frame", gFrame)
        k = cv2.waitKey()
        if k == 13:
            break

    # 框选感兴趣区域
    cv2.destroyWindow("pick frame")
    gROI = cv2.selectROI("ROI frame", gFrame, False)
    if (not gROI):
        print("空框选,退出")
        quit()

    # 初始化追踪器
    gTracker = cv2.TrackerKCF_create()
    gTracker.init(gFrame, gROI)

    # 循环帧读取,开始跟踪
    while True:
        gCapStatus, gFrame = gVideoDevice.read()
        if (gCapStatus):
            # 展示跟踪图片
            status, coord = gTracker.update(gFrame)
            if status:
                message = {"coord": [((int(coord[0]), int(coord[1])),
                                      (int(coord[0] + coord[2]), int(coord[1] + coord[3])))]}
                p1 = (int(coord[0]), int(coord[1]))
                p2 = (int(coord[0] + coord[2]), int(coord[1] + coord[3]))
                cv2.rectangle(gFrame, p1, p2, (255, 0, 0), 2, 1)
                message['msg'] = "is tracking"
            else:
                message['msg'] = "KCF error,需要重新使用调用跟踪器"
            cv2.imshow('tracked image', gFrame)
            print(message)
            key = cv2.waitKey(1)
            if key == 27:
                break
        else:
            print("捕获帧失败")
            quit()

我这边还修改了 python调用opencv库中的KCF等跟踪算法 的一些小地方,你也可以自己补充使用不同的跟踪器。

class MessageItem(object):
    # 用于封装信息的类,包含图片和其他信息
    def __init__(self, frame, message):
        self._frame = frame
        self._message = message

    def getFrame(self):
        # 图片信息
        return self._frame

    def getMessage(self):
        # 文字信息,json格式
        return self._message


class Tracker(object):
    '''
    追踪者模块,用于追踪指定目标
    '''

    def __init__(self, tracker_type="BOOSTING", draw_coord=True):
        '''
        初始化追踪器种类
        '''
        # 获得opencv版本
        (major_ver, minor_ver, subminor_ver) = (cv2.__version__).split('.')
        self.tracker_types = ['BOOSTING', 'MIL', 'KCF', 'TLD', 'MEDIANFLOW', 'GOTURN']
        self.tracker_type = tracker_type
        self.isWorking = False
        self.draw_coord = draw_coord
        # 构造追踪器
        if int(major_ver) < 3:
            self.tracker = cv2.Tracker_create(tracker_type)
        else:
            if tracker_type == 'BOOSTING':
                self.tracker = cv2.TrackerBoosting_create()
            if tracker_type == 'MIL':
                self.tracker = cv2.TrackerMIL_create()
            if tracker_type == 'KCF':
                self.tracker = cv2.TrackerKCF_create()
            if tracker_type == 'TLD':
                self.tracker = cv2.TrackerTLD_create()
            if tracker_type == 'MEDIANFLOW':
                self.tracker = cv2.TrackerMedianFlow_create()
            if tracker_type == 'GOTURN':
                self.tracker = cv2.TrackerGOTURN_create()

    def initWorking(self, frame, box):
        '''
        追踪器工作初始化
        frame:初始化追踪画面
        box:追踪的区域
        '''
        if not self.tracker:
            raise Exception("追踪器未初始化")
        status = self.tracker.init(frame, box)
        if not status:
            raise Exception("追踪器工作初始化失败")
        self.coord = box
        self.isWorking = True

    def track(self, frame):
        '''
        开启追踪
        '''
        message = None
        if self.isWorking:
            status, self.coord = self.tracker.update(frame)
            if status:
                message = {"coord": [((int(self.coord[0]), int(self.coord[1])),
                                      (int(self.coord[0] + self.coord[2]), int(self.coord[1] + self.coord[3])))]}
                if self.draw_coord:
                    p1 = (int(self.coord[0]), int(self.coord[1]))
                    p2 = (int(self.coord[0] + self.coord[2]), int(self.coord[1] + self.coord[3]))
                    cv2.rectangle(frame, p1, p2, (255, 0, 0), 2, 1)
                    message['msg'] = "is tracking"
        return MessageItem(frame, message)


def TrackerDemo():
    # 初始化视频捕获设备
    # gVideoDevice = cv2.VideoCapture("./video.avi")
    gVideoDevice = cv2.VideoCapture(0)
    if not gVideoDevice.isOpened():
        print('open video failed')
        return
    else:
        print('open video succeeded')

    # 选择 框选帧
    print("按 enter 为当前帧,否则继续下一帧")
    while True:
        gCapStatus, gFrame = gVideoDevice.read()
        cv2.imshow("pick frame", gFrame)
        k = cv2.waitKey()
        if k == 13:
            break

    # 框选感兴趣区域region of interest
    cv2.destroyWindow("pick frame")
    gROI = cv2.selectROI("ROI frame", gFrame, False)
    if (not gROI):
        print("空框选,退出")
        quit()

    # 初始化追踪器
    gTracker = Tracker(tracker_type="KCF")
    gTracker.initWorking(gFrame, gROI)

    # 循环帧读取,开始跟踪
    while True:
        gCapStatus, gFrame = gVideoDevice.read()
        if (gCapStatus):
            # 展示跟踪图片
            _item = gTracker.track(gFrame)
            cv2.imshow("track result", _item.getFrame())

            if _item.getMessage():
                # 打印跟踪数据
                print(_item.getMessage())
            else:
                # 丢失,重新用初始ROI初始
                print("丢失,重新使用初始ROI开始")
                gTracker = Tracker(tracker_type="KCF")
                gTracker.initWorking(gFrame, gROI)

            _key = cv2.waitKey(1) & 0xFF
            if (_key == ord('q')) | (_key == 27):
                break
            if (_key == ord('r')):
                # 用户请求用初始ROI
                print("用户请求用初始ROI")
                gTracker = Tracker(tracker_type="KCF")
                gTracker.initWorking(gFrame, gROI)

        else:
            print("捕获帧失败")
            quit()

4.Meanshift算法跟踪视频中对象

后续我还简单了解到跟踪视频中对象的Meanshift和Camshift算法,也是不错的跟踪方法,而且更好理解。

Meanshift(均值漂移)是一种在一组数据的密度分布中寻找局部极值的稳定的方法。Meanshift不仅可以用于图像滤波,视频跟踪,还可以用于图像分割。
通过给出一组多维数据点,其维数是(x,y,r,g,b),均值漂移可以用一个窗口扫描空间来找到数据密度最大的区域,可以理解为数据分布最集中的区域。

Meanshift背后的直觉很简单,假设你有点的集合。(它可以是像素分布,例如直方图反投影)。你会得到一个小窗口(可能是一个圆形),并且必须将该窗口移到最大像素密度(或最大点数)的区域。

更多可以了解 滤波与模糊操作python opencv入门 Meanshift 和 Camshift 算法(40)

def demo4():
    cap = cv2.VideoCapture('../实验10 视频处理/video.avi')
    # 视频的第一帧
    ret, frame = cap.read()
    # 设置窗口的初始位置
    x, y, w, h = 510, 286, 50, 50
    track_window = (x, y, w, h)
    # 设置初始ROI来追踪
    roi = frame[y:y + h, x:x + w]
    hsv_roi = cv2.cvtColor(roi, cv2.COLOR_BGR2HSV)
    mask = cv2.inRange(hsv_roi, np.array((0., 60., 32.)), np.array((180., 255., 255.)))
    roi_hist = cv2.calcHist([hsv_roi], [0], mask, [180], [0, 180])
    cv2.normalize(roi_hist, roi_hist, 0, 255, cv2.NORM_MINMAX)
    # 设置终止条件,可以是10次迭代,也可以至少移动1 pt
    term_crit = (cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 1)
    while (1):
        ret, frame = cap.read()
        if ret == True:
            dst = cv2.pyrMeanShiftFiltering(frame, 10, 50)  # 均值迁移
            # cv2.namedWindow("meanshift_demo", cv2.WINDOW_NORMAL)
            cv2.imshow("meanshift_demo", dst)

            hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
            dst = cv2.calcBackProject([hsv], [0], roi_hist, [0, 180], 1)
            # 应用meanshift来获取新位置
            ret, track_window = cv2.meanShift(dst, track_window, term_crit)
            # 在图像上绘制
            x, y, w, h = track_window
            print("window: ", track_window)
            img2 = cv2.rectangle(frame, (x, y), (x + w, y + h), 255, 2)
            cv2.imshow('img2', img2)
            k = cv2.waitKey(30) & 0xff
            if k == 27:
                break
        else:
            break

均值迁移 pyrMeanShiftFiltering

Meanshift跟踪对象

有关Python-OpenCV视频帧间差分、高斯混合建模、背景差分提取前景目标标示轮廓、KCF目标跟踪、Meanshift算法跟踪的更多相关文章

  1. ruby-on-rails - 使用 Sublime Text 3 突出显示 HTML 背景语法中的 ERB? - 2

    所以我在关注Railscast,我注意到在html.erb文件中,ruby代码有一个微弱的背景高亮效果,以区别于其他代码HTML文档。我知道Ryan使用TextMate。我正在使用SublimeText3。我怎样才能达到同样的效果?谢谢! 最佳答案 为SublimeText安装ERB包。假设您安装了SublimeText包管理器*,只需点击cmd+shift+P即可获得命令菜单,然后键入installpackage并选择PackageControl:InstallPackage获取包管理器菜单。在该菜单中,键入ERB并在看到包时选择

  2. ruby-on-rails - 使用 Rmagick 或 ImageMagick 在背景上放置标题 - 2

    我有一张背景图片,我想在其中添加一个文本框。我想弄清楚如何将标题放置在其顶部的正确位置。(我使用标题是因为我需要自动换行功能)。现在,我只能让文本显示在左上角,但我需要能够手动定位它的开始位置。require'RMagick'require'Pry'includeMagicktext="Loremipsumdolorsitamet"img=ImageList.new('template001.jpg')img 最佳答案 这是使用convert的ImageMagick命令行的答案。如果你想在Rmagick中使用这个方法,你必须自己移植

  3. 区块链之加解密算法&数字证书 - 2

    目录一.加解密算法数字签名对称加密DES(DataEncryptionStandard)3DES(TripleDES)AES(AdvancedEncryptionStandard)RSA加密法DSA(DigitalSignatureAlgorithm)ECC(EllipticCurvesCryptography)非对称加密签名与加密过程非对称加密的应用对称加密与非对称加密的结合二.数字证书图解一.加解密算法加密简单而言就是通过一种算法将明文信息转换成密文信息,信息的的接收方能够通过密钥对密文信息进行解密获得明文信息的过程。根据加解密的密钥是否相同,算法可以分为对称加密、非对称加密、对称加密和非

  4. 100个python算法超详细讲解:画直线 - 2

    1.问题描述使用Python的turtle(海龟绘图)模块提供的函数绘制直线。2.问题分析一幅复杂的图形通常都可以由点、直线、三角形、矩形、平行四边形、圆、椭圆和圆弧等基本图形组成。其中的三角形、矩形、平行四边形又可以由直线组成,而直线又是由两个点确定的。我们使用Python的turtle模块所提供的函数来绘制直线。在使用之前我们先介绍一下turtle模块的相关知识点。turtle模块提供面向对象和面向过程两种形式的海龟绘图基本组件。面向对象的接口类如下:1)TurtleScreen类:定义图形窗口作为绘图海龟的运动场。它的构造器需要一个tkinter.Canvas或ScrolledCanva

  5. ruby - 如何从 URL 中删除 Google 跟踪参数 (UTM)? - 2

    我有一堆要清理的URL。它们都包含UTM参数,在这种情况下不是必需的,或者是有害的。示例:http://houseofbuttons.tumblr.com/post/22326009438?utm_source=feedburner&utm_medium=feed&utm_campaign=Feed%3A+HouseOfButtons+%28House+of+Buttons%29所有可能的参数都以utm_开头。如何使用ruby​​脚本/结构轻松删除它们而不破坏其他潜在的“好”URL参数? 最佳答案 您可以将正则表达式应用于url以清

  6. ruby - 在 Ruby 中实现 Luhn 算法 - 2

    我一直在尝试用Ruby实现Luhn算法。我一直在执行以下步骤:该公式根据其包含的校验位验证数字,该校验位通常附加到部分帐号以生成完整帐号。此帐号必须通过以下测试:从最右边的校验位开始向左移动,每第二个数字的值加倍。将乘积的数字(例如,10=1+0=1、14=1+4=5)与原始数字的未加倍数字相加。如果总模10等于0(如果总和以零结尾),则根据Luhn公式该数字有效;否则无效。http://en.wikipedia.org/wiki/Luhn_algorithm这是我想出的:defvalidCreditCard(cardNumber)sum=0nums=cardNumber.to_s.s

  7. Ruby 斐波那契算法 - 2

    下面是我写的一个计算斐波那契数列中的值的方法:deffib(n)ifn==0return0endifn==1return1endifn>=2returnfib(n-1)+(fib(n-2))endend它工作到n=14,但在那之后我收到一条消息说程序响应时间太长(我正在使用repl.it)。有人知道为什么会这样吗? 最佳答案 Naivefibonacci进行了大量的重复计算-在fib(14)fib(4)中计算了很多次。您可以将内存添加到您的算法中以使其更快:deffib(n,memo={})ifn==0||n==1returnnen

  8. ruby-on-rails - Rails Asset Pipeline 更好的错误或堆栈跟踪 - 2

    刚刚将应用程序从rails3.0.9升级到3.2.1,当我运行bundleexecrakeassets:precompile时出现错误,这很好,但是回溯没有告诉我在哪里语法问题来self的css或scss文件。我尝试对“0ee5c0e69c92af0”进行greping,但该字符串没有出现在我的项目中。bundleexecrakeassets:precompile:allRAILS_ENV=productionRAILS_GROUPS=assets--trace**Invokeassets:precompile:all(first_time)**Executeassets:precom

  9. ruby-on-rails - Rails add_index 算法 : :concurrently still causes database lock up during migration - 2

    为了防止在迁移到生产站点期间出现数据库事务错误,我们遵循了https://github.com/LendingHome/zero_downtime_migrations中列出的建议。(具体由https://robots.thoughtbot.com/how-to-create-postgres-indexes-concurrently-in概述),但在特别大的表上创建索引期间,即使是索引创建的“并发”方法也会锁定表并导致该表上的任何ActiveRecord创建或更新导致各自的事务失败有PG::InFailedSqlTransaction异常。下面是我们运行Rails4.2(使用Acti

  10. ruby-on-rails - 在所有页面上使用 Prawn 的背景图像 - 2

    我在View中有这段代码prawn_document(:page_size=>"A4",:top_margin=>80,:bottom_margin=>40,:background=>"public/uploads/1.png")do|pdf|creation_date=Time.now.strftime('%d-%m-%Y')posts=@posts.eachdo|post|pdf.pad(10)dopdf.textpost.titlepdf.textpost.textendendpdf.page_count.timesdo|i|pdf.go_to_page(i+1)pdf.draw

随机推荐