Photoshop中的混合模式公式详解
图层混合简介
图层混合(blend)顾名思义,就是把两个图层混合成一个。
最基本的混合是alpha融合(alpha compositing),这是一个遵循光的反射与透射等(简化版)物理学原理的混合方式。
各个图像处理软件中,往往还提供很多具有一定艺术风格的混合方法,这是本文的主要讨论内容。但须注意,为了更深入地理解混合模式,alpha融合是必需要掌握的前提知识。
为了更好地理解本文内容,我们做如下约定:
- 所有色彩是归一化的,也就是值域在[0, 1]之间(而非[0, 255]),否则不仅公式不容易写,而且还不适配hdr的情况。
- 一般情况下,混合都是逐像素,大部分时候甚至逐通道计算。(在Photoshop中,仅有
深色
和浅色
不是逐通道计算) - 部分混合模式满足交换律,即交换两个图层的上下位置,结果不变。
- 一些混合模式的结果要求截断到[0, 1]之间,否则会发生不可预测的结果,这些混合模式仅能作用于int类型的数值;还有一些则不要求,这些则能作用于float类型的数值,其结果可以越过[0, 1]范围。
Photoshop中的混合模式
截止2022版本,Photoshop中有27个混合模式,分为六大类,各类别以及包含的混合模式为:
- 正常类别:
正常,溶解
- 变暗类别:
变暗,正片叠底,颜色加深,线性加深,深色
- 变亮类别:
变亮,滤色,颜色减淡,线性减淡(添加),浅色
- 复杂类别:
叠加,柔光,强光,亮光,线性光,点光,实色混合
- 差异类别:
差值,排除,减去,划分
- HSL类别:
色相,饱和度,颜色,明度
除了溶解(Dissolve)
模式以外,其他的混合都有明确的公式,溶解靠随机数实现,随着透明度降低,有越来越多的随机像素点的alpha通道会变成0,这样就会随机地显示出下方图层,表现得好像溶解了一样。本文主要讲有公式的那些混合模式,溶解不在此列,所以这里简单描述一下,后面就不再提了。
为了全面理解混合模式的公式,需要首先从RGB出发(即alpha通道恒等于1),在了解了RGB的混合之后,要基于此知识进一步推导RGBA的混合公式,也就是含透明度信息情况下的公式,真正写程序时候一般使用RGBA的公式即可,因为RGBA涵盖了RGB的情况。
部分混合模式在RGB情况下,交换前景与背景图后效果相同,此类混合模式有:
变暗,正片叠底,线性加深,深色,变亮,滤色,线性减淡(添加),浅色,差值,排除,实色混合
公式符号约定
fg:foreground,表示前景,即处于上方的图层
bg:background,表示背景,即处于下方的图层
f g c fg_c fgc:表示前景的rgb部分,其中脚标_c是color的意思。背景的脚标含义同理,下面不再赘述。
f g α fg_\alpha fgα:表示前景的透明通道
f g c α fg_{c\alpha} fgcα:表示前景rgb与alpha通道相乘的结果,即alpha预乘。
在写代码时,还需注意除法
的问题:所有的除法必需要考虑避免除以0的情况,因此碰到除法时应做如下保护:
a b = a m a x ( b , e p s ) \frac{a}{b} = \frac{a}{max(b, eps)} ba=max(b,eps)a
接下来的公式中不会赘述这一点,但各位读者写代码时需自行注意。
RGB混合公式
正常(Normal)
仅显示上方图层。
N o r m a l ( f g c , b g c ) = f g c Normal(fg_c,bg_c) = fg_c Normal(fgc,bgc)=fgc
变暗(Darken)
取两图层中数值较小者。
D a r k e n ( f g c , b g c ) = m i n ( f g c , b g c ) Darken(fg_c, bg_c) = min(fg_c, bg_c) Darken(fgc,bgc)=min(fgc,bgc)
正片叠底(Multiply)
上下两图层相乘。
M u l t i p l y ( f g c , b g c ) = f g c ∗ b g c Multiply(fg_c, bg_c) = fg_c * bg_c Multiply(fgc,bgc)=fgc∗bgc
前景与背景为同一个图片时,等同于如下曲线调节:
https://www.desmos.com/calculator/8blh2zzaqc
颜色加深(Color Burn)
让背景图层颜色变暗,变暗的程度由前景决定,前景越暗,背景变暗的程度就越强。
C o l o r B u r n ( f g c , b g c ) = { 0 , if: f g c = = 0 1 − m i n ( 1 − b g c f g c , 1 ) , if: f g c > 0 ColorBurn(fg_c, bg_c) = \begin{cases} 0, &\text{if: } fg_c == 0 \\[2ex] 1 - min(\frac{1 - bg_c}{fg_c}, 1), &\text {if: } fg_c>0 \end{cases} ColorBurn(fgc,bgc)=⎩ ⎨ ⎧0,1−min(fgc1−bgc,1),if: fgc==0if: fgc>0
前景与背景为同一个图片时,等同于如下曲线调节:
https://www.desmos.com/calculator/5xdaojgl3p
线性加深(Linear Burn)
前景与背景相加后减1。效果类似颜色加深,只是变暗的曲线是线性的。
L i n e a r B u r n ( f g c , b g c ) = m a x ( f g c + b g c − 1 , 0 ) LinearBurn(fg_c, bg_c) =max(fg_c+bg_c-1, 0) LinearBurn(fgc,bgc)=max(fgc+bgc−1,0)
前景与背景为同一个图片时,等同于如下曲线调节:
https://www.desmos.com/calculator/akmwl8cert
深色(Darker Color)
计算上下两通道的灰度值,谁的灰度值小,结果颜色就取相应图层的rgb颜色。
注意该模式是少有的非逐通道
计算的混合模式。该模式不产生fg和bg以外的新的颜色。
变亮(Lighten)
取两图层中数值较大者。
L i g h t e n ( f g c , b g c ) = m a x ( f g c , b g c ) Lighten(fg_c, bg_c)=max(fg_c,bg_c) Lighten(fgc,bgc)=max(fgc,bgc)
滤色(Screen)
滤色是模拟光的反射与透射原理设计的混合模式。如果光照到图片后,我们把反射回来的记为我们看到的图像本身,那么透射过去的就是(1-image),也可以称之为负片
,光透过两个图层以后的负片就是(1-fg)(1-bg)
,对这个结果整体再来个负片,就是滤色的效果。光透过多个图层后一定是越来越暗的,所以最终再做一个整体的负片,就一定是变亮的,并且根据物理原理(或更简单一些,可以根据公式),如果各个输入图层在[0, 1]之间,那么结果就一定也在[0, 1]之间,不会产生数值截断,这是一个非常好的特性。
滤色是非常重要的一种混合模式,是各类发光效果最最常用的混合模式(bloom、glow等)。
S c r e e n ( f g c , b g c ) = 1 − ( 1 − f g c ) ∗ ( 1 − b g c ) Screen(fg_c,bg_c)=1-(1-fg_c)*(1-bg_c) Screen(fgc,bgc)=1−(1−fgc)∗(1−bgc)
前景与背景为同一个图片时,等同于如下曲线调节:
https://www.desmos.com/calculator/sa04opay5a
颜色减淡(Color Dodge)
让背景图层颜色变亮,变亮的程度由前景决定,前景越亮,背景变亮的程度就越强。
C o l o r D o d g e ( f g c , b g c ) = { 1 , if: f g c = = 1 m i n ( b g c 1 − f g c , 1 ) , otherwise ColorDodge(fg_c, bg_c) = \begin{cases} 1, &\text{if: } fg_c == 1 \\[2ex] min(\frac{bg_c}{1-fg_c}, 1), &\text {otherwise} \end{cases} ColorDodge(fgc,bgc)=⎩ ⎨ ⎧1,min(1−fgcbgc,1),if: fgc==1otherwise
前景与背景为同一个图片时,等同于如下曲线调节:
https://www.desmos.com/calculator/8c5gfbfenq
线性减淡 / 添加(Linear Dodge / Add)
前景与背景相加。效果类似颜色减淡,只是变亮的曲线是线性的。
L i n e a r D o d g e ( f g c , b g c ) = f g c + b g c LinearDodge(fg_c, bg_c) =fg_c+bg_c LinearDodge(fgc,bgc)=fgc+bgc
前景与背景为同一个图片时,等同于如下曲线调节:
https://www.desmos.com/calculator/f4rclxxoj8
浅色(Lighter Color)
计算上下两通道的灰度值,谁的灰度值大,结果颜色就取相应图层的rgb颜色。
注意该模式是少有的非逐通道
计算的混合模式。该模式不产生fg和bg以外的新的颜色。
叠加(Overlay)
当背景
小于0.5时,使用multiply混合(变暗效果);反之则使用screen混合(变亮效果)。也就是说混合结果使得暗的更暗,亮的更亮,即对比度增强的效果(s形曲线)。
特别注意
:该混合模式的控制图层是bg(即if条件中的图层),这与其他绝大部分的混合模式都不一样。
叠加与后面的强光(Hard Light)主体公式一样,但是条件不同。叠加的控制图层是bg,强光是fg,注意区别。
O v e r l a y ( f g c , b g c ) = { 2 ∗ f g c ∗ b g c , if: b g c < = 0.5 1 − 2 ∗ ( 1 − f g c ) ∗ ( 1 − b g c ) , otherwise Overlay(fg_c, bg_c) = \begin{cases} 2*fg_c*bg_c, &\text{if: } bg_c<=0.5 \\[2ex] 1-2*(1-fg_c)*(1-bg_c), &\text {otherwise} \end{cases} Overlay(fgc,bgc)=⎩ ⎨ ⎧2∗fgc∗bgc,1−2∗(1−fgc)∗(1−bgc),if: bgc<=0.5otherwise
前景与背景为同一个图片时,等同于如下曲线调节:
https://www.desmos.com/calculator/oymxhxdn1l
柔光(Soft Light)
公式比较复杂,不好解释。效果类似叠加(overlay),但是程度要更缓一些。另外该效果的控制图层是fg(与大部分混合模式一样)。
如果想要融合图像和某个高频纹理,柔光往往是不错的选择。
S o f t L i g h t ( f g c , b g c ) = { b g c − ( 1 − 2 ∗ f g c ) ∗ b g c ∗ ( 1 − b g c ) , if: f g c < = 0.5 b g c + ( 2 ∗ f g c − 1 ) ∗ ( D ( b g c ) − b g c ) , otherwise SoftLight(fg_c, bg_c) = \begin{cases} bg_c - (1-2*fg_c)*bg_c*(1-bg_c), &\text{if: } fg_c<=0.5 \\[2ex] bg_c + (2*fg_c-1)*(D(bg_c)-bg_c), &\text {otherwise} \end{cases} SoftLight(fgc,bgc)=⎩ ⎨ ⎧bgc−(1−2∗fgc)∗bgc∗(1−bgc),bgc+(2∗fgc−1)∗(D(bgc)−bgc),if: fgc<=0.5otherwise
其中
D ( x ) = { ( ( 16 ∗ x − 12 ) ∗ x + 4 ) ∗ x , if: x < = 0.25 x , otherwise D(x) = \begin{cases} ((16*x-12)*x+4)*x, &\text{if: } x<=0.25 \\[2ex] \sqrt{x}, &\text {otherwise} \end{cases} D(x)=⎩ ⎨ ⎧((16∗x−12)∗x+4)∗x,x,if: x<=0.25otherwise
下图的两个注意点:
- 红色虚线是叠加模式,用于跟柔光做对比,可以看到柔光比叠加的曲线对比度小一些
- 当上下图层一样时,上面公式中D(x)的x<=0.25的条件不会触发。
前景与背景为同一个图片时,等同于如下曲线调节:
https://www.desmos.com/calculator/ud2enyjmma
https://www.desmos.com/calculator/2elal9gopd
强光(Hard Light)
强光与叠加(Overlay)的主体公式是一样的,但是条件不一样,强光的控制图层是fg,而叠加是bg,注意区别。
H a r d L i g h t ( f g c , b g c ) = { 2 ∗ f g c ∗ b g c , if: f g c < = 0.5 1 − 2 ∗ ( 1 − f g c ) ∗ ( 1 − b g c ) , otherwise HardLight(fg_c, bg_c) = \begin{cases} 2*fg_c*bg_c, &\text{if: } fg_c<=0.5 \\[2ex] 1-2*(1-fg_c)*(1-bg_c), &\text {otherwise} \end{cases} HardLight(fgc,bgc)=⎩ ⎨ ⎧2∗fgc∗bgc,1−2∗(1−fgc)∗(1−bgc),if: fgc<=0.5otherwise
前景与背景为同一个图片时,等同于如下曲线调节:
https://www.desmos.com/calculator/rbnfcptnyb
亮光(Vivid Light)
公式比较复杂,会产生有强烈截断现象的对比度增强效果。
V i v i d L i g h t ( f g c , b g c ) = { b g c − ( 1 − b g c ) ∗ ( 1 − 2 ∗ f g c ) 2 ∗ f g c , if: f g c < = 0.5 b g c + b g c ∗ ( 2 ∗ f g c − 1 ) 2 ∗ ( 1 − f g c ) , otherwise VividLight(fg_c, bg_c) = \begin{cases} bg_c - \frac{(1-bg_c)*(1-2*fg_c)}{2*fg_c}, &\text{if: } fg_c<=0.5 \\[2ex] bg_c + \frac{bg_c*(2*fg_c-1)}{2*(1-fg_c)}, &\text {otherwise} \end{cases} VividLight(fgc,bgc)=⎩ ⎨ ⎧bgc−2∗fgc(1−bgc)∗(1−2∗fgc),bgc+2∗(1−fgc)bgc∗(2∗fgc−1),if: fgc<=0.5otherwise
前景与背景为同一个图片时,等同于如下曲线调节:
https://www.desmos.com/calculator/capvg8fb3p
线性光(Linear Light)
类似亮光,会产生有强烈截断现象的对比度增强效果,但曲线是线性的。
L i n e a r L i g h t ( f g c , b g c ) = b g + 2 ∗ f g − 1 LinearLight(fg_c, bg_c) =bg+2*fg-1 LinearLight(fgc,bgc)=bg+2∗fg−1
前景与背景为同一个图片时,等同于如下曲线调节:
https://www.desmos.com/calculator/kh3sslrzuf
点光(Pin Light)
根据前景图层的亮度信息,对背景图层进行色彩替代,至于替代成什么,参考如下公式即可,就不在语言上过多解释了,公式比语言更清楚。
P i n L i g h t ( f g c , b g c ) = { m i n ( b g , 2 ∗ f g ) , if: f g c < = 0.5 m a x ( b g , 2 ∗ f g − 1 ) , otherwise PinLight(fg_c, bg_c) = \begin{cases} min(bg, 2*fg), &\text{if: } fg_c<=0.5 \\[2ex] max(bg, 2*fg - 1), &\text {otherwise} \end{cases} PinLight(fgc,bgc)=⎩ ⎨ ⎧min(bg,2∗fg),max(bg,2∗fg−1),if: fgc<=0.5otherwise
实色混合(Hard Mix)
根据 fg+bg>=1 的条件进行判断,结果要么取1,要么取0。所以结果只有红、黄、蓝、青、黄、洋红、黑、白
八种颜色。
H a r d M i x ( f g c , b g c ) = { 1 , if: f g c + b g c > = 1 0 , otherwise HardMix(fg_c, bg_c) = \begin{cases} 1, &\text{if: } fg_c+bg_c>=1 \\[2ex] 0, &\text {otherwise} \end{cases} HardMix(fgc,bgc)=⎩ ⎨ ⎧1,0,if: fgc+bgc>=1otherwise
差值(Difference)
两图层之差的绝对值。
D i f f e r e n c e ( f g c , b g c ) = ∣ b g c − f g c ∣ Difference(fg_c, bg_c) =|bg_c-fg_c| Difference(fgc,bgc)=∣bgc−fgc∣
排除(Exclusion)
与差值
的效果类似,但是结果的对比度会低一些。
E x c l u s i o n ( f g c , b g c ) = f g c + b g c − 2 ∗ f g c ∗ b g c Exclusion(fg_c, bg_c) =fg_c + bg_c - 2*fg_c*bg_c Exclusion(fgc,bgc)=fgc+bgc−2∗fgc∗bgc
减去(Subtract)
背景减去前景。
S u b t r a c t ( f g c , b g c ) = b g c − f g c Subtract(fg_c, bg_c)=bg_c-fg_c Subtract(fgc,bgc)=bgc−fgc
划分(Divide)
背景除以前景。
D i v i d e ( f g c , b g c ) = b g c f g c Divide(fg_c, bg_c)=\frac{bg_c}{fg_c} Divide(fgc,bgc)=fgcbgc
下面几个是HSL混合模式,主要功能就是对背景图层色相、饱和度、明度等属性进行替换,替换为前景图层的相应值。
字面上似乎挺容易理解,很多资料会根据字面意思把色彩转到HSL或者HSV域来做替换行为,但实际上Photoshop不是这么干的。
具体可以参考Adobe早期的pdf reference,尽管这是为pdf软件写的说明,而非Photoshop,但里面有讲到这几个混合模式,需要依赖一些公用函数,截图如下:
色相(Hue)
背景图层的色相替换为前景图层的相应值。
H u e B l e n d ( f g c , b g c ) = S e t L u m ( S e t S a t ( f g c , S a t ( b g c ) ) , L u m ( b g c ) ) HueBlend(fg_c, bg_c) = SetLum(SetSat(fg_c, Sat(bg_c)), Lum(bg_c)) HueBlend(fgc,bgc)=SetLum(SetSat(fgc,Sat(bgc)),Lum(bgc))
饱和度(Saturation)
背景图层的饱和度替换为前景图层的相应值。
S a t u r a t i o n B l e n d ( f g c , b g c ) = S e t L u m ( S e t S a t ( b g c , S a t ( f g c ) ) , L u m ( b g c ) ) SaturationBlend(fg_c, bg_c) = SetLum(SetSat(bg_c, Sat(fg_c)), Lum(bg_c)) SaturationBlend(fgc,bgc)=SetLum(SetSat(bgc,Sat(fgc)),Lum(bgc))
颜色(Color)
背景图层的色相和饱和度替换为前景图层的相应值。
C o l o r B l e n d ( f g c , b g c ) = S e t L u m ( f g c , L u m ( b g c ) ) ColorBlend(fg_c, bg_c) = SetLum(fg_c, Lum(bg_c)) ColorBlend(fgc,bgc)=SetLum(fgc,Lum(bgc))
明度(Luminosity)
背景图层的明度替换为前景图层的相应值。
L u m i n o s i t y B l e n d ( f g c , b g c ) = S e t L u m ( b g c , L u m ( f g c ) ) LuminosityBlend(fg_c, bg_c) = SetLum(bg_c, Lum(fg_c)) LuminosityBlend(fgc,bgc)=SetLum(bgc,Lum(fgc))
代码
# -*- coding: utf-8 -*-
import numpy as npEPS = 1e-5# ------ util functions ------
def get_luminance(bgr):return bgr[..., 2] * 0.3 + bgr[..., 1] * 0.59 + bgr[..., 0] * 0.11def get_saturation(bgr):min_color = np.minimum(np.minimum(bgr[..., 0], bgr[..., 1]), bgr[..., 2])max_color = np.maximum(np.maximum(bgr[..., 0], bgr[..., 1]), bgr[..., 2])return max_color - min_colordef clip_color(bgr):lumi = get_luminance(bgr)min_color = np.minimum(np.minimum(bgr[..., 0], bgr[..., 1]), bgr[..., 2])max_color = np.maximum(np.maximum(bgr[..., 0], bgr[..., 1]), bgr[..., 2])idx1 = min_color < 0.idx2 = max_color > 1.lumi = lumi[..., np.newaxis]min_color = min_color[..., np.newaxis]max_color = max_color[..., np.newaxis]color = bgr.copy()color[idx1] = (lumi + (color - lumi) * lumi /np.maximum(lumi - min_color, EPS))[idx1]color[idx2] = (lumi + (color - lumi) * (1. - lumi) /np.maximum(max_color - lumi, EPS))[idx2]return colordef set_luminance(bgr, lumi):diff = lumi - get_luminance(bgr)diff = diff[..., np.newaxis]color = bgr + diffcolor = clip_color(color)return colordef set_saturation(bgr, sat):color = bgr.copy()h, w, c = color.shapecolor = color.reshape([-1, c])try:sat = sat.reshape([-1])except:passidx_min = np.argmin(color, axis=1)idx_max = 2 - np.argmax(color[..., ::-1], axis=1)idx_mid = np.ones_like(idx_min) * 3 - (idx_min + idx_max)idx_all = np.array([[0, 1, 2]]).repeat(repeats=h * w, axis=0)idx_min = idx_all == idx_min[..., np.newaxis]idx_max = idx_all == idx_max[..., np.newaxis]idx_mid = idx_all == idx_mid[..., np.newaxis]min_color = color[idx_min]max_color = color[idx_max]mid_color = color[idx_mid]color[idx_mid] = (mid_color - min_color) * sat / np.maximum(max_color - min_color, EPS)color[idx_max] = satcolor[idx_min] = 0.idx = max_color == min_colorcolor[idx, :] = 0.color = color.reshape([h, w, c])return color# ------ blend functions ------
def normal_blend(fg, bg):return fgdef darken_blend(fg, bg):return np.minimum(fg, bg)def multiply_blend(fg, bg):return fg * bgdef color_burn_blend(fg, bg):return 1. - np.minimum((1. - bg) / np.maximum(fg, EPS), 1.)def linear_burn_blend(fg, bg):return np.maximum(fg + bg - 1., 0)def darker_color_blend(fg, bg):fg_lumi = get_luminance(fg)bg_lumi = get_luminance(bg)idx = fg_lumi <= bg_lumires = bg.copy()res[idx] = fg[idx]return resdef lighten_blend(fg, bg):return np.maximum(fg, bg)def screen_blend(fg, bg):return 1. - (1. - fg) * (1. - bg)def color_dodge_blend(fg, bg):return np.minimum(bg / np.maximum(1. - fg, EPS), 1.)def linear_dodge_blend(fg, bg):return np.minimum(fg + bg, 1.)def lighter_color_blend(fg, bg):fg_lumi = get_luminance(fg)bg_lumi = get_luminance(bg)idx = fg_lumi >= bg_lumires = bg.copy()res[idx] = fg[idx]return resdef overlay_blend(fg, bg):idx = bg <= 0.5res = 1. - 2. * (1. - fg) * (1. - bg)res[idx] = (2. * fg * bg)[idx]return resdef soft_light_blend(fg, bg):def _fun(x):return ((16. * x - 12.) * x + 4.) * xidx_1 = fg <= 0.5idx_2 = bg <= 0.25res = fg.copy()res[idx_1] = (bg - (1 - 2. * fg) * bg * (1. - bg))[idx_1]idx = (~idx_1) & idx_2res[idx] = (bg + (2. * fg - 1.) * (_fun(bg) - bg))[idx]idx = (~idx_1) & (~idx_2)res[idx] = (bg + (2. * fg - 1.) * (np.sqrt(bg) - bg))[idx]return resdef hard_light_blend(fg, bg):idx = fg <= 0.5res = 1. - 2. * (1. - fg) * (1. - bg)res[idx] = (2. * fg * bg)[idx]return resdef vivid_light_blend(fg, bg):idx = fg <= 0.5res1 = bg - (1. - bg) * (1. - 2. * fg) / np.maximum(2. * fg, EPS)res = bg + bg * (2. * fg - 1.) / np.maximum(2. * (1. - fg), EPS)res[idx] = res1[idx]res = np.clip(res, 0., 1.)return resdef linear_light_blend(fg, bg):res = bg + 2. * fg - 1.res = np.clip(res, 0., 1.)return resdef pin_light_blend(fg, bg):idx = fg <= 0.5res = np.maximum(bg, 2. * fg - 1.)res[idx] = np.minimum(bg, 2. * fg)[idx]return resdef hard_mix_blend(fg, bg):idx = fg + bg >= 1. # NOTE: greater than or equalres = np.zeros_like(fg)res[idx] = 1.return resdef difference_blend(fg, bg):return np.abs(bg - fg)def exclusion_blend(fg, bg):return fg + bg - 2. * fg * bgdef subtract_blend(fg, bg):res = bg - fgres = np.maximum(res, 0.)return resdef divide_blend(fg, bg):return bg / np.maximum(fg, EPS)def hue_blend(fg, bg):return set_luminance(set_saturation(fg, get_saturation(bg)),get_luminance(bg))def saturation_blend(fg, bg):return set_luminance(set_saturation(bg, get_saturation(fg)),get_luminance(bg))def color_blend(fg, bg):return set_luminance(fg, get_luminance(bg))def luminosity_blend(fg, bg):return set_luminance(bg, get_luminance(fg))
RGBA混合(图层带透明度)
Porter(1984)在alpha融合相关的理论中,根据两个图层的透明度关系,把图像划分为如下四个区域:
其中有三个区域是我们可见的:
- description = A ∩ B ‾ A \cap \overline{B} A∩B,area = α A ( 1 − α B ) \alpha_A(1-\alpha_B) αA(1−αB),color = c A c_A cA
- description = A ‾ ∩ B \overline{A} \cap B A∩B,area = ( 1 − α A ) α B (1-\alpha_A)\alpha_B (1−αA)αB,color = c B c_B cB
- description = A ∩ B A \cap B A∩B,area = α A α B \alpha_A\alpha_B αAαB,color = B l e n d ( c A , c B ) Blend(c_A,c_B) Blend(cA,cB)
结合alpha融合的理论,以及上述区域划分,在RGBA情况下混合公式的通用计算方式为:
o u t α = f g α + b g α − f g α ∗ b g α o u t c = f g c ∗ f g α ∗ ( 1 − b g α ) + b g c ∗ b g α ∗ ( 1 − f g α ) + f g α ∗ b g α ∗ B l e n d ( f g c , b g c ) o u t α \begin{aligned} out_\alpha &= fg_\alpha + bg_\alpha - fg_\alpha * bg_\alpha \\[2ex] out_c &= \frac {fg_c * fg_\alpha*(1-bg_\alpha) + bg_c * bg_\alpha*(1-fg_\alpha) + fg_\alpha * bg_\alpha *Blend(fg_c, bg_c)} {out_\alpha} \end{aligned} outαoutc=fgα+bgα−fgα∗bgα=outαfgc∗fgα∗(1−bgα)+bgc∗bgα∗(1−fgα)+fgα∗bgα∗Blend(fgc,bgc)
上述是非预乘的公式,根据之前的符号约定: c o l o r c α = c o l o r c ∗ c o l o r α color_{c\alpha} = color_{c}*color_{\alpha} colorcα=colorc∗colorα。
那么预乘的公式为:
o u t α = f g α + b g α − f g α ∗ b g α o u t c α = f g c α ( 1 − b g α ) + b g c α ∗ ( 1 − f g α ) + f g α ∗ b g α ∗ B l e n d ( f g c , b g c ) \begin{aligned} out_\alpha &= fg_\alpha + bg_\alpha - fg_\alpha * bg_\alpha \\[2ex] out_{c\alpha} &= fg_{c\alpha}(1-bg_\alpha) + bg_{c\alpha}*(1-fg_\alpha) + fg_\alpha * bg_\alpha *Blend(fg_c, bg_c) \end{aligned} outαoutcα=fgα+bgα−fgα∗bgα=fgcα(1−bgα)+bgcα∗(1−fgα)+fgα∗bgα∗Blend(fgc,bgc)
参考资料
Adobe官网资料(英文版):https://helpx.adobe.com/photoshop/using/blending-modes.html
Adobe官网资料(中文版,有些翻译不太准确):https://helpx.adobe.com/cn/photoshop/using/blending-modes.html
PDF reference:https://opensource.adobe.com/dc-acrobat-sdk-docs/pdfstandards/pdfreference1.7old.pdf
Porter(1984),https://dl.acm.org/doi/pdf/10.1145/964965.808606
https://www.w3.org/TR/compositing-1/
相关文章:
Photoshop中的混合模式公式详解
图层混合简介 图层混合(blend)顾名思义,就是把两个图层混合成一个。 最基本的混合是alpha融合(alpha compositing),这是一个遵循光的反射与透射等(简化版)物理学原理的混合方式。 各…...
Vue 自定义指令 Directive 的高级使用与最佳实践
前言 Vue.js 是一个非常流行的前端框架,它的核心理念是通过声明式的方式来描述 UI 和数据绑定。除了模板语法和组件系统,Vue 还提供了一个强大的功能——自定义指令。 自定义指令可以让我们对 DOM 元素进行底层操作,下面让我们通过一个有趣的…...
万字图文实战:从0到1构建 UniApp + Vue3 + TypeScript 移动端跨平台开源脚手架
🚀 作者主页: 有来技术 🔥 开源项目: youlai-mall 🍃 vue3-element-admin 🍃 youlai-boot 🍃 vue-uniapp-template 🌺 仓库主页: Gitee 💫 Github …...
在WebStorm遇到Error: error:0308010C:digital envelope routines::unsupported报错时的解决方案
作者:CSDN-PleaSure乐事 欢迎大家阅读我的博客 希望大家喜欢 使用环境:WebStorm 目录 介绍 解决 分析 方法一:设置环境变量 使用WebStorm 使用其他编译器 方法二:使用nvm切换nodejs版本 方法三:更新依赖版本 介…...
数据库产品中SQL注入防护功能应该包含哪些功能
数据库产品中 SQL 注入防护功能应包含以下几方面: 输入验证与过滤功能: 数据类型和格式验证:检查用户输入的数据是否符合预期的数据类型,比如对于一个应该是整数类型的字段,检查输入是否为整数;对于字符串…...
Ribbon客户端负载均衡策略测试及其改进
文章目录 一、目的概述二、验证步骤1、源码下载2、导入IDE3、运行前修改配置4、策略说明5、修改策略 三、最终结论四、改进措施1. 思路分析2. 核心代码3. 测试页面 一、目的概述 为了验证Ribbon客户端负载均衡策略在负载节点失效的情况下,是否具有故障转移的功能&a…...
linux网络编程5——Posix API和网络协议栈,使用TCP实现P2P通信
文章目录 Posix API和网络协议栈,使用TCP实现P2P通信1. socket()2. bind()3. listen()4. connect()5. accept()6. read()/write(), recv()/send()7. 内核tcp数据传输7.1 TCP流量控制7.2 TCP拥塞控制——慢启动/拥塞避免/快速恢复/快速重传 8. shutdown()9. close()9…...
低代码平台中的功能驱动开发:模块化与领域设计
在现代软件开发中,尤其是在低代码平台的背景下,清晰地定义功能和模块是成功的关键。功能驱动开发强调功能的优先性,模块化设计则确保系统的可维护性和可扩展性。本文将探讨如何在低代码平台中有效地将功能与模块结合起来,形成一个…...
HTTP和HTTPS基本概念,主要区别,应用场景
HTTP和 HTTPS是用于在网络中传输数据的协议,虽然它们的功能类似,但在安全性上存在显著差异。 1. HTTP 的基本概念 定义:HTTP 是一种无状态的、面向请求-响应的协议,用于客户端(如浏览器)和服务器之间传输…...
node.js使用Sequelize ORM操作数据库
一、什么是ORM ORM是在数据库和编程语言之间建立一种映射关系,这样可以让我们有非常简单的代码,来实现各种数据库的操作。 例如:使用mysql去查找表(表名称为Articles) SELECT * FROM Articles;但是我们使用ORM的话&…...
STM32-Modbus协议(一文通)
Modbus协议原理 RT-Thread官网开源modbus RT-Thread官方提供 FreeModbus开源。 野火有移植的例程。 QT经常用 libModbus库。 Modbus是什么? Modbus协议,从字面理解它包括Mod和Bus两部分,首先它是一种bus,即总线协议,和…...
100. 不同方向的投影视图
本节课给大家讲解,通过UI按钮界面交互改变threejs相机的观察视角。 x轴方向观察 // 通过UI按钮改变相机观察角度 document.getElementById(x).addEventListener(click, function () {camera.position.set(500, 0, 0); //x轴方向观察camera.lookAt(0, 0, 0); //重新…...
Appium中的api(三)
目录 Appium中的api(三) 1.输入和清空内容 1--输入内容 2--清空内容 2.获取文本内容 3.获取文本位置 4.获取文本的大小(即获取控件的宽和高) 5.滑动api 6.拖拽api 7.如何获取手机分辨率 8.如何截图 9.模拟按键事件api 10.操作通知栏 案例:App自动化模拟 …...
踩坑:关于使用ceph pg repair引发的业务阻塞
概述 在某次故障回溯中,发现引发集群故障,slow io,pg stuck的罪魁祸首竟是做了一次ceph pg repair $pgid。然而ceph pg repair作为使用频率极高的,用来修复pg不一致的常用手段,平时可能很少注意其使用规范和可能带来的…...
瞬间升级!电子文档华丽变身在线题库,效率翻倍✨
👋嘿小伙伴们,有个超赞的秘籍要告诉你们——土著刷题能将你的电子文档一键变身在线题库!😉 你还没发现这个宝藏功能吗?快来瞧瞧! 🌟是不是常被一堆电子版的学习资料搞得头昏脑涨,学习…...
如何动态改变本地的ip
在当今数字化时代,网络连接已成为我们日常生活和工作中不可或缺的一部分。无论是出于隐私保护、突破地域限制,还是为了测试和优化网络应用,动态改变本地IP地址的需求日益增多。本文将详细介绍如何安全、有效地实现这一目标,旨在帮…...
Spring Boot框架在中小企业设备管理中的创新应用
4系统概要设计 4.1概述 本系统采用B/S结构(Browser/Server,浏览器/服务器结构)和基于Web服务两种模式,是一个适用于Internet环境下的模型结构。只要用户能连上Internet,便可以在任何时间、任何地点使用。系统工作原理图如图4-1所示: 图4-1系统工作原理…...
Ceph入门到精通-Osd db扩容
ceph-bluestore-tool 是一个在 BlueStore 实例上执行低级管理操作的实用程序。 以下命令可用于 ceph-bluestore-tool 语法 ceph-bluestore-tool COMMAND [ --dev DEVICE … ] [ -i OSD_ID ] [ --path OSD_PATH ] [ --out-dir DIR ] [ --log-file | -l filename ] [ --deep ]c…...
windows msvc2017 x64编译AWS SDK CPP库
在本文中,我们将介绍如何编译AWS SDK C库,以便在您的项目中使用。AWS SDK C库提供了与Amazon Web Services交互的接口,允许您在C应用程序中使用AWS服务。 一、准备工作 在开始编译AWS SDK C库之前,请确保您的系统已经安装了以下…...
铜业机器人剥片 - SNK施努卡
SNK施努卡有色行业电解车间铜业机器人剥片 铜业机器人剥片技术是针对传统人工剥片效率低下、工作环境恶劣及生产质量不稳定的痛点而发展起来的自动化解决方案。 面临人工剥片的诸多挑战,包括低效率、工作环境差、人员流动大以及产品质量控制不精确等问题。 人工剥片…...
非接触式竖向位移、水平位移视频实时在线监测的设备分类及选型
前言 视觉是人工智能正在快速发展的一个分支,简单说来,机器视觉就是用机器代替人眼来做测量和判断。在结构健康自动化监测方面,机器视觉采用光学图像结合智能算法和物联网技术,利用先进的智能靶标识别及亚像素处理等技术ÿ…...
Svelte 5 正式发布:新一代前端框架!
10 月 22 日,Svelte 5 正式发布!该版本带来的更新主要包括: 重写框架:Svelte 5 是从头开始重写的,使得应用更快、更小、更可靠,并且代码更一致和符合习惯。 向后兼容:Svelte 5 几乎完全向后兼容…...
85.【C语言】数据结构之顺序表的中间插入和删除及遍历查找
目录 3.操作顺序表 1.分析中间插入函数 函数的参数 代码示例 图片分析 main.c部分改为 在SeqList.h添加SLInsert函数的声明 运行结果 2.分析中间删除函数 函数的参数 代码示例 图片分析 main.c部分改为 在SeqList.h添加SLErase函数的声明 运行结果 承接84.【C语…...
触觉智能Purple Pi OH鸿蒙开发板成功适配OpenHarmony5.0 Release,开启新征程!
10月22日,触觉智能Purple Pi OH鸿蒙开发板迎来了重大系统版本升级,成功适配OpenHarmony5.0 Release,为嵌入式开发者和科技爱好者们带来了全新的机遇与挑战! 触觉智能 Purple Pi OH 开发板一直以来都以其高品质和超高性价比而著称。…...
分布式解决方案---分布式ID
目录 是什么 特点 全局唯一 高并发 高可用 怎么做 实现方案 是什么 分布式ID是指在分布式系统中生成的唯一标识符。由于分布式系统的特点,多个节点可能会同时生成ID,因此需要确保每个ID在整个系统中是唯一的。 重点就是唯一性!&#x…...
httpd服务
文章目录 1、搭建一个网络yum源2、基于域名访问的虚拟主机3、基于端口来访问域名4、搭建个人网站5、加密访问显示自定义网页内容 1、搭建一个网络yum源 [roottest01 conf.d]# cat repo.conf <virtualhost *:80>documentroot /var/www/html/ServerName 10.104.43.154ali…...
Linux系统安装Redis详细操作步骤(二进制发布包安装方式)
安装方式介绍 在Linux系统中,安装软件的方式主要有四种,这四种安装方式的特点如下: 安装方式特点二进制发布包安装软件已经针对具体平台编译打包发布,只要解压,修改配置即可rpm安装软件已经按照redhat的包管理规范进…...
Jenkins和Gitlab整合构建CI/CD流水线
配置环境 虚拟机建议4G起步 192.168.58.199 192.168.58.200 部署Jenkins 部署Jenkins参考这篇文章:Jenkins安装部署_connecting to pkg.jenkins.io (pkg.jenkins.io)|151.-CSDN博客 安装完毕之后根据下图操作 选择git,添加git仓库克隆url,选…...
14 C语言中的关键字
C语言中的关键字 在C语言中,关键字是一些预定义的单词,它们具有特殊的意义和用途。这些关键字不能用作标识符,比如变量名、函数名等,因为它们被保留用于特定的语言结构和操作。 关键字的分类 C语言的关键字可以分为几个主要类别…...
(11)(2.1.7) FETtec OneWire ESCs(一)
文章目录 前言 1 去哪里买 2 连接 3 组态 前言 !Note 此功能在固件版本 4.1.1 及更高版本上可用。 OneWire 是 FETtec 的 ESC 双向通信协议(ESC bi-directional communication protocol)。 FETtec OneWire ESC 的遥测信息被发送回自动驾驶仪: 电…...
单页网站设计制作/企业网站推广方案设计毕业设计
目录useraddpasswduserdelusermodidsusudo1.useradduseradd - create a new user or update default new user information. 命令一般用来创建新用户或更新默认新用户信息。选项-c , --comment COMMENT 给新用户添加备注;-d, -- home-dir HOME_DIR为主目录指定一个名…...
wordpress home插件/新东方托福班价目表
很多朋友看完macOS Big Sur的介绍就把系统升级到big sur了,面对半成品的开发者预览版(Developer Preview),很多人表示无法接受,可降回10.15.6 的时候,提示不能回退老版本,那么macOS Big Sur如何…...
贵州最好的网站建设推广公司/东莞疫情最新消息今天又封了
一、去掉图片间隙:hack1、img{ display:block; }hack2、将<div></div>与<img>写在同一行 二、ie6双倍浮向(双倍边距)出现情况:在浮动和外边距同事出现时会出现双向浮动。hack:给浮动元素添加&#x…...
商城网站怎么做/如何做好线上推广
当前云桌面厂家虽然有很多但是就按类别来说我们可以把它分为两大类的,一类就是服务器在本地的私有云桌面,还有一类就是服务器不在本地的公有云桌面。那么他们两者之间到底有什么不一样的呢,我们是选择私有云桌面好还是公有云桌面更好的呢&…...
临沂网站推广排名/免费推广链接
1.onCreate( )方法是android应用程序中最常见的方法之一:翻译过来就是说,onCreate()函数是在activity初始化的时候调用的,通常情况下,我们需要在onCreate()中调用setContentView(int)函数填充屏幕的UI,一般通过findVie…...
建立网站教程视频/汕头网站建设方案推广
随着信息技术的发展,计算机在我们的日常生活中扮演了越来越重要的作用,通过现有的技术以及当今在计算机领域还在研究中的尖端科技分析,计算机的发展将趋向超高速、超小型、平行处理和智能化,量子、光子、分子和纳米计算机将具有感知、思考、判…...