在OpenCV中保留原颜色亮度的同时替换指定区域颜色
如何精准替换掩码区域为指定颜色并保留原亮度(OpenCV实现)
问题背景
需要将图像中推土机的黄色区域替换为指定十六进制颜色(本次为红色),同时保留该区域的光影亮度,避免颜色单一。目前已通过HSV阈值创建了黄色掩码,但直接修改HSV参数的方式无法精准匹配目标RGB/十六进制颜色,达不到理想效果。
现有代码:
import cv2 import numpy as np import os import math from numpy import inf # set the bounds for the light and dark shade of color you want to mask lower_yellow = np.array([20, 100, 100]) upper_yellow = np.array([30, 255, 255]) all_images = sorted(os.listdir('data/audi_a6')) for i, image in enumerate(all_images): print(image, end='\r') img = cv2.imread(f'data/lego/{image}') hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) yellow_mask = cv2.inRange(hsv, lower_yellow, upper_yellow) inv_mask = cv2.bitwise_not(yellow_mask) h, s, v = cv2.split(hsv) h = np.mod(h - h + 182, 180) s = np.clip(s + 120, 0, 255) v = np.clip(v, 0, 255) hsv = cv2.merge([h, s, v]) bgr = cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR) result = cv2.bitwise_or(cv2.bitwise_and(img, img, mask=inv_mask), cv2.bitwise_and(bgr, bgr, mask=yellow_mask)) # Save the result cv2.imwrite(f'output_change_color/lego/frame_{i:03}.jpg', result)
解决方案
核心思路是保留原区域的V(亮度)通道,替换为目标颜色的H(色调)和S(饱和度)通道,既能精准匹配目标颜色,又能完整保留原有的光影层次。具体实现步骤如下:
关键修改点
- 精准转换目标颜色:将十六进制颜色转为OpenCV兼容的BGR格式,再转为HSV提取目标H、S值,避免手动调参的误差。
- 保留亮度通道:仅在掩码区域替换H和S值,完全保留原图像的V通道,确保光影效果不变。
- 修复路径问题:统一图像读取路径,添加输出目录自动创建逻辑,避免运行报错。
修改后的完整代码
import cv2 import numpy as np import os # 目标十六进制颜色转BGR(OpenCV默认使用BGR格式) hex_color = "#FF0000" # 本次目标为红色 # 十六进制转RGB后转为BGR顺序 rgb = tuple(int(hex_color.lstrip('#')[i:i+2], 16) for i in (0, 2, 4)) target_bgr = np.array([rgb[2], rgb[1], rgb[0]], dtype=np.uint8) # 将目标BGR颜色转为HSV,提取目标色调(H)和饱和度(S) target_hsv = cv2.cvtColor(np.uint8([[target_bgr]]), cv2.COLOR_BGR2HSV)[0][0] target_h, target_s = target_hsv[0], target_hsv[1] # 黄色区域的HSV阈值(可根据实际图像微调) lower_yellow = np.array([20, 100, 100]) upper_yellow = np.array([30, 255, 255]) # 获取图像列表(确保路径与读取路径一致) all_images = sorted(os.listdir('data/audi_a6')) # 确保输出目录存在 os.makedirs('output_change_color/lego', exist_ok=True) for i, image in enumerate(all_images): print(image, end='\r') # 读取原图像 img = cv2.imread(f'data/audi_a6/{image}') # 转为HSV颜色空间 hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) # 创建黄色区域掩码 yellow_mask = cv2.inRange(hsv, lower_yellow, upper_yellow) inv_mask = cv2.bitwise_not(yellow_mask) # 拆分HSV通道 h, s, v = cv2.split(hsv) # 仅在黄色掩码区域替换H和S为目标值,保留原亮度V h[yellow_mask > 0] = target_h s[yellow_mask > 0] = target_s # 合并修改后的HSV通道 modified_hsv = cv2.merge([h, s, v]) # 转回BGR格式 modified_bgr = cv2.cvtColor(modified_hsv, cv2.COLOR_HSV2BGR) # 合并原图非掩码区域和修改后的掩码区域 result = cv2.bitwise_or( cv2.bitwise_and(img, img, mask=inv_mask), cv2.bitwise_and(modified_bgr, modified_bgr, mask=yellow_mask) ) # 保存结果 cv2.imwrite(f'output_change_color/lego/frame_{i:03}.jpg', result)
说明
- 若需要替换为其他颜色,仅需修改
hex_color变量的十六进制值即可,无需调整HSV阈值(除非目标颜色的掩码需要重新设定)。 - 黄色区域的HSV阈值可根据实际图像的黄色深浅微调,确保掩码精准覆盖目标区域。
内容的提问来源于stack exchange,提问作者diegobc11
相关产品推荐
相关产品推荐

