From a484edc1fd9676c9ef595c013b4ef8b571ed3db8 Mon Sep 17 00:00:00 2001 From: luboslenco Date: Mon, 20 Oct 2025 10:27:25 +0200 Subject: [PATCH] Neural nodes progress --- paint/sources/neural_nodes/edit_image_node.ts | 31 +++- .../neural_nodes/inpaint_image_node.ts | 155 +++++++++++++++++ .../neural_nodes/text_to_image_node.ts | 39 ++++- paint/sources/neural_nodes/tile_image_node.ts | 163 ++++++++++++++++++ 4 files changed, 374 insertions(+), 14 deletions(-) create mode 100644 paint/sources/neural_nodes/inpaint_image_node.ts create mode 100644 paint/sources/neural_nodes/tile_image_node.ts diff --git a/paint/sources/neural_nodes/edit_image_node.ts b/paint/sources/neural_nodes/edit_image_node.ts index 374feec6..d8014999 100644 --- a/paint/sources/neural_nodes/edit_image_node.ts +++ b/paint/sources/neural_nodes/edit_image_node.ts @@ -54,12 +54,33 @@ function edit_image_node_button(node_id: i32) { iron_write_png(dir + path_sep + "input.png", gpu_get_texture_pixels(input), input.width, input.height, 0); let argv: string[] = [ - dir + "/sd_qie", "--diffusion-model", dir + "/Qwen-Image-Edit-2509-Q8_0.gguf", "--vae", dir + "/qwen_image_vae.safetensors", "--qwen2vl", - dir + "/Qwen2.5-VL-7B-Instruct-Q8_0.gguf", "--qwen2vl_vision", dir + "/Qwen2.5-VL-7B-Instruct.mmproj-Q8_0.gguf", "--sampling-method", "euler", + dir + "/sd_qie", + "--diffusion-model", + dir + "/Qwen-Image-Edit-2509-Q8_0.gguf", + "--vae", + dir + "/qwen_image_vae.safetensors", + "--qwen2vl", + dir + "/Qwen2.5-VL-7B-Instruct-Q8_0.gguf", + "--qwen2vl_vision", + dir + "/Qwen2.5-VL-7B-Instruct.mmproj-Q8_0.gguf", + "--sampling-method", + "euler", "--offload-to-cpu", - // "--diffusion-fa", - "--cfg-scale", "2.5", "--flow-shift", "3", "--steps", "20", "-s", "-1", "-W", "512", "-H", "512", "-p", "'" + prompt + "'", "-r", - dir + "/input.png", "-o", dir + "/output.png", null + "--steps", + "40", + "-s", + "-1", + "-W", + "512", + "-H", + "512", + "-p", + "'" + prompt + "'", + "-r", + dir + "/input.png", + "-o", + dir + "/output.png", + null ]; iron_exec_async(argv[0], argv.buffer); diff --git a/paint/sources/neural_nodes/inpaint_image_node.ts b/paint/sources/neural_nodes/inpaint_image_node.ts new file mode 100644 index 00000000..c1f0d964 --- /dev/null +++ b/paint/sources/neural_nodes/inpaint_image_node.ts @@ -0,0 +1,155 @@ + +let inpaint_image_node_result: gpu_texture_t = null; + +function inpaint_image_node_init() { + array_push(nodes_material_neural, inpaint_image_node_def); + map_set(parser_material_node_vectors, "NEURAL_INPAINT_IMAGE", inpaint_image_node_vector); + map_set(ui_nodes_custom_buttons, "inpaint_image_node_button", inpaint_image_node_button); +} + +function inpaint_image_node_vector(node: ui_node_t, socket: ui_node_socket_t): string { + if (inpaint_image_node_result == null) { + return "float3(0.0, 0.0, 0.0)"; + } + let tex_name: string = parser_material_node_name(node); + map_set(data_cached_images, tex_name, inpaint_image_node_result); + let tex: bind_tex_t = parser_material_make_bind_tex(tex_name, tex_name); + let texstore: string = parser_material_texture_store(node, tex, tex_name, color_space_t.AUTO); + return texstore + ".rgb"; +} + +function inpaint_image_node_button(node_id: i32) { + let canvas: ui_node_canvas_t = ui_nodes_get_canvas(true); + let node: ui_node_t = ui_get_node(canvas.nodes, node_id); + + let models: string[] = [ "Qwen Image Edit" ]; + let model: i32 = ui_combo(ui_handle(__ID__), models, tr("Model")); + + if (iron_exec_async_done == 0) { + ui_button("Cancel..."); + } + else if (ui_button("Run")) { + let inp: ui_node_socket_t = node.inputs[0]; + let from_node: ui_node_t = null; + for (let i: i32 = 0; i < canvas.links.length; ++i) { + let l: ui_node_link_t = canvas.links[i]; + if (l.to_id == inp.node_id) { + from_node = ui_get_node(canvas.nodes, l.from_id); + break; + } + } + + let input: gpu_texture_t = ui_nodes_get_node_preview_image(from_node); + if (input != null) { + + sys_notify_on_next_frame(function(input: gpu_texture_t) { + let dir: string; + if (path_is_protected()) { + dir = iron_internal_save_path() + "models"; + } + else { + dir = iron_internal_files_location() + path_sep + "models"; + } + iron_write_png(dir + path_sep + "input.png", gpu_get_texture_pixels(input), input.width, input.height, 0); + + let prompt: string = "Remove red square"; + let negative: string = ""; + + let argv: string[] = [ + dir + "/sd_qie", + "--diffusion-model", + dir + "/Qwen-Image-Edit-2509-Q4_K_S.gguf", + "--vae", + dir + "/qwen_image_vae.safetensors", + "--qwen2vl", + dir + "/Qwen2.5-VL-7B-Instruct-Q8_0.gguf", + "--qwen2vl_vision", + dir + "/Qwen2.5-VL-7B-Instruct.mmproj-Q8_0.gguf", + "--sampling-method", + "euler", + "--offload-to-cpu", + "--cfg-scale", + "1.0", + "--flow-shift", + "1", + "--steps", + "40", + "-s", + "-1", + "-W", + "512", + "-H", + "512", + "-p", + "'" + prompt + "'", + "-n", + "'" + negative + "'", + "-r", + dir + "/input.png", + "-o", + dir + "/output.png", + null + ]; + + iron_exec_async(argv[0], argv.buffer); + sys_notify_on_update(inpaint_image_node_check_result, dir); + }, input); + } + } +} + +function inpaint_image_node_check_result(dir: string) { + if (iron_exec_async_done == 1) { + let file: string = dir + path_sep + "output.png"; + if (iron_file_exists(file)) { + inpaint_image_node_result = iron_load_texture(file); + } + sys_remove_update(inpaint_image_node_check_result); + } +} + +let inpaint_image_node_def: ui_node_t = { + id : 0, + name : _tr("Inpaint Image"), + type : "NEURAL_INPAINT_IMAGE", + x : 0, + y : 0, + color : 0xff4982a0, + inputs : [ { + id : 0, + node_id : 0, + name : _tr("Color"), + type : "RGBA", + color : 0xffc7c729, + default_value : f32_array_create_xyzw(0.0, 0.0, 0.0, 1.0), + min : 0.0, + max : 1.0, + precision : 100, + display : 0 + } ], + outputs : [ { + id : 0, + node_id : 0, + name : _tr("Color"), + type : "RGBA", + color : 0xffc7c729, + default_value : f32_array_create_xyzw(0.0, 0.0, 0.0, 1.0), + min : 0.0, + max : 1.0, + precision : 100, + display : 0 + } ], + buttons : [ { + name : "inpaint_image_node_button", + type : "CUSTOM", + output : -1, + default_value : f32_array_create_x(0), + data : null, + min : 0.0, + max : 1.0, + precision : 100, + height : 2 + } ], + width : 0, + flags : 0 +}; diff --git a/paint/sources/neural_nodes/text_to_image_node.ts b/paint/sources/neural_nodes/text_to_image_node.ts index 6ff9fac4..70f9faaa 100644 --- a/paint/sources/neural_nodes/text_to_image_node.ts +++ b/paint/sources/neural_nodes/text_to_image_node.ts @@ -50,16 +50,37 @@ function text_to_image_node_run_qwen(dir: string, prompt: string): string[] { function text_to_image_node_run_wan(dir: string, prompt: string): string[] { let argv: string[] = [ - dir + "/sd", "-M", "vid_gen", "--diffusion-model", dir + "/Wan2.2-T2V-A14B-LowNoise-Q8_0.gguf", "--high-noise-diffusion-model", - dir + "/Wan2.2-T2V-A14B-HighNoise-Q8_0.gguf", "--vae", dir + "/wan_2.1_vae.safetensors", "--t5xxl", dir + "/umt5-xxl-encoder-Q8_0.gguf", - "--sampling-method", "euler", "--steps", "20", - "--high-noise-sampling-method", "euler", "--high-noise-steps", "10", "-W", "512", "-H", "512", + dir + "/sd", + "-M", + "vid_gen", + "--diffusion-model", + dir + "/Wan2.2-T2V-A14B-LowNoise-Q8_0.gguf", + "--high-noise-diffusion-model", + dir + "/Wan2.2-T2V-A14B-HighNoise-Q8_0.gguf", + "--vae", + dir + "/wan_2.1_vae.safetensors", + "--t5xxl", + dir + "/umt5-xxl-encoder-Q8_0.gguf", + "--sampling-method", + "euler", + "--steps", + "20", + "--high-noise-sampling-method", + "euler", + "--high-noise-steps", + "10", + "-W", + "512", + "-H", + "512", "--offload-to-cpu", - // "--flow-shift", "3.0", - // "--cfg-scale", "3.5", - // "--high-noise-cfg-scale", "3.5", - // "--diffusion-fa", - "-s", "-1", "-o", dir + "/output.png", "-p", "'" + prompt + "'", null + "-s", + "-1", + "-o", + dir + "/output.png", + "-p", + "'" + prompt + "'", + null ]; return argv; } diff --git a/paint/sources/neural_nodes/tile_image_node.ts b/paint/sources/neural_nodes/tile_image_node.ts new file mode 100644 index 00000000..357f5472 --- /dev/null +++ b/paint/sources/neural_nodes/tile_image_node.ts @@ -0,0 +1,163 @@ + +let tile_image_node_result: gpu_texture_t = null; + +function tile_image_node_init() { + array_push(nodes_material_neural, tile_image_node_def); + map_set(parser_material_node_vectors, "NEURAL_TILE_IMAGE", tile_image_node_vector); + map_set(ui_nodes_custom_buttons, "tile_image_node_button", tile_image_node_button); +} + +function tile_image_node_vector(node: ui_node_t, socket: ui_node_socket_t): string { + if (tile_image_node_result == null) { + return "float3(0.0, 0.0, 0.0)"; + } + let tex_name: string = parser_material_node_name(node); + map_set(data_cached_images, tex_name, tile_image_node_result); + let tex: bind_tex_t = parser_material_make_bind_tex(tex_name, tex_name); + let texstore: string = parser_material_texture_store(node, tex, tex_name, color_space_t.AUTO); + return texstore + ".rgb"; +} + +function tile_image_node_button(node_id: i32) { + let canvas: ui_node_canvas_t = ui_nodes_get_canvas(true); + let node: ui_node_t = ui_get_node(canvas.nodes, node_id); + + let models: string[] = [ "Qwen Image Edit" ]; + let model: i32 = ui_combo(ui_handle(__ID__), models, tr("Model")); + + if (iron_exec_async_done == 0) { + ui_button("Cancel..."); + } + else if (ui_button("Run")) { + let inp: ui_node_socket_t = node.inputs[0]; + let from_node: ui_node_t = null; + for (let i: i32 = 0; i < canvas.links.length; ++i) { + let l: ui_node_link_t = canvas.links[i]; + if (l.to_id == inp.node_id) { + from_node = ui_get_node(canvas.nodes, l.from_id); + break; + } + } + + let input: gpu_texture_t = ui_nodes_get_node_preview_image(from_node); + if (input != null) { + + sys_notify_on_next_frame(function(input: gpu_texture_t) { + let tile: gpu_texture_t = gpu_create_render_target(512, 512); + draw_begin(tile); + draw_scaled_image(input, -256, -256, 512, 512); + draw_scaled_image(input, 256, -256, 512, 512); + draw_scaled_image(input, -256, 256, 512, 512); + draw_scaled_image(input, 256, 256, 512, 512); + draw_end(); + + let dir: string; + if (path_is_protected()) { + dir = iron_internal_save_path() + "models"; + } + else { + dir = iron_internal_files_location() + path_sep + "models"; + } + iron_write_png(dir + path_sep + "input.png", gpu_get_texture_pixels(tile), tile.width, tile.height, 0); + + let prompt: string = "Blend seams"; + let negative: string = ""; + + let argv: string[] = [ + dir + "/sd_qie", + "--diffusion-model", + dir + "/Qwen-Image-Edit-2509-Q8_0.gguf", + "--vae", + dir + "/qwen_image_vae.safetensors", + "--qwen2vl", + dir + "/Qwen2.5-VL-7B-Instruct-Q8_0.gguf", + "--qwen2vl_vision", + dir + "/Qwen2.5-VL-7B-Instruct.mmproj-Q8_0.gguf", + "--sampling-method", + "euler", + "--offload-to-cpu", + "--cfg-scale", + "2.5", + "--flow-shift", + "3", + "--steps", + "20", + "-s", + "-1", + "-W", + "512", + "-H", + "512", + "-p", + "'" + prompt + "'", + "-n", + "'" + negative + "'", + "-r", + dir + "/input.png", + "-o", + dir + "/output.png", + null + ]; + + iron_exec_async(argv[0], argv.buffer); + sys_notify_on_update(tile_image_node_check_result, dir); + }, input); + } + } +} + +function tile_image_node_check_result(dir: string) { + if (iron_exec_async_done == 1) { + let file: string = dir + path_sep + "output.png"; + if (iron_file_exists(file)) { + tile_image_node_result = iron_load_texture(file); + } + sys_remove_update(tile_image_node_check_result); + } +} + +let tile_image_node_def: ui_node_t = { + id : 0, + name : _tr("Tile Image"), + type : "NEURAL_TILE_IMAGE", + x : 0, + y : 0, + color : 0xff4982a0, + inputs : [ { + id : 0, + node_id : 0, + name : _tr("Color"), + type : "RGBA", + color : 0xffc7c729, + default_value : f32_array_create_xyzw(0.0, 0.0, 0.0, 1.0), + min : 0.0, + max : 1.0, + precision : 100, + display : 0 + } ], + outputs : [ { + id : 0, + node_id : 0, + name : _tr("Color"), + type : "RGBA", + color : 0xffc7c729, + default_value : f32_array_create_xyzw(0.0, 0.0, 0.0, 1.0), + min : 0.0, + max : 1.0, + precision : 100, + display : 0 + } ], + buttons : [ { + name : "tile_image_node_button", + type : "CUSTOM", + output : -1, + default_value : f32_array_create_x(0), + data : null, + min : 0.0, + max : 1.0, + precision : 100, + height : 2 + } ], + width : 0, + flags : 0 +};