You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

热帧是否被转为RGB?TIFF热帧温度值提取正确性问询

问题

我有一批TIFF帧,想通过计算温度值来确定呼吸速率,但不确定代码是否正确提取了温度值——目前输出的信号看起来像是随机值,怀疑代码自动把帧转成了RGB或8位灰度图,导致原始16位温度数据丢失。

原始代码

import tifffile as tiff
import cv2
import numpy as np
import os
import argparse
import csv
import pandas as pd
import matplotlib.pyplot as plt


# Construct argument parser and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-v", "--video", required=True, help="Path to input  directory containing TIFF files")
args = vars(ap.parse_args())

input_dir = args["video"]
print("Input directory: " + input_dir)

def select_roi(image):
    """
    Allows user to select a region of interest (ROI) from the 16-bit image.
    """
    # Normalize 16-bit data for display without altering its original values
    normalized_image = cv2.normalize(image, None, 0, 255, cv2.NORM_MINMAX).astype(np.uint8)
    color_map = cv2.applyColorMap(normalized_image, cv2.COLORMAP_JET)
    cv2.imshow("Select Nose", color_map)
    bbox = cv2.selectROI("Select Nose", color_map, fromCenter=False, showCrosshair=True)
    cv2.destroyAllWindows()

    # Print ROI coordinates
    x, y, w, h = bbox
    print(f"ROI's boundary is x1={x}, y1={y}, x2={x+w}, y2={y+h}")
    return bbox

# Open CSV file for writing results
csv_file = "nose_pixel_values.csv"
with open(csv_file, mode='w', newline='') as file:
    writer = csv.writer(file)
    writer.writerow(["Frame", "Average Pixel Value"])  # CSV header

# Initialize ROI coordinates
x, y, w, h = None, None, None, None

# Loop through all TIFF files in the specified directory
for tiff_file in sorted(os.listdir(input_dir)):
    if tiff_file.endswith(".tiff"):
        file_path = os.path.join(input_dir, tiff_file)
        with tiff.TiffFile(file_path) as tif:
            gray16_frames = tif.asarray()
        print(f"Processing '{tiff_file}' with {len(gray16_frames)} frames.")

        # Get ROI from the first frame of the first TIFF file only
        if x is None:
            first_frame = gray16_frames[0]
            bbox = select_roi(first_frame)
            x, y, w, h = bbox

        for i, frame in enumerate(gray16_frames):
            # Extract the ROI based on the selected bbox
            roi = frame[y:y+h, x:x+w]

            # Calculate the average pixel value in the ROI (16-bit)
            avg_pixel_value = np.mean(roi)

            print(f"Frame {i}: Average pixel value : {avg_pixel_value}")
            print(f"Frame {i}: shape : {frame.shape}, data type : {frame.dtype}, min: {frame.min()}, max: {frame.max()}")

            # Write the result to the CSV file
            with open(csv_file, mode='a', newline='') as file:
                writer = csv.writer(file)
                writer.writerow([i, avg_pixel_value])

print(f"Finished processing all files.")

问题分析

你的代码并未丢失16位原始数据(tifffile.asarray()默认会保留uint16格式),但输出看起来像随机值的核心原因是:

  • 每个TIFF文件内的帧索引都从0开始,导致CSV中帧索引重复,序列完全混乱
  • 频繁打开/关闭CSV文件的操作可能导致数据写入不连续,进一步加剧"随机"假象
  • 缺少像素值到实际温度的校准转换(如果预期输出是温度值而非原始像素值)

修正后的代码

import tifffile as tiff
import cv2
import numpy as np
import os
import argparse
import pandas as pd
import matplotlib.pyplot as plt

# 构造参数解析器
ap = argparse.ArgumentParser()
ap.add_argument("-v", "--video", required=True, help="输入TIFF文件所在目录路径")
args = vars(ap.parse_args())

input_dir = args["video"]
print(f"输入目录: {input_dir}")

def select_roi(image):
    """从16位图像中选择感兴趣区域(ROI)"""
    # 仅为显示做归一化,不修改原始16位数据
    normalized_image = cv2.normalize(image, None, 0, 255, cv2.NORM_MINMAX).astype(np.uint8)
    color_map = cv2.applyColorMap(normalized_image, cv2.COLORMAP_JET)
    cv2.imshow("选择鼻子区域", color_map)
    bbox = cv2.selectROI("选择鼻子区域", color_map, fromCenter=False, showCrosshair=True)
    cv2.destroyAllWindows()

    x, y, w, h = bbox
    print(f"ROI边界: x1={x}, y1={y}, x2={x+w}, y2={y+h}")
    return bbox

# 初始化全局帧计数器和结果缓存列表
total_frame_idx = 0
results = []

# 初始化ROI坐标
x, y, w, h = None, None, None, None

# 遍历目录下所有TIFF文件
for tiff_file in sorted(os.listdir(input_dir)):
    if tiff_file.endswith(".tiff"):
        file_path = os.path.join(input_dir, tiff_file)
        # 读取原始16位TIFF帧
        with tiff.TiffFile(file_path) as tif:
            gray16_frames = tif.asarray()
        print(f"处理文件: {tiff_file}, 帧数量: {len(gray16_frames)}")

        # 仅从第一个TIFF文件的第一帧选择ROI
        if x is None:
            first_frame = gray16_frames[0]
            bbox = select_roi(first_frame)
            x, y, w, h = bbox

        # 处理当前文件的每帧
        for frame in gray16_frames:
            # 提取ROI,确保使用原始16位数据
            roi = frame[y:y+h, x:x+w]
            # 计算ROI平均像素值,保留高精度
            avg_pixel_value = np.mean(roi, dtype=np.float64)
            
            print(f"全局帧索引: {total_frame_idx}, 平均像素值: {avg_pixel_value}")
            print(f"帧属性: 形状={frame.shape}, 数据类型={frame.dtype}, 最小值={frame.min()}, 最大值={frame.max()}")
            
            # 缓存结果,避免频繁IO
            results.append([total_frame_idx, avg_pixel_value])
            total_frame_idx += 1

# 批量写入CSV
csv_file = "nose_pixel_values.csv"
df = pd.DataFrame(results, columns=["Frame", "Average Pixel Value"])
df.to_csv(csv_file, index=False)
print(f"处理完成,结果已保存至 {csv_file}")

# 绘制温度变化曲线,方便与预期输出对比
plt.figure(figsize=(10,6))
plt.plot(df["Frame"], df["Average Pixel Value"], label="ROI平均像素值")
plt.xlabel("帧索引")
plt.ylabel("平均像素值(对应温度)")
plt.title("呼吸相关温度变化曲线")
plt.legend()
plt.savefig("temperature_curve.png")
plt.show()

关键改进点

  1. 全局帧索引:用total_frame_idx替代局部索引,保证所有TIFF文件的帧序列连贯,彻底解决索引重复导致的"随机值"问题
  2. 批量IO操作:用列表缓存所有结果,最后通过Pandas一次性写入CSV,提升效率并避免数据写入异常
  3. 保留数据精度:计算均值时指定np.float64,确保16位原始数据的精度不丢失
  4. 可视化验证:新增曲线绘制功能,直接生成变化趋势图,方便和预期输出对比

额外建议

如果你的预期输出是实际温度值而非原始像素值,需要添加设备校准转换:

# 示例:根据设备校准公式转换为温度(请替换为你的设备参数)
calibration_a = 0.1  # 斜率
calibration_b = 20   # 截距
df["Temperature"] = df["Average Pixel Value"] * calibration_a + calibration_b

内容的提问来源于stack exchange,提问作者Kingshook

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 00:24:54