使用np.clip实现pyplot imshow的vmax功能出现结果差异问题
问题:np.clip实现imshow的vmax效果与内置vmax结果不一致
我想用np.clip实现matplotlib中imshow的vmax功能,但对比内置vmax时得到不同结果。当size设为900这类大值时差异明显,设为10这类小值时结果正常,怀疑和uint8类型存储有关;手动给axs[0]和axs[1]添加vmin=0、vmax=255后,结果变得更异常。
复现代码
import numpy as np import matplotlib.pyplot as plt from PIL import Image np.random.seed(0) size = 900 percentile_clip = 30 data_source = np.random.rand(size, size) data_source = data_source * (255/data_source.max()) data_source = data_source.astype('uint8') calc_clip = int(np.percentile(data_source, percentile_clip)) img1 = Image.fromarray(data_source, mode='L') img2 = Image.fromarray(np.clip(data_source,0,calc_clip), mode='L') fig, axs = plt.subplots(1, 3,figsize=(16,8)) axs[0].imshow(img1,cmap='magma') axs[0].set_xlabel('img1') axs[1].imshow(img2,cmap='magma') axs[1].set_xlabel('img2 with clip') axs[2].imshow(img1,cmap='magma',vmin = 0, vmax = calc_clip) axs[2].set_xlabel('img1 with vmax') print('calc_clip:',calc_clip)
问题原因及解决方法
核心原因:uint8类型的截断与imshow的自动归一化逻辑差异
- np.clip的行为:对
uint8数组执行np.clip(data_source,0,calc_clip)后,所有大于calc_clip的值被直接设为calc_clip,数组仍为uint8类型。但imshow显示这个数组时,会自动把数组的最小值和最大值作为颜色映射范围(默认vmin=np.min(img2),vmax=np.max(img2)),此时img2的最大值是calc_clip,颜色映射会把0~calc_clip对应到colormap的全范围。 - imshow的vmax参数行为:
axs[2]中设置vmax=calc_clip时,颜色映射范围是0~calc_clip,原数组中大于calc_clip的值会被映射到colormap的最大值,而非修改数据本身。这是两者的本质区别:np.clip修改数据,vmax仅修改颜色映射规则。
大size时差异明显的原因
当size较小时,原数据的数值分布更集中,np.clip后的数据分布和原数据在0~calc_clip范围内的分布差异不大;而size很大时,原数据中大于calc_clip的数值数量更多,np.clip把这些值都压到calc_clip,导致img2的数值分布和原数据在颜色映射下的表现差异显著。
让np.clip效果与vmax一致的方法
如果要让img2的显示效果和axs[2]一致,需要在显示img2时手动指定颜色映射范围为原数据的0~255,这样被clip到calc_clip的值会对应colormap中calc_clip的位置,和vmax=calc_clip时原数据中大于calc_clip的值映射到同一位置:
axs[1].imshow(img2, cmap='magma', vmin=0, vmax=255)
添加vmin=0、vmax=255后结果异常的原因
原axs[0]显示img1时,默认vmin=np.min(img1)(0)、vmax=np.max(img1)(255),所以添加该参数和默认行为一致;但axs[1]添加该参数后,会把img2中0calc_clip的数值映射到0255的颜色范围,相当于拉伸了颜色,因此和axs[2]的差异更大。
内容的提问来源于stack exchange,提问作者kaycaborr
相关产品推荐
相关产品推荐

