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
image_1 saved image_2 start save
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
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
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
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
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
found saved
InĀ [2]: