- 开发板:lilygo-t-display-s3
- 开发框架:platformIO-arduino
- 其他组件:MLX90640 24x32 红外摄像头
- 红外图像采集:使用Adafruit的驱动库完成对红外图像的采集
- 红外图像显示:采集完成的数据,将会被插值后使用假彩色显示在屏幕上,屏幕的驱动库依赖于TFT-eSPI库
- 数据串口传输:采集完成的原始数据也会从串口发送出去,每次发送一帧的数据
主要完成:1.接收数据;2.显示假彩色图像;3.显示最高最低温度;4.保存一帧数据
import numpy as np
import serial
import csv,time,os
import cv2
def read_ir_data(port='/dev/ttyACM0', baud_rate=115200, output_dir='saved_images'):
"""
从串口读取一帧温度数据。
每帧数据为一行文本(以换行符结尾),其中包含768个用逗号分隔的浮点数。
显示伪彩色图像,并在按下 's' 键时保存图像,按 'q' 键退出。
"""
# 如果保存目录不存在,则创建
if not os.path.exists(output_dir):
os.makedirs(output_dir)
ser = None
# 持续尝试打开串口设备,直到成功连接
print(f"正在尝试从 {port} 连接设备...", end="")
while ser is None:
try:
ser = serial.Serial(port, baud_rate, timeout=1)
print(f"\nConnected to {port} at {baud_rate} baud")
except serial.SerialException as e:
print(".", end="")
time.sleep(1)
print(f"[INFO] 开始接收数据,按 'q' 退出,按 's' 保存伪彩色图像")
cv2.namedWindow("MLX90640 Video", cv2.WINDOW_NORMAL)
img_count = 0
try:
while True:
# 读取一行数据
try:
line = ser.readline().decode().strip()
except UnicodeDecodeError:
continue
if not line:
continue
parts = line.split(',')
if len(parts) != 768:
continue
try:
values = [float(x) for x in parts]
except ValueError:
continue
# 转换为 2D 数组、归一化,并生成伪彩色图像
frame = np.array(values).reshape((24, 32))
# 计算当前帧的最小温度和最大温度
min_temp_val = np.min(frame)
max_temp_val = np.max(frame)
norm_frame = np.clip((frame - min_temp_val) / (max_temp_val - min_temp_val), 0, 1)
norm_frame = (norm_frame * 255).astype(np.uint8)
color_frame = cv2.applyColorMap(norm_frame, cv2.COLORMAP_JET)
display_img = cv2.resize(color_frame, (320, 240), interpolation=cv2.INTER_CUBIC)
# 生成左侧伪彩色对照条,采用与归一化同一映射
bar_width = 50
bar_height = display_img.shape[0] # 与显示图像同高
# 生成垂直梯度,从上到下由 255 到 0
gradient = np.linspace(255, 0, bar_height, dtype=np.uint8)
gradient = np.tile(gradient.reshape(bar_height, 1), (1, bar_width))
color_bar = cv2.applyColorMap(gradient, cv2.COLORMAP_JET)
# 构造合成图像:左侧为对照条,右侧为显示图像,上部分整体高度与显示图像一致,下方为标注区域
text_height = 30
final_width = bar_width + display_img.shape[1]
final_height = display_img.shape[0] + text_height
final_img = np.zeros((final_height, final_width, 3), dtype=np.uint8)
# 将伪彩色对照条放在合成图像左侧
final_img[:bar_height, :bar_width] = color_bar
# 将显示图像放在对照条右侧
final_img[:bar_height, bar_width:final_width] = display_img
# 在合成图像的底部标注最小温度和最大温度
label = f"Min: {min_temp_val:.2f} C Max: {max_temp_val:.2f} C"
# 设置字体、大小及厚度
font = cv2.FONT_HERSHEY_SIMPLEX
font_scale = 0.7
thickness = 2
# 获取文本尺寸以便居中
(text_w, text_h), baseline = cv2.getTextSize(label, font, font_scale, thickness)
text_x = (final_width - text_w) // 2
text_y = bar_height + (text_height + text_h) // 2
cv2.putText(final_img, label, (text_x, text_y), font, font_scale, (255, 255, 255), thickness, cv2.LINE_AA)
# 显示图像
cv2.imshow("MLX90640 Video", final_img)
# 检测键盘按键
key = cv2.waitKey(1) & 0xFF
if key == ord('q'):
print("[INFO] 用户退出,程序结束。")
break
elif key == ord('s'):
# 按s键时保存图像
timestamp = time.strftime('%Y%m%d_%H%M%S')
filename = os.path.join(output_dir, f"ir_image_{timestamp}_{img_count}.png")
cv2.imwrite(filename, display_img)
img_count += 1
print(f"[INFO] 图像保存到: {filename}")
finally:
ser.close()
cv2.destroyAllWindows()
if __name__ == "__main__":
read_ir_data(port="/dev/ttyACM0",output_dir="1")