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