私的AI研究会 > AI_Program9
これまで検証してきた結果をもとに、Python で生成 AI プログラムを書く
GUI 画像生成のプログラムを書く2 <統合版> |
(base) PS > conda activate sd_test (sd_test) PS > cd workspace_3/sd_test
名称 | 概要 | 機能 | |
① | sd_100.py | Step40~52,81,91 の機能を起動時に選択して GUI で操作する | txt2img/img2img/pix2pix/controlnet-pix2pix/inpaint/outpaint/ scribble/openpose/face_recognition/ADetailer/Paint-By-Example |
② | sd_101.py | Step53 の機能(Step40~52,81,91を含む)を GUI で操作する | txt2img/img2img/controlnet-pix2pix/inpaint/outpaint/scribble/ openpose/ADetailer/canny/lineart/depth/segment/shuffle/ normal_map/lineart_anime/mlsd/tile |
コマンドオプション | 引数 | 初期値の例 | 意味 | GUIプログラム | |
100 | 101 | ||||
--pros_sel | str | 処理するプログラム名(必須指定) | |||
--result_image | str | './sd_results3/sd.png' | 保存するファイルパスとヘッダ名の指定 | ||
--cpu | bool | False | cpu mode. | ||
--log | int | 3 | Log level(-1/0/1/2/3/4/5) | ||
--model_dir | str | '/StabilityMatrix/Data/Models/StableDiffusion' | モデルフォルダのパス | ||
--model_path | str | 'SD1.5/beautifulRealistic_brav5.safetensors' | モデルファイル | 〇 | 〇 |
--ctrl_model_dir | str | '/StabilityMatrix/Data/Models/ControlNet' | コントロールネット・モデルフォルダのパス | ||
--ctrl_model_path | str | 'control_v11p_sd15_inpaint_fp16.safetensors' | コントロールネット・モデルファイル | 〇 | 〇 |
--image_path | str | 'images/sd_038_test.png' | 入力画像のファイル・パス名 | 〇 | 〇 |
--ctrl_image_path | str | 'images/sd_038_test_mask.png' | コントロールネット・入力画像のファイル・パス名 | 〇 | |
--max_size | int | 0 | 入力画像リサイズの最大値(0=入力画像サイズ) | 〇 | 〇 |
--prompt | str | '微笑んでいる女性' | 画像生成のためのプロンプト(日本語/英語) | 〇 | 〇 |
--seed | int | 12345678 | シード値(-1の時はランダムに生成) | 〇 | 〇 |
--width | int | 512 | 生成画像サイズの幅 | 〇 | 〇 |
--height | int | 512 | 生成画像サイズの高さ | 〇 | 〇 |
--step | int | 20 | 生成ステップ数 | 〇 | 〇 |
--scale | float | 7.0 | ガイダンススケール値 | 〇 | 〇 |
--image_scale | float | 1.5 | イメージ・ガイダンススケール値 | ||
--cc_scale | float | 1.0 | controlnet conditioning scale | ||
--strength | float | 0.6 | 変化の強さを表すパラメータ | 〇 | 〇 |
--neg_prompt | str | '最悪の品質、おかしい人体構造' | 画像生成のためのネガティブ・プロンプト(日本語/英語) | 〇 | 〇 |
--ip_image_path | str | '' | IP-Adapter 入力画像('' の時は IP-Adapter を使用しない) | 〇 | |
--ip_scale | float | 0.5 | ip_adapter_scale パラメータ | 〇 | |
--scheduler | str | 'non' | Scheduler 'non/euler/uni/DPM' | 〇 | |
--mode | bool | True | パラメータ設定モード(advanced/norma) | ||
str | 'canny' | aplication mode 'canny/inpaint/outpaint/scribble/openpose/pix2pix' | 〇 | ||
--ext | Extensions (sd_048/sd_051) '' or 'girl' or 'boy' | ||||
str | '' | Extensions ADetailer '' or 'girl' or 'boy' | 〇 |
pros_sel | sd_081 | sd_091 | sd_040 | sd_042 |
機能 | テキストから画像生成 | 画像をテキストで変更 | 画像から画像を変換生成 | 画像から画像を変換生成 |
text2image | image2image | instruct-pix2pix | controlnet instruct-pix2pix | |
advanced 画面 | ![]() | ![]() | ![]() | ![]() |
normal 画面 | ![]() | ![]() | ![]() | ![]() |
pros_sel | sd_044 | sd_046 | sd_047 | sd_048 |
機能 | 画像の一部を変更 | 画像の外側を書き加える | 手描きの線画から画像を生成 | 画像から同じ姿勢の画像を生成 |
controlnet inpaint | outpaint | controlnet scribble | controlnet openpose | |
advanced 画面 | ![]() | ![]() | ![]() | ![]() |
normal 画面 | ![]() | ![]() | ![]() | ![]() |
pros_sel | sd_050 | sd_051 | sd_052 | |
機能 | 顔の崩れを修正する1 | 顔の崩れを修正する2 | 画像の一部を変換する | |
face_recognition inpaint | ADetailer | Paint-By-Example | ||
advanced 画面 | ![]() | ![]() | ![]() | |
normal 画面 | ![]() | ![]() | ![]() |
python sd_100.py --pros_sel sd_[081 / 091 / 040 / 042 / 044 / 046 / 047 / 048 / 050 / 051 / 052]・処理プロセスにより SD1.5 と SDXL のモデルに対応する(SD1.5 モデルは「SD1.5/」ディレクトリ名のフォルダに配置されていること)
python sd_100.py --pros_sel sd_[081 / 091 / 040 / 042 / 044 / 046 / 047 / 048 / 050 / 051 / 052]
(sd_test) PS > python sd_100.py --pros_sel sd_048 Stable Diffusion with diffusers(100) Ver 0.05 <sd_048>: Starting application... --pros_sel : sd_048 --result_image : ./sd_results3/sd.png --cpu : False --log : 3 --model_dir : /StabilityMatrix/Data/Models/StableDiffusion --model_path : SD1.5/beautifulRealistic_brav5.safetensors --ctrl_model_dir : /StabilityMatrix/Data/Models/ControlNet --ctrl_model_path : control_v11p_sd15_openpose_fp16.safetensors --image_path : images/sd_048_test1.png --max_size : 0 --prompt : ダンスを踊る女性 --seed : -1 --step : 20 --neg_prompt : 最悪の品質、おかしい人体構造 --mode : True ** Start 1 ** prompt: Dancing Woman neg_prompt: Worst quality, funny body structure seed: 2292601352 Fetching 11 files: 100%|███████████████████████████████| 11/11 [00:00<?, ?it/s] Loading pipeline components...: 100%|████████████| 6/6 [00:00<00:00, 32.07it/s] 100%|██████████████████████████████████████████| 20/20 [00:02<00:00, 6.93it/s] result_file: ./sd_results3/sd_00012_2292601352.png ** Complete ** 00:00:08 ** Start 1 ** prompt: Dancing Woman neg_prompt: Worst quality, funny body structure seed: 1070611060 Fetching 11 files: 100%|███████████████████████████████| 11/11 [00:00<?, ?it/s] Loading pipeline components...: 100%|████████████| 6/6 [00:00<00:00, 31.88it/s] 100%|██████████████████████████████████████████| 20/20 [00:02<00:00, 7.23it/s] result_file: ./sd_results3/sd_00013_1070611060.png ** Complete ** 00:00:05 Finished.
python sd_100.py --pros_sel sd_081 --prompt '高精細、コーヒーショップで窓際に座っている女性' --ip_image_path 'images/sd_080_test.png' --seed 12345678
python sd_100.py --pros_sel sd_091 --ip_image_path 'images/sd_080_test.png'
python sd_100.py --pros_sel sd_040 --prompt '春の場面にする' --ip_image_path 'images/flower.jpg' --ip_scale 0.2
python sd_100.py --pros_sel sd_042 --ip_image_path 'images/beach.jpg'
python sd_100.py --pros_sel sd_044 --ip_image_path 'images/sd_046_test.png'
python sd_100.py --pros_sel sd_046 --ip_image_path 'images/sd_040_test.png'
python sd_100.py --pros_sel sd_047 --prompt 'ビーチに置かれたオレンジ色のコーヒーカップ' --ip_image_path 'images/beach.jpg'
python sd_100.py --pros_sel sd_048 --seed 4260871861 --step 50 --ip_image_path 'images/fashion.jpg'
python sd_100.py --pros_sel sd_048 --seed 4260871861 --step 50 --ip_image_path 'images/fashion.jpg' --ext 'girl'
# -*- coding: utf-8 -*- ##-------------------------------------------------- ## Stable Diffusion with diffusers(100) Ver 0.10 ## img2img GUI interface ## 2025.08.14 Masahiro Izutsu ##-------------------------------------------------- ## sd_100.py ## Ver 0.00 2025.07.17 Trial version ## Ver 0.05 2025.07.20 sd_100.py 統合版対応 ## Ver 0.06 2025.07.27 sd_081 IP-Adapter 対応 ## Ver 0.07 2025.08.01 sd_050 対応 ## Ver 0.08 2025.08.02 sd_051 ADetailer 対応 ## Ver 0.09 2025.08.14 sd_047 scribble 修正 ## Ver 0.10 2025.08.19 sd_101 関連 修正 # タイトル title = 'Stable Diffusion with diffusers(100) Ver 0.10' import warnings warnings.simplefilter('ignore') # インポート&初期設定 import os import numpy as np import time import csv import cv2 import PySimpleGUI as sg import my_logging import my_thumbnail import my_dialog import my_imagetool import sd_tools as sdt import sd_results as sdr # 定数定義 DEF_THEME = 'BlueMono' CANVAS_SIZE = 512 DEF_SEED_VAL = '12345678' CSV_LOG_FILE = 'result_100.csv' KEY_CANCEL = '-Cancel-' KEY_IMAGE = '-Image-' KEY_INPUT_IMAGE = '-Input_Image-' KEY_IMAGE_SEL = '-Image_sel-' KEY_CTRL_IMAGE_SEL = '-Ctrl_Image_sel-' KEY_MODELSEL = '-Model_sel-' KEY_CTRL_MODELSEL = '-Ctrl_Model_sel-' KEY_SEED_INPUT = '-Seed_input-' KEY_ADVANCED = '-Advanced-' KEY_EXIT = '-Exit-' KEY_GENERATE = '-Generate-' KEY_INPUT_TXT = '-Input_text-' KEY_CTRL_MODEL_TXT ='-ctrl_model_text-' KEY_CTRL_IMGPATH_TXT = '--ctrl_image_path_text--' KEY_MAXSIZE_TXT = '-MaxSize_text-' KEY_WIDTH_TXT = '--Image_width_text--' KEY_HEIGHT_TXT = '--Image_height_text--' KEY_STEP_TXT = '-Step_text-' KEY_SCALE_TXT = '-Scale_text-' KEY_STRENGTH_TXT = '-strength_text-' KEY_SEED_CLR = '--seed_clear--' KEY_SEED_SET = '--seed_set--' KEY_PROMPT_JP_TXT = '-PromptJP_text-' KEY_PROMPT_TXT = '-Prompt_text-' KEY_NEG_PROMPT_JP_TXT = '-Negative_PromptJP_text-' KEY_NEG_PROMPT_TXT = '-Negative_Prompt_text-' SKEY_RESULT_PATH = '-result_path-' SKEY_RESULT_FILE = '-result_file-' SKEY_DEF_OUTPATH = '-default_image-' # csv save KEY_PROS_SEL = '--Proscess--' KEY_OUTPATH = '-Output-' KEY_SEED = '-Seed-' KEY_PROMPT_JP = '-PromptJP-' KEY_PROMPT = '-Prompt-' KEY_INPUTPATH = '-Input-' KEY_MAXSIZE = '-MaxSize-' KEY_STEP = '-Step-' KEY_SCALE = '-Scale-' KEY_STRENGTH = '-strength-' KEY_MODEL_DIR = '-model_dir-' KEY_MODEL = '-Model-' KEY_CTRL_IMGPATH = '--ctrl_image_path--' KEY_CTRL_DIR = '-ctrl_model_dir-' KEY_CTRL_MODEL = '-ctrl_model-' KEY_WIDTH = '--Image_width--' KEY_HEIGHT = '--Image_height--' KEY_IMG_SCALE = '--Image_scale--' KEY_CC_SCALE = '--Control_scale--' KEY_DEVICE = '-Device-' KEY_TIME = '-Time-' KEY_LOOP = '-Loop-' KEY_NEG_PROMPT_JP = '-Negative_PromptJP-' KEY_NEG_PROMPT = '-Negative_Prompt-' KEY_IP_IMGPATH = '-Ip_image_path-' KEY_IP_SCALE = '-Ip_scale-' KEY_EXTENTION = '-Extention-' # ---------- # コマンドライン定義 def_result_image = './sd_results3/sd.png' def_cpu = 'store_true' def_log = '3' def_model_dir = '' def_model_path = '' def_ctrl_model_dir = '' def_ctrl_model_path = '' def_image_path = '' def_control_image_path = '' def_max_size = '' def_prompt = '' def_seed = '' def_width = '' def_height = '' def_step = '' def_scale = '' def_image_scale = '' def_cc_scale = '' def_strength = 0.6 def_neg_prompt = '' def_ip_image_path = '' def_ip_scale = '' def_mode = 'store_false' def_ext = '' opt_list = [ ['pros_sel','','sd_100'], # 0 ['result_image', def_result_image, 'path to output image file'], # 1 ['cpu', def_cpu, 'cpu mode'], # 2 ['log', def_log, 'Log level(-1/0/1/2/3/4/5) Default value is \'3\''], # 3 ['model_dir', def_model_dir, 'Model directory'], # 4 ['model_path', def_model_path, 'Model Path'], # 5 ['ctrl_model_dir', def_ctrl_model_dir, 'ControlNet Model directory'], # 6 ['ctrl_model_path', def_ctrl_model_path, 'ControlNet Model Path'], # 7 ['image_path', def_image_path, 'Sourcs image file path'], # 8 ['ctrl_image_path', '', 'Control image file path'], # 9 ['max_size', def_max_size, 'image max size (0=source)'], # 10 ['prompt', def_prompt, 'Prompt text'], # 11 ['seed', def_seed, 'Seed parameter (-1 = rundom)'], # 12 ['width', def_width, 'image size width'], # 13 ['height', def_height, 'image size height'], # 14 ['step', def_step, 'infer step'], # 15 ['scale', def_scale, 'gaidanse scale'], # 16 ['image_scale', def_image_scale, 'image gaidanse scale'], # 17 ['cc_scale', def_cc_scale, 'controlnet conditioning scale'], # 18 ['strength', def_strength, 'strength value'], # 19 ['neg_prompt', def_neg_prompt, 'Negative Prompt text'], # 20 ['ip_image_path', def_ip_image_path, 'IP-Adapter image filr path'], # 21 ['ip_scale', def_ip_scale, 'IP-Adapter scale'], # 22 ['mode', def_mode, 'aplication mode'], ['ext', def_ext, 'Extensions \'\' or \'girl\' or \'boy\''], ] # コマンドオプションを設定する def set_option(opt, sdt_list): for n in range(len(opt_list)): if opt_list[n][1] == '': for n0 in range(len(sd_list)): if opt_list[n][0] == sd_list[n0][0]: opt_list[n][1] = sd_list[n0][1] opt_list[0][2] = sd_list[0][2] if opt.result_image == '': opt.result_image = opt_list[1][1] if opt.log == '': opt.log = opt_list[3][1] if opt.model_dir == '': opt.model_dir = opt_list[4][1] if opt.model_path == '': opt.model_path = opt_list[5][1] if opt.ctrl_model_dir == '': opt.ctrl_model_dir = opt_list[6][1] if opt.ctrl_model_path == '': opt.ctrl_model_path = opt_list[7][1] if opt.image_path == '': opt.image_path = opt_list[8][1] if opt.ctrl_image_path == '': opt.ctrl_image_path = opt_list[9][1] if opt.max_size == '': opt.max_size = opt_list[10][1] if opt.prompt == '': opt.prompt = opt_list[11][1] if opt.seed == '': opt.seed = opt_list[12][1] if opt.width == '': opt.width = opt_list[13][1] if opt.height == '': opt.height = opt_list[14][1] if opt.step == '': opt.step = opt_list[15][1] if opt.scale == '': opt.scale = opt_list[16][1] if opt.image_scale == '': opt.image_scale = opt_list[17][1] if opt.cc_scale == '': opt.cc_scale = opt_list[18][1] if opt.strength == '': opt.strength = opt_list[19][1] if opt.neg_prompt == '': opt.neg_prompt = opt_list[20][1] def set_pros_hedder(pros): opt_list[0][2] = pros sdt.opt_list[0][2] = pros sdr.opt_list[0][2] = pros # ウィジェットのデータの取得(1回目) def get_paramlist(window, values, param): param[KEY_MODEL] = window[KEY_MODEL].DisplayText param[KEY_MAXSIZE] = int(values[KEY_MAXSIZE]) param[KEY_SEED] = sdt.get_random_seed_value(values[KEY_SEED_INPUT]) param[KEY_WIDTH] = int(values[KEY_WIDTH]) param[KEY_HEIGHT] = int(values[KEY_HEIGHT]) param[KEY_STEP] = int(values[KEY_STEP]) param[KEY_SCALE] = float(values[KEY_SCALE]) param[KEY_STRENGTH] = float(values[KEY_STRENGTH]) param[KEY_PROMPT_JP] = values[KEY_PROMPT_JP] param[KEY_PROMPT] = sdt.trans_jp2en(param[KEY_PROMPT_JP]) param[KEY_NEG_PROMPT_JP] = values[KEY_NEG_PROMPT_JP] param[KEY_NEG_PROMPT] = sdt.trans_jp2en(param[KEY_NEG_PROMPT_JP]) param[KEY_CTRL_MODEL] = window[KEY_CTRL_MODEL].DisplayText param[KEY_LOOP] = int(values[KEY_LOOP]) if param[KEY_LOOP] < 1: param[KEY_LOOP] = 1 if param[KEY_LOOP] > 50: param[KEY_LOOP] = 50 # ウィジェットのデータの取得(2回目以降) def get_paramlist2(window, values, param): if int(values[KEY_SEED_INPUT]) > 0: param[KEY_SEED] = param[KEY_SEED] + 1 else: param[KEY_SEED] = sdt.get_random_seed_value(values[KEY_SEED_INPUT]) # マスク画像ファイル名を得る(sd_044/sd_052) def get_mask_file(image_path): work_path = sdt.get_work_path(logger) _, mask_path = sdt.get_source_mask_path(image_path, logger) if not os.path.isfile(mask_path): import sd_mask sd_mask.mask_paint(image_path, work_path, False, logger) return mask_path # 画像生成 def generate_image(param, logger = None): pros_sel = sdt._get_process_name(opt_list) # pros_sel 処理方法 image_path_save = param[KEY_INPUTPATH] # image_path push # 前処理 if pros_sel == 'sd_048': image_path = param[KEY_INPUTPATH] if not sdt.is_pose(image_path): pose_path = sd.pre_generation(image_path, logger) param[KEY_INPUTPATH] = pose_path param[KEY_CTRL_IMGPATH] = pose_path # ポーズ画像 elif pros_sel == 'sd_047': from PIL import Image image_path = param[KEY_INPUTPATH] if sdt.get_image_channel(image_path) == 3: img = Image.open(image_path) msk = sdt.scribble_preprocessor(img) mask_path = sdt.get_scribble_path(image_path, logger) sdt.image_save2(msk, mask_path, dispname = mask_path, wait_s = 1) # 1秒 wait param[KEY_CTRL_IMGPATH] = mask_path # 生成マスク画像 elif pros_sel == 'sd_046': image_path = param[KEY_INPUTPATH] src_path, mask_path = sd.pre_generation(image_path, logger) param[KEY_INPUTPATH] = src_path param[KEY_CTRL_IMGPATH] = mask_path elif pros_sel == 'sd_044': image_path = param[KEY_INPUTPATH] mask_path = get_mask_file(image_path) param[KEY_CTRL_IMGPATH] = mask_path elif pros_sel == 'sd_081': if param[KEY_IP_IMGPATH] == '': image_path = param[KEY_INPUTPATH] param[KEY_IP_IMGPATH] = image_path # 入力画像を IP-Adapter param[KEY_INPUTPATH] = '' model = param[KEY_MODEL] if param[KEY_MODEL_DIR] == '' else param[KEY_MODEL_DIR] + '/' + param[KEY_MODEL] image_path = param[KEY_INPUTPATH] prompt = param[KEY_PROMPT] max_size = param[KEY_MAXSIZE] seed = param[KEY_SEED] num_inference_steps = param[KEY_STEP] guidance_scale = param[KEY_SCALE] strength = param[KEY_STRENGTH] device = param[KEY_DEVICE] out_path = param[SKEY_RESULT_PATH] + '/' + sdt.make_filename_by_seq(param[SKEY_RESULT_PATH], param[SKEY_RESULT_FILE], seq_digit=5, ex= f'{seed}-{pros_sel}') param[KEY_OUTPATH] = out_path ctrl_model_dir = param[KEY_CTRL_DIR] ctrl_model_path = param[KEY_CTRL_MODEL] if param[KEY_CTRL_DIR] == '' else param[KEY_CTRL_DIR] + '/' + param[KEY_CTRL_MODEL] width = param[KEY_WIDTH] height = param[KEY_HEIGHT] cc_scale = param[KEY_CC_SCALE] img_scale = param[KEY_IMG_SCALE] neg_prompt = param[KEY_NEG_PROMPT] ip_image_path = param[KEY_IP_IMGPATH] ip_scale = param[KEY_IP_SCALE] if ip_scale == '': ip_scale = 0.5 extention = param[KEY_EXTENTION] logger.debug(f'** pros_sel: {pros_sel}') logger.debug(f'model: {model}') logger.info(f'prompt: {prompt}') logger.info(f'neg_prompt: {neg_prompt}') logger.info(f'image_path: {image_path}') logger.debug(f'max_size: {max_size}') logger.info(f'seed: {seed}') logger.debug(f'num_inference_steps: {num_inference_steps}') logger.debug(f'guidance_scale: {guidance_scale}') logger.debug(f'strength: {strength}') logger.debug(f'device: {device}') logger.debug(f'loop: {param[KEY_LOOP]}') logger.debug(f'out_path: {out_path}') logger.debug(f'ctrl_model_path: {ctrl_model_path}') if ip_image_path == '': logger.debug(f'IP-Adapter image: {ip_image_path}') else: logger.info(f'IP-Adapter image: {ip_image_path}') logger.info(f'IP-Adapter scale: {ip_scale}') src_image = None # 元画像 ip_image = None # IP 画像 if image_path != '': src_image = sdt._get_resize_image(image_path, max_size, logger) if ip_image_path != '': ip_image = sdt._get_resize_image(ip_image_path, max_size, logger) if pros_sel == 'sd_040': image = sd.image_generation(model, src_image, prompt, seed, num_inference_steps, width, height, guidance_scale, img_scale, neg_prompt, ip_image, ip_scale, device) elif pros_sel == 'sd_042': image = sd.image_generation(model, ctrl_model_path, src_image, prompt, seed, num_inference_steps, width, height, guidance_scale, cc_scale, neg_prompt, ip_image, ip_scale, device) elif pros_sel == 'sd_044' or pros_sel == 'sd_046': ctrl_image_path = param[KEY_CTRL_IMGPATH] sdt.image_disp(ctrl_image_path, ctrl_image_path, wait_s = 1) # マスク表示 msk_image = sdt._get_resize_image(ctrl_image_path, max_size, logger) img_ctrl = sdt.make_inpaint_condition(src_image, msk_image) # コントロール画像 image = sd.image_generation(model, ctrl_model_path, src_image, msk_image, img_ctrl, prompt, seed, num_inference_steps, width, height, guidance_scale, cc_scale, strength, neg_prompt, ip_image, ip_scale, device) elif pros_sel == 'sd_047': image = sd.image_generation(model, ctrl_model_path, src_image, prompt, seed, num_inference_steps, neg_prompt, ip_image, ip_scale, device) elif pros_sel == 'sd_048': if extention =="": image = sd.image_generation(model, ctrl_model_path, src_image, prompt, seed, num_inference_steps, neg_prompt, ip_image, ip_scale, device) else: image = sd.image_generation_ad(extention, model, ctrl_model_path, src_image, prompt, seed, num_inference_steps, neg_prompt, ip_image, ip_scale, device) elif pros_sel == 'sd_050': image = sd.image_generation(model, image_path_save, prompt, seed, num_inference_steps, width, height, guidance_scale, strength, neg_prompt = neg_prompt, device = device) elif pros_sel == 'sd_051': image = sd.image_generation_ex(extention, src_image, device, prompt, model, seed, num_inference_steps) elif pros_sel == 'sd_052': image_path = param[KEY_INPUTPATH] ctrl_image_path = param[KEY_CTRL_IMGPATH] mask_path = get_mask_file(image_path) sdt.image_disp(mask_path, mask_path, wait_s = 1) # マスク表示 msk_image = sdt._get_resize_image(mask_path, max_size, logger) ex_image = sdt._get_resize_image(ctrl_image_path, max_size, logger) sdt.image_disp(ctrl_image_path, ctrl_image_path, wait_s = 1) # eximage 表示 image = sd.image_generation(model, src_image, msk_image, ex_image, seed, num_inference_steps, width, height, device) elif pros_sel == 'sd_091': image = sd.image_generation(model, prompt, src_image, seed, num_inference_steps, guidance_scale, strength, neg_prompt, ip_image, ip_scale, device) elif pros_sel == 'sd_081': image = sd.image_generation(model, prompt, seed, num_inference_steps, guidance_scale, width, height, neg_prompt, ip_image, ip_scale, device) if image is None: logger.info(f'{sdt.RED}Unable to generate image !!{sdt.NOCOLOR}') else: image.save(out_path) logger.info(f'result_file: {out_path}') param[KEY_INPUTPATH] = image_path_save # image_path pop sdt.device_empty_cache(device) # メモリー開放 # ** main関数 ** def main(opt, logger = None): # ------------------------------------------ # キャンバスをクリア def clear_canvas(key, msg, color): frame = np.zeros((CANVAS_SIZE, CANVAS_SIZE, 3), np.uint8) frame[:,:,] = 0xf0 return msg_out_canvas(key, frame, msg, color) def msg_out_canvas(key, frame, msg, color): x0,y0,x1,y1 = cv2_putText(img=frame, text=msg, org=(CANVAS_SIZE//2, CANVAS_SIZE//2), fontFace=font_face, fontScale=16, color=color, mode=2,areaf=True) cv2.rectangle(frame,(x0-8, y0), (x1+8, y1), (0xf0,0xf0,0xf0), -1) img = cv2_putText(img=frame, text=msg, org=(CANVAS_SIZE//2, CANVAS_SIZE//2), fontFace=font_face, fontScale=16, color=color, mode=2) img = cv2.imencode('.png', frame)[1].tobytes() window[key].update(img) return frame # キャンバスへ画像の表示 def update_canvas(key, imgfile): if os.path.isfile(imgfile): frame = cv2.imread(imgfile) frame = my_imagetool.frame_square(frame, (240, 240, 240)) frame = cv2.resize(frame, dsize = (CANVAS_SIZE, CANVAS_SIZE)) img = cv2.imencode('.png', frame)[1].tobytes() window[key].update(img) else: msg = 'Input Image' if key == KEY_INPUT_IMAGE else 'Generate Image' frame = clear_canvas(key, msg, (0,0,0)) return frame # ウイジェットの更新 def update_widget(): frame = update_canvas(KEY_IMAGE, param[KEY_OUTPATH]) frame_input = update_canvas(KEY_INPUT_IMAGE, param[KEY_INPUTPATH]) window[KEY_OUTPATH].update(param[KEY_OUTPATH]) window[KEY_SEED].update(param[KEY_SEED_INPUT]) # シード値の表示 window[KEY_PROMPT_JP].update(param[KEY_PROMPT_JP]) window[KEY_PROMPT].update(param[KEY_PROMPT]) window[KEY_INPUTPATH].update(param[KEY_INPUTPATH]) window[KEY_MODEL].update(param[KEY_MODEL]) window[KEY_MAXSIZE].update(param[KEY_MAXSIZE]) window[KEY_STEP].update(param[KEY_STEP]) window[KEY_SCALE].update(param[KEY_SCALE]) window[KEY_STRENGTH].update(param[KEY_STRENGTH]) window[KEY_NEG_PROMPT_JP].update(param[KEY_NEG_PROMPT_JP]) window[KEY_NEG_PROMPT].update(param[KEY_NEG_PROMPT]) window[KEY_WIDTH].update(param[KEY_WIDTH]) window[KEY_HEIGHT].update(param[KEY_HEIGHT]) window[KEY_CTRL_MODEL].update(param[KEY_CTRL_MODEL]) window[KEY_CTRL_IMGPATH].update(param[KEY_CTRL_IMGPATH]) return frame # ウイジェットの禁止・許可 def set_enb_dis(disabled): window[KEY_IMAGE_SEL].update(disabled = disabled) window[KEY_MODELSEL].update(disabled = disabled) window[KEY_GENERATE].update(disabled = disabled) window[KEY_LOOP].update(disabled = disabled) window[KEY_EXIT].update(disabled = disabled) # モード変更 def change_mode(advanced): pros_sel = sdt._get_process_name(opt_list) # pros_sel 処理方法 enb = pros_sel != 'sd_081' and pros_sel != 'sd_051' and pros_sel != 'sd_052' window[KEY_CTRL_MODEL_TXT].update(visible = advanced and enb) window[KEY_CTRL_MODEL].update(visible = advanced and enb) window[KEY_CTRL_MODELSEL].update(visible = advanced and enb) enb = pros_sel != 'sd_081' window[KEY_MAXSIZE_TXT].update(visible = advanced and enb) window[KEY_MAXSIZE].update(visible = advanced and enb) window[KEY_STEP_TXT].update(visible = advanced and enb) window[KEY_STEP].update(visible = advanced and enb) enb = pros_sel != 'sd_081' and pros_sel != 'sd_051' and pros_sel != 'sd_052' window[KEY_SCALE_TXT].update(visible = advanced and enb) window[KEY_SCALE].update(visible = advanced and enb) enb = pros_sel != 'sd_040' and pros_sel != 'sd_081' and pros_sel != 'sd_051' and pros_sel != 'sd_052' window[KEY_STRENGTH_TXT].update(visible = advanced and enb) window[KEY_STRENGTH].update(visible = advanced and enb) enb = pros_sel == 'sd_044' or pros_sel == 'sd_046' or pros_sel == 'sd_047' or pros_sel == 'sd_048' window[KEY_CTRL_IMGPATH_TXT].update(visible = advanced and enb) window[KEY_CTRL_IMGPATH].update(visible = advanced and enb) window[KEY_CTRL_IMAGE_SEL].update(visible = advanced and enb) enb = pros_sel != 'sd_052' window[KEY_PROMPT_JP_TXT].update(visible = advanced and enb) window[KEY_PROMPT_TXT].update(visible = advanced and enb) window[KEY_NEG_PROMPT_JP_TXT].update(visible = advanced and enb) window[KEY_NEG_PROMPT_TXT].update(visible = advanced and enb) window[KEY_PROMPT_JP].update(visible = advanced and enb) window[KEY_PROMPT].update(visible = advanced and enb) window[KEY_NEG_PROMPT_JP].update(visible = advanced and enb) window[KEY_NEG_PROMPT].update(visible = advanced and enb) # ------------------------------------------ # パラメータ設定 pros_sel = sdt._get_process_name(opt_list) # pros_sel 処理方法 advanced = opt.mode device = sdt._get_device(opt, logger) result_path = sdt._get_result_path(opt, logger) result_file = sdt._get_result_file(opt, logger) image_path = sdt._get_source_image_path(opt, logger) prompt = sdt._get_prompt(opt, logger) model_path = sdt._get_model_path(opt, logger) max_size = sdt._get_max_size(opt, logger) height, width = sdt._get_image_size(opt, logger) seed = DEF_SEED_VAL num_inference_steps = sdt._get_inference_steps(opt, logger) guidance_scale = sdt._get_guidance_scale(opt, logger) strength = sdt._get_strength(opt, logger) neg_prompt = sdt._get_negative_prompt(opt, logger) ip_image_path = sdt._get_ip_image_path(opt, logger) ip_scale = sdt._get_ip_scale(opt, logger) param = sdr.param param[KEY_PROS_SEL] = pros_sel param[KEY_OUTPATH] = '' param[KEY_PROMPT_JP] = opt.prompt param[KEY_PROMPT] = prompt param[KEY_INPUTPATH] = image_path param[KEY_MODEL] = opt.model_path param[KEY_MAXSIZE] = max_size param[KEY_SEED_INPUT] = opt.seed param[KEY_SEED] = seed param[KEY_STEP] = num_inference_steps param[KEY_SCALE] = guidance_scale param[KEY_STRENGTH] = strength param[KEY_DEVICE] = device param[KEY_LOOP] = 1 param[KEY_TIME] = sdt.elapsed_time_str(0) param[SKEY_RESULT_PATH] = result_path param[SKEY_RESULT_FILE] = result_file param[KEY_MODEL_DIR] = opt.model_dir param[KEY_CTRL_IMGPATH] = sdt._get_control_image_path(opt, logger) param[KEY_CTRL_DIR] = sdt._get_controlnet_model_dir(opt, logger) param[KEY_CTRL_MODEL] = opt.ctrl_model_path param[KEY_WIDTH] = width param[KEY_HEIGHT] = height param[KEY_IMG_SCALE] = sdt._get_image_guidance_scale(opt, logger) param[KEY_CC_SCALE] = sdt._get_controlnet_conditioning_scale(opt, logger) param[KEY_NEG_PROMPT_JP] = opt.neg_prompt param[KEY_NEG_PROMPT] = neg_prompt param[KEY_IP_IMGPATH] = ip_image_path param[KEY_IP_SCALE] = ip_scale param[KEY_EXTENTION] = opt.ext sdr.logout_data(param, logger) csvfile = param[SKEY_RESULT_PATH]+ '/' + CSV_LOG_FILE # 出力画像ログファイル名 # 出力フォルダ os.makedirs(result_path, exist_ok = True) # 作業フォルダ work_path = sdt.get_work_path(logger) os.makedirs(work_path, exist_ok = True) # フォント取得 from my_puttext import get_font, cv2_putText font_face = get_font() # ウィンドウのテーマ sg.theme(DEF_THEME) canvas_input_img = sg.Image(size = (CANVAS_SIZE, CANVAS_SIZE), key=KEY_INPUT_IMAGE) canvas_img = sg.Image(size = (CANVAS_SIZE, CANVAS_SIZE), key=KEY_IMAGE) # モードボタン初期設定 if advanced: b0 = 'Advance' b1 = 'LightCyan' b2 = 'cornflower blue' else: b0 = 'Normal' b1 = 'cornflower blue' b2 = 'LightCyan' col_left = [ [canvas_input_img], [sg.Text("Input File", size=(14, 1), key=KEY_INPUT_TXT), sg.Text(param[KEY_INPUTPATH], size=(39,1), text_color='#008800', background_color='LightSteelBlue1', key=KEY_INPUTPATH), sg.Button('Image', size=(6, 1), key=KEY_IMAGE_SEL)], [sg.Text("Model", size=(14, 1)), sg.Text(param[KEY_MODEL], size=(39,1), text_color='#008800', background_color='LightSteelBlue1', key=KEY_MODEL), sg.Button('Model', size=(6, 1), key=KEY_MODELSEL)], [sg.Text("Prompt input", size=(14, 1), key=KEY_PROMPT_JP_TXT), sg.Multiline(param[KEY_PROMPT_JP], size=(52,2), key=KEY_PROMPT_JP)], [sg.Text("Prompt", size=(14, 1), key=KEY_PROMPT_TXT), sg.Multiline(param[KEY_PROMPT], size=(52,2), text_color='#008800', background_color='LightSteelBlue1', key=KEY_PROMPT)], [sg.Text("Negative Prompt in", size=(14, 1), key=KEY_NEG_PROMPT_JP_TXT), sg.Multiline(param[KEY_NEG_PROMPT_JP], size=(52,2), key=KEY_NEG_PROMPT_JP)], [sg.Text("Negative Prompt", size=(14, 1), key=KEY_NEG_PROMPT_TXT), sg.Multiline(param[KEY_NEG_PROMPT], size=(52,2), text_color='#008800', background_color='LightSteelBlue1', key=KEY_NEG_PROMPT)], [sg.Text("ControlNet File", size=(14, 1), key=KEY_CTRL_IMGPATH_TXT), sg.Text(param[KEY_CTRL_IMGPATH], size=(39,1), text_color='#008800', background_color='LightSteelBlue1', key=KEY_CTRL_IMGPATH), sg.Button('C Image', size=(6, 1), key=KEY_CTRL_IMAGE_SEL)], ] col_right = [ [canvas_img], [sg.Text("Output File", size=(14, 1)), sg.Text(param[KEY_OUTPATH], size=(39,1), key=KEY_OUTPATH), sg.Button(b0, button_color=(b1, b2), size=(6, 1), key=KEY_ADVANCED)], [sg.Text("ControlNet Model", size=(14, 1), key=KEY_CTRL_MODEL_TXT), sg.Text(param[KEY_CTRL_MODEL], size=(39,1), text_color='#008800', background_color='LightSteelBlue1', key=KEY_CTRL_MODEL), sg.Button('C Model', size=(6, 1), key=KEY_CTRL_MODELSEL)], [sg.Text("Gen Image Width", size=(14, 1), key=KEY_WIDTH_TXT), sg.Input(param[KEY_WIDTH], size=(10,1), key=KEY_WIDTH), sg.Text("Height", size=(5, 1), key=KEY_HEIGHT_TXT), sg.Input(param[KEY_HEIGHT], size=(10,1), key=KEY_HEIGHT), sg.Text(" Max size", size=(8, 1), key=KEY_MAXSIZE_TXT), sg.Input(param[KEY_MAXSIZE], size=(10,1), key=KEY_MAXSIZE)], [sg.Text("Seed (-1=Random)", size=(14, 1)), sg.Input(param[KEY_SEED_INPUT], size=(18,1), key=KEY_SEED_INPUT), sg.Button('←', size=(4, 1), key=KEY_SEED_SET), sg.Text(param[KEY_SEED], size=(14,1), text_color='#008800', background_color='LightSteelBlue1', key=KEY_SEED), sg.Button('-1', size=(5, 1), key=KEY_SEED_CLR)], [sg.Text("Detail (Steps)", size=(14, 1), key=KEY_STEP_TXT), sg.Slider((10, 150), float(param[KEY_STEP]), 1, orientation='h', size=(42, 5), key=KEY_STEP)], [sg.Text("Guidance Scale", size=(14, 1), key=KEY_SCALE_TXT), sg.Slider((1, 50), float(param[KEY_SCALE]), 0.1, orientation='h', size=(42, 5), key=KEY_SCALE)], [sg.Text("Strength", size=(14, 1), key=KEY_STRENGTH_TXT), sg.Slider((0, 1), float(param[KEY_STRENGTH]), 0.1, orientation='h', size=(42, 5), key=KEY_STRENGTH)], [sg.Text("", size=(14, 1))], [sg.Text("Loop count (1-50)", size=(14, 1)), sg.Input(param[KEY_LOOP], size=(2,1), justification='right', key=KEY_LOOP), sg.Text("", size=(2, 1)), sg.Button('Generate', size=(10, 1), key=KEY_GENERATE), sg.Text("", size=(8, 1)), sg.Button('Exit', size=(10, 1), key=KEY_EXIT)] ] # ウィンドウのレイアウト layout = [[sg.Column(col_left, vertical_alignment='top'), sg.Column(col_right, vertical_alignment='top')]] # ウィンドウオブジェクトの作成 window = sg.Window(title, layout, finalize=True, return_keyboard_events=True, disable_close=True) #「X」無効 # ユーザーイベントの定義 canvas_img.bind('<ButtonPress>', '_click_on') canvas_input_img.bind('<ButtonPress>', '_click_on') # キャンバス初期化 pros_sel = sdt._get_process_name(opt_list) # pros_sel 処理方法 ss = param[KEY_SEED_INPUT] # 最初のシード入力値 if pros_sel == 'sd_081' and param[KEY_IP_IMGPATH] != '': param[KEY_INPUTPATH] = param[KEY_IP_IMGPATH] # IP-Adapter を 入力画像 bf = False # 常に初期化する 2025/07/28 if bf: frame = update_widget() param[KEY_SEED] = param[KEY_SEED_INPUT] param[KEY_SEED_INPUT] = ss window[KEY_SEED].update(param[KEY_SEED]) window[KEY_SEED_INPUT].update(param[KEY_SEED_INPUT] ) else: frame = update_canvas(KEY_IMAGE, param[KEY_OUTPATH]) frame_input = update_canvas(KEY_INPUT_IMAGE, param[KEY_INPUTPATH]) # 出力結果フォルダを調べる sel_ext = {'.bmp', '.png', '.jpeg', '.jpg', '.tif'} sellist = sdt.get_file_list_sel('sd_results3/*', sel_ext) param[SKEY_DEF_OUTPATH] = param[KEY_OUTPATH] if len(sellist) == 0 else sellist[0] new_make_f = False window[KEY_PROMPT].update(disabled = True) change_mode(advanced) # イベントのループ while True: event, values = window.read(timeout = 30) # 画像生成 if new_make_f: # 処理プロセス logger.info(f'{sdt.CYAN}** Start {param[KEY_LOOP]} **{sdt.NOCOLOR}') input_img_path = param[KEY_INPUTPATH] start_time = time.time() generate_image(param, logger) param[KEY_TIME] = sdt.elapsed_time_str(time.time() - start_time) sdr.result_csv(csvfile, param, logger) frame = update_canvas(KEY_IMAGE, param[KEY_OUTPATH]) param[SKEY_DEF_OUTPATH] = param[KEY_OUTPATH] # 現在の画像パス window[KEY_OUTPATH].update(param[KEY_OUTPATH]) param[KEY_LOOP] = param[KEY_LOOP] - 1 window[KEY_LOOP].update(param[KEY_LOOP]) if input_img_path != param[KEY_INPUTPATH]: frame_input = update_canvas(KEY_INPUT_IMAGE, param[KEY_INPUTPATH]) window[KEY_INPUTPATH].update(param[KEY_INPUTPATH] ) if param[KEY_LOOP] < 1: param[KEY_LOOP] = 1 window[KEY_LOOP].update(param[KEY_LOOP]) set_enb_dis(False) new_make_f = False else: get_paramlist2(window, values, param) window[KEY_SEED].update(param[KEY_SEED]) logger.info(f'{sdt.CYAN}** Complete **{sdt.NOCOLOR} {param[KEY_TIME]}') # 終了 if event == KEY_EXIT or event == sg.WIN_CLOSED: break # KEY_IMAGE if event == KEY_IMAGE + '_click_on': logger.debug(f'{event}') set_enb_dis(True) def_file = param[SKEY_DEF_OUTPATH] if os.path.isfile(def_file): imgfile = sdr.image_dialog(param, csvfile, def_file, title='Result Image file select', xn=10, yn=3, thumb_size=128, gap=4, logger=logger) if os.path.isfile(imgfile): logger.debug(f'Output select: {imgfile}') bf = sdr.read_result_csv(csvfile, param, logger, imgfile) if bf: frame = update_widget() param[SKEY_DEF_OUTPATH] = param[KEY_OUTPATH] else: sdr.read_result_csv(csvfile, param, logger, def_file) frame = update_widget() param[SKEY_DEF_OUTPATH] = param[KEY_OUTPATH] set_enb_dis(False) # KEY_INPUT_IMAGE if event == KEY_INPUT_IMAGE + '_click_on': logger.debug(f'{event}') set_enb_dis(True) def_file = param[KEY_INPUTPATH] if os.path.isfile(def_file): imgfile = my_thumbnail.image_dialog(def_file, 'Image file select', my_thumbnail.DEF_THEME, 10, 4, ret = '', logger = logger) bf = True if pros_sel == 'sd_044': mskfile = get_mask_file(imgfile) if mskfile != '': param[KEY_CTRL_IMGPATH] = mskfile else: bf = False if bf and os.path.isfile(imgfile): logger.debug(f'Input select: {imgfile}') param[KEY_INPUTPATH] = imgfile update_widget() set_enb_dis(False) # モード・チェンジ if event == KEY_ADVANCED: logger.debug(f'{event} {not advanced}') if advanced: window[KEY_ADVANCED].update('Normal', button_color=('cornflower blue', 'LightCyan')) advanced = False else: window[KEY_ADVANCED].update('Advance', button_color=('LightCyan', 'cornflower blue')) advanced = True change_mode(advanced) # シード値セット・ボタン if event == KEY_SEED_SET: logger.debug(f'{event}') window[KEY_SEED_INPUT].update(param[KEY_SEED]) # シード値クリア・ボタン if event == KEY_SEED_CLR: logger.debug(f'{event}') window[KEY_SEED_INPUT].update('-1') # 画像選択ボタン if event == KEY_IMAGE_SEL: logger.debug(f'{event}') set_enb_dis(True) imgfile = my_dialog.select_image_file(initdir=os.path.dirname(param[KEY_INPUTPATH])) bf = True if pros_sel == 'sd_044': mskfile = get_mask_file(imgfile) if mskfile != '': param[KEY_CTRL_IMGPATH] = mskfile else: bf = False if bf and len(imgfile) > 0: logger.debug(f'Input select: {imgfile}') param[KEY_INPUTPATH] = imgfile frame_input = update_canvas(KEY_INPUT_IMAGE, param[KEY_INPUTPATH]) window[KEY_INPUTPATH].update(param[KEY_INPUTPATH]) set_enb_dis(False) # Model ボタン if event == KEY_MODELSEL: logger.debug(f'{event}') set_enb_dis(True) s0 = param[KEY_MODEL] id = s0.find('/') s1 = '/' + s0[:id] if id > 0 else '' s2 = param[KEY_MODEL_DIR] + s1 ttl = 'モデルファイルを選択' types = [("Model file", ".safetensors .pt"), ("model", ".safetensors"), ("pt file", ".pt") ] filename = my_dialog.file_dialog(ttl, types, s2) if len(filename) > 0: s3 = os.path.basename(filename) # ファイル名 s4 = os.path.dirname(filename) # ディレクトリ名 id = s4.find('SD1.5') if id >= 0 and id == len(s4) - 5: # SD1.5 s5 = s4[:id - 1] s3 = 'SD1.5/' + s3 else: # SDXL s5 = s4 param[KEY_MODEL] = s3 s6 = os.path.abspath(param[KEY_MODEL_DIR]) s7 = s6.replace('\\', '/') if s7 == s5: # モデルディレクトリは変更不可 window[KEY_MODEL].update(param[KEY_MODEL]) logger.debug(f'{param[KEY_MODEL_DIR]}/{param[KEY_MODEL]}') else: my_dialog.warning_dialog(ttl, 'モデルディレクトリは変更できません') set_enb_dis(False) # Genarate ボタン if event == KEY_GENERATE: logger.debug(f'{event}') set_enb_dis(True) get_paramlist(window, values, param) frame = msg_out_canvas(KEY_IMAGE, frame, 'Generating ...', (240,0,0)) window[KEY_OUTPATH].update('') window[KEY_PROMPT].update(param[KEY_PROMPT]) window[KEY_SEED].update(param[KEY_SEED]) window[KEY_NEG_PROMPT].update(param[KEY_NEG_PROMPT]) new_make_f = True # ウィンドウ終了処理 window.close() # main関数エントリーポイント(実行開始) if __name__ == "__main__": # コマンドライン入力設定 parser = sdt.parse_args(None, opt_list) opt = parser.parse_args() if opt.pros_sel == 'sd_040': import sd_040 as sd elif opt.pros_sel == 'sd_042': import sd_042 as sd elif opt.pros_sel == 'sd_044': import sd_044 as sd elif opt.pros_sel == 'sd_046': import sd_046 as sd elif opt.pros_sel == 'sd_047': import sd_047 as sd elif opt.pros_sel == 'sd_048': import sd_048 as sd elif opt.pros_sel == 'sd_050': import sd_050 as sd elif opt.pros_sel == 'sd_051': import sd_051 as sd elif opt.pros_sel == 'sd_052': import sd_052 as sd elif opt.pros_sel == 'sd_091': import sd_091 as sd elif opt.pros_sel == 'sd_081': import sd_081 as sd else: exit(0) # オプションリスト変更 sd_list = sd.opt_list set_option(opt, sd_list) title = title + ' <' + opt.pros_sel + '>' set_pros_hedder(opt.pros_sel) # アプリケーション・ログ設定 module = os.path.basename(__file__) module_name = os.path.splitext(module)[0] logger = my_logging.get_module_logger_sel(module_name, int(opt.log)) # 初期設定 if opt.pros_sel == 'sd_051': if opt.image_path == '': opt.image_path = 'images/sd_050_test1.png' sdt.display_info(opt, title) main(opt, logger) logger.info('\nFinished.\n')※ 上記ソースコードは表示の都合上、半角コード '}' が 全角 '}'になっていることに注意
python sd_101.py
(sd_test) PS > python sd_101.py Stable Diffusion with diffusers(101) Ver 0.00: Starting application... --result_image : sd_results4/sd.png --cpu : False --log : 3 --model_dir : /StabilityMatrix/Data/Models/StableDiffusion --model_path : SD1.5/beautifulRealistic_brav5.safetensors --ctrl_model_dir : /StabilityMatrix/Data/Models/ControlNet --ctrl_model_path : control_v11p_sd15_canny_fp16.safetensors --image_path : images/vermeer.png --max_size : 0 --prompt : 微笑んでいる女性 --seed : 12345678 --width : 512 --height : 512 --step : 20 --scale : 7.0 --cc_scale : 1.0 --strength : 0.6 --neg_prompt : 最悪の品質、おかしい人体構造 --ip_scale : 0.5 --scheduler : euler --mode : canny ** mode <canny> sd_101 ** ** Start 1 ** prompt: Woman smiling neg_prompt: Worst quality, funny body structure image_path: images/vermeer.png seed: 12345678 Fetching 11 files: 100%|███████████████████████████████| 11/11 [00:00<?, ?it/s] Loading pipeline components...: 100%|████████████| 6/6 [00:00<00:00, 16.09it/s] ** scheduler: euler 100%|██████████████████████████████████████████| 20/20 [00:02<00:00, 8.95it/s] result_file: sd_results4/sd_00000_12345678-canny.png ** Complete ** 00:00:06 Finished.
# -*- coding: utf-8 -*- ##-------------------------------------------------- ## Stable Diffusion with diffusers(101) Ver 0.01 ## ControlNet GUI interface ## 2025.08.16 Masahiro Izutsu ##-------------------------------------------------- ## sd_101.py ## Ver 0.00 2025.08.16 Trial version ## Ver 0.01 2025.08.19 統合版 # タイトル title = 'Stable Diffusion with diffusers(101) Ver 0.01' import warnings warnings.simplefilter('ignore') # インポート&初期設定 import os import numpy as np import time import csv import cv2 from PIL import Image import PySimpleGUI as sg import my_logging import my_thumbnail import my_dialog import my_imagetool import sd_tools as sdt import sd_results as sdr import sd_053 as sdm # 定数定義 DEF_THEME = 'BlueMono' CANVAS_SIZE = 512 CANVAS_SIZE_S = 180 DEF_SEED_VAL = '12345678' CSV_LOG_FILE = 'result_101.csv' PROCESS_DIR = '/process' KEY_IMAGE = '-Image-' KEY_INPUT_IMAGE = '-Input_Image-' KEY_PROS_IMAGE = '-Proces_Image-' KEY_EXIT = '-Exit-' KEY_GENERATE = '-Generate-' KEY_RESET = '-Reset-' KEY_IMAGE_SEL = '-Image_sel-' KEY_MODELSEL = '-Model_sel-' KEY_MODESEL = '-Mode_sel-' KEY_SEED_INPUT = '-Seed_input-' KEY_INPUT_TXT = '-Input_text-' KEY_MAXSIZE_TXT = '-MaxSize_text-' KEY_WIDTH_TXT = '--Image_width_text--' KEY_HEIGHT_TXT = '--Image_height_text--' KEY_SEED_CLR = '--seed_clear--' KEY_SEED_SET = '--seed_set--' KEY_PROMPT_JP_TXT = '-PromptJP_text-' KEY_PROMPT_TXT = '-Prompt_text-' KEY_NEG_PROMPT_JP_TXT = '-Negative_PromptJP_text-' KEY_NEG_PROMPT_TXT = '-Negative_Prompt_text-' KEY_IP_IMGPATH_SEL = '-Ip_image_path_sel-' SKEY_RESULT_PATH = '-result_path-' SKEY_RESULT_FILE = '-result_file-' SKEY_DEF_OUTPATH = '-default_image-' # csv save KEY_PROS_SEL = '--Proscess--' KEY_OUTPATH = '-Output-' KEY_SEED = '-Seed-' KEY_PROMPT_JP = '-PromptJP-' KEY_PROMPT = '-Prompt-' KEY_INPUTPATH = '-Input-' KEY_MAXSIZE = '-MaxSize-' KEY_STEP = '-Step-' KEY_SCALE = '-Scale-' KEY_STRENGTH = '-strength-' KEY_MODEL_DIR = '-model_dir-' KEY_MODEL = '-Model-' KEY_CTRL_IMGPATH = '--ctrl_image_path--' KEY_CTRL_DIR = '-ctrl_model_dir-' KEY_CTRL_MODEL = '-ctrl_model-' KEY_WIDTH = '--Image_width--' KEY_HEIGHT = '--Image_height--' KEY_IMG_SCALE = '--Image_scale--' KEY_CC_SCALE = '--Control_scale--' KEY_DEVICE = '-Device-' KEY_TIME = '-Time-' KEY_LOOP = '-Loop-' KEY_NEG_PROMPT_JP = '-Negative_PromptJP-' KEY_NEG_PROMPT = '-Negative_Prompt-' KEY_IP_IMGPATH = '-Ip_image_path-' KEY_IP_SCALE = '-Ip_scale-' KEY_EXTENTION = '-Extention-' KEY_SCHEDULER = '-Scheduler-' KEY_APMODE = '-ApMode-' # ---------- # コマンドライン定義 def_mode = sdm.MODE_canny opt_list = [ ['pros_sel','','sd_101'], # 0 ['result_image', 'sd_results4/sd.png', 'path to output image file'], # 1 ['cpu', 'store_true', 'cpu mode'], # 2 ['log', '3', 'Log level(-1/0/1/2/3/4/5) Default value is \'3\''], # 3 ['model_dir', '', 'Model directory'], # 4 ['model_path', '', 'Model Path'], # 5 ['ctrl_model_dir', '', 'ControlNet Model directory'], # 6 ['ctrl_model_path', '', 'ControlNet Model Path'], # 7 ['image_path', '', 'Sourcs image file path'], # 8 ['ctrl_image_path', '', 'Control image file path'], # 9 ['max_size', 0, 'image max size (0=source)'], # 10 ['prompt', '', 'Prompt text'], # 11 ['seed', 12345678, 'Seed parameter (-1 = rundom)'], # 12 ['width', 512, 'image size width'], # 13 ['height', 512, 'image size height'], # 14 ['step', 20, 'infer step'], # 15 ['scale', 7.0, 'gaidanse scale'], # 16 ['cc_scale', 1.0, 'controlnet conditioning scale'], # 17 ['strength', 0.6, 'strength value'], # 18 ['neg_prompt', '', 'Negative Prompt text'], # 19 ['ip_image_path', '', 'IP-Adapter image filr path'], # 20 ['ip_scale', 0.5, 'IP-Adapter scale'], # 21 ['scheduler', '', "Scheduler 'non/euler/uni/DPM'"], # 22 ['ext', '', "Extensions (ADetailer) '' or 'girl' or 'boy'"], # 23 ['mode', def_mode, "aplication mode 'canny/inpaint/outpaint/scribble/openpose/pix2pix/txt2img/img2img/lineart/softedge/shuffle/depth'"], ] def set_pros_hedder(pros): opt_list[0][2] = pros sdt.opt_list[0][2] = pros sdr.opt_list[0][2] = pros def get_mode_list(image_path, width, height): mode_list = sdm.mode_list.copy() if os.path.isfile(image_path): img = Image.open(image_path) w, h = img.size if w == h: mode_list.remove(sdm.MODE_outpaint) if w > width//2 or h > height//2: mode_list.remove(sdm.MODE_tile) else: mode_list.clear() mode_list.append(sdm.MODE_txt2img) return mode_list # ウィジェットのデータの取得(1回目) def get_paramlist(window, values, param): param[KEY_MODEL] = window[KEY_MODEL].DisplayText param[KEY_MAXSIZE] = int(values[KEY_MAXSIZE]) param[KEY_SEED] = sdt.get_random_seed_value(values[KEY_SEED_INPUT]) param[KEY_WIDTH] = int(values[KEY_WIDTH]) param[KEY_HEIGHT] = int(values[KEY_HEIGHT]) param[KEY_STEP] = int(values[KEY_STEP]) param[KEY_SCALE] = float(values[KEY_SCALE]) param[KEY_STRENGTH] = float(values[KEY_STRENGTH]) param[KEY_PROMPT_JP] = values[KEY_PROMPT_JP] param[KEY_PROMPT] = sdt.trans_jp2en(param[KEY_PROMPT_JP]) param[KEY_NEG_PROMPT_JP] = values[KEY_NEG_PROMPT_JP] param[KEY_NEG_PROMPT] = sdt.trans_jp2en(param[KEY_NEG_PROMPT_JP]) param[KEY_IP_SCALE] = float(values[KEY_IP_SCALE]) param[KEY_LOOP] = int(values[KEY_LOOP]) if param[KEY_LOOP] < 1: param[KEY_LOOP] = 1 if param[KEY_LOOP] > 50: param[KEY_LOOP] = 50 # ウィジェットのデータの取得(2回目以降) def get_paramlist2(window, values, param): if int(values[KEY_SEED_INPUT]) > 0: param[KEY_SEED] = param[KEY_SEED] + 1 else: param[KEY_SEED] = sdt.get_random_seed_value(values[KEY_SEED_INPUT]) # 画像生成 def generate_image(opt, param, logger = None): image_path_save = param[KEY_INPUTPATH] # image_path push opt.image_path = param[KEY_INPUTPATH] opt.prompt = param[KEY_PROMPT] opt.neg_prompt = param[KEY_NEG_PROMPT] opt.model = param[KEY_MODEL] opt.width = param[KEY_WIDTH] opt.height = param[KEY_HEIGHT] opt.max_size = param[KEY_MAXSIZE] opt.num_inference_steps = param[KEY_STEP] opt.guidance_scale = param[KEY_SCALE] opt.strength = param[KEY_STRENGTH] ap_mode = param[KEY_APMODE] model = param[KEY_MODEL] if param[KEY_MODEL_DIR] == '' else param[KEY_MODEL_DIR] + '/' + param[KEY_MODEL] image_path = param[KEY_INPUTPATH] prompt = param[KEY_PROMPT] max_size = param[KEY_MAXSIZE] seed = param[KEY_SEED] num_inference_steps = param[KEY_STEP] guidance_scale = param[KEY_SCALE] strength = param[KEY_STRENGTH] device = param[KEY_DEVICE] fname = sdt.make_filename_by_seq(param[SKEY_RESULT_PATH], param[SKEY_RESULT_FILE], seq_digit=5, ex= f'{seed}-{ap_mode}') out_path = param[SKEY_RESULT_PATH] + '/' + fname process_path = sdt.get_process_path(out_path) param[KEY_OUTPATH] = out_path ctrl_model_dir = param[KEY_CTRL_DIR] ctrl_model_path = param[KEY_CTRL_MODEL] if param[KEY_CTRL_DIR] == '' else param[KEY_CTRL_DIR] + '/' + param[KEY_CTRL_MODEL] width = param[KEY_WIDTH] height = param[KEY_HEIGHT] cc_scale = param[KEY_CC_SCALE] img_scale = param[KEY_IMG_SCALE] neg_prompt = param[KEY_NEG_PROMPT] ip_image_path = param[KEY_IP_IMGPATH] ip_scale = param[KEY_IP_SCALE] if ip_scale == '': ip_scale = 0.5 extention = param[KEY_EXTENTION] logger.debug(f'** ap_mode: {ap_mode}') logger.debug(f'model: {model}') logger.info(f'prompt: {prompt}') logger.info(f'neg_prompt: {neg_prompt}') logger.info(f'image_path: {image_path}') logger.debug(f'max_size: {max_size}') logger.info(f'seed: {seed}') logger.debug(f'num_inference_steps: {num_inference_steps}') logger.debug(f'guidance_scale: {guidance_scale}') logger.debug(f'strength: {strength}') logger.debug(f'device: {device}') logger.debug(f'loop: {param[KEY_LOOP]}') logger.debug(f'out_path: {out_path}') logger.debug(f'ctrl_model_path: {ctrl_model_path}') if ip_image_path == '': logger.debug(f'IP-Adapter image: {ip_image_path}') else: logger.info(f'IP-Adapter image: {ip_image_path}') logger.info(f'IP-Adapter scale: {ip_scale}') src_image = None # 元画像 ip_image = None # IP 画像 if image_path != '': src_image = sdt._get_resize_image(image_path, max_size, logger) if ip_image_path != '': ip_image = sdt._get_resize_image(ip_image_path, max_size, logger) # 入力画像の前処理 src_image, msk_image, img_ctrl = sdm.pre_generation(opt, logger) if opt.mode != sdm.MODE_txt2img and src_image is None: logger.info(f'{sdt.RED}Processing will be stopped !!{sdt.NOCOLOR}') return # Prosess画像 if opt.mode == sdm.MODE_inpaint: img = msk_image elif opt.mode == sdm.MODE_txt2img and ip_image is not None: img = ip_image else: img = src_image if img is not None: img.save(process_path) strength = sdt._get_strength(opt, logger) # 画像生成 image = sdm.image_generation(pl, opt.ext, model, ctrl_model_path, src_image, msk_image, img_ctrl, prompt, seed, num_inference_steps, width, height, guidance_scale, cc_scale, strength, neg_prompt, ip_image, ip_scale, device, logger) if image is None: logger.info(f'{sdt.RED}Unable to generate image !!{sdt.NOCOLOR}') else: image.save(out_path) logger.info(f'result_file: {out_path}') param[KEY_INPUTPATH] = image_path_save # image_path pop sdt.device_empty_cache(device) # メモリー開放 # ** main関数 ** def main(opt, pl, logger = None): # ------------------------------------------ # キャンバスをクリア def clear_canvas(key, msg, color, sz = CANVAS_SIZE): frame = np.zeros((sz, sz, 3), np.uint8) frame[:,:,] = 0xf0 return msg_out_canvas(key, frame, msg, color, sz) def msg_out_canvas(key, frame, msg, color, sz = CANVAS_SIZE): x0,y0,x1,y1 = cv2_putText(img=frame, text=msg, org=(sz//2, sz//2), fontFace=font_face, fontScale=16, color=color, mode=2,areaf=True) cv2.rectangle(frame,(x0-8, y0), (x1+8, y1), (0xf0,0xf0,0xf0), -1) img = cv2_putText(img=frame, text=msg, org=(sz//2, sz//2), fontFace=font_face, fontScale=16, color=color, mode=2) img = cv2.imencode('.png', frame)[1].tobytes() window[key].update(img) return frame # キャンバスへ画像の表示 def update_canvas(key, imgfile, sz = CANVAS_SIZE): if os.path.isfile(imgfile): frame = cv2.imread(imgfile) frame = my_imagetool.frame_square(frame, (240, 240, 240)) frame = cv2.resize(frame, dsize = (sz, sz)) img = cv2.imencode('.png', frame)[1].tobytes() window[key].update(img) else: msg = 'Input Image' if key == KEY_INPUT_IMAGE else 'Genarate Image' if key == KEY_IMAGE else 'Process Image' frame = clear_canvas(key, msg, (0,0,0), sz) return frame # ウイジェットの更新 def update_widget(): frame = update_canvas(KEY_IMAGE, param[KEY_OUTPATH]) frame_input = update_canvas(KEY_INPUT_IMAGE, param[KEY_INPUTPATH]) window[KEY_OUTPATH].update(param[KEY_OUTPATH]) window[KEY_SEED].update(param[KEY_SEED_INPUT]) # シード値の表示 window[KEY_PROMPT_JP].update(param[KEY_PROMPT_JP]) window[KEY_PROMPT].update(param[KEY_PROMPT]) window[KEY_INPUTPATH].update(param[KEY_INPUTPATH]) window[KEY_MODEL].update(param[KEY_MODEL]) window[KEY_MAXSIZE].update(param[KEY_MAXSIZE]) window[KEY_STEP].update(param[KEY_STEP]) window[KEY_SCALE].update(param[KEY_SCALE]) window[KEY_STRENGTH].update(param[KEY_STRENGTH]) window[KEY_NEG_PROMPT_JP].update(param[KEY_NEG_PROMPT_JP]) window[KEY_NEG_PROMPT].update(param[KEY_NEG_PROMPT]) window[KEY_WIDTH].update(param[KEY_WIDTH]) window[KEY_HEIGHT].update(param[KEY_HEIGHT]) window[KEY_SCHEDULER].update(param[KEY_SCHEDULER]) window[KEY_EXTENTION].update(param[KEY_EXTENTION]) window[KEY_IP_IMGPATH].update(param[KEY_IP_IMGPATH]) window[KEY_IP_SCALE].update(param[KEY_IP_SCALE]) window[KEY_APMODE].update(param[KEY_APMODE]) # ウイジェットの禁止・許可 def set_enb_dis(disabled): window[KEY_IMAGE_SEL].update(disabled = disabled) window[KEY_MODELSEL].update(disabled = disabled) window[KEY_GENERATE].update(disabled = disabled) window[KEY_LOOP].update(disabled = disabled) window[KEY_EXIT].update(disabled = disabled) # モード変更 def change_mode(opt, mode): param[KEY_APMODE] = opt.mode = mode pl.select_mode(opt.mode, device, opt.model_dir, opt.ctrl_model_dir, opt.scheduler, opt.model_path) param[KEY_CTRL_MODEL] = opt.ctrl_model_path = pl.ctrl_model # パイプラインから設定 if mode == sdm.MODE_txt2img: param[KEY_INPUTPATH] = image_path = '' update_mode(opt) # モード更新 def update_mode(opt): window[KEY_INPUTPATH].update(param[KEY_INPUTPATH]) frame = update_canvas(KEY_IMAGE, param[KEY_OUTPATH]) frame_input = update_canvas(KEY_INPUT_IMAGE, param[KEY_INPUTPATH]) mode_list = get_mode_list(image_path, width, height) window[KEY_APMODE].Update(values = mode_list) new_mode = ap_mode if ap_mode in mode_list else mode_list[0] window[KEY_APMODE].Update(new_mode) param[KEY_CTRL_MODEL] = opt.ctrl_model_path = pl.ctrl_model # パイプラインから設定 # ------------------------------------------ # パラメータ設定 pros_sel = sdt._get_process_name(opt_list) # pros_sel 処理方法 ap_mode = opt.mode device = sdt._get_device(opt, logger) result_path = sdt._get_result_path(opt, logger) proces_path = result_path + PROCESS_DIR result_file = sdt._get_result_file(opt, logger) image_path = sdt._get_source_image_path(opt, logger) prompt = sdt._get_prompt(opt, logger) model_path = sdt._get_model_path(opt, logger) max_size = sdt._get_max_size(opt, logger) height, width = sdt._get_image_size(opt, logger) seed = DEF_SEED_VAL num_inference_steps = sdt._get_inference_steps(opt, logger) guidance_scale = sdt._get_guidance_scale(opt, logger) strength = sdt._get_strength(opt, logger) neg_prompt = sdt._get_negative_prompt(opt, logger) ip_image_path = sdt._get_ip_image_path(opt, logger) ip_scale = sdt._get_ip_scale(opt, logger) param = sdr.param param[KEY_PROS_SEL] = pros_sel param[KEY_OUTPATH] = '' param[KEY_PROMPT_JP] = opt.prompt param[KEY_PROMPT] = prompt param[KEY_INPUTPATH] = image_path param[KEY_MODEL] = opt.model_path param[KEY_MAXSIZE] = max_size param[KEY_SEED_INPUT] = opt.seed param[KEY_SEED] = seed param[KEY_STEP] = num_inference_steps param[KEY_SCALE] = guidance_scale param[KEY_STRENGTH] = strength param[KEY_DEVICE] = device param[KEY_LOOP] = 1 param[KEY_TIME] = sdt.elapsed_time_str(0) param[SKEY_RESULT_PATH] = result_path param[SKEY_RESULT_FILE] = result_file param[KEY_MODEL_DIR] = opt.model_dir param[KEY_CTRL_IMGPATH] = sdt._get_control_image_path(opt, logger) param[KEY_CTRL_DIR] = sdt._get_controlnet_model_dir(opt, logger) param[KEY_CTRL_MODEL] = opt.ctrl_model_path param[KEY_WIDTH] = width param[KEY_HEIGHT] = height param[KEY_IMG_SCALE] = sdt._get_image_guidance_scale(opt, logger) param[KEY_CC_SCALE] = sdt._get_controlnet_conditioning_scale(opt, logger) param[KEY_NEG_PROMPT_JP] = opt.neg_prompt param[KEY_NEG_PROMPT] = neg_prompt param[KEY_IP_IMGPATH] = ip_image_path param[KEY_IP_SCALE] = ip_scale param[KEY_EXTENTION] = opt.ext param[KEY_SCHEDULER] = opt.scheduler param[KEY_APMODE] = opt.mode sdr.logout_data(param, logger) csvfile = param[SKEY_RESULT_PATH]+ '/' + CSV_LOG_FILE # 出力画像ログファイル名 # 出力フォルダ os.makedirs(result_path, exist_ok = True) os.makedirs(proces_path, exist_ok = True) # 作業フォルダ work_path = sdt.get_work_path(logger) os.makedirs(work_path, exist_ok = True) # フォント取得 from my_puttext import get_font, cv2_putText font_face = get_font() # ウィンドウのテーマ sg.theme(DEF_THEME) mode_list = get_mode_list(image_path, width, height) canvas_input_img = sg.Image(size = (CANVAS_SIZE, CANVAS_SIZE), key=KEY_INPUT_IMAGE) canvas_img = sg.Image(size = (CANVAS_SIZE, CANVAS_SIZE), key=KEY_IMAGE) canvas_pros = sg.Image(size = (CANVAS_SIZE_S, CANVAS_SIZE_S), key=KEY_PROS_IMAGE) col_top_l = [ [canvas_input_img], [sg.Text("Input File", size=(14, 1), key=KEY_INPUT_TXT), sg.Text(param[KEY_INPUTPATH], size=(39,1), text_color='#008800', background_color='LightSteelBlue1', key=KEY_INPUTPATH), sg.Button('Image', size=(6, 1), key=KEY_IMAGE_SEL) ], ] col_top_r = [ [canvas_img], [sg.Text("Output File", size=(8, 1)), sg.Text(param[KEY_OUTPATH], size=(54,1), text_color='#008800', background_color='LightSteelBlue1', key=KEY_OUTPATH) ], ] col_left = [ [sg.Text("Model", size=(14, 1)), sg.Text(param[KEY_MODEL], size=(39,1), text_color='#008800', background_color='LightSteelBlue1', key=KEY_MODEL), sg.Button('Model', size=(6, 1), key=KEY_MODELSEL)], [sg.Text("Prompt input", size=(14, 1), key=KEY_PROMPT_JP_TXT), sg.Multiline(param[KEY_PROMPT_JP], size=(52,2), key=KEY_PROMPT_JP)], [sg.Text("Prompt", size=(14, 1), key=KEY_PROMPT_TXT), sg.Multiline(param[KEY_PROMPT], size=(52,2), text_color='#008800', background_color='LightSteelBlue1', key=KEY_PROMPT)], [sg.Text("Negative Prompt in", size=(14, 1), key=KEY_NEG_PROMPT_JP_TXT), sg.Multiline(param[KEY_NEG_PROMPT_JP], size=(52,2), key=KEY_NEG_PROMPT_JP)], [sg.Text("Negative Prompt", size=(14, 1), key=KEY_NEG_PROMPT_TXT), sg.Multiline(param[KEY_NEG_PROMPT], size=(52,2), text_color='#008800', background_color='LightSteelBlue1', key=KEY_NEG_PROMPT)], [sg.Text("IP-Adapter", size=(14, 1)), sg.Text(param[KEY_IP_IMGPATH], size=(39,1), text_color='#008800', background_color='LightSteelBlue1', key=KEY_IP_IMGPATH), sg.Button('IPimage', size=(6, 1), key=KEY_IP_IMGPATH_SEL)], ] col_center = [ [sg.Text("Width", size=(8, 1), key=KEY_WIDTH_TXT), sg.Input(param[KEY_WIDTH], size=(5,1), key=KEY_WIDTH), sg.Text("Height", size=(5, 1), key=KEY_HEIGHT_TXT), sg.Input(param[KEY_HEIGHT], size=(5,1), key=KEY_HEIGHT), sg.Text("Max", size=(4, 1), key=KEY_MAXSIZE_TXT), sg.Input(param[KEY_MAXSIZE], size=(5,1), key=KEY_MAXSIZE)], [sg.Text("Seed", size=(8, 1)), sg.Input(param[KEY_SEED_INPUT], size=(10,1), key=KEY_SEED_INPUT), sg.Button('←', size=(3, 1), key=KEY_SEED_SET), sg.Text(param[KEY_SEED], size=(8,1), text_color='#008800', background_color='LightSteelBlue1', key=KEY_SEED), sg.Button('-1', size=(3, 1), key=KEY_SEED_CLR)], [sg.Text("Steps", size=(8, 1)), sg.Slider((10, 150), float(param[KEY_STEP]), 1, orientation='h', size=(26, 5), key=KEY_STEP)], [sg.Text("Scale", size=(8, 1)), sg.Slider((1, 50), float(param[KEY_SCALE]), 0.1, orientation='h', size=(26, 5), key=KEY_SCALE)], [sg.Text("Strength", size=(8, 1)), sg.Slider((0, 1), float(param[KEY_STRENGTH]), 0.1, orientation='h', size=(26, 5), key=KEY_STRENGTH)], [sg.Text("", size=(8, 1))], [sg.Text("Mode", size=(8, 1)), sg.Combo(mode_list, mode_list[0], enable_events = True, readonly = True, key=KEY_APMODE), sg.Text(" Scheduler", size=(9, 1)), sg.Combo(sdm.scheduler_list, opt.scheduler, enable_events = True, readonly = True, key=KEY_SCHEDULER)], [sg.Text("Extention", size=(8, 1)), sg.Combo(sdm.adetailer_list, sdm.adetailer_list[0], enable_events = True, readonly = True, key=KEY_EXTENTION), sg.Text("IPscale", size=(5, 1)), sg.Slider((0, 1), float(param[KEY_IP_SCALE]), 0.1, orientation='h', size=(13, 5), key=KEY_IP_SCALE)], ] col_right = [ [canvas_pros], ] col_bottom = [ [sg.Button('Reset', size=(6, 1), font=('Arial', 8), key=KEY_RESET), sg.Text("", size=(65, 1)), sg.Text("Loop count (1-50)", size=(14, 1)), sg.Input(param[KEY_LOOP], size=(2,1), justification='right', key=KEY_LOOP), sg.Text("", size=(2, 1)), sg.Button('Generate', size=(10, 1), key=KEY_GENERATE), sg.Text("", size=(4, 1)), sg.Button('Exit', size=(10, 1), key=KEY_EXIT)] ] # ウィンドウのレイアウト layout = [ [sg.Column(col_top_l, vertical_alignment='top'), sg.Column(col_top_r, vertical_alignment='top')], [sg.Column(col_left, vertical_alignment='top'), sg.Column(col_center, vertical_alignment='top'), sg.Column(col_right, vertical_alignment='top')], [sg.Column(col_bottom)], ] # ウィンドウオブジェクトの作成 window = sg.Window(title, layout, finalize=True, return_keyboard_events=True, disable_close=True) #「X」無効 # ユーザーイベントの定義 canvas_img.bind('<ButtonPress>', '_click_on') canvas_input_img.bind('<ButtonPress>', '_click_on') # キャンバス初期化 ss = param[KEY_SEED_INPUT] # 最初のシード入力値 if ap_mode == sdm.MODE_txt2img and param[KEY_IP_IMGPATH] != '': param[KEY_INPUTPATH] = param[KEY_IP_IMGPATH] # IP-Adapter を 入力画像 bf = False # 常に初期化する 2025/07/28 if bf: update_widget() param[KEY_SEED] = param[KEY_SEED_INPUT] param[KEY_SEED_INPUT] = ss window[KEY_SEED].update(param[KEY_SEED]) window[KEY_SEED_INPUT].update(param[KEY_SEED_INPUT] ) else: frame = update_canvas(KEY_IMAGE, param[KEY_OUTPATH]) frame_input = update_canvas(KEY_INPUT_IMAGE, param[KEY_INPUTPATH]) frame_pros = update_canvas(KEY_PROS_IMAGE, '', sz = CANVAS_SIZE_S) # 出力結果フォルダを調べる sel_ext = {'.bmp', '.png', '.jpeg', '.jpg', '.tif'} sellist = sdt.get_file_list_sel(result_path + '/*', sel_ext) param[SKEY_DEF_OUTPATH] = param[KEY_OUTPATH] if len(sellist) == 0 else sellist[0] new_make_f = False window[KEY_PROMPT].update(disabled = True) # イベントのループ while True: event, values = window.read(timeout = 30) # 画像生成 if new_make_f: # 処理プロセス logger.info(f'{sdt.CYAN}** Start {param[KEY_LOOP]} **{sdt.NOCOLOR}') input_img_path = param[KEY_INPUTPATH] start_time = time.time() generate_image(opt, param, logger) param[KEY_TIME] = sdt.elapsed_time_str(time.time() - start_time) sdr.result_csv(csvfile, param, logger) frame = update_canvas(KEY_IMAGE, param[KEY_OUTPATH]) process_path = sdt.get_process_path(param[KEY_OUTPATH]) frame_pros = update_canvas(KEY_PROS_IMAGE, process_path, sz = CANVAS_SIZE_S) param[SKEY_DEF_OUTPATH] = param[KEY_OUTPATH] # 現在の画像パス window[KEY_OUTPATH].update(param[KEY_OUTPATH]) param[KEY_LOOP] = param[KEY_LOOP] - 1 window[KEY_LOOP].update(param[KEY_LOOP]) if input_img_path != param[KEY_INPUTPATH]: frame_input = update_canvas(KEY_INPUT_IMAGE, param[KEY_INPUTPATH]) window[KEY_INPUTPATH].update(param[KEY_INPUTPATH] ) if param[KEY_LOOP] < 1: param[KEY_LOOP] = 1 window[KEY_LOOP].update(param[KEY_LOOP]) set_enb_dis(False) new_make_f = False else: get_paramlist2(window, values, param) window[KEY_SEED].update(param[KEY_SEED]) logger.info(f'{sdt.CYAN}** Complete **{sdt.NOCOLOR} {param[KEY_TIME]}') # 終了 if event == KEY_EXIT or event == sg.WIN_CLOSED: break # KEY_IMAGE if event == KEY_IMAGE + '_click_on': logger.debug(f'{event}') set_enb_dis(True) def_file = param[SKEY_DEF_OUTPATH] if os.path.isfile(def_file): imgfile = sdr.image_dialog(param, csvfile, def_file, title='Result Image file select', xn=10, yn=3, thumb_size=128, gap=4, logger=logger) if os.path.isfile(imgfile): logger.debug(f'Output select: {imgfile}') bf = sdr.read_result_csv(csvfile, param, logger, imgfile) if not bf: sdr.read_result_csv(csvfile, param, logger, def_file) update_widget() param[SKEY_DEF_OUTPATH] = param[KEY_OUTPATH] param[KEY_DEVICE] = device = sdt._get_device(opt, logger) # device は実行環境に ap_mode = opt.mode = param[KEY_APMODE] scheduler = opt.scheduler = param[KEY_SCHEDULER] base_model = opt.base_model = param[KEY_MODEL] model_dir = opt.model_dir = param[KEY_MODEL_DIR] ctrl_model_dir = param[KEY_CTRL_DIR] pl.select_mode(opt.mode, device, model_dir, ctrl_model_dir, scheduler, base_model) if ap_mode == sdm.MODE_txt2img: update_mode(opt) frame = update_canvas(KEY_IMAGE, param[KEY_OUTPATH]) process_path = sdt.get_process_path(param[KEY_OUTPATH]) frame_pros = update_canvas(KEY_PROS_IMAGE, process_path, sz = CANVAS_SIZE_S) set_enb_dis(False) # KEY_INPUT_IMAGE if event == KEY_INPUT_IMAGE + '_click_on': logger.debug(f'{event}') set_enb_dis(True) def_file = param[KEY_INPUTPATH] if os.path.isfile(def_file): imgfile = my_thumbnail.image_dialog(def_file, 'Image file select', my_thumbnail.DEF_THEME, 10, 4, ret = '', logger = logger) if os.path.isfile(imgfile): logger.debug(f'Input select: {imgfile}') param[KEY_INPUTPATH] = image_path = imgfile update_mode(opt) if ap_mode == sdm.MODE_txt2img and param[KEY_IP_IMGPATH] == '': param[KEY_IP_IMGPATH] = ip_image_path = imgfile # IP-Adapter を 入力画像 window[KEY_IP_IMGPATH].update(param[KEY_IP_IMGPATH]) set_enb_dis(False) # Extention 選択 if event == KEY_EXTENTION: logger.debug(f'{event} {values[KEY_EXTENTION]}') param[KEY_EXTENTION] = opt.ext = extention = values[KEY_EXTENTION] # モード選択 if event == KEY_APMODE: ap_mode = values[KEY_APMODE] logger.debug(f'{event} {ap_mode}') change_mode(opt, ap_mode) # スケジューラー選択 if event == KEY_SCHEDULER: logger.debug(f'{event} {values[KEY_SCHEDULER]}') param[KEY_SCHEDULER] = opt.scheduler = pl.scheduler = values[KEY_SCHEDULER] # シード値セット・ボタン if event == KEY_SEED_SET: logger.debug(f'{event}') window[KEY_SEED_INPUT].update(param[KEY_SEED]) # シード値クリア・ボタン if event == KEY_SEED_CLR: logger.debug(f'{event}') window[KEY_SEED_INPUT].update('-1') # IP-Adapter 画像選択ボタン if event == KEY_IP_IMGPATH_SEL: logger.debug(f'{event}') set_enb_dis(True) imgfile = my_dialog.select_image_file(initdir=os.path.dirname(param[KEY_INPUTPATH])) param[KEY_IP_IMGPATH] = ip_image_path = imgfile logger.debug(f'IP-Adapter select: {imgfile}') window[KEY_IP_IMGPATH].update(param[KEY_IP_IMGPATH]) set_enb_dis(False) # 画像選択ボタン if event == KEY_IMAGE_SEL: logger.debug(f'{event}') set_enb_dis(True) imgfile = my_dialog.select_image_file(initdir=os.path.dirname(param[KEY_INPUTPATH])) if len(imgfile) > 0: logger.debug(f'Input select: {imgfile}') param[KEY_INPUTPATH] = image_path = imgfile update_mode(opt) if ap_mode == sdm.MODE_txt2img and param[KEY_IP_IMGPATH] == '': param[KEY_IP_IMGPATH] = ip_image_path = imgfile # IP-Adapter を 入力画像 window[KEY_IP_IMGPATH].update(param[KEY_IP_IMGPATH]) set_enb_dis(False) # Model ボタン if event == KEY_MODELSEL: logger.debug(f'{event}') set_enb_dis(True) s0 = param[KEY_MODEL] id = s0.find('/') s1 = '/' + s0[:id] if id > 0 else '' s2 = param[KEY_MODEL_DIR] + s1 ttl = 'モデルファイルを選択' types = [("Model file", ".safetensors .pt"), ("model", ".safetensors"), ("pt file", ".pt") ] filename = my_dialog.file_dialog(ttl, types, s2) if len(filename) > 0: s3 = os.path.basename(filename) # ファイル名 s4 = os.path.dirname(filename) # ディレクトリ名 id = s4.find('SD1.5') if id >= 0 and id == len(s4) - 5: # SD1.5 s5 = s4[:id - 1] s3 = 'SD1.5/' + s3 else: # SDXL s5 = s4 base_model = s3 s6 = os.path.abspath(param[KEY_MODEL_DIR]) s7 = s6.replace('\\', '/') if s7 == s5: # モデルディレクトリは変更不可 param[KEY_MODEL] = opt.model = base_model window[KEY_MODEL].update(param[KEY_MODEL]) logger.debug(f'{param[KEY_MODEL_DIR]}/{param[KEY_MODEL]}') else: my_dialog.warning_dialog(ttl, 'モデルディレクトリは変更できません') set_enb_dis(False) # Genarate ボタン if event == KEY_GENERATE: logger.debug(f'{event}') set_enb_dis(True) get_paramlist(window, values, param) frame = msg_out_canvas(KEY_IMAGE, frame, 'Generating ...', (240,0,0)) window[KEY_OUTPATH].update('') window[KEY_PROMPT].update(param[KEY_PROMPT]) window[KEY_SEED].update(param[KEY_SEED]) window[KEY_NEG_PROMPT].update(param[KEY_NEG_PROMPT]) logger.info(f' {sdt.MAGENTA}** mode <{ap_mode}> {pros_sel} **{sdt.NOCOLOR}') new_make_f = True # Reset ボタン if event == KEY_RESET: logger.debug(f'{event}') opt.scheduler = '' opt.image_path = '' opt.prompt = '' opt.neg_prompt = '' sdm.set_initilal(opt) param[KEY_SCHEDULER] = pl.scheduler = opt.scheduler param[KEY_STEP] = opt.step param[KEY_INPUTPATH] = image_path = opt.image_path param[KEY_PROMPT_JP] = opt.prompt param[KEY_NEG_PROMPT_JP] = opt.neg_prompt window[KEY_SCHEDULER].update(param[KEY_SCHEDULER]) window[KEY_STEP].update(param[KEY_STEP]) window[KEY_INPUTPATH].update(param[KEY_INPUTPATH]) window[KEY_PROMPT_JP].update(param[KEY_PROMPT_JP]) window[KEY_NEG_PROMPT_JP].update(param[KEY_NEG_PROMPT_JP]) frame_input = update_canvas(KEY_INPUT_IMAGE, param[KEY_INPUTPATH]) frame = update_canvas(KEY_IMAGE, '') frame_pros = update_canvas(KEY_PROS_IMAGE, '', sz = CANVAS_SIZE_S) param[KEY_EXTENTION] = opt.ext = extention = '' window[KEY_EXTENTION].update(param[KEY_EXTENTION]) # ベースモデルの初期化 pl.select_mode(ap_mode, pl.device, pl.model_dir, pl.ctrl_model_dir, pl.scheduler) param[KEY_MODEL] = opt.model_path = pl.base_model param[KEY_CTRL_MODEL] = opt.ctrl_model_path = pl.ctrl_model window[KEY_MODEL].update(param[KEY_MODEL]) # ウィンドウ終了処理 window.close() # main関数エントリーポイント(実行開始) if __name__ == "__main__": parser = sdt.parse_args(None, opt_list) opt = parser.parse_args() set_pros_hedder('sd_101') # アプリケーション・ログ設定 module = os.path.basename(__file__) module_name = os.path.splitext(module)[0] logger = my_logging.get_module_logger_sel(module_name, int(opt.log)) pl = sdm.make_pipeline(opt) if pl is None: logger.info(f'{sdt.RED}Invalid mode name !!{sdt.NOCOLOR}') exit(0) sdt.display_info(opt, title) main(opt, pl, logger) logger.info('\nFinished.\n')※ 上記ソースコードは表示の都合上、半角コード '}' が 全角 '}'になっていることに注意
python sd_mask.py
(sd_test) python sd_mask.py Stable Diffusion with diffusers (sd_mask) Ver 0.01: Starting application... --image_path : sample2/dog.png --log : 3 result_file: D:/anaconda_win/workspace_3/sd_test/images_work/dog_mask.png Finished.
ファイル名:'実行ディレクトリ'/images_work/'入力画像ファイル名'_mask.'入力画像ファイルの拡張子'~
機能 | 戻り値 | 関数 |
マスク作成 | - | mask_paint(image_path, work_path, chenge = True, logger = None) |
# -*- coding: utf-8 -*- ##-------------------------------------------------- ## Stable Diffusion with diffusers Ver 0.01 ## ## 2025.08.06 Masahiro Izutsu ##-------------------------------------------------- ## sd_mask.py ## Ver 0.00 2025.08.06 xmask.py ## Ver 0.01 2025.08.06 sd_mask.py マスク描画 import warnings warnings.simplefilter('ignore') # インポート&初期設定 import os import argparse import cv2 import numpy as np import PySimpleGUI as sg import my_logging import my_dialog import my_thumbnail import sd_tools as sdt # 定数定義 DEF_THEME = 'BlueMono' CANVAS_SIZE = 512 KEY_CANVAS = 'CANVAS' def_image_path = 'images/heron.jpg' # タイトル title = 'Stable Diffusion with diffusers (sd_mask) Ver 0.01' # コマンドライン・オプション (argparse) 名前/初期値/ヘルプ opt_list = [ ['image_path', def_image_path , 'path to source image file'], ['log', '3', 'Log level(-1/0/1/2/3/4/5) Default value is \'3\''], ] # マスク作成 def mask_paint(image_path, work_path, chenge = True, logger = None): x_size = 0 # 入力画像の横幅 y_size = 0 # 入力画像の高さ def update_canvas(frame, frame_bak, imgdisp_f): if imgdisp_f: brend_img = cv2.addWeighted(frame_bak, 0.7, frame, 0.3, 0) img = cv2.imencode('.png', brend_img)[1].tobytes() else: img = cv2.imencode('.png', frame)[1].tobytes() window[KEY_CANVAS].update(img) def image_read_with_mask(filepath): msk_image = None image = image_read(filepath) src_path, mask_path = sdt.get_source_mask_path(filepath) if os.path.isfile(mask_path): msk_image = image_read(mask_path) return image, msk_image def image_read(filepath): global x_size, y_size image = cv2.imread(filepath) y_size, x_size = image.shape[:2] if x_size != CANVAS_SIZE or y_size != CANVAS_SIZE: image = cv2.resize(image, dsize = (CANVAS_SIZE, CANVAS_SIZE)) return image def mask_image_save(mask_path, msk_frame): global x_size, y_size im_gray = cv2.cvtColor(msk_frame, cv2.COLOR_BGR2GRAY) if x_size != CANVAS_SIZE or y_size != CANVAS_SIZE: im_gray = cv2.resize(im_gray, dsize = (x_size, y_size)) cv2.imwrite(mask_path, im_gray) KEY_OK = '-Ok-' KEY_CANCEL = '-Cancel-' KEY_MASK_DRAW = '-MaskDraw-' KEY_MASK_CLEAR = '-MaskClear-' KEY_PEN_S = '-PenS-' KEY_PEN_M = '-PenM-' KEY_PEN_L = '-PenL-' KEY_MASK = '-Mask-' KEY_MSK_CLEAR = '-MaskAllClear-' KEY_MSK_IMAGE = '-MaskImage-' KEY_SRC_IMAGE = '-SourceImage-' # ウイジェットの禁止・許可 def set_enb_dis(disabled): window[KEY_OK].update(disabled = disabled) window[KEY_CANCEL].update(disabled = disabled) window[KEY_MSK_CLEAR].update(disabled = disabled) window[KEY_MSK_IMAGE].update(disabled = disabled) window[KEY_SRC_IMAGE].update(disabled = disabled) # ウィンドウのテーマ sg.theme(DEF_THEME) canvas = sg.Image(size = (CANVAS_SIZE, CANVAS_SIZE), key=KEY_CANVAS) # ウィンドウのレイアウト col_top = [ [canvas], ] col_radio = [ [sg.Frame("Mask:", [ [sg.Radio('Draw', group_id='mask',enable_events = True, default=True, key=KEY_MASK_DRAW), sg.Radio('Clear', group_id='mask',enable_events = True, default=False, key=KEY_MASK_CLEAR)],]), sg.Frame("Pen size:", [ [sg.Radio('S', group_id='size',enable_events = True, default=False, key=KEY_PEN_S), sg.Radio('M', group_id='size',enable_events = True, default=True, key=KEY_PEN_M), sg.Radio('L', group_id='size',enable_events = True, default=False, key=KEY_PEN_L)],]), sg.Checkbox('mask', enable_events = True, default = True, key = KEY_MASK), sg.Button('Clear', size=(4, 1), key=KEY_MSK_CLEAR), sg.Button('Mask', size=(4, 1), disabled= not chenge, key=KEY_MSK_IMAGE), sg.Button('Image', size=(4, 1), disabled= not chenge, key=KEY_SRC_IMAGE), ] ] col_btn = [ [ sg.Button('Ok', size=(8, 1), key=KEY_OK, focus=True), sg.Button('Cancel', size=(8, 1), key=KEY_CANCEL), sg.Text("", size=(1, 1)) ] ] layout = [ [sg.Column(col_top, vertical_alignment='top')], [sg.Column(col_radio, justification='l')], [sg.Column(col_btn, justification='r') ], ] # ウィンドウオブジェクトの作成 window = sg.Window(title, layout, finalize=True, return_keyboard_events=True, use_default_focus=False) # ユーザーイベントの定義 canvas.bind('<Motion>', '_motion') canvas.bind('<ButtonPress>', '_click_on') canvas.bind('<ButtonRelease>', '_click_off') canvas.bind('<Double-Button>', '_double_click') # 初期設定 pen_size = 7 pen_color = (255, 255, 255) mask_f = True imgdisp_f = True press_f = False frame = np.zeros((CANVAS_SIZE, CANVAS_SIZE, 3), np.uint8) frame[:,:,] = 0x0 frame_bak, msk_image =image_read_with_mask(image_path) if msk_image is not None: frame = msk_image update_canvas(frame, frame_bak, imgdisp_f) new_make_f = False # イベントのループ while True: event, values = window.read() if new_make_f: new_make_f = False if event == KEY_CANCEL or event == sg.WIN_CLOSED: break if event == KEY_OK: src_path, mask_path = sdt.get_source_mask_path(image_path, logger) mask_image_save(mask_path, frame) logger.info(f'result_file: {mask_path}') break if event == 'CANVAS_motion': x = canvas.user_bind_event.x y = canvas.user_bind_event.y logger.debug(f'{event} x = {x}, y = {y}, press_f = {press_f}') if press_f: cv2.circle(frame, (x, y), pen_size, pen_color, thickness=-1) update_canvas(frame, frame_bak, imgdisp_f) if event == 'CANVAS_click_on': press_f = True logger.debug(f'{event}') if event == 'CANVAS_click_off': press_f = False logger.debug(f'{event}') pass if event == 'CANVAS_double_click': logger.debug(f'{event}') if event == KEY_MASK_DRAW: pen_color = (255, 255, 255) logger.debug(f'{event} pen_color = {pen_color}') if event == KEY_MASK_CLEAR: pen_color = (0, 0, 0) logger.debug(f'{event} pen_color = {pen_color}') if event == KEY_PEN_S: pen_size = 3 logger.debug(f'{event} pen_color = {pen_color}') if event == KEY_PEN_M: pen_size = 7 logger.debug(f'{event} pen_color = {pen_color}') if event == KEY_PEN_L: pen_size = 13 logger.debug(f'{event} pen_color = {pen_color}') if event == KEY_MASK: imgdisp_f = not imgdisp_f logger.debug(f'{event} imgdisp_f = {imgdisp_f}') update_canvas(frame, frame_bak, imgdisp_f) if event == KEY_MSK_CLEAR: logger.debug(f'{event}') frame[:,:,] = 0x0 update_canvas(frame, frame_bak, imgdisp_f) if event == KEY_MSK_IMAGE: set_enb_dis(True) fpath = my_thumbnail.image_dialog(file_path=work_path, title='Mask Image', theme=my_thumbnail.DEF_THEME, xn=10, yn=4, thumb_size=128, gap=4, logger=logger) if len(fpath) > 0 and os.path.isfile(fpath): frame = image_read(fpath) update_canvas(frame, frame_bak, imgdisp_f) set_enb_dis(False) logger.debug(f'{event} mask_path = {fpath}') if event == KEY_SRC_IMAGE: set_enb_dis(True) fpath = my_dialog.select_image_file(initdir=os.path.dirname(image_path)) if len(fpath) > 0 and os.path.isfile(fpath): frame_bak, msk_image =image_read_with_mask(fpath) if msk_image is not None: frame = msk_image image_path = fpath update_canvas(frame, frame_bak, imgdisp_f) set_enb_dis(False) logger.debug(f'{event} image_path = {image_path}') # ウィンドウ終了処理 window.close() # ** main関数 ** def main(opt, logger): image_path = sdt._get_source_image_path(opt, logger) work_path = sdt.get_work_path(logger) # 出力フォルダ os.makedirs(work_path, exist_ok = True) mask_paint(image_path, work_path, True, logger) return # main関数エントリーポイント(実行開始) if __name__ == "__main__": parser = sdt.parse_args(None, opt_list) opt = parser.parse_args() # アプリケーション・ログ設定 module = os.path.basename(__file__) module_name = os.path.splitext(module)[0] logger = my_logging.get_module_logger_sel(module_name, int(opt.log)) sdt.display_info(opt, title) main(opt, logger) logger.info('\nFinished.\n')※ 上記ソースコードは表示の都合上、半角コード '}' が 全角 '}'になっていることに注意
python sd_canny.py
(sd_test) PS > python sd_canny.py Stable Diffusion with diffusers (sd_canny) Ver 0.01: Starting application... --image_path : images/vermeer.png --log : 3 canny_file: D:/anaconda_win/workspace_3/sd_test/images_work/vermeer_canny.png Finished.
ファイル名:'実行ディレクトリ'/images_work/'入力画像ファイル名'_canny.'入力画像ファイルの拡張子'~
機能 | 戻り値 | 関数 |
canny 画像作成 | - | canny_process(image_path, chenge = True, logger = None) |
# -*- coding: utf-8 -*- ##-------------------------------------------------- ## Stable Diffusion with diffusers Ver 0.01 ## ## 2025.08.09 Masahiro Izutsu ##-------------------------------------------------- ## sd_canny.py ## Ver 0.00 2025.08.06 xmask.py ## Ver 0.01 2025.08.09 sd_canny.py canny 処理 import warnings warnings.simplefilter('ignore') # インポート&初期設定 import os import argparse import cv2 import numpy as np import PySimpleGUI as sg import my_logging import my_dialog import my_thumbnail import sd_tools as sdt # 定数定義 DEF_THEME = 'BlueMono' CANVAS_SIZE = 512 KEY_CANVAS_IMAG = 'CANVAS_imag' KEY_CANVAS_PROS = 'CANVAS_pros' def_image_path = 'images/vermeer.png' def_result_image = 'results/sd_canny.png' # タイトル title = 'Stable Diffusion with diffusers (sd_canny) Ver 0.01' # コマンドライン・オプション (argparse) 名前/初期値/ヘルプ opt_list = [ ['image_path', def_image_path , 'path to source image file'], ['log', '3', 'Log level(-1/0/1/2/3/4/5) Default value is \'3\''], ] # canny 画像作成 def canny_process(image_path, chenge = True, logger = None): x_size = 0 # 入力画像の横幅 y_size = 0 # 入力画像の高さ def update_canvas(key, frame): img = cv2.imencode('.png', frame)[1].tobytes() window[key].update(img) def update_canny(frame, low_threshold, high_threshold): image_canny = cv2.Canny(frame, low_threshold, high_threshold) update_canvas(KEY_CANVAS_PROS, image_canny) return image_canny def image_read(filepath): global x_size, y_size image = cv2.imread(filepath) y_size, x_size = image.shape[:2] if x_size != CANVAS_SIZE or y_size != CANVAS_SIZE: image = cv2.resize(image, dsize = (CANVAS_SIZE, CANVAS_SIZE)) return image def gray_image_save(path, image): global x_size, y_size im_gray = image if image.ndim == 2 else cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) if x_size != CANVAS_SIZE or y_size != CANVAS_SIZE: im_gray = cv2.resize(im_gray, dsize = (x_size, y_size)) cv2.imwrite(path, im_gray) # canny パラメータ自動計算(精度はもう一つなので使用せず) def _get_threshold(image): med_val = np.median(image) sigma = 0.33 # 0.33 min_val = int(max(0, (1.0 - sigma) * med_val)) max_val = int(max(255, (1.0 + sigma) * med_val)) return min_val, max_val KEY_OK = '-Ok-' KEY_CANCEL = '-Cancel-' KEY_LOW_THR = '-Low_threshold-' KEY_HIGH_THR = '-High_threshold-' KEY_SRC_IMAGE = '-SourceImage-' # ウイジェットの禁止・許可 def set_enb_dis(disabled): window[KEY_OK].update(disabled = disabled) window[KEY_CANCEL].update(disabled = disabled) window[KEY_LOW_THR].update(disabled = disabled) window[KEY_HIGH_THR].update(disabled = disabled) window[KEY_SRC_IMAGE].update(disabled = disabled) low_threshold = 100 high_threshold = 200 # ウィンドウのテーマ sg.theme(DEF_THEME) canvas_img = sg.Image(size = (CANVAS_SIZE, CANVAS_SIZE), key=KEY_CANVAS_IMAG) canvas_prs = sg.Image(size = (CANVAS_SIZE, CANVAS_SIZE), key=KEY_CANVAS_PROS) # ウィンドウのレイアウト col_left = [ [canvas_img], [sg.Text("", size=(50, 1)), sg.Button('Image', size=(8, 1), disabled= not chenge, key=KEY_SRC_IMAGE)], ] col_right = [ [canvas_prs], [sg.Text("low_threshold", size=(14, 1)), sg.Slider((0, 360), int(low_threshold), 1, orientation='h', size=(42, 5), key=KEY_LOW_THR, enable_events=True)], [sg.Text("high_threshold", size=(14, 1)), sg.Slider((0, 360), int(high_threshold), 1, orientation='h', size=(42, 5), key=KEY_HIGH_THR, enable_events=True)], ] col_btn = [ [ sg.Button('Ok', size=(8, 1), key=KEY_OK, focus=True), sg.Button('Cancel', size=(8, 1), key=KEY_CANCEL), sg.Text("", size=(1, 1)) ] ] layout = [ [sg.Column(col_left, vertical_alignment='top'), sg.Column(col_right, vertical_alignment='top')], [sg.Column(col_btn, justification='r') ], ] # ウィンドウオブジェクトの作成 window = sg.Window(title, layout, finalize=True, return_keyboard_events=True, use_default_focus=False) # ユーザーイベントの定義 # 初期設定 frame_prs = np.zeros((CANVAS_SIZE, CANVAS_SIZE, 3), np.uint8) frame_prs[:,:,] = 0x0 update_canvas(KEY_CANVAS_PROS, frame_prs) frame =image_read(image_path) update_canvas(KEY_CANVAS_IMAG, frame) frame_prs = update_canny(frame, low_threshold, high_threshold) new_make_f = False # イベントのループ while True: event, values = window.read() if new_make_f: new_make_f = False if event == KEY_CANCEL or event == sg.WIN_CLOSED: break if event == KEY_OK: canny_path = sdt.get_canny_path(image_path, logger) gray_image_save(canny_path, frame_prs) logger.info(f'canny_file: {canny_path}') break if event == KEY_LOW_THR: low_threshold = int(values[KEY_LOW_THR]) if low_threshold > high_threshold: high_threshold = low_threshold window[KEY_HIGH_THR].update(high_threshold) frame_prs = update_canny(frame, low_threshold, high_threshold) logger.debug(f'{event} low_threshold = {low_threshold}') if event == KEY_HIGH_THR: high_threshold = int(values[KEY_HIGH_THR]) if low_threshold > high_threshold: low_threshold = high_threshold window[KEY_LOW_THR].update(low_threshold) frame_prs = update_canny(frame, low_threshold, high_threshold) logger.debug(f'{event} high_threshold = {high_threshold}') if event == KEY_SRC_IMAGE: set_enb_dis(True) fpath = my_dialog.select_image_file(initdir=os.path.dirname(image_path)) if len(fpath) > 0 and os.path.isfile(fpath): frame =image_read(fpath) image_path = fpath update_canvas(KEY_CANVAS_IMAG, frame) frame_prs = update_canny(frame, low_threshold, high_threshold) set_enb_dis(False) logger.debug(f'{event} image_path = {image_path}') # ウィンドウ終了処理 window.close() # ** main関数 ** def main(opt, logger): image_path = sdt._get_source_image_path(opt, logger) work_path = sdt.get_work_path(logger) # 出力フォルダ os.makedirs(work_path, exist_ok = True) canny_process(image_path, True, logger) return # main関数エントリーポイント(実行開始) if __name__ == "__main__": parser = sdt.parse_args(None, opt_list) opt = parser.parse_args() # アプリケーション・ログ設定 module = os.path.basename(__file__) module_name = os.path.splitext(module)[0] logger = my_logging.get_module_logger_sel(module_name, int(opt.log)) sdt.display_info(opt, title) main(opt, logger) logger.info('\nFinished.\n')※ 上記ソースコードは表示の都合上、半角コード '}' が 全角 '}'になっていることに注意
機能 | 戻り値 | 関数 |
結果を CSV ファイルに残す | - | result_csv(csvfile, param, logger) |
結果ログからパラメータを取得 | bool | read_result_csv(csvfile, param, logger, imgfile = '0', pros_f = False) |
動的パラメータの出力 | - | logout_data(param, logger) |
生成画選択ダイアログ | image_file | image_dialog(param, csvfile, file_path, title='', theme=DEF_THEME, xn=10, yn=3, thumb_size=128, gap=4, file_ext=my_thumbnail.IMAGE_EXT, logger=None) |
# -*- coding: utf-8 -*- ##-------------------------------------------------- ## sd_test basic tools for results Ver 0.09 ## ## 2025.08.02 Masahiro Izutsu ##-------------------------------------------------- ## sd_results.py ## Ver 0.00 2025.07.17 Trial version ## Ver 0.05 2025.07.21 sd_100.py 統合版対応 ## Ver 0.06 2025.07.27 sd_081 IP-Adapter 対応 ## Ver 0.07 2025.08.02 sd_051 ADetailer 対応 ## Ver 0.08 2025.08.12 sd_053 統合版 対応 ## Ver 0.09 2025.08.19 sd_101 統合版 対応 import warnings warnings.simplefilter('ignore') # タイトル title = 'sd_test basic tools Ver 0.09' # インポート&初期設定 import os import cv2 import csv import PySimpleGUI as sg import tkinter as tk from PIL import Image, ImageTk import my_logging import my_csv import my_thumbnail import sd_tools as sdt # 定数定義 DEF_THEME = 'GrayGrayGray' KEY_SEED_INPUT = '-Seed_input-' SKEY_RESULT_PATH = '-result_path-' SKEY_RESULT_FILE = '-result_file-' SKEY_DEF_OUTPATH = '-default_image-' # csv save KEY_PROS_SEL = '--Proscess--' KEY_OUTPATH = '-Output-' KEY_SEED = '-Seed-' KEY_PROMPT_JP = '-PromptJP-' KEY_PROMPT = '-Prompt-' KEY_INPUTPATH = '-Input-' KEY_MAXSIZE = '-MaxSize-' KEY_STEP = '-Step-' KEY_SCALE = '-Scale-' KEY_STRENGTH = '-strength-' KEY_MODEL_DIR = '-model_dir-' KEY_MODEL = '-Model-' KEY_CTRL_IMGPATH = '--ctrl_image_path--' KEY_CTRL_DIR = '-ctrl_model_dir-' KEY_CTRL_MODEL = '-ctrl_model-' KEY_WIDTH = '--Image_width--' KEY_HEIGHT = '--Image_height--' KEY_IMG_SCALE = '--Image_scale--' KEY_CC_SCALE = '--Control_scale--' KEY_DEVICE = '-Device-' KEY_TIME = '-Time-' KEY_LOOP = '-Loop-' KEY_NEG_PROMPT_JP = '-Negative_PromptJP-' KEY_NEG_PROMPT = '-Negative_Prompt-' KEY_IP_IMGPATH = '-Ip_image_path-' KEY_IP_SCALE = '-Ip_scale-' KEY_EXTENTION = '-Extention-' KEY_SCHEDULER = '-Scheduler-' KEY_APMODE = '-ApMode-' # 共通データ param = {} param[KEY_PROS_SEL] = '' param[KEY_OUTPATH] = './sd_results3/sd_00000_0.png' param[KEY_PROMPT_JP] = '雪の中の場面にする' param[KEY_PROMPT] = 'Make it a scene in the snow' param[KEY_INPUTPATH] = 'images/sd_040_test.png' param[KEY_MODEL_DIR] = '' param[KEY_MODEL] = 'timbrooks/instruct-pix2pix' param[KEY_MAXSIZE] = 0 param[KEY_SEED] = 0 param[KEY_SEED_INPUT] = 0 param[KEY_STEP] = 20 param[KEY_SCALE] = 7.0 param[KEY_STRENGTH] = 0.5 param[KEY_DEVICE] = 'cuda' param[KEY_LOOP] = 1 param[KEY_TIME] = '00:00:09' param[KEY_CTRL_IMGPATH] = '' param[KEY_CTRL_DIR] = '' param[KEY_CTRL_MODEL] = '' param[KEY_WIDTH] = 512 param[KEY_HEIGHT] = 512 param[KEY_IMG_SCALE] = 1.5 param[KEY_CC_SCALE] = 1.0 param[KEY_NEG_PROMPT_JP] = '最悪の品質、おかしい人体構造' param[KEY_NEG_PROMPT] = 'Worst quality, funny body structure' param[SKEY_RESULT_PATH] = '' param[SKEY_RESULT_FILE] = '' param[SKEY_DEF_OUTPATH] = '' param[KEY_IP_IMGPATH] = '' param[KEY_IP_SCALE] = 0.5 param[KEY_EXTENTION] = '' param[KEY_SCHEDULER] = '' param[KEY_APMODE] = '' opt_list = [ ['pros_sel','','sd_resilts'], # 0 ] # 結果を CSV ファイルに残す def result_csv(csvfile, param, logger): my_csv_treatment = my_csv.CSVtreatment(csvfile, 'utf_8_sig') s = my_csv_treatment.read_csv() if len(s) ==0: data = [] data.append(KEY_PROS_SEL) data.append(KEY_OUTPATH) data.append(KEY_SEED) data.append(KEY_PROMPT_JP) data.append(KEY_PROMPT) data.append(KEY_INPUTPATH) data.append(KEY_MAXSIZE) data.append(KEY_STEP) data.append(KEY_SCALE) data.append(KEY_STRENGTH) data.append(KEY_MODEL_DIR) data.append(KEY_MODEL) data.append(KEY_CTRL_IMGPATH) data.append(KEY_CTRL_DIR) data.append(KEY_CTRL_MODEL) data.append(KEY_WIDTH) data.append(KEY_HEIGHT) data.append(KEY_IMG_SCALE) data.append(KEY_CC_SCALE) data.append(KEY_DEVICE) data.append(KEY_TIME) data.append(KEY_LOOP) data.append(KEY_NEG_PROMPT_JP) data.append(KEY_NEG_PROMPT) data.append(KEY_IP_IMGPATH) data.append(KEY_IP_SCALE) data.append(KEY_EXTENTION) data.append(KEY_SCHEDULER) data.append(KEY_APMODE) my_csv_treatment.write_csv(data) logger.debug(data) data = [] data.append(param[KEY_PROS_SEL]) data.append(param[KEY_OUTPATH]) data.append(str(param[KEY_SEED])) data.append(param[KEY_PROMPT_JP]) data.append(param[KEY_PROMPT]) data.append(param[KEY_INPUTPATH]) data.append(str(param[KEY_MAXSIZE])) data.append(str(param[KEY_STEP])) data.append(str(param[KEY_SCALE])) data.append(str(param[KEY_STRENGTH])) data.append(param[KEY_MODEL_DIR]) data.append(param[KEY_MODEL]) data.append(param[KEY_CTRL_IMGPATH]) data.append(param[KEY_CTRL_DIR]) data.append(param[KEY_CTRL_MODEL]) data.append(param[KEY_WIDTH]) data.append(param[KEY_HEIGHT]) data.append(param[KEY_IMG_SCALE]) data.append(param[KEY_CC_SCALE]) data.append(param[KEY_DEVICE]) data.append(param[KEY_TIME]) data.append(param[KEY_LOOP]) data.append(param[KEY_NEG_PROMPT_JP]) data.append(param[KEY_NEG_PROMPT]) data.append(param[KEY_IP_IMGPATH]) data.append(param[KEY_IP_SCALE]) data.append(param[KEY_EXTENTION]) data.append(param[KEY_SCHEDULER]) data.append(param[KEY_APMODE]) my_csv_treatment.append_csv(data) logger.debug(data) # 結果ログからパラメータを取得 def read_result_csv(csvfile, param, logger, imgfile = '0', pros_f = False): def _get_val(dd, key): return dd.get(key, '') def _get_val_int(dd, key): val = dd.get(key, '') if val != '': val = int(val) return val def _get_val_float(dd, key): val = dd.get(key, '') if val != '': val = float(val) return val param[KEY_PROS_SEL] = sdt._get_process_name(opt_list) bf = False dds = {} dd = {} try: with open(csvfile, 'r', encoding = 'utf_8_sig') as f: reader = csv.DictReader(f) if imgfile == '0': # 最初の行 for dd in reader: bf = True break elif imgfile == '1': # 同じ処理の最後の行 for dd in reader: if param[KEY_PROS_SEL] == dd.get(KEY_PROS_SEL, 'not found'): dds = dd.copy() bf = True continue if dds == {}: bf = False else: dd = dds.copy() else: # 出力ファイルの行 img_path = os.path.basename(imgfile) for dd in reader: out_path = os.path.basename(dd[KEY_OUTPATH]) if out_path == img_path and (pros_f or param[KEY_PROS_SEL] == dd.get(KEY_PROS_SEL, 'not found')): bf = True break except FileNotFoundError as e: pass except csv.Error as e: logger.debug(e) if bf: param[KEY_PROS_SEL] = _get_val(dd, KEY_PROS_SEL) param[KEY_OUTPATH] = _get_val(dd, KEY_OUTPATH) param[KEY_SEED_INPUT] = _get_val_int(dd, KEY_SEED) param[KEY_PROMPT_JP] = _get_val(dd, KEY_PROMPT_JP) param[KEY_PROMPT] = _get_val(dd, KEY_PROMPT) param[KEY_INPUTPATH] = _get_val(dd, KEY_INPUTPATH) param[KEY_MAXSIZE] = _get_val_int(dd, KEY_MAXSIZE) param[KEY_STEP] = _get_val_int(dd, KEY_STEP) param[KEY_SCALE] = _get_val_float(dd, KEY_SCALE) param[KEY_STRENGTH] = _get_val_float(dd, KEY_STRENGTH) param[KEY_MODEL_DIR] = _get_val(dd, KEY_MODEL_DIR) param[KEY_MODEL] = _get_val(dd, KEY_MODEL) param[KEY_CTRL_IMGPATH] = _get_val(dd, KEY_CTRL_IMGPATH) param[KEY_CTRL_DIR] = _get_val(dd, KEY_CTRL_DIR) param[KEY_CTRL_MODEL] = _get_val(dd, KEY_CTRL_MODEL) param[KEY_WIDTH] = _get_val_int(dd, KEY_WIDTH) param[KEY_HEIGHT] = _get_val_int(dd, KEY_HEIGHT) param[KEY_IMG_SCALE] = _get_val_float(dd, KEY_IMG_SCALE) param[KEY_CC_SCALE] = _get_val_float(dd, KEY_CC_SCALE) param[KEY_NEG_PROMPT_JP] = _get_val(dd, KEY_NEG_PROMPT_JP) param[KEY_NEG_PROMPT] = _get_val(dd, KEY_NEG_PROMPT) param[KEY_IP_IMGPATH] = _get_val(dd, KEY_IP_IMGPATH) param[KEY_IP_SCALE] = _get_val_float(dd, KEY_IP_SCALE) param[KEY_EXTENTION] = _get_val(dd, KEY_EXTENTION) param[KEY_SCHEDULER] = _get_val(dd, KEY_SCHEDULER) param[KEY_APMODE] = _get_val(dd, KEY_APMODE) logout_data(param, logger) elif os.path.isfile(imgfile): param[KEY_OUTPATH] = imgfile param[KEY_SEED_INPUT] = sdt.path2seed(imgfile) # ファイル名に含まれるシード値 logout_data(param, logger) elif bf and dd.get(KEY_PROS_SEL, '') == '': # csvファイルのバージョンチェック os.remove(csvfile) return bf # 動的パラメータの出力 def logout_data(param, logger): logger.debug('---------------------------------------------') logger.debug(param[KEY_OUTPATH]) logger.debug(param[KEY_SEED]) logger.debug(param[KEY_PROMPT_JP]) logger.debug(param[KEY_PROMPT]) logger.debug(param[KEY_INPUTPATH]) logger.debug(param[KEY_MAXSIZE]) logger.debug(param[KEY_STEP]) logger.debug(param[KEY_SCALE]) logger.debug(param[KEY_STRENGTH]) logger.debug(param[KEY_MODEL_DIR]) logger.debug(param[KEY_MODEL]) logger.debug(param[KEY_CTRL_IMGPATH]) logger.debug(param[KEY_CTRL_DIR]) logger.debug(param[KEY_CTRL_MODEL]) logger.debug(param[KEY_WIDTH]) logger.debug(param[KEY_HEIGHT]) logger.debug(param[KEY_IMG_SCALE]) logger.debug(param[KEY_CC_SCALE]) logger.debug(param[KEY_DEVICE]) logger.debug(param[KEY_NEG_PROMPT_JP]) logger.debug(param[KEY_NEG_PROMPT]) logger.debug(param[KEY_IP_IMGPATH]) logger.debug(param[KEY_IP_SCALE]) logger.debug(param[KEY_EXTENTION]) logger.debug(param[KEY_SCHEDULER]) logger.debug(param[KEY_APMODE]) logger.debug('---------------------------------------------') # 生成画選択ダイアログ def image_dialog(param, csvfile, file_path, title='', theme=DEF_THEME, xn=10, yn=3, thumb_size=128, gap=4, file_ext=my_thumbnail.IMAGE_EXT, logger=None): param_save = param.copy() # パラメータ push pros_sel = sdt._get_process_name(sdt.opt_list) # pros_sel 処理方法 image_file = '' br = read_result_csv(csvfile, param, logger, file_path, True) if not br: print(sdt.RED + '生成画像がありません!' + sdt.NOCOLOR) return image_file KEY_IMG_SEL = '-ImgSel-' KEY_TXTIMG = '-TextImg-' KEY_OK = '-Ok-' KEY_CANCEL = '-Cancel-' KEY_PAGE = '-Page-' KEY_PAGEUP = '-PageUp-' KEY_PAGEDOWN = '-PageDown-' def update_param(file_path, bok): br = read_result_csv(csvfile, param, logger, file_path, True) if br: cc = cf if pros_sel == param[KEY_PROS_SEL] else '#880000' window[KEY_PROS_SEL].update(param[KEY_PROS_SEL], text_color=cc) window[KEY_PROMPT_JP].update(param[KEY_PROMPT_JP]) window[KEY_PROMPT].update(param[KEY_PROMPT]) window[KEY_INPUTPATH].update(param[KEY_INPUTPATH]) window[KEY_MODEL_DIR].update(param[KEY_MODEL_DIR]) window[KEY_MODEL].update(param[KEY_MODEL]) window[KEY_MAXSIZE].update(param[KEY_MAXSIZE]) window[KEY_SEED].update(param[KEY_SEED_INPUT]) # シード値 window[KEY_STEP].update(param[KEY_STEP]) window[KEY_SCALE].update(param[KEY_SCALE]) window[KEY_STRENGTH].update(param[KEY_STRENGTH]) window[KEY_DEVICE].update(param[KEY_DEVICE]) window[KEY_TIME].update(param[KEY_TIME]) window[KEY_CTRL_IMGPATH].update(param[KEY_CTRL_IMGPATH]) window[KEY_CTRL_DIR].update(param[KEY_CTRL_DIR]) window[KEY_CTRL_MODEL].update(param[KEY_CTRL_MODEL]) window[KEY_WIDTH].update(param[KEY_WIDTH]) window[KEY_HEIGHT].update(param[KEY_HEIGHT]) window[KEY_IMG_SCALE].update(param[KEY_IMG_SCALE]) window[KEY_CC_SCALE].update(param[KEY_CC_SCALE]) window[KEY_NEG_PROMPT_JP].update(param[KEY_NEG_PROMPT_JP]) window[KEY_NEG_PROMPT].update(param[KEY_NEG_PROMPT]) window[KEY_IP_IMGPATH].update(param[KEY_IP_IMGPATH]) window[KEY_IP_SCALE].update(param[KEY_IP_SCALE]) window[KEY_EXTENTION].update(param[KEY_EXTENTION]) window[KEY_SCHEDULER].update(param[KEY_SCHEDULER]) window[KEY_APMODE].update(param[KEY_APMODE]) if bok: window[KEY_OK].update(disabled = pros_sel != param[KEY_PROS_SEL]) # Thumbnail オブジェクト作成 Thumb = my_thumbnail.Thumbnail(xn, yn, thumb_size, gap, file_ext) image_file = file_path frame = Thumb.initialize(image_file, False) image_file = Thumb.get_sel_filepath() # ウィンドウのテーマ sg.theme(theme) cf = '#008800' # テキストの色 cb = 'LightSteelBlue1' # 背景の色 canvas = sg.Image(size = Thumb.get_canvas_size(), key='CANVAS') # ウィンドウのレイアウト col_top = [ [sg.Text(image_file, background_color='White', size=(32, 1), key = KEY_IMG_SEL), sg.Text('', size=(32, 1), key = KEY_TXTIMG)], [canvas], ] col_btn = [ [ sg.Text("Page: 1/1", size=(12, 1), key=KEY_PAGE), sg.Button('▼', size=(2, 1), key=KEY_PAGEUP), sg.Button('▲', size=(2, 1), key=KEY_PAGEDOWN), sg.Text("", size=(4, 1)), sg.Button('Ok', size=(8, 1), key=KEY_OK, focus=True), sg.Button('Cancel', size=(8, 1), key=KEY_CANCEL), sg.Text("", size=(1, 1)) ] ] col_left = [ [sg.Text("Process", size=(14, 1)), sg.Text(param[KEY_PROS_SEL], size=(47,1), text_color=cf, background_color=cb, key=KEY_PROS_SEL)], [sg.Text("Prompt jp", size=(14, 1)), sg.Multiline(param[KEY_PROMPT_JP], size=(52,3), text_color=cf, background_color=cb, disabled=True, key=KEY_PROMPT_JP)], [sg.Text("Prompt", size=(14, 1)), sg.Multiline(param[KEY_PROMPT], size=(52,2), text_color=cf, background_color=cb, disabled=True, key=KEY_PROMPT)], [sg.Text("Negative Prompt jp", size=(14, 1)), sg.Multiline(param[KEY_NEG_PROMPT_JP], size=(52,3), text_color=cf, background_color=cb, disabled=True, key=KEY_NEG_PROMPT_JP)], [sg.Text("Negative Prompt", size=(14, 1)), sg.Multiline(param[KEY_NEG_PROMPT], size=(52,2), text_color=cf, background_color=cb, disabled=True, key=KEY_NEG_PROMPT)], ] col_center = [ [sg.Text("Model Directory", size=(14, 1)), sg.Text(param[KEY_MODEL_DIR], size=(48,1), text_color=cf, background_color=cb, key=KEY_MODEL_DIR)], [sg.Text("Model", size=(14, 1)), sg.Text(param[KEY_MODEL], size=(48,1), text_color=cf, background_color=cb, key=KEY_MODEL)], [sg.Text("Input Image", size=(14, 1)), sg.Text(param[KEY_INPUTPATH], size=(48,1), text_color=cf, background_color=cb, key=KEY_INPUTPATH)], [sg.Text("Control Image", size=(14, 1)), sg.Text(param[KEY_CTRL_IMGPATH], size=(48,1), text_color=cf, background_color=cb, key=KEY_CTRL_IMGPATH)], [sg.Text("Control Model Dir", size=(14, 1)), sg.Text(param[KEY_CTRL_DIR], size=(48,1), text_color=cf, background_color=cb, key=KEY_CTRL_DIR)], [sg.Text("Control Model", size=(14, 1)), sg.Text(param[KEY_CTRL_MODEL], size=(48,1), text_color=cf, background_color=cb, key=KEY_CTRL_MODEL)], [sg.Text("ip-adapter image", size=(14, 1)), sg.Text(param[KEY_IP_IMGPATH], size=(48,1), text_color=cf, background_color=cb, key=KEY_IP_IMGPATH)], [sg.Text("Scheduler", size=(14, 1)), sg.Text(param[KEY_SCHEDULER], size=(11,1), text_color=cf, background_color=cb, key=KEY_SCHEDULER), sg.Text("", size=(7,1)), sg.Text("Max size", size=(8, 1)), sg.Text(param[KEY_MAXSIZE], size=(11,1), text_color=cf, background_color=cb, key=KEY_MAXSIZE)], [sg.Text("Device", size=(14, 1)), sg.Text(param[KEY_DEVICE], size=(11,1), text_color=cf, background_color=cb, key=KEY_DEVICE), sg.Text("", size=(7,1)), sg.Text("Extentions", size=(8, 1)), sg.Text(param[KEY_EXTENTION], size=(11,1), text_color=cf, background_color=cb, key=KEY_EXTENTION)], [sg.Text("Time", size=(14, 1)), sg.Text(param[KEY_TIME], size=(11,1), text_color=cf, background_color=cb, key=KEY_TIME), sg.Text("", size=(7,1)), sg.Text("Mode", size=(8, 1)), sg.Text(param[KEY_APMODE], size=(11,1), text_color=cf, background_color=cb, key=KEY_APMODE)], ] col_right = [ [sg.Text("Seed Value", size=(14, 1)), sg.Text(param[KEY_SEED_INPUT], size=(11,1), text_color=cf, background_color=cb, key=KEY_SEED)], [sg.Text("Steps", size=(14, 1)), sg.Text(param[KEY_STEP], size=(11,1), text_color=cf, background_color=cb, key=KEY_STEP)], [sg.Text("Guidance Scale", size=(14, 1)), sg.Text(param[KEY_SCALE], size=(11,1), text_color=cf, background_color=cb, key=KEY_SCALE)], [sg.Text("Strength", size=(14, 1)), sg.Text(param[KEY_STRENGTH], size=(11,1), text_color=cf, background_color=cb, key=KEY_STRENGTH)], [sg.Text("Image Width", size=(14, 1)), sg.Text(param[KEY_WIDTH], size=(11,1), text_color=cf, background_color=cb, key=KEY_WIDTH)], [sg.Text("Image Height", size=(14, 1)), sg.Text(param[KEY_HEIGHT], size=(11,1), text_color=cf, background_color=cb, key=KEY_HEIGHT)], [sg.Text("Image Scale", size=(14, 1)), sg.Text(param[KEY_IMG_SCALE], size=(11,1), text_color=cf, background_color=cb, key=KEY_IMG_SCALE)], [sg.Text("Control Scale", size=(14, 1)), sg.Text(param[KEY_CC_SCALE], size=(11,1), text_color=cf, background_color=cb, key=KEY_CC_SCALE)], [sg.Text("ip-adapter Scale", size=(14, 1)), sg.Text(param[KEY_IP_SCALE], size=(11,1), text_color=cf, background_color=cb, key=KEY_IP_SCALE)], ] layout = [ [sg.Column(col_top, vertical_alignment='top')], [sg.Column(col_left, vertical_alignment='top'), sg.Column(col_center, vertical_alignment='top'), sg.Column(col_right, vertical_alignment='top')], [sg.Column(col_btn, justification='r') ], ] # ウィンドウオブジェクトの作成 window = sg.Window(title, layout, finalize=True, return_keyboard_events=True, use_default_focus=False) img = cv2.imencode('.png', frame)[1].tobytes() window['CANVAS'].update(img) # ユーザーイベントの定義 canvas.bind('<Motion>', '_motion') canvas.bind('<ButtonPress>', '_click_on') canvas.bind('<ButtonRelease>', '_click_off') canvas.bind('<Double-Button>', '_double_click') page_offset, page_max = Thumb.get_page_max() window[KEY_PAGE].update(f'Page: {page_offset + 1}/{page_max}') window[KEY_PAGEUP].update(disabled = not Thumb.check_page_up()) window[KEY_PAGEDOWN].update(disabled = not Thumb.check_page_down()) new_make_f = False # イベントのループ while True: event, values = window.read() if new_make_f: new_make_f = False if event == KEY_CANCEL or event == sg.WIN_CLOSED: for key in param.keys(): # パラメータ pop param[key] = param_save[key] image_file = '' break if event == KEY_OK: logger.debug(f'{event} {image_file}') break if event == KEY_PAGEUP: logger.debug(f'{event}') frame = Thumb.page_up() if frame is not None: window[KEY_TXTIMG].update('') img = cv2.imencode('.png', frame)[1].tobytes() window['CANVAS'].update(img) page_offset, page_max = Thumb.get_page_max() window[KEY_PAGE].update(f'Page: {page_offset + 1}/{page_max}') window[KEY_PAGEUP].update(disabled = not Thumb.check_page_up()) window[KEY_PAGEDOWN].update(disabled = not Thumb.check_page_down()) if event == KEY_PAGEDOWN: logger.debug(f'{event}') frame = Thumb.page_down() if frame is not None: window[KEY_TXTIMG].update('') img = cv2.imencode('.png', frame)[1].tobytes() window['CANVAS'].update(img) page_offset, page_max = Thumb.get_page_max() window[KEY_PAGE].update(f'Page: {page_offset + 1}/{page_max}') window[KEY_PAGEUP].update(disabled = not Thumb.check_page_up()) window[KEY_PAGEDOWN].update(disabled = not Thumb.check_page_down()) if event == 'CANVAS_motion': x = canvas.user_bind_event.x y = canvas.user_bind_event.y filename, frame = Thumb.pixel2file(x, y) window[KEY_TXTIMG].update(filename) img = cv2.imencode('.png', frame)[1].tobytes() window['CANVAS'].update(img) update_param(filename, False) if event == 'CANVAS_click_on': image_file, frame = Thumb.select_file() window[KEY_IMG_SEL].update(image_file) img = cv2.imencode('.png', frame)[1].tobytes() window['CANVAS'].update(img) logger.debug(f'{event} {image_file}') update_param(filename, True) if event == 'CANVAS_click_off': pass if event == 'CANVAS_double_click': ftype = my_thumbnail.get_file_type(image_file) if ftype== 0 or ftype == 1 or ftype == 2: sdt.image_disp(image_file, image_file) # ウィンドウ終了処理 window.close() return image_file #-----Test routine----- if __name__ == "__main__": # アプリケーション・ログ設定 module = os.path.basename(__file__) module_name = os.path.splitext(module)[0] logger = my_logging.get_module_logger_sel(module_name, 3) def_file = 'sd_results3/sd_00001_0.png' imgfile = image_dialog(param, 'sd_results3/result_100.csv', def_file, 'Results Image file select', my_thumbnail.DEF_THEME, 10, 3, logger = logger) logger.info('\nFinished.\n')※ 上記ソースコードは表示の都合上、半角コード '}' が 全角 '}'になっていることに注意
opt_list = [ ['cpu', 'store_true', 'cpu mode'], ['log', '3', 'Log level(-1/0/1/2/3/4/5) Default value is \'3\''], : ]・初期値が「''(空文字列)」の場合、パラメータは登録するが「基本情報の表示」で表示されない
機能 | 戻り値 | 関数 |
コマンド入力関連 | ||
コマンドライン・オプションの設定 | parser | parse_args(parser, pars_list): |
オプション・リストを変更する | bool | change_option(opt_list, key, val) |
コマンドライン・オプションチェック | res | is_option(opt, key) |
基本情報の表示 | - | display_info(opt, title) |
画像関連 | ||
チャネル数の取得 | int | get_image_channel(image_path) |
イメージ変換 PIL → OpenCV | image | pil2cv(image) |
pil2cv_np(image) | ||
イメージ変換 OpenCV → PIL | image | cv2pil(image) |
cv2pil_np(image) | ||
イメージファイルの表示 | - | image_disp(image_path = , dispname = , maxsize = 800, wait_s = 0) |
OpenCV イメージ 結果の保存と表示 | - | image_save(image, save_path = '', dispname = '', maxsize = 1000, wait_s = 0) |
PIL イメージ 結果の保存と表示 | - | image_save2(image, save_path = '', dispname = '', maxsize = 1000, wait_s = 0) |
コントロールイメージ作成 | image | make_inpaint_condition(image, image_mask) |
線画作成 | image | line_drawing_image(img) |
Canny 処理 (PIL) | image | canny_preprocessor(image, low_threshold, high_threshold) |
Scribble 処理 (PIL) | image | scribble_preprocessor(image, scribble = True) |
Linart 処理 (PIL) | image | lineart_preprocessor(image) |
Softedge 処理 (PIL) | image | softedge_preprocessor(image, safe = True) |
Shuffle 処理 (PIL) | image | shuffle_preprocessor(image) |
Depth 処理 (PIL) | image | depth_preprocessor(image) |
Segment 処理 (PIL) | image | seg_preprocessor(image) |
Normal_map 処理 (PIL) | image | normal_preprocessor(image) |
lineart anime 処理 (PIL) | image | anime_preprocessor(image) |
MLSD 処理 (PIL) | image | mlsd_preprocessor(image) |
Tile 処理 (PIL) | image | tile_preprocessor(image, resolution = 512) |
マスク作成 | image | mask_square(image, size) |
その他 | ||
モデルを調べる(SD1.5 モデルは SD1.5/フォルダ内にある前提) | bool | is_sd15(model) |
ポーズ・ファイルか調べる | bool | is_pose(filepath) |
ファイルパスから拡張子で選択したファイル一覧を得る | list | get_file_list_sel(dirpass, sel_ext) |
フォルダ内で連番のファイル名を得る | filenale | make_filename_by_seq(dirname, filename, seq_digit = 3, ex = '') |
生成画像のファイル名からシード値を得る | seed | path2seed(filepath) |
経過時間(秒)を hh:mm:ssフォーマットに変換 | hh:mm:ss | elapsed_time_str(seconds) |
ランダムなシード値を得る | seed | get_random_seed_value(n) |
日本語から英語に翻訳 | 英語文字列 | trans_jp2en(str) |
メモリー開放 | - | device_empty_cache(device) |
作業フォルダ名を得る | work_path | get_work_path(logger = None) |
出力ファイル名から処理ファイル名を得る | process_path | get_process_path(result_path) |
ソース・マスク画像ファイル名を得る | src_path, mask_path | get_source_mask_path(image_path, logger = None) |
ポーズ画像画像ファイル名を得る | pose_path | get_pose_path(image_path, logger = None) |
Canny 画像ファイル名を得る | canny_path | get_canny_path(image_path, logger = None) |
scribble 画像ファイル名を得る | scribble_path | get_scribble_path(image_path, logger = None) |
lineart 画像ファイル名を得る | lineart_path | get_lineart_path(image_path, logger = None) |
softedge 画像ファイル名を得る | softedge_path | get_softedge_path(image_path, logger = None) |
shuffle 画像ファイル名を得る | shuffle_path | get_shuffle_path(image_path, logger = None) |
depth 画像ファイル名を得る | depth_path | get_depth_path(image_path, logger = None) |
segment 画像ファイル名を得る | seg_path | get_seg_path(image_path, logger = None) |
normal_map 画像ファイル名を得る | normal_path | get_normal_path(image_path, logger = None) |
lineart_anime 画像ファイル名を得る | anime_path | get_anime_path(image_path, logger = None) |
MLSD 画像ファイル名を得る | mlsd_path | get_mlsd_path(image_path, logger = None) |
Tile 画像ファイル名を得る | tile_path | get_tile_path(image_path, logger = None) |
diffusers parameter | ||
log 出力 | - | log_debug(msg, logger = None) |
log_info(msg, logger = None) | ||
--device | device | _get_device(opt, logger = None) |
--result_image_path | result_image_path | _get_result_image_path(opt, logger = None) |
--result_path | result_path | _get_result_path(opt, logger = None) |
--result_file | result_file | _get_result_file(opt, logger = None) |
--prompt(日本語) | prompt(英語) | _get_prompt(opt, logger = None) |
--neg_prompt(日本語) | neg_prompt(英語) | _get_negative_prompt(opt, logger = None) |
--model_dir | model_dir | _get_model_dir(opt, logger = None) |
--model_path | model_path | _get_model_path(opt, logger = None) |
--ctrl_model_dir | ctrl_model_dir | _get_controlnet_model_dir(opt, logger = None) |
--ctrl_model_path | ctrl_model_path | _get_controlnet_model_path(opt, logger = None) |
--image_path | image_path | _get_source_image_path(opt, logger = None) |
--image_path から PIL画像オブジェクトを得る | image | _get_source_image(opt, logger = None) |
--ctrl_image_path | ctrl_image_path | _get_control_image_path(opt, logger = None) |
--ctrl_image_path から PIL画像オブジェクトを得る | image | _get_control_image(opt, logger = None) |
パスから画像を読んでリサイズする | image | _get_resize_image(image_path, max_size, logger) |
--height, --width | height, width | _get_image_size(opt, logger = None) |
--max_size | max_size | _get_max_size(opt, logger = None) |
--seed(-1 の時は乱数生成) | seed | _get_seed_value(opt, logger = None) |
--num_inference_steps | num_inference_steps | _get_inference_steps(opt, logger = None) |
--guidance_scale | guidance_scale | _get_guidance_scale(opt, logger = None) |
--image_guidance_scale | image_guidance_scale | _get_image_guidance_scale(opt, logger = None) |
--strength | strength | _get_strength(opt, logger = None) |
--cc_scale | cc_scale | _get_controlnet_conditioning_scale(opt, logger = None) |
--ip_image_path | ip_image_path | _get_ip_image_path(opt, logger = None) |
--ip_image_path PIL画像オブジェクトを得る | image | _get_ip_image(opt, logger = None) |
# -*- coding: utf-8 -*- ##-------------------------------------------------- ## sd_test basic tools Ver 0.11 ## ## 2025.08.19 Masahiro Izutsu ##-------------------------------------------------- ## sd_tools.py ## Ver 0.00 2025.07.17 Trial version ## Ver 0.05 2025.07.20 sd_100.py 統合版対応 ## Ver 0.06 2025.07.27 sd_081 IP-Adapter 対応 ## Ver 0.07 2025.08.02 sd_051 ADetailer 対応 ## Ver 0.08 2025.08.11 sd_053 canny 対応 ## Ver 0.09 2025.08.12 sd_053 統合版 対応 ## Ver 0.10 2025.08.14 sd_047 scribble 修正 ## Ver 0.11 2025.08.19 sd_101 統合版 対応 # タイトル title = 'sd_test basic tools Ver 0.11' # Color Escape Code GREEN = '\033[1;32m' RED = '\033[1;31m' NOCOLOR = '\033[0m' YELLOW = '\033[1;33m' CYAN = '\033[1;36m' BLUE = '\033[1;34m' MAGENTA = '\033[1;35m' # インポート&初期設定 import os import re import glob import random import argparse import numpy as np import cv2 from PIL import Image import torch import my_imagetool # 定数定義 IMAGES_WORK_DIR = 'images_work' ## ----- コマンド入力関連 -------------- # 初期値 def_result_image = '' def_cpu = 'store_true' def_log = '3' def_model_dir = '' def_model_path = '' def_ctrl_model_dir = '' def_ctrl_model_path = '' def_image_path = '' def_control_image_path = '' def_max_size = 0 def_prompt = '' def_seed = -1 def_width = 512 def_height = 512 def_step = 30 def_scale = 7.0 def_image_scale = 1.5 def_cc_scale = 1.0 def_strength = None def_neg_prompt = ' ' def_ip_image_path = '' def_ip_scale = 0.5 def_mode = 'store_false' def_ext = '' # コマンドライン・オプション (argparse) 名前/初期値/ヘルプ opt_list = [ ['pros_sel','','sd_tools'], # 0 ['result_image', def_result_image, 'path to output image file'], # 1 ['cpu', def_cpu, 'cpu mode'], # 2 ['log', def_log, 'Log level(-1/0/1/2/3/4/5) Default value is \'3\''], # 3 ['model_dir', def_model_dir, 'Model directory'], # 4 ['model_path', def_model_path, 'Model Path'], # 5 ['ctrl_model_dir', def_ctrl_model_dir, 'ControlNet Model directory'], # 6 ['ctrl_model_path', def_ctrl_model_path, 'ControlNet Model Path'], # 7 ['image_path', def_image_path, 'Sourcs image file path'], # 8 ['ctrl_image_path', '', 'Control image file path'], # 9 ['max_size', def_max_size, 'image max size (0=source)'], # 10 ['prompt', def_prompt, 'Prompt text'], # 11 ['seed', def_seed, 'Seed parameter (-1 = rundom)'], # 12 ['width', def_width, 'image size width'], # 13 ['height', def_height, 'image size height'], # 14 ['step', def_step, 'infer step'], # 15 ['scale', def_scale, 'gaidanse scale'], # 16 ['image_scale', def_image_scale, 'image gaidanse scale'], # 17 ['cc_scale', def_cc_scale, 'controlnet conditioning scale'], # 18 ['strength', def_strength, 'strength value'], # 19 ['neg_prompt', def_neg_prompt, 'Negative Prompt text'], # 20 ['ip_image_path', def_ip_image_path, 'IP-Adapter image filr path'], # 21 ['ip_scale', def_ip_scale, 'IP-Adapter scale'], # 22 ['scheduler', '', "Scheduler 'non/euler/uni/DPM'"], # 23 ['ext', def_ext, 'Extensions'], # 24 ['mode', def_mode, 'aplication mode'], # 25 ] # コマンドライン・オプションの設定 def parse_args(parser, pars_list): if parser == None: parser = argparse.ArgumentParser() for pars in pars_list: p = '--' + pars[0] if pars[1] == 'store_true' or pars[1] == 'store_false': parser.add_argument(p, dest = pars[0], action = pars[1], help = pars[2]) else: parser.add_argument(p, default = pars[1], help = pars[2]) return parser # オプション・リストを変更する def change_option(opt_list, key, val): bf = False for ol in opt_list: if ol[0] == key: ol[1] = val bf = True return bf def _get_process_name(opt_list): return opt_list[0][2] def _show_opt_list(opt_list): for ol in opt_list: print(ol) # 基本情報の表示 def display_info(opt, title): if title != '': print('\n' + GREEN + title + ': Starting application...' + '\n' + NOCOLOR) opt_dict = vars(opt) # NameSpace型を辞書型に変換 keys = opt_dict.keys() # key一覧取得 for key in keys: if opt_dict[key] != '': s = key + ' ' * 24 s0 = s[:24] print(f' --{YELLOW}{s0} : {NOCOLOR} {opt_dict[key]}') if title != '': print(' ') # コマンドライン・オプションチェック def is_option(opt, key): opt_dict = vars(opt) # NameSpace型を辞書型に変換 res = opt_dict.get(key) if res == '': res = None return res ## ----- 画像関連 ---------------------- # Segment color ada_palette = np.asarray([ [0, 0, 0], [120, 120, 120], [180, 120, 120], [6, 230, 230], [80, 50, 50], [4, 200, 3], [120, 120, 80], [140, 140, 140], [204, 5, 255], [230, 230, 230], [4, 250, 7], [224, 5, 255], [235, 255, 7], [150, 5, 61], [120, 120, 70], [8, 255, 51], [255, 6, 82], [143, 255, 140], [204, 255, 4], [255, 51, 7], [204, 70, 3], [0, 102, 200], [61, 230, 250], [255, 6, 51], [11, 102, 255], [255, 7, 71], [255, 9, 224], [9, 7, 230], [220, 220, 220], [255, 9, 92], [112, 9, 255], [8, 255, 214], [7, 255, 224], [255, 184, 6], [10, 255, 71], [255, 41, 10], [7, 255, 255], [224, 255, 8], [102, 8, 255], [255, 61, 6], [255, 194, 7], [255, 122, 8], [0, 255, 20], [255, 8, 41], [255, 5, 153], [6, 51, 255], [235, 12, 255], [160, 150, 20], [0, 163, 255], [140, 140, 140], [250, 10, 15], [20, 255, 0], [31, 255, 0], [255, 31, 0], [255, 224, 0], [153, 255, 0], [0, 0, 255], [255, 71, 0], [0, 235, 255], [0, 173, 255], [31, 0, 255], [11, 200, 200], [255, 82, 0], [0, 255, 245], [0, 61, 255], [0, 255, 112], [0, 255, 133], [255, 0, 0], [255, 163, 0], [255, 102, 0], [194, 255, 0], [0, 143, 255], [51, 255, 0], [0, 82, 255], [0, 255, 41], [0, 255, 173], [10, 0, 255], [173, 255, 0], [0, 255, 153], [255, 92, 0], [255, 0, 255], [255, 0, 245], [255, 0, 102], [255, 173, 0], [255, 0, 20], [255, 184, 184], [0, 31, 255], [0, 255, 61], [0, 71, 255], [255, 0, 204], [0, 255, 194], [0, 255, 82], [0, 10, 255], [0, 112, 255], [51, 0, 255], [0, 194, 255], [0, 122, 255], [0, 255, 163], [255, 153, 0], [0, 255, 10], [255, 112, 0], [143, 255, 0], [82, 0, 255], [163, 255, 0], [255, 235, 0], [8, 184, 170], [133, 0, 255], [0, 255, 92], [184, 0, 255], [255, 0, 31], [0, 184, 255], [0, 214, 255], [255, 0, 112], [92, 255, 0], [0, 224, 255], [112, 224, 255], [70, 184, 160], [163, 0, 255], [153, 0, 255], [71, 255, 0], [255, 0, 163], [255, 204, 0], [255, 0, 143], [0, 255, 235], [133, 255, 0], [255, 0, 235], [245, 0, 255], [255, 0, 122], [255, 245, 0], [10, 190, 212], [214, 255, 0], [0, 204, 255], [20, 0, 255], [255, 255, 0], [0, 153, 255], [0, 41, 255], [0, 255, 204], [41, 0, 255], [41, 255, 0], [173, 0, 255], [0, 245, 255], [71, 0, 255], [122, 0, 255], [0, 255, 184], [0, 92, 255], [184, 255, 0], [0, 133, 255], [255, 214, 0], [25, 194, 194], [102, 255, 0], [92, 0, 255], ]) # チャネル数の取得 def get_image_channel(image_path): pil_image = Image.open(image_path) ch = 0 if pil_image.mode == 'L' or pil_image.mode == 'P': ch = 1 elif pil_image.mode == 'RGB': ch = 3 elif pil_image.mode == 'RGBA': ch = 4 return ch # イメージ変換 PIL → OpenCV def pil2cv(image): new_image = np.array(image, dtype=np.uint8) if new_image.ndim == 2: # モノクロ pass elif new_image.shape[2] == 3: # カラー new_image = cv2.cvtColor(new_image, cv2.COLOR_RGB2BGR) elif new_image.shape[2] == 4: # 透過 new_image = cv2.cvtColor(new_image, cv2.COLOR_RGBA2BGRA) return new_image def pil2cv_np(image): new_image = np.array(image, dtype=np.uint8) if new_image.ndim == 2: # モノクロ pass elif new_image.shape[2] == 3: # カラー new_image = new_image[:, :, ::-1] elif new_image.shape[2] == 4: # 透過 new_image = new_image[:, :, [2, 1, 0, 3]] return new_image # イメージ変換 OpenCV → PIL def cv2pil(image): new_image = image.copy() if new_image.ndim == 2: # モノクロ pass elif new_image.shape[2] == 3: # カラー new_image = cv2.cvtColor(new_image, cv2.COLOR_BGR2RGB) elif new_image.shape[2] == 4: # 透過 new_image = cv2.cvtColor(new_image, cv2.COLOR_BGRA2RGBA) new_image = Image.fromarray(new_image) return new_image def cv2pil_np(image): new_image = image.copy() if new_image.ndim == 2: # モノクロ pass elif new_image.shape[2] == 3: # カラー new_image = cv2.cvtColor(new_image, cv2.COLOR_BGR2RGB) elif new_image.shape[2] == 4: # 透過 new_image = cv2.cvtColor(new_image, cv2.COLOR_BGRA2RGBA) new_image = Image.fromarray(new_image) return new_image # イメージファイルの表示 def image_disp(image_path = '', dispname = '', maxsize = 800, wait_s = 0): image = cv2.imread(image_path) dispf = dispname != '' my_imagetool.image_disp(image, winname = dispname, dispf = dispf, save_path = '', maxsize = maxsize, wait_s = wait_s) # OpenCV イメージ 結果の保存と表示 def image_save(image, save_path = '', dispname = '', maxsize = 800, wait_s = 0): dispf = dispname != '' my_imagetool.image_disp(image, winname = dispname, dispf = dispf, save_path = save_path, maxsize = maxsize, wait_s = wait_s) # PIL イメージ 結果の保存と表示 def image_save2(image, save_path = '', dispname = '', maxsize = 800, wait_s = 0): image = pil2cv(image) dispf = dispname != '' my_imagetool.image_disp(image, winname = dispname, dispf = dispf, save_path = save_path, maxsize = maxsize, wait_s = wait_s) # コントロールイメージ作成 def make_inpaint_condition(image, image_mask): image = np.array(image.convert("RGB")).astype(np.float32) / 255.0 image_mask = np.array(image_mask.convert("L")).astype(np.float32) / 255.0 assert image.shape[0:1] == image_mask.shape[0:1], "image and image_mask must have the same image size" image[image_mask > 0.5] = -1.0 # set as masked pixel image = np.expand_dims(image, 0).transpose(0, 3, 1, 2) image = torch.from_numpy(image) return image # 線画作成 def line_drawing_image(img): # 画像のコントラストを調整 gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) gray = cv2.equalizeHist(gray) # ガウシアンブラーを適用 gray_blurred = cv2.GaussianBlur(gray, (5, 5), 0) # エッジ検出で線画を生成(反転) edges = cv2.adaptiveThreshold(gray_blurred, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 11, 7) edges = 255 - edges return edges # Canny 処理 (PIL) def canny_preprocessor(image, low_threshold, high_threshold): image_arr = np.array(image) image_arr = cv2.Canny(image_arr, low_threshold, high_threshold) image_arr = image_arr[:, :, None] image_arr = np.concatenate((image_arr, image_arr, image_arr), axis=2) canny_image = Image.fromarray(image_arr) return canny_image # Scribble 処理 (PIL) def scribble_preprocessor(image, scribble = True): from controlnet_aux import HEDdetector processor = HEDdetector.from_pretrained('lllyasviel/Annotators') control_image = processor(image, scribble = scribble) img_gray = control_image.convert('L') return img_gray # Linart 処理 (PIL) def lineart_preprocessor(image): from controlnet_aux import LineartDetector processor = LineartDetector.from_pretrained('lllyasviel/Annotators') control_image = processor(image) img_gray = control_image.convert('L') return img_gray # Softedge 処理 (PIL) def softedge_preprocessor(image, safe = True): from controlnet_aux import PidiNetDetector processor = PidiNetDetector.from_pretrained('lllyasviel/Annotators') control_image = processor(image, safe = safe) img_gray = control_image.convert('L') return img_gray # Shuffle 処理 (PIL) def shuffle_preprocessor(image): from controlnet_aux import ContentShuffleDetector processor = ContentShuffleDetector() control_image = processor(image) return control_image # Depth 処理 (PIL) def depth_preprocessor(image): from transformers import pipeline as transformersPipeline, logging logging.set_verbosity_error() depth_estimator = transformersPipeline('depth-estimation') image = depth_estimator(image)['depth'] image = np.array(image) image = image[:, :, None] image = np.concatenate([image, image, image], axis=2) control_image = Image.fromarray(image) img_gray = control_image.convert('L') return img_gray # Segment 処理 (PIL) def seg_preprocessor(image): from transformers import AutoImageProcessor, UperNetForSemanticSegmentation, logging logging.set_verbosity_error() image_processor = AutoImageProcessor.from_pretrained("openmmlab/upernet-convnext-small") pixel_values = image_processor(image, return_tensors="pt").pixel_values with torch.no_grad(): image_segmentor = UperNetForSemanticSegmentation.from_pretrained("openmmlab/upernet-convnext-small") outputs = image_segmentor(pixel_values) seg = image_processor.post_process_semantic_segmentation(outputs, target_sizes=[image.size[::-1]])[0] color_seg = np.zeros((seg.shape[0], seg.shape[1], 3), dtype=np.uint8) # height, width, 3 for label, color in enumerate(ada_palette): color_seg[seg == label, :] = color color_seg = color_seg.astype(np.uint8) return Image.fromarray(color_seg) # Normal_map 処理 (PIL) def normal_preprocessor(image): from controlnet_aux import NormalBaeDetector processor = NormalBaeDetector.from_pretrained('lllyasviel/Annotators') control_image = processor(image) return control_image # lineart anime 処理 (PIL) def anime_preprocessor(image): from controlnet_aux import LineartAnimeDetector processor = LineartAnimeDetector.from_pretrained('lllyasviel/Annotators') control_image = processor(image) img_gray = control_image.convert('L') return img_gray # MLSD 処理 (PIL) def mlsd_preprocessor(image): from controlnet_aux import MLSDdetector processor = MLSDdetector.from_pretrained('lllyasviel/Annotators') control_image = processor(image) img_gray = control_image.convert('L') return img_gray # Tile 処理 (PIL) def tile_preprocessor(image, resolution = 512): image = image.convert("RGB") W, H = image.size k = float(resolution) / min(H, W) H *= k W *= k H = int(round(H / 64.0)) * 64 W = int(round(W / 64.0)) * 64 img = image.resize((W, H), resample=Image.LANCZOS) return img # マスク作成 def mask_square(image, size): img_h, img_w = image.shape[:2] x0 = 0 y0 = 0 x1 = 0 y1 = 0 if img_h > img_w: size = img_h x0 = int((size - img_w) / 2) x1 = x0 + img_w y1 = size else: size = img_w y0 = int((size - img_h) / 2) y1 = y0 + img_h x1 = size # 白ベースの画像を生成 dist = np.array([size, size, 1]) # 縦×横 3チャンネル img = np.full(dist, 255, dtype=np.uint8) img[y0:y1, x0 + 16:x1 - 32] = 0 # 中央部分を黒(左右 16ピクセルづつ狭く) return img ## ------------------------------------- # モデルを調べる(SD1.5 モデルは SD1.5/フォルダ内にある前提) # in: model モデル名 # out: bool True = SD1.5, False = SDXL def is_sd15(model): return ('SD1.5' in model) # ポーズ・ファイルか調べる def is_pose(filepath): s = os.path.splitext(filepath) return s[0][-5:] == '_pose' # ファイルパスから拡張子で選択したファイル一覧を得る def get_file_list_sel(dirpass, sel_ext): flist = sorted(glob.glob(dirpass)) result = [] for name in flist: for ext in sel_ext: if ext == os.path.splitext(name)[1]: s = name.replace(os.sep,'/') result.append(s) return result # フォルダ内で連番のファイル名を得る(ヘッダ + 連番 で検索) # in: filename ヘッダー名.拡張子 # seq_digit 連番桁数 # ex 追加文字列 # out: 新しいファイル名 def make_filename_by_seq(dirname, filename, seq_digit = 3, ex = ''): filename_without_ext, ext = os.path.splitext(filename) pattern = f"{filename_without_ext}_([0-9]*){ext}" prog = re.compile(pattern) files = glob.glob( os.path.join(dirname, f"{filename_without_ext}_[0-9]*{ext}") ) max_seq = -1 kn = len(filename_without_ext) + seq_digit + 1 # ヘッダ + 連番桁数 + 1 (_) for f in files: fn0, _ = os.path.splitext(os.path.basename(f)) fn1 = fn0[: min(len(fn0), kn)] + ext m = prog.match(os.path.basename(fn1)) # ファイル名 kn まで比較 if m: max_seq = max(max_seq, int(m.group(1))) new_filename = f"{filename_without_ext}_{max_seq+1:0{seq_digit}}_{ex}{ext}" return new_filename # 生成画像のファイル名からシード値を得る def path2seed(filepath): s = os.path.splitext(os.path.basename(filepath))[0] n = s.rfind('_') return int(s[n + 1:]) # 経過時間(秒)を hh:mm:ssフォーマットに変換 def elapsed_time_str(seconds): seconds = int(seconds + 0.5) # 秒数を四捨五入 h = seconds // 3600 # 時の取得 m = (seconds - h * 3600) // 60 # 分の取得 s = seconds - h * 3600 - m * 60 # 秒の取得 return f"{h:02}:{m:02}:{s:02}" # hh:mm:ss形式の文字列で返す # ランダムなシード値を得る def get_random_seed_value(n): seed = int(n) if seed == -1: seed = random.randint(0, 2**32-1) return seed # 日本語から英語に翻訳 def trans_jp2en(str): from translate import Translator if len(str) != len(str.encode('utf-8')): trans = Translator('en','ja').translate prompt = trans(str) # 日本語→英語 else: prompt = str return prompt # メモリー開放 def device_empty_cache(device): if device == 'cuda': torch.cuda.empty_cache() elif device == 'mps': torch.mps.empty_cache() # 作業フォルダ名を得る def get_work_path(logger = None): work_path = os.getcwd().replace(os.sep,'/') + '/' + IMAGES_WORK_DIR log_debug(f'work_path: {work_path}', logger) return work_path # 出力ファイル名から処理ファイル名を得る def get_process_path(result_path): s = os.path.split(result_path) process_path = s[0] + '/process/' + s[1] return process_path # ソース・マスク画像ファイル名を得る def get_source_mask_path(image_path, logger = None): work_path = get_work_path() file = work_path + '/' + os.path.basename(image_path) s = os.path.splitext(file) src_path = s[0] + '_src' + s[1] mask_path = s[0] + '_mask' + s[1] log_debug(f'src_path: {src_path}', logger) log_debug(f'mask_path: {mask_path}', logger) return src_path, mask_path # ポーズ画像ファイル名を得る def get_pose_path(image_path, logger = None): work_path = get_work_path() file = work_path + '/' + os.path.basename(image_path) s = os.path.splitext(file) pose_path = s[0] + '_pose' + s[1] log_debug(f'pose_path: {pose_path}', logger) return pose_path # Canny 画像ファイル名を得る def get_canny_path(image_path, logger = None): work_path = get_work_path() file = work_path + '/' + os.path.basename(image_path) s = os.path.splitext(file) canny_path = s[0] + '_canny' + s[1] log_debug(f'canny_path: {canny_path}', logger) return canny_path # scribble 画像ファイル名を得る def get_scribble_path(image_path, logger = None): work_path = get_work_path() file = work_path + '/' + os.path.basename(image_path) s = os.path.splitext(file) scribble_path = s[0] + '_scribble' + s[1] log_debug(f'scribble_path: {scribble_path}', logger) return scribble_path # lineart 画像ファイル名を得る def get_lineart_path(image_path, logger = None): work_path = get_work_path() file = work_path + '/' + os.path.basename(image_path) s = os.path.splitext(file) lineart_path = s[0] + '_lineart' + s[1] log_debug(f'lineart_path: {lineart_path}', logger) return lineart_path # softedge 画像ファイル名を得る def get_softedge_path(image_path, logger = None): work_path = get_work_path() file = work_path + '/' + os.path.basename(image_path) s = os.path.splitext(file) softedge_path = s[0] + '_softedge' + s[1] log_debug(f'softedge_path: {softedge_path}', logger) return softedge_path # shuffle 画像ファイル名を得る def get_shuffle_path(image_path, logger = None): work_path = get_work_path() file = work_path + '/' + os.path.basename(image_path) s = os.path.splitext(file) shuffle_path = s[0] + '_shuffle' + s[1] log_debug(f'shuffle_path: {shuffle_path}', logger) return shuffle_path # depth 画像ファイル名を得る def get_depth_path(image_path, logger = None): work_path = get_work_path() file = work_path + '/' + os.path.basename(image_path) s = os.path.splitext(file) depth_path = s[0] + '_depth' + s[1] log_debug(f'depth_path: {depth_path}', logger) return depth_path # segment 画像ファイル名を得る def get_seg_path(image_path, logger = None): work_path = get_work_path() file = work_path + '/' + os.path.basename(image_path) s = os.path.splitext(file) seg_path = s[0] + '_seg' + s[1] log_debug(f'seg_path: {seg_path}', logger) return seg_path # normal_map 画像ファイル名を得る def get_normal_path(image_path, logger = None): work_path = get_work_path() file = work_path + '/' + os.path.basename(image_path) s = os.path.splitext(file) normal_path = s[0] + '_normal' + s[1] log_debug(f'normal_path: {normal_path}', logger) return normal_path # lineart_anime 画像ファイル名を得る def get_anime_path(image_path, logger = None): work_path = get_work_path() file = work_path + '/' + os.path.basename(image_path) s = os.path.splitext(file) anime_path = s[0] + '_anime' + s[1] log_debug(f'anime_path: {anime_path}', logger) return anime_path # MLSD 画像ファイル名を得る def get_mlsd_path(image_path, logger = None): work_path = get_work_path() file = work_path + '/' + os.path.basename(image_path) s = os.path.splitext(file) mlsd_path = s[0] + '_mlsd' + s[1] log_debug(f'mlsd_path: {mlsd_path}', logger) return mlsd_path # Tile 画像ファイル名を得る def get_tile_path(image_path, logger = None): work_path = get_work_path() file = work_path + '/' + os.path.basename(image_path) s = os.path.splitext(file) tile_path = s[0] + '_tile' + s[1] log_debug(f'tile_path: {tile_path}', logger) return tile_path ## ----- diffusers parameter ----------- # log 出力 def log_debug(msg, logger = None): if logger is not None: logger.debug(msg) def log_info(msg, logger = None): if logger is not None: logger.info(msg) # -- device -- def _get_device(opt, logger = None): gpu_d = torch.cuda.is_available() # GPU 確認 if not opt.cpu and not gpu_d: opt.cpu = True device = 'cpu' if opt.cpu else 'cuda' log_debug(f'device: {device}', logger) return device # -- result_image_path -- def _get_result_image_path(opt, logger = None): result_image_path = opt.result_image log_debug(f'result_image_path: {result_image_path}', logger) return result_image_path # -- result_path -- def _get_result_path(opt, logger = None): result_path = os.path.dirname(opt.result_image) log_debug(f'result_path: {result_path}', logger) return result_path # -- result_file -- def _get_result_file(opt, logger = None): result_file = os.path.basename(opt.result_image) log_debug(f'result_file: {result_file}', logger) return result_file # -- prompt -- def _get_prompt(opt, logger = None): prompt = def_prompt if is_option(opt, 'prompt') == None else trans_jp2en(opt.prompt) log_debug(f'prompt: {prompt}', logger) return prompt # -- negative prompt -- def _get_negative_prompt(opt, logger = None): neg_prompt = def_neg_prompt if is_option(opt, 'neg_prompt') == None else trans_jp2en(opt.neg_prompt) log_debug(f'neg_prompt: {neg_prompt}', logger) return neg_prompt # -- model_dir -- def _get_model_dir(opt, logger = None): model_dir = def_model_dir if is_option(opt, 'model_dir') == None else opt.model_dir log_debug(f'model_dir: {model_dir}', logger) return model_dir # -- model_path -- def _get_model_path(opt, logger = None): model_dir = _get_model_dir(opt, logger) path = def_model_path if is_option(opt, 'model_path') == None else opt.model_path model_path = path if model_dir == '' else model_dir + '/' + path log_debug(f'model_path: {model_path}', logger) return model_path # -- controlnet model_dir -- def _get_controlnet_model_dir(opt, logger = None): ctrl_model_dir = def_ctrl_model_dir if is_option(opt, 'ctrl_model_dir') == None else opt.ctrl_model_dir log_debug(f'ctrl_model_dir: {ctrl_model_dir}', logger) return ctrl_model_dir # -- controlnet model_path -- def _get_controlnet_model_path(opt, logger = None): ctrl_model_dir = _get_controlnet_model_dir(opt, logger) path = def_ctrl_model_path if is_option(opt, 'ctrl_model_path') == None else opt.ctrl_model_path ctrl_model_path = path if ctrl_model_dir == '' else ctrl_model_dir + '/' + path log_debug(f'controlnet model_path: {ctrl_model_path}', logger) return ctrl_model_path # -- source image path -- def _get_source_image_path(opt, logger = None): image_path = def_image_path if is_option(opt, 'image_path') == None else opt.image_path log_debug(f'image_path: {image_path}', logger) return image_path # -- source image -- def _get_source_image(opt, logger = None): image_path = def_image_path if is_option(opt, 'image_path') == None else opt.image_path max_size = def_max_size if is_option(opt, 'max_size') == None else int(opt.max_size) image = _get_resize_image(image_path, max_size, logger) log_debug(f'image_path: {image_path}', logger) return image # -- control image path -- def _get_control_image_path(opt, logger = None): ctrl_image_path = def_control_image_path if is_option(opt, 'ctrl_image_path') == None else opt.ctrl_image_path log_debug(f'ctrl_image_path: {ctrl_image_path}', logger) return ctrl_image_path # -- control source image -- def _get_control_image(opt, logger = None): ctrl_image_path = def_ctrl_image_path if is_option(opt, 'ctrl_image_path') == None else opt.ctrl_image_path max_size = def_max_size if is_option(opt, 'max_size') == None else int(opt.max_size) image = _get_resize_image(ctrl_image_path, max_size, logger) log_debug(f'ctrl_image_path: {ctrl_image_path}', logger) return image # -- resize image -- def _get_resize_image(image_path, max_size, logger = None): image = Image.open(image_path) w, h = image.size bf, h, w = my_imagetool.check_size(h, w, maxsize = max_size) if bf: image = image.resize((w, h), resample=Image.BICUBIC) log_debug(f'image size: width = {w}, height = {h}', logger) return image # -- height, width -- def _get_image_size(opt, logger = None): width = def_width if is_option(opt, 'width') == None else int(opt.width) height = def_height if is_option(opt, 'height') == None else int(opt.height) log_debug(f'width: {width}, height: {height}', logger) return height, width # -- max_size -- def _get_max_size(opt, logger = None): max_size = def_max_size if is_option(opt, 'max_size') == None else int(opt.max_size) log_debug(f'max_size: {max_size}', logger) return max_size # -- seed -- def _get_seed_value(opt, logger = None): seed = def_seed if is_option(opt, 'seed') == None else get_random_seed_value(opt.seed) log_debug(f'seed: {seed}', logger) return seed # -- num_inference_steps -- def _get_inference_steps(opt, logger = None): num_inference_steps = def_step if is_option(opt, 'step') == None else int(opt.step) log_debug(f'step: {num_inference_steps}', logger) return num_inference_steps # -- guidance_scale -- def _get_guidance_scale(opt, logger = None): guidance_scale = def_scale if is_option(opt, 'scale') == None else float(opt.scale) log_debug(f'scale: {guidance_scale}', logger) return guidance_scale # -- image guidance_scale -- def _get_image_guidance_scale(opt, logger = None): image_guidance_scale = def_image_scale if is_option(opt, 'image_scale') == None else float(opt.image_scale) log_debug(f'image guidance scale: {image_guidance_scale}', logger) return image_guidance_scale # -- strength -- def _get_strength(opt, logger = None): strength = def_strength if is_option(opt, 'strength') == None else float(opt.strength) log_debug(f'strength: {strength}', logger) return strength # -- controlnet conditioning scale -- def _get_controlnet_conditioning_scale(opt, logger = None): cc_scale = def_cc_scale if is_option(opt, 'cc_scale') == None else float(opt.cc_scale) log_debug(f'controlnet conditioning scale: {cc_scale}', logger) return cc_scale ## ----- diffusers parameter(option) --- # -- ip-adapter image path -- def _get_ip_image_path(opt, logger = None): ip_image_path = def_ip_image_path if is_option(opt, 'ip_image_path') == None else opt.ip_image_path log_debug(f'ip_image_path: {ip_image_path}', logger) return ip_image_path # -- ip source image -- def _get_ip_image(opt, logger = None): ip_image_path = def_ip_image_path if is_option(opt, 'ip_image_path') == None else opt.ip_image_path if os.path.isfile(ip_image_path): max_size = def_max_size if is_option(opt, 'max_size') == None else int(opt.max_size) image = _get_resize_image(ip_image_path, max_size, logger) log_debug(f'ip_image_path: {ip_image_path}', logger) else: image = None return image # -- guidance_scale -- def _get_ip_scale(opt, logger = None): ip_scale = def_ip_scale if is_option(opt, 'ip_scale') == None else float(opt.ip_scale) log_debug(f'ip adaoter scale: {ip_scale}', logger) return ip_scale ## ------------------------------------- #-----Test routine----- if __name__ == "__main__": source_path = './images/kamo.jpg' source_path = './images/sd_040_test.png' opt_list = [ ['result_image', './sd_results/sd.png', 'path to output image file'], ['cpu', 'store_true', 'cpu mode'], ['log', '3', 'Log level(-1/0/1/2/3/4/5) Default value is \'3\''], ['model_dir', '/StabilityMatrix/Data/Models/StableDiffusion', 'Model directory'], ['model_path', 'SD1.5/v1-5-pruned-emaonly.safetensors', 'Model Path'], ['prompt', '満開の欄', 'Prompt text'], ['seed', -1, 'Seed parameter (-1 = rundom)'], ['width', 512, 'image size width'], ['height', 512, 'image size height'], ['step', 30, 'infer step'], ['scale', 7.0, 'gaidanse scale'], ] parser = parse_args(None, opt_list) opt = parser.parse_args() display_info(opt, title) #------------ print('*** 画像表示 test ***') image_disp(source_path, source_path) print('*** 線画作成 test ***') img = cv2.imread(source_path) img2 = line_drawing_image(img) image_save(img2, 'test1.png', dispname = title) ''' # OpenCV 保存と表示 img = cv2.imread(source_path) image_save(img, 'test1.png', dispname = title) # PIL 保存と表示 img = Image.open(source_path) image_save2(img, 'test2.png', dispname = title) ''' #------------ print('*** diffusers parameter test ***') print(f'device : {_get_device(opt)}') #------------※ 上記ソースコードは表示の都合上、半角コード '}' が 全角 '}'になっていることに注意
PukiWiki 1.5.2 © 2001-2019 PukiWiki Development Team. Powered by PHP 7.4.33. HTML convert time: 0.118 sec.