如何在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时,软件自动校正对比度也能保留原始数值,但自己修改代码始终无法实现相同效果。
原始图像与拉伸后图像对比:

核心思路
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
相关产品推荐
相关产品推荐

