光流法的介绍

2024-02-02 06:18
文章标签 介绍 光流法

本文主要是介绍光流法的介绍,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

流法是比较经典的运动估计方法,本文不仅叙述简单明了,而且附代码,故收藏.

在空间中,运动可以用运动场描述。而在一个图像平面上,物体的运动往往是通过图像序列中不同图象灰度分布的不同体现的。从而,空间中的运动场转移到图像上就表示为光流场,光流场反映了图像上每一点灰度的变化趋势。

光流可以看作带有灰度的像素点在图像平面运动产生的瞬时速度场。下面我们推导光流方程:

假设E(x,y,t)为(x,y)点在时刻t的灰度(照度)。设t+dt时刻该点运动到(x+dx,y+dy)点,他的照度为E(x+dx,y+dy,t+dt)。我们认为,由于对应同一个点,所以

E(x,y,t) = E(x+dx,y+dy,t+dt)   —— 光流约束方程

将上式右边做泰勒展开,并令dt->0,则得到:Exu+Eyv+Et = 0,其中:

Ex = dE/dx   Ey = dE/dy   Et = dE/dt   u = dx/dt   v = dy/dt

上面的Ex,Ey,Et的计算都很简单,用离散的差分代替导数就可以了。光流法的主要任务就是通过求解光流约束方程求出u,v。但是由于只有一个方程,所以这是个病态问题。所以人们提出了各种其他的约束方程以联立求解。但是由于我们用于摄像机固定的这一特定情况,所以问题可以大大简化。

摄像机固定的情形

在摄像机固定的情形下,运动物体的检测其实就是分离前景和背景的问题。我们知道对于背景,理想情况下,其光流应当为0,只有前景才有光流。所以我们并不要求通过求解光流约束方程求出u,v。我么只要求出亮度梯度方向的速率就可以了,即求出sqrt(u*u+v*v)。

而由光流约束方程可以很容易求到梯度方向的光流速率为 V = abs(Et/sqrt(Ex*Ex+Ey*Ey))。这样我们设定一个阈值T。

V(x,y) > T 则(x,y)是前景 ,反之是背景

C++实现

在实现摄像机固定情况的光流法时,需要有两帧连续的图像,下面的算法针对RGB24格式的图像计算光流:

void calculate(unsigned char* buf)
 {
  int Ex,Ey,Et;
  int gray1,gray2;
  int u;
  int i,j;
  memset(opticalflow,0,width*height*sizeof(int));
  memset(output,255,size);
  for(i=2;i<height-2;i++){
   for(j=2;j<width-2;j++){
    gray1 = int(((int)(buf[(i*width+j)*3])
     +(int)(buf[(i*width+j)*3+1])
     +(int)(buf[(i*width+j)*3+2]))*1.0/3);
    gray2 = int(((int)(prevframe[(i*width+j)*3])
     +(int)(prevframe[(i*width+j)*3+1])
     +(int)(prevframe[(i*width+j)*3+2]))*1.0/3);
    Et = gray1 - gray2;
    gray2 = int(((int)(buf[(i*width+j+1)*3])
     +(int)(buf[(i*width+j+1)*3+1])
     +(int)(buf[(i*width+j+1)*3+2]))*1.0/3);
    Ex = gray2 - gray1;
    gray2 = int(((int)(buf[((i+1)*width+j)*3])
     +(int)(buf[((i+1)*width+j)*3+1])
     +(int)(buf[((i+1)*width+j)*3+2]))*1.0/3);
    Ey = gray2 - gray1;
    Ex = ((int)(Ex/10))*10;
    Ey = ((int)(Ey/10))*10;
    Et = ((int)(Et/10))*10;
    u = (int)((Et*10.0)/(sqrt((Ex*Ex+Ey*Ey)*1.0))+0.1);
    opticalflow[i*width+j] = u;
    if(abs(u)>10){
     output[(i*width+j)*3] = 0;
     output[(i*width+j)*3+1] = 0;
     output[(i*width+j)*3+2] = 0;
    }
   }
  }
  memcpy(prevframe,buf,size);
 }

//

/另一个代码

/

/

WW_RETURN HumanMotion::ImgOpticalFlow(IplImage *pre_grey,IplImage *grey)
/*************************************************
  Function:
  Description:  光流法计算运动速度与方向      
  Date:   2006-6-14
  Author:  
  Input:                       
  Output:        
  Return:        
  Others:         
*************************************************/
{

 IplImage *velx = cvCreateImage( cvSize(grey->width ,grey->height),IPL_DEPTH_32F, 1 );
 IplImage *vely = cvCreateImage( cvSize(grey->width ,grey->height),IPL_DEPTH_32F, 1 );

 velx->origin =  vely->origin = grey->origin;
 CvSize winSize = cvSize(5,5);
 cvCalcOpticalFlowLK( prev_grey, grey, winSize, velx, vely );
 
 cvAbsDiff( grey,prev_grey, abs_img );
 cvThreshold( abs_img, abs_img, 29, 255, CV_THRESH_BINARY); 

 CvScalar xc,yc; 
 for(int y =0 ;y<velx->height; y++)
  for(int x =0;x<velx->width;x++ )
  {
   xc = cvGetAt(velx,y,x);
   yc = cvGetAt(vely,y,x);

   
   float x_shift= (float)xc.val[0];
   float y_shift= (float)yc.val[0];
   const int winsize=5;  //计算光流的窗口大小


   if((x%(winsize*2)==0) && (y%(winsize*2)==0) )
   {

    if(x_shift!=0 || y_shift!=0)
    {
     
     if(x>winsize && y>winsize && x <(velx->width-winsize) && y<(velx->height-winsize) )
     {

      cvSetImageROI( velx, cvRect( x-winsize, y-winsize, 2*winsize, 2*winsize));
      CvScalar total_x = cvSum(velx);
      float xx = (float)total_x.val[0];
      cvResetImageROI(velx);

      cvSetImageROI( vely, cvRect( x-winsize, y-winsize, 2*winsize, 2*winsize));
      CvScalar total_y = cvSum(vely);
      float yy = (float)total_y.val[0];
      cvResetImageROI(vely);
      
      cvSetImageROI( abs_img, cvRect( x-winsize, y-winsize, 2*winsize, 2*winsize));
      CvScalar total_speed = cvSum(abs_img);
      float ss = (float)total_speed.val[0]/(4*winsize*winsize)/255;
      cvResetImageROI(abs_img);

      const double ZERO = 0.000001;
      const double pi = 3.1415926;
      double alpha_angle;

      if(xx<ZERO && xx>-ZERO)
       alpha_angle = pi/2;
      else
       alpha_angle = abs(atan(yy/xx));
      
      if(xx<0 && yy>0) alpha_angle = pi - alpha_angle ;
      if(xx<0 && yy<0) alpha_angle = pi + alpha_angle ;
      if(xx>0 && yy<0) alpha_angle = 2*pi - alpha_angle ;


      
      CvScalar line_color;
      float scale_factor = ss*100;
      line_color = CV_RGB(255,0,0);
      CvPoint pt1,pt2;
      pt1.x = x;
      pt1.y = y;
      pt2.x = static_cast<int>(x + scale_factor*cos(alpha_angle));
      pt2.y = static_cast<int>(y + scale_factor*sin(alpha_angle));

      cvLine( image, pt1, pt2 , line_color, 1, CV_AA, 0 );
      CvPoint p;
      p.x = (int) (pt2.x + 6 * cos(alpha_angle - pi / 4*3));
      p.y = (int) (pt2.y + 6 * sin(alpha_angle - pi / 4*3));
      cvLine( image, p, pt2, line_color, 1, CV_AA, 0 );
      p.x = (int) (pt2.x + 6 * cos(alpha_angle + pi / 4*3));
      p.y = (int) (pt2.y + 6 * sin(alpha_angle + pi / 4*3));
      cvLine( image, p, pt2, line_color, 1, CV_AA, 0 );

      /*
      line_color = CV_RGB(255,255,0);
      pt1.x = x-winsize;
      pt1.y = y-winsize;
      pt2.x = x+winsize;
      pt2.y = y+winsize;
      cvRectangle(image, pt1,pt2,line_color,1,CV_AA,0);
      */

     }
    }
   }
  }


 cvShowImage( "Contour", abs_img);
 cvShowImage( "Contour2", vely);

 cvReleaseImage(&velx);
 cvReleaseImage(&vely);
 cvWaitKey(20);
 
 return WW_OK;

}

这篇关于光流法的介绍的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/669709

相关文章

MySQL复杂SQL之多表联查/子查询详细介绍(最新整理)

《MySQL复杂SQL之多表联查/子查询详细介绍(最新整理)》掌握多表联查(INNERJOIN,LEFTJOIN,RIGHTJOIN,FULLJOIN)和子查询(标量、列、行、表子查询、相关/非相关、... 目录第一部分:多表联查 (JOIN Operations)1. 连接的类型 (JOIN Types)

java中BigDecimal里面的subtract函数介绍及实现方法

《java中BigDecimal里面的subtract函数介绍及实现方法》在Java中实现减法操作需要根据数据类型选择不同方法,主要分为数值型减法和字符串减法两种场景,本文给大家介绍java中BigD... 目录Java中BigDecimal里面的subtract函数的意思?一、数值型减法(高精度计算)1.

Pytorch介绍与安装过程

《Pytorch介绍与安装过程》PyTorch因其直观的设计、卓越的灵活性以及强大的动态计算图功能,迅速在学术界和工业界获得了广泛认可,成为当前深度学习研究和开发的主流工具之一,本文给大家介绍Pyto... 目录1、Pytorch介绍1.1、核心理念1.2、核心组件与功能1.3、适用场景与优势总结1.4、优

Java实现本地缓存的常用方案介绍

《Java实现本地缓存的常用方案介绍》本地缓存的代表技术主要有HashMap,GuavaCache,Caffeine和Encahche,这篇文章主要来和大家聊聊java利用这些技术分别实现本地缓存的方... 目录本地缓存实现方式HashMapConcurrentHashMapGuava CacheCaffe

Spring Security介绍及配置实现代码

《SpringSecurity介绍及配置实现代码》SpringSecurity是一个功能强大的Java安全框架,它提供了全面的安全认证(Authentication)和授权(Authorizatio... 目录简介Spring Security配置配置实现代码简介Spring Security是一个功能强

JSR-107缓存规范介绍

《JSR-107缓存规范介绍》JSR是JavaSpecificationRequests的缩写,意思是Java规范提案,下面给大家介绍JSR-107缓存规范的相关知识,感兴趣的朋友一起看看吧... 目录1.什么是jsR-1072.应用调用缓存图示3.JSR-107规范使用4.Spring 缓存机制缓存是每一

Java中 instanceof 的用法详细介绍

《Java中instanceof的用法详细介绍》在Java中,instanceof是一个二元运算符(类型比较操作符),用于检查一个对象是否是某个特定类、接口的实例,或者是否是其子类的实例,这篇文章... 目录引言基本语法基本作用1. 检查对象是否是指定类的实例2. 检查对象是否是子类的实例3. 检查对象是否

什么是ReFS 文件系统? ntfs和refs的优缺点区别介绍

《什么是ReFS文件系统?ntfs和refs的优缺点区别介绍》最近有用户在Win11Insider的安装界面中发现,可以使用ReFS来格式化硬盘,这是不是意味着,ReFS有望在未来成为W... 数十年以来,Windows 系统一直将 NTFS 作为「内置硬盘」的默认文件系统。不过近些年来,微软还在研发一款名

C#使用StackExchange.Redis实现分布式锁的两种方式介绍

《C#使用StackExchange.Redis实现分布式锁的两种方式介绍》分布式锁在集群的架构中发挥着重要的作用,:本文主要介绍C#使用StackExchange.Redis实现分布式锁的... 目录自定义分布式锁获取锁释放锁自动续期StackExchange.Redis分布式锁获取锁释放锁自动续期分布式

redis过期key的删除策略介绍

《redis过期key的删除策略介绍》:本文主要介绍redis过期key的删除策略,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录第一种策略:被动删除第二种策略:定期删除第三种策略:强制删除关于big key的清理UNLINK命令FLUSHALL/FLUSHDB命