精华内容
下载资源
问答
  • 光流检测
    千次阅读 多人点赞
    2021-03-19 14:30:45

    1. 引言

    RAFT: Recurrent All-Pairs Field Transforms for Optical Flow 是 2D 光流检测领域内里具有里程碑意义的一个工作。本文模型无论是性能效果,还是参数量、运行效率,相比之前 state-of-the-art 方法都有了很大的提升。github上放出的代码也写得非常清晰,为了方便更多小伙伴了解这篇工作,下面结合原文中画的模型图对主要代码进行解释。


    2. 整体框架介绍

    RAFT框架图
    在这里插入图片描述

    输入为连续的两帧图像,Feature Encoder(权值共享)提取两张图像的特征,并构建多尺度 4D Correlation Volumes 相关性查找表;Context Encoder 单独提取第一帧图的特征,并沿 channel 维度将其 split 成两部分,一部分作为 GRU 的初始隐状态,在后续迭代过程中会不断更新,另一部分用于和任意迭代过程中的光流图以及相关性图结合,作为 GRU(不懂的话请先了解一下 GRU 的大致原理)的一般输入;设置初始光流图为全 0,通过一系列的 GRU 更新光流,最后使用一个特殊的上采样操作得到原始分辨率的光流图(在前面提取特征过程中会将图像下采样 8 倍)。


    3. 代码讲解

    3.1. RAFT 核心代码

    下面是 RAFT 的核心代码(以 4.8M 参数量版本模型为例,删减了不重要的部分),直接来看 forward() 函数,走一遍流程。后续章节将具体讲解其中一些此处未展示的重要代码。

    class RAFT(nn.Module):
        def __init__(self, args):
            super(RAFT, self).__init__()
            self.args = args
            self.hidden_dim = hdim = 128
            self.context_dim = cdim = 128
            args.corr_levels = 4
            args.corr_radius = 4
            self.args.dropout = 0
            self.args.alternate_corr = False
    
            # feature network, context network, and update block
            self.fnet = BasicEncoder(output_dim=256, norm_fn='instance', dropout=args.dropout)
            self.cnet = BasicEncoder(output_dim=hdim+cdim, norm_fn='batch', dropout=args.dropout)
            self.update_block = BasicUpdateBlock(self.args, hidden_dim=hdim)
    
        def initialize_flow(self, img):
            ...
    
        def upsample_flow(self, flow, mask):
            ...
    
        def forward(self, image1, image2, iters=12, flow_init=None, upsample=True, test_mode=False):
            """ Estimate optical flow between pair of frames """
    
    		# step 1:预处理
            image1 = 2 * (image1 / 255.0) - 1.0  # 图像归一化
            image2 = 2 * (image2 / 255.0) - 1.0  # 图像归一化
    
            image1 = image1.contiguous()
            image2 = image2.contiguous()
    
            hdim = self.hidden_dim
            cdim = self.context_dim
    
            # step 2:Feature Encoder 提取两图特征(权值共享)
            with autocast(enabled=self.args.mixed_precision):
                fmap1, fmap2 = self.fnet([image1, image2])
    
            fmap1 = fmap1.float()
            fmap2 = fmap2.float()
    
    		# step 3:初始化 Correlation Volumes 相关性查找表
            corr_fn = CorrBlock(fmap1, fmap2, radius=self.args.corr_radius)
    
            # step 4:Context Encoder 提取第一帧图特征
            with autocast(enabled=self.args.mixed_precision):
                cnet = self.cnet(image1)
                net, inp = torch.split(cnet, [hdim, cdim], dim=1)  # net 为 GRU 的隐状态,inp 后续与其他特征结合作为 GRU 的一般输入
                net = torch.tanh(net)
                inp = torch.relu(inp)
    
    		# step 5:更新光流
    		# 初始化光流的坐标信息,coords0 为初始时刻的坐标,coords1 为当前迭代的坐标,此处两坐标数值相等
            coords0, coords1 = self.initialize_flow(image1)
    
            flow_predictions = []
            for itr in range(iters):
                coords1 = coords1.detach()
                corr = corr_fn(coords1)  # 从相关性查找表中获取当前坐标的对应特征
    
                flow = coords1 - coords0  # 计算当前迭代的光流
                with autocast(enabled=self.args.mixed_precision):
                    net, up_mask, delta_flow = self.update_block(net, inp, corr, flow)  # GRU 获取更新的隐状态,用于上采样的 mask,以及光流残差
    
                # F(t+1) = F(t) + \Delta(t)
                coords1 = coords1 + delta_flow  # 更新光流
    
                # step 6:上采样光流(此处为了训练网络,对每次迭代的光流都进行了上采样,实际 inference 时,只需要保留最后一次迭代后的上采样)
                flow_up = self.upsample_flow(coords1 - coords0, up_mask)
    
                flow_predictions.append(flow_up)
    
            if test_mode:
                return coords1 - coords0, flow_up  # inference 时仅使用上采样的光流 flow_up
    
            return flow_predictions
    

    总结流程:
    step 1:预处理
    step 2:Feature Encoder 提取两图特征(网络很简单,不做具体讲解)
    step 3:初始化 Correlation Volumes 相关性查找表(讲解见 Sec. 3.2.)
    step 4:Context Encoder 提取第一帧图特征(网络同 Feature Encoder)
    step 5:更新光流(讲解见 Sec. 3.3.,从相关性查找表中获取当前坐标对应特征的步骤放在 Sec. 3.2. 中讲解)
    step 6:上采样光流(讲解见 Sec. 3.4.)

    3.2. Correlation Volumes 相关性查找表

    在 step 3 初始化相关性查找表时,调用 __init__() 函数;在 step 5 查找对应特征时,调用 __call__() 函数。

    class CorrBlock:
        def __init__(self, fmap1, fmap2, num_levels=4, radius=4):
            self.num_levels = num_levels
            self.radius = radius
            self.corr_pyramid = []
    
            # all pairs correlation
            corr = CorrBlock.corr(fmap1, fmap2)  # 对两图特征使用矩阵乘法得到相关性查找表
    
            batch, h1, w1, dim, h2, w2 = corr.shape
            corr = corr.reshape(batch*h1*w1, dim, h2, w2)  # (b,h,w,1,h,w) -> (bhw,1,h,w)
    
            self.corr_pyramid.append(corr)
            for i in range(self.num_levels-1):
                corr = F.avg_pool2d(corr, 2, stride=2)  # 使用平均 pooling 的方式获得多尺度查找表
                self.corr_pyramid.append(corr)
    
        def __call__(self, coords):
            r = self.radius
            coords = coords.permute(0, 2, 3, 1)  # (b,2,h,w) -> (b,h,w,2) 当前坐标,包含x和y两个方向,由 meshgrid() 函数得到,细节见 Sec. 3.3.
            batch, h1, w1, _ = coords.shape
    
            out_pyramid = []
            for i in range(self.num_levels):
                corr = self.corr_pyramid[i]  # (bhw,1,h,w) 某一尺度的相关性查找表
                dx = torch.linspace(-r, r, 2*r+1)  # (2r+1) x方向的相对位置查找范围
                dy = torch.linspace(-r, r, 2*r+1)  # (2r+1) y方向的相对位置查找范围
                delta = torch.stack(torch.meshgrid(dy, dx), axis=-1).to(coords.device)  # 查找窗 (2r+1,2r+1,2)
    
                centroid_lvl = coords.reshape(batch*h1*w1, 1, 1, 2) / 2**i  # (b,h,w,2) -> (bhw,1,1,2) 某一尺度下的坐标
                delta_lvl = delta.view(1, 2*r+1, 2*r+1, 2)  # (2r+1,2r+1,2) -> (1,2r+1,2r+1,2) 查找窗
                coords_lvl = centroid_lvl + delta_lvl  # (bhw,1,1,2) + (1,2r+1,2r+1,2) -> (bhw,2r+1,2r+1,2) 可以形象理解为:对于 bhw 这么多待查找的点,每一个点需要搜索 (2r+1)*(2r+1) 邻域范围内的其他点,每个点包含 x 和 y 两个坐标值
    
                corr = bilinear_sampler(corr, coords_lvl)  # (bhw,1,2r+1,2r+1) 在查找表上搜索每个点的邻域特征,获得相关性图
                corr = corr.view(batch, h1, w1, -1) # (bhw,1,2r+1,2r+1) -> (b,h,w,(2r+1)*(2r+1))
                out_pyramid.append(corr)
    
            out = torch.cat(out_pyramid, dim=-1)
            return out.permute(0, 3, 1, 2).contiguous().float()
    
        @staticmethod
        def corr(fmap1, fmap2):
            batch, dim, ht, wd = fmap1.shape
            fmap1 = fmap1.view(batch, dim, ht*wd)  # 第一帧图特征 (b,c,h,w) -> (b,c,hw)
            fmap2 = fmap2.view(batch, dim, ht*wd)  # 第二帧图特征 (b,c,h,w) -> (b,c,hw)
    
            corr = torch.matmul(fmap1.transpose(1,2), fmap2)  # (b,hw,c) * (b,c,hw) -> (b,hw,hw) 后两维使用矩阵乘法,第一维由广播得到
            corr = corr.view(batch, ht, wd, 1, ht, wd)  # (b,hw,hw) -> (b,h,w,1,h,w)
            return corr / torch.sqrt(torch.tensor(dim).float())  # 这里除的意义不是很明确,应该有一定的数学意义,有了解的小伙伴可以在评论区补充一下(不影响理解)
    
    def bilinear_sampler(img, coords, mode='bilinear', mask=False):
        """ Wrapper for grid_sample, uses pixel coordinates """
        H, W = img.shape[-2:]
        xgrid, ygrid = coords.split([1,1], dim=-1)  # (bhw,2r+1,2r+1,1)
        xgrid = 2*xgrid/(W-1) - 1  # x方向归一化
        ygrid = 2*ygrid/(H-1) - 1  # y方向归一化
    
        grid = torch.cat([xgrid, ygrid], dim=-1)  # (bhw,2r+1,2r+1,2)
        img = F.grid_sample(img, grid, align_corners=True)  # img: (bhw,1,h,w) -> (bhw,1,2r+1,2r+1) 根据搜索范围 grid 在查找表 img 中采样对应特征
    
        return img
    

    3.3. GRU 更新光流

    光流初始化。

    class RAFT(nn.Module):
    	def initialize_flow(self, img):
            """ Flow is represented as difference between two coordinate grids flow = coords1 - coords0"""
            N, C, H, W = img.shape
            coords0 = coords_grid(N, H//8, W//8).to(img.device)  # (b,2,h,w)
            coords1 = coords_grid(N, H//8, W//8).to(img.device)  # (b,2,h,w)
    
            # optical flow computed as difference: flow = coords1 - coords0
            return coords0, coords1
    
    def coords_grid(batch, ht, wd):
        coords = torch.meshgrid(torch.arange(ht), torch.arange(wd))  # (h,w),(h,w)
        coords = torch.stack(coords[::-1], dim=0).float()  # (2,h,w)
        return coords[None].repeat(batch, 1, 1, 1)  # (b,2,h,w)
    

    GRU 更新光流,BasicMotionEncoder 和 FlowHead 的网络结构相对简单,不展开解释。

    class BasicUpdateBlock(nn.Module):
        def __init__(self, args, hidden_dim=128, input_dim=128):
            super(BasicUpdateBlock, self).__init__()
            self.args = args
            self.encoder = BasicMotionEncoder(args)
            self.gru = SepConvGRU(hidden_dim=hidden_dim, input_dim=128+hidden_dim)
            self.flow_head = FlowHead(hidden_dim, hidden_dim=256)
    
            self.mask = nn.Sequential(
                nn.Conv2d(128, 256, 3, padding=1),
                nn.ReLU(inplace=True),
                nn.Conv2d(256, 64*9, 1, padding=0))
    
        def forward(self, net, inp, corr, flow, upsample=True):
            motion_features = self.encoder(flow, corr)  # 结合光流和相关性图提取特征
            inp = torch.cat([inp, motion_features], dim=1)  # 连接 Context Encoder 提取的特征和上面提取的特征
    
            net = self.gru(net, inp)  # GRU 迭代,更新隐状态 net
            delta_flow = self.flow_head(net)  # 由隐状态得到光流残差
    
            # scale mask to balence gradients
            mask = .25 * self.mask(net)  # 由隐状态得到上采样 mask
            return net, mask, delta_flow
    
    class SepConvGRU(nn.Module):
        def __init__(self, hidden_dim=128, input_dim=192+128):
            super(SepConvGRU, self).__init__()
            self.convz1 = nn.Conv2d(hidden_dim+input_dim, hidden_dim, (1,5), padding=(0,2))
            self.convr1 = nn.Conv2d(hidden_dim+input_dim, hidden_dim, (1,5), padding=(0,2))
            self.convq1 = nn.Conv2d(hidden_dim+input_dim, hidden_dim, (1,5), padding=(0,2))
    
            self.convz2 = nn.Conv2d(hidden_dim+input_dim, hidden_dim, (5,1), padding=(2,0))
            self.convr2 = nn.Conv2d(hidden_dim+input_dim, hidden_dim, (5,1), padding=(2,0))
            self.convq2 = nn.Conv2d(hidden_dim+input_dim, hidden_dim, (5,1), padding=(2,0))
    
    
        def forward(self, h, x):
        	# 将 3x3 卷积替换成 1x5 和 5x1 的两次卷积,在不提高参数量的情况下增大感受野,下面使用的数学计算见 GRU 公式
            # horizontal
            hx = torch.cat([h, x], dim=1)
            z = torch.sigmoid(self.convz1(hx))
            r = torch.sigmoid(self.convr1(hx))
            q = torch.tanh(self.convq1(torch.cat([r*h, x], dim=1)))        
            h = (1-z) * h + z * q
    
            # vertical
            hx = torch.cat([h, x], dim=1)
            z = torch.sigmoid(self.convz2(hx))
            r = torch.sigmoid(self.convr2(hx))
            q = torch.tanh(self.convq2(torch.cat([r*h, x], dim=1)))       
            h = (1-z) * h + z * q
    
            return h
    

    在这里插入图片描述

    3.4. 上采样光流

    8 倍上采样的过程可以简单描述为:每个像素点都要扩展成 8*8 个像素点,具体方式是每个扩展的像素点由原像素点及周围的 8 邻域像素点(总共 9 个像素点)加权得到,而权重则是由网络生成的,因此权重矩阵的参数量为 b*c*h*w*9*8*8(x,y 坐标使用相同的权重,不用再乘 2)。实验证明这种上采样对于光流任务非常有效,在物体边缘能够获得如丝般顺滑的效果。感兴趣的小伙伴也可以在其他需要上采样的任务中进行尝试,或许会有意想不到的效果。

    class RAFT(nn.Module):
        def upsample_flow(self, flow, mask):
            """ Upsample flow field [H/8, W/8, 2] -> [H, W, 2] using convex combination """
            N, _, H, W = flow.shape
            mask = mask.view(N, 1, 9, 8, 8, H, W)  # (b,9*8*8,h,w) -> (b,1,9,8,8,h,w)
            mask = torch.softmax(mask, dim=2)  # 权重归一化
    
            up_flow = F.unfold(8 * flow, [3,3], padding=1)  # (b,2,h,w) -> (b,2*3*3,h*w)
            # 提取每个像素点以及周围的 8 邻域像素点特征(总共 9 个像素点)重新排列到 channel 维度上
            # 这里 8*flow 的原因是上采样后图像的尺度变大了,为了匹配尺度增大的像素坐标,光流也要按同样的倍率(8 倍)上采样
            up_flow = up_flow.view(N, 2, 9, 1, 1, H, W)  # (b,2*3*3,h*w) -> (b,2,9,1,1,h,w)
    
            up_flow = torch.sum(mask * up_flow, dim=2)  # (b,1,9,8,8,h,w) * (b,2,9,1,1,h,w) -> (b,2,9,8,8,h,w) ->(sum) (b,2,8,8,h,w)
            up_flow = up_flow.permute(0, 1, 4, 2, 5, 3)  # (b,2,8,8,h,w) -> (b,2,h,8,w,8)
            return up_flow.reshape(N, 2, 8*H, 8*W)  # (b,2,h,8,w,8) -> (b,2,8h,8w)
    
    更多相关内容
  • 基于光流算法,检测运动物体的光流场,并给出每个像素点的运动状态。程序里面还给出了相关例子。
  • opencv光流检测(一)

    2021-07-20 22:05:57
    Lucas–Kanade光流算法是一种两帧差分的光流估计算法。它由Bruce D. Lucas 和 Takeo Kanade提出 [1]。 LK光流法有三个假设条件: 1、亮度恒定:一个像素点随着时间的变化,其亮度值(像素灰度值)是恒定不变的。这...

    转自:https://www.cnblogs.com/riddick/p/10586662.html

            Lucas–Kanade光流算法是一种两帧差分的光流估计算法。它由Bruce D. Lucas 和 Takeo Kanade提出 [1]。

    LK光流法有三个假设条件:

            1、亮度恒定:一个像素点随着时间的变化,其亮度值(像素灰度值)是恒定不变的。这是光流法的基本设定。所有光流法都必须满足。

            2、小运动: 时间的变化不会引起位置的剧烈变化。这样才能利用相邻帧之间的位置变化引起的灰度值变化,去求取灰度对位置的偏导数。所有光流法必须满足。

            3、空间一致:即前一帧中相邻像素点在后一帧中也是相邻的。这是LK光流法独有的假定。因为为了求取x,y方向的速度,需要建立多个方程联立求解。而空间一致假设就可以利用邻域n个像素点来建立n个方程。 

    LK光流算法原理的数学推导:

            假设前一帧时间为t, 后一帧时间为t+δt。则前一帧I的像素点I(x, y, z, t)在后一帧中的位置为I(x+δx, y+δy, z+δz, t+δt )。

    ① 根据亮度恒定假设:

     ② 根据小运动假设, 将上式右侧用泰勒级数展开:

            H.O.T是泰勒级数展开式的高阶项,小运动情况下可以 忽略为0.

    ③ 根据上面两个公式可以得到:

            或者下面的公式: 

             而对于二维图像而言,只需要考虑x, y, t即可,其中Ix,Iy  It分别为图像在(x, y, t)方向的差分,写为如下形式:  

             ④ 现在有两个未知数,只有一个方程。因此用到第三个假设:即空间一致性假设,LK算法是利用3x3窗口内的9个像素点建立9个方程。简写为下面的形式:  

             写成矩阵形式: 

             当然两个未知数,9个方程,这是一个超定问题,采用最小二乘法解决:

             写成如下形式:

             根据上式通过累加邻域像素点在三个维度的偏导数并做矩阵运算,即可算出该点的光流(Vx,Vy)。

    展开全文
  • oflow.js-JavaScript中的光流检测当我旅行并且飞行很长时间时,我只是为了好玩而制作了这个小玩具。 该库使您可以检测视频中的光流。 这是一个,可让您控制球并查看视频每个区域中的运动。 这款小型也是在飞机上创建...
  • 针对传统的目标跟踪算法需要人工选择目标且不能较好地处理目标的尺度变化问题,提出融合光流检测与模板匹配的目标跟踪算法。首先通过结合光流信息与图像分割结果从视频中自动地检测和提取运动目标,实现基于检测的...
  • 本文档介绍了对图像进行阴影检测与处理,对相关研究人员具有一定的借鉴意义
  • opencv光流检测(三)

    2021-07-20 22:16:48
    1、定义 空间运动物体在观察成像平面上的像素运动的瞬时速度,是利用图像序列中像素在...而通俗来讲,把图像中的每一个点的瞬时速度和方向找出来就是光流。 2、光流有什么用 通过光流判断物体距离我们的远近。 ...

    转自:https://blog.csdn.net/gh_home/article/details/51502933

    1、定义

            空间运动物体在观察成像平面上的像素运动的瞬时速度,是利用图像序列中像素在时间域上的变化以及相邻帧之间的相关性来找到上一帧跟当前帧之间存在的对应关系,从而计算出相邻帧之间物体的运动信息的一种方法。也就是说,由空间域到图像平面的投影。而通俗来讲,把图像中的每一个点的瞬时速度和方向找出来就是光流。

    2、光流有什么用

            通过光流判断物体距离我们的远近。 一般而言,远景的物体相对来说光流较小,而近景物体光流较大,尤其是动态场景中的运动物体。

    3、如何计算光流

            光流法的第一层推导。

    3.1、计算光流的几个假设

            1)相邻帧之间的亮度恒定;

            2)相邻视频帧的取帧时间连续,或者,相邻帧之间物体的运动比较“微小”;

            3)保持空间一致性;即,同一子图像的像素点具有相同运动。

    3.2、光流的基本方程

            假设位于(x,y,t)的像素的亮度是 I(x,y,t), 该像素在两个图像帧之间移动了Δx、Δy、Δt,由泰勒展开式我们可以得到:

             由假设1可知:

            故有:

             这个方程便是我们计算光流的基本方程。

            其中Δx/Δt和Δy/Δt为该点在x和y方向上的瞬时速度,也就是光流。∂I/∂t代表像素点在时间上的变化。可以通过在像素位置(x,y)处两张图像之间的像素差。

            但是这个我们要计算一个点的光流,有两个未知数,只有一个方程,该如何解呢?针对这个问题,有很多解决方法:

            - 基于梯度的方法

            - 基于匹配的方法

            - 基于能量的方法

            - 基于相位的方法

            下面我们介绍一个很经典的算法, LK光流提取算法。

    4、LK-光流算法

            基本假设:光流在像素点的邻域是一个常数。

            然后使用最小二乘法对邻域中的所有像素点求解基本的光流方程。因为方程个数已经超过了为未知量个数,假定像素位置p周围的领域像素由q1q2q3...,那么就有:

             将其写为矩阵的形式,则有:

             然后通过伪逆的形式解最小二乘就可以求解到光流v:

     L-K 光流算法的缺点:

            我们首先要设置一个邻域的窗口,之后计算光流。当窗口较大时,光流计算更鲁棒;当窗口较小时,光流计算更正确。

            原因在于,当图像中每一个部分的运动都不一致的时候,如果开的窗口过大,很容易违背假设:窗口(邻域)内的所有点光流一致,这可能与实际不一致,所以窗口小,包含的像素少,更精确些。当运动较为剧烈的时候,无法实现光流的基本假设,即:I(x+Δx,y+Δy,t+Δt)=I(x,y,t),因为Δx、Δy而我们的领域小于实际运动的位移,所以当我们的领域加大的时候,算法更鲁棒。

            如何解决这一问题,我们采用了金字塔的方法,即窗口固定,将图像生成金字塔,在每一层金字塔上都用同一个大小的窗口来进行光流计算。那么在图像大小较小的时候,窗口显得较大,此时的光流可以跟踪速度较快的目标,而在原始图像的时候,光流窗口相对较小,得到的光流就更准确。这是一个由粗到精的过程。

            以上是光流的第一层推导。

    5、光流法的第二层推导

            光流法的基本方程:

              其中I(x,y)指原图,J(x,y)指参考图。dx、dy为在(ux,uy)处的光流向量。wx、wy为基于坐标点(ux,uy)的搜索窗口(半径)。

            默认情况下,我们认为dx、dy应该小于(ux,uy),也就是在I图中的一个点对应J图时,应该不会超过(ux,uy)这个窗口的。那么基于这个假设就出现了上面提到的关于窗口大小与光流准确性及鲁棒性带来的矛盾。下面讲如何利用图像金字塔来解决这个矛盾。

    5.1、如何建立图像金字塔

             按照此公式建立,其中ILIL代笔第L层的图像金字塔。初始情况下使I0I0为原始图像。按照上述公司,由第L-1层图像到第L层图像是通过以下步骤:

            1)对L-1层图像进行低通滤波,滤波模板如下:

             2)将滤波后的图像抽取偶数行偶数列的像素重组为第L层图像,因此最后L-1层的图像大小是L层的一半。

            但是对于图像滤波时边缘像素怎么办呢?由于模板的半径是1,那么就首先将原始图像的像素扩展一圈,宽度为一个像素,扩展出来的像素值与其相近的原始图像边缘像素一致,即:

     5.2、根据金字塔逐层计算光流

            首先引入两个光流量:剩余光流量dL和猜测光流量gL。

            核心思想在于:对第L层的光流,是通过由第L-1层的精确光流得到的导出光流量gL以及这一层以gL为基准再次进行匹配微调后得到的剩余光流量dL。即第L层的光流kL为:

             gL由上一层的精确光流猜测得来:

             基于的事实是:既然第L-1到第L层是降采样一半,那么光流量也应该减一半。

            但是这样粗略的估算是不准确的,所以第L层还需要通过基本的光流方程得出剩余光流量 dL去修正这个值,使得第L层的光流更精确.怎么得到 dL,就是基本的光流方程:

             这里可以发现,关于最终光流的搜索范围事实上是变大了的,如图所示:

             所有都归功于上一层光流提供的先验信息使得光流跳出了wx、wy的约束,可以到更远的地方,这样就解决了上面提到的关于窗口大小的矛盾。事实上,对于在原始图像(L=0)上开一个d*d的搜索框,在金字塔的情况下,总的搜索半径可以达到:

             对于一般的视频做光流而言,采用3*3的窗口,图像金字塔的层数有2-3层就够了。因为覆盖范围以及很广了,最后得到的原始图像的光流按照一下公式计算而来:

    6、计算光流基本方程

            首先光流基本方程是一个最优化的问题,可以通过求导求其最优解:

             对基本光流方程求导:

              对B(x+vx,y+vy)进行一阶泰勒展开:

             计算两张图像的差:

             计算图像在wx、wy邻域内的差分,这里可以采用Sharr算子代表差分:

            这样就算出了LK光流。但是这一光流的假设是泰勒一阶展开足够精确。那么为了进一步使得计算出来的光流更加精确,我们采用牛顿逼近法进行迭代。

            核心思想在于:在上一次计算出LK光流后,将得到的光流vk用来更新参考图像的像素位置(平移),之后再次计算光流。重新更新vk。如此循环直到迭代次数达到上限或者误差量ε小于设定的阈值时停止。 按照上述情况来看,停止迭代后,A图和B图两张图在(x,y)点处像素完全重合的。

            这里是最终迭代结束后的精确光流量:

     7、总结算法

    展开全文
  • 基于光流法的光流检测matlab算法,代码很详细.7z
  • 基于深度卷积神经网络的运动目标光流检测方法.pdf
  • 面向非结构化道路的特征光流检测,石朝侠,王燕清,道路环境中的可通行区域视觉检测为智能车辆自主导航提供了廉价的解决方案。非结构化道路局部区域的灰度不一致性给传统图像分割方
  • 程序1 获得单个图像的光流厂,光流矢量图 光流X,Y轴的数据结果,光流检测结果 程序2 可以对视频采集,并且识别每一个视频前后帧的不同得到数据结果 程序三 对视频采集,可以获得其光流厂的具体数据,并且获得...

    多种方法实现光流法,光流法检测目标检测 和目标追踪的 matlab  b 编程实现

    程序1 获得单个图像的光流厂,光流矢量图 光流X,Y轴的数据结果,光流检测结果

    程序2  可以对视频采集,并且识别每一个视频前后帧的不同得到数据结果

    程序三  对视频采集,可以获得其光流厂的具体数据,并且获得矢量图

    1950年,Gibson首先提出了光流的概念,所谓光流就是指图像表现运动的速度。物体在运动的时候之所以能被人眼发现,就是因为当物体运动时,会在人的视网膜上形成一系列的连续变化的图像,这些变化信息在不同时间,不断的流过眼睛视网膜,就好像一种光流过一样,故称之为光流。

    光流法检测运动物体的原理:首先给图像中每个像素点赋予一个速度矢量(光流),这样就形成了光流场。如果图像中没有运动物体,光流场连续均匀,如果有运动物体,运动物体的光流和图像的光流不同,光流场不再连续均匀。从而可以检测出运动物体及位置。

    应用背景:

    来检测目标检测 来做目标追踪的可以  相关性来找到上一帧跟当前帧之间存在的对应关系,从而计算出相邻帧之间物体的运动信息的一种方法。可以用来检测运动抖动物体

    关键技术:

    故称之为光流(optical flow)。

    编程处理中:实现了光溜数据的提取 ,还有得到有效的因素,在x洲和y洲中,都可以得到较大的较广泛的实现。

    程序1 获得单个图像的光流厂,光流矢量图 光流X,Y轴的数据结果,光流检测结果

     这里展示了光流的曲线 还有光流的数据结果

     在x周和y洲方向的展示结果

    可以看出在光流上 可以看出原始数据结果

    当我们更换其他数据的时候,可以得到如下的数据结果

     

     程序2  可以对视频采集,并且识别每一个视频前后帧的不同得到数据结果

     

     

     

     程序三  对视频采集,可以获得其光流厂的具体数据,并且获得矢量图

     

     

     源程序见:多种方法实现光流法,光流法检测目标检测和目标追踪-Matlab文档类资源-CSDN下载

    如有侵权,请联系作者删除。

    展开全文
  • 光流检测运动目标

    2018-07-25 09:28:29
    经典的光流法,实现视频中运动目标的运动轨迹,以及目标轮廓的显现。效果良好,分享给大家学习交流
  • 程序1 获得单个图像的光流厂,光流矢量图 光流X,Y轴的数据结果,光流检测结果 程序2 可以对视频采集,并且识别每一个视频前后帧的不同得到数据结果 程序3 对视频采集,可以获得其光流厂的具体数据,并且获得矢量图 ...
  • opencv光流检测(二)

    2021-07-20 22:09:59
    转自:https://blog.csdn.net/ap1005834/article/details/51226660
  • 光流检测(optical flow)

    千次阅读 2013-12-18 14:28:18
    函数解析 cvGoodFeaturesToTrack,http://blog.csdn.net/moc062066/article/details/6634120,和 cvFindCornerSubPix ,...两个函数的基础,接下来就是在视频中检测光流(optical flo
  • 光流法运动目标检测

    万次阅读 2019-05-21 10:07:11
    接上篇,OpenCV视频目标跟踪及背景分割器,本篇介绍OpenCV—python目标跟踪==》光流法 回顾: 目标跟踪是对摄像头视频中的移动目标进行定位的过程。实时目标跟踪是许多计算机视觉应用的重要任务,如监控、基于感知...
  • 光流检测,在很多图像处理中得到应用,该代码是MATLAB程序
  • 基于光流检测跟踪视频中的汽车 https://michaelbeechan.blog.csdn.net/article/details/116033918
  • 摸鱼了一个星期以后,我开始正经了解光流法。 一、运动目标检测的综述 参考链接:运动目标检测综述 1.光流光流是空间运动物体被观测面上的像素点运动产生的瞬时速度场,包含了物体表面结构和动态行为的重要信息。...
  • 基于光流法的深度学习在工业运动检测的应用.pdf
  • 基于光流场的交通汽车检测跟踪Traffic Vehicle Detection and Tracking Based on Optical Flow Field
  • 光流法实现无人驾驶前方目标识别与目标追踪,用MATLAB编写
  • 什么是光流:追踪相邻帧像素间的移动 前提条件:恒定相位幅度 基于相位方法的三个步骤: 将图像序列经过一些正交的Gabor滤波器,计算出相位的时间梯度 给定时间跨度的非线性相位时间梯度会被滤掉 在一个位置的剩下...
  • 光流法进行运动目标检测
  • 光流的概念是指在连续的两帧图像当中,由于图像中的物体移动或者摄像头的移动而使得图像中的目标形成的矢量运动轨迹叫做光流。本质上光流是个向量场,表示了一个像素点从第一帧过渡到第二帧的运动过程,体现该像素点...
  • 光流检测运动物体

    千次阅读 2015-11-27 22:28:00
    光流稠密度检测运动物体calcOpticalFlowFarneback()方法的理解 prev:前一帧图像8位 next:当前帧图像8位 flow:计算出的光流图像 pyr_scale:金字塔层数,取经典参数0.5 levels:初始化金字塔层数
  • 实现光流检测代码如下: from __future__ import print_function import numpy as np import cv2 #import video def draw_flow(img, flow, step=16): h,...
  • 光流法实现两张图片光流检测以及矢量方向变化
  • 关于光流法的运动图像目标检测 已调试成功
  • python简单LK稀疏光流法运动目标检测

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 9,855
精华内容 3,942
关键字:

光流检测