Search

BlenderControlNet

from bpy.app.handlers import persistent import bpy import os import shutil import time import tempfile import base64 import requests IMAGE_FOLDER = "C:\\Users\\antea\\OneDrive\\Desktop\\Test" DEFAULT_PARAMS = { "prompt": "1 girl dancing on the beach, ocean background, sunset glow, best quality", "negative_prompt": "worst quality, low quality, lowres, normal quality", "width": 512, "height": 512, "seed": -1, "subseed": -1, "subseed_strength": 0, "batch_size": 1, "n_iter": 1, "steps": 20, "cfg_scale": 7, "restore_faces": False, "sampler_index": "DPM++ 2M Karras", "controlnet_module": "depth", "controlnet_model": "control_sd15_depth [fef5e48e]", "controlnet_resize_mode": "Scale to Fit (Inner Fit)", "controlnet_guidance": 1.0, "controlnet_weight": 1, "controlnet_guessmode": True, "controlnet_mask": [], "enable_hr": False, "denoising_strength": 0.5, "hr_scale": 2, "hr_upscale": "Latent (bicubic antialiased)", } @persistent def render_complete_handler(scene): is_img_ready = bpy.data.images['Render Result'].has_data if is_img_ready: send_to_api(scene) else: print("Rendered image is not ready.") def send_to_api(scene): # prepare output filenames timestamp = int(time.time()) before_output_filename_prefix = f"{timestamp}-1-before" after_output_filename_prefix = f"{timestamp}-2-after" # save rendered image to temp and then read it back in temp_input_file = save_render_to_file(scene, before_output_filename_prefix) if not temp_input_file: return False img_file = open(temp_input_file, 'rb') # save the before image save_before_image(scene, before_output_filename_prefix) # prepare data for API params = DEFAULT_PARAMS params["controlnet_input_image"] = [ base64.b64encode(img_file.read()).decode()] img_file.close() #print( params["controlnet_input_image"]) # send to API output_file = actually_send_to_api(params, after_output_filename_prefix) # if we got a successful image created, load it into the scene if output_file: new_output_file = None # save the after image new_output_file = save_after_image( scene, after_output_filename_prefix, output_file) # if we saved a new output image, use it if new_output_file: output_file = new_output_file # load the image into image editor try: img = bpy.data.images.load(output_file, check_existing=False) for window in bpy.data.window_managers['WinMan'].windows: for area in window.screen.areas: if area.type == 'IMAGE_EDITOR': area.spaces.active.image = img except: return print("Couldn't load the image.") return True else: return False def actually_send_to_api(params, filename_prefix): # create headers headers = { "User-Agent": "Blender/" + bpy.app.version_string, "Accept": "*/*", "Accept-Encoding": "gzip, deflate, br", } # prepare server url server_url = "http://localhost:7860" + "/controlnet/txt2img" # send API request try: response = requests.post( server_url, json=params, headers=headers, timeout=1000) except requests.exceptions.ConnectionError: return print(f"The Automatic1111 server couldn't be found.") except requests.exceptions.MissingSchema: return print(f"The url for your Automatic1111 server is invalid.") except requests.exceptions.ReadTimeout: return print("The Automatic1111 server timed out.") # handle the response if response.status_code == 200: return handle_api_success(response, filename_prefix) else: return handle_api_error(response) def handle_api_success(response, filename_prefix): try: response_obj = response.json() base64_img = response_obj["images"][0] except: print("Automatic1111 response content: ") return print("Received an unexpected response from the Automatic1111 server.") # create a temp file try: output_file = create_temp_file(filename_prefix + "-") except: return print("Couldn't create a temp file to save image.") # decode base64 image try: img_binary = base64.b64decode( base64_img.replace("data:image/png;base64,", "")) except: return print("Couldn't decode base64 image.") # save the image to the temp file try: with open(output_file, 'wb') as file: file.write(img_binary) except: return print("Couldn't write to temp file.") # return the temp file return output_file def handle_api_error(response): if response.status_code == 404: import json try: response_obj = response.json() if response_obj.get('detail') and response_obj['detail'] == "Not Found": return print("It looks like the Automatic1111 server is running, but it's not in API mode.") elif response_obj.get('detail') and response_obj['detail'] == "Sampler not found": return print("The sampler you selected is not available.") else: return print(f"An error occurred in the Automatic1111 server. Full server response: {json.dumps(response_obj)}") except: return print("It looks like the Automatic1111 server is running, but it's not in API mode.") else: return print("An error occurred in the Automatic1111 server.") def create_temp_file(prefix, suffix=".png"): return tempfile.NamedTemporaryFile(prefix=prefix, suffix=suffix).name def save_render_to_file(scene, filename_prefix): try: temp_file = create_temp_file(filename_prefix + "-", suffix=".png") except: return print("Couldn't create temp file for image") try: orig_render_file_format = scene.render.image_settings.file_format orig_render_color_mode = scene.render.image_settings.color_mode orig_render_color_depth = scene.render.image_settings.color_depth scene.render.image_settings.file_format = 'PNG' scene.render.image_settings.color_mode = 'RGBA' scene.render.image_settings.color_depth = '8' bpy.data.images['Render Result'].save_render(temp_file) scene.render.image_settings.file_format = orig_render_file_format scene.render.image_settings.color_mode = orig_render_color_mode scene.render.image_settings.color_depth = orig_render_color_depth except: return print("Couldn't save rendered image") return temp_file def save_before_image(scene, filename_prefix): ext = ".png" filename = f"{filename_prefix}{ext}" full_path_and_filename = os.path.join(os.path.abspath( bpy.path.abspath(IMAGE_FOLDER)), filename) try: bpy.data.images['Render Result'].save_render( bpy.path.abspath(full_path_and_filename)) except: return print(f"Couldn't save 'before' image to {bpy.path.abspath(full_path_and_filename)}") def save_after_image(scene, filename_prefix, img_file): filename = f"{filename_prefix}.png" full_path_and_filename = os.path.join(os.path.abspath( bpy.path.abspath(IMAGE_FOLDER)), filename) try: copy_file(img_file, full_path_and_filename) return full_path_and_filename except: return print(f"Couldn't save 'after' image to {bpy.path.abspath(full_path_and_filename)}") def get_output_width(scene): return round(scene.render.resolution_x * scene.render.resolution_percentage / 100) def get_output_height(scene): return round(scene.render.resolution_y * scene.render.resolution_percentage / 100) def copy_file(src, dest): shutil.copy2(src, dest) bpy.app.handlers.render_complete.clear() bpy.app.handlers.render_complete.append(render_complete_handler)
Python
복사
API 사용 가이드
API 확인
CommandLine Arg 확인
BlenderControlNet 리포지터리
Blender-ControlNet
coolzilj
이슈
DEFAULT_PARAMS = { "enable_hr": False, "denoising_strength": 0, "firstphase_width": 0, "firstphase_height": 0, "hr_scale": 2, "hr_upscaler": "string", "hr_second_pass_steps": 0, "hr_resize_x": 0, "hr_resize_y": 0, "prompt": "", "styles": [ "string" ], "seed": -1, "subseed": -1, "subseed_strength": 0, "seed_resize_from_h": -1, "seed_resize_from_w": -1, "sampler_name": "string", "batch_size": 1, "n_iter": 1, "steps": 50, "cfg_scale": 7, "width": 512, "height": 512, "restore_faces": False, "tiling": False, "negative_prompt": "string", "eta": 0, "s_churn": 0, "s_tmax": 0, "s_tmin": 0, "s_noise": 1, "override_settings": {}, "override_settings_restore_afterwards": True, "script_args": [], "sampler_index": "Euler", "script_name": "string", "controlnet_units": [ { "input_image": "", "mask": "", "module": "depth", "model": "control_sd15_depth [fef5e48e]", "weight": 1, "resize_mode": "Scale to Fit (Inner Fit)", "lowvram": False, "processor_res": 64, "threshold_a": 64, "threshold_b": 64, "guidance": 1, "guidance_start": 0, "guidance_end": 1, "guessmode": True } ] } type(DEFAULT_PARAMS) DEFAULT_PARAMS['controlnet_units'] type(DEFAULT_PARAMS['controlnet_units'])
Python
복사
Allow other script to control this extension