color_tools.image.blend
Photoshop-compatible blend mode operations for image layers.
This module provides all 27 standard Photoshop blend modes for compositing two images together, using numpy for efficient per-pixel math and Pillow for image I/O.
Requires: pip install color-match-tools[image]
Blend Mode Categories:
Normal: normal, dissolve
Darken: darken, multiply, color_burn, linear_burn, darker_color
Lighten: lighten, screen, color_dodge, linear_dodge, lighter_color
Contrast: overlay, soft_light, hard_light, vivid_light, linear_light,
pin_light, hard_mix
Comparative: difference, exclusion, subtract, divide
Component: hue, saturation, color, luminosity
Primary API:
For most users, the only two names you need are:
blend_images(base_path, blend_path, mode, opacity, output_path)— loads two images, applies a blend mode, and returns a PILImage.BLEND_MODES— dict mapping every mode name to its numpy function, useful for listing or validating available modes.
The 27 individual mode functions (multiply, screen, etc.) are also
exposed for advanced use. They operate directly on normalized float32 numpy
arrays with shape (H, W, 3) in the [0.0, 1.0] range. If you call
them directly you are responsible for array preparation, clipping, and
reconverting back to uint8. Use blend_images() unless you are managing
your own image pipeline.
Example:
>>> from color_tools.image import blend_images, BLEND_MODES
>>>
>>> # Blend two images using multiply mode at 80% opacity
>>> result = blend_images("base.png", "layer.png", mode="multiply", opacity=0.8)
>>> result.save("output.png")
>>>
>>> # List all available blend modes
>>> print(sorted(BLEND_MODES.keys()))
- color_tools.image.blend.dissolve(a, b)[source]
Dissolve: random 50/50 pixel selection from base or blend.
For opacity-controlled dissolve, use blend_images() which uses the opacity value as the random selection threshold.
- color_tools.image.blend.multiply(a, b)[source]
Multiply: darkens by multiplying both layers (like overlapping transparencies).
- color_tools.image.blend.color_burn(a, b)[source]
Color Burn: darkens base by increasing contrast toward blend color.
- color_tools.image.blend.linear_burn(a, b)[source]
Linear Burn: darkens base by decreasing brightness.
- color_tools.image.blend.darker_color(a, b)[source]
Darker Color: keep whichever full pixel has lower overall luminance.
- color_tools.image.blend.screen(a, b)[source]
Screen: lightens by inverting, multiplying, then inverting again.
- color_tools.image.blend.color_dodge(a, b)[source]
Color Dodge: lightens base by decreasing contrast toward blend color.
- color_tools.image.blend.linear_dodge(a, b)[source]
Linear Dodge (Add): lightens base by adding blend brightness.
- color_tools.image.blend.lighter_color(a, b)[source]
Lighter Color: keep whichever full pixel has higher overall luminance.
- color_tools.image.blend.overlay(a, b)[source]
Overlay: multiply where base is dark, screen where base is light.
- color_tools.image.blend.soft_light(a, b)[source]
Soft Light: subtle lighten/darken using the W3C/Photoshop piecewise formula.
Matches Photoshop output (not the simpler Pegtop approximation):
if b <= 0.5: a - (1 - 2b) × a × (1 - a) else: a + (2b - 1) × (D(a) - a)
- where D(a) = ((16a - 12)a + 4)a if a <= 0.25
= √a if a > 0.25
- color_tools.image.blend.hard_light(a, b)[source]
Hard Light: overlay with base and blend roles swapped.
- color_tools.image.blend.vivid_light(a, b)[source]
Vivid Light: color burn where blend is dark, color dodge where blend is light.
- color_tools.image.blend.linear_light(a, b)[source]
Linear Light: linear burn or linear dodge depending on blend brightness.
- color_tools.image.blend.pin_light(a, b)[source]
Pin Light: darken or lighten depending on blend layer value.
- color_tools.image.blend.hard_mix(a, b)[source]
Hard Mix: extreme posterization — each channel becomes 0 or 1.
- color_tools.image.blend.exclusion(a, b)[source]
Exclusion: lower-contrast alternative to Difference.
- color_tools.image.blend.subtract(a, b)[source]
Subtract: subtracts blend from base, clamped to black.
- color_tools.image.blend.hue(a, b)[source]
Hue: hue from blend layer, saturation and luminosity from base.
- color_tools.image.blend.saturation(a, b)[source]
Saturation: saturation from blend layer, hue and luminosity from base.
- color_tools.image.blend.color(a, b)[source]
Color: hue and saturation from blend layer, luminosity from base.
- color_tools.image.blend.luminosity(a, b)[source]
Luminosity: luminosity from blend layer, hue and saturation from base.
- color_tools.image.blend.blend_images(base_path, blend_path, mode='normal', opacity=1.0, output_path=None)[source]
Blend two images using a Photoshop-compatible blend mode.
Both images are converted to RGBA before blending. The blend mode is applied only to the RGB channels; alpha is composited separately using standard src-over with opacity. The blend layer is resized to match the base image if their sizes differ.
- Parameters:
base_path (
str|Path) – Path to the base (background) image.blend_path (
str|Path) – Path to the blend (top) layer image.mode (
str) – Blend mode name. See BLEND_MODES for all options.opacity (
float) – Blend layer opacity in [0.0, 1.0]. Default 1.0.output_path (
str|Path|None) – If provided, the result is saved to this path.
- Return type:
- Returns:
Composited PIL Image in RGBA mode.
- Raises:
ValueError – If mode is not in BLEND_MODES or opacity is out of range.
ImportError – If Pillow or numpy are not installed.
Example
>>> result = blend_images("base.png", "layer.png", mode="multiply", opacity=0.8) >>> result.save("output.png")