diff --git a/Cis.Application/Cis.Application.csproj b/Cis.Application/Cis.Application.csproj index dcda911..f99bc03 100644 --- a/Cis.Application/Cis.Application.csproj +++ b/Cis.Application/Cis.Application.csproj @@ -18,6 +18,10 @@ + + + + diff --git a/Cis.Application/Cm/Entity/CmMarkLabel.cs b/Cis.Application/Cm/Entity/CmMarkLabel.cs index bb0429a..cb19a1f 100644 --- a/Cis.Application/Cm/Entity/CmMarkLabel.cs +++ b/Cis.Application/Cm/Entity/CmMarkLabel.cs @@ -7,6 +7,18 @@ [Tenant(CmInfo.DbName)] public class CmMarkLabel : EntityBase { + /// + /// 相机 Id + /// + [SugarColumn(ColumnDescription = "相机Id")] + public long CbCameraId { get; set; } + + /// + /// 标记组 Id + /// + [SugarColumn(ColumnDescription = "标记组Id")] + public long CmMarkGroupId { get; set; } + /// /// 名称 /// @@ -14,39 +26,39 @@ public class CmMarkLabel : EntityBase public string Name { get; set; } /// - /// Pan 位置 + /// Pan 坐标 /// - [SugarColumn(ColumnDescription = "Pan位置")] + [SugarColumn(ColumnDescription = "Pan坐标")] public double PanPosition { get; set; } /// - /// Tilt 位置 + /// Tilt 坐标 /// - [SugarColumn(ColumnDescription = "Tilt位置")] + [SugarColumn(ColumnDescription = "Tilt坐标")] public double TiltPosition { get; set; } /// - /// Zoom 位置 + /// Zoom 坐标 /// - [SugarColumn(ColumnDescription = "Zoom位置")] + [SugarColumn(ColumnDescription = "Zoom坐标")] public double ZoomPosition { get; set; } + [SugarColumn(ColumnDescription = "画布宽度")] + public double CanvasWidth { get; set; } + + [SugarColumn(ColumnDescription = "画布高度")] + public double CanvasHeight { get; set; } + + [SugarColumn(ColumnDescription = "画布 left 距离")] + public double CanvasLeft { get; set; } + + [SugarColumn(ColumnDescription = "画布 top 距离")] + public double CanvasTop { get; set; } + /// /// 备注 /// [SugarColumn(ColumnDescription = "备注", Length = 256)] [MaxLength(256)] public string Remark { get; set; } - - /// - /// 相机 Id - /// - [SugarColumn(ColumnDescription = "相机Id")] - public long CbCameraId { get; set; } - - /// - /// 标记组 Id - /// - [SugarColumn(ColumnDescription = "标记组Id")] - public long CmMarkGroupId { get; set; } } \ No newline at end of file diff --git a/Cis.Application/Core/Algo/HikMarkSeacher.cs b/Cis.Application/Core/Algo/HikMarkSeacher.cs new file mode 100644 index 0000000..78cc79e --- /dev/null +++ b/Cis.Application/Core/Algo/HikMarkSeacher.cs @@ -0,0 +1,77 @@ +using System.Drawing; + +namespace Cis.Application.Core; + +public class HikMarkSeacher : MarkSearcherBase +{ + public HikMarkSeacher(CameraCalcInfo cameraCalcInfo) : base(cameraCalcInfo) + { + } + + #region Implement + + protected override double ConvertPanPosToAngle(double panPos) + { + double ret = (0.1 * HexToDecMa(panPos)) / (180 * Math.PI); + ret = (ret >= 0) ? ret : (2 * Math.PI + ret); + return ret; + } + + protected override double ConvertTiltPosToAngle(double tiltPos, double tiltMinPos = 0) + { + double ndiff; + if (tiltPos > tiltMinPos) + ndiff = HexToDecMa(tiltPos) - HexToDecMa(tiltMinPos); + else + ndiff = HexToDecMa(tiltPos) + HexToDecMa(13824) - HexToDecMa(tiltMinPos); + double ret = (0.1 * ndiff) / (180 * Math.PI); + return ret; + } + + protected override PointF GetFOfMatrixByZoomPos(double zoomPos) + { + PointF pointF = new() + { + X = (float)GetFx(zoomPos), + Y = (float)GetFy(zoomPos) + }; + return pointF; + } + + protected override void CalcSensor() + { + CameraCalcInfo.MinFocusX = 1783.6 / CameraCalcInfo.ImageWidth; + CameraCalcInfo.MinFocusY = 1781.4 / CameraCalcInfo.ImageHeight; + } + + #endregion Implement + + #region Util + + protected virtual double GetFx(double zoomPos) + { + CameraCalcInfo calcInfo = CameraCalcInfo; + return calcInfo.MinFocusX * GetZoomTag(zoomPos); + } + + protected virtual double GetFy(double zoomPos) + { + CameraCalcInfo calcInfo = CameraCalcInfo; + return calcInfo.MinFocusY * GetZoomTag(zoomPos); + } + + protected virtual double GetZoomTag(double zoomPos) + { + double ret = HexToDecMa(zoomPos) * 0.1; + ret = (ret - 1) * 0.65 + 1; + return ret; + } + + protected double HexToDecMa(double wHex) + { + double ret = (wHex / 4096) * 1000 + ((wHex % 4096) / 256) * 100 + ((wHex % 256) / 16) * 10 + (wHex % 16); + return ret; + } + + #endregion Util +} \ No newline at end of file diff --git a/Cis.Application/Core/Algo/MarkSearcherBase.cs b/Cis.Application/Core/Algo/MarkSearcherBase.cs index 033a34b..ab8f86e 100644 --- a/Cis.Application/Core/Algo/MarkSearcherBase.cs +++ b/Cis.Application/Core/Algo/MarkSearcherBase.cs @@ -1,6 +1,207 @@ -namespace Cis.Application.Core; +using MathNet.Numerics.LinearAlgebra; +using System.Drawing; -public class MarkSearcherBase +namespace Cis.Application.Core; + +public abstract class MarkSearcherBase { + #region Attr + + /// + /// 当前相机参数信息 + /// + protected CameraCalcInfo CameraCalcInfo { get; set; } + + protected List MarkLabelInfoList { get; set; } = new(); + + protected MatrixBuilder MBuilder { get; set; } = Matrix.Build; + + /// + /// 世界坐标转化为相机坐标矩阵 + /// + protected Matrix World2CameraMatrix { get; set; } + + #endregion Attr + + public MarkSearcherBase(CameraCalcInfo cameraCalcInfo) + { + InitCameraCalcInfoRelated(cameraCalcInfo); + } + + protected void InitCameraCalcInfoRelated(CameraCalcInfo cameraCalcInfo) + { + CameraCalcInfo = cameraCalcInfo; + CalcSensor(); + World2CameraMatrix = ConvertWorldToCamera(cameraCalcInfo); + } + + #region Calc + + /// + /// 计算标签位置过程 + /// + /// + public void Calc(CameraCalcInfo cameraCalcInfo) + { + //判断 + if (IsCameraRotate(cameraCalcInfo)) + InitCameraCalcInfoRelated(cameraCalcInfo); + + if (World2CameraMatrix == null || MarkLabelInfoList.Count == 0) + return; + + for (int index = 0; index < MarkLabelInfoList.Count; index++) + { + MarkLabelCalcInfo label = MarkLabelInfoList[index]; + Matrix labelC2WMatrix = ConvertCameraToWorld(label); + Matrix labelPointMatrix = MBuilder.DenseOfArray(new double[,] + { + { label.CanvasWidth / CameraCalcInfo.ImageWidth - 0.5 }, + { label.CanvasHeight / CameraCalcInfo.ImageHeight - 0.5 }, + { 1 } + }); + Matrix lResult = labelC2WMatrix.Multiply(labelPointMatrix); + Matrix pResult = World2CameraMatrix.Multiply(lResult); + + double x = pResult[0, 0] / pResult[2, 0] + 0.5; + double y = pResult[1, 0] / pResult[2, 0] + 0.5; + MarkLabelCalcResult labelCalcResult = new(); + if (x > 0.99 || x < 0.01 || y > 0.99 || y < 0.01 || pResult[2, 0] < 0) + { + labelCalcResult.InFlag = false; + } + else + { + labelCalcResult.InFlag = true; + labelCalcResult.CanvasLeft = x * CameraCalcInfo.ImageWidth; + labelCalcResult.CanvasTop = y * CameraCalcInfo.ImageHeight; + } + } + } + + /// + /// 判断相机是否进行了转动,转动了则需要重新计算世界坐标到相机坐标的转换矩阵 + /// + /// + /// + protected bool IsCameraRotate(CameraCalcInfo newInfo) + { + return CameraCalcInfo == null || + CameraCalcInfo.PtzInfo.Pan != newInfo.PtzInfo.Pan || + CameraCalcInfo.PtzInfo.Tilt != newInfo.PtzInfo.Tilt || + CameraCalcInfo.PtzInfo.Zoom != newInfo.PtzInfo.Zoom; + } + + /// + /// 获取将世界坐标系中的点转化为相机坐标系中的点的转换矩阵 + /// + /// + /// + protected Matrix ConvertWorldToCamera(CameraCalcInfo cameraCalcInfo) + { + double panAngle = ConvertPanPosToAngle(cameraCalcInfo.PtzInfo.Pan); + double tiltAngle = ConvertTiltPosToAngle(cameraCalcInfo.PtzInfo.Tilt); + PointF pointF = GetFOfMatrixByZoomPos(cameraCalcInfo.PtzInfo.Zoom); + double sinPan = Math.Sin(panAngle); + double cosPan = Math.Cos(panAngle); + double sinTilt = Math.Sin(tiltAngle); + double cosTilt = Math.Cos(tiltAngle); + + Matrix fMatrix = MBuilder.DenseOfArray(new double[,] + { + { pointF.X, 0, 0 }, + { 0, pointF.Y, 0 }, + { 0, 0, 1 }, + }); + + Matrix rotateTiltMatrix = MBuilder.DenseOfArray(new double[,] + { + { 1, 0, 0 }, + { 0, cosTilt, sinTilt }, + { 0, -sinTilt, cosTilt }, + }); + + Matrix midResult = fMatrix.Multiply(rotateTiltMatrix); + + Matrix rotatePanMatrix = MBuilder.DenseOfArray(new double[,] + { + { cosPan, 0, sinPan }, + { 0, 1, 0 }, + { -sinPan, 0, cosPan }, + }); + + return midResult.Multiply(rotatePanMatrix); + } + + /// + /// 获取将相机坐标系中的点转化为世界坐标系中的点的转换矩阵 + /// + /// + /// + protected Matrix ConvertCameraToWorld(MarkLabelCalcInfo labelCalcInfo) + { + double panAngle = ConvertPanPosToAngle(labelCalcInfo.PtzInfo.Pan); + double tiltAngle = ConvertTiltPosToAngle(labelCalcInfo.PtzInfo.Tilt); + PointF pointF = GetFOfMatrixByZoomPos(labelCalcInfo.PtzInfo.Zoom); + double sinPan = Math.Sin(panAngle); + double cosPan = Math.Cos(panAngle); + double sinTilt = Math.Sin(tiltAngle); + double cosTilt = Math.Cos(tiltAngle); + + Matrix rotatePanMatrix = MBuilder.DenseOfArray(new double[,] { + { cosPan, 0, -sinPan }, + { 0, 1, 0 }, + { sinPan, 0, cosPan } + }); + + Matrix rotateTiltMatrix = MBuilder.DenseOfArray(new double[,] { + { 1, 0, 0 }, + { 0, cosTilt, -sinTilt }, + { 0, sinTilt, cosTilt } + }); + + Matrix midResult = rotatePanMatrix.Multiply(rotateTiltMatrix); + + Matrix fMatrix = MBuilder.DenseOfArray(new double[,] { + { (1 / pointF.X), 0, 0 }, + { 0, (1 / pointF.Y), 0 }, + { 0, 0, 1 } + }); + + return midResult.Multiply(fMatrix); + } + + /// + /// 此方法计算在球机zoom值最小的情况下成像矩阵中的 f 本质为获取像元大小 + /// 尝试方案1:通过计算的方式来获取 + /// 尝试方案2:通过张友定相机标定的方法来生成成像矩阵中的 f + /// + protected virtual void CalcSensor() + { + } + + #endregion Calc + + #region Util + + /// + /// 将Pan值转化为角度 + /// + /// + protected abstract double ConvertPanPosToAngle(double panPos); + + /// + /// 将Tilt转化为角度 + /// + /// + protected abstract double ConvertTiltPosToAngle(double tiltPos, double tiltMinPos = 0); + + /// + /// 根据当前zoom值获取相机矩阵参数 + /// + /// + /// + protected abstract PointF GetFOfMatrixByZoomPos(double zoomPos); + #endregion Util } \ No newline at end of file diff --git a/Cis.Application/Core/Center/CameraDataCenter.cs b/Cis.Application/Core/Center/CameraDataCenter.cs index 5dc864b..3791f54 100644 --- a/Cis.Application/Core/Center/CameraDataCenter.cs +++ b/Cis.Application/Core/Center/CameraDataCenter.cs @@ -9,15 +9,15 @@ public class CameraDataCenter { #region Attr - private Thread _thread { get; set; } - private List _tbPtzCameraList { get; set; } - private ConcurrentDictionary _cameraPtzInfoDict { get; set; } - private readonly SqlSugarRepository _cbCameraRep; private readonly SqlSugarRepository _cmMarkLableRep; private readonly SqlSugarRepository _tbPtzCameraRep; private readonly PtzServerApi _ptzServerApi; + private Thread _thread { get; set; } + private List _tbPtzCameraList { get; set; } + private ConcurrentDictionary _cameraPtzInfoDict { get; set; } + #endregion Attr public CameraDataCenter() diff --git a/Cis.Application/Core/Entity/CameraCalcInfo.cs b/Cis.Application/Core/Entity/CameraCalcInfo.cs index e34c9b6..49ff7b7 100644 --- a/Cis.Application/Core/Entity/CameraCalcInfo.cs +++ b/Cis.Application/Core/Entity/CameraCalcInfo.cs @@ -1,32 +1,104 @@ namespace Cis.Application.Core; /// -/// 相机 Ptz 信息 +/// 相机计算信息 /// public class CameraCalcInfo { + /// + /// Ptz 信息 + /// + public PtzInfo PtzInfo { get; set; } + /// /// 图像的宽度 /// public int ImageWidth { get; set; } = 1920; /// - /// 图像的宽度 + /// 图像的高度 /// public int ImageHeight { get; set; } = 1080; /// - /// Pan + /// 最小焦距 + /// + public double MinFocusX { get; set; } = 0f; + + public double MinFocusY { get; set; } = 0f; +} + +/// +/// Ptz 信息 +/// +public class PtzInfo +{ + /// + /// Pan 坐标 /// public double Pan { get; set; } /// - /// Tilt + /// Tilt 坐标 /// public double Tilt { get; set; } /// - /// Zoom + /// Zoom 坐标 /// public double Zoom { get; set; } +} + +/// +/// 标记标签计算信息 +/// +public class MarkLabelCalcInfo +{ + /// + /// Ptz 信息 + /// + public PtzInfo PtzInfo { get; set; } + + /// + /// 画布宽度 + /// + public double CanvasWidth { get; set; } + + /// + /// 画布高度 + /// + public double CanvasHeight { get; set; } + + /// + /// 画布 left 距离 + /// + public double CanvasLeft { get; set; } + + /// + /// 画布 top 距离 + /// + public double CanvasTop { get; set; } +} + +/// +/// 标记标签计算结果 +/// +public class MarkLabelCalcResult +{ + /// + /// true 显示(在当前视频画面里面) + /// false 不显示(不在当前视频画面里面) + /// + public bool InFlag { get; set; } + + /// + /// 画布 left 距离 + /// + public double CanvasLeft { get; set; } + + /// + /// 画布 top 距离 + /// + public double CanvasTop { get; set; } + } \ No newline at end of file