using System; using System.Collections.Generic; using System.Collections.Concurrent; using System.Linq; using System.Text; using System.Threading.Tasks; namespace JoyD.Windows.CS.Toprie { /// /// 温度数据模型类,用于存储和处理红外热像仪采集的温度数据 /// public class TemperatureData { /// /// 静态温度数据映射表,线程安全 /// 键:宽_高 /// 值:x_y -> x,y /// private static readonly ConcurrentDictionary>> _temperatureMap = new ConcurrentDictionary>>(); /// /// 原始温度数据矩阵(未温补) /// 格式: [行][列] /// public float[,] TemperatureMatrix { get; private set; } /// /// 实际温度数据矩阵(温补后的512×384矩阵) /// 格式: [行][列] /// public float[,] RealTemperatureMatrix { get; private set; } /// /// 温度数据的宽度(列数) /// public int Width { get; private set; } /// /// 温度数据的高度(行数) /// public int Height { get; private set; } /// /// 温度采集时间戳 /// public DateTime Timestamp { get; set; } /// /// 最低温度值 /// public float MinTemperature { get; private set; } /// /// 最高温度值 /// public float MaxTemperature { get; private set; } /// /// 平均温度值 /// public float AverageTemperature { get; private set; } /// /// 根据原始数据创建温度数据实例 /// /// 原始温度数据(每个温度点为2字节) /// 温度矩阵宽度 /// 温度矩阵高度 /// 温度补偿值(从sdk_get_comp_temp获取) public TemperatureData(byte[] rawData, int width, int height, float compensationValue = 0) { if (rawData == null) throw new ArgumentNullException(nameof(rawData)); if (width <= 0 || height <= 0) throw new ArgumentException("宽度和高度必须大于0"); Width = width; Height = height; TemperatureMatrix = new float[height, width]; // 创建512×384的实际温度矩阵 RealTemperatureMatrix = new float[384, 512]; Timestamp = DateTime.Now; String key = $"{width}_{height}"; ConcurrentDictionary> map = _temperatureMap.GetOrAdd(key, new ConcurrentDictionary>()); if (map.IsEmpty) { // 计算映射比例(从原始分辨率映射到512×384) float rowRatio = 384.0f / Height; float colRatio = 512.0f / Width; for (int row = 0; row < 384; row++) { for (int col = 0; col < 512; col++) { int x = (int)(col / colRatio); int y = (int)(row / rowRatio); key = $"{x}_{y}"; List toXY = map.GetOrAdd(key, new List()); toXY.Add(row); toXY.Add(col); } } } ParseRawData(rawData, compensationValue); CalculateStatistics(); } /// /// 解析原始温度数据 /// /// 原始温度数据 /// 温度补偿值 private void ParseRawData(byte[] rawData, float compensationValue) { // 检查数据是否有效 if (rawData == null || rawData.Length == 0) { throw new ArgumentNullException(nameof(rawData), "原始温度数据为空"); } // 根据SDK文档要求,使用24字节头部 const int HEADER_SIZE = 24; // 确保数据长度足够(至少包含头部) if (rawData.Length < HEADER_SIZE) { throw new ArgumentException("数据长度不足,无法解析"); } // 计算温度数据长度和像素总数 int dataLength = rawData.Length - HEADER_SIZE; int pixelCount = dataLength / 2; // 每个像素2字节 // 计算最大可处理的像素数 int maxPixels = Math.Min(pixelCount, Width * Height); // 跳过24字节头部,按照行优先顺序解析温度数据 for (int i = 0; i < maxPixels; i++) { // 计算行列索引 int row = i / Width; int col = i % Width; if (row < Height && col < Width) { // 计算数据偏移量(跳过24字节头部) int offset = HEADER_SIZE + i * 2; if (offset + 1 < rawData.Length) { // 根据SDK文档要求,温度值计算方法:(H×256+L)/10,单位为摄氏度 // H为高8位,L为低8位 int highByte = rawData[offset + 1]; // 高8位 int lowByte = rawData[offset]; // 低8位 int tempValue = (highByte << 8) | lowByte; // 计算实际温度值:(H×256+L)/10 float rawTemperature = tempValue / 10.0f; // 应用温度补偿值 float compensatedTemperature = rawTemperature + compensationValue; // 原始温度存入TemperatureMatrix(未温补) TemperatureMatrix[row, col] = rawTemperature; // 温补后的温度存入RealTemperatureMatrix(映射到512×384矩阵) MapToRealTemperatureMatrix(row, col, compensatedTemperature); } } } } /// /// 将原始温度数据映射到512×384的实际温度矩阵 /// /// 源数据行索引 /// 源数据列索引 /// 温度值 private void MapToRealTemperatureMatrix(int sourceRow, int sourceCol, float temperature) { // 计算映射比例(从原始分辨率映射到512×384) float rowRatio = 384.0f / Height; float colRatio = 512.0f / Width; // 计算目标位置 int targetRow = (int)(sourceRow * rowRatio); int targetCol = (int)(sourceCol * colRatio); // 确保目标位置在有效范围内 targetRow = Math.Min(383, Math.Max(0, targetRow)); targetCol = Math.Min(511, Math.Max(0, targetCol)); // 设置实际温度值 RealTemperatureMatrix[targetRow, targetCol] = temperature; } /// /// 计算温度统计信息 /// private void CalculateStatistics() { float sum = 0; float min = float.MaxValue; float max = float.MinValue; int count = 0; for (int row = 0; row < Height; row++) { for (int col = 0; col < Width; col++) { float temp = TemperatureMatrix[row, col]; sum += temp; min = Math.Min(min, temp); max = Math.Max(max, temp); count++; } } MinTemperature = min; MaxTemperature = max; AverageTemperature = count > 0 ? sum / count : 0; } /// /// 获取指定区域内的温度统计信息 /// /// 起始行 /// 起始列 /// 结束行 /// 结束列 /// 区域温度统计信息 public RegionTemperatureStats GetRegionStatistics(int startRow, int startCol, int endRow, int endCol) { // 参数验证和边界检查 startRow = Math.Max(0, startRow); startCol = Math.Max(0, startCol); endRow = Math.Min(Height - 1, endRow); endCol = Math.Min(Width - 1, endCol); if (startRow > endRow || startCol > endCol) return new RegionTemperatureStats(0, 0, 0, 0); float sum = 0; float min = float.MaxValue; float max = float.MinValue; int count = 0; for (int row = startRow; row <= endRow; row++) { for (int col = startCol; col <= endCol; col++) { float temp = TemperatureMatrix[row, col]; sum += temp; min = Math.Min(min, temp); max = Math.Max(max, temp); count++; } } float avg = count > 0 ? sum / count : 0; return new RegionTemperatureStats(min, max, avg, count); } /// /// 将温度矩阵转换为字节数组(用于保存或传输) /// /// 温度数据的字节数组表示 public byte[] ToByteArray() { // 创建一个包含所有必要信息的字节数组 // 包括宽度、高度、时间戳和温度数据 int headerSize = 4 + 4 + 8; // width(4) + height(4) + timestamp(8) int dataSize = Width * Height * 4; // 每个温度值使用4字节float byte[] result = new byte[headerSize + dataSize]; // 写入头信息 Buffer.BlockCopy(BitConverter.GetBytes(Width), 0, result, 0, 4); Buffer.BlockCopy(BitConverter.GetBytes(Height), 0, result, 4, 4); Buffer.BlockCopy(BitConverter.GetBytes(Timestamp.Ticks), 0, result, 8, 8); // 写入温度数据 int dataIndex = headerSize; for (int row = 0; row < Height; row++) { for (int col = 0; col < Width; col++) { byte[] tempBytes = BitConverter.GetBytes(TemperatureMatrix[row, col]); Buffer.BlockCopy(tempBytes, 0, result, dataIndex, 4); dataIndex += 4; } } return result; } /// /// 从字节数组创建TemperatureData实例 /// /// 包含温度数据的字节数组 /// TemperatureData实例 public static TemperatureData FromByteArray(byte[] data) { if (data == null || data.Length < 16) // 至少需要头信息的长度 throw new ArgumentException("无效的数据格式"); int width = BitConverter.ToInt32(data, 0); int height = BitConverter.ToInt32(data, 4); long ticks = BitConverter.ToInt64(data, 8); DateTime timestamp = new DateTime(ticks); // 验证数据完整性 int expectedSize = 16 + width * height * 4; if (data.Length != expectedSize) throw new ArgumentException("数据长度不匹配"); // 创建温度矩阵 float[,] matrix = new float[height, width]; int dataIndex = 16; for (int row = 0; row < height; row++) { for (int col = 0; col < width; col++) { matrix[row, col] = BitConverter.ToSingle(data, dataIndex); dataIndex += 4; } } // 创建TemperatureData实例 TemperatureData tempData = new TemperatureData { Width = width, Height = height, TemperatureMatrix = matrix, Timestamp = timestamp }; tempData.CalculateStatistics(); return tempData; } /// /// 私有构造函数,用于FromByteArray方法 /// private TemperatureData() { // 初始化实际温度矩阵 RealTemperatureMatrix = new float[384, 512]; } /// /// 验证温度数据的头部信息 /// /// 包含头部信息的原始数据 /// 如果头部信息有效返回true,否则返回false private bool ValidateHeader(byte[] rawData) { // 根据SDK实际实现,头部信息的前几个字节包含标识字符和数据长度 // 检查原始数据不为空且长度足够 if (rawData == null || rawData.Length < 9) return false; // 1. 验证头部标识字符是否为"+TEMP" string headerMark = Encoding.ASCII.GetString(rawData, 0, 5).TrimEnd('\0'); if (headerMark != "+TEMP") { System.Diagnostics.Debug.WriteLine($"警告: 头部标识不匹配,期望'+TEMP',实际为'{headerMark}'"); return false; } // 2. 检查数据长度是否合理(至少包含头部+最小温度数据) if (rawData.Length < 9 + 2) // 9字节头部 + 至少1个温度点(2字节) return false; return true; } /// /// 从头部解析温度数据长度 /// /// 包含头部信息的原始数据 /// 温度数据长度(字节数) private int GetPayloadLengthFromHeader(byte[] rawData) { // 根据SDK实际实现,payload_length在头部的第5-8字节 // 注意:采用低字节在前的存储方式 if (rawData.Length >= 9) { return BitConverter.ToInt32(rawData, 5); } return 0; } /// /// 根据像素数量和已知分辨率比例推算宽高 /// /// 像素总数 /// 宽高数组,格式为[宽度, 高度] private int[] GetWidthHeightByPixelCount(int pixelCount) { // 已知设备支持的分辨率比例为4:3 // 检查是否为标准分辨率: // 240×180 = 43200像素 // 384×288 = 110592像素 // 标准分辨率映射 var standardResolutions = new Dictionary { { 43200, new int[] { 240, 180 } }, { 110592, new int[] { 384, 288 } } }; // 如果匹配标准分辨率,安全地返回对应值 if (standardResolutions.TryGetValue(pixelCount, out int[] resolution)) { return resolution; } // 否则尝试根据像素数推算最接近4:3比例的宽高 float ratio = 4.0f / 3.0f; int width = (int)Math.Sqrt(pixelCount * ratio); int height = (int)(width / ratio); // 确保宽高相乘尽可能接近像素数 while (width * height < pixelCount && width * (height + 1) <= pixelCount) { height++; } return new int[] { width, height }; } } /// /// 区域温度统计信息 /// public struct RegionTemperatureStats { /// /// 区域最低温度 /// public float MinTemperature { get; } /// /// 区域最高温度 /// public float MaxTemperature { get; } /// /// 区域平均温度 /// public float AverageTemperature { get; } /// /// 统计的温度点数量 /// public int PointCount { get; } /// /// 构造函数 /// /// 最低温度 /// 最高温度 /// 平均温度 /// 温度点数量 public RegionTemperatureStats(float minTemp, float maxTemp, float avgTemp, int count) { MinTemperature = minTemp; MaxTemperature = maxTemp; AverageTemperature = avgTemp; PointCount = count; } } }