
本教程针对色觉障碍辅助场景,提出一种融合hsv颜色模型与otsu二值化的蓝区量化方法,有效解决rgb空间下灰/白图像误判为“高蓝度”的问题,确保评分真正反映大面积、高饱和度的蓝色主导区域。
本教程针对色觉障碍辅助场景,提出一种融合hsv颜色模型与otsu二值化的蓝区量化方法,有效解决rgb空间下灰/白图像误判为“高蓝度”的问题,确保评分真正反映大面积、高饱和度的蓝色主导区域。
在RGB色彩空间中直接比较R/G/B通道值(如 blue > red + 40)存在根本性局限:它无法区分光谱意义上的蓝色与亮度均衡的灰色/白色(例如 (120,120,120) 在RGB中满足 blue > red+40 不成立,但 (105,100,100) 却可能因微小偏差被误判)。更关键的是,RGB对人眼感知的“蓝色感”(即色调 hue 和饱和度 saturation)不敏感——一个低饱和、高亮度的青灰像素(如 (180,200,210))在RGB中蓝通道值很高,却毫无视觉蓝色特征。这正是 image20.png(纯灰阶图)仍获得非零强度分(24.97%)的根本原因:其蓝色通道噪声或轻微偏移被原始掩码捕获,而该掩码未排除低饱和度区域。
✅ 正确解法是转向人类视觉更一致的HSV色彩空间:
-
Hue(色调) 精确定义“是否为蓝色”(蓝相区间约为
180°–270°,即0.5–0.75归一化值); - Saturation(饱和度) 过滤灰/白(灰阶饱和度 ≈ 0);
- Value(明度) 可选控制最低亮度,避免过暗不可见区域干扰。
同时,引入 Otsu自适应阈值法 对蓝色相关特征图进行智能二值化,替代固定阈值,显著提升对光照变化和噪声的鲁棒性。
以下是完整、可直接运行的优化方案:
立即学习“Python免费学习笔记(深入)”;
import numpy as np
import cv2
from PIL import Image
def calculate_blue_score(image_path, hue_low=0.5, hue_high=0.75, sat_min=0.2, val_min=0.2):
"""
基于HSV空间的蓝区覆盖度与强度联合评分(0.9×coverage + 0.1×intensity)
:param image_path: 图像路径
:param hue_low/hue_high: 蓝色色调范围(归一化0-1)
:param sat_min: 最低饱和度阈值(过滤灰白)
:param val_min: 最低明度阈值(过滤过暗区域)
:return: (coverage_ratio, avg_blue_intensity_in_mask, combined_score)
"""
# 1. 加载并转换为HSV
pil_img = Image.open(image_path).convert('RGB')
rgb_array = np.array(pil_img)
hsv_array = cv2.cvtColor(rgb_array, cv2.COLOR_RGB2HSV).astype(np.float32)
# 2. 归一化HSV:OpenCV中H∈[0,179], S/V∈[0,255] → 转为[0,1]
h_norm = hsv_array[:, :, 0] / 179.0
s_norm = hsv_array[:, :, 1] / 255.0
v_norm = hsv_array[:, :, 2] / 255.0
# 3. 构建HSV蓝区掩码:色调在蓝区间 AND 饱和度足够 AND 明度足够
hue_mask = (h_norm >= hue_low) & (h_norm <= hue_high)
sat_mask = s_norm >= sat_min
val_mask = v_norm >= val_min
blue_mask = hue_mask & sat_mask & val_mask # 形状: (H, W), bool
# 4. 使用Otsu对S通道(或自定义蓝度特征)做自适应二值化,进一步提纯
# 这里以饱和度图为例(也可用H距离蓝中心的加权图)
if np.any(blue_mask):
s_uint8 = (s_norm * 255).astype(np.uint8)
_, otsu_mask = cv2.threshold(s_uint8, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
otsu_mask = otsu_mask.astype(bool)
# 结合原始HSV掩码与Otsu结果(取交集,更严格)
final_mask = blue_mask & otsu_mask
else:
final_mask = blue_mask
# 5. 计算指标
coverage = np.mean(final_mask) # 覆盖率:True像素占比
# 强度:仅对final_mask内像素,取原始RGB蓝通道均值(归一化到0-1)
rgb_img = np.array(pil_img)
blue_channel = rgb_img[:, :, 2].astype(np.float32) / 255.0
intensity = np.mean(blue_channel[final_mask]) if np.any(final_mask) else 0.0
score = 0.9 * coverage + 0.1 * intensity
return coverage, intensity, score
# 使用示例
if __name__ == "__main__":
# 测试两张关键图像
cov20, int20, scr20 = calculate_blue_score("image20.png")
cov127, int127, scr127 = calculate_blue_score("image127.png")
print(f"image20.png — Coverage: {cov20:.4f} ({cov20*100:.2f}%), "
f"Intensity: {int20:.4f}, Score: {scr20:.4f}")
print(f"image127.png — Coverage: {cov127:.4f} ({cov127*100:.2f}%), "
f"Intensity: {int127:.4f}, Score: {scr127:.4f}")? 关键改进说明与注意事项:
-
HSV取代RGB:彻底规避灰/白像素因通道微小差异被误判的问题;
sat_min=0.2是经验值,可根据实际数据微调(0.15–0.3),越低越宽松,越高越严格。 - Otsu自适应阈值:自动学习图像内部饱和度分布的双峰,比固定阈值更能适应不同光照条件下的蓝色表现。
- 掩码交集策略:先用HSV粗筛,再用Otsu精修,兼顾语义准确性与鲁棒性。
-
强度计算逻辑:仍使用原始RGB蓝通道值(归一化后),因其直观反映设备记录的蓝光能量,与覆盖度形成合理互补;若需完全感知一致,可改用HSV的
V或S×V乘积。 -
依赖安装:
pip install opencv-python-headless numpy pillow(无需额外安装nice-colorsys,OpenCV已内置完备HSV支持)。
通过此方案,image20.png 的 coverage 将稳定趋近于 0.0(因灰阶饱和度≈0),其综合得分将远低于真实蓝图(如 image127.png),从而真正实现“大面积蓝色优先”的设计目标。


















