prompts_from_file.py 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. import copy
  2. import math
  3. import os
  4. import random
  5. import sys
  6. import traceback
  7. import shlex
  8. import modules.scripts as scripts
  9. import gradio as gr
  10. from modules import sd_samplers
  11. from modules.processing import Processed, process_images
  12. from PIL import Image
  13. from modules.shared import opts, cmd_opts, state
  14. def process_string_tag(tag):
  15. return tag
  16. def process_int_tag(tag):
  17. return int(tag)
  18. def process_float_tag(tag):
  19. return float(tag)
  20. def process_boolean_tag(tag):
  21. return True if (tag == "true") else False
  22. prompt_tags = {
  23. "sd_model": None,
  24. "outpath_samples": process_string_tag,
  25. "outpath_grids": process_string_tag,
  26. "prompt_for_display": process_string_tag,
  27. "prompt": process_string_tag,
  28. "negative_prompt": process_string_tag,
  29. "styles": process_string_tag,
  30. "seed": process_int_tag,
  31. "subseed_strength": process_float_tag,
  32. "subseed": process_int_tag,
  33. "seed_resize_from_h": process_int_tag,
  34. "seed_resize_from_w": process_int_tag,
  35. "sampler_index": process_int_tag,
  36. "sampler_name": process_string_tag,
  37. "batch_size": process_int_tag,
  38. "n_iter": process_int_tag,
  39. "steps": process_int_tag,
  40. "cfg_scale": process_float_tag,
  41. "width": process_int_tag,
  42. "height": process_int_tag,
  43. "restore_faces": process_boolean_tag,
  44. "tiling": process_boolean_tag,
  45. "do_not_save_samples": process_boolean_tag,
  46. "do_not_save_grid": process_boolean_tag
  47. }
  48. def cmdargs(line):
  49. args = shlex.split(line)
  50. pos = 0
  51. res = {}
  52. while pos < len(args):
  53. arg = args[pos]
  54. assert arg.startswith("--"), f'must start with "--": {arg}'
  55. assert pos+1 < len(args), f'missing argument for command line option {arg}'
  56. tag = arg[2:]
  57. if tag == "prompt" or tag == "negative_prompt":
  58. pos += 1
  59. prompt = args[pos]
  60. pos += 1
  61. while pos < len(args) and not args[pos].startswith("--"):
  62. prompt += " "
  63. prompt += args[pos]
  64. pos += 1
  65. res[tag] = prompt
  66. continue
  67. func = prompt_tags.get(tag, None)
  68. assert func, f'unknown commandline option: {arg}'
  69. val = args[pos+1]
  70. if tag == "sampler_name":
  71. val = sd_samplers.samplers_map.get(val.lower(), None)
  72. res[tag] = func(val)
  73. pos += 2
  74. return res
  75. def load_prompt_file(file):
  76. if file is None:
  77. lines = []
  78. else:
  79. lines = [x.strip() for x in file.decode('utf8', errors='ignore').split("\n")]
  80. return None, "\n".join(lines), gr.update(lines=7)
  81. class Script(scripts.Script):
  82. def title(self):
  83. return "Prompts from file or textbox"
  84. def ui(self, is_img2img):
  85. checkbox_iterate = gr.Checkbox(label="Iterate seed every line", value=False, elem_id=self.elem_id("checkbox_iterate"))
  86. checkbox_iterate_batch = gr.Checkbox(label="Use same random seed for all lines", value=False, elem_id=self.elem_id("checkbox_iterate_batch"))
  87. prompt_txt = gr.Textbox(label="List of prompt inputs", lines=1, elem_id=self.elem_id("prompt_txt"))
  88. file = gr.File(label="Upload prompt inputs", type='bytes', elem_id=self.elem_id("file"))
  89. file.change(fn=load_prompt_file, inputs=[file], outputs=[file, prompt_txt, prompt_txt])
  90. # We start at one line. When the text changes, we jump to seven lines, or two lines if no \n.
  91. # We don't shrink back to 1, because that causes the control to ignore [enter], and it may
  92. # be unclear to the user that shift-enter is needed.
  93. prompt_txt.change(lambda tb: gr.update(lines=7) if ("\n" in tb) else gr.update(lines=2), inputs=[prompt_txt], outputs=[prompt_txt])
  94. return [checkbox_iterate, checkbox_iterate_batch, prompt_txt]
  95. def run(self, p, checkbox_iterate, checkbox_iterate_batch, prompt_txt: str):
  96. lines = [x.strip() for x in prompt_txt.splitlines()]
  97. lines = [x for x in lines if len(x) > 0]
  98. p.do_not_save_grid = True
  99. job_count = 0
  100. jobs = []
  101. for line in lines:
  102. if "--" in line:
  103. try:
  104. args = cmdargs(line)
  105. except Exception:
  106. print(f"Error parsing line {line} as commandline:", file=sys.stderr)
  107. print(traceback.format_exc(), file=sys.stderr)
  108. args = {"prompt": line}
  109. else:
  110. args = {"prompt": line}
  111. n_iter = args.get("n_iter", 1)
  112. if n_iter != 1:
  113. job_count += n_iter
  114. else:
  115. job_count += 1
  116. jobs.append(args)
  117. print(f"Will process {len(lines)} lines in {job_count} jobs.")
  118. if (checkbox_iterate or checkbox_iterate_batch) and p.seed == -1:
  119. p.seed = int(random.randrange(4294967294))
  120. state.job_count = job_count
  121. images = []
  122. all_prompts = []
  123. infotexts = []
  124. for n, args in enumerate(jobs):
  125. state.job = f"{state.job_no + 1} out of {state.job_count}"
  126. copy_p = copy.copy(p)
  127. for k, v in args.items():
  128. setattr(copy_p, k, v)
  129. proc = process_images(copy_p)
  130. images += proc.images
  131. if checkbox_iterate:
  132. p.seed = p.seed + (p.batch_size * p.n_iter)
  133. all_prompts += proc.all_prompts
  134. infotexts += proc.infotexts
  135. return Processed(p, images, p.seed, "", all_prompts=all_prompts, infotexts=infotexts)