Files
JoyD/Windows/CS/Framework4.0/Toprie/Toprie/TemperatureData.cs

475 lines
18 KiB
C#
Raw Normal View History

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("数据长度不足,无法解析");
}
// 计算温度数据长度和像素总数
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);
}
}
}
}
/// <summary>
/// 将原始温度数据映射到512×384的实际温度矩阵
/// </summary>
/// <param name="sourceRow">源数据行索引</param>
/// <param name="sourceCol">源数据列索引</param>
/// <param name="temperature">温度值</param>
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;
}
/// <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;
}
}
}