2025-12-26 03:18:21 +08:00
|
|
|
|
using OpenCvSharp;
|
|
|
|
|
|
|
|
|
|
|
|
namespace SHH.CameraSdk;
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// [零延迟核心] 智能帧(内存复用+引用计数)
|
|
|
|
|
|
/// 功能:封装 OpenCV Mat 实现物理内存复用,通过引用计数管理生命周期,避免 GC 频繁回收导致的性能抖动
|
|
|
|
|
|
/// 特性:引用归零自动回池,全程无内存分配/释放开销,支撑高帧率实时流处理
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public class SmartFrame : IDisposable
|
|
|
|
|
|
{
|
|
|
|
|
|
#region --- 私有资源与状态 (Private Resources & States) ---
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary> 所属帧池:用于引用归零后自动回收复用 </summary>
|
|
|
|
|
|
private readonly FramePool _pool;
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary> 引用计数器:线程安全,控制帧的生命周期 </summary>
|
|
|
|
|
|
/// <remarks> 初始值 0,激活后设为 1;引用归零则自动回池 </remarks>
|
|
|
|
|
|
private int _refCount = 0;
|
|
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
|
|
#region --- 公共属性 (Public Properties) ---
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary> 帧数据物理内存载体(OpenCV Mat 对象) </summary>
|
|
|
|
|
|
/// <remarks> 内存由帧池预分配,全程复用,不触发 GC </remarks>
|
|
|
|
|
|
public Mat InternalMat { get; private set; }
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary> 帧激活时间戳(记录帧被取出池的时刻) </summary>
|
|
|
|
|
|
public DateTime Timestamp { get; private set; }
|
|
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
2025-12-27 07:05:07 +08:00
|
|
|
|
#region --- 衍生数据属性 (Derivative Properties) ---
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// [衍生] 目标图像副本(处理后的图像)
|
|
|
|
|
|
/// <para>用途:存储经过缩放、增强后的图像,供 UI 预览或 Web 推流直接使用</para>
|
|
|
|
|
|
/// <para>生命周期:完全跟随 SmartFrame,主帧销毁时自动释放</para>
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public Mat? TargetMat { get; private set; }
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary> 变换类型(标记该 TargetMat 是被缩小了还是放大了) </summary>
|
|
|
|
|
|
public FrameScaleType ScaleType { get; private set; } = FrameScaleType.None;
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary> [快捷属性] 目标图像宽度 (若 TargetMat 为空则返回 0) </summary>
|
|
|
|
|
|
public int TargetWidth => TargetMat?.Width ?? 0;
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary> [快捷属性] 目标图像高度 (若 TargetMat 为空则返回 0) </summary>
|
|
|
|
|
|
public int TargetHeight => TargetMat?.Height ?? 0;
|
|
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
2025-12-26 03:18:21 +08:00
|
|
|
|
#region --- 构造与激活 (Constructor & Activation) ---
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 初始化智能帧(由帧池调用,外部禁止直接实例化)
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="pool">所属帧池实例</param>
|
|
|
|
|
|
/// <param name="width">帧宽度</param>
|
|
|
|
|
|
/// <param name="height">帧高度</param>
|
|
|
|
|
|
/// <param name="type">帧数据类型(如 MatType.CV_8UC3)</param>
|
|
|
|
|
|
internal SmartFrame(FramePool pool, int width, int height, MatType type)
|
|
|
|
|
|
{
|
|
|
|
|
|
_pool = pool;
|
|
|
|
|
|
// 预分配物理内存:内存块在帧池生命周期内复用,避免频繁申请/释放
|
|
|
|
|
|
InternalMat = new Mat(height, width, type);
|
2025-12-27 07:05:07 +08:00
|
|
|
|
|
|
|
|
|
|
// 确保激活时清理旧的衍生数据(防止脏数据残留)
|
|
|
|
|
|
ResetDerivatives();
|
2025-12-26 03:18:21 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// [生产者调用] 从帧池取出时激活帧
|
|
|
|
|
|
/// 功能:初始化引用计数,标记激活时间戳
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public void Activate()
|
|
|
|
|
|
{
|
|
|
|
|
|
// 激活后引用计数设为 1,代表生产者(驱动/管道)持有该帧
|
|
|
|
|
|
_refCount = 1;
|
|
|
|
|
|
// 记录帧被取出池的时间,用于后续延迟计算
|
|
|
|
|
|
Timestamp = DateTime.Now;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
2025-12-27 07:05:07 +08:00
|
|
|
|
#region --- 衍生数据操作 (Derivatives Management) ---
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 挂载处理后的目标图像
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="processedMat">已处理的 Mat(所有权移交给 SmartFrame)</param>
|
|
|
|
|
|
/// <param name="scaleType">变换类型(缩小/放大/无)</param>
|
|
|
|
|
|
public void AttachTarget(Mat processedMat, FrameScaleType scaleType)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 防御性编程:如果之前已有 TargetMat,先释放,防止内存泄漏
|
|
|
|
|
|
if (TargetMat != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
TargetMat.Dispose();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TargetMat = processedMat;
|
|
|
|
|
|
ScaleType = scaleType;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 内部清理:释放衍生数据
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private void ResetDerivatives()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (TargetMat != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
TargetMat.Dispose();
|
|
|
|
|
|
TargetMat = null;
|
|
|
|
|
|
}
|
|
|
|
|
|
ScaleType = FrameScaleType.None;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
2025-12-26 03:18:21 +08:00
|
|
|
|
#region --- 引用计数管理 (Reference Count Management) ---
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// [消费者调用] 增加引用计数
|
|
|
|
|
|
/// 适用场景:帧需要被多模块同时持有(如同时分发到 UI 和 AI 分析)
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public void AddRef()
|
|
|
|
|
|
{
|
|
|
|
|
|
// 原子递增:线程安全,避免多线程竞争导致计数错误
|
|
|
|
|
|
Interlocked.Increment(ref _refCount);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
|
|
#region --- 释放与回池 (Disposal & Pool Return) ---
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// [消费者调用] 释放引用计数
|
|
|
|
|
|
/// 核心逻辑:引用归零后自动将帧归还至帧池,实现内存复用
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public void Dispose()
|
|
|
|
|
|
{
|
|
|
|
|
|
// 原子递减:线程安全,确保计数准确
|
|
|
|
|
|
if (Interlocked.Decrement(ref _refCount) <= 0)
|
|
|
|
|
|
{
|
2025-12-27 07:05:07 +08:00
|
|
|
|
// 1. 彻底清理衍生数据(TargetMat 通常是 new 出来的,必须 Dispose)
|
|
|
|
|
|
ResetDerivatives();
|
|
|
|
|
|
|
|
|
|
|
|
// 2. 归还到池中复用 (InternalMat 不释放,继续保留在内存池中)
|
2025-12-26 03:18:21 +08:00
|
|
|
|
_pool.Return(this);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
}
|