26
26
27
27
from blendmodes .blendtype import BlendType
28
28
29
+ HALF_THRESHOLD = 0.5
30
+
29
31
30
32
def normal (background : np .ndarray , foreground : np .ndarray ) -> np .ndarray :
31
33
"""BlendType.NORMAL."""
@@ -76,7 +78,7 @@ def glow(background: np.ndarray, foreground: np.ndarray) -> np.ndarray:
76
78
def overlay (background : np .ndarray , foreground : np .ndarray ) -> np .ndarray :
77
79
"""BlendType.OVERLAY."""
78
80
return np .where (
79
- background < 0.5 ,
81
+ background < HALF_THRESHOLD ,
80
82
2 * background * foreground ,
81
83
1.0 - (2 * (1.0 - background ) * (1.0 - foreground )),
82
84
)
@@ -125,7 +127,7 @@ def softlight(background: np.ndarray, foreground: np.ndarray) -> np.ndarray:
125
127
def hardlight (background : np .ndarray , foreground : np .ndarray ) -> np .ndarray :
126
128
"""BlendType.HARDLIGHT."""
127
129
return np .where (
128
- foreground < 0.5 ,
130
+ foreground < HALF_THRESHOLD ,
129
131
np .minimum (background * 2 * foreground , 1.0 ),
130
132
np .minimum (1.0 - ((1.0 - background ) * (1.0 - (foreground - 0.5 ) * 2.0 )), 1.0 ),
131
133
)
@@ -148,16 +150,16 @@ def divide(background: np.ndarray, foreground: np.ndarray) -> np.ndarray:
148
150
149
151
def pinlight (background : np .ndarray , foreground : np .ndarray ) -> np .ndarray :
150
152
"""BlendType.PINLIGHT."""
151
- return np .minimum (background , 2 * foreground ) * (foreground < 0.5 ) + np .maximum (
153
+ return np .minimum (background , 2 * foreground ) * (foreground < HALF_THRESHOLD ) + np .maximum (
152
154
background , 2 * (foreground - 0.5 )
153
- ) * (foreground >= 0.5 )
155
+ ) * (foreground >= HALF_THRESHOLD )
154
156
155
157
156
158
def vividlight (background : np .ndarray , foreground : np .ndarray ) -> np .ndarray :
157
159
"""BlendType.VIVIDLIGHT."""
158
- return colourburn (background , foreground * 2 ) * (foreground < 0.5 ) + colourdodge (
160
+ return colourburn (background , foreground * 2 ) * (foreground < HALF_THRESHOLD ) + colourdodge (
159
161
background , 2 * (foreground - 0.5 )
160
- ) * (foreground >= 0.5 )
162
+ ) * (foreground >= HALF_THRESHOLD )
161
163
162
164
163
165
def exclusion (background : np .ndarray , foreground : np .ndarray ) -> np .ndarray :
@@ -543,60 +545,54 @@ def blendLayersArray(
543
545
"""
544
546
545
547
# Convert the Image.Image to a numpy array if required
546
- if isinstance (background , Image .Image ):
547
- background = np .array (background .convert ("RGBA" ))
548
- if isinstance (foreground , Image .Image ):
549
- foreground = np .array (foreground .convert ("RGBA" ))
548
+ bg = np .array (background .convert ("RGBA" )) if isinstance (background , Image .Image ) else background
549
+ fg = np .array (foreground .convert ("RGBA" )) if isinstance (foreground , Image .Image ) else foreground
550
550
551
551
# do any offset shifting first
552
552
if offsets [0 ] > 0 :
553
- foreground = np .hstack (
554
- (np .zeros ((foreground .shape [0 ], offsets [0 ], 4 ), dtype = np .float64 ), foreground )
555
- )
553
+ fg = np .hstack ((np .zeros ((bg .shape [0 ], offsets [0 ], 4 ), dtype = np .float64 ), fg ))
556
554
elif offsets [0 ] < 0 :
557
- if offsets [0 ] > - 1 * foreground .shape [1 ]:
558
- foreground = foreground [:, - 1 * offsets [0 ] :, :]
555
+ if offsets [0 ] > - 1 * fg .shape [1 ]:
556
+ fg = fg [:, - 1 * offsets [0 ] :, :]
559
557
else :
560
558
# offset offscreen completely, there is nothing left
561
- return np .zeros (background .shape , dtype = np .float64 )
559
+ return np .zeros (bg .shape , dtype = np .float64 )
562
560
if offsets [1 ] > 0 :
563
- foreground = np .vstack (
564
- (np .zeros ((offsets [1 ], foreground .shape [1 ], 4 ), dtype = np .float64 ), foreground )
565
- )
561
+ fg = np .vstack ((np .zeros ((offsets [1 ], fg .shape [1 ], 4 ), dtype = np .float64 ), fg ))
566
562
elif offsets [1 ] < 0 :
567
- if offsets [1 ] > - 1 * foreground .shape [0 ]:
568
- foreground = foreground [- 1 * offsets [1 ] :, :, :]
563
+ if offsets [1 ] > - 1 * fg .shape [0 ]:
564
+ fg = fg [- 1 * offsets [1 ] :, :, :]
569
565
else :
570
566
# offset offscreen completely, there is nothing left
571
- return np .zeros (background .shape , dtype = np .float64 )
567
+ return np .zeros (bg .shape , dtype = np .float64 )
572
568
573
569
# resize array to fill small images with zeros
574
- if foreground .shape [0 ] < background .shape [0 ]:
575
- foreground = np .vstack (
570
+ if fg .shape [0 ] < bg .shape [0 ]:
571
+ fg = np .vstack (
576
572
(
577
- foreground ,
573
+ fg ,
578
574
np .zeros (
579
- (background .shape [0 ] - foreground .shape [0 ], foreground .shape [1 ], 4 ),
575
+ (bg .shape [0 ] - fg .shape [0 ], fg .shape [1 ], 4 ),
580
576
dtype = np .float64 ,
581
577
),
582
578
)
583
579
)
584
- if foreground .shape [1 ] < background .shape [1 ]:
585
- foreground = np .hstack (
580
+ if fg .shape [1 ] < bg .shape [1 ]:
581
+ fg = np .hstack (
586
582
(
587
- foreground ,
583
+ fg ,
588
584
np .zeros (
589
- (foreground .shape [0 ], background .shape [1 ] - foreground .shape [1 ], 4 ),
585
+ (fg .shape [0 ], bg .shape [1 ] - fg .shape [1 ], 4 ),
590
586
dtype = np .float64 ,
591
587
),
592
588
)
593
589
)
594
590
595
591
# crop the source if the backdrop is smaller
596
- foreground = foreground [: background .shape [0 ], : background .shape [1 ], :]
592
+ fg = fg [: bg .shape [0 ], : bg .shape [1 ], :]
597
593
598
- lower_norm = background / 255.0
599
- upper_norm = foreground / 255.0
594
+ lower_norm = bg / 255.0
595
+ upper_norm = fg / 255.0
600
596
601
597
upper_alpha = upper_norm [:, :, 3 ] * opacity
602
598
lower_alpha = lower_norm [:, :, 3 ]
@@ -639,10 +635,14 @@ def alpha_comp_shell(
639
635
640
636
blend_rgb = blend (lower_rgb , upper_rgb , blendType )
641
637
642
- lower_rgb_part = np . multiply ((( 1.0 - upper_alpha ) * lower_alpha )[:, :, None ], lower_rgb )
643
- upper_rgb_part = np . multiply ((( 1.0 - lower_alpha ) * upper_alpha )[:, :, None ], upper_rgb )
644
- blended_rgb_part = np . multiply (( lower_alpha * upper_alpha )[:, :, None ], blend_rgb )
638
+ lower_rgb_factor = ( 1.0 - upper_alpha ) * lower_alpha
639
+ upper_rgb_factor = ( 1.0 - lower_alpha ) * upper_alpha
640
+ blended_rgb_factor = lower_alpha * upper_alpha
645
641
646
- out_rgb = np .divide ((lower_rgb_part + upper_rgb_part + blended_rgb_part ), out_alpha [:, :, None ])
642
+ out_rgb = (
643
+ lower_rgb_factor [:, :, None ] * lower_rgb
644
+ + upper_rgb_factor [:, :, None ] * upper_rgb
645
+ + blended_rgb_factor [:, :, None ] * blend_rgb
646
+ ) / out_alpha [:, :, None ]
647
647
648
648
return out_rgb , out_alpha
0 commit comments