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

如何在OpenCV中处理16位辐射图像且保留原始辐射值?

16位热像图可视化增强且保留原始辐射值的解决方案

问题背景

我用exiftools提取了热像图的原始辐射数据,需要增强图像可视化效果以方便标注生成分割mask,但必须保留16位灰度图的原始辐射值。提取的原始PNG过于灰暗难以看清,尝试过min-max归一化(单张图数值范围例如16792-19663,不同图像范围有差异),虽然能让图像适合标注,但会拉伸原始数值,不符合需求。

现有处理代码(会修改原始像素值):

for filename in glob.iglob("*.png"):
    if "raw" in filename:
        img = cv2.imread(filename, -1)
        #max = np.max(img)
        #min = np.min(img)
        img_16bits = cv2.normalize(img, None, 0, 65535, cv2.NORM_MINMAX, dtype = cv2.CV_16U)
        basename = os.path.splitext(os.path.basename(filename))[0]
        cv2.imwrite(basename+"_"+"16bits"+".png",img_16bits) 

观察到的现象:用plt.imshow搭配灰度色阶绘制时,可视化效果良好且原始数值未改变;导入ImageJ时,软件自动校正对比度也能保留原始数值,但自己修改代码始终无法实现相同效果。

原始图像与拉伸后图像对比:
Raw Image
Processed Image

核心思路

plt.imshow和ImageJ的增强逻辑都是在显示层面调整对比度(窗宽窗位),不修改原始像素数据。我们需要复刻这个逻辑:要么在标注时动态增强显示,要么生成嵌入显示映射信息的图像文件(保留原始像素)。

解决方案

方案1:标注时动态增强显示(不修改原始图像)

直接读取原始16位图像,仅在显示时做对比度映射,标注操作基于原始像素值:

import cv2
import numpy as np
import glob

for filename in glob.iglob("*.png"):
    if "raw" in filename:
        # 读取原始16位热像图,像素值完全保留
        img_raw = cv2.imread(filename, -1)
        
        # 用百分位计算显示范围(避免 outliers 影响对比度)
        p2, p98 = np.percentile(img_raw, (2, 98))
        # 生成仅用于显示的8位增强图像,不修改原始数据
        img_display = cv2.convertScaleAbs(img_raw, alpha=255/(p98-p2), beta=-255*p2/(p98-p2))
        
        # 显示增强后的图像,用于标注
        cv2.imshow("Enhanced Thermal Image (Raw Data Preserved)", img_display)
        
        # 在这里添加标注逻辑(例如鼠标绘制mask),mask基于原始图像的尺寸和像素位置
        # ... 你的标注代码 ...
        
        # 按ESC退出当前图像
        k = cv2.waitKey(0)
        if k == 27:
            break
cv2.destroyAllWindows()

方案2:生成带显示映射的图像文件(保留原始像素)

如果需要生成一个能直接打开看到清晰效果,但实际像素值仍为原始16位的文件,推荐使用TIFF格式(支持嵌入显示LUT):

from PIL import Image
import numpy as np
import glob
import os

for filename in glob.iglob("*.png"):
    if "raw" in filename:
        # 读取原始16位图像
        img_raw = np.array(Image.open(filename))
        
        # 计算基于百分位的显示范围
        p2, p98 = np.percentile(img_raw, (2, 98))
        # 创建显示用的LUT(查找表),将p2-p98区间映射到0-65535
        lut = np.linspace(0, 65535, 65536, dtype=np.uint16)
        lut = np.clip((lut - p2) * (65535/(p98-p2)), 0, 65535).astype(np.uint16)
        
        # 创建PIL图像并嵌入LUT,保存为TIFF
        img = Image.fromarray(img_raw, mode='I;16')
        save_path = os.path.splitext(filename)[0] + "_enhanced_display.tif"
        img.save(save_path, save_all=True, lut=lut)

生成的TIFF文件打开时会自动应用LUT显示增强效果,但用代码读取时得到的仍是原始16位辐射值。

为什么之前的方法不行

cv2.normalize会直接修改原始像素值,将数据拉伸到0-65535范围,破坏了原始辐射信息。而plt.imshow和ImageJ只是在显示时对像素值做临时映射,并未改变图像文件中的原始数据。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 12:01:20