using MathNet.Numerics.LinearAlgebra; using System.Drawing; 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 }