opencv基于帧差法的运动目标跟踪的clock函数返回值溢出问题

 

Opencv基于帧差法的运动目标跟踪的clock函数溢出问题

 

摘要:

 运行在arm平台上的基于帧差法的运动目标跟踪,用到opencv的相关函数有:颜色空间转换函数cvCvtColor()、计算数组差的绝对值函数cvAbsDiff()、图像二值化函数cvThreshold()、更新运动历史图像函数cvUpdateMotionHistory()、图像矩阵转换函数cvConvert()、图像平滑函数cvSmooth()、向下采样函数cvPyrDown()、图像膨胀函数cvDilate()、向上采样函数cvPyrUp()、创建内存存储器函数cvCreateMemStorage()、创建序列函数cvCreateSeq()、从二值图像中检索轮廓函数cvFindContours()。

1.现象

  在arm平台,对同一个有运动目标的视频进行循环跟踪,发现在运行了230余次后出现了不能检测到运动目标轮廓的问题。而后又运行一段时间,又可以重新检测到运动目标的轮廓。

即cvFindContours( void* img, CvMemStorage* storage, CvSeq** firstContour, int cntHeaderSize, int mode, int method, CvPoint offset )函数带回的firstContour为NULL。

2.定位过程

2.1

 思考:

  在观看测试视频之后发现,除去有运动目标的90帧左右的视频,其余200多帧的视频都是没有运动目标出现的。为了排除测试视频的影响,把测试视频没有运动目标的的200多帧剪掉之后,保证了测试视频的每一帧都有运动目标。

测试条件:

  1. arm平台
  2. 相同的测试工程

结果:

  用裁剪之后的视频重新进行测试发现现象和裁剪之前的视频测试的现象有了差别:原视频是运行230余次出现了不能检测到运动目标轮廓的问题,而裁剪之后的视频却是运行了800余次才出现了不能检测到运动目标轮廓的问题。通过对两段长短不同,但运动目标存在帧数相同的视频的测试对比,明确了该问题和视频中的是运动目标存在帧数占比无关。

2.2

思考:

    在明确了周期性检测不到运动目标轮廓的问题和测试视频中运动目标存在帧数占比无关之后。考虑到硬件平台差异,于是考虑将同样的测试工程运行到标准Linux和Windows上进行平台差异性测试。

测试条件:

  1. windows平台,标准Linux平台
  2. 相同的测试工程
  3. 相同的测试视频
  4. 各平台的Opencv-2.4.9版本库

结果:

  用相同的测试工程对同一段视频进行测试,在标准Linux平台和Windows平台上进行循环跟踪,测试程序运行5000余次,并没有复现到在arm平台上的周期性检测不到运动目标轮廓的问题。从这组测试可以得出Opencv基于帧差法的运动目标跟踪周期性检测不到运动目标轮廓有一定的平台差异性。而具体的差异点在哪里还需要进一步定位。

 

2.3

思考:

    明确了问题复现时有平台差异性,进一步考虑各个平台的环境差异。首先标准Linux平台上由于有通过软件源安装的高版本的Opencv库,并不能确定测试工程运行在Linux平台上时Opencv库的单一性。而arm平台上的Opencv-2.4.9的库又过于老旧,大胆怀疑是否是因为Opencv版本过低才引起问题。经查阅相关资料决定统一用高版本的Opencv再次进行平台差异性测试。篇幅有限,在此涉及的Opencv3.x版本库的交叉编译不再一一陈述。

测试条件:

  1. 标准Linux平台,arm平台
  2. 相同的测试工程
  3. 相同的测试视频
  4. Opencv3.1.0版本库

 结果:

    arm平台在更新了Opencv-3.1.0版本的库后,重复2.1做的循环测试,依然稳定复现周期性的检测不到运动目标轮廓的问题。而标准Linux平台依然复现不到该问题。

2.4

思考:

    经过2.1-2.3的视频差异化,平台差异化,Opencv版本差异化测试之后,arm平台稳定复现问题,而标准Linux平台经过长时间运行依然稳定。查阅网上资料发现检索轮廓函数cvFindContours的c++版本接口findContours不用外部调用者创建内存存储器和创建动态序列,即不用调用cvCreateMemStorage()和 cvCreateSeq()两个函数。而这两个函数又是Opencv的动态内存管理函数,创建的内存存储器还需要在外部释放,由于不熟悉Opencv的动态内存管理。所以考虑用findContours()接口替代cvFindContours(),降低复杂度。

测试条件:

  1. arm平台
  2. Opencv-2.4.9库
  3. cvFindContours()替换为findContours()

结果:

    用和2.1-2.4相同的测试工程测试之后,发现通过findContours()检测轮廓依然会有周期性检测不到运动目标轮廓的问题,即findContours()带回的轮廓点数组为空,vector<vector< Point>>类型的输出数组size为0。这组测试的结论为cvFindContours()和findContours()的检测结果并无差异,通过查看findContours()函数的源码发现内部调用的也是cvCreateMemStorage()和 cvCreateSeq()函数进行的动态序列管理,由此可知cvFindContours()和findContours()是殊途同归的两个函数。

2.5

思考:

    为了进一步定位问题,缩小问题范围,将其他业务代码屏蔽,只进行图像的读入、帧差和目标跟踪,并用二分法确定是哪个函数调用引起的问题。

测试条件:

  1. arm平台
  2. 屏蔽了非Opencv的业务代码
  3. Opencv-2.4.9版本

结果:

    用二分法对测试工程的调用的Opencv函数逐个排查,发现如果不调用cvUpdateMotionHistory()函数,用二值化后的图像直接进入cvFindContours()轮廓检测函数,对测试视频循环检测1000多次并没有复现到检测不到运动目标轮廓的问题。

2.6

思考:

    通过2.5的测试,发现是调用cvUpdateMotionHistory()函数才会复现问题,查看cvUpdateMotionHistory()函数源码发现,其内部关键的一个比较语句含义比较复杂。即

而Opencv官方文档对该函数的解释是

该函数只是更新像素点的运动历史。也就说更新的不是图像,而是对图像中像素点运动情况的更新。

  • silhouette(x,y) != 0时,即该像素点发生运动,所以要对其进行更新,

mhi(x,y) = timestamp表示运动发生的时刻


②silhouette(x,y)= 0时,即该像素点未发生运动,但还需检测对该点的跟踪时间是否超过了预设最大跟踪时间,即判断mhi(x,y)与(timestamp - duration)的大小。此时mhi(x,y)即为该点最近一次发生运动的时刻值,如其小于(timestamp - duration),表示该点运动时刻已经超出跟踪时间,故可以舍弃。
而当mhi(x,y)大于或者等于(timestamp - duration)时,表示该点此刻虽未发生运动,但还在跟踪时间内,所以不对该点发生运动的时间标记进行操作。

timestamp是系统运行到cvUpdateMotionHistory()函数处的时间戳,而硬件平台不同,CPU的运行速度快慢不一致,所以,做帧差的两帧图像的timestamp的间隔并不相同。duration的实际含义是运动跟踪的持续时间,可以根据需求,并结合CPU运行速度,先由视频流的Fps计算出做帧差运算的两帧的时间间隔,再得出duration的值。公式如下:

duration >= 持续跟踪帧数差(N -1) * 帧差运算的两帧的时间间隔

N为做帧差的图像缓冲数组的大小。

即duration的含义可理解为运动历史图像窗口持续时长。

测试条件:

  1. arm平台
  2. arm平台做帧差的两帧图像时间间隔为30-40ms
  3. durantion设置为80ms,即运动历史图像长度为80ms,两帧图像的间隔

结果:

     对测试视频做的循环跟踪测试,依然稳定复现周期性的检测不到运动目标轮廓的问题。

2.7

思考:

运动目标跟踪的几个主要图像:

  1. 做完帧差的二值化图像
  2. 更新运动历史后的MHI图像
  3. 根据MHI图像转换的运动图像

对这几个关键的图像的像素值加上观察窗口,观察调用cvAbsDiff(),cvThreshold(),cvUpdateMotionHistory(),cvFindContours()函数前后的像素值变化。

测试条件:

(1)加上图像像素值观察窗口

如图所示,pstSilhImage是做完帧差并二值化后的图像,g_stHumanDetectAlgInfo.stMhiAnalysisInfo.pstMhiImage是更新运动历史图像信息后的图像。

g_stHumanDetectAlgInfo.stMhiAnalysisInfo.pstMotionImage是从g_stHumanDetectAlgInfo.stMhiAnalysisInfo.pstMhiImage转换过来的运动分析图像。

 

结果:

    当在arm平台复现问题时,发现g_stHumanDetectAlgInfo.stMhiAnalysisInfo.pstMotionImage内容为全0,

[ShowImageStat-3083]: iZeroCount 104432 iOneCount 0 (pstImage->width * pstImage->height) 104432

而stHumanDetectAlgInfo.stMhiAnalysisInfo.pstMotionImage来自g_stHumanDetectAlgInfo.stMhiAnalysisInfo.pstMhiImage,所以由此断定是cvUpdateMotionHistory()函数持续进入了(val < delbound) ? 0 分支了,

结合delbound = (timestamp - mhi_duration)。而timestamp是由clock()函数得来的,

 

经查阅资料,发现clock()在返回值是有符号的long类型。而有符号的long类型的取值范围是-2147483,648 ~ 2147483647,换算为时间就是35.79分钟timestamp后溢出变为负数,也就是cvUpdateMotionHistory()函数会持续进入(val < delbound) ? 0分支,直到timestamp再次翻转为正数。

 

结论:

    Opencv官方例程里对于cvUpdateMotionHistory()函数的使用,入参timestamp也是根据clock()函数得来的,但是clock()函数无论在什么平台都会有返回值溢出的问题,只是早晚的问题。所以可以用clock_gettime()函数替代clock()函数,即clock_gettime()得到的时间戳是单调递增的,不会有溢出问题。

 

                                       

 

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值