using MathNet.Numerics.LinearAlgebra; using System.Collections.Concurrent; using System.Drawing; namespace Cis.Application.Core; public abstract class MarkSearcherBase { #region Attr /// /// 当前相机参数信息 /// protected CameraCalcInfo _cameraCalcInfo { get; set; } /// /// _markLabelInfoList 锁对象,写锁 /// private static readonly object mliListLock = new(); /// /// 需要计算的 markLabelInfo 列表 /// protected ConcurrentDictionary _markLabelInfoDict { get; set; } = new(); protected MatrixBuilder MBuilder { get; set; } = Matrix.Build; /// /// 世界坐标转化为相机坐标矩阵 /// protected Matrix World2CameraMatrix { get; set; } #endregion Attr public MarkSearcherBase(CameraCalcInfo cameraCalcInfo) { UpdateCameraCalcInfoRelated(cameraCalcInfo); } #region Calc /// /// 计算标签位置过程 /// /// public List Calc() { List resultList = new(); if (World2CameraMatrix == null || _markLabelInfoDict.IsEmpty) return resultList; foreach (MarkLabelCalcInfo item in _markLabelInfoDict.Values) { Matrix labelC2WMatrix = ConvertCameraToWorld(item); Matrix labelPointMatrix = MBuilder.DenseOfArray(new double[,] { { item.CanvasWidth / _cameraCalcInfo.ImageWidth - 0.5 }, { item.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; if (x > 0.99 || x < 0.01 || y > 0.99 || y < 0.01 || pResult[2, 0] < 0) labelCalcResult = MarkLabelCalcResult.New(item.Id, false); else labelCalcResult = MarkLabelCalcResult.New(item.Id, true, (x * _cameraCalcInfo.ImageWidth), (y * _cameraCalcInfo.ImageHeight)); resultList.Add(labelCalcResult); } return resultList; } public async Task> CalcAsync() { return await Task.Run(Calc); } /// /// 判断相机是否进行了转动,转动了则需要重新计算世界坐标到相机坐标的转换矩阵 /// /// /// protected bool IsCameraRotate(PtzInfo newInfo) { return _cameraCalcInfo.PtzInfo.Pan != newInfo.Pan || _cameraCalcInfo.PtzInfo.Tilt != newInfo.Tilt || _cameraCalcInfo.PtzInfo.Zoom != newInfo.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 #region Operate Attr protected void UpdateCameraCalcInfoRelated(CameraCalcInfo cameraCalcInfo) { _cameraCalcInfo = cameraCalcInfo; CalcSensor(); World2CameraMatrix = ConvertWorldToCamera(cameraCalcInfo); } public void UpdateCameraCalcInfo(PtzInfo ptzInfo) { if (IsCameraRotate(ptzInfo)) { _cameraCalcInfo.PtzInfo = ptzInfo; World2CameraMatrix = ConvertWorldToCamera(_cameraCalcInfo); } } public bool AddMarkLabelCalcInfo(MarkLabelCalcInfo info) { lock (mliListLock) { if (_markLabelInfoDict.ContainsKey(info.Id)) return false; return _markLabelInfoDict.TryAdd(info.Id, info); } } public bool DeleteMarkLabelCalcInfo(long id) { lock (mliListLock) { if (!_markLabelInfoDict.ContainsKey(id)) return true; return _markLabelInfoDict.Remove(id); } } public bool ExistsMarkLabelCalcInfo(long id) { if (_markLabelInfoDict.ContainsKey(id)) return true; return false; } #endregion Operate Attr }