OpenCV计算I(x,y)/Io(x,y)时出现NaN及图像显示差异问题
问题原因与解决方案
一、cv2.meanStdDev返回NaN的原因及解决
核心原因
分母图像Io(x,y)中存在0值像素,导致除法运算生成NaN:
尼康NEF原始图像转灰度后,暗部区域(比如未校准的黑场、死像素)可能出现0值。当使用cv2.divide做除法时,0作为分母会直接产生NaN值,而cv2.meanStdDev无法处理包含NaN的数组,因此返回NaN。
解决办法
给分母加极小偏移量:
将图像转成浮点型后,给所有像素添加一个极小的非零值(比如1e-6),彻底避免除以0的情况:import rawpy import cv2 import numpy as np # 读取原始图像并转灰度 with rawpy.imread('init.nef') as raw: init_rgb = raw.postprocess() Io = cv2.cvtColor(init_rgb, cv2.COLOR_RGB2GRAY).astype(np.float32) + 1e-6 with rawpy.imread('brut.nef') as raw: brut_rgb = raw.postprocess() I = cv2.cvtColor(brut_rgb, cv2.COLOR_RGB2GRAY).astype(np.float32) # 执行除法 test = cv2.divide(I, Io)用numpy除法过滤0分母:
使用np.divide的where参数,指定仅在分母非0时执行除法,0分母的位置设为默认值(比如0):test = np.divide(I, Io, out=np.zeros_like(I, dtype=np.float32), where=Io != 0)
二、matplotlib与cv2.imshow显示差异的原因及解决
核心原因
两者对图像数据的处理逻辑完全不同:
- 数据范围与类型解析不同:
cv2.imshow仅正确显示两种格式:8位无符号整数(0-255)、32位浮点(0.0-1.0)。如果你的test是浮点型且范围超出0-1(比如I比Io亮,导致部分像素值>1),cv2.imshow会直接截断超出部分,显示为白色;而matplotlib.pyplot.imshow会自动将整个图像的像素值归一化到0-1范围后显示,所以明暗效果不同。- 若
test是16位整数,cv2.imshow会自动缩放到8位显示,matplotlib则默认按原始数值处理,导致亮度差异。
- 默认颜色映射不同:
cv2.imshow显示灰度图用线性灰度映射,而matplotlib默认使用viridis彩色映射(除非手动指定cmap='gray'),这会直接导致视觉上的颜色差异。
解决办法
统一数据格式与范围:
将test归一化到0-1浮点型或0-255的8位整数,确保两者解析规则一致:# 归一化到0-1浮点型 test_norm = cv2.normalize(test, None, 0.0, 1.0, cv2.NORM_MINMAX, dtype=cv2.CV_32F) # 或者转成0-255的8位整数 test_8bit = cv2.normalize(test, None, 0, 255, cv2.NORM_MINMAX, dtype=cv2.CV_8U)统一显示设置:
- 使用matplotlib时指定灰度映射:
import matplotlib.pyplot as plt plt.imshow(test_norm, cmap='gray') plt.show() - 使用
cv2.imshow直接显示归一化后的图像:cv2.imshow('test', test_norm) cv2.waitKey(0) cv2.destroyAllWindows()
这样两者的显示效果就会一致。
- 使用matplotlib时指定灰度映射:
内容的提问来源于stack exchange,提问作者Suntory
相关产品推荐
相关产品推荐

