Files
JoyD/Windows/CS/Framework4.0/Toprie/Toprie/TemperatureData.cs
2025-11-06 09:28:50 +08:00

471 lines
17 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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
{
/// <summary>
/// 温度数据模型类,用于存储和处理红外热像仪采集的温度数据
/// </summary>
public class TemperatureData
{
/// <summary>
/// 静态温度数据映射表,线程安全
/// 键宽_高
/// 值x_y -> x,y
/// </summary>
private static readonly ConcurrentDictionary<string, ConcurrentDictionary<string,List<int>>> _temperatureMap = new ConcurrentDictionary<string, ConcurrentDictionary<string, List<int>>>();
/// <summary>
/// 原始温度数据矩阵(未温补)
/// 格式: [行][列]
/// </summary>
public float[,] TemperatureMatrix { get; private set; }
/// <summary>
/// 实际温度数据矩阵温补后的512×384矩阵
/// 格式: [行][列]
/// </summary>
public float[,] RealTemperatureMatrix { get; private set; }
/// <summary>
/// 温度数据的宽度(列数)
/// </summary>
public int Width { get; private set; }
/// <summary>
/// 温度数据的高度(行数)
/// </summary>
public int Height { get; private set; }
/// <summary>
/// 温度采集时间戳
/// </summary>
public DateTime Timestamp { get; set; }
/// <summary>
/// 最低温度值
/// </summary>
public float MinTemperature { get; private set; }
/// <summary>
/// 最高温度值
/// </summary>
public float MaxTemperature { get; private set; }
/// <summary>
/// 平均温度值
/// </summary>
public float AverageTemperature { get; private set; }
/// <summary>
/// 根据原始数据创建温度数据实例
/// </summary>
/// <param name="rawData">原始温度数据每个温度点为2字节</param>
/// <param name="width">温度矩阵宽度</param>
/// <param name="height">温度矩阵高度</param>
/// <param name="compensationValue">温度补偿值从sdk_get_comp_temp获取</param>
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<String,List<int>> map = _temperatureMap.GetOrAdd(key, new ConcurrentDictionary<string, List<int>>());
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<int> toXY = map.GetOrAdd(key, new List<int>());
toXY.Add(row);
toXY.Add(col);
}
}
}
ParseRawData(rawData, compensationValue);
CalculateStatistics();
}
/// <summary>
/// 解析原始温度数据
/// </summary>
/// <param name="rawData">原始温度数据</param>
/// <param name="compensationValue">温度补偿值</param>
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("数据长度不足,无法解析");
}
String key = $"{Width}_{Height}";
bool hasMap = _temperatureMap.TryGetValue(key, out ConcurrentDictionary<String, List<int>> map);
// 计算温度数据长度和像素总数
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] = compensatedTemperature;
if (hasMap)
{
// 温补后的温度存入RealTemperatureMatrix映射到512×384矩阵
MapToRealTemperatureMatrix($"{col}_{row}", map, compensatedTemperature);
}
}
}
}
}
/// <summary>
/// 将原始温度数据映射到512×384的实际温度矩阵
/// </summary>
/// <param name="key">映射键</param>
/// <param name="map">映射表</param>
/// <param name="temperature">温度值</param>
private void MapToRealTemperatureMatrix(String key, ConcurrentDictionary<String, List<int>> map, float temperature)
{
if (!map.TryGetValue(key, out List<int> xy)) return;
for (int idx = 0; idx < xy.Count; idx += 2)
{
// 设置实际温度值
RealTemperatureMatrix[xy[idx], xy[idx+1]] = temperature;
}
}
/// <summary>
/// 计算温度统计信息
/// </summary>
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;
}
/// <summary>
/// 获取指定区域内的温度统计信息
/// </summary>
/// <param name="startRow">起始行</param>
/// <param name="startCol">起始列</param>
/// <param name="endRow">结束行</param>
/// <param name="endCol">结束列</param>
/// <returns>区域温度统计信息</returns>
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);
}
/// <summary>
/// 将温度矩阵转换为字节数组(用于保存或传输)
/// </summary>
/// <returns>温度数据的字节数组表示</returns>
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;
}
/// <summary>
/// 从字节数组创建TemperatureData实例
/// </summary>
/// <param name="data">包含温度数据的字节数组</param>
/// <returns>TemperatureData实例</returns>
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;
}
/// <summary>
/// 私有构造函数用于FromByteArray方法
/// </summary>
private TemperatureData()
{
// 初始化实际温度矩阵
RealTemperatureMatrix = new float[384, 512];
}
/// <summary>
/// 验证温度数据的头部信息
/// </summary>
/// <param name="rawData">包含头部信息的原始数据</param>
/// <returns>如果头部信息有效返回true否则返回false</returns>
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;
}
/// <summary>
/// 从头部解析温度数据长度
/// </summary>
/// <param name="rawData">包含头部信息的原始数据</param>
/// <returns>温度数据长度(字节数)</returns>
private int GetPayloadLengthFromHeader(byte[] rawData)
{
// 根据SDK实际实现payload_length在头部的第5-8字节
// 注意:采用低字节在前的存储方式
if (rawData.Length >= 9)
{
return BitConverter.ToInt32(rawData, 5);
}
return 0;
}
/// <summary>
/// 根据像素数量和已知分辨率比例推算宽高
/// </summary>
/// <param name="pixelCount">像素总数</param>
/// <returns>宽高数组,格式为[宽度, 高度]</returns>
private int[] GetWidthHeightByPixelCount(int pixelCount)
{
// 已知设备支持的分辨率比例为4:3
// 检查是否为标准分辨率:
// 240×180 = 43200像素
// 384×288 = 110592像素
// 标准分辨率映射
var standardResolutions = new Dictionary<int, int[]> {
{ 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 };
}
}
/// <summary>
/// 区域温度统计信息
/// </summary>
public struct RegionTemperatureStats
{
/// <summary>
/// 区域最低温度
/// </summary>
public float MinTemperature { get; }
/// <summary>
/// 区域最高温度
/// </summary>
public float MaxTemperature { get; }
/// <summary>
/// 区域平均温度
/// </summary>
public float AverageTemperature { get; }
/// <summary>
/// 统计的温度点数量
/// </summary>
public int PointCount { get; }
/// <summary>
/// 构造函数
/// </summary>
/// <param name="minTemp">最低温度</param>
/// <param name="maxTemp">最高温度</param>
/// <param name="avgTemp">平均温度</param>
/// <param name="count">温度点数量</param>
public RegionTemperatureStats(float minTemp, float maxTemp, float avgTemp, int count)
{
MinTemperature = minTemp;
MaxTemperature = maxTemp;
AverageTemperature = avgTemp;
PointCount = count;
}
}
}