如何使用PIL模块替换图片指定色域(如整片天空)的像素颜色
解决方案
问题根源
你当前的代码仅对RGB值完全相等的像素做替换,而自然场景下的天空存在渐变、光影差异,存在大量和你提取的基准色接近但数值不完全一致的像素,因此只能替换部分区域。
优化方法1:RGB颜色容差匹配
通过计算当前像素和目标替换色的色彩欧氏距离,设置合理的容差阈值,只要距离小于阈值就判定为需要替换的天空像素:
from PIL import Image, ImageColor import math im = Image.open("iphone.jpg", "r") to_be_replaced = ImageColor.getcolor("#5f86c9", "RGB") newcolor = ImageColor.getcolor("#c9bb5f", "RGB") # 容差阈值,可根据实际效果调整,数值越大匹配的颜色范围越广 threshold = 30 pix = im.load() width, height = im.size for w in range(width): for h in range(height): r, g, b = pix[(w, h)] # 计算RGB色彩欧氏距离 color_diff = math.sqrt((r - to_be_replaced[0])**2 + (g - to_be_replaced[1])**2 + (b - to_be_replaced[2])**2) if color_diff < threshold: pix[w, h] = newcolor im.save('test.png', 'PNG')
优化方法2:HSV色彩空间匹配(推荐)
RGB空间的色彩距离和人眼感知的颜色相似度匹配度较低,你可以将图片转为HSV(色相、饱和度、亮度)空间,只匹配天空蓝色对应的色相范围,不受亮度、饱和度差异影响,识别精度更高:
from PIL import Image, ImageColor import colorsys im = Image.open("iphone.jpg", "r") newcolor = ImageColor.getcolor("#c9bb5f", "RGB") # 天空蓝色对应的色相范围,可根据实际场景调整,蓝色色相一般在0.55~0.7区间(HSV色相取值0~1) h_low, h_high = 0.55, 0.7 # 饱和度下限,避免把灰白色的云也误判为天空 s_low = 0.2 pix = im.load() width, height = im.size for w in range(width): for h in range(height): r, g, b = pix[(w, h)] # 转HSV,取值都归一化到0~1区间 h_val, s_val, v_val = colorsys.rgb_to_hsv(r/255, g/255, b/255) if h_low <= h_val <= h_high and s_val >= s_low: pix[w, h] = newcolor im.save('test.png', 'PNG')
参数调整说明
- 容差类参数可以先给一个中间值,再根据生成的效果上下调整:如果还有天空没替换完就调大匹配范围,如果有非天空区域被误替换就调小匹配范围。
- 如果图片中的天空有大量云朵,可以额外增加亮度判断逻辑,避免云朵被一同替换。
内容的提问来源于stack exchange,提问作者Asad Kareem
相关产品推荐
相关产品推荐

