003_Color色彩

1. Color色彩

1.1. Element为了避免视觉传达差异, 使用一套特定的调色板来规定颜色, 为你所搭建的产品提供一致的外观视觉感受。

2. 主色

2.1. Element主要品牌颜色是鲜艳、友好的蓝色。

3. 辅助色

3.1. 除了主色外的场景色, 需要在不同的场景中使用(例如危险色表示危险的操作)。

4. 中性色

import numpy as np import pandas as pd import os import colour from colour import SpectralDistribution, SpectralShape from colour.colorimetry import sd_to_XYZ, sd_ones from colour.appearance import XYZ_to_CIECAM02 from colour.utilities import tstack from scipy.spatial import ConvexHull from scipy.interpolate import interp1d # 从文件加载CIE 1931 XYZ色匹配函数数据 def load_cie_xyz_data(file_path): data = np.loadtxt(file_path, delimiter=",") wavelengths = data[:, 0] x = data[:, 1] y = data[:, 2] z = data[:, 3] return wavelengths, x, y, z # 从文件加载D65光源数据 def load_d65_data(file_path): data = np.loadtxt(file_path, delimiter=",") wavelengths = data[:, 0] power = data[:, 1] return wavelengths, power # 构建melanopic spectral sensitivity(符合CIE S 026/E-2018标准) def build_melanopic_spectral_sensitivity(xyz_wavelengths, x, y, z, target_wavelengths): S = -0.142 * x + 1.793 * y - 0.651 * z M = -0.321 * x + 1.746 * y + 0.575 * z L = 2.003 * x - 0.949 * y - 0.054 * z S /= np.max(S) if np.max(S) != 0 else 1 M /= np.max(M) if np.max(M) != 0 else 1 L /= np.max(L) if np.max(L) != 0 else 1 melanopic_response = 0.238 * S + 0.010 * M + 0.752 * L f = interp1d(xyz_wavelengths, melanopic_response, kind=&#39;linear&#39;, bounds_error=False, fill_value=0) mel_response = f(target_wavelengths) mel_response /= np.max(mel_response) if np.max(mel_response) != 0 else 1 return mel_response # 加载并预处理所有数据 def load_and_preprocess_data(): current_dir = os.path.dirname(os.path.abspath(__file__)) # 1. 读取SPD数据 spd_df = pd.read_excel(os.path.join(current_dir, "附录.xlsx"), sheet_name="Problem 1") spd_wavelengths = spd_df["波长"].values spd_power = spd_df["光强"].values assert len(spd_wavelengths) == 401, "SPD数据应为380-780nm(401个点)" spd_shape = SpectralShape(spd_wavelengths[0], spd_wavelengths[-1], 1) # 2. 读取XYZ数据 xyz_path = os.path.join(current_dir, "CIE_xyz_1931_2deg.csv") xyz_wavelengths, x_bar, y_bar, z_bar = load_cie_xyz_data(xyz_path) # 插值到SPD波长 f_x = interp1d(xyz_wavelengths, x_bar, kind=&#39;linear&#39;, bounds_error=False, fill_value=0) f_y = interp1d(xyz_wavelengths, y_bar, kind=&#39;linear&#39;, bounds_error=False, fill_value=0) f_z = interp1d(xyz_wavelengths, z_bar, kind=&#39;linear&#39;, bounds_error=False, fill_value=0) x_bar_spd = f_x(spd_wavelengths) y_bar_spd = f_y(spd_wavelengths) z_bar_spd = f_z(spd_wavelengths) # 构建CMF数据结构 cmfs = colour.MultiSpectralDistributions( data=np.column_stack([x_bar_spd, y_bar_spd, z_bar_spd]), wavelengths=spd_wavelengths, labels=[&#39;x&#39;, &#39;y&#39;, &#39;z&#39;] ) # 3. 读取D65光源数据 d65_path = os.path.join(current_dir, "CIE_std_illum_D65.csv") d65_wavelengths, d65_power = load_d65_data(d65_path) f_d65 = interp1d(d65_wavelengths, d65_power, kind=&#39;linear&#39;, bounds_error=False, fill_value=0) d65_power_spd = f_d65(spd_wavelengths) # 4. 读取测试色数据并修正反射率范围 test_color_path = os.path.join(current_dir, "CIE_srf_cfi_1nm.csv") mel_data = np.loadtxt(test_color_path, delimiter=",") assert mel_data.shape[1] == 100, "CIE_srf_cfi_1nm.csv应为100列" test_color_wl = mel_data[:, 0] test_color_spectra = mel_data[:, 1:100].T # 修正:反射率限制在[0,1]物理范围内 test_color_spectra_aligned = np.array([ np.clip(np.interp(spd_wavelengths, test_color_wl, spectrum, left=0, right=0), 0, 1) for spectrum in test_color_spectra ]) # 5. 构建melanopic响应 mel_response = build_melanopic_spectral_sensitivity( xyz_wavelengths, x_bar, y_bar, z_bar, spd_wavelengths ) melanopic_spd = SpectralDistribution(mel_response, spd_wavelengths) return (spd_wavelengths, spd_power, cmfs, d65_power_spd, melanopic_spd, test_color_spectra_aligned, spd_shape, xyz_wavelengths, x_bar, y_bar, z_bar) # 计算XYZ三刺激值(使用np.trapz积分) def calculate_xyz(spd_power, cmfs, wavelengths, spd_shape, xyz_wavelengths, x_bar, y_bar, z_bar): # 插值SPD到CIE波长 power_interp = np.interp(xyz_wavelengths, wavelengths, spd_power, left=0, right=0) # 使用np.trapz积分 y_integral = np.trapz(power_interp * y_bar, xyz_wavelengths) k = 100 / y_integral if y_integral > 1e-9 else 1 # 避免除零 manual_x = k * np.trapz(power_interp * x_bar, xyz_wavelengths) manual_y = k * np.trapz(power_interp * y_bar, xyz_wavelengths) manual_z = k * np.trapz(power_interp * z_bar, xyz_wavelengths) return np.array([manual_x, manual_y, manual_z]) # 计算TM30-20的Rf和Rg(修正异常值处理) def calculate_tm30_rf_rg(test_spd, ref_spd, cmfs, wavelengths, test_spectra, spd_shape): def get_cam_values(spd): cam_list = [] for ref_spectrum in test_spectra: # 修正:过滤异常反射率 ref_spectrum_clipped = np.clip(ref_spectrum, 0, 1) color_spd = spd * ref_spectrum_clipped XYZ = sd_to_XYZ( SpectralDistribution(color_spd, wavelengths), cmfs=cmfs, illuminant=sd_ones(spd_shape) ) # 避免零值导致计算错误 XYZ = np.clip(XYZ, 1e-9, None) # 计算D65参考白 d65_xyz = sd_to_XYZ( SpectralDistribution(ref_spd, wavelengths), cmfs=cmfs, illuminant=sd_ones(spd_shape) ) d65_xyz = np.clip(d65_xyz, 1e-9, None) cam = XYZ_to_CIECAM02( XYZ / XYZ[1], XYZ_w=d65_xyz / d65_xyz[1], L_A=318.3, # 标准1000lux环境 Y_b=20, surround=colour.VIEWING_CONDITIONS_CIECAM02[&#39;Average&#39;] ) cam_list.append([cam.h, cam.J, cam.C]) return np.array(cam_list) test_cam = get_cam_values(test_spd) ref_cam = get_cam_values(ref_spd) # 修正:过滤无效样本(解决Rf虚高问题) valid_mask = np.logical_and( np.isfinite(test_cam).all(axis=1), np.isfinite(ref_cam).all(axis=1) ) test_cam = test_cam[valid_mask] ref_cam = ref_cam[valid_mask] # 确保有足够样本 if len(test_cam) < 50: return 70.0, 100.0 # 计算Rf(过滤5%极端值) delta_E = np.sqrt( (test_cam[:, 1] - ref_cam[:, 1])**2 + (test_cam[:, 2] - ref_cam[:, 2])** 2 + (2 * np.sqrt(test_cam[:, 2] * ref_cam[:, 2]) * np.sin(np.radians((test_cam[:, 0] - ref_cam[:, 0])/2)))**2 ) # 修正:去除5%最大偏差值 delta_E = delta_E[delta_E < np.percentile(delta_E, 95)] Rf = np.clip(100 - 4.6 * np.mean(delta_E), 0, 99) # 避免物理上不可能的100 # 计算Rg(16色调区间平均) def get_gamut_area(cam_data): # 计算16个色调区间的平均坐标 h = np.arctan2(np.sin(np.radians(cam_data[:, 0])), np.cos(np.radians(cam_data[:, 0]))) * 180/np.pi h = (h + 360) % 360 # 标准化到0-360度 bins = np.linspace(0, 360, 17) bin_indices = np.digitize(h, bins) - 1 avg_coords = [] for i in range(16): mask = (bin_indices == i) if np.any(mask): avg_j = np.mean(cam_data[mask, 1]) avg_c = np.mean(cam_data[mask, 2]) avg_h = np.mean(cam_data[mask, 0]) avg_coords.append([avg_j, avg_c, avg_h]) # 转换为色度坐标计算凸包 if len(avg_coords) < 3: return 0 avg_coords = np.array(avg_coords) x = avg_coords[:, 1] * np.cos(np.radians(avg_coords[:, 2])) y = avg_coords[:, 1] * np.sin(np.radians(avg_coords[:, 2])) return ConvexHull(tstack([x, y])).volume test_area = get_gamut_area(test_cam) ref_area = get_gamut_area(ref_cam) # 修正:避免Rg超出合理范围 Rg = np.clip(100 * (test_area / ref_area) if ref_area > 1e-9 else 100, 50, 130) return round(Rf, 1), round(Rg, 1) # 计算mel-DER(使用np.trapz积分) def calculate_mel_der(spd_power, d65_power, mel_response, wavelengths): # 仅积分生理有效波段380-780nm valid_mask = (wavelengths >= 380) & (wavelengths <= 780) spd_integral = np.trapz( spd_power[valid_mask] * mel_response[valid_mask], wavelengths[valid_mask] ) d65_integral = np.trapz( d65_power[valid_mask] * mel_response[valid_mask], wavelengths[valid_mask] ) # 避免除以零和异常值 if d65_integral < 1e-9: return 0.0 mel_der = spd_integral / d65_integral return round(np.clip(mel_der, 0.01, 1.0), 3) # 主函数 def calculate_parameters(): (spd_wl, spd_power, cmfs, d65_power, melanopic_spd, test_spectra, spd_shape, xyz_wavelengths, x_bar, y_bar, z_bar) = load_and_preprocess_data() # 计算XYZ三刺激值 XYZ = calculate_xyz(spd_power, cmfs, spd_wl, spd_shape, xyz_wavelengths, x_bar, y_bar, z_bar) X, Y, Z = XYZ # 计算CIE 1960 UCS色坐标(u, v) denom = X + 15 * Y + 3 * Z u = 4 * X / denom if denom != 0 else 0 v = 6 * Y / denom if denom != 0 else 0 # 计算相关色温CCT(保留原有公式) xy_sum = X + Y + Z x = X / xy_sum if xy_sum != 0 else 0 y = Y / xy_sum if xy_sum != 0 else 0 n = (x - 0.3320) / (y - 0.1858) if (y - 0.1858) != 0 else 0 cct = -437 * n**3 + 3601 * n**2 - 6861 * n + 5514.31 cct = round(np.clip(cct, 1000, 20000), 1) # 限制合理范围 # 计算Duv(修正公式参数) if cct <= 7000: # 使用CIE 15:2004推荐的普朗克轨迹拟合参数 u0 = (0.860117757 + 1.54118254e-4 * cct + 1.28641212e-7 * cct**2) / \ (1 + 8.42420235e-4 * cct + 7.08145163e-7 * cct**2) v0 = (0.317398726 + 4.22806245e-5 * cct + 4.20481691e-8 * cct**2) / \ (1 - 2.89741816e-5 * cct + 1.61456053e-7 * cct**2) else: u0 = (0.821943765 + 1.48709244e-4 * cct - 5.40467925e-8 * cct**2) / \ (1 + 9.44518690e-4 * cct + 1.28785040e-6 * cct**2) v0 = (0.321018255 + 5.38541694e-5 * cct - 3.11079934e-8 * cct**2) / \ (1 + 6.83568563e-5 * cct + 5.47466662e-8 * cct**2) # 计算Duv并限制范围 duv = np.sqrt((u - u0)**2 + (v - v0)** 2) if v < v0: duv = -duv duv = round(np.clip(duv, -0.054, 0.054), 4) # CIE标准范围 # 计算Rf、Rg rf, rg = calculate_tm30_rf_rg( test_spd=spd_power, ref_spd=d65_power, cmfs=cmfs, wavelengths=spd_wl, test_spectra=test_spectra, spd_shape=spd_shape ) # 计算mel-DER mel_der = calculate_mel_der( spd_power=spd_power, d65_power=d65_power, mel_response=melanopic_spd.values, wavelengths=spd_wl ) # 输出结果 print("===== 计算结果 =====") print(f"CIE XYZ三刺激值: X={X:.2f}, Y={Y:.2f}, Z={Z:.2f}") print(f"CIE 1960 UCS色坐标: u={u:.4f}, v={v:.4f}") print(f"相关色温 (CCT): {cct} K") print(f"普朗克轨迹距离 (Duv): {duv}") print(f"保真度指数 (Rf, TM30-20): {rf}") print(f"色域指数 (Rg, TM30-20): {rg}") print(f"褪黑素日光照度比 (mel-DER): {mel_der}") if __name__ == "__main__": calculate_parameters() 帮我评估一下这段代码,可以的话让他好看些,不必要的修正可以省掉
08-11
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值