最近想要实现Photoshop图层混合中的色相模式,在网上查阅了众多关于混合模式实现的文章:
PS中混合模式是什么意思?
photoshop图层混合模式中色相模式的原理是什么?
Sketch图层混合模式(Blending)详解
PS中27种图层混合模式原理详解
Python: PS 图层混合算法汇总
Python 模块layeris(GitHub:https://github.com/subwaymatch/layer-is-python/tree/fa10c2c7927a6a2b93f477bfdd57e254080308ce)
其中关于色相模式的描述基本为:色相混合模式是选择基色的亮度和饱和度值与混合色进行混合而创建的效果,混合后的亮度及饱和度取决于基色,但色相取决于混合色。
其关于色相混合的Python实现是,背景变换到HSV然后直接替换色相H,再变换到rgb空间。并且实验GIMP软件(类似ps的开源软件),其效果也是如此。
(代码来自layeris)
def hue(self, target_hue):
image_hsv_data = matplotlib.colors.rgb_to_hsv(self.image_data)
image_hsv_data[:, :, 0] = target_hue
self.image_data = matplotlib.colors.hsv_to_rgb(image_hsv_data)
return self
原图 前景图 色相混合结果
GIMP色相混合结果 ps色相混合结果
分析:可以看到ps版本的结果看起来更加舒服,这是因为ps的结果除了应用了前景图的色相,还保持了原图的亮度。这里的亮度不是hsv空间的v,而是Lab空间的L。
根据上述的实验和分析的结论,尝试同时进行两件事情:
然而,H更改将会导致L变化,L更改将会导致H、S变化,我采用了循环迭代上述2步操作,实现了和ps基本一致的色相混合。代码如下:
import cv2 as cv
import numpy as np
import matplotlib
class LayerImage(object):
def __init__(self, image_data):
self.image_data = image_data
def hue_blend(self, blend_data, rep_times=2):
'''
色相模式图层混合,对前景图和底图,使用前景图在HSV空间的H(色相)替换底图的色相,同时保持底图在替换色相后Lab空间的L保持不变,
而更改H和L是相互影响的,通过多次迭代H和L替换实现平衡
blend_data: 提供色相的前景图,尺寸需和背景图一致
rep_times: 迭代H和L替换的次数,为2时与ps效果相同
'''
assert self.image_data.shape[:2] == blend_data.shape[:2], 'the shape of blend_data is not same as image_data'
image = self.image_data
if self.image_data.shape[2] == 4:
image_alpha = np.expand_dims(image[...,3], axis=2)
else:
image_alpha = None
image_L = cv.cvtColor(image, cv.COLOR_BGR2LAB)[..., 0]
color_map_H = cv.cvtColor(blend_data, cv.COLOR_BGR2HSV)[:,:,0]
for i in range(rep_times): #
image_HSV = cv.cvtColor(image, cv.COLOR_BGR2HSV)
image_HSV[:,:,0] = color_map_H
image_BGR = cv.cvtColor(image_HSV, cv.COLOR_HSV2BGR)
image_Lab = cv.cvtColor(image_BGR, cv.COLOR_BGR2LAB)
image_Lab[..., 0] = image_L
image = cv.cvtColor(image_Lab, cv.COLOR_LAB2BGR)
image_HSV = cv.cvtColor(image, cv.COLOR_BGR2HSV)
image_HSV[:,:,0] = color_map_H
image = cv.cvtColor(image_HSV, cv.COLOR_HSV2BGR)
if not image_alpha is None:
image = np.concatenate((image,image_alpha), axis=2)
cv.imwrite('result3.png', image)
return image
def grayscale(self):
self.image_data = np.dot(self.image_data[..., :3], [
0.2989, 0.5870, 0.1140])
self.image_data = np.stack(
(self.image_data,) * 3, axis=-1)
return self
if __name__ == "__main__":
color_map = cv.imread('标准主体_拓色副本.png', cv.IMREAD_UNCHANGED)
image = cv.imread('标准主体.png', cv.IMREAD_UNCHANGED)
a = LayerImage(image).grayscale().image_data
cv.imwrite('result3.png', a)
LayerImage(image).hue_blend(color_map, rep_times=2)
结果:
原图 前景图 实现效果 ps色相混合结果