许选用donet平台.net 4.5
去官网下载如下SDK与说明文档
安装完找到样本文件夹,CSHARP我们选用X64
打开SLN解决方案我用的是VS2017
调试一下GXMulitCam程序
今天要解决的主要是:
1.显示刷新帧率
2.多相机并行采集问题
更新帧率,主要来源于这个函数
帧率的获取由CFPS对象下的UpdateFps()函数实现
先来看看国家重点计划对象是怎么样的一个结构
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
namespace GxIAPINET.Sample.Common
{
public class CFps
{
UInt64 m_nFrameCount = 0; ///< 从上次计算完毕开始累积的帧数
double m_dBeginTime = 0.0; ///< 第一帧之前的一帧的时间(初始为0)
double m_dEndTime = 0.0; ///< 最后一帧的时间
double m_dFps = 0.0; ///< 通过帧数与时间间隔之比得出的帧率(帧/秒)
double m_dCurrentFps = 0.0; ///< 当前的帧率,可能是预测得到的(帧/秒)
UInt64 m_nTotalFrameCount = 0; ///< 累积的帧数
CStopWatch m_objTime = new CStopWatch(); ///< 计时器
object m_objLock = new object();
/// <summary>
/// 构造函数
/// </summary>
public CFps()
{
//重置所有参数
Reset();
}
/// <summary>
/// 获取最近一次的帧率
/// </summary>
/// <returns>当前帧图像</returns>
public double GetFps()
{
lock (m_objLock)
{
//返回当前的帧率
return m_dCurrentFps;
}
}
/// <summary>
/// 获取累积的总帧数
/// </summary>
/// <returns>当前帧图像</returns>
public UInt64 GetTotalFrameCount()
{
lock (m_objLock)
{
return m_nTotalFrameCount;
}
}
/// <summary>
/// 增加帧数
/// </summary>
public void IncreaseFrameNum()
{
lock (m_objLock)
{
//累积帧数
m_nTotalFrameCount++;
//增加帧数
m_nFrameCount++;
//更新时间间隔
m_dEndTime = m_objTime.ElapsedTime();
}
}
/// <summary>
/// 更新帧率
/// 如果该函数被调用的频率超过了帧频率,则帧率会降为零
/// </summary>
public void UpdateFps()
{
lock (m_objLock)
{
//计算时间间隔
double dInterval = m_dEndTime - m_dBeginTime;
//时间间隔大于零(有新帧)
if (dInterval > 0)
{
m_dFps = 1000.0 * m_nFrameCount / dInterval;
m_nFrameCount = 0; //累积帧数清零
m_dBeginTime = m_dEndTime; //更新起始时间
m_dCurrentFps = m_dFps;
}
else if (dInterval == 0) //时间间隔等于零(无新帧)
{
//如果上次的帧率非零,则调整帧率
if (m_dCurrentFps != 0)
{
//从上一帧到现在的经历的时间(毫秒)
double dCurrentInterval = m_objTime.ElapsedTime() - m_dBeginTime;
//根据当前帧率计算更新帧率的时间阈值
double dPeriod = 1000.0 / m_dCurrentFps; //上次的帧周期(毫秒)
const double RATIO = 1.5; //超过帧周期的多少倍,帧率才更新
double dThresh = RATIO * dPeriod; //多长时间没有来帧,帧率就更新
//如果超过2秒没有来帧,则帧率降为零。
const double ZERO_FPS_INTERVAL = 2000;
if (dCurrentInterval > ZERO_FPS_INTERVAL)
{
m_dCurrentFps = 0;
}
//如果在2秒之内已经超过1.5倍的帧周期没有来帧,则降低帧率
else if (dCurrentInterval > dThresh)
{
m_dCurrentFps = m_dFps / (dCurrentInterval / (1000.0 / m_dFps));
}
else { }
}
else { }
}
else { }
}
}
/// <summary>
/// 将计时器恢复为初始状态
/// </summary>
public void Reset()
{
m_nFrameCount = 0;
m_dBeginTime = 0.0;
m_dEndTime = 0.0;
m_nTotalFrameCount = 0;
m_dFps = 0.0;
m_dCurrentFps = 0.0;
m_objTime.Start(); //重启计时器
}
}
}
CFPS():可以看到第一个函数为构造函数,用于初始化,调用了重置函数,用于全部初始化为0
GetFps():用于外部获得当前帧率,函数内部使用了锁,锁锁经常会应用于防止多线程操作导致公用变量值出现不确定的异常,用于确保操作的安全性
GetTotalFrameCount():用于外部获得目前总共有采集了多少帧图像
IncreaseFrameNum():主要有三个功能,首先是对totalFrame更新自增1,这个很容易理解,对总采集的帧数1,其次是对帧数又自增1,这个帧数又可以理解为是在某一时间段内的帧数,这个某一时间段指的是上次结束计算帧率到下一次结束之间,即:新的一个用于计算帧率的时间段,最后更新结束时间,这个表示,这一帧的采集时间,用于计算时间段截止时间
UpdateFps():用于计算这个时间段里帧率是多少。首先先计算这个时间段的长度,得到dInterval,如果间隔大于0,则证明EndTime> StartTime即采集了新帧。此时,帧率m_dFps = 1000.0 * m_nFrameCount / dInterval;(为什么要乘1000呢,因为时间以毫秒来及时,单位换算)。随后将这个时间段里的帧数m_nFrameCount置为0,并将初试时间置为最后一帧的结束时间。那么如果,dInterval == 0呢,也就是没有采集新帧,则将上一次的StartTime与现在这一时刻相减,求时间间隔dCurrentInterval,如果dCurrentInterval> 2s,则直接置CurrentFps = 0,否则就是在2s内没有新帧来而已,那么就要降低帧率,看dCurrentInterval是否大于1.5倍的帧周期(帧周期由上次的帧率,1 /帧率得出),如果大于的话,m_dCurrentFps = m_dFps /(dCurrentInterval /(1000.0 / m_dFps));
在这里我们看到时间均使用了一个类CStopWatch,让我们来看看细节
using System;
using System.Diagnostics;
namespace GxIAPINET.Sample.Common
{
public class CStopWatch
{
double m_dStartTime = 0.0; ///< 开始时间
double m_dStopTime = 0.0; ///< 停止时间
public CStopWatch()
{
Start();
}
/// <summary>
/// 开始计数
/// </summary>
public void Start()
{
m_dStartTime = Stopwatch.GetTimestamp();
}
/// <summary>
/// 停止计数
/// </summary>
/// <returns>时间差单位ms</returns>
public double Stop()
{
m_dStopTime = Stopwatch.GetTimestamp();
double theElapsedTime = ElapsedTime();
m_dStartTime = m_dStopTime;
return theElapsedTime;
}
/// <summary>
/// 获取时间差
/// </summary>
/// <returns>时间差单位ms</returns>
public double ElapsedTime()
{
m_dStopTime = Stopwatch.GetTimestamp();
double dTimeElapsed = (m_dStopTime - m_dStartTime) * 1000.0;
return dTimeElapsed / Stopwatch.Frequency;
}
}
}
细节很简单,四个函数
CStopWatch():初始化,调用启动()方法,由此可见,启动()方法也是用于初始化
启动():Stopwatch.GetTimestamp();得到当前时间
停止():得到结束时间
ElapsedTime():表示时间过去了多少,也就是时间差,当然是停止时间 - 开始时间,停止时间所以要获取当前时间,再乘以1000,因为GetTimestamp()默认调用系统时间也就是DateTime.Now() ,最后需要用差值除以频率作矫正
至此,我们就知道了如何显示帧率。接下拉,请稍等我一会,我去测试。
OK,我的程序中也顺利的可以测试帧率了
帧率这里有需要注意的几点:
1.要随时锁
2.在主线程,通过计时器来调用updateFps()
3.通过代表显示图像的时候,记住也要锁(this)
总结:
其实思路很简单,分时间段记录framecount,记录时间段Starttime Endtime并不断更新,同过简单计算即可。