Blend Subtract¶

Gives an example of using blend subtract to isolate the differences between two images.

InĀ [1]:
from pathlib import Path

import brushcue

IMAGE_1_PATH = "/home/dito/dev/monorepo/writing/graphics/chapters/blend/assets/subtract-demo/image_1.JPG"
IMAGE_2_PATH = "/home/dito/dev/monorepo/writing/graphics/chapters/blend/assets/subtract-demo/image_2.JPG"
OUTPUT_ROOT = "/home/dito/dev/monorepo/writing/graphics/chapters/blend/assets/subtract-demo"
ctx = brushcue.Context()
InĀ [2]:
from IPython.display import Image
import random

working_color_profile = brushcue.color_profile_s_r_g_b()

WIDTH = 1920
HEIGHT = 1080

def save_image(composition: brushcue.Graph, name: str):
    print(f"{name} start save")
    path = Path(f"{OUTPUT_ROOT}/{name}.png")
    path.parent.mkdir(parents=True, exist_ok=True)
    result = composition.execute(ctx).as_composition()
    output_bytes = result.to_image_bytes(ctx)
    with open(path, "wb") as f:
        f.write(output_bytes)
    display(Image(path))
    print(f"{name} saved")

def generate_base_image_painter(iterations: int) -> brushcue.Graph:
    rng = random.Random(200)
    painter = brushcue.painter_new(working_color_profile)
    painter = brushcue.painter_add_rectangle_with_render_style(
        painter,
        brushcue.point2f_from_components(WIDTH / 2, HEIGHT / 2),
        brushcue.vector2f_constant(WIDTH, HEIGHT),
        0,
        brushcue.render_style_fill_only(brushcue.fill_solid(brushcue.r_g_b_a_color_constant(0, 0, 0, 1))),
                    brushcue.transform2_to_list(brushcue.transform2_identity())
    )
    for idx in range(iterations):
        x_coordinate = rng.uniform(0, WIDTH)
        y_coordinate = rng.uniform(0, HEIGHT)

        r = rng.uniform(0, 1)
        g = rng.uniform(0, 1)
        b = rng.uniform(0, 1)

        radius = rng.uniform(80, 150)

        render_style = brushcue.render_style_fill_only(brushcue.fill_solid(
            brushcue.r_g_b_a_color_constant(r, g, b, 1)
        ))

        painter = brushcue.painter_add_ellipse_with_render_style(
            painter,
            brushcue.point2f_from_components(x_coordinate, y_coordinate),
            brushcue.vector2f_constant(radius, radius),
            0,
            render_style,
            brushcue.transform2_to_list(brushcue.transform2_identity())
        )

    composition = brushcue.composition_painter(painter)
    composition = brushcue.composition_crop(composition, brushcue.bounds2f_from_x_y_width_height(0, 0, WIDTH, HEIGHT))
    return composition

image_1 = generate_base_image_painter(2999)
save_image(image_1, "image_1")
image_2 = generate_base_image_painter(3000)
save_image(image_2, "image_2")
difference = brushcue.composition_blend_subtract(image_2, image_1, brushcue.transform2_identity())
save_image(difference, "difference")
absolute_value = brushcue.composition_absolute_value(difference)
save_image(absolute_value, "absolute_value")
grayscale = brushcue.composition_grayscale(absolute_value)
save_image(grayscale, "grayscale")
threshold = brushcue.composition_color_threshold(grayscale, 0.01)
save_image(threshold, "threshold")
thresholded_with_min_alpha = brushcue.composition_color_transformer_shader(
    threshold,
    "if (input.a < 0.01) { return vec4<f32>(input.rgb, 0.2); } else { return input; }",
    "",
    brushcue.composition_color_profile(threshold),
    brushcue.composition_color_profile(threshold),
    brushcue.dictionary_create()
)
blended_over = brushcue.composition_blend_stencil(thresholded_with_min_alpha, image_2, brushcue.transform2_identity())
save_image(blended_over, "found")
image_1 start save
new pipeline: painter_Tessellated_fill_solid_no_brush_sample_true
new pipeline: painter_Elliptical_fill_solid_no_brush_sample_true
No description has been provided for this image
image_1 saved
image_2 start save
No description has been provided for this image
image_2 saved
difference start save
[image_recipe::TextureCache] reusing tiled texture requested_hash=1887323197918917245 cache_key=1887323197918917245
new pipeline: color_transformer_shader_rgba16float_compute_10540239407552119869_inarr_false
new pipeline: blend_Subtract_rgba16float_compute_bg_false_fg_false
new pipeline: color_transformer_shader_rgba16float_compute_16861657794771480865_inarr_false
No description has been provided for this image
difference saved
absolute_value start save
[image_recipe::TextureCache] reusing tiled texture requested_hash=2762462211564881687 cache_key=2762462211564881687
[image_recipe::TextureCache] reusing tiled texture requested_hash=1887323197918917245 cache_key=1887323197918917245
new pipeline: color_transformer_shader_rgba16float_compute_9490472502153166128_inarr_false
No description has been provided for this image
absolute_value saved
grayscale start save
[image_recipe::TextureCache] reusing tiled texture requested_hash=2762462211564881687 cache_key=2762462211564881687
[image_recipe::TextureCache] reusing tiled texture requested_hash=1887323197918917245 cache_key=1887323197918917245
new pipeline: color_transformer_shader_rgba16float_compute_12041777719757925520_inarr_false
new pipeline: color_transformer_shader_rgba16float_compute_70442839873914227_inarr_false
No description has been provided for this image
grayscale saved
threshold start save
[image_recipe::TextureCache] reusing tiled texture requested_hash=2762462211564881687 cache_key=2762462211564881687
[image_recipe::TextureCache] reusing tiled texture requested_hash=1887323197918917245 cache_key=1887323197918917245
new pipeline: color_transformer_shader_rgba16float_compute_14669891994802337450_inarr_false
new pipeline: color_transformer_shader_rgba16float_compute_5819160800384090615_inarr_false
No description has been provided for this image
threshold saved
found start save
[image_recipe::TextureCache] reusing tiled texture requested_hash=1887323197918917245 cache_key=1887323197918917245
[image_recipe::TextureCache] reusing tiled texture requested_hash=2762462211564881687 cache_key=2762462211564881687
new pipeline: color_transformer_shader_rgba16float_compute_17752146534006353786_inarr_false
new pipeline: blend_Stencil_rgba16float_compute_bg_false_fg_false
No description has been provided for this image
found saved
InĀ [2]: