BrushCue Example: Film Noir¶

Open In Colab

You can use this tool online at https://www.brushcue.com/tools/film-noir

In [ ]:
!pip install brushcue
In [1]:
import brushcue
from PIL import Image
import io

input_image_1 = brushcue.composition_monet_women_with_parasol() # insert your own image here
float_constant_2 = brushcue.float_constant(0.5)
float_constant_3 = brushcue.float_constant(0.20000000298023224)
float_constant_4 = brushcue.float_constant(1.0)
float_constant_5 = brushcue.float_constant(0.5)
float_constant_6 = brushcue.float_constant(3.75)
float_constant_7 = brushcue.float_constant(2.0)
string_constant_8 = brushcue.string_constant("let texture_dimens = textureDimensions(input_texture);\nlet width = f32(texture_dimens.x);\nlet height = f32(texture_dimens.y);\nlet avg_dimen = (width + height) * 0.5;\nlet uv = vec2<f32>(f32(position.x) / width, f32(position.y) / height);\nlet L = input.x;\nlet a = input.y;\nlet b = input.z;\nlet alpha = input.a;\n\nlet grain_strength = (1.0 - L) * grain_strength_param * 0.1;\n\nlet fine_freq = fine_grain_frequency * avg_dimen / 1024.0;\nlet medium_freq = medium_grain_frequency * avg_dimen / 1024.0;\nlet high_freq = high_grain_frequency * avg_dimen / 1024.0;\n\n// Fine grain noise\nlet p = uv * fine_freq;\nlet pi = floor(p);\nlet pf = fract(p);\nlet u = pf * pf * (3.0 - 2.0 * pf);\n\nlet h1 = fract(sin(dot(pi, vec2<f32>(12.9898, 78.233))) * 43758.5453);\nlet h2 = fract(sin(dot(pi + vec2<f32>(1.0, 0.0), vec2<f32>(12.9898, 78.233))) * 43758.5453);\nlet h3 = fract(sin(dot(pi + vec2<f32>(0.0, 1.0), vec2<f32>(12.9898, 78.233))) * 43758.5453);\nlet h4 = fract(sin(dot(pi + vec2<f32>(1.0, 1.0), vec2<f32>(12.9898, 78.233))) * 43758.5453);\n\nlet v0 = mix(h1, h2, u.x);\nlet v1 = mix(h3, h4, u.x);\nlet fine_noise = mix(v0, v1, u.y);\n\n// Medium grain noise\nlet medium_p = uv * medium_freq;\nlet medium_pi = floor(medium_p);\nlet medium_pf = fract(medium_p);\nlet medium_u = medium_pf * medium_pf * (3.0 - 2.0 * medium_pf);\n\nlet mh1 = fract(sin(dot(medium_pi, vec2<f32>(12.9898, 78.233))) * 43758.5453);\nlet mh2 = fract(sin(dot(medium_pi + vec2<f32>(1.0, 0.0), vec2<f32>(12.9898, 78.233))) * 43758.5453);\nlet mh3 = fract(sin(dot(medium_pi + vec2<f32>(0.0, 1.0), vec2<f32>(12.9898, 78.233))) * 43758.5453);\nlet mh4 = fract(sin(dot(medium_pi + vec2<f32>(1.0, 1.0), vec2<f32>(12.9898, 78.233))) * 43758.5453);\n\nlet mv0 = mix(mh1, mh2, medium_u.x);\nlet mv1 = mix(mh3, mh4, medium_u.x);\nlet medium_noise = mix(mv0, mv1, medium_u.y);\n\n// High grain noise\nlet high_p = uv * high_freq;\nlet high_pi = floor(high_p);\nlet high_pf = fract(high_p);\nlet high_u = high_pf * high_pf * (3.0 - 2.0 * high_pf);\n\nlet hh1 = fract(sin(dot(high_pi, vec2<f32>(12.9898, 78.233))) * 43758.5453);\nlet hh2 = fract(sin(dot(high_pi + vec2<f32>(1.0, 0.0), vec2<f32>(12.9898, 78.233))) * 43758.5453);\nlet hh3 = fract(sin(dot(high_pi + vec2<f32>(0.0, 1.0), vec2<f32>(12.9898, 78.233))) * 43758.5453);\nlet hh4 = fract(sin(dot(high_pi + vec2<f32>(1.0, 1.0), vec2<f32>(12.9898, 78.233))) * 43758.5453);\n\nlet hv0 = mix(hh1, hh2, high_u.x);\nlet hv1 = mix(hh3, hh4, high_u.x);\nlet high_noise = mix(hv0, hv1, high_u.y);\n\n// Combine noises with separate weights, normalize to -0.5 to 0.5\nlet combined_noise = (fine_noise * fine_weight + medium_noise * medium_weight + high_noise * high_weight) - 0.5;\nlet grain_amount = combined_noise * grain_strength;\n\n// Apply grain primarily to luminance\nlet L_grained = clamp(L + grain_amount, 0.0, 1.0);\n\n// Minimal color grain for subtle variation\nlet a_grained = clamp(a + grain_amount * 0.02, -0.4, 0.4);\nlet b_grained = clamp(b + grain_amount * 0.02, -0.4, 0.4);\n\nreturn vec4<f32>(L_grained, a_grained, b_grained, alpha);")
string_constant_9 = brushcue.string_constant("// No helpers")
color_profile_oklaba_10 = brushcue.color_profile_ok_lab_a()
color_profile_oklaba_11 = brushcue.color_profile_ok_lab_a()
bool_constant_12 = brushcue.bool_constant(True)
string_constant_13 = brushcue.string_constant("grain_strength_param")
grain_strength_14 = brushcue.float_constant(1.5)
string_constant_15 = brushcue.string_constant("high_weight")
high_grain_weight_16 = brushcue.float_constant(0.20000000298023224)
string_constant_17 = brushcue.string_constant("high_grain_frequency")
high_grain_frequency_18 = brushcue.float_constant(300.0)
string_constant_19 = brushcue.string_constant("medium_weight")
low_grain_weight_20 = brushcue.float_constant(0.30000001192092896)
string_constant_21 = brushcue.string_constant("medium_grain_frequency")
low_grain_frequency_22 = brushcue.float_constant(50.0)
string_constant_23 = brushcue.string_constant("fine_weight")
medium_grain_weight_24 = brushcue.float_constant(0.5)
dictionary_create_25 = brushcue.dictionary_create()
string_constant_26 = brushcue.string_constant("fine_grain_frequency")
medium_grain_frequency_27 = brushcue.float_constant(150.0)
curve_pivoted_sigmoid_28 = brushcue.curve_pivoted_sigmoid(float_constant_5, float_constant_6)
add_fine_frequency_to_dictionary_29 = brushcue.float_add_to_dictionary(dictionary_create_25, string_constant_26, medium_grain_frequency_27)
add_fine_weight_to_dictionary_30 = brushcue.float_add_to_dictionary(add_fine_frequency_to_dictionary_29, string_constant_23, medium_grain_weight_24)
add_medium_frequency_to_dictionary_31 = brushcue.float_add_to_dictionary(add_fine_weight_to_dictionary_30, string_constant_21, low_grain_frequency_22)
add_medium_weight_to_dictionary_32 = brushcue.float_add_to_dictionary(add_medium_frequency_to_dictionary_31, string_constant_19, low_grain_weight_20)
add_high_frequency_to_dictionary_33 = brushcue.float_add_to_dictionary(add_medium_weight_to_dictionary_32, string_constant_17, high_grain_frequency_18)
add_high_weight_to_dictionary_34 = brushcue.float_add_to_dictionary(add_high_frequency_to_dictionary_33, string_constant_15, high_grain_weight_16)
add_grain_strength_to_dictionary_35 = brushcue.float_add_to_dictionary(add_high_weight_to_dictionary_34, string_constant_13, grain_strength_14)
film_grain_shader_36 = brushcue.composition_custom_transformer_shader(input_image_1, string_constant_8, string_constant_9, color_profile_oklaba_10, color_profile_oklaba_11, add_grain_strength_to_dictionary_35, bool_constant_12)
composition_gaussian_blur_37 = brushcue.composition_gaussian_blur(film_grain_shader_36, float_constant_7)
composition_grayscale_38 = brushcue.composition_grayscale(composition_gaussian_blur_37)
composition_lightness_curve_39 = brushcue.composition_l_curve(composition_grayscale_38, curve_pivoted_sigmoid_28)
film_noir_result_40 = brushcue.composition_vignette(composition_lightness_curve_39, float_constant_2, float_constant_3, float_constant_4)

ctx = brushcue.Context()
result = film_noir_result_40.execute(ctx)
composition = result.as_composition()
data_bytes = composition.to_image_bytes(ctx)
img = Image.open(io.BytesIO(data_bytes))
img.thumbnail((400, 400)) # remove this line for full resolution
img
Out[1]:
No description has been provided for this image