miku.gg expressions generator
by mikudev (discord)
The following script batches the emotions prompts for a miku.gg character, you should use img2img inpainting and run this script. It generates a folder with all the inpainted expressions inside outputs/expression_packs
.
Requirements:
- stable-diffusion-webui
- stable-diffusion-webui-rembg
- Lora: constricted-pupils-wide-eyed
- Lora: ahe-07
- Lora: scared-expression
- Lora: smug-expression
- some good anime model, like meinamix
The script should be placed inside the scripts
folder of automatic1111.
miku_expressions_gen.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 | import copy
import random
import shlex
import os
import datetime
from rembg import remove, new_session
from PIL import Image
import io
import modules.scripts as scripts
import gradio as gr
from modules import sd_samplers, errors
from modules.processing import Processed, process_images
from modules.shared import state
suffix_lists = {
'Standard Emotions (29 imgs)': [
'angry=(angry:1.2)',
'tired=(tired:1.2), half-closed eyes, (eye bags:1.3), (tired expression,:1.6), bags under eyes, stress, open mouth',
'shy=(shy:1.3), blush, bright pupils, (looking down:1.3)',
'shocked=(surprised:1.3), open open mouth, <lora:constricted_pupils_v0.2a:1>, looking at viewer',
'scorn=(sneer:1.2), disgust, disappointed, looking at viewer',
'proud=smile, bitch, wide eyes, (smug:1.4), looking at viewer',
'scared=(frightened:1.3), wide eyes, (open mouth:1.2), sweaty, looking away',
'sad=(sad:1.3), teary eyes, downturned mouth, looking down, (tired:1.2)',
'rage=(furious:1.4), (furrowed eyebrows:1.3), (teeth:1.4), flared nostrils, red face',
'pleasure=smiling, closed eyes, (relaxed:1.3), glowing, (blush:1.1)',
'neutral=(relaxed:1.4), looking at viewer, calm, (slight smile:0.9)',
'lustful=(heart-shaped pupils:1.4), lip biting, (blushing:1.3), (seductive smile:1.6), (evil grin:1.2)',
'longing=(longing:1.3), distant gaze, (sad smile:1.2), hopeful, wistful',
'intrigued=(thinking:1.3), raised eyebrows',
'hopeful=(happy:1.3), (bright eyes:1.4), slight smile, looking up',
'happy=(happy:1.3), bright eyes, relaxed, (laughing:1.2)',
'guilty=(nervous:1.2), (looking down:1.4), (blush:1.2), (sweat:1.3)',
'frustrated=(frustrated:1.1), (pout:1.4), squinting eyes, stressed, (angry:1.2), tired',
'exhausted=(exhausted:1.3), half-closed eyes, (tired:1.4), yawning, (bags under eyes:1.2), (sweat:1.2)',
'excited=(excited:1.3), wide eyes, wide smile, open mouth, glowing eyes',
'embarrassed=(embarrassed:1.3), (blushing:1.2), (looking down:1.4), nervous',
'disgusted=(disgust:1.2), open mouth, disgust shaded_face, unhappy, glare, glaring at viewer',
'disappointed=(disappointed:1.3), (sad:1.2), frown, (looking down:1.2)',
'curious=curious, thinking, (surprised:1.3)',
'confused=(thinking:1.3), confused, squinting, (half-closed eyes:0.9)',
'begging=pleading, (wide eyes:1.1), open mouth, (looking up:1.2), sweat, blush, (scared:1.2)',
'blushed=(blush:1.3), embarrassed, (averted gaze:1.2), smiling, shy',
'annoyed=(annoyed:1.4), furrowed brow, narrow eyes, frowning, irritated',
'amused=(amused:1.3), (laughing:1.2), (smiling:1.4), bright eyes, relaxed, open mouth',
],
'Lewd Emotions (16 imgs)': [
'desire=(longing:1.3), lustful, (heart-shaped pupils:1.2), lip biting, (seductive smile:1.6), intense gaze',
'pleasure=(seductive smile:1.6), closed eyes, (relaxed:1.3), glowing, (blush:1.1), (satisfied:1.2)',
'anticipation=(excited:1.3), wide eyes, (open mouth:1.2), lean forward, (eager:1.4)',
'condescension=looking down, disappointed',
'arousal=(blushing:1.4), dilated pupils, (lip biting:1.3), (gaze:1.6), (lustful:1.4)',
'ecstasy=(ahg:1.2), rolling eyes, (tongue:1.2), <lora:ahe:1>)',
'relief=ahg, <lora:ahe:1>, (sigh:1.4), relaxed, (smile:1.2), (soft eyes:1.3), (lightened expression:1.5)',
'release=ahg, rolling eyes, sweat, drooling, wavy mouth, tears, smile, <lora:ahe:1>',
'intensity=ahg, rolling eyes, (tongue:0.8), crying, tears, teeth, snot, fucked silly, implied sex, (face blush:1.2), (saliva:1.2), sweat, breath, wide-eyed, (constricted pupils:1.1), steam, (empty eyes:1.2), dashed eyes, (heart-shaped pupils:1.2), <lora:ahe:1>',
'comfort=relaxed, (calm:1.4), (smile:1.2), (closed eyes:1.3), (peaceful:1.5)',
'humiliation=(blushing:1.4), looking down, (embarrassed:1.3), (uncomfortable:1.5), (awkward:1.4)',
'discomfort=(sweat:1.3), sad, embarrassed, looking to the side, ',
'submission=rolling eyes, ahegao, tongue out, drooling, saliva, sweat',
'pain=(scared expression:0.7), clenched teeth, ((tears)), <lora:scared-expression-v1.8:1>',
'teasing=smug,open mouth, <lora:Smug:1>,:3, naughty face',
'arrogant=smug, closed mouth, <lora:Smug:1>, smirk'
]
}
def process_string_tag(tag):
return tag
def process_int_tag(tag):
return int(tag)
def process_float_tag(tag):
return float(tag)
def process_boolean_tag(tag):
return True if (tag == "true") else False
prompt_tags = {
"sd_model": None,
"outpath_samples": process_string_tag,
"outpath_grids": process_string_tag,
"prompt_for_display": process_string_tag,
"prompt": process_string_tag,
"negative_prompt": process_string_tag,
"styles": process_string_tag,
"seed": process_int_tag,
"subseed_strength": process_float_tag,
"subseed": process_int_tag,
"seed_resize_from_h": process_int_tag,
"seed_resize_from_w": process_int_tag,
"sampler_index": process_int_tag,
"sampler_name": process_string_tag,
"batch_size": process_int_tag,
"n_iter": process_int_tag,
"steps": process_int_tag,
"cfg_scale": process_float_tag,
"width": process_int_tag,
"height": process_int_tag,
"restore_faces": process_boolean_tag,
"tiling": process_boolean_tag,
"do_not_save_samples": process_boolean_tag,
"do_not_save_grid": process_boolean_tag
}
def cmdargs(line):
args = shlex.split(line)
pos = 0
res = {}
while pos < len(args):
arg = args[pos]
assert arg.startswith("--"), f'must start with "--": {arg}'
assert pos+1 < len(args), f'missing argument for command line option {arg}'
tag = arg[2:]
if tag == "prompt" or tag == "negative_prompt":
pos += 1
prompt = args[pos]
pos += 1
while pos < len(args) and not args[pos].startswith("--"):
prompt += " "
prompt += args[pos]
pos += 1
res[tag] = prompt
continue
func = prompt_tags.get(tag, None)
assert func, f'unknown commandline option: {arg}'
val = args[pos+1]
if tag == "sampler_name":
val = sd_samplers.samplers_map.get(val.lower(), None)
res[tag] = func(val)
pos += 2
return res
class Script(scripts.Script):
def title(self):
return "miku.gg: inpaint expressions"
def ui(self, is_img2img):
# Dropdown for selecting the suffix list
suffix_list_dropdown = gr.Dropdown(label="Choose am emotion pack",
choices=list(suffix_lists.keys()),
value="Standard Emotions (29 imgs)",
type="value",
elem_id=self.elem_id("suffix_list_dropdown"))
# Generate checkbox for each suffix in the selected list
all_checkboxes = [item for sublist in suffix_lists.values() for item in sublist]
suffix_checkboxes = [gr.Checkbox(value=True, label=suffix.split('=')[0], elem_id=suffix) for suffix in all_checkboxes]
def update_checkboxes(selection):
new_checkboxes = [suffix.split('=')[0] for suffix in suffix_lists[selection]]
updates = []
for checkbox in suffix_checkboxes:
if checkbox.label in new_checkboxes:
updates.append(checkbox.update(visible=True))
else:
updates.append(checkbox.update(visible=False))
return tuple(updates)
# Bind the dropdown selection change to the function
suffix_list_dropdown.change(fn=update_checkboxes, inputs=[suffix_list_dropdown], outputs=[*suffix_checkboxes], show_progress=False)
return [suffix_list_dropdown, *suffix_checkboxes]
def run(self, p, suffix_list_dropdown: str, *suffix_checkbox_values: bool):
print(suffix_list_dropdown)
print(suffix_checkbox_values)
suffixes = suffix_lists.get(suffix_list_dropdown, None)
if not suffixes:
raise ValueError(f"No suffix list found for key: {suffix_list_dropdown}")
# Only process the selected suffixes
suffixes = [suffix for suffix, selected in zip(suffixes, suffix_checkbox_values) if selected]
p.do_not_save_grid = True
job_count = 0
jobs = []
for suffix in suffixes:
name, suff = suffix.split('=')
new_prompt = p.prompt + " " + suff
args = {"prompt": new_prompt, "name": name}
job_count += args.get("n_iter", p.n_iter)
jobs.append(args)
print(f"Will process {len(suffixes)} suffixes in {job_count} jobs.")
if p.seed == -1:
p.seed = int(random.randrange(4294967294))
state.job_count = job_count
# Create a directory with label being "expressions_" + current time
current_time = datetime.datetime.now().strftime("%Y%m%d-%H%M%S")
output_folder = f"outputs/expressions_packs/expressions_{current_time}"
os.makedirs(output_folder, exist_ok=True)
images = []
all_prompts = []
infotexts = []
for args in jobs:
state.job = f"{state.job_no + 1} out of {state.job_count}"
copy_p = copy.copy(p)
for k, v in args.items():
setattr(copy_p, k, v)
proc = process_images(copy_p)
current_images = []
# Remove the background of each image
for image in proc.images:
# Convert PIL Image to byte data
byte_arr = io.BytesIO()
image.save(byte_arr, format='PNG')
input_img = byte_arr.getvalue()
# Remove background using rembg
output_img_bytes = remove(input_img, session=new_session('isnet-anime'))
# Convert byte data back to PIL Image
output_img = Image.open(io.BytesIO(output_img_bytes))
# Append the output image to the list
current_images.append(output_img)
images.append(output_img)
# Save each image to the newly created folder
for img, name in zip(current_images, [args['name']] * len(current_images)):
img.save(os.path.join(output_folder, f"{name}.png"))
all_prompts += proc.all_prompts
infotexts += proc.infotexts
return Processed(p, images, p.seed, "", all_prompts=all_prompts, infotexts=infotexts)
|