私的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/paintex |
| ② | sd_101.py | 統合版 Step60 の機能(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/paintex |
| コマンドオプション | 引数 | 初期値の例 | 意味 | 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 | '' | ADetailer ' ' or 'girl' or 'boy', Upscaler ' ' or 'x4' or 'x2' | 〇 | ||
| 名称 | 概要 | 機能 |
| sd_mask.py | マスク作成プログラム | もとになるイメージ画像を表示しながら簡単にマスク画像を作成 |
| sd_canny.py | canny 画像生成プログラム | 画像のエッジ検出手法「canny」で画像生成 |
| sd_rembg.py | rembg 背景消去プログラム | 画像に写っているオブジェクトを識別し、それ以外を削除(透明化) |
| sd_blend.py | 画像ブレンド合成プログラム | 2枚のイメージ画像をマスク画像でブレンドする |
| sd_results.py | GUI共通プログラム | 生成画像パラメータ保存・表示 |
| sd_tools.py | 汎用共通プログラム | このプロジェクトで共通に利用するパッケージ・モジュール |
| 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/」ディレクトリ名のフォルダに配置されていること)
(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.09.10 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.09.10 Linux 不具合修正
# タイトル
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), font=(font_face,10), 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), font=(font_face,10), 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.04
## ControlNet GUI interface
## 2025.09.10 Masahiro Izutsu
##--------------------------------------------------
## sd_101.py
## Ver 0.00 2025.08.16 Trial version
## Ver 0.01 2025.08.19 統合版
## Ver 0.02 2025.08.22 sd_053.py → sd_060.py 変更対応
## Ver 0.03 2025.08.28 sd_052.py 対応
## Ver 0.04 2025.09.10 Linux 不具合修正
# タイトル
title = 'Stable Diffusion with diffusers(101) Ver 0.04'
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_060 as sdm
import sd_052 as sde
import sd_053 as sdu
# 定数定義
DEF_THEME = 'BlueMono'
CANVAS_SIZE = 512
CANVAS_SIZE_S = 180
DEF_SEED_VAL = '12345678'
CSV_LOG_FILE = 'result_101.csv'
PROCESS_DIR = '/process'
MODE_paintex = 'paintex'
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_SCALE_TXT = '-Scale_text-'
KEY_STRENGTH_TXT = '-strength_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_SCALE_TXT = '-Ip_scale_text-'
KEY_IP_IMGPATH_SEL = '-Ip_image_path_sel-'
KEY_IP_IMGPATH_TXT = '-Ip_image_path_text-'
KEY_EXTENTION2 = '-Extention2-'
KEY_SCHEDULER_TXT = '-Scheduler_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-'
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', -1, '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/...'"], # 24
]
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()
mode_list.append(MODE_paintex)
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
# 初期設定
def set_initilal(opt):
if opt.mode == MODE_paintex:
opt.image_path = sde.DEF_IMAGE_PATH
opt.ip_image_path = sde.DEF_CTRL_IMAGE
opt.model_path = sde.DEF_MODEL_PATH
opt.step = 20
else:
sdm.set_initilal(opt)
opt.ip_image_path = ''
return
# ウィジェットのデータの取得(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]
extention = param[KEY_EXTENTION]
ext = "" if extention == '' else '-' + extention
fname = sdt.make_filename_by_seq(param[SKEY_RESULT_PATH], param[SKEY_RESULT_FILE], seq_digit=5, ex= f'{seed}-{ap_mode}{ext}')
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
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)
# 入力画像の前処理
if opt.mode == MODE_paintex:
opt.ctrl_image_path = opt.ip_image_path
src_image, msk_image, img_ctrl = sde.pre_generation(opt, logger)
opt.ctrl_image_path = ''
else:
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 or opt.mode == MODE_paintex:
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)
# 画像生成
if opt.mode == MODE_paintex:
image = sde.image_generation(param[KEY_MODEL], src_image, msk_image, img_ctrl, seed, num_inference_steps, width, height, device)
else:
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:
# Uposcaler
if sdu.UPSCALE_x4 in extention or sdu.UPSCALE_x2 in extention:
image = sdu.image_generation(extention, '', image, prompt, seed, 20, device, logger)
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_IP_IMGPATH].update(param[KEY_IP_IMGPATH])
window[KEY_IP_SCALE].update(param[KEY_IP_SCALE])
window[KEY_APMODE].update(param[KEY_APMODE])
s = ''
for lst in sdm.adetailer_list:
if lst in param[KEY_EXTENTION]: s = lst
window[KEY_EXTENTION].update(s)
s = ''
for lst in sdu.upscaler_list:
if lst in param[KEY_EXTENTION]: s = lst
window[KEY_EXTENTION2].update(s)
# ウイジェットの禁止・許可
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)
window[KEY_IP_IMGPATH_SEL].update(disabled = disabled)
window[KEY_RESET].update(disabled = disabled)
window[KEY_STEP].update(disabled = disabled)
window[KEY_SCALE].update(disabled = disabled)
window[KEY_STRENGTH].update(disabled = disabled)
window[KEY_SCHEDULER].update(disabled = disabled)
window[KEY_EXTENTION].update(disabled = disabled)
window[KEY_EXTENTION2].update(disabled = disabled)
window[KEY_IP_SCALE].update(disabled = disabled)
window[KEY_APMODE].update(disabled = disabled)
window[KEY_SEED_SET].update(disabled = disabled)
window[KEY_SEED_CLR].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):
ap_mode = param[KEY_APMODE]
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(param[KEY_INPUTPATH], 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 # パイプラインから設定
# ウイジェット表示・非表示
def widget_visible():
ap_mode = param[KEY_APMODE]
enb = os.path.isfile(param[KEY_IP_IMGPATH]) or (ap_mode == sdm.MODE_txt2img and os.path.isfile(param[KEY_INPUTPATH]))
window[KEY_IP_SCALE_TXT].update(visible = enb)
window[KEY_IP_SCALE].update(visible = enb)
if ap_mode == MODE_paintex:
s0 = 'Exsample Image'
s1 = 'Eximage'
enb = False
else:
s0 = 'IP-Adapter'
s1 = 'IPimage'
enb = True
window[KEY_IP_IMGPATH_TXT].update(s0)
window[KEY_IP_IMGPATH_SEL].update(s1)
window[KEY_PROMPT_JP_TXT].update(visible = enb)
window[KEY_PROMPT_TXT].update(visible = enb)
window[KEY_NEG_PROMPT_JP_TXT].update(visible = enb)
window[KEY_NEG_PROMPT_TXT].update(visible = enb)
window[KEY_PROMPT_JP].update(visible = enb)
window[KEY_PROMPT].update(visible = enb)
window[KEY_NEG_PROMPT_JP].update(visible = enb)
window[KEY_NEG_PROMPT].update(visible = enb)
window[KEY_SCALE_TXT].update(visible = enb)
window[KEY_SCALE].update(visible = enb)
window[KEY_STRENGTH_TXT].update(visible = enb)
window[KEY_STRENGTH].update(visible = enb)
window[KEY_SCHEDULER_TXT].update(visible = enb)
window[KEY_SCHEDULER].update(visible = enb)
# ------------------------------------------
# パラメータ設定
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), font=(font_face,10), 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), font=(font_face,10), 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), key=KEY_IP_IMGPATH_TXT),
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), key=KEY_SCALE_TXT),
sg.Slider((1, 50), float(param[KEY_SCALE]), 0.1, orientation='h', size=(26, 5), key=KEY_SCALE)],
[sg.Text("Strength", size=(8, 1), key=KEY_STRENGTH_TXT),
sg.Slider((0, 1), float(param[KEY_STRENGTH]), 0.1, orientation='h', size=(26, 5), key=KEY_STRENGTH)],
[sg.Text("IPscale", size=(8, 1), key=KEY_IP_SCALE_TXT),
sg.Slider((0, 1), float(param[KEY_IP_SCALE]), 0.1, orientation='h', size=(26, 5), key=KEY_IP_SCALE)],
[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), justification='right', key=KEY_SCHEDULER_TXT),
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.Combo(sdu.upscaler_list, sdu.upscaler_list[0], enable_events = True, readonly = True, key=KEY_EXTENTION2),],
]
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')
canvas_pros.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)
window[KEY_NEG_PROMPT].update(disabled = True)
widget_visible()
# イベントのループ
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' or event == KEY_PROS_IMAGE + '_click_on':
logger.debug(f'{event}')
set_enb_dis(True)
if event == KEY_PROS_IMAGE + '_click_on':
def_file = sdt.get_process_path(param[SKEY_DEF_OUTPATH])
msg = 'Process Image file select'
else:
def_file = param[SKEY_DEF_OUTPATH]
msg = 'Result Image file select'
if os.path.isfile(def_file):
imgfile = sdr.image_dialog(param, csvfile, def_file, title=msg, 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])
widget_visible()
set_enb_dis(False)
# Extention 選択
if event == KEY_EXTENTION or event == KEY_EXTENTION2:
s1 = values[KEY_EXTENTION]
s2 = values[KEY_EXTENTION2]
s = '' if s1 == '' and s2 == '' else s1 + '_' + s2 if s1 != '' and s2 != '' else s1 + s2
param[KEY_EXTENTION] = opt.ext = extention = s
logger.debug(f'{event} {values[KEY_EXTENTION]} {values[KEY_EXTENTION2]} , ext: {extention}')
# モード選択
if event == KEY_APMODE:
ap_mode = values[KEY_APMODE]
logger.debug(f'{event} {ap_mode}')
change_mode(opt, ap_mode)
widget_visible()
# スケジューラー選択
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])
if ap_mode == sdm.MODE_txt2img:
param[KEY_INPUTPATH] = image_path = '' # IP-Adapter を 入力画像
update_mode(opt)
widget_visible()
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])
widget_visible()
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 = ''
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])
param[KEY_SEED_INPUT] = DEF_SEED_VAL
window[KEY_SEED_INPUT].update(param[KEY_SEED_INPUT] )
param[KEY_IP_IMGPATH] = ip_image_path = opt.ip_image_path
window[KEY_IP_IMGPATH].update(param[KEY_IP_IMGPATH] )
# ベースモデルの初期化
if opt.mode == MODE_paintex:
param[KEY_MODEL] = opt.model_path
else:
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')
※ 上記ソースコードは表示の都合上、半角コード '}' が 全角 '}'になっていることに注意 ※ 更新:2025/08/28python sd_mask.py
(sd_test) python sd_mask.py Stable Diffusion with diffusers (sd_mask) Ver 0.02: 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.02
##
## 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 マスク描画
## Ver 0.02 2025.09.02 描画モード・エッジ処理追加
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.02'
# コマンドライン・オプション (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-'
KEY_MODE_PEN = '-ModePen-'
KEY_MODE_LINE = '-ModeLine-'
KEY_MODE_RECT = '-ModeRect-'
KEY_MODE_ELPS = '-ModeElps-'
KEY_LEVEL = '-Level-'
# ウイジェットの禁止・許可
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)
draw_mode = KEY_MODE_PEN
pen_size = 13
level = 1
# ウィンドウのレイアウト
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=pen_size==3, key=KEY_PEN_S),
sg.Radio('M', group_id='size',enable_events = True, default=pen_size==7, key=KEY_PEN_M),
sg.Radio('L', group_id='size',enable_events = True, default=pen_size==13, 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),],
[sg.Frame("Draw mode:", [
[sg.Radio('Pen', group_id='mode',enable_events = True, default=draw_mode==KEY_MODE_PEN, key=KEY_MODE_PEN),
sg.Radio('Line', group_id='mode',enable_events = True, default=draw_mode==KEY_MODE_LINE, key=KEY_MODE_LINE),
sg.Radio('Rect', group_id='mode',enable_events = True, default=draw_mode==KEY_MODE_RECT, key=KEY_MODE_RECT),
sg.Radio('Elps', group_id='mode',enable_events = True, default=draw_mode==KEY_MODE_ELPS, key=KEY_MODE_ELPS)],]),
sg.Text(" Mask edge:", size=(10, 1)),
sg.Slider((1, 255), level, 2, orientation='h', size=(16, 5), key=KEY_LEVEL, enable_events=True),
]
]
col_btn = [
[sg.Text("", size=(8, 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))
]
]
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_color = (255, 255, 255)
mask_f = True
imgdisp_f = True
press_f = False
x = 0
y = 0
x0 = 0
y0 = 0
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
frame_org = frame.copy()
frame_edg = frame.copy()
update_canvas(frame, frame_bak, imgdisp_f)
new_make_f = False
# イベントのループ
while True:
event, values = window.read(timeout = 30)
if new_make_f:
new_make_f = False
if event == KEY_CANCEL or event == sg.WIN_CLOSED:
update_canvas(frame_org, frame_bak, imgdisp_f)
logger.debug(f'{event}')
break
if event == KEY_OK:
update_canvas(frame_org, frame_bak, imgdisp_f)
src_path, mask_path = sdt.get_source_mask_path(image_path, logger)
img = frame_org if level == 1 else frame_edg
mask_image_save(mask_path, img)
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 not press_f:
# クロスヘア・カーソル
img = np.zeros((CANVAS_SIZE, CANVAS_SIZE, 3), np.uint8)
img[:,:,] = 0x0
cv2.line(img, (0, y), (CANVAS_SIZE - 1, y), (255, 255, 255))
cv2.line(img, (x, 0), (x, CANVAS_SIZE - 1), (255, 255, 255))
frame = cv2.bitwise_xor(frame_org, img)
window[KEY_LEVEL].update(value=1)
if draw_mode == KEY_MODE_PEN:
cv2.circle(frame, (x, y), pen_size, pen_color, thickness=-1)
if draw_mode == KEY_MODE_LINE and press_f:
frame = frame_org.copy()
cv2.line(frame, (x0, y0), (x, y), pen_color, pen_size * 2)
if draw_mode == KEY_MODE_RECT and press_f:
frame = frame_org.copy()
cv2.rectangle(frame, (x0, y0), (x, y), pen_color, cv2.FILLED)
if draw_mode == KEY_MODE_ELPS and press_f:
frame = frame_org.copy()
ax = abs(x - x0)
ay = abs(y - y0)
if ax >= 0 and ay >= 0:
cv2.ellipse(frame, (x0, y0), (ax, ay), angle=0, startAngle=0, endAngle=360, color=pen_color, thickness=-1)
update_canvas(frame, frame_bak, imgdisp_f)
if event == 'CANVAS_click_on':
x0 = x
y0 = y
press_f = True
frame = frame_org.copy()
logger.debug(f'{event}')
if event == 'CANVAS_click_off':
press_f = False
frame_org = frame.copy()
logger.debug(f'{event}')
pass
if event == 'CANVAS_double_click':
logger.debug(f'{event}')
if event == KEY_MASK_DRAW:
update_canvas(frame_org, frame_bak, imgdisp_f)
pen_color = (255, 255, 255)
logger.debug(f'{event} pen_color: {pen_color}')
if event == KEY_MASK_CLEAR:
update_canvas(frame_org, frame_bak, imgdisp_f)
pen_color = (0, 0, 0)
logger.debug(f'{event} pen_color: {pen_color}')
# Edge
if event == KEY_LEVEL:
level = int(values[KEY_LEVEL])
logger.debug(f'{event}, Level: {level}')
kernel_size = level
sigma = 0
frame_edg = cv2.GaussianBlur(frame_org, (kernel_size, kernel_size), sigma)
update_canvas(frame_edg, frame_bak, imgdisp_f)
# Pen size
if event == KEY_PEN_S:
update_canvas(frame_org, frame_bak, imgdisp_f)
pen_size = 3
logger.debug(f'{event} pen_size: {pen_size}')
if event == KEY_PEN_M:
update_canvas(frame_org, frame_bak, imgdisp_f)
pen_size = 7
logger.debug(f'{event} pen_size: {pen_size}')
if event == KEY_PEN_L:
update_canvas(frame_org, frame_bak, imgdisp_f)
pen_size = 13
logger.debug(f'{event} pen_size: {pen_size}')
# Draw mode
if event == KEY_MODE_PEN or event == KEY_MODE_LINE or event == KEY_MODE_RECT or event == KEY_MODE_ELPS:
update_canvas(frame_org, frame_bak, imgdisp_f)
draw_mode = event
logger.debug(f'{event} draw_mode: {draw_mode}')
if event == KEY_MASK:
frame = frame_org.copy()
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
frame_org = frame.copy()
update_canvas(frame, frame_bak, imgdisp_f)
if event == KEY_MSK_IMAGE:
set_enb_dis(True)
update_canvas(frame_org, frame_bak, imgdisp_f)
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)
frame_org = frame.copy()
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 os.path.isfile(fpath):
frame_bak, msk_image =image_read_with_mask(fpath)
if msk_image is not None:
frame = msk_image
frame_org = frame.copy()
image_path = fpath
update_canvas(frame, frame_bak, imgdisp_f)
update_canvas(frame_org, 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')
※ 上記ソースコードは表示の都合上、半角コード '}' が 全角 '}'になっていることに注意
pip install rembg
pip install onnxruntime・「numpy」バージョンのエラーとなる場合 → ImportError: needs NumPy 2.2 or less. Got NumPy 2.3.
pip install numpy==2.2.6
python sd_rembg.py
(sd_test) PS > python sd_rembg.py Stable Diffusion with diffusers (sd_rembg) Ver 0.01: Starting application... --image_path : images/sd_080_test2.png --log : 3 result_file: D:/anaconda_win/workspace_3/sd_test/images_work/sd_080_test2_rembg.png Finished.
ファイル名:'実行ディレクトリ'/images_work/'入力画像ファイル名'_renbg.'入力画像ファイルの拡張子'~
| 機能 | 戻り値 | 関数 |
| 背景を消しマスク画像を得る | alpha_image | remove_background(image_path) |
| 背景を消し処理画像とマスク画像を得る | img_pil, alpha_image | remove_background2(image_path) |
| rembg 画像ファイル名を得る | rembg_path | get_rembg_path(image_path, logger = None) |
# -*- coding: utf-8 -*-
##--------------------------------------------------
## Stable Diffusion with diffusers Ver 0.01
##
## 2025.08.29 Masahiro Izutsu
##--------------------------------------------------
## sd_rembg.py
## Ver 0.00 2025.08.29 rembg_test.py
## Ver 0.01 2025.08.29 sd_rembg.py 背景消去
# インポート&初期設定
import os
import argparse
import cv2
import numpy as np
import PySimpleGUI as sg
from rembg import remove
from PIL import Image
import my_logging
import my_dialog
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/sd_080_test2.png'
def_result_image = 'results/sd_rembg.png'
# タイトル
title = 'Stable Diffusion with diffusers (sd_rembg) 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\''],
]
# rembg 画像ファイル名を得る
def get_rembg_path(image_path, logger = None):
work_path = sdt.get_work_path()
file = work_path + '/' + os.path.basename(image_path)
s = os.path.splitext(file)
rembg_path = s[0] + '_rembg' + s[1]
sdt.log_debug(f'rembg_path: {rembg_path}', logger)
return rembg_path
# 背景を消す
def remove_background(filepath):
_, alpha_image = remove_background2(filepath)
return alpha_image
def remove_background2(filepath):
img = Image.open(filepath)
img_pil = remove(img)
r, g, b, alpha_image = img_pil.split() # 画像をチャネルごとに分離
return img_pil, alpha_image
# マスク作成
def rembg_paint(image_path, work_path, chenge = True, logger = None):
x_size = 0 # 入力画像の横幅
y_size = 0 # 入力画像の高さ
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, frame):
img = cv2.imencode('.png', frame)[1].tobytes()
window[key].update(img)
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)
def update_process(fimage_path):
frame_pil = remove_background(image_path)
frame_prs = np.array(frame_pil, dtype=np.uint8)
if invert_f:
frame_prs = 255 - frame_prs
if x_size != CANVAS_SIZE or y_size != CANVAS_SIZE:
frame_prs = cv2.resize(frame_prs, dsize = (CANVAS_SIZE, CANVAS_SIZE))
update_canvas(KEY_CANVAS_PROS, frame_prs)
return frame_prs
KEY_OK = '-Ok-'
KEY_CANCEL = '-Cancel-'
KEY_MASK_DRAW = '-MaskDraw-'
KEY_MASK_CLEAR = '-MaskClear-'
KEY_MASK = '-Mask-'
KEY_INVERT = '-invert-'
KEY_SRC_IMAGE = '-SourceImage-'
KEY_GENERATE = '-Generate-'
# ウイジェットの禁止・許可
def set_enb_dis(disabled):
window[KEY_OK].update(disabled = disabled)
window[KEY_CANCEL].update(disabled = disabled)
window[KEY_SRC_IMAGE].update(disabled = disabled)
window[KEY_GENERATE].update(disabled = disabled)
# フォント取得
from my_puttext import get_font, cv2_putText
font_face = get_font()
# ウィンドウのテーマ
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)
mask_f = True
invert_f = False
# ウィンドウのレイアウト
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("", size=(2, 1)), sg.Button('Genarate', size=(8, 1), key=KEY_GENERATE, focus=True),
sg.Checkbox('invert', enable_events = True, default = invert_f, key = KEY_INVERT)],
]
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 = clear_canvas(KEY_CANVAS_PROS, 'Generate Image', (0,0,0))
new_make_f = False
# イベントのループ
while True:
event, values = window.read(timeout = 30)
if new_make_f:
frame_prs = update_process(image_path)
set_enb_dis(False)
new_make_f = False
if event == KEY_CANCEL or event == sg.WIN_CLOSED:
logger.debug(f'{event}')
break
if event == KEY_OK:
logger.debug(f'{event}')
rembg_path = get_rembg_path(image_path, logger)
gray_image_save(rembg_path, frame_prs)
logger.info(f'result_file: {rembg_path}')
break
if event == KEY_INVERT:
logger.debug(f'{event}')
invert_f = not invert_f
logger.debug(f'{event} invert_f = {invert_f}')
frame_prs = 255 - frame_prs
update_canvas(KEY_CANVAS_PROS, frame_prs)
if event == KEY_GENERATE:
logger.debug(f'{event}')
set_enb_dis(True)
new_make_f = True
if event == KEY_SRC_IMAGE:
logger.debug(f'{event}')
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):
image_path = fpath
frame =image_read(image_path)
update_canvas(KEY_CANVAS_IMAG, frame)
new_make_f = True
else:
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)
rembg_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')
※ 上記ソースコードは表示の都合上、半角コード '}' が 全角 '}'になっていることに注意 ※ 更新:2025/08/30python sd_blend.py
(sd_test) python sd_blend.py Stable Diffusion with diffusers (sd_blend) Ver 0.01: Starting application... --result_image : results/sd.png --image_path : images/sd_080_test2.png --ctrl_image_path : images/beach.jpg --log : 3 result_file: D:/anaconda_win/workspace_3/sd_test/images_work/sd_080_test2_mask.png result_file: results/sd_sd_080_test2-beach.png Finished.
ファイル名:'実行ディレクトリ'/results/'sd_' + '入力画像1ファイル名'+ '-' + '入力画像2ファイル名'.'入力画像1ファイルの拡張子'~
| 機能 | 戻り値 | 関数 |
| 合成画像ファイル名を得る | path | get_result_path(image_path, ctrl_image_path, result_path, logger = None) |
| 画像ブレンド合成 | - | image_blend(image_path, ctrl_image_path, result_path, chenge = True, logger = None) |
# -*- coding: utf-8 -*-
##--------------------------------------------------
## Stable Diffusion with diffusers Ver 0.01
##
## 2025.09.02 Masahiro Izutsu
##--------------------------------------------------
## sd_blend.py
## Ver 0.00 2025.09.01 sd_blend_test.py
## Ver 0.01 2025.09.02 sd_blend.py
# インポート&初期設定
import os
import argparse
import cv2
import numpy as np
import PySimpleGUI as sg
from rembg import remove
from PIL import Image
import my_logging
import my_dialog
import sd_tools as sdt
import sd_mask
# 定数定義
DEF_THEME = 'BlueMono'
CANVAS_SIZE = 512
CANVAS_SIZE_S = 256
KEY_CANVAS_IMAG = 'CANVAS_imag'
KEY_CANVAS_IMAG2 = 'CANVAS_imag2'
KEY_CANVAS_MASK = 'CANVAS_mask'
KEY_CANVAS_PROS = 'CANVAS_pros'
def_image_path = 'images/sd_080_test2.png'
def_ctrl_image_path = 'images/beach.jpg'
def_result_image = 'results/sd_rembg.png'
# タイトル
title = 'Stable Diffusion with diffusers (sd_blend) Ver 0.01'
# コマンドライン・オプション (argparse) 名前/初期値/ヘルプ
opt_list = [
['result_image', 'results/sd.png', 'path to output image file'],
['image_path', def_image_path , 'path to source image file'],
['ctrl_image_path', def_ctrl_image_path , 'path to contyrol image file'],
['log', '3', 'Log level(-1/0/1/2/3/4/5) Default value is \'3\''],
]
# 合成画像ファイル名を得る
def get_result_path(image_path, ctrl_image_path, result_path, logger = None):
file1 = os.path.splitext(os.path.basename(image_path))[0]
file2 = os.path.splitext(os.path.basename(ctrl_image_path))[0]
s = os.path.splitext(result_path)
path = s[0] + '_' +file1 + '-' + file2 + s[1]
sdt.log_debug(f'path: {path}', logger)
return path
# 画像ブレンド合成
def image_blend(image_path, ctrl_image_path, result_path, chenge = True, logger = None):
x_size = 0 # 入力画像の横幅
y_size = 0 # 入力画像の高さ
x2_size = 0 # 入力画像の横幅
y2_size = 0 # 入力画像の高さ
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, frame):
image = frame.copy()
sz = CANVAS_SIZE if key == KEY_CANVAS_PROS else CANVAS_SIZE_S
if x_size != sz or y_size != sz:
image = cv2.resize(image, dsize = (sz, sz))
img = cv2.imencode('.png', image)[1].tobytes()
window[key].update(img)
def image_read(filepath):
image = cv2.imread(filepath)
y_size, x_size = image.shape[:2]
return image
def draw_gray_scale(key, frame, level):
frame[:,:,] = level
update_canvas(key, frame)
return frame
def update_process(image1, image2, mask):
img1 = cv2.resize(image1, dsize = (CANVAS_SIZE, CANVAS_SIZE))
img1 = cv2.cvtColor(img1, cv2.COLOR_BGR2RGB)
img1 = Image.fromarray(img1)
img2 = cv2.resize(image2, dsize = (CANVAS_SIZE, CANVAS_SIZE))
img2 = cv2.cvtColor(img2, cv2.COLOR_BGR2RGB)
img2 = Image.fromarray(img2)
msk = cv2.cvtColor(mask, cv2.COLOR_BGR2GRAY)
msk = Image.fromarray(msk)
prs = Image.composite(img1, img2, msk)
frame_prs = np.array(prs, dtype=np.uint8)
frame_prs = cv2.cvtColor(frame_prs, cv2.COLOR_RGB2BGR)
update_canvas(KEY_CANVAS_PROS, frame_prs)
return frame_prs
def image_save(path, image):
if x_size != CANVAS_SIZE or y_size != CANVAS_SIZE:
image = cv2.resize(image, dsize = (x_size, y_size))
cv2.imwrite(path, image)
KEY_OK = '-Ok-'
KEY_CANCEL = '-Cancel-'
KEY_MASK_DRAW = '-MaskDraw-'
KEY_MASK_CLEAR = '-MaskClear-'
KEY_MASK = '-Mask-'
KEY_INVERT = '-invert-'
KEY_GENERATE = '-Generate-'
KEY_INPUTPATH = '-Input-'
KEY_CTRL_IMGPATH = '--ctrl_image_path--'
KEY_OUTPATH = '-Output-'
KEY_LEVEL = '-Level-'
KEY_INVERT = '-invert-'
# ウイジェットの禁止・許可
def set_enb_dis(disabled):
window[KEY_OK].update(disabled = disabled)
window[KEY_CANCEL].update(disabled = disabled)
# フォント取得
from my_puttext import get_font, cv2_putText
font_face = get_font()
# ウィンドウのテーマ
sg.theme(DEF_THEME)
canvas_img = sg.Image(size = (CANVAS_SIZE_S, CANVAS_SIZE_S), key=KEY_CANVAS_IMAG)
canvas_img2 = sg.Image(size = (CANVAS_SIZE_S, CANVAS_SIZE_S), key=KEY_CANVAS_IMAG2)
canvas_msk = sg.Image(size = (CANVAS_SIZE_S, CANVAS_SIZE_S), key=KEY_CANVAS_MASK)
canvas_prs = sg.Image(size = (CANVAS_SIZE, CANVAS_SIZE), key=KEY_CANVAS_PROS)
mask_f = True
invert_f = False
# ウィンドウのレイアウト
col_left = [
[canvas_img],
[canvas_img2],
[canvas_msk],
]
col_right = [
[canvas_prs],
[sg.Text("", size=(10, 1))],
[sg.Text("Input File", size=(10, 1)),
sg.Text('', size=(51,1), text_color='#008800', background_color='LightSteelBlue1', key=KEY_INPUTPATH)],
[sg.Text("Input File 2", size=(10, 1)),
sg.Text('', size=(51,1), text_color='#008800', background_color='LightSteelBlue1', key=KEY_CTRL_IMGPATH)],
[sg.Text("Result File", size=(10, 1)),
sg.Text('', size=(51,1), text_color='#008800', background_color='LightSteelBlue1', key=KEY_OUTPATH)],
[sg.Text("", size=(10, 1))],
[sg.Text("Blend Level", size=(10, 1)),
sg.Slider((1, 255), 127, 2, orientation='h', size=(46, 5), key=KEY_LEVEL, enable_events=True)],
[sg.Checkbox('invert', enable_events = True, default = invert_f, key = KEY_INVERT)],
]
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)
# ユーザーイベントの定義
canvas_img.bind('<ButtonPress>', '_click_on')
canvas_img2.bind('<ButtonPress>', '_click_on')
canvas_msk.bind('<ButtonPress>', '_click_on')
canvas_prs.bind('<ButtonPress>', '_click_on')
# 初期設定
frame_prs = np.zeros((CANVAS_SIZE, CANVAS_SIZE, 3), np.uint8)
frame_prs[:,:,] = 0x0
update_canvas(KEY_CANVAS_PROS, frame_prs)
frame = cv2.imread(image_path)
y_size, x_size = frame.shape[:2]
update_canvas(KEY_CANVAS_IMAG, frame)
frame2 = cv2.imread(ctrl_image_path)
y2_size, x2_size = frame2.shape[:2]
update_canvas(KEY_CANVAS_IMAG2, frame2)
frame_msk = np.zeros((CANVAS_SIZE, CANVAS_SIZE, 3), np.uint8)
frame_msk = draw_gray_scale(KEY_CANVAS_MASK, frame_msk, 128)
frame_msk_org = frame_msk.copy()
frame_prs = update_process(frame, frame2, frame_msk)
window[KEY_INPUTPATH].update(image_path)
window[KEY_CTRL_IMGPATH].update(ctrl_image_path)
path = get_result_path(image_path, ctrl_image_path, result_path, logger)
window[KEY_OUTPATH].update(path)
new_make_f = False
mask_edit = False
# イベントのループ
while True:
event, values = window.read(timeout = 30)
if new_make_f:
new_make_f = False
if event == KEY_CANCEL or event == sg.WIN_CLOSED:
logger.debug(f'{event}')
break
if event == KEY_OK:
logger.debug(f'{event}')
save_path = get_result_path(image_path, ctrl_image_path, result_path, logger)
image_save(save_path, frame_prs)
logger.info(f'result_file: {save_path}')
break
if event == KEY_LEVEL:
level = int(values[KEY_LEVEL])
logger.debug(f'{event}, Level: {level}')
if mask_edit:
kernel_size = level
sigma = 0
frame_msk = cv2.GaussianBlur(frame_msk_org, (kernel_size, kernel_size), sigma)
update_canvas(KEY_CANVAS_MASK, frame_msk)
else:
frame_msk = draw_gray_scale(KEY_CANVAS_MASK, frame_msk, level)
frame_prs = update_process(frame, frame2, frame_msk)
if event == KEY_INVERT:
logger.debug(f'{event}')
invert_f = not invert_f
logger.debug(f'{event} invert_f = {invert_f}')
frame_msk = 255 - frame_msk
frame_msk_org = frame_msk.copy()
update_canvas(KEY_CANVAS_MASK, frame_msk)
frame_prs = update_process(frame, frame2, frame_msk)
# KEY_CANVAS_IMAG
if event == KEY_CANVAS_IMAG + '_click_on':
logger.debug(f'{event}')
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):
image_path = fpath
frame = cv2.imread(image_path)
y_size, x_size = frame.shape[:2]
update_canvas(KEY_CANVAS_IMAG, frame)
frame_prs = update_process(frame, frame2, frame_msk)
window[KEY_INPUTPATH].update(image_path)
path = get_result_path(image_path, ctrl_image_path, result_path, logger)
window[KEY_OUTPATH].update(path)
set_enb_dis(False)
logger.debug(f'{event} ctrl_image_path = {ctrl_image_path}')
# KEY_CANVAS_IMAG2
if event == KEY_CANVAS_IMAG2 + '_click_on':
logger.debug(f'{event}')
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):
ctrl_image_path = fpath
frame2 = cv2.imread(ctrl_image_path)
y2_size, x2_size = frame2.shape[:2]
update_canvas(KEY_CANVAS_IMAG2, frame2)
frame_prs = update_process(frame, frame2, frame_msk)
window[KEY_CTRL_IMGPATH].update(ctrl_image_path)
path = get_result_path(image_path, ctrl_image_path, result_path, logger)
window[KEY_OUTPATH].update(path)
set_enb_dis(False)
logger.debug(f'{event} image_path = {image_path}')
# KEY_CANVAS_MASK'
if event == KEY_CANVAS_MASK + '_click_on':
logger.debug(f'{event}')
set_enb_dis(True)
_, msk_path = sdt.get_source_mask_path(image_path, logger)
work_path = sdt.get_work_path(logger)
sd_mask.mask_paint(image_path, work_path, False, logger)
if os.path.isfile(msk_path):
frame_msk = cv2.imread(msk_path)
frame_msk = cv2.resize(frame_msk, dsize = (CANVAS_SIZE, CANVAS_SIZE))
frame_msk_org = frame_msk.copy()
mask_edit = True
kernel_size = 1
sigma = 0
frame_msk = cv2.GaussianBlur(frame_msk, (kernel_size, kernel_size), sigma)
window[KEY_LEVEL].update(range=(1,511))
window[KEY_LEVEL].update(value=1)
update_canvas(KEY_CANVAS_MASK, frame_msk)
frame_prs = update_process(frame, frame2, frame_msk)
set_enb_dis(False)
logger.debug(f'{event} image_path = {image_path}')
# KEY_CANVAS_PROS'
if event == KEY_CANVAS_PROS + '_click_on':
logger.debug(f'{event}')
# ウィンドウ終了処理
window.close()
# ** main関数 **
def main(opt, logger):
image_path = sdt._get_source_image_path(opt, logger)
ctrl_image_path = sdt._get_control_image_path(opt, logger)
result_path = sdt._get_result_path(opt, logger)
# 出力フォルダ
os.makedirs(result_path, exist_ok = True)
image_blend(image_path, ctrl_image_path, opt.result_image, 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')
※ 上記ソースコードは表示の都合上、半角コード '}' が 全角 '}'になっていることに注意 ※ 更新:2025/08/30| 機能 | 戻り値 | 関数 |
| 結果を 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.10
##
## 2025.09.10 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 統合版 対応
## Ver 0.10 2025.09.10 Linux 不具合修正
import warnings
warnings.simplefilter('ignore')
# タイトル
title = 'sd_test basic tools Ver 0.10'
# インポート&初期設定
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()
# フォント取得
from my_puttext import get_font
font_face = get_font()
# ウィンドウのテーマ
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), font=(font_face,10), 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), font=(font_face,10), 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 = new_image[:, :, ::-1]
elif new_image.shape[2] == 4: # 透過
new_image = new_image[:, :, [2, 1, 0, 3]]
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_control_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)}')
#------------
※ 上記ソースコードは表示の都合上、半角コード '}' が 全角 '}'になっていることに注意 ※ 更新:2025/08/28PukiWiki 1.5.4 © 2001-2022 PukiWiki Development Team. Powered by PHP 7.4.33. HTML convert time: 0.160 sec.













































































