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;
}
}
}