Werli commited on
Commit
099a463
·
verified ·
1 Parent(s): 2d36110

Upload 12 files

Browse files

Hello! I'm back with some updates!
1. Removed Florence as it's outdated and there are already many alternatives available on HF. If I find another suitable alternative, I'll try to implement it.
2. After some experimentation with colors, I managed to create a new Gradio theme, which has both dark and light versions.
3. Realized that some models are good at tagging, while others aren't, but sometimes the 'bad' models are more accurate. This gave me an idea... So, I implemented a script to run two models. It basically tags images twice, replaces duplicates, and outputs a diversified result.
4. Lastly, fixed some minor issues.

It took me 3 days, but finally managed to finish! Have fun!

.gitattributes CHANGED
@@ -28,3 +28,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
28
  1girl.png filter=lfs diff=lfs merge=lfs -text
29
  images/image1.png filter=lfs diff=lfs merge=lfs -text
30
  images/image2.png filter=lfs diff=lfs merge=lfs -text
 
 
28
  1girl.png filter=lfs diff=lfs merge=lfs -text
29
  images/image1.png filter=lfs diff=lfs merge=lfs -text
30
  images/image2.png filter=lfs diff=lfs merge=lfs -text
31
+ images/1girl.png filter=lfs diff=lfs merge=lfs -text
app.py CHANGED
@@ -1,28 +1,21 @@
1
- import os,io,copy,json,requests,spaces,gradio as gr,numpy as np
2
- import argparse,huggingface_hub,onnxruntime as rt,pandas as pd,traceback,tempfile,zipfile,re,ast,time
3
- from datetime import datetime,timezone
4
  from collections import defaultdict
5
- from PIL import Image,ImageOps
6
- from modules.booru import booru_gradio,on_select
7
  from apscheduler.schedulers.background import BackgroundScheduler
8
- from modules.classifyTags import classify_tags,process_tags
9
- from modules.reorganizer_model import reorganizer_list,reorganizer_class
10
- from modules.tag_enhancer import prompt_enhancer
11
- from modules.florence2 import process_image,single_task_list,update_task_dropdown
12
 
13
  os.environ['PYTORCH_ENABLE_MPS_FALLBACK']='1'
 
 
14
 
15
- TITLE = "Multi-Tagger v1.2"
16
  DESCRIPTION = """
17
- Multi-Tagger is a versatile application for advanced image analysis and captioning. Perfect for AI artists or enthusiasts, it offers a range of features:
18
-
19
- - **Automatic Tag Categorization**: Tags are grouped into categories.
20
- - **Tag Enhancement**: Boost your prompts with enhanced descriptions using a built-in prompt enhancer.
21
- - **Reorganizer**: Use a reorganizer model to format tags into a natural-language description.
22
- - **Batch Support**: Upload and process multiple images simultaneously.
23
- - **Downloadable Output**: Get almost all results as downloadable `.txt`, `.json`, and `.png` files in a `.zip` archive.
24
- - **Image Fetcher**: Search for images from **Gelbooru** using flexible tag filters.
25
- - **CUDA** and **CPU** support.
26
  """
27
 
28
  # Dataset v3 series of models:
@@ -44,75 +37,59 @@ SWINV2_MODEL_IS_DSV1_REPO = "deepghs/idolsankaku-swinv2-tagger-v1"
44
  MODEL_FILENAME = "model.onnx"
45
  LABEL_FILENAME = "selected_tags.csv"
46
 
47
- kaomojis=['0_0','(o)_(o)','+_+','+_-','._.','<o>_<o>','<|>_<|>','=_=','>_<','3_3','6_9','>_o','@_@','^_^','o_o','u_u','x_x','|_|','||_||']
48
- def parse_args()->argparse.Namespace:parser=argparse.ArgumentParser();parser.add_argument('--score-slider-step',type=float,default=.05);parser.add_argument('--score-general-threshold',type=float,default=.35);parser.add_argument('--score-character-threshold',type=float,default=.85);parser.add_argument('--share',action='store_true');return parser.parse_args()
49
- def load_labels(dataframe)->list[str]:name_series=dataframe['name'];name_series=name_series.map(lambda x:x.replace('_',' ')if x not in kaomojis else x);tag_names=name_series.tolist();rating_indexes=list(np.where(dataframe['category']==9)[0]);general_indexes=list(np.where(dataframe['category']==0)[0]);character_indexes=list(np.where(dataframe['category']==4)[0]);return tag_names,rating_indexes,general_indexes,character_indexes
50
  def mcut_threshold(probs):sorted_probs=probs[probs.argsort()[::-1]];difs=sorted_probs[:-1]-sorted_probs[1:];t=difs.argmax();thresh=(sorted_probs[t]+sorted_probs[t+1])/2;return thresh
51
-
52
  class Timer:
53
- def __init__(self):self.start_time=time.perf_counter();self.checkpoints=[('Start',self.start_time)]
54
- def checkpoint(self,label='Checkpoint'):now=time.perf_counter();self.checkpoints.append((label,now))
55
- def report(self,is_clear_checkpoints=True):
56
- max_label_length=max(len(label)for(label,_)in self.checkpoints);prev_time=self.checkpoints[0][1]
57
- for(label,curr_time)in self.checkpoints[1:]:elapsed=curr_time-prev_time;print(f"{label.ljust(max_label_length)}: {elapsed:.3f} seconds");prev_time=curr_time
58
  if is_clear_checkpoints:self.checkpoints.clear();self.checkpoint()
59
  def report_all(self):
60
- print('\n> Execution Time Report:');max_label_length=max(len(label)for(label,_)in self.checkpoints)if len(self.checkpoints)>0 else 0;prev_time=self.start_time
61
- for(label,curr_time)in self.checkpoints[1:]:elapsed=curr_time-prev_time;print(f"{label.ljust(max_label_length)}: {elapsed:.3f} seconds");prev_time=curr_time
62
  total_time=self.checkpoints[-1][1]-self.start_time;print(f"{'Total Execution Time'.ljust(max_label_length)}: {total_time:.3f} seconds\n");self.checkpoints.clear()
63
- def restart(self):self.start_time=time.perf_counter();self.checkpoints=[('Start',self.start_time)]
64
  class Predictor:
65
  def __init__(self):
66
  self.model_target_size = None
67
  self.last_loaded_repo = None
68
  def download_model(self, model_repo):
69
- csv_path = huggingface_hub.hf_hub_download(
70
- model_repo,
71
- LABEL_FILENAME,
72
- )
73
- model_path = huggingface_hub.hf_hub_download(
74
- model_repo,
75
- MODEL_FILENAME,
76
- )
77
  return csv_path, model_path
78
  def load_model(self, model_repo):
79
  if model_repo == self.last_loaded_repo:
80
  return
81
-
82
  csv_path, model_path = self.download_model(model_repo)
83
-
84
  tags_df = pd.read_csv(csv_path)
85
  sep_tags = load_labels(tags_df)
86
-
87
  self.tag_names = sep_tags[0]
88
  self.rating_indexes = sep_tags[1]
89
  self.general_indexes = sep_tags[2]
90
  self.character_indexes = sep_tags[3]
91
-
92
  model = rt.InferenceSession(model_path)
93
  _, height, width, _ = model.get_inputs()[0].shape
94
  self.model_target_size = height
95
-
96
  self.last_loaded_repo = model_repo
97
  self.model = model
98
  def prepare_image(self, path):
99
  image = Image.open(path)
100
  image = image.convert("RGBA")
101
  target_size = self.model_target_size
102
-
103
  canvas = Image.new("RGBA", image.size, (255, 255, 255))
104
  canvas.alpha_composite(image)
105
  image = canvas.convert("RGB")
106
-
107
  # Pad image to square
108
  image_shape = image.size
109
  max_dim = max(image_shape)
110
  pad_left = (max_dim - image_shape[0]) // 2
111
  pad_top = (max_dim - image_shape[1]) // 2
112
-
113
  padded_image = Image.new("RGB", (max_dim, max_dim), (255, 255, 255))
114
  padded_image.paste(image, (pad_left, pad_top))
115
-
116
  # Resize
117
  if max_dim != target_size:
118
  padded_image = padded_image.resize(
@@ -134,211 +111,229 @@ class Predictor:
134
  else:
135
  with open(file_path, 'w+', encoding="utf-8") as file:
136
  file.write(content)
137
-
138
  return file_path
139
-
140
  def predict(
141
  self,
142
  gallery,
143
  model_repo,
 
144
  general_thresh,
145
  general_mcut_enabled,
146
  character_thresh,
147
  character_mcut_enabled,
148
  characters_merge_enabled,
149
- reorganizer_model_repo,
150
  additional_tags_prepend,
151
  additional_tags_append,
152
  tag_results,
153
  progress=gr.Progress()
154
  ):
155
- # Clear tag_results before starting a new prediction
156
- tag_results.clear()
157
-
158
- gallery_len = len(gallery)
159
- print(f"Predict load model: {model_repo}, gallery length: {gallery_len}")
160
-
161
- timer = Timer() # Create a timer
162
- progressRatio = 0.5 if reorganizer_model_repo else 1
163
- progressTotal = gallery_len + 1
164
- current_progress = 0
165
-
166
- self.load_model(model_repo)
 
 
 
 
 
 
 
 
 
 
167
  current_progress += progressRatio/progressTotal;
168
- progress(current_progress, desc="Initialize wd model finished")
169
- timer.checkpoint(f"Initialize wd model")
170
-
171
- txt_infos = []
172
- output_dir = tempfile.mkdtemp()
173
- if not os.path.exists(output_dir):
174
- os.makedirs(output_dir)
175
-
176
- sorted_general_strings = ""
177
- # Create categorized output string
178
- categorized_output_strings = []
179
- rating = None
180
- character_res = None
181
- general_res = None
182
-
183
- if reorganizer_model_repo:
184
- print(f"Reorganizer load model {reorganizer_model_repo}")
185
- reorganizer = reorganizer_class(reorganizer_model_repo, loadModel=True)
186
- current_progress += progressRatio/progressTotal;
187
- progress(current_progress, desc="Initialize reoganizer model finished")
188
- timer.checkpoint(f"Initialize reoganizer model")
189
-
190
- timer.report()
191
-
192
- prepend_list = [tag.strip() for tag in additional_tags_prepend.split(",") if tag.strip()]
193
- append_list = [tag.strip() for tag in additional_tags_append.split(",") if tag.strip()]
194
- if prepend_list and append_list:
195
- append_list = [item for item in append_list if item not in prepend_list]
196
-
197
- # Dictionary to track counters for each filename
198
- name_counters = defaultdict(int)
199
-
200
- for idx, value in enumerate(gallery):
201
- try:
202
- image_path = value[0]
203
- image_name = os.path.splitext(os.path.basename(image_path))[0]
204
-
205
- # Increment the counter for the current name
206
- name_counters[image_name] += 1
207
-
208
- if name_counters[image_name] > 1:
209
- image_name = f"{image_name}_{name_counters[image_name]:02d}"
210
-
211
- image = self.prepare_image(image_path)
212
-
213
- input_name = self.model.get_inputs()[0].name
214
- label_name = self.model.get_outputs()[0].name
215
- print(f"Gallery {idx:02d}: Starting run wd model...")
216
- preds = self.model.run([label_name], {input_name: image})[0]
217
-
218
- labels = list(zip(self.tag_names, preds[0].astype(float)))
219
-
220
- # First 4 labels are actually ratings: pick one with argmax
221
- ratings_names = [labels[i] for i in self.rating_indexes]
222
- rating = dict(ratings_names)
223
-
224
- # Then we have general tags: pick any where prediction confidence > threshold
225
- general_names = [labels[i] for i in self.general_indexes]
226
-
227
  if general_mcut_enabled:
228
- general_probs = np.array([x[1] for x in general_names])
229
- general_thresh = mcut_threshold(general_probs)
230
-
231
- general_res = [x for x in general_names if x[1] > general_thresh]
232
- general_res = dict(general_res)
233
-
234
- # Everything else is characters: pick any where prediction confidence > threshold
235
- character_names = [labels[i] for i in self.character_indexes]
236
-
237
  if character_mcut_enabled:
238
- character_probs = np.array([x[1] for x in character_names])
239
- character_thresh = mcut_threshold(character_probs)
240
- character_thresh = max(0.15, character_thresh)
241
-
242
- character_res = [x for x in character_names if x[1] > character_thresh]
243
- character_res = dict(character_res)
244
- character_list = list(character_res.keys())
245
-
246
- sorted_general_list = sorted(
247
- general_res.items(),
248
- key=lambda x: x[1],
249
- reverse=True,
250
- )
251
- sorted_general_list = [x[0] for x in sorted_general_list]
252
- # Remove values from character_list that already exist in sorted_general_list
253
- character_list = [item for item in character_list if item not in sorted_general_list]
254
- # Remove values from sorted_general_list that already exist in prepend_list or append_list
255
- if prepend_list:
256
- sorted_general_list = [item for item in sorted_general_list if item not in prepend_list]
257
- if append_list:
258
- sorted_general_list = [item for item in sorted_general_list if item not in append_list]
259
-
260
- sorted_general_list = prepend_list + sorted_general_list + append_list
261
-
262
- sorted_general_strings = ", ".join((character_list if characters_merge_enabled else []) + sorted_general_list).replace("(", "\(").replace(")", "\)")
263
-
264
- classified_tags, unclassified_tags = classify_tags(sorted_general_list)
265
-
266
- # Create a single string of ALL categorized tags for the current image
267
- categorized_output_string = ', '.join([', '.join(tags) for tags in classified_tags.values()])
268
- categorized_output_strings.append(categorized_output_string)
269
- # Collect all categorized output strings into a single string
270
- final_categorized_output = ', '.join(categorized_output_strings)
271
-
272
- # Create a .txt file for "Output (string)" and "Categorized Output (string)"
273
- txt_content = f"Output (string): {sorted_general_strings}\nCategorized Output (string): {final_categorized_output}"
274
- txt_file = self.create_file(txt_content, output_dir, f"{image_name}_output.txt")
275
- txt_infos.append({"path": txt_file, "name": f"{image_name}_output.txt"})
276
-
277
- # Create a .json file for "Categorized (tags)"
278
- json_content = json.dumps(classified_tags, indent=4)
279
- json_file = self.create_file(json_content, output_dir, f"{image_name}_categorized_tags.json")
280
- txt_infos.append({"path": json_file, "name": f"{image_name}_categorized_tags.json"})
281
-
282
- # Save a copy of the uploaded image in PNG format
283
- image_path = value[0]
284
- image = Image.open(image_path)
285
- image.save(os.path.join(output_dir, f"{image_name}.png"), format="PNG")
286
- txt_infos.append({"path": os.path.join(output_dir, f"{image_name}.png"), "name": f"{image_name}.png"})
287
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
288
  current_progress += progressRatio/progressTotal;
289
- progress(current_progress, desc=f"image{idx:02d}, predict finished")
290
- timer.checkpoint(f"image{idx:02d}, predict finished")
291
-
292
- if reorganizer_model_repo:
293
- print(f"Starting reorganizer...")
294
- reorganize_strings = reorganizer.reorganize(sorted_general_strings)
295
- reorganize_strings = re.sub(r" *Title: *", "", reorganize_strings)
296
- reorganize_strings = re.sub(r"\n+", ",", reorganize_strings)
297
- reorganize_strings = re.sub(r",,+", ",", reorganize_strings)
298
- sorted_general_strings += ",\n\n" + reorganize_strings
299
-
300
- current_progress += progressRatio/progressTotal;
301
- progress(current_progress, desc=f"image{idx:02d}, reorganizer finished")
302
- timer.checkpoint(f"image{idx:02d}, reorganizer finished")
303
-
304
- txt_file = self.create_file(sorted_general_strings, output_dir, image_name + ".txt")
305
- txt_infos.append({"path":txt_file, "name": image_name + ".txt"})
306
-
307
- # Store the result in tag_results using image_path as the key
308
- tag_results[image_path] = {
309
- "strings": sorted_general_strings,
310
- "strings2": categorized_output_string, # Store the categorized output string here
311
- "classified_tags": classified_tags,
312
- "rating": rating,
313
- "character_res": character_res,
314
- "general_res": general_res,
315
- "unclassified_tags": unclassified_tags,
316
- "enhanced_tags": "" # Initialize as empty string
317
- }
318
-
319
- timer.report()
320
- except Exception as e:
321
- print(traceback.format_exc())
322
- print("Error predict: " + str(e))
323
- # Zip creation logic:
324
- download = []
325
- if txt_infos is not None and len(txt_infos) > 0:
326
- downloadZipPath = os.path.join(output_dir, "Multi-tagger-" + datetime.now().strftime("%Y%m%d-%H%M%S") + ".zip")
327
- with zipfile.ZipFile(downloadZipPath, 'w', zipfile.ZIP_DEFLATED) as taggers_zip:
328
- for info in txt_infos:
329
- # Get file name from lookup
330
- taggers_zip.write(info["path"], arcname=info["name"])
331
- download.append(downloadZipPath)
332
- # End zip creation logic
333
- if reorganizer_model_repo:
334
- reorganizer.release_vram()
335
- del reorganizer
336
-
337
- progress(1, desc=f"Predict completed")
338
- timer.report_all() # Print all recorded times
339
- print("Predict is complete.")
340
-
341
- return download, sorted_general_strings, final_categorized_output, classified_tags, rating, character_res, general_res, unclassified_tags, tag_results
 
342
  def get_selection_from_gallery(gallery: list, tag_results: dict, selected_state: gr.SelectData):
343
  if not selected_state:
344
  return selected_state
@@ -350,20 +345,20 @@ def get_selection_from_gallery(gallery: list, tag_results: dict, selected_state:
350
  "character_res": "",
351
  "general_res": "",
352
  "unclassified_tags": "{}",
353
- "enhanced_tags": ""
354
  }
355
  if selected_state.value["image"]["path"] in tag_results:
356
  tag_result = tag_results[selected_state.value["image"]["path"]]
357
- return (selected_state.value["image"]["path"], selected_state.value["caption"]), tag_result["strings"], tag_result["strings2"], tag_result["classified_tags"], tag_result["rating"], tag_result["character_res"], tag_result["general_res"], tag_result["unclassified_tags"], tag_result["enhanced_tags"]
358
- def append_gallery(gallery:list,image:str):
359
  if gallery is None:gallery=[]
360
- if not image:return gallery,None
361
- gallery.append(image);return gallery,None
362
- def extend_gallery(gallery:list,images):
363
  if gallery is None:gallery=[]
364
  if not images:return gallery
365
  gallery.extend(images);return gallery
366
- def remove_image_from_gallery(gallery:list,selected_image:str):
367
  if not gallery or not selected_image:return gallery
368
  selected_image=ast.literal_eval(selected_image)
369
  if selected_image in gallery:gallery.remove(selected_image)
@@ -371,26 +366,25 @@ def remove_image_from_gallery(gallery:list,selected_image:str):
371
  args = parse_args()
372
  predictor = Predictor()
373
  dropdown_list = [
374
- EVA02_LARGE_MODEL_DSV3_REPO,
375
- SWINV2_MODEL_DSV3_REPO,
376
- CONV_MODEL_DSV3_REPO,
377
- VIT_MODEL_DSV3_REPO,
378
- VIT_LARGE_MODEL_DSV3_REPO,
379
  # ---
380
- MOAT_MODEL_DSV2_REPO,
381
- SWIN_MODEL_DSV2_REPO,
382
- CONV_MODEL_DSV2_REPO,
383
- CONV2_MODEL_DSV2_REPO,
384
- VIT_MODEL_DSV2_REPO,
385
  # ---
386
- SWINV2_MODEL_IS_DSV1_REPO,
387
- EVA02_LARGE_MODEL_IS_DSV1_REPO,
388
  ]
389
-
390
  def _restart_space():
391
  HF_TOKEN=os.getenv('HF_TOKEN')
392
  if not HF_TOKEN:raise ValueError('HF_TOKEN environment variable is not set.')
393
- huggingface_hub.HfApi().restart_space(repo_id='Werli/Multi-Tagger',token=HF_TOKEN,factory_reboot=False)
394
  scheduler=BackgroundScheduler()
395
  # Add a job to restart the space every 2 days (172800 seconds)
396
  restart_space_job = scheduler.add_job(_restart_space, "interval", seconds=172800)
@@ -399,235 +393,128 @@ next_run_time_utc=restart_space_job.next_run_time.astimezone(timezone.utc)
399
  NEXT_RESTART=f"Next Restart: {next_run_time_utc.strftime('%Y-%m-%d %H:%M:%S')} (UTC) - The space will restart every 2 days to ensure stability and performance. It uses a background scheduler to handle the restart process."
400
 
401
  css = """
402
- #output {height: 500px; overflow: auto; border: 1px solid #ccc;}
403
- label.float.svelte-i3tvor {position: relative !important;}
404
- .reduced-height.svelte-11chud3 {height: calc(80% - var(--size-10));}
 
 
 
 
 
 
405
  """
406
-
407
- with gr.Blocks(title=TITLE, css=css, theme=gr.themes.Soft(), fill_width=True) as demo:
408
  gr.Markdown(value=f"<h1 style='text-align: center; margin-bottom: 1rem'>{TITLE}</h1>")
409
- gr.Markdown(value=DESCRIPTION)
410
- gr.Markdown(NEXT_RESTART)
411
  with gr.Tab(label="Waifu Diffusion"):
412
  with gr.Row():
413
  with gr.Column():
414
- submit = gr.Button(value="Submit", variant="primary", size="lg")
415
  with gr.Column(variant="panel"):
416
  # Create an Image component for uploading images
417
  image_input = gr.Image(label="Upload an Image or clicking paste from clipboard button", type="filepath", sources=["upload", "clipboard"], height=150)
418
  with gr.Row():
419
  upload_button = gr.UploadButton("Upload multiple images", file_types=["image"], file_count="multiple", size="sm")
420
  remove_button = gr.Button("Remove Selected Image", size="sm")
421
- gallery = gr.Gallery(columns=5, rows=5, show_share_button=False, interactive=True, height="500px", label="Grid of images")
422
- model_repo = gr.Dropdown(
423
- dropdown_list,
424
- value=EVA02_LARGE_MODEL_DSV3_REPO,
425
- label="Model",
426
- )
427
- with gr.Row():
428
- general_thresh = gr.Slider(
429
- 0,
430
- 1,
431
- step=args.score_slider_step,
432
- value=args.score_general_threshold,
433
- label="General Tags Threshold",
434
- scale=3,
435
- )
436
- general_mcut_enabled = gr.Checkbox(
437
- value=False,
438
- label="Use MCut threshold",
439
- scale=1,
440
  )
 
 
 
 
 
441
  with gr.Row():
442
- character_thresh = gr.Slider(
443
- 0,
444
- 1,
445
- step=args.score_slider_step,
446
- value=args.score_character_threshold,
447
- label="Character Tags Threshold",
448
- scale=3,
449
- )
450
- character_mcut_enabled = gr.Checkbox(
451
- value=False,
452
- label="Use MCut threshold",
453
- scale=1,
454
- )
455
  with gr.Row():
456
- characters_merge_enabled = gr.Checkbox(
457
- value=True,
458
- label="Merge characters into the string output",
459
- scale=1,
460
- )
461
  with gr.Row():
462
- reorganizer_model_repo = gr.Dropdown(
463
- [None] + reorganizer_list,
464
- value=None,
465
- label="Reorganizer Model",
466
- info="Use a model to create a description for you",
467
- )
468
  with gr.Row():
469
  additional_tags_prepend = gr.Text(label="Prepend Additional tags (comma split)")
470
  additional_tags_append = gr.Text(label="Append Additional tags (comma split)")
471
  with gr.Row():
472
  clear = gr.ClearButton(
473
- components=[
474
- gallery,
475
- model_repo,
476
- general_thresh,
477
- general_mcut_enabled,
478
- character_thresh,
479
- character_mcut_enabled,
480
- characters_merge_enabled,
481
- reorganizer_model_repo,
482
- additional_tags_prepend,
483
- additional_tags_append,
484
- ],
485
- variant="secondary",
486
- size="lg",
487
- )
488
  with gr.Column(variant="panel"):
489
- download_file = gr.File(label="Download includes: All outputs* and image(s)") # 0
490
  character_res = gr.Label(label="Output (characters)") # 1
491
- sorted_general_strings = gr.Textbox(label="Output (string)*", show_label=True, show_copy_button=True) # 2
492
- final_categorized_output = gr.Textbox(label="Categorized (string)* - If it's too long, select an image to display tags correctly.", show_label=True, show_copy_button=True) # 3
493
- pe_generate_btn = gr.Button(value="ENHANCE TAGS", size="lg", variant="primary") # 4
494
- enhanced_tags = gr.Textbox(label="Enhanced Tags", show_label=True, show_copy_button=True) # 5
495
- prompt_enhancer_model = gr.Radio(["Medium", "Long", "Flux"], label="Model Choice", value="Medium", info="Enhance your prompts with Medium or Long answers") # 6
496
- categorized = gr.JSON(label="Categorized (tags)* - JSON") # 7
497
- rating = gr.Label(label="Rating") # 8
498
- general_res = gr.Label(label="Output (tags)") # 9
499
- unclassified = gr.JSON(label="Unclassified (tags)") # 10
500
- clear.add(
501
- [
502
- download_file,
503
- sorted_general_strings,
504
- final_categorized_output,
505
- categorized,
506
- rating,
507
- character_res,
508
- general_res,
509
- unclassified,
510
- prompt_enhancer_model,
511
- enhanced_tags,
512
- ]
513
- )
514
  tag_results = gr.State({})
515
  # Define the event listener to add the uploaded image to the gallery
516
  image_input.change(append_gallery, inputs=[gallery, image_input], outputs=[gallery, image_input])
517
- # When the upload button is clicked, add the new images to the gallery
518
  upload_button.upload(extend_gallery, inputs=[gallery, upload_button], outputs=gallery)
519
  # Event to update the selected image when an image is clicked in the gallery
520
  selected_image = gr.Textbox(label="Selected Image", visible=False)
521
- gallery.select(get_selection_from_gallery,inputs=[gallery, tag_results],outputs=[selected_image, sorted_general_strings, final_categorized_output, categorized, rating, character_res, general_res, unclassified, enhanced_tags])
522
  # Event to remove a selected image from the gallery
523
  remove_button.click(remove_image_from_gallery, inputs=[gallery, selected_image], outputs=gallery)
524
- # Event to for the Prompt Enhancer Button
525
- pe_generate_btn.click(lambda tags,model:prompt_enhancer('','',tags,model)[0],inputs=[final_categorized_output,prompt_enhancer_model],outputs=[enhanced_tags])
526
- submit.click(
527
- predictor.predict,
528
- inputs=[
529
- gallery,
530
- model_repo,
531
- general_thresh,
532
- general_mcut_enabled,
533
- character_thresh,
534
- character_mcut_enabled,
535
- characters_merge_enabled,
536
- reorganizer_model_repo,
537
- additional_tags_prepend,
538
- additional_tags_append,
539
- tag_results,
540
- ],
541
- outputs=[download_file, sorted_general_strings, final_categorized_output, categorized, rating, character_res, general_res, unclassified, tag_results,],
542
- )
543
  gr.Examples(
544
  [["images/1girl.png", VIT_LARGE_MODEL_DSV3_REPO, 0.35, False, 0.85, False]],
545
- inputs=[
546
- image_input,
547
- model_repo,
548
- general_thresh,
549
- general_mcut_enabled,
550
- character_thresh,
551
- character_mcut_enabled,
552
- ],
553
- )
554
- with gr.Tab(label="Florence 2 Image Captioning"):
555
- with gr.Row():
556
- with gr.Column(variant="panel"):
557
- input_img = gr.Image(label="Input Picture")
558
- task_type = gr.Radio(choices=['Single task', 'Cascaded task'], label='Task type selector', value='Single task')
559
- task_prompt = gr.Dropdown(choices=single_task_list, label="Task Prompt", value="Caption")
560
- task_type.change(fn=update_task_dropdown, inputs=task_type, outputs=task_prompt)
561
- text_input = gr.Textbox(label="Text Input (optional)")
562
- submit_btn = gr.Button(value="Submit")
563
- with gr.Column(variant="panel"):
564
- output_text = gr.Textbox(label="Output Text", show_label=True, show_copy_button=True, lines=8)
565
- output_img = gr.Image(label="Output Image")
566
- gr.Examples(
567
- examples=[
568
- ["images/image1.png", 'Object Detection'],
569
- ["images/image2.png", 'OCR with Region']
570
- ],
571
- inputs=[input_img, task_prompt],
572
- outputs=[output_text, output_img],
573
- fn=process_image,
574
- cache_examples=False,
575
- label='Try examples'
576
- )
577
- submit_btn.click(process_image, [input_img, task_prompt, text_input], [output_text, output_img])
578
  with gr.Tab("Booru Image Fetcher"):
579
  with gr.Row():
580
  with gr.Column():
581
  gr.Markdown("### ⚙️ Search Parameters")
582
- site = gr.Dropdown(label="Select Source", choices=["Gelbooru", "Rule34", "Xbooru"], value="Xbooru")
583
- Tags = gr.Textbox(label="Tags (comma-separated)", placeholder="e.g. solo, 1girl, 1boy, artist name, character, black hair, granblue fantasy, ...")
584
- exclude_tags = gr.Textbox(label="Exclude Tags (comma-separated)", placeholder="e.g. animated, watermark, username, ...")
585
  score = gr.Number(label="Minimum Score", value=0)
586
  count = gr.Slider(label="Number of Images", minimum=1, maximum=20, step=1, value=1)
587
  Safe = gr.Checkbox(label="Include Safe", value=True)
588
  Questionable = gr.Checkbox(label="Include Questionable", value=True)
589
- Explicit = gr.Checkbox(label="Include Explicit", value=False)
590
  submit_btn = gr.Button("Fetch Images", variant="primary")
591
-
592
  with gr.Column():
593
  gr.Markdown("### 📄 Results")
594
  images_output = gr.Gallery(label="Images", columns=3, rows=2, object_fit="contain", height=500)
595
- tags_output = gr.Textbox(label="Tags", placeholder="Select an image to show tags", lines=5, show_copy_button=True)
596
  post_url_output = gr.Textbox(label="Post URL", lines=1, show_copy_button=True)
597
  image_url_output = gr.Textbox(label="Image URL", lines=1, show_copy_button=True)
598
-
599
  # State to store tags, URLs
600
  tags_state = gr.State([])
601
  post_url_state = gr.State([])
602
  image_url_state = gr.State([])
603
-
604
- submit_btn.click(
605
- fn=booru_gradio,
606
- inputs=[Tags, exclude_tags, score, count, Safe, Questionable, Explicit, site],
607
- outputs=[images_output, tags_state, post_url_state, image_url_state],
608
- )
609
-
610
- images_output.select(
611
- fn=on_select,
612
- inputs=[tags_state, post_url_state, image_url_state],
613
- outputs=[tags_output, post_url_output, image_url_output],
614
- )
615
- gr.Markdown("""
616
- ---
617
- ComfyUI version: [Comfyui-Gelbooru](https://github.com/1mckw/Comfyui-Gelbooru)
618
- """)
619
- with gr.Tab(label="Categorizer++"):
620
  with gr.Row():
621
  with gr.Column(variant="panel"):
622
- input_tags = gr.Textbox(label="Input Tags", placeholder="1girl, cat, horns, blue hair, ...\nor\n? 1girl 1234567? cat 1234567? horns 1234567? blue hair 1234567? ...", lines=4)
623
- submit_button = gr.Button(value="Submit", variant="primary", size="lg")
624
  with gr.Column(variant="panel"):
625
  categorized_string = gr.Textbox(label="Categorized (string)", show_label=True, show_copy_button=True, lines=8)
626
  categorized_json = gr.JSON(label="Categorized (tags) - JSON")
627
  submit_button.click(process_tags, inputs=[input_tags], outputs=[categorized_string, categorized_json])
628
  with gr.Column(variant="panel"):
629
- pe_generate_btn = gr.Button(value="ENHANCE TAGS", size="lg", variant="primary")
630
- enhanced_tags = gr.Textbox(label="Enhanced Tags", show_label=True, show_copy_button=True)
631
- prompt_enhancer_model = gr.Radio(["Medium", "Long", "Flux"], label="Model Choice", value="Medium", info="Enhance your prompts with Medium or Long answers")
632
- pe_generate_btn.click(lambda tags,model:prompt_enhancer('','',tags,model)[0],inputs=[categorized_string,prompt_enhancer_model],outputs=[enhanced_tags])
633
- demo.queue(max_size=2).launch()
 
1
+ import os, io, copy, json, requests, spaces, gradio as gr, numpy as np
2
+ import argparse, huggingface_hub, onnxruntime as rt, pandas as pd, traceback, tempfile, zipfile, re, ast, time
3
+ from datetime import datetime, timezone
4
  from collections import defaultdict
5
+ from PIL import Image, ImageOps
6
+ from modules.booru import booru_gradio, on_select
7
  from apscheduler.schedulers.background import BackgroundScheduler
8
+ from modules.classifyTags import classify_tags, process_tags
9
+ from modules.beautify_model import beautify_list, beautify_class
10
+ from modules.tag_enhancer import prompt_summarizer
 
11
 
12
  os.environ['PYTORCH_ENABLE_MPS_FALLBACK']='1'
13
+ os.environ['OMP_NUM_THREADS'] = '8' # Optimize CPU utilization? Test...
14
+ #os.environ['TF_ENABLE_ONEDNN_OPTS'] = '0'
15
 
16
+ TITLE = "Multi-Tagger v1.3"
17
  DESCRIPTION = """
18
+ Multi-Tagger is a versatile application for advanced image analysis and captioning. Supports <b>CUDA</b> and <b>CPU</b>.
 
 
 
 
 
 
 
 
19
  """
20
 
21
  # Dataset v3 series of models:
 
37
  MODEL_FILENAME = "model.onnx"
38
  LABEL_FILENAME = "selected_tags.csv"
39
 
40
+ kaomojis=['0_0', '(o)_(o)', '+_+', '+_-', '._.', '<o>_<o>', '<|>_<|>', '=_=', '>_<', '3_3', '6_9', '>_o', '@_@', '^_^', 'o_o', 'u_u', 'x_x', '|_|', '||_||']
41
+ def parse_args()->argparse.Namespace:parser=argparse.ArgumentParser();parser.add_argument('--score-slider-step', type=float, default=.05);parser.add_argument('--score-general-threshold', type=float, default=.35);parser.add_argument('--score-character-threshold', type=float, default=.85);parser.add_argument('--share', action='store_true');return parser.parse_args()
42
+ def load_labels(dataframe)->list[str]:name_series=dataframe['name'];name_series=name_series.map(lambda x:x.replace('_', ' ')if x not in kaomojis else x);tag_names=name_series.tolist();rating_indexes=list(np.where(dataframe['category']==9)[0]);general_indexes=list(np.where(dataframe['category']==0)[0]);character_indexes=list(np.where(dataframe['category']==4)[0]);return tag_names, rating_indexes, general_indexes, character_indexes
43
  def mcut_threshold(probs):sorted_probs=probs[probs.argsort()[::-1]];difs=sorted_probs[:-1]-sorted_probs[1:];t=difs.argmax();thresh=(sorted_probs[t]+sorted_probs[t+1])/2;return thresh
 
44
  class Timer:
45
+ def __init__(self):self.start_time=time.perf_counter();self.checkpoints=[('Start', self.start_time)]
46
+ def checkpoint(self, label='Checkpoint'):now=time.perf_counter();self.checkpoints.append((label, now))
47
+ def report(self, is_clear_checkpoints=True):
48
+ max_label_length=max(len(label)for(label, _)in self.checkpoints);prev_time=self.checkpoints[0][1]
49
+ for(label, curr_time)in self.checkpoints[1:]:elapsed=curr_time-prev_time;print(f"{label.ljust(max_label_length)}: {elapsed:.3f} seconds");prev_time=curr_time
50
  if is_clear_checkpoints:self.checkpoints.clear();self.checkpoint()
51
  def report_all(self):
52
+ print('\n> Execution Time Report:');max_label_length=max(len(label)for(label, _)in self.checkpoints)if len(self.checkpoints)>0 else 0;prev_time=self.start_time
53
+ for(label, curr_time)in self.checkpoints[1:]:elapsed=curr_time-prev_time;print(f"{label.ljust(max_label_length)}: {elapsed:.3f} seconds");prev_time=curr_time
54
  total_time=self.checkpoints[-1][1]-self.start_time;print(f"{'Total Execution Time'.ljust(max_label_length)}: {total_time:.3f} seconds\n");self.checkpoints.clear()
55
+ def restart(self):self.start_time=time.perf_counter();self.checkpoints=[('Start', self.start_time)]
56
  class Predictor:
57
  def __init__(self):
58
  self.model_target_size = None
59
  self.last_loaded_repo = None
60
  def download_model(self, model_repo):
61
+ csv_path = huggingface_hub.hf_hub_download(model_repo, LABEL_FILENAME, )
62
+ model_path = huggingface_hub.hf_hub_download(model_repo, MODEL_FILENAME, )
 
 
 
 
 
 
63
  return csv_path, model_path
64
  def load_model(self, model_repo):
65
  if model_repo == self.last_loaded_repo:
66
  return
 
67
  csv_path, model_path = self.download_model(model_repo)
 
68
  tags_df = pd.read_csv(csv_path)
69
  sep_tags = load_labels(tags_df)
 
70
  self.tag_names = sep_tags[0]
71
  self.rating_indexes = sep_tags[1]
72
  self.general_indexes = sep_tags[2]
73
  self.character_indexes = sep_tags[3]
 
74
  model = rt.InferenceSession(model_path)
75
  _, height, width, _ = model.get_inputs()[0].shape
76
  self.model_target_size = height
 
77
  self.last_loaded_repo = model_repo
78
  self.model = model
79
  def prepare_image(self, path):
80
  image = Image.open(path)
81
  image = image.convert("RGBA")
82
  target_size = self.model_target_size
 
83
  canvas = Image.new("RGBA", image.size, (255, 255, 255))
84
  canvas.alpha_composite(image)
85
  image = canvas.convert("RGB")
 
86
  # Pad image to square
87
  image_shape = image.size
88
  max_dim = max(image_shape)
89
  pad_left = (max_dim - image_shape[0]) // 2
90
  pad_top = (max_dim - image_shape[1]) // 2
 
91
  padded_image = Image.new("RGB", (max_dim, max_dim), (255, 255, 255))
92
  padded_image.paste(image, (pad_left, pad_top))
 
93
  # Resize
94
  if max_dim != target_size:
95
  padded_image = padded_image.resize(
 
111
  else:
112
  with open(file_path, 'w+', encoding="utf-8") as file:
113
  file.write(content)
 
114
  return file_path
 
115
  def predict(
116
  self,
117
  gallery,
118
  model_repo,
119
+ model_repo_2,
120
  general_thresh,
121
  general_mcut_enabled,
122
  character_thresh,
123
  character_mcut_enabled,
124
  characters_merge_enabled,
125
+ beautify_model_repo,
126
  additional_tags_prepend,
127
  additional_tags_append,
128
  tag_results,
129
  progress=gr.Progress()
130
  ):
131
+ # Clear tag_results before starting a new prediction
132
+ tag_results.clear()
133
+ gallery_len = len(gallery)
134
+ print(f"Predict load model: {model_repo}, gallery length: {gallery_len}")
135
+ timer = Timer() # Create a timer
136
+ progressRatio = 0.5 if beautify_model_repo else 1
137
+ progressTotal = gallery_len + 1
138
+ current_progress = 0
139
+ # Initialize variables that need to be accessible throughout the function
140
+ final_categorized_output = ""
141
+ categorized_output_strings = []
142
+ txt_infos = []
143
+ output_dir = tempfile.mkdtemp()
144
+ if not os.path.exists(output_dir):
145
+ os.makedirs(output_dir)
146
+ self.load_model(model_repo)
147
+ current_progress += progressRatio/progressTotal;
148
+ progress(current_progress, desc="Initialize wd model finished")
149
+ timer.checkpoint(f"Initialize wd model")
150
+ if beautify_model_repo:
151
+ print(f"Load model {beautify_model_repo}")
152
+ beautify = beautify_class(beautify_model_repo, loadModel=True)
153
  current_progress += progressRatio/progressTotal;
154
+ progress(current_progress, desc="Initialize beautify model finished")
155
+ timer.checkpoint(f"Initialize beautify model")
156
+ timer.report()
157
+ # Dictionary to track counters for each filename
158
+ name_counters = defaultdict(int)
159
+ for idx, value in enumerate(gallery):
160
+ try:
161
+ image_path = value[0]
162
+ image_name = os.path.splitext(os.path.basename(image_path))[0]
163
+ # Increment the counter for the current name
164
+ name_counters[image_name] += 1
165
+ if name_counters[image_name] > 1:
166
+ image_name = f"{image_name}_{name_counters[image_name]:02d}"
167
+ image = self.prepare_image(image_path)
168
+ # Run first model
169
+ print(f"Gallery {idx:02d}: Starting run first model ({model_repo})...")
170
+ self.load_model(model_repo)
171
+ input_name = self.model.get_inputs()[0].name
172
+ label_name = self.model.get_outputs()[0].name
173
+ preds = self.model.run([label_name], {input_name: image})[0]
174
+ labels = list(zip(self.tag_names, preds[0].astype(float)))
175
+ # Process first model results
176
+ ratings_names = [labels[i] for i in self.rating_indexes]
177
+ rating = dict(ratings_names)
178
+
179
+ general_names = [labels[i] for i in self.general_indexes]
180
+ if general_mcut_enabled:
181
+ general_probs = np.array([x[1] for x in general_names])
182
+ general_thresh_temp = mcut_threshold(general_probs)
183
+ else:
184
+ general_thresh_temp = general_thresh
185
+ general_res = [x for x in general_names if x[1] > general_thresh_temp]
186
+ general_res = dict(general_res)
187
+ character_names = [labels[i] for i in self.character_indexes]
188
+ if character_mcut_enabled:
189
+ character_probs = np.array([x[1] for x in character_names])
190
+ character_thresh_temp = mcut_threshold(character_probs)
191
+ character_thresh_temp = max(0.15, character_thresh_temp)
192
+ else:
193
+ character_thresh_temp = character_thresh
194
+ character_res = [x for x in character_names if x[1] > character_thresh_temp]
195
+ character_res = dict(character_res)
196
+ # Collect tags from first model
197
+ character_list_1 = list(character_res.keys())
198
+ sorted_general_list_1 = sorted(general_res.items(), key=lambda x: x[1], reverse=True)
199
+ sorted_general_list_1 = [x[0] for x in sorted_general_list_1]
200
+ # Run second model if selected and different from first
201
+ if model_repo_2 and model_repo_2 != model_repo:
202
+ print(f"Gallery {idx:02d}: Starting run second model ({model_repo_2})...")
203
+ self.load_model(model_repo_2)
204
+ preds_2 = self.model.run([label_name], {input_name: image})[0]
205
+ labels_2 = list(zip(self.tag_names, preds_2[0].astype(float)))
206
+ # Process second model results
207
+ general_names_2 = [labels_2[i] for i in self.general_indexes]
 
 
 
 
 
208
  if general_mcut_enabled:
209
+ general_probs_2 = np.array([x[1] for x in general_names_2])
210
+ general_thresh_temp_2 = mcut_threshold(general_probs_2)
211
+ else:
212
+ general_thresh_temp_2 = general_thresh
213
+ general_res_2 = [x for x in general_names_2 if x[1] > general_thresh_temp_2]
214
+ general_res_2 = dict(general_res_2)
215
+ character_names_2 = [labels_2[i] for i in self.character_indexes]
 
 
216
  if character_mcut_enabled:
217
+ character_probs_2 = np.array([x[1] for x in character_names_2])
218
+ character_thresh_temp_2 = mcut_threshold(character_probs_2)
219
+ character_thresh_temp_2 = max(0.15, character_thresh_temp_2)
220
+ else:
221
+ character_thresh_temp_2 = character_thresh
222
+ character_res_2 = [x for x in character_names_2 if x[1] > character_thresh_temp_2]
223
+ character_res_2 = dict(character_res_2)
224
+ # Collect tags from second model
225
+ character_list_2 = list(character_res_2.keys())
226
+ sorted_general_list_2 = sorted(general_res_2.items(), key=lambda x: x[1], reverse=True)
227
+ sorted_general_list_2 = [x[0] for x in sorted_general_list_2]
228
+ # Combine results from both models (+ remove duplicates)
229
+ combined_character_list = list(set(character_list_1 + character_list_2))
230
+ combined_general_list = list(set(sorted_general_list_1 + sorted_general_list_2))
231
+ else:
232
+ # Only first model was used
233
+ combined_character_list = character_list_1
234
+ combined_general_list = sorted_general_list_1
235
+ # Remove values from combined_character_list that already exist in combined_general_list
236
+ combined_character_list = [item for item in combined_character_list if item not in combined_general_list]
237
+ # Handle prepend/append tags
238
+ prepend_list = [tag.strip() for tag in additional_tags_prepend.split(",") if tag.strip()]
239
+ append_list = [tag.strip() for tag in additional_tags_append.split(",") if tag.strip()]
240
+ if prepend_list and append_list:
241
+ append_list = [item for item in append_list if item not in prepend_list]
242
+ # Remove values from combined_general_list that already exist in prepend_list or append_list
243
+ if prepend_list:
244
+ combined_general_list = [item for item in combined_general_list if item not in prepend_list]
245
+ if append_list:
246
+ combined_general_list = [item for item in combined_general_list if item not in append_list]
247
+ combined_general_list = prepend_list + combined_general_list + append_list
248
+ sorted_general_strings = ", ".join((combined_character_list if characters_merge_enabled else []) + combined_general_list).replace("(", "\\(").replace(")", "\\)")
249
+ classified_tags, unclassified_tags = classify_tags(combined_general_list)
250
+ # Create a single string of ALL categorized tags for the current image
251
+ categorized_output_string = ', '.join([', '.join(tags) for tags in classified_tags.values()])
252
+ categorized_output_strings.append(categorized_output_string)
253
+ # Collect all categorized output strings into a single string
254
+ final_categorized_output = ', '.join(categorized_output_strings).replace("(", "\\(").replace(")", "\\)")
255
+ # Create a .txt file for "Output (string)" and "Categorized Output (string)"
256
+ txt_content = f"Output (string): {sorted_general_strings}\nCategorized Output (string): {final_categorized_output}"
257
+ txt_file = self.create_file(txt_content, output_dir, f"{image_name}_output.txt")
258
+ txt_infos.append({"path": txt_file, "name": f"{image_name}_output.txt"})
259
+ # Create a .json file for "Categorized (tags)"
260
+ json_content = json.dumps(classified_tags, indent=4)
261
+ json_file = self.create_file(json_content, output_dir, f"{image_name}_categorized_tags.json")
262
+ txt_infos.append({"path": json_file, "name": f"{image_name}_categorized_tags.json"})
263
+ # Save a copy of the uploaded image in PNG format
264
+ image_path = value[0]
265
+ image = Image.open(image_path)
266
+ image.save(os.path.join(output_dir, f"{image_name}.png"), format="PNG")
267
+ txt_infos.append({"path": os.path.join(output_dir, f"{image_name}.png"), "name": f"{image_name}.png"})
268
+ current_progress += progressRatio/progressTotal;
269
+ progress(current_progress, desc=f"image{idx:02d}, predict finished")
270
+ timer.checkpoint(f"image{idx:02d}, predict finished")
271
+ if beautify_model_repo:
272
+ print(f"Starting beautify...")
273
+ beautify_strings = beautify.beautify(sorted_general_strings)
274
+ # Handle potential None returns from beautify
275
+ if beautify_strings is None:
276
+ beautify_strings = "Beautify failed - see console logs"
277
+ else:
278
+ beautify_strings = re.sub(r"Title:", "", beautify_strings)
279
+ beautify_strings = re.sub(r"\n+", ",", beautify_strings)
280
+ beautify_strings = re.sub(r",,+", ",", beautify_strings)
281
+ sorted_general_strings += ",\n\n" + beautify_strings
282
  current_progress += progressRatio/progressTotal;
283
+ progress(current_progress, desc=f"image{idx:02d}, beautify finished!")
284
+ timer.checkpoint(f"image{idx:02d}, beautify finished!")
285
+ txt_file = self.create_file(sorted_general_strings, output_dir, image_name + ".txt")
286
+ txt_infos.append({"path":txt_file, "name": image_name + ".txt"})
287
+
288
+ # Store the result in tag_results using image_path as the key
289
+ tag_results[image_path] = {
290
+ "strings": sorted_general_strings,
291
+ "strings2": categorized_output_string, # Store the categorized output string here
292
+ "classified_tags": classified_tags,
293
+ "rating": rating,
294
+ "character_res": character_res,
295
+ "general_res": general_res,
296
+ "unclassified_tags": unclassified_tags,
297
+ "summarize_tags": "" # Initialize as empty string
298
+ }
299
+ timer.report()
300
+ except Exception as e:
301
+ print(traceback.format_exc())
302
+ print("Error predict: " + str(e))
303
+ # Zip creation logic:
304
+ download = []
305
+ if txt_infos is not None and len(txt_infos) > 0:
306
+ downloadZipPath = os.path.join(output_dir, "Multi-Tagger-" + datetime.now().strftime("%Y%m%d-%H%M%S") + ".zip")
307
+ with zipfile.ZipFile(downloadZipPath, 'w', zipfile.ZIP_DEFLATED) as taggers_zip:
308
+ for info in txt_infos:
309
+ # Get file name from lookup
310
+ taggers_zip.write(info["path"], arcname=info["name"])
311
+ download.append(downloadZipPath)
312
+ # End zip creation logic
313
+
314
+ if beautify_model_repo:
315
+ beautify.release_vram()
316
+ del beautify
317
+ progress(1, desc=f"Predict completed")
318
+ timer.report_all() # Print all recorded times
319
+ print("Predict is complete.")
320
+ # Make sure all required variables are returned with proper defaults
321
+ if 'sorted_general_strings' not in locals():
322
+ sorted_general_strings = ""
323
+ if 'final_categorized_output' not in locals():
324
+ final_categorized_output = ""
325
+ if 'classified_tags' not in locals():
326
+ classified_tags = {}
327
+ if 'rating' not in locals():
328
+ rating = {}
329
+ if 'character_res' not in locals():
330
+ character_res = {}
331
+ if 'general_res' not in locals():
332
+ general_res = {}
333
+ if 'unclassified_tags' not in locals():
334
+ unclassified_tags = []
335
+
336
+ return download, sorted_general_strings, final_categorized_output, classified_tags, rating, character_res, general_res, unclassified_tags, tag_results
337
  def get_selection_from_gallery(gallery: list, tag_results: dict, selected_state: gr.SelectData):
338
  if not selected_state:
339
  return selected_state
 
345
  "character_res": "",
346
  "general_res": "",
347
  "unclassified_tags": "{}",
348
+ "summarize_tags": ""
349
  }
350
  if selected_state.value["image"]["path"] in tag_results:
351
  tag_result = tag_results[selected_state.value["image"]["path"]]
352
+ return (selected_state.value["image"]["path"], selected_state.value["caption"]), tag_result["strings"], tag_result["strings2"], tag_result["classified_tags"], tag_result["rating"], tag_result["character_res"], tag_result["general_res"], tag_result["unclassified_tags"], tag_result["summarize_tags"]
353
+ def append_gallery(gallery:list, image:str):
354
  if gallery is None:gallery=[]
355
+ if not image:return gallery, None
356
+ gallery.append(image);return gallery, None
357
+ def extend_gallery(gallery:list, images):
358
  if gallery is None:gallery=[]
359
  if not images:return gallery
360
  gallery.extend(images);return gallery
361
+ def remove_image_from_gallery(gallery:list, selected_image:str):
362
  if not gallery or not selected_image:return gallery
363
  selected_image=ast.literal_eval(selected_image)
364
  if selected_image in gallery:gallery.remove(selected_image)
 
366
  args = parse_args()
367
  predictor = Predictor()
368
  dropdown_list = [
369
+ EVA02_LARGE_MODEL_DSV3_REPO,
370
+ SWINV2_MODEL_DSV3_REPO,
371
+ CONV_MODEL_DSV3_REPO,
372
+ VIT_MODEL_DSV3_REPO,
373
+ VIT_LARGE_MODEL_DSV3_REPO,
374
  # ---
375
+ MOAT_MODEL_DSV2_REPO,
376
+ SWIN_MODEL_DSV2_REPO,
377
+ CONV_MODEL_DSV2_REPO,
378
+ CONV2_MODEL_DSV2_REPO,
379
+ VIT_MODEL_DSV2_REPO,
380
  # ---
381
+ SWINV2_MODEL_IS_DSV1_REPO,
382
+ EVA02_LARGE_MODEL_IS_DSV1_REPO,
383
  ]
 
384
  def _restart_space():
385
  HF_TOKEN=os.getenv('HF_TOKEN')
386
  if not HF_TOKEN:raise ValueError('HF_TOKEN environment variable is not set.')
387
+ huggingface_hub.HfApi().restart_space(repo_id='Werli/Multi-Tagger', token=HF_TOKEN, factory_reboot=False)
388
  scheduler=BackgroundScheduler()
389
  # Add a job to restart the space every 2 days (172800 seconds)
390
  restart_space_job = scheduler.add_job(_restart_space, "interval", seconds=172800)
 
393
  NEXT_RESTART=f"Next Restart: {next_run_time_utc.strftime('%Y-%m-%d %H:%M:%S')} (UTC) - The space will restart every 2 days to ensure stability and performance. It uses a background scheduler to handle the restart process."
394
 
395
  css = """
396
+ #custom-gallery {--row-height: 180px;display: grid;grid-auto-rows: min-content;gap: 10px;}
397
+ #custom-gallery .thumbnail-item {height: var(--row-height);width: 100%;position: relative;overflow: hidden;border-radius: 8px;box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);transition: transform 0.2s ease, box-shadow 0.2s ease;}
398
+ #custom-gallery .thumbnail-item:hover {transform: translateY(-3px);box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);}
399
+ #custom-gallery .thumbnail-item img {width: auto;height: 100%;max-width: 100%;max-height: var(--row-height);object-fit: contain;margin: 0 auto;display: block;}
400
+ #custom-gallery .thumbnail-item img.portrait {max-width: 100%;}
401
+ #custom-gallery .thumbnail-item img.landscape {max-height: 100%;}
402
+ .gallery-container {max-height: 500px;overflow-y: auto;padding-right: 0px;--size-80: 500px;}
403
+ .thumbnails {display: flex;position: absolute;bottom: 0;width: 120px;overflow-x: scroll;padding-top: 320px;padding-bottom: 280px;padding-left: 4px;flex-wrap: wrap;}
404
+ #custom-gallery .thumbnail-item img {width: auto;height: 100%;max-width: 100%;max-height: var(--row-height);object-fit: initial;width: fit-content;margin: 0px auto;display: block;}
405
  """
406
+ with gr.Blocks(title=TITLE, css=css, theme="Werli/Multi-Tagger", fill_width=True) as demo:
 
407
  gr.Markdown(value=f"<h1 style='text-align: center; margin-bottom: 1rem'>{TITLE}</h1>")
408
+ #gr.Markdown(value=DESCRIPTION)
409
+ gr.Markdown(value=f"<p style='text-align: center;'>{DESCRIPTION}</p>")
410
  with gr.Tab(label="Waifu Diffusion"):
411
  with gr.Row():
412
  with gr.Column():
413
+ submit = gr.Button(value="SUBMIT", variant="primary", size="lg")
414
  with gr.Column(variant="panel"):
415
  # Create an Image component for uploading images
416
  image_input = gr.Image(label="Upload an Image or clicking paste from clipboard button", type="filepath", sources=["upload", "clipboard"], height=150)
417
  with gr.Row():
418
  upload_button = gr.UploadButton("Upload multiple images", file_types=["image"], file_count="multiple", size="sm")
419
  remove_button = gr.Button("Remove Selected Image", size="sm")
420
+ gallery = gr.Gallery(
421
+ columns=2,
422
+ show_share_button=False,
423
+ interactive=True,
424
+ height="auto",
425
+ label="Grid of images",
426
+ preview=False,
427
+ elem_id="custom-gallery" # Added for custom styling
 
 
 
 
 
 
 
 
 
 
 
428
  )
429
+ with gr.Column(variant="panel"):
430
+ model_repo = gr.Dropdown(dropdown_list, value=EVA02_LARGE_MODEL_DSV3_REPO, label="1st Model", )
431
+ PLUS = "+?"
432
+ gr.Markdown(value=f"<p style='text-align: center;'>{PLUS}</p>")
433
+ model_repo_2 = gr.Dropdown([None] + dropdown_list, value=None, label="2nd Model (Optional)", info="Select another model for diversified results.", )
434
  with gr.Row():
435
+ general_thresh = gr.Slider(0, 1, step=args.score_slider_step, value=args.score_general_threshold, label="General Tags Threshold", scale=3, )
436
+ general_mcut_enabled = gr.Checkbox(value=False, label="Use MCut threshold", scale=1, )
 
 
 
 
 
 
 
 
 
 
 
437
  with gr.Row():
438
+ character_thresh = gr.Slider(0, 1, step=args.score_slider_step, value=args.score_character_threshold, label="Character Tags Threshold", scale=3, )
439
+ character_mcut_enabled = gr.Checkbox(value=False, label="Use MCut threshold", scale=1, )
 
 
 
440
  with gr.Row():
441
+ characters_merge_enabled = gr.Checkbox(value=True, label="Merge characters into the string output", scale=1, )
442
+ with gr.Row():
443
+ beautify_model_repo = gr.Dropdown([None] + beautify_list, value=None, label="Beautify Model", info="Use a model to describe or 'beautify' a single image into a readable English article.", )
 
 
 
444
  with gr.Row():
445
  additional_tags_prepend = gr.Text(label="Prepend Additional tags (comma split)")
446
  additional_tags_append = gr.Text(label="Append Additional tags (comma split)")
447
  with gr.Row():
448
  clear = gr.ClearButton(
449
+ components=[gallery, model_repo, general_thresh, general_mcut_enabled, character_thresh, character_mcut_enabled, characters_merge_enabled, beautify_model_repo, additional_tags_prepend, additional_tags_append, ], variant="secondary", size="lg", )
450
+ with gr.Row():
451
+ rating = gr.Label(label="Rating")
 
 
 
 
 
 
 
 
 
 
 
 
452
  with gr.Column(variant="panel"):
453
+ download_file = gr.File(label="Download") # 0
454
  character_res = gr.Label(label="Output (characters)") # 1
455
+ sorted_general_strings = gr.Textbox(label="Output", show_label=True, show_copy_button=True, lines=5) # 2
456
+ final_categorized_output = gr.Textbox(label="Categorized", info="If tagging multiple images and got long tags, please select an image to display tags correctly.", show_label=True, show_copy_button=True, lines=5) # 3
457
+ pe_generate_btn = gr.Button(value="SUMMARIZE TAGS", size="lg", variant="primary") # 4
458
+ summarize_tags = gr.Textbox(label="Summarized Tags", show_label=True, show_copy_button=True, lines=6) # 5
459
+ prompt_summarizer_model = gr.Radio(["Medium", "Long", "Flux"], label="Model Choice", value="Medium", info="Summarize your prompts with medium or long answers. It's recommended for Flux.") # 6
460
+ categorized = gr.JSON(label="Categorized (tags) - JSON") # 7
461
+ general_res = gr.Label(label="Output (tags)") # 8
462
+ unclassified = gr.JSON(label="Unclassified (tags)") # 9
463
+ clear.add([download_file, sorted_general_strings, final_categorized_output, categorized, rating, character_res, general_res, unclassified, prompt_summarizer_model, summarize_tags, ])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
464
  tag_results = gr.State({})
465
  # Define the event listener to add the uploaded image to the gallery
466
  image_input.change(append_gallery, inputs=[gallery, image_input], outputs=[gallery, image_input])
467
+ # When the upload button is clicked, add the new images to the gallery
468
  upload_button.upload(extend_gallery, inputs=[gallery, upload_button], outputs=gallery)
469
  # Event to update the selected image when an image is clicked in the gallery
470
  selected_image = gr.Textbox(label="Selected Image", visible=False)
471
+ gallery.select(get_selection_from_gallery, inputs=[gallery, tag_results], outputs=[selected_image, sorted_general_strings, final_categorized_output, categorized, rating, character_res, general_res, unclassified, summarize_tags])
472
  # Event to remove a selected image from the gallery
473
  remove_button.click(remove_image_from_gallery, inputs=[gallery, selected_image], outputs=gallery)
474
+ # Event to for the Prompt Beautify Button
475
+ pe_generate_btn.click(lambda tags, model:prompt_summarizer('', '', tags, model)[0], inputs=[final_categorized_output, prompt_summarizer_model], outputs=[summarize_tags])
476
+ submit.click(predictor.predict, inputs=[gallery, model_repo, model_repo_2, general_thresh, general_mcut_enabled, character_thresh, character_mcut_enabled, characters_merge_enabled, beautify_model_repo, additional_tags_prepend, additional_tags_append, tag_results, ], outputs=[download_file, sorted_general_strings, final_categorized_output, categorized, rating, character_res, general_res, unclassified, tag_results, ], )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
477
  gr.Examples(
478
  [["images/1girl.png", VIT_LARGE_MODEL_DSV3_REPO, 0.35, False, 0.85, False]],
479
+ inputs=[image_input, model_repo, general_thresh, general_mcut_enabled, character_thresh, character_mcut_enabled, ],)
480
+ gr.Markdown(NEXT_RESTART)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
481
  with gr.Tab("Booru Image Fetcher"):
482
  with gr.Row():
483
  with gr.Column():
484
  gr.Markdown("### ⚙️ Search Parameters")
485
+ site = gr.Dropdown(label="Select Source", choices=["Gelbooru (Not working)", "Rule34", "Xbooru"], value="Xbooru")
486
+ Tags = gr.Textbox(label="Tags (comma-separated)", placeholder="e.g. solo, 1girl, 1boy, artist name, character, black hair, granblue fantasy, ...", lines=3)
487
+ exclude_tags = gr.Textbox(label="Exclude Tags (comma-separated)", placeholder="e.g. animated, watermark, username, ...", lines=3)
488
  score = gr.Number(label="Minimum Score", value=0)
489
  count = gr.Slider(label="Number of Images", minimum=1, maximum=20, step=1, value=1)
490
  Safe = gr.Checkbox(label="Include Safe", value=True)
491
  Questionable = gr.Checkbox(label="Include Questionable", value=True)
492
+ Explicit = gr.Checkbox(label="Include Explicit (18+)", value=False)
493
  submit_btn = gr.Button("Fetch Images", variant="primary")
 
494
  with gr.Column():
495
  gr.Markdown("### 📄 Results")
496
  images_output = gr.Gallery(label="Images", columns=3, rows=2, object_fit="contain", height=500)
497
+ tags_output = gr.Textbox(label="Tags", placeholder="Select an image to display tags", lines=6, show_copy_button=True)
498
  post_url_output = gr.Textbox(label="Post URL", lines=1, show_copy_button=True)
499
  image_url_output = gr.Textbox(label="Image URL", lines=1, show_copy_button=True)
 
500
  # State to store tags, URLs
501
  tags_state = gr.State([])
502
  post_url_state = gr.State([])
503
  image_url_state = gr.State([])
504
+ submit_btn.click(fn=booru_gradio, inputs=[Tags, exclude_tags, score, count, Safe, Questionable, Explicit, site], outputs=[images_output, tags_state, post_url_state, image_url_state], )
505
+ images_output.select(fn=on_select, inputs=[tags_state, post_url_state, image_url_state], outputs=[tags_output, post_url_output, image_url_output], )
506
+ with gr.Tab(label="Misc"):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
507
  with gr.Row():
508
  with gr.Column(variant="panel"):
509
+ input_tags = gr.Textbox(label="Input Tags", placeholder="1girl, cat, horns, blue hair, ...\nor\n? 1girl 1234567? cat 1234567? horns 1234567? blue hair 1234567? ...", lines=4)
510
+ submit_button = gr.Button(value="SUBMIT", variant="primary", size="lg")
511
  with gr.Column(variant="panel"):
512
  categorized_string = gr.Textbox(label="Categorized (string)", show_label=True, show_copy_button=True, lines=8)
513
  categorized_json = gr.JSON(label="Categorized (tags) - JSON")
514
  submit_button.click(process_tags, inputs=[input_tags], outputs=[categorized_string, categorized_json])
515
  with gr.Column(variant="panel"):
516
+ pe_generate_btn = gr.Button(value="SUMMARIZE TAGS", size="lg", variant="primary")
517
+ summarize_tags = gr.Textbox(label="Summarized Tags", show_label=True, show_copy_button=True, lines=5)
518
+ prompt_summarizer_model = gr.Radio(["Medium", "Long", "Flux"], label="Model Choice", value="Medium", info="Summarize your prompts with medium or long answers. It's recommended for Flux.")
519
+ pe_generate_btn.click(lambda tags, model:prompt_summarizer('', '', tags, model)[0], inputs=[categorized_string, prompt_summarizer_model], outputs=[summarize_tags])
520
+ demo.queue(max_size=10).launch(show_error=True)
images/1girl.png CHANGED

Git LFS Details

  • SHA256: 2a9c1586e694c46e35fe4a1c64b139b0174deb5ff6808b835db546091cfeecb0
  • Pointer size: 132 Bytes
  • Size of remote file: 8.92 MB

Git LFS Details

  • SHA256: 7d8ade3203fada5d53e6923e02dca3679efb248a0c62df5de89b55729dbec091
  • Pointer size: 132 Bytes
  • Size of remote file: 5.18 MB
modules/beautify_model.py ADDED
@@ -0,0 +1,99 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import io,copy,requests,spaces,gradio as gr,numpy as np
3
+ from transformers import T5ForConditionalGeneration, T5Tokenizer
4
+
5
+ LAMINI_PROMPT_LONG= "gokaygokay/Lamini-Prompt-Enchance-Long"
6
+
7
+ class beautify_class:
8
+ def __init__(self, repoId: str, device: str = None, loadModel: bool = False):
9
+ self.modelPath = self.download_model(repoId)
10
+ if device is None:
11
+ import torch
12
+ self.totalVram = 0
13
+ if torch.cuda.is_available():
14
+ try:
15
+ deviceId = torch.cuda.current_device()
16
+ self.totalVram = torch.cuda.get_device_properties(deviceId).total_memory / (1024 * 1024 * 1024)
17
+ except Exception as e:
18
+ print(traceback.format_exc())
19
+ print("Error detect vram: " + str(e))
20
+ device = "cuda" if self.totalVram > (8 if "8B" in repoId else 4) else "cpu"
21
+ else:
22
+ device = "cpu"
23
+ self.device = device
24
+ self.system_prompt = "Summarize, beautify and enhance the following English labels describing a single image into a readable English article:\n\n"
25
+ if loadModel:
26
+ self.load_model()
27
+
28
+ def download_model(self, repoId):
29
+ import huggingface_hub
30
+ allowPatterns = [
31
+ #"tf_model.h5",
32
+ #"model.ckpt.index",
33
+ #"flax_model.msgpack",
34
+ #"pytorch_model.bin",
35
+ "config.json",
36
+ "generation_config.json",
37
+ "model.safetensors",
38
+ "tokenizer.json",
39
+ "tokenizer_config.json",
40
+ "special_tokens_map.json",
41
+ "vocab.json",
42
+ "added_tokens.json",
43
+ "spiece.model"
44
+ ]
45
+ kwargs = {"allow_patterns": allowPatterns,}
46
+ try:
47
+ return huggingface_hub.snapshot_download(repoId, **kwargs)
48
+ except (huggingface_hub.utils.HfHubHTTPError, requests.exceptions.ConnectionError) as exception:
49
+ import warnings
50
+ warnings.warn(
51
+ "An error occurred while synchronizing the model %s from the Hugging Face Hub:\n%s",
52
+ repoId,
53
+ exception,
54
+ )
55
+ warnings.warn(
56
+ "Trying to load the model directly from the local cache, if it exists."
57
+ )
58
+ kwargs["local_files_only"] = True
59
+ return huggingface_hub.snapshot_download(repoId, **kwargs)
60
+
61
+ def load_model(self):
62
+ import transformers
63
+ try:
64
+ print('\n\nLoading model: %s\n\n' % self.modelPath)
65
+ self.Tokenizer = T5Tokenizer.from_pretrained(self.modelPath)
66
+ self.Model = T5ForConditionalGeneration.from_pretrained(self.modelPath).to(self.device)
67
+ except Exception as e:
68
+ self.release_vram()
69
+ raise e
70
+
71
+ def release_vram(self):
72
+ try:
73
+ import torch
74
+ if torch.cuda.is_available():
75
+ if getattr(self, "Model", None) is not None:
76
+ self.Model.to('cpu')
77
+ del self.Model
78
+ if getattr(self, "Tokenizer", None) is not None:
79
+ del self.Tokenizer
80
+ import gc
81
+ gc.collect()
82
+ torch.cuda.empty_cache()
83
+ print("release vram end.")
84
+ except Exception as e:
85
+ print(traceback.format_exc())
86
+ print("Error release vram: " + str(e))
87
+
88
+ def beautify(self, text: str, max_length: int = 400):
89
+ try:
90
+ input_ids = self.Tokenizer(self.system_prompt + text, return_tensors="pt").input_ids.to(self.device)
91
+ output = self.Model.generate(input_ids, max_length=max_length, no_repeat_ngram_size=3, num_beams=2, early_stopping=True)
92
+ result = self.Tokenizer.decode(output[0], skip_special_tokens=True)
93
+ return result
94
+ except Exception as e:
95
+ print(traceback.format_exc())
96
+ print("Error found: " + str(e))
97
+ return None
98
+
99
+ beautify_list=[LAMINI_PROMPT_LONG]
modules/classifyTags.py CHANGED
@@ -1,157 +1,156 @@
1
- from collections import defaultdict
2
- import re
3
- # Define grouping rules (categories and keywords)
4
- # Provided categories and reversed_categories
5
- categories={
6
- 'Explicit':['sex', '69', 'paizuri', 'cum', 'precum', 'areola_slip', 'hetero', 'erection', 'oral', 'fellatio', 'yaoi', 'ejaculation', 'ejaculating', 'masturbation', 'handjob', 'bulge', 'rape', '_rape', 'doggystyle', 'threesome', 'missionary', 'object_insertion', 'nipple', 'nipples', 'pussy', 'anus', 'penis', 'groin', 'testicles', 'testicle', 'anal', 'cameltoe', 'areolae', 'dildo', 'clitoris', 'top-down_bottom-up', 'gag', 'groping', 'gagged', 'gangbang', 'orgasm', 'femdom', 'incest', 'bukkake', 'breast_out', 'vaginal', 'vagina', 'public_indecency', 'breast_sucking', 'folded', 'cunnilingus', '_cunnilingus', 'foreskin', 'bestiality', 'footjob', 'uterus', 'womb', 'flaccid', 'defloration', 'butt_plug', 'cowgirl_position', 'reverse_cowgirl_position', 'squatting_cowgirl_position', 'reverse_upright_straddle', 'irrumatio', 'deepthroat', 'pokephilia', 'gaping', 'orgy', 'cleft_of_venus', 'futanari', 'futasub', 'futa', 'cumdrip', 'fingering', 'vibrator', 'partially_visible_vulva', 'penetration', 'penetrated', 'cumshot', 'exhibitionism', 'breast_milk', 'grinding', 'clitoral', 'urethra', 'phimosis', 'cervix', 'impregnation', 'tribadism', 'molestation', 'pubic_hair', 'clothed_female_nude_male', 'clothed_male_nude_female', 'clothed_female_nude_female', 'clothed_male_nude_male', 'sex_machine', 'milking_machine', 'ovum', 'chikan', 'pussy_juice_drip_through_clothes', 'ejaculating_while_penetrated', 'suspended_congress', 'reverse_suspended_congress', 'spread_pussy_under_clothes', 'anilingus', 'reach-around', 'humping', 'consensual_tentacles', 'tentacle_pit', 'cum_in_'],
7
- 'Appearance Status':['backless', 'bandaged_neck', 'bleeding', 'blood', '_blood', 'blush', 'body_writing', 'bodypaint', 'bottomless', 'breath', 'bruise', 'butt_crack', 'cold', 'covered_mouth', 'crack', 'cross-section', 'crotchless', 'crying', 'curvy', 'cuts', 'dirty', 'dripping', 'drunk', 'from_mouth', 'glowing', 'hairy', 'halterneck', 'hot', 'injury', 'latex', 'leather', 'levitation', 'lipstick_mark', '_markings', 'makeup', 'mole', 'moles', 'no_bra', 'nosebleed', 'nude', 'outfit', 'pantylines', 'peeing', 'piercing', '_piercing', 'piercings', 'pregnant', 'public_nudity', 'reverse', '_skin', '_submerged', 'saliva', 'scar', 'scratches', 'see-through', 'shadow', 'shibari', 'sideless', 'skindentation', 'sleeping', 'tan', 'soap_bubbles', 'steam', 'steaming_body', 'stitches', 'sweat', 'sweatdrop', 'sweaty', 'tanlines', 'tattoo', 'tattoo', 'tears', 'topless', 'transparent', 'trefoil', 'trembling', 'veins', 'visible_air', 'wardrobe_malfunction', 'wet', 'x-ray', 'unconscious', 'handprint'],
8
- 'Action Pose':['afloat', 'afterimage', 'against_fourth_wall', 'against_wall', 'aiming', 'all_fours',"another's_mouth",'arm_', 'arm_support', 'arms_', 'arms_behind_back', 'asphyxiation', 'attack', 'back', 'ballet', 'bara', 'bathing', 'battle', 'bdsm', 'beckoning', 'bent_over', 'bite_mark', 'biting', 'bondage', 'breast_suppress', 'breathing', 'burning', 'bust_cup', 'carry', 'carrying', 'caught', 'chained', 'cheek_squash', 'chewing', 'cigarette', 'clapping', 'closed_eye', 'come_hither', 'cooking', 'covering', 'cuddling', 'dancing', '_docking', 'destruction', 'dorsiflexion', 'dreaming', 'dressing', 'drinking', 'driving', 'dropping', 'eating', 'exercise', 'expansion', 'exposure', 'facing', 'failure', 'fallen_down', 'falling', 'feeding', 'fetal_position', 'fighting', 'finger_on_trigger', 'finger_to_cheek', 'finger_to_mouth', 'firing', 'fishing', 'flashing', 'fleeing', 'flexible', 'flexing', 'floating', 'flying', 'fourth_wall', 'freediving', 'frogtie', '_grab', 'girl_on_top', 'giving', 'grabbing', 'grabbing_', 'gymnastics', '_hold', 'hadanugi_dousa', 'hairdressing', 'hand_', 'hand_on', 'hand_on_wall', 'hands_', 'headpat', 'hiding', 'holding', 'hug', 'hugging', 'imagining', 'in_container', 'in_mouth', 'in_palm', 'jealous', 'jumping', 'kabedon', 'kicking', 'kiss', 'kissing', 'kneeling', '_lift', 'lactation', 'laundry', 'licking', 'lifted_by_self', 'looking', 'lowleg', 'lying', 'melting', 'midair', 'moaning', '_open', 'on_back', 'on_bed', 'on_ground', 'on_lap', 'on_one_knee', 'one_eye_closed', 'open_', 'over_mouth', 'own_mouth', '_peek', '_pose', '_press', '_pull', 'padding', 'paint', 'painting_(action)', 'palms_together', 'pee', 'peeking', 'pervert', 'petting', 'pigeon-toed', 'piggyback', 'pinching', 'pinky_out', 'pinned', 'plantar_flexion', 'planted', 'playing', 'pocky', 'pointing', 'poke', 'poking', 'pouring', 'pov', 'praying', 'presenting', 'profanity', 'pulled_by_self', 'pulling', 'pump_action', 'punching', '_rest', 'raised', 'reaching', 'reading', 'reclining', 'reverse_grip', 'riding', 'running', '_slip', 'salute', 'screaming', 'seiza', 'selfie', 'sewing', 'shaking', 'shoe_dangle', 'shopping', 'shouting', 'showering', 'shushing', 'singing', 'sitting', 'slapping', 'smell', 'smelling', 'smoking', 'smother', 'solo', 'spanked', 'spill', 'spilling', 'spinning', 'splashing', 'split', 'squatting', 'squeezed', 'breasts_squeezed_together', 'standing', 'standing_on_', 'staring', 'straddling', 'strangling', 'stretching', 'surfing', 'suspension', 'swimming', 'talking', 'teardrop', 'tearing_clothes', 'throwing', 'tied_up', 'tiptoes', 'toe_scrunch', 'toothbrush', 'trigger_discipline', 'tripping', 'tsundere', 'turning_head', 'twitching', 'two-handed', 'tying', '_up', 'unbuttoned', 'undressed', 'undressing', 'unsheathed', 'unsheathing', 'unzipped', 'unzipping', 'upright_straddle', 'v', 'V', 'vore', '_wielding', 'wading', 'walk-in', 'walking', 'wariza', 'waving', 'wedgie', 'wrestling', 'writing', 'yawning', 'yokozuwari', '_conscious', 'massage', 'struggling', 'shrugging', 'drugged', 'tentacles_under_clothes', 'restrained_by_tentacles', 'tentacles_around_arms', 'tentacles_around_legs', 'restrained_legs', 'restrained_tail', 'restrained_arms', 'tentacles_on_female', 'archery', 'cleaning', 'tempura', 'facepalm', 'sadism'],
9
- 'Headwear':['antennae', 'antlers', 'aura', 'bandaged_head', 'bandana', 'bandeau', 'beanie', 'beanie', 'beret', 'bespectacled', 'blindfold', 'bonnet', '_cap', 'circlet', 'crown', '_drill', '_drills', 'diadem', '_eyewear', 'ear_covers', 'ear_ornament', 'ear_tag', 'earbuds', 'earclip', 'earmuffs', 'earphones', 'earpiece', 'earring', 'earrings', 'eyeliner', 'eyepatch', 'eyewear_on_head', 'facial', 'fedora', 'glasses', 'goggles', '_headwear', 'hachimaki', 'hair_bobbles', 'hair_ornament', 'hair_rings', 'hair_tie', 'hairband', 'hairclip', 'hairpin', 'hairpods', 'halo', 'hat', 'head-mounted_display', 'head_wreath', 'headband', 'headdress', 'headgear', 'headphones', 'headpiece', 'headset', 'helm', 'helmet', 'hood', 'kabuto_(helmet)', 'kanzashi', '_mask', 'maid_headdress', 'mask', 'mask', 'mechanical_ears', 'mechanical_eye', 'mechanical_horns', 'mob_cap', 'monocle', 'neck_ruff', 'nightcap', 'on_head', 'pince-nez', 'qingdai_guanmao', 'scarf_over_mouth', 'scrunchie', 'sunglasses',"tam_o'_shanter",'tate_eboshi', 'tiara', 'topknot', 'turban', 'veil', 'visor', 'wig', 'mitre', 'tricorne', 'bicorne'],
10
- 'Handwear':['arm_warmers', 'armband', 'armlet', 'bandaged_arm', 'bandaged_fingers', 'bandaged_hand', 'bandaged_wrist', 'bangle', 'bracelet', 'bracelets', 'bracer', 'cuffs', 'elbow_pads', '_gauntlets', '_glove', '_gloves', 'gauntlets', 'gloves', 'kote', 'kurokote', 'mechanical_arm', 'mechanical_arms', 'mechanical_hands', 'mittens', 'mitts', 'nail_polish', 'prosthetic_arm', 'wrist_cuffs', 'wrist_guards', 'wristband', 'yugake'],
11
- 'One-Piece Outfit':['bodystocking', 'bodysuit', 'dress', 'furisode', 'gown', 'hanfu', 'jumpsuit', 'kimono', 'leotard', 'microdress', 'one-piece', 'overalls', 'robe', 'spacesuit', 'sundress', 'yukata'],
12
- 'Upper Body Clothing':['aiguillette', 'apron', '_apron', 'armor', '_armor', 'ascot', 'babydoll', 'bikini', '_bikini', 'blazer', '_blazer', 'blouse', '_blouse', 'bowtie', '_bowtie', 'bra', '_bra', 'breast_curtain', 'breast_curtains', 'breast_pocket', 'breastplate', 'bustier', 'camisole', 'cape', 'capelet', 'cardigan', 'center_opening', 'chemise', 'chest_jewel', 'choker', 'cloak', 'coat', 'coattails', 'collar', '_collar', 'corset', 'criss-cross_halter', 'crop_top', 'dougi', 'feather_boa', 'gakuran', 'hagoromo', 'hanten_(clothes)', 'haori', 'harem_pants', 'harness', 'hoodie', 'jacket', '_jacket', 'japanese_clothes', 'kappougi', 'kariginu', 'lapels', 'lingerie', '_lingerie', 'maid', 'mechanical_wings', 'mizu_happi', 'muneate', 'neckerchief', 'necktie', 'negligee', 'nightgown', 'pajamas', '_pajamas', 'pauldron', 'pauldrons', 'plunging_neckline', 'raincoat', 'rei_no_himo', 'sailor_collar', 'sarashi', 'scarf', 'serafuku', 'shawl', 'shirt', 'shoulder_', 'sleepwear', 'sleeve', 'sleeveless', 'sleeves', '_sleeves', 'sode', 'spaghetti_strap', 'sportswear', 'strapless', 'suit', 'sundress', 'suspenders', 'sweater', 'swimsuit', '_top', '_torso', 't-shirt', 'tabard', 'tailcoat', 'tank_top', 'tasuki', 'tie_clip', 'tunic', 'turtleneck', 'tuxedo', '_uniform', 'undershirt', 'uniform', 'v-neck', 'vambraces', 'vest', 'waistcoat'],
13
- 'Lower Body Clothing':['bare_hips', 'bloomers', 'briefs', 'buruma', 'crotch_seam', 'cutoffs', 'denim', 'faulds', 'fundoshi', 'g-string', 'garter_straps', 'hakama', 'hip_vent', 'jeans', 'knee_pads', 'loincloth', 'mechanical_tail', 'microskirt', 'miniskirt', 'overskirt', 'panties', 'pants', 'pantsu', 'panty_straps', 'pelvic_curtain', 'petticoat', 'sarong', 'shorts', 'side_slit', 'skirt', 'sweatpants', 'swim_trunks', 'thong', 'underwear', 'waist_cape'],
14
- 'Foot & Legwear':['anklet', 'bandaged_leg', 'boot', 'boots', '_footwear', 'flats', 'flip-flops', 'geta', 'greaves', '_heels', 'kneehigh', 'kneehighs', '_legwear', 'leg_warmers', 'leggings', 'loafers', 'mary_janes', 'mechanical_legs', 'okobo', 'over-kneehighs', 'pantyhose', 'prosthetic_leg', 'pumps', '_shoe', '_sock', 'sandals', 'shoes', 'skates', 'slippers', 'sneakers', 'socks', 'spikes', 'tabi', 'tengu-geta', 'thigh_strap', 'thighhighs', 'uwabaki', 'zouri', 'legband', 'ankleband'],
15
- 'Other Accessories':['alternate_', 'anklet', 'badge', 'beads', 'belt', 'belts', 'bow', 'brooch', 'buckle', 'button', 'buttons', '_clothes', '_costume', '_cutout', 'casual', 'charm', 'clothes_writing', 'clothing_aside', 'costume', 'cow_print', 'cross', 'd-pad', 'double-breasted', 'drawstring', 'epaulettes', 'fabric', 'fishnets', 'floral_print', 'formal', 'frills', '_garter', 'gem', 'holster', 'jewelry', '_knot', 'lace', 'lanyard', 'leash', 'magatama', 'mechanical_parts', 'medal', 'medallion', 'naked_bandage', 'necklace', '_ornament', '(ornament)', 'o-ring', 'obi', 'obiage', 'obijime', '_pin', '_print', 'padlock', 'patterned_clothing', 'pendant', 'piercing', 'plaid', 'pocket', 'polka_dot', 'pom_pom_(clothes)', 'pom_pom_(clothes)', 'pouch', 'ribbon', '_ribbon', '_stripe', '_stripes', 'sash', 'shackles', 'shimenawa', 'shrug_(clothing)', 'skin_tight', 'spandex', 'strap', 'sweatband', '_trim', 'tassel', 'zettai_ryouiki', 'zipper'],
16
- 'Facial Expression':['ahegao', 'anger_vein', 'angry', 'annoyed', 'confused', 'drooling', 'embarrassed', 'expressionless', 'eye_contact', '_face', 'frown', 'fucked_silly', 'furrowed_brow', 'glaring', 'gloom_(expression)', 'grimace', 'grin', 'happy', 'jitome', 'laughing', '_mouth', 'nervous', 'notice_lines', 'o_o', 'parted_lips', 'pout', 'puff_of_air', 'restrained', 'sad', 'sanpaku', 'scared', 'scowl', 'serious', 'shaded_face', 'shy', 'sigh', 'sleepy', 'smile', 'smirk', 'smug', 'snot', 'spoken_ellipsis', 'spoken_exclamation_mark', 'spoken_interrobang', 'spoken_question_mark', 'squiggle', 'surprised', 'tareme', 'tearing_up', 'thinking', 'tongue', 'tongue_out', 'torogao', 'tsurime', 'turn_pale', 'wide-eyed', 'wince', 'worried', 'heartbeat'],
17
- 'Facial Emoji':['!!', '!', '!?', '+++', '+_+', '...', '...?', '._.', '03:00', '0_0', ':/', ':3', ':<', ':>', ':>=', ':d', ':i', ':o', ':p', ':q', ':t', ':x', ':|', ';(', ';)', ';3', ';d', ';o', ';p', ';q', '=_=', '>:(', '>:)', '>_<', '>_o', '>o<', '?', '??', '@_@', '\\m/', '\n/', '\\o/', '\\||/', '^^^', '^_^', 'c:', 'd:', 'o_o', 'o3o', 'u_u', 'w', 'x', 'x_x', 'xd', 'zzz', '|_|'],
18
- 'Head':['afro', 'ahoge', 'animal_ear_fluff', '_bangs', '_bun', 'bald', 'beard', 'blunt_bangs', 'blunt_ends', 'bob_cut', 'bowl_cut', 'braid', 'braids', 'buzz_cut', 'circle_cut', 'colored_tips', 'cowlick', 'dot_nose', 'dreadlocks', '_ear', '_ears', '_eye', '_eyes', 'enpera', 'eyeball', 'eyebrow', 'eyebrow_cut', 'eyebrows', 'eyelashes', 'eyeshadow', 'faceless', 'facepaint', 'facial_mark', 'fang', 'forehead', 'freckles', 'goatee', '_hair', '_horn', '_horns', 'hair_', 'hair_bun', 'hair_flaps', 'hair_intakes', 'hair_tubes', 'half_updo', 'head_tilt', 'heterochromia', 'hime_cut', 'hime_cut', 'horns', 'in_eye', 'inverted_bob', 'kemonomimi_mode', 'lips', 'mascara', 'mohawk', 'mouth_', 'mustache', 'nose', 'one-eyed', 'one_eye', 'one_side_up', '_pupils', 'parted_bangs', 'pompadour', 'ponytail', 'ringlets', '_sclera', 'sideburns', 'sidecut', 'sidelock', 'sidelocks', 'skull', 'snout', 'stubble', 'swept_bangs', 'tails', 'teeth', 'third_eye', 'twintails', 'two_side_up', 'undercut', 'updo', 'v-shaped_eyebrows', 'whiskers', 'tentacle_hair'],
19
- 'Hands':['_arm', '_arms', 'claws', '_finger', '_fingers', 'fingernails', '_hand', '_nail', '_nails', 'palms', 'rings', 'thumbs_up'],
20
- 'Upper Body':['abs', 'armpit', 'armpits', 'backboob', 'belly', 'biceps', 'breast_rest', 'breasts', 'button_gap', 'cleavage', 'collarbone', 'dimples_of_venus', 'downblouse', 'flat_chest', 'linea_alba', 'median_furrow', 'midriff', 'nape', 'navel', 'pectorals', 'ribs', '_shoulder', '_shoulders', 'shoulder_blades', 'sideboob', 'sidetail', 'spine', 'stomach', 'strap_gap', 'toned', 'underboob', 'underbust'],
21
- 'Lower Body':['ankles', 'ass', 'barefoot', 'crotch', 'feet', 'highleg', 'hip_bones', 'hooves', 'kneepits', 'knees', 'legs', 'soles', 'tail', 'thigh_gap', 'thighlet', 'thighs', 'toenail', 'toenails', 'toes', 'wide_hips'],
22
- 'Creature':['(animal)', 'anglerfish', 'animal', 'bear', 'bee', 'bird', 'bug', 'butterfly', 'cat', 'chick', 'chicken', 'chinese_zodiac', 'clownfish', 'coral', 'crab', 'creature', 'crow', 'dog', 'dove', 'dragon', 'duck', 'eagle', 'fish', 'fish', 'fox', 'fox', 'frog', 'frog', 'goldfish', 'hamster', 'horse', 'jellyfish', 'ladybug', 'lion', 'mouse', 'octopus', 'owl', 'panda', 'penguin', 'pig', 'pigeon', 'rabbit', 'rooster', 'seagull', 'shark', 'sheep', 'shrimp', 'snail', 'snake', 'squid', 'starfish', 'tanuki', 'tentacles', 'goo_tentacles', 'plant_tentacles', 'crotch_tentacles', 'mechanical_tentacles', 'squidward_tentacles', 'suction_tentacles', 'penis_tentacles', 'translucent_tentacles', 'back_tentacles', 'red_tentacles', 'green_tentacles', 'blue_tentacles', 'black_tentacles', 'pink_tentacles', 'purple_tentacles', 'face_tentacles', 'tentacles_everywhere', 'milking_tentacles', 'tiger', 'turtle', 'weasel', 'whale', 'wolf', 'parrot', 'sparrow', 'unicorn'],
23
- 'Plant':['bamboo', 'bouquet', 'branch', 'bush', 'cherry_blossoms', 'clover', 'daisy', '(flower)', 'flower', 'flower', 'gourd', 'hibiscus', 'holly', 'hydrangea', 'leaf', 'lily_pad', 'lotus', 'moss', 'palm_leaf', 'palm_tree', 'petals', 'plant', 'plum_blossoms', 'rose', 'spider_lily', 'sunflower', 'thorns', 'tree', 'tulip', 'vines', 'wisteria', 'acorn'],
24
- 'Food':['apple', 'baguette', 'banana', 'baozi', 'beans', 'bento', 'berry', 'blueberry', 'bread', 'broccoli', 'burger', 'cabbage', 'cake', 'candy', 'carrot', 'cheese', 'cherry', 'chili_pepper', 'chocolate', 'coconut', 'cookie', 'corn', 'cream', 'crepe', 'cucumber', 'cucumber', 'cupcake', 'curry', 'dango', 'dessert', 'doughnut', 'egg', 'eggplant', '_(food)', '_(fruit)', 'food', 'french_fries', 'fruit', 'grapes', 'ice_cream', 'icing', 'lemon', 'lettuce', 'lollipop', 'macaron', 'mandarin_orange', 'meat', 'melon', 'mochi', 'mushroom', 'noodles', 'omelet', 'omurice', 'onigiri', 'onion', 'pancake', 'parfait', 'pasties', 'pastry', 'peach', 'pineapple', 'pizza', 'popsicle', 'potato', 'pudding', 'pumpkin', 'radish', 'ramen', 'raspberry', 'rice', 'roasted_sweet_potato', 'sandwich', 'sausage', 'seaweed', 'skewer', 'spitroast', 'spring_onion', 'strawberry', 'sushi', 'sweet_potato', 'sweets', 'taiyaki', 'takoyaki', 'tamagoyaki', 'tempurakanbea', 'toast', 'tomato', 'vegetable', 'wagashi', 'wagashi', 'watermelon', 'jam', 'popcorn'],
25
- 'Beverage':['alcohol', 'beer', 'coffee', 'cola', 'drink', 'juice', 'juice_box', 'milk', 'sake', 'soda', 'tea', '_tea', 'whiskey', 'wine', 'cocktail'],
26
- 'Music':['band', 'baton_(conducting)', 'beamed', 'cello', 'concert', 'drum', 'drumsticks', 'eighth_note', 'flute', 'guitar', 'harp', 'horn', '(instrument)', 'idol', 'instrument', 'k-pop', 'lyre', '(music)', 'megaphone', 'microphone', 'music', 'musical_note', 'phonograph', 'piano', 'plectrum', 'quarter_note', 'recorder', 'sixteenth_note', 'sound_effects', 'trumpet', 'utaite', 'violin', 'whistle'],
27
- 'Weapons & Equipment':['ammunition', 'arrow_(projectile)', 'axe', 'bandolier', 'baseball_bat', 'beretta_92', 'bolt_action', 'bomb', 'bullet', 'bullpup', 'cannon', 'chainsaw', 'crossbow', 'dagger', 'energy_sword', 'explosive', 'fighter_jet', 'gohei', 'grenade', 'gun', 'hammer', 'handgun', 'holstered', 'jet', 'katana', 'knife', 'kunai', 'lance', 'mallet', 'nata_(tool)', 'polearm', 'quiver', 'rapier', 'revolver', 'rifle', 'rocket_launcher', 'scabbard', 'scope', 'scythe', 'sheath', 'sheathed', 'shield', 'shotgun', 'shuriken', 'spear', 'staff', 'suppressor', 'sword', 'tank', 'tantou', 'torpedo', 'trident', '(weapon)', 'wand', 'weapon', 'whip', 'yumi_(bow)', 'h&k_hk416', 'rocket_launcher', 'heckler_&_koch', '_weapon'],
28
- 'Vehicles':['aircraft', 'airplane', 'bicycle', 'boat', 'car', 'caterpillar_tracks', 'flight_deck', 'helicopter', 'motor_vehicle', 'motorcycle', 'ship', 'spacecraft', 'spoiler_(automobile)', 'train', 'truck', 'watercraft', 'wheel', 'wheelbarrow', 'wheelchair', 'inflatable_raft'],
29
- 'Buildings':['apartment', 'aquarium', 'architecture', 'balcony', 'building', 'cafe', 'castle', 'church', 'gym', 'hallway', 'hospital', 'house', 'library', '(place)', 'porch', 'restaurant', 'restroom', 'rooftop', 'shop', 'skyscraper', 'stadium', 'stage', 'temple', 'toilet', 'tower', 'train_station', 'veranda'],
30
- 'Indoor':['bath', 'bathroom', 'bathtub', 'bed', 'bed_sheet', 'bedroom', 'blanket', 'bookshelf', 'carpet', 'ceiling', 'chair', 'chalkboard', 'classroom', 'counter', 'cupboard', 'curtains', 'cushion', 'dakimakura', 'desk', 'door', 'doorway', 'drawer', '_floor', 'floor', 'futon', 'indoors', 'interior', 'kitchen', 'kotatsu', 'locker', 'mirror', 'pillow', 'room', 'rug', 'school_desk', 'shelf', 'shouji', 'sink', 'sliding_doors', 'stairs', 'stool', 'storeroom', 'table', 'tatami', 'throne', 'window', 'windowsill', 'bathhouse', 'chest_of_drawers'],
31
- 'Outdoor':['alley', 'arch', 'beach', 'bridge', 'bus_stop', 'bush', 'cave', '(city)', 'city', 'cliff', 'crescent', 'crosswalk', 'day', 'desert', 'fence', 'ferris_wheel', 'field', 'forest', 'grass', 'graveyard', 'hill', 'lake', 'lamppost', 'moon', 'mountain', 'night', 'ocean', 'onsen', 'outdoors', 'path', 'pool', 'poolside', 'railing', 'railroad', 'river', 'road', 'rock', 'sand', 'shore', 'sky', 'smokestack', 'snow', 'snowball', 'snowman', 'street', 'sun', 'sunlight', 'sunset', 'tent', 'torii', 'town', 'tree', 'turret', 'utility_pole', 'valley', 'village', 'waterfall'],
32
- 'Objects':['anchor', 'android', 'armchair', '(bottle)', 'backpack', 'bag', 'ball', 'balloon', 'bandages', 'bandaid', 'bandaids', 'banknote', 'banner', 'barcode', 'barrel', 'baseball', 'basket', 'basketball', 'beachball', 'bell', 'bench', 'binoculars', 'board_game', 'bone', 'book', 'bottle', 'bowl', 'box', 'box_art', 'briefcase', 'broom', 'bucket', '(chess)', '(computer)', '(computing)', '(container)', 'cage', 'calligraphy_brush', 'camera', 'can', 'candle', 'candlestand', 'cane', 'card', 'cartridge', 'cellphone', 'chain', 'chandelier', 'chess', 'chess_piece', 'choko_(cup)', 'chopsticks', 'cigar', 'clipboard', 'clock', 'clothesline', 'coin', 'comb', 'computer', 'condom', 'controller', 'cosmetics', 'couch', 'cowbell', 'crazy_straw', 'cup', 'cutting_board', 'dice', 'digital_media_player', 'doll', 'drawing_tablet', 'drinking_straw', 'easel', 'electric_fan', 'emblem', 'envelope', 'eraser', 'feathers', 'figure', 'fire', 'fishing_rod', 'flag', 'flask', 'folding_fan', 'fork', 'frying_pan', '(gemstone)', 'game_console', 'gears', 'gemstone', 'gift', 'glass', 'glowstick', 'gold', 'handbag', 'handcuffs', 'handheld_game_console', 'hose', 'id_card', 'innertube', 'iphone',"jack-o'-lantern",'jar', 'joystick', 'key', 'keychain', 'kiseru', 'ladder', 'ladle', 'lamp', 'lantern', 'laptop', 'letter', 'letterboxed', 'lifebuoy', 'lipstick', 'liquid', 'lock', 'lotion', '_machine', 'map', 'marker', 'model_kit', 'money', 'monitor', 'mop', 'mug', 'needle', 'newspaper', 'nintendo', 'nintendo_switch', 'notebook', '(object)', 'ofuda', 'orb', 'origami', '(playing_card)', 'pack', 'paddle', 'paintbrush', 'pan', 'paper', 'parasol', 'patch', 'pc', 'pen', 'pencil', 'pencil', 'pendant_watch', 'phone', 'pill', 'pinwheel', 'plate', 'playstation', 'pocket_watch', 'pointer', 'poke_ball', 'pole', 'quill', 'racket', 'randoseru', 'remote_control', 'ring', 'rope', 'sack', 'saddle', 'sakazuki', 'satchel', 'saucer', 'scissors', 'scroll', 'seashell', 'seatbelt', 'shell', 'shide', 'shopping_cart', 'shovel', 'shower_head', 'silk', 'sketchbook', 'smartphone', 'soap', 'sparkler', 'spatula', 'speaker', 'spoon', 'statue', 'stethoscope', 'stick', 'sticker', 'stopwatch', 'string', 'stuffed_', 'stylus', 'suction_cups', 'suitcase', 'surfboard', 'syringe', 'talisman', 'tanzaku', 'tape', 'teacup', 'teapot', 'teddy_bear', 'television', 'test_tube', 'tiles', 'tokkuri', 'tombstone', 'torch', 'towel', 'toy', 'traffic_cone', 'tray', 'treasure_chest', 'uchiwa', 'umbrella', 'vase', 'vial', 'video_game', 'viewfinder', 'volleyball', 'wallet', 'watch', 'watch', 'whisk', 'whiteboard', 'wreath', 'wrench', 'wristwatch', 'yunomi', 'ace_of_hearts', 'inkwell', 'compass', 'ipod', 'sunscreen', 'rocket', 'cobblestone'],
33
- 'Character Design':['+boys', '+girls', '1other', '39', '_boys', '_challenge', '_connection', '_female', '_fur', '_girls', '_interface', '_male', '_man', '_person', 'abyssal_ship', 'age_difference', 'aged_down', 'aged_up', 'albino', 'alien', 'alternate_muscle_size', 'ambiguous_gender', 'amputee', 'androgynous', 'angel', 'animalization', 'ass-to-ass', 'assault_visor', 'au_ra', 'baby', 'bartender', 'beak', 'bishounen', 'borrowed_character', 'boxers', 'boy', 'breast_envy', 'breathing_fire', 'bride', 'broken', 'brother_and_sister', 'brothers', 'camouflage', 'cheating_(relationship)', 'cheerleader', 'chibi', 'child', 'clone', 'command_spell', 'comparison', 'contemporary', 'corpse', 'corruption', 'cosplay', 'couple', 'creature_and_personification', 'crossdressing', 'crossover', 'cyberpunk', 'cyborg', 'cyclops', 'damaged', 'dancer', 'danmaku', 'darkness', 'death', 'defeat', 'demon', 'disembodied_', 'draph', 'drone', 'duel', 'dwarf', 'egyptian', 'electricity', 'elezen', 'elf', 'enmaided', 'erune', 'everyone', 'evolutionary_line', 'expressions', 'fairy', 'family', 'fangs', 'fantasy', 'fashion', 'fat', 'father_and_daughter', 'father_and_son', 'fewer_digits', 'fins', 'flashback', 'fluffy', 'fumo_(doll)', 'furry', 'fusion', 'fuuin_no_tsue', 'gameplay_mechanics', 'genderswap', 'ghost', 'giant', 'giantess', 'gibson_les_paul', 'girl', 'goblin', 'groom', 'guro', 'gyaru', 'habit', 'harem', 'harpy', 'harvin', 'heads_together', 'health_bar', 'height_difference', 'hitodama', 'horror_(theme)', 'humanization', 'husband_and_wife', 'hydrokinesis', 'hypnosis', 'hyur', 'idol', 'insignia', 'instant_loss', 'interracial', 'interspecies', 'japari_bun', 'jeweled_branch_of_hourai', 'jiangshi', 'jirai_kei', 'joints', 'karakasa_obake', 'keyhole', 'kitsune', 'knight', 'kodona', 'kogal', 'kyuubi', 'lamia', 'left-handed', 'loli', 'lolita', 'look-alike', 'machinery', 'magic', 'male_focus', 'manly', 'matching_outfits', 'mature_female', 'mecha', 'mermaid', 'meta', 'miko', 'milestone_celebration', 'military', 'mind_control', 'miniboy', 'minigirl',"miqo'te",'monster', 'monsterification', 'mother_and_daughter', 'mother_and_son', 'multiple_others', 'muscular', 'nanodesu_(phrase)', 'narrow_waist', 'nekomata', 'netorare', 'ninja', 'no_humans', 'nontraditional', 'nun', 'nurse', 'object_namesake', 'obliques', 'office_lady', 'old', 'on_body', 'onee-shota', 'oni', 'orc', 'others', 'otoko_no_ko', 'oversized_object', 'paint_splatter', 'pantyshot', 'pawpads', 'persona', 'personality', 'personification', 'pet_play', 'petite', 'pirate', 'playboy_bunny', 'player_2', 'plugsuit', 'plump', 'poi', 'pokemon', 'police', 'policewoman', 'pom_pom_(cheerleading)', 'princess', 'prosthesis', 'pun', 'puppet', 'race_queen', 'radio_antenna', 'real_life_insert', 'redesign', 'reverse_trap', 'rigging', 'robot', 'rod_of_remorse', 'sailor', 'salaryman', 'samurai', 'sangvis_ferri', 'scales', 'scene_reference', 'school', 'sheikah', 'shota', 'shrine', 'siblings', 'side-by-side', 'sidesaddle', 'sisters', 'size_difference', 'skeleton', 'skinny', 'slave', 'slime_(substance)', 'soldier', 'spiked_shell', 'spokencharacter', 'steampunk', 'streetwear', 'striker_unit', 'strongman', 'submerged', 'suggestive', 'super_saiyan', 'superhero', 'surreal', 'take_your_pick', 'tall', 'talons', 'taur', 'teacher', 'team_rocket', 'three-dimensional_maneuver_gear', 'time_paradox', 'tomboy', 'traditional_youkai', 'transformation', 'trick_or_treat', 'tusks', 'twins', 'ufo', 'under_covers', 'v-fin', 'v-fin', 'vampire', 'virtual_youtuber', 'waitress', 'watching_television', 'wedding', 'what', 'when_you_see_it', 'wife_and_wife', 'wing', 'wings', 'witch', 'world_war_ii', 'yandere', 'year_of', 'yes', 'yin_yang', 'yordle',"you're_doing_it_wrong",'you_gonna_get_raped', 'yukkuri_shiteitte_ne', 'yuri', 'zombie', '(alice_in_wonderland)', '(arknights)', '(blue_archive)', '(cosplay)', '(creature)', '(emblem)', '(evangelion)', '(fate)', '(fate/stay_night)', '(ff11)', '(fire_emblem)', '(genshin_impact)', '(grimm)', '(houseki_no_kuni)', '(hyouka)', '(idolmaster)', '(jojo)', '(kancolle)', '(kantai_collection)', '(kill_la_kill)', '(league_of_legends)', '(legends)', '(lyomsnpmp)', '(machimazo)', '(madoka_magica)', '(mecha)', '(meme)', '(nier:automata)', '(organ)', '(overwatch)', '(pokemon)', '(project_moon)', '(project_sekai)', '(sao)', '(senran_kagura)', '(splatoon)', '(touhou)', '(tsukumo_sana)', '(youkai_watch)', '(yu-gi-oh!_gx)', '(zelda)', 'sextuplets', 'imperial_japanese_army', 'extra_faces', '_miku'],
34
- 'Composition':['abstract', 'anime_coloring', 'animification', 'back-to-back', 'bad_anatomy', 'blurry', 'border', 'bound', 'cameo', 'cheek-to-cheek', 'chromatic_aberration', 'close-up', 'collage', 'color_guide', 'colorful', 'comic', 'contrapposto', 'cover', 'cowboy_shot', 'crosshatching', 'depth_of_field', 'dominatrix', 'dutch_angle', '_focus', 'face-to-face', 'fake_screenshot', 'film_grain', 'fisheye', 'flat_color', 'foreshortening', 'from_above', 'from_behind', 'from_below', 'from_side', 'full_body', 'glitch', 'greyscale', 'halftone', 'head_only', 'heads-up_display', 'high_contrast', 'horizon', '_inset', 'inset', 'jaggy_lines', '1koma', '2koma', '3koma', '4koma', '5koma', 'leaning', 'leaning_forward', 'leaning_to_the_side', 'left-to-right_manga', 'lens_flare', 'limited_palette', 'lineart', 'lineup', 'lower_body', '(medium)', 'marker_(medium)', 'meme', 'mixed_media', 'monochrome', 'multiple_views', 'muted_color', 'oekaki', 'on_side', 'out_of_frame', 'outline', 'painting', 'parody', 'partially_colored', 'partially_underwater_shot', 'perspective', 'photorealistic', 'picture_frame', 'pillarboxed', 'portrait', 'poster_(object)', 'product_placement', 'profile', 'realistic', 'recording', 'retro_artstyle', '(style)', '_style', 'sandwiched', 'science_fiction', 'sepia', 'shikishi', 'side-by-side', 'sideways', 'sideways_glance', 'silhouette', 'sketch', 'spot_color', 'still_life', 'straight-on', 'symmetry', '(texture)', 'tachi-e', 'taking_picture', 'tegaki', 'too_many', 'traditional_media', 'turnaround', 'underwater', 'upper_body', 'upside-down', 'upskirt', 'variations', 'wide_shot', '_design', 'symbolism', 'rounded_corners', 'surrounded'],
35
- 'Season':['akeome', 'anniversary', 'autumn', 'birthday', 'christmas', '_day', 'festival', 'halloween', 'kotoyoro', 'nengajou', 'new_year', 'spring_(season)', 'summer', 'tanabata', 'valentine', 'winter'],
36
- 'Background':['_background', 'backlighting', 'bloom', 'bokeh', 'brick_wall', 'bubble', 'cable', 'caustics', 'cityscape', 'cloud', 'confetti', 'constellation', 'contrail', 'crowd', 'crystal', 'dark', 'debris', 'dusk', 'dust', 'egasumi', 'embers', 'emphasis_lines', 'energy', 'evening', 'explosion', 'fireworks', 'fog', 'footprints', 'glint', 'graffiti', 'ice', 'industrial_pipe', 'landscape', 'light', 'light_particles', 'light_rays', 'lightning', 'lights', 'moonlight', 'motion_blur', 'motion_lines', 'mountainous_horizon', 'nature', '(planet)', 'pagoda', 'people', 'pillar', 'planet', 'power_lines', 'puddle', 'rain', 'rainbow', 'reflection', 'ripples', 'rubble', 'ruins', 'scenery', 'shade', 'shooting_star', 'sidelighting', 'smoke', 'snowflakes', 'snowing', 'space', 'sparkle', 'sparks', 'speed_lines', 'spider_web', 'spotlight', 'star_(sky)', 'stone_wall', 'sunbeam', 'sunburst', 'sunrise', '_theme', 'tile_wall', 'twilight', 'wall_clock', 'wall_of_text', 'water', 'waves', 'wind', 'wire', 'wooden_wall', 'lighthouse'],
37
- 'Patterns':['arrow', 'bass_clef', 'blank_censor', 'circle', 'cube', 'heart', 'hexagon', 'hexagram', 'light_censor', '(pattern)', 'pattern', 'pentagram', 'roman_numeral', '(shape)', '(symbol)', 'shape', 'sign', 'symbol', 'tally', 'treble_clef', 'triangle', 'tube', 'yagasuri'],
38
- 'Censorship':['blur_censor', '_censor', '_censoring', 'censored', 'character_censor', 'convenient', 'hair_censor', 'heart_censor', 'identity_censor', 'maebari', 'novelty_censor', 'soap_censor', 'steam_censor', 'tail_censor', 'uncensored'],
39
- 'Others':['2007', '2008', '2009', '2010', '2011', '2012', '2013', '2014', '2015', '2016', '2017', '2018', '2019', '2020', '2021', '2022', '2023', '2024', 'artist', 'artist_name', 'artistic_error', 'asian', '(company)', 'character_name', 'content_rating', 'copyright', 'cover_page', 'dated', 'english_text', 'japan', 'layer', 'logo', 'name', 'numbered', 'page_number', 'pixiv_id', 'ranguage', 'reference_sheet', 'signature', 'speech_bubble', 'subtitled', 'text', 'thank_you', 'typo', 'username', 'wallpaper', 'watermark', 'web_address', 'screwdriver', 'translated'],
40
- 'Quality Tags':['masterpiece', '_quality', 'highres', 'absurdres', 'ultra-detailed', 'lowres']}
41
-
42
- reversed_categories = {value: key for key, values in categories.items() for value in values}
43
-
44
- # Precompute keyword lengths
45
- keyword_lengths = {keyword: len(keyword) for keyword in reversed_categories}
46
-
47
- # Trie for efficient keyword matching
48
- class TrieNode:
49
- def __init__(self):
50
- self.children = {}
51
- self.category = None
52
-
53
- def build_trie(keywords):
54
- root = TrieNode()
55
- for keyword, category in reversed_categories.items():
56
- node = root
57
- for char in keyword:
58
- if char not in node.children:
59
- node.children[char] = TrieNode()
60
- node = node.children[char]
61
- node.category = category
62
- return root
63
-
64
- trie_root = build_trie(reversed_categories)
65
-
66
- def find_category(trie_root, tag):
67
- node = trie_root
68
- for char in tag:
69
- if char in node.children:
70
- node = node.children[char]
71
- if node.category:
72
- return node.category
73
- else:
74
- break
75
- return None
76
-
77
- def classify_tags(tags: list[str], local_test: bool = False):
78
- # Dictionary for automatic classification
79
- classified_tags: defaultdict[str, list] = defaultdict(list)
80
- fuzzy_match_tags: defaultdict[str, list] = defaultdict(list)
81
- unclassified_tags: list[str] = []
82
-
83
- # Logic for automatic grouping
84
- for tag in tags:
85
- classified = False
86
- tag_new = tag.replace(" ", "_").replace("-", "_").replace("\\(", "(").replace("\\)", ")") # Replace spaces in source tags with underscores
87
-
88
- # Exact match using the trie
89
- category = find_category(trie_root, tag_new)
90
- if category:
91
- classified = True
92
- else:
93
- # Fuzzy match
94
- tag_parts = tag_new.split("_")
95
- for keyword, keyword_length in keyword_lengths.items():
96
- if keyword in tag_new and keyword_length > 3: # Adjust the threshold if needed
97
- classified = True
98
- category = reversed_categories[keyword]
99
- break
100
-
101
- if classified and tag not in classified_tags[category]: # Avoid duplicates
102
- classified_tags[category].append(tag)
103
- elif not classified and tag not in unclassified_tags:
104
- unclassified_tags.append(tag) # Unclassified tags
105
-
106
- if local_test:
107
- # Output the grouping result
108
- for category, tags in classified_tags.items():
109
- print(f"{category}:")
110
- print(", ".join(tags))
111
- print()
112
-
113
- print()
114
- print("Fuzzy match:")
115
- for category, tags in fuzzy_match_tags.items():
116
- print(f"{category}:")
117
- print(", ".join(tags))
118
- print()
119
- print()
120
-
121
- if len(unclassified_tags) > 0:
122
- print(f"\nUnclassified tags: {len(unclassified_tags)}")
123
- print(f"{unclassified_tags[:200]}") # Display some unclassified tags
124
-
125
- return classified_tags, unclassified_tags
126
-
127
- # Code for "Tag Categorizer" tab
128
- def process_tags(input_tags: str):
129
- # Split tags using regex to handle both commas and question marks
130
- tags = []
131
- for tag in re.split(r'\?|,|\n', input_tags):
132
- tag = tag.strip()
133
- if tag:
134
- # Remove numbers at the end of tags
135
- tag = re.sub(r'\b\d+\b', '', tag).strip()
136
-
137
- # Replace underscores with spaces
138
- tag = tag.replace('_', ' ')
139
-
140
- # Escape parentheses (handle both escaped and unescaped)
141
- if '(' in tag or ')' in tag:
142
- # First, replace existing backslashes to handle properly
143
- tag = tag.replace('\\', '')
144
- # Replace parentheses with escaped versions
145
- tag = tag.replace('(', r'$').replace(')', r'$')
146
-
147
- if tag: # Only add if tag is not empty after processing
148
- tags.append(tag)
149
-
150
- # Classify the cleaned tags
151
- classified_tags, unclassified_tags = classify_tags(tags)
152
-
153
- # Create the outputs
154
- categorized_string = ', '.join([tag for category in classified_tags.values() for tag in category])
155
- categorized_json = {category: tags for category, tags in classified_tags.items()}
156
-
157
- return categorized_string, categorized_json, "" # Initialize enhanced_prompt as empty
 
1
+ from collections import defaultdict
2
+ import re
3
+ # Define grouping rules (categories and keywords)
4
+ # Provided categories and reversed_categories
5
+ categories={
6
+ 'Explicit':['sex', '69', 'paizuri', 'cum', 'precum', 'areola_slip', 'hetero', 'erection', 'oral', 'fellatio', 'yaoi', 'ejaculation', 'ejaculating', 'masturbation', 'handjob', 'bulge', 'rape', '_rape', 'doggystyle', 'threesome', 'missionary', 'object_insertion', 'nipple', 'nipples', 'pussy', 'anus', 'penis', 'groin', 'testicles', 'testicle', 'anal', 'cameltoe', 'areolae', 'dildo', 'clitoris', 'top-down_bottom-up', 'gag', 'groping', 'gagged', 'gangbang', 'orgasm', 'femdom', 'incest', 'bukkake', 'breast_out', 'vaginal', 'vagina', 'public_indecency', 'breast_sucking', 'folded', 'cunnilingus', '_cunnilingus', 'foreskin', 'bestiality', 'footjob', 'uterus', 'womb', 'flaccid', 'defloration', 'butt_plug', 'cowgirl_position', 'reverse_cowgirl_position', 'squatting_cowgirl_position', 'reverse_upright_straddle', 'irrumatio', 'deepthroat', 'pokephilia', 'gaping', 'orgy', 'cleft_of_venus', 'futanari', 'futasub', 'futa', 'cumdrip', 'fingering', 'vibrator', 'partially_visible_vulva', 'penetration', 'penetrated', 'cumshot', 'exhibitionism', 'breast_milk', 'grinding', 'clitoral', 'urethra', 'phimosis', 'cervix', 'impregnation', 'tribadism', 'molestation', 'pubic_hair', 'clothed_female_nude_male', 'clothed_male_nude_female', 'clothed_female_nude_female', 'clothed_male_nude_male', 'sex_machine', 'milking_machine', 'ovum', 'chikan', 'pussy_juice_drip_through_clothes', 'ejaculating_while_penetrated', 'suspended_congress', 'reverse_suspended_congress', 'spread_pussy_under_clothes', 'anilingus', 'reach-around', 'humping', 'consensual_tentacles', 'tentacle_pit', 'cum_in_'],
7
+ 'Appearance Status':['backless', 'bandaged_neck', 'bleeding', 'blood', '_blood', 'blush', 'body_writing', 'bodypaint', 'bottomless', 'breath', 'bruise', 'butt_crack', 'cold', 'covered_mouth', 'crack', 'cross-section', 'crotchless', 'crying', 'curvy', 'cuts', 'dirty', 'dripping', 'drunk', 'from_mouth', 'glowing', 'hairy', 'halterneck', 'hot', 'injury', 'latex', 'leather', 'levitation', 'lipstick_mark', '_markings', 'makeup', 'mole', 'moles', 'no_bra', 'nosebleed', 'nude', 'outfit', 'pantylines', 'peeing', 'piercing', '_piercing', 'piercings', 'pregnant', 'public_nudity', 'reverse', '_skin', '_submerged', 'saliva', 'scar', 'scratches', 'see-through', 'shadow', 'shibari', 'sideless', 'skindentation', 'sleeping', 'tan', 'soap_bubbles', 'steam', 'steaming_body', 'stitches', 'sweat', 'sweatdrop', 'sweaty', 'tanlines', 'tattoo', 'tattoo', 'tears', 'topless', 'transparent', 'trefoil', 'trembling', 'veins', 'visible_air', 'wardrobe_malfunction', 'wet', 'x-ray', 'unconscious', 'handprint'],
8
+ 'Action Pose':['afloat', 'afterimage', 'against_fourth_wall', 'against_wall', 'aiming', 'all_fours',"another's_mouth",'arm_', 'arm_support', 'arms_', 'arms_behind_back', 'asphyxiation', 'attack', 'back', 'ballet', 'bara', 'bathing', 'battle', 'bdsm', 'beckoning', 'bent_over', 'bite_mark', 'biting', 'bondage', 'breast_suppress', 'breathing', 'burning', 'bust_cup', 'carry', 'carrying', 'caught', 'chained', 'cheek_squash', 'chewing', 'cigarette', 'clapping', 'closed_eye', 'come_hither', 'cooking', 'covering', 'cuddling', 'dancing', '_docking', 'destruction', 'dorsiflexion', 'dreaming', 'dressing', 'drinking', 'driving', 'dropping', 'eating', 'exercise', 'expansion', 'exposure', 'facing', 'failure', 'fallen_down', 'falling', 'feeding', 'fetal_position', 'fighting', 'finger_on_trigger', 'finger_to_cheek', 'finger_to_mouth', 'firing', 'fishing', 'flashing', 'fleeing', 'flexible', 'flexing', 'floating', 'flying', 'fourth_wall', 'freediving', 'frogtie', '_grab', 'girl_on_top', 'giving', 'grabbing', 'grabbing_', 'gymnastics', '_hold', 'hadanugi_dousa', 'hairdressing', 'hand_', 'hand_on', 'hand_on_wall', 'hands_', 'headpat', 'hiding', 'holding', 'hug', 'hugging', 'imagining', 'in_container', 'in_mouth', 'in_palm', 'jealous', 'jumping', 'kabedon', 'kicking', 'kiss', 'kissing', 'kneeling', '_lift', 'lactation', 'laundry', 'licking', 'lifted_by_self', 'looking', 'lowleg', 'lying', 'melting', 'midair', 'moaning', '_open', 'on_back', 'on_bed', 'on_ground', 'on_lap', 'on_one_knee', 'one_eye_closed', 'open_', 'over_mouth', 'own_mouth', '_peek', '_pose', '_press', '_pull', 'padding', 'paint', 'painting_(action)', 'palms_together', 'pee', 'peeking', 'pervert', 'petting', 'pigeon-toed', 'piggyback', 'pinching', 'pinky_out', 'pinned', 'plantar_flexion', 'planted', 'playing', 'pocky', 'pointing', 'poke', 'poking', 'pouring', 'pov', 'praying', 'presenting', 'profanity', 'pulled_by_self', 'pulling', 'pump_action', 'punching', '_rest', 'raised', 'reaching', 'reading', 'reclining', 'reverse_grip', 'riding', 'running', '_slip', 'salute', 'screaming', 'seiza', 'selfie', 'sewing', 'shaking', 'shoe_dangle', 'shopping', 'shouting', 'showering', 'shushing', 'singing', 'sitting', 'slapping', 'smell', 'smelling', 'smoking', 'smother', 'solo', 'spanked', 'spill', 'spilling', 'spinning', 'splashing', 'split', 'squatting', 'squeezed', 'breasts_squeezed_together', 'standing', 'standing_on_', 'staring', 'straddling', 'strangling', 'stretching', 'surfing', 'suspension', 'swimming', 'talking', 'teardrop', 'tearing_clothes', 'throwing', 'tied_up', 'tiptoes', 'toe_scrunch', 'toothbrush', 'trigger_discipline', 'tripping', 'tsundere', 'turning_head', 'twitching', 'two-handed', 'tying', '_up', 'unbuttoned', 'undressed', 'undressing', 'unsheathed', 'unsheathing', 'unzipped', 'unzipping', 'upright_straddle', 'v', 'V', 'vore', '_wielding', 'wading', 'walk-in', 'walking', 'wariza', 'waving', 'wedgie', 'wrestling', 'writing', 'yawning', 'yokozuwari', '_conscious', 'massage', 'struggling', 'shrugging', 'drugged', 'tentacles_under_clothes', 'restrained_by_tentacles', 'tentacles_around_arms', 'tentacles_around_legs', 'restrained_legs', 'restrained_tail', 'restrained_arms', 'tentacles_on_female', 'archery', 'cleaning', 'tempura', 'facepalm', 'sadism'],
9
+ 'Headwear':['antennae', 'antlers', 'aura', 'bandaged_head', 'bandana', 'bandeau', 'beanie', 'beanie', 'beret', 'bespectacled', 'blindfold', 'bonnet', '_cap', 'circlet', 'crown', '_drill', '_drills', 'diadem', '_eyewear', 'ear_covers', 'ear_ornament', 'ear_tag', 'earbuds', 'earclip', 'earmuffs', 'earphones', 'earpiece', 'earring', 'earrings', 'eyeliner', 'eyepatch', 'eyewear_on_head', 'facial', 'fedora', 'glasses', 'goggles', '_headwear', 'hachimaki', 'hair_bobbles', 'hair_ornament', 'hair_rings', 'hair_tie', 'hairband', 'hairclip', 'hairpin', 'hairpods', 'halo', 'hat', 'head-mounted_display', 'head_wreath', 'headband', 'headdress', 'headgear', 'headphones', 'headpiece', 'headset', 'helm', 'helmet', 'hood', 'kabuto_(helmet)', 'kanzashi', '_mask', 'maid_headdress', 'mask', 'mask', 'mechanical_ears', 'mechanical_eye', 'mechanical_horns', 'mob_cap', 'monocle', 'neck_ruff', 'nightcap', 'on_head', 'pince-nez', 'qingdai_guanmao', 'scarf_over_mouth', 'scrunchie', 'sunglasses',"tam_o'_shanter",'tate_eboshi', 'tiara', 'topknot', 'turban', 'veil', 'visor', 'wig', 'mitre', 'tricorne', 'bicorne'],
10
+ 'Handwear':['arm_warmers', 'armband', 'armlet', 'bandaged_arm', 'bandaged_fingers', 'bandaged_hand', 'bandaged_wrist', 'bangle', 'bracelet', 'bracelets', 'bracer', 'cuffs', 'elbow_pads', '_gauntlets', '_glove', '_gloves', 'gauntlets', 'gloves', 'kote', 'kurokote', 'mechanical_arm', 'mechanical_arms', 'mechanical_hands', 'mittens', 'mitts', 'nail_polish', 'prosthetic_arm', 'wrist_cuffs', 'wrist_guards', 'wristband', 'yugake'],
11
+ 'One-Piece Outfit':['bodystocking', 'bodysuit', 'dress', 'furisode', 'gown', 'hanfu', 'jumpsuit', 'kimono', 'leotard', 'microdress', 'one-piece', 'overalls', 'robe', 'spacesuit', 'sundress', 'yukata'],
12
+ 'Upper Body Clothing':['aiguillette', 'apron', '_apron', 'armor', '_armor', 'ascot', 'babydoll', 'bikini', '_bikini', 'blazer', '_blazer', 'blouse', '_blouse', 'bowtie', '_bowtie', 'bra', '_bra', 'breast_curtain', 'breast_curtains', 'breast_pocket', 'breastplate', 'bustier', 'camisole', 'cape', 'capelet', 'cardigan', 'center_opening', 'chemise', 'chest_jewel', 'choker', 'cloak', 'coat', 'coattails', 'collar', '_collar', 'corset', 'criss-cross_halter', 'crop_top', 'dougi', 'feather_boa', 'gakuran', 'hagoromo', 'hanten_(clothes)', 'haori', 'harem_pants', 'harness', 'hoodie', 'jacket', '_jacket', 'japanese_clothes', 'kappougi', 'kariginu', 'lapels', 'lingerie', '_lingerie', 'maid', 'mechanical_wings', 'mizu_happi', 'muneate', 'neckerchief', 'necktie', 'negligee', 'nightgown', 'pajamas', '_pajamas', 'pauldron', 'pauldrons', 'plunging_neckline', 'raincoat', 'rei_no_himo', 'sailor_collar', 'sarashi', 'scarf', 'serafuku', 'shawl', 'shirt', 'shoulder_', 'sleepwear', 'sleeve', 'sleeveless', 'sleeves', '_sleeves', 'sode', 'spaghetti_strap', 'sportswear', 'strapless', 'suit', 'sundress', 'suspenders', 'sweater', 'swimsuit', '_top', '_torso', 't-shirt', 'tabard', 'tailcoat', 'tank_top', 'tasuki', 'tie_clip', 'tunic', 'turtleneck', 'tuxedo', '_uniform', 'undershirt', 'uniform', 'v-neck', 'vambraces', 'vest', 'waistcoat'],
13
+ 'Lower Body Clothing':['bare_hips', 'bloomers', 'briefs', 'buruma', 'crotch_seam', 'cutoffs', 'denim', 'faulds', 'fundoshi', 'g-string', 'garter_straps', 'hakama', 'hip_vent', 'jeans', 'knee_pads', 'loincloth', 'mechanical_tail', 'microskirt', 'miniskirt', 'overskirt', 'panties', 'pants', 'pantsu', 'panty_straps', 'pelvic_curtain', 'petticoat', 'sarong', 'shorts', 'side_slit', 'skirt', 'sweatpants', 'swim_trunks', 'thong', 'underwear', 'waist_cape'],
14
+ 'Foot & Legwear':['anklet', 'bandaged_leg', 'boot', 'boots', '_footwear', 'flats', 'flip-flops', 'geta', 'greaves', '_heels', 'kneehigh', 'kneehighs', '_legwear', 'leg_warmers', 'leggings', 'loafers', 'mary_janes', 'mechanical_legs', 'okobo', 'over-kneehighs', 'pantyhose', 'prosthetic_leg', 'pumps', '_shoe', '_sock', 'sandals', 'shoes', 'skates', 'slippers', 'sneakers', 'socks', 'spikes', 'tabi', 'tengu-geta', 'thigh_strap', 'thighhighs', 'uwabaki', 'zouri', 'legband', 'ankleband'],
15
+ 'Other Accessories':['alternate_', 'anklet', 'badge', 'beads', 'belt', 'belts', 'bow', 'brooch', 'buckle', 'button', 'buttons', '_clothes', '_costume', '_cutout', 'casual', 'charm', 'clothes_writing', 'clothing_aside', 'costume', 'cow_print', 'cross', 'd-pad', 'double-breasted', 'drawstring', 'epaulettes', 'fabric', 'fishnets', 'floral_print', 'formal', 'frills', '_garter', 'gem', 'holster', 'jewelry', '_knot', 'lace', 'lanyard', 'leash', 'magatama', 'mechanical_parts', 'medal', 'medallion', 'naked_bandage', 'necklace', '_ornament', '(ornament)', 'o-ring', 'obi', 'obiage', 'obijime', '_pin', '_print', 'padlock', 'patterned_clothing', 'pendant', 'piercing', 'plaid', 'pocket', 'polka_dot', 'pom_pom_(clothes)', 'pom_pom_(clothes)', 'pouch', 'ribbon', '_ribbon', '_stripe', '_stripes', 'sash', 'shackles', 'shimenawa', 'shrug_(clothing)', 'skin_tight', 'spandex', 'strap', 'sweatband', '_trim', 'tassel', 'zettai_ryouiki', 'zipper'],
16
+ 'Facial Expression':['ahegao', 'anger_vein', 'angry', 'annoyed', 'confused', 'drooling', 'embarrassed', 'expressionless', 'eye_contact', '_face', 'frown', 'fucked_silly', 'furrowed_brow', 'glaring', 'gloom_(expression)', 'grimace', 'grin', 'happy', 'jitome', 'laughing', '_mouth', 'nervous', 'notice_lines', 'o_o', 'parted_lips', 'pout', 'puff_of_air', 'restrained', 'sad', 'sanpaku', 'scared', 'scowl', 'serious', 'shaded_face', 'shy', 'sigh', 'sleepy', 'smile', 'smirk', 'smug', 'snot', 'spoken_ellipsis', 'spoken_exclamation_mark', 'spoken_interrobang', 'spoken_question_mark', 'squiggle', 'surprised', 'tareme', 'tearing_up', 'thinking', 'tongue', 'tongue_out', 'torogao', 'tsurime', 'turn_pale', 'wide-eyed', 'wince', 'worried', 'heartbeat'],
17
+ 'Facial Emoji':['!!', '!', '!?', '+++', '+_+', '...', '...?', '._.', '03:00', '0_0', ':/', ':3', ':<', ':>', ':>=', ':d', ':i', ':o', ':p', ':q', ':t', ':x', ':|', ';(', ';)', ';3', ';d', ';o', ';p', ';q', '=_=', '>:(', '>:)', '>_<', '>_o', '>o<', '?', '??', '@_@', '\\m/', '\n/', '\\o/', '\\||/', '^^^', '^_^', 'c:', 'd:', 'o_o', 'o3o', 'u_u', 'w', 'x', 'x_x', 'xd', 'zzz', '|_|'],
18
+ 'Head':['afro', 'ahoge', 'animal_ear_fluff', '_bangs', '_bun', 'bald', 'beard', 'blunt_bangs', 'blunt_ends', 'bob_cut', 'bowl_cut', 'braid', 'braids', 'buzz_cut', 'circle_cut', 'colored_tips', 'cowlick', 'dot_nose', 'dreadlocks', '_ear', '_ears', '_eye', '_eyes', 'enpera', 'eyeball', 'eyebrow', 'eyebrow_cut', 'eyebrows', 'eyelashes', 'eyeshadow', 'faceless', 'facepaint', 'facial_mark', 'fang', 'forehead', 'freckles', 'goatee', '_hair', '_horn', '_horns', 'hair_', 'hair_bun', 'hair_flaps', 'hair_intakes', 'hair_tubes', 'half_updo', 'head_tilt', 'heterochromia', 'hime_cut', 'hime_cut', 'horns', 'in_eye', 'inverted_bob', 'kemonomimi_mode', 'lips', 'mascara', 'mohawk', 'mouth_', 'mustache', 'nose', 'one-eyed', 'one_eye', 'one_side_up', '_pupils', 'parted_bangs', 'pompadour', 'ponytail', 'ringlets', '_sclera', 'sideburns', 'sidecut', 'sidelock', 'sidelocks', 'skull', 'snout', 'stubble', 'swept_bangs', 'tails', 'teeth', 'third_eye', 'twintails', 'two_side_up', 'undercut', 'updo', 'v-shaped_eyebrows', 'whiskers', 'tentacle_hair'],
19
+ 'Hands':['_arm', '_arms', 'claws', '_finger', '_fingers', 'fingernails', '_hand', '_nail', '_nails', 'palms', 'rings', 'thumbs_up'],
20
+ 'Upper Body':['abs', 'armpit', 'armpits', 'backboob', 'belly', 'biceps', 'breast_rest', 'breasts', 'button_gap', 'cleavage', 'collarbone', 'dimples_of_venus', 'downblouse', 'flat_chest', 'linea_alba', 'median_furrow', 'midriff', 'nape', 'navel', 'pectorals', 'ribs', '_shoulder', '_shoulders', 'shoulder_blades', 'sideboob', 'sidetail', 'spine', 'stomach', 'strap_gap', 'toned', 'underboob', 'underbust'],
21
+ 'Lower Body':['ankles', 'ass', 'barefoot', 'crotch', 'feet', 'highleg', 'hip_bones', 'hooves', 'kneepits', 'knees', 'legs', 'soles', 'tail', 'thigh_gap', 'thighlet', 'thighs', 'toenail', 'toenails', 'toes', 'wide_hips'],
22
+ 'Creature':['(animal)', 'anglerfish', 'animal', 'bear', 'bee', 'bird', 'bug', 'butterfly', 'cat', 'chick', 'chicken', 'chinese_zodiac', 'clownfish', 'coral', 'crab', 'creature', 'crow', 'dog', 'dove', 'dragon', 'duck', 'eagle', 'fish', 'fish', 'fox', 'fox', 'frog', 'frog', 'goldfish', 'hamster', 'horse', 'jellyfish', 'ladybug', 'lion', 'mouse', 'octopus', 'owl', 'panda', 'penguin', 'pig', 'pigeon', 'rabbit', 'rooster', 'seagull', 'shark', 'sheep', 'shrimp', 'snail', 'snake', 'squid', 'starfish', 'tanuki', 'tentacles', 'goo_tentacles', 'plant_tentacles', 'crotch_tentacles', 'mechanical_tentacles', 'squidward_tentacles', 'suction_tentacles', 'penis_tentacles', 'translucent_tentacles', 'back_tentacles', 'red_tentacles', 'green_tentacles', 'blue_tentacles', 'black_tentacles', 'pink_tentacles', 'purple_tentacles', 'face_tentacles', 'tentacles_everywhere', 'milking_tentacles', 'tiger', 'turtle', 'weasel', 'whale', 'wolf', 'parrot', 'sparrow', 'unicorn'],
23
+ 'Plant':['bamboo', 'bouquet', 'branch', 'bush', 'cherry_blossoms', 'clover', 'daisy', '(flower)', 'flower', 'flower', 'gourd', 'hibiscus', 'holly', 'hydrangea', 'leaf', 'lily_pad', 'lotus', 'moss', 'palm_leaf', 'palm_tree', 'petals', 'plant', 'plum_blossoms', 'rose', 'spider_lily', 'sunflower', 'thorns', 'tree', 'tulip', 'vines', 'wisteria', 'acorn'],
24
+ 'Food':['apple', 'baguette', 'banana', 'baozi', 'beans', 'bento', 'berry', 'blueberry', 'bread', 'broccoli', 'burger', 'cabbage', 'cake', 'candy', 'carrot', 'cheese', 'cherry', 'chili_pepper', 'chocolate', 'coconut', 'cookie', 'corn', 'cream', 'crepe', 'cucumber', 'cucumber', 'cupcake', 'curry', 'dango', 'dessert', 'doughnut', 'egg', 'eggplant', '_(food)', '_(fruit)', 'food', 'french_fries', 'fruit', 'grapes', 'ice_cream', 'icing', 'lemon', 'lettuce', 'lollipop', 'macaron', 'mandarin_orange', 'meat', 'melon', 'mochi', 'mushroom', 'noodles', 'omelet', 'omurice', 'onigiri', 'onion', 'pancake', 'parfait', 'pasties', 'pastry', 'peach', 'pineapple', 'pizza', 'popsicle', 'potato', 'pudding', 'pumpkin', 'radish', 'ramen', 'raspberry', 'rice', 'roasted_sweet_potato', 'sandwich', 'sausage', 'seaweed', 'skewer', 'spitroast', 'spring_onion', 'strawberry', 'sushi', 'sweet_potato', 'sweets', 'taiyaki', 'takoyaki', 'tamagoyaki', 'tempurakanbea', 'toast', 'tomato', 'vegetable', 'wagashi', 'wagashi', 'watermelon', 'jam', 'popcorn'],
25
+ 'Beverage':['alcohol', 'beer', 'coffee', 'cola', 'drink', 'juice', 'juice_box', 'milk', 'sake', 'soda', 'tea', '_tea', 'whiskey', 'wine', 'cocktail'],
26
+ 'Music':['band', 'baton_(conducting)', 'beamed', 'cello', 'concert', 'drum', 'drumsticks', 'eighth_note', 'flute', 'guitar', 'harp', 'horn', '(instrument)', 'idol', 'instrument', 'k-pop', 'lyre', '(music)', 'megaphone', 'microphone', 'music', 'musical_note', 'phonograph', 'piano', 'plectrum', 'quarter_note', 'recorder', 'sixteenth_note', 'sound_effects', 'trumpet', 'utaite', 'violin', 'whistle'],
27
+ 'Weapons & Equipment':['ammunition', 'arrow_(projectile)', 'axe', 'bandolier', 'baseball_bat', 'beretta_92', 'bolt_action', 'bomb', 'bullet', 'bullpup', 'cannon', 'chainsaw', 'crossbow', 'dagger', 'energy_sword', 'explosive', 'fighter_jet', 'gohei', 'grenade', 'gun', 'hammer', 'handgun', 'holstered', 'jet', 'katana', 'knife', 'kunai', 'lance', 'mallet', 'nata_(tool)', 'polearm', 'quiver', 'rapier', 'revolver', 'rifle', 'rocket_launcher', 'scabbard', 'scope', 'scythe', 'sheath', 'sheathed', 'shield', 'shotgun', 'shuriken', 'spear', 'staff', 'suppressor', 'sword', 'tank', 'tantou', 'torpedo', 'trident', '(weapon)', 'wand', 'weapon', 'whip', 'yumi_(bow)', 'h&k_hk416', 'rocket_launcher', 'heckler_&_koch', '_weapon'],
28
+ 'Vehicles':['aircraft', 'airplane', 'bicycle', 'boat', 'car', 'caterpillar_tracks', 'flight_deck', 'helicopter', 'motor_vehicle', 'motorcycle', 'ship', 'spacecraft', 'spoiler_(automobile)', 'train', 'truck', 'watercraft', 'wheel', 'wheelbarrow', 'wheelchair', 'inflatable_raft'],
29
+ 'Buildings':['apartment', 'aquarium', 'architecture', 'balcony', 'building', 'cafe', 'castle', 'church', 'gym', 'hallway', 'hospital', 'house', 'library', '(place)', 'porch', 'restaurant', 'restroom', 'rooftop', 'shop', 'skyscraper', 'stadium', 'stage', 'temple', 'toilet', 'tower', 'train_station', 'veranda'],
30
+ 'Indoor':['bath', 'bathroom', 'bathtub', 'bed', 'bed_sheet', 'bedroom', 'blanket', 'bookshelf', 'carpet', 'ceiling', 'chair', 'chalkboard', 'classroom', 'counter', 'cupboard', 'curtains', 'cushion', 'dakimakura', 'desk', 'door', 'doorway', 'drawer', '_floor', 'floor', 'futon', 'indoors', 'interior', 'kitchen', 'kotatsu', 'locker', 'mirror', 'pillow', 'room', 'rug', 'school_desk', 'shelf', 'shouji', 'sink', 'sliding_doors', 'stairs', 'stool', 'storeroom', 'table', 'tatami', 'throne', 'window', 'windowsill', 'bathhouse', 'chest_of_drawers'],
31
+ 'Outdoor':['alley', 'arch', 'beach', 'bridge', 'bus_stop', 'bush', 'cave', '(city)', 'city', 'cliff', 'crescent', 'crosswalk', 'day', 'desert', 'fence', 'ferris_wheel', 'field', 'forest', 'grass', 'graveyard', 'hill', 'lake', 'lamppost', 'moon', 'mountain', 'night', 'ocean', 'onsen', 'outdoors', 'path', 'pool', 'poolside', 'railing', 'railroad', 'river', 'road', 'rock', 'sand', 'shore', 'sky', 'smokestack', 'snow', 'snowball', 'snowman', 'street', 'sun', 'sunlight', 'sunset', 'tent', 'torii', 'town', 'tree', 'turret', 'utility_pole', 'valley', 'village', 'waterfall'],
32
+ 'Objects':['anchor', 'android', 'armchair', '(bottle)', 'backpack', 'bag', 'ball', 'balloon', 'bandages', 'bandaid', 'bandaids', 'banknote', 'banner', 'barcode', 'barrel', 'baseball', 'basket', 'basketball', 'beachball', 'bell', 'bench', 'binoculars', 'board_game', 'bone', 'book', 'bottle', 'bowl', 'box', 'box_art', 'briefcase', 'broom', 'bucket', '(chess)', '(computer)', '(computing)', '(container)', 'cage', 'calligraphy_brush', 'camera', 'can', 'candle', 'candlestand', 'cane', 'card', 'cartridge', 'cellphone', 'chain', 'chandelier', 'chess', 'chess_piece', 'choko_(cup)', 'chopsticks', 'cigar', 'clipboard', 'clock', 'clothesline', 'coin', 'comb', 'computer', 'condom', 'controller', 'cosmetics', 'couch', 'cowbell', 'crazy_straw', 'cup', 'cutting_board', 'dice', 'digital_media_player', 'doll', 'drawing_tablet', 'drinking_straw', 'easel', 'electric_fan', 'emblem', 'envelope', 'eraser', 'feathers', 'figure', 'fire', 'fishing_rod', 'flag', 'flask', 'folding_fan', 'fork', 'frying_pan', '(gemstone)', 'game_console', 'gears', 'gemstone', 'gift', 'glass', 'glowstick', 'gold', 'handbag', 'handcuffs', 'handheld_game_console', 'hose', 'id_card', 'innertube', 'iphone',"jack-o'-lantern",'jar', 'joystick', 'key', 'keychain', 'kiseru', 'ladder', 'ladle', 'lamp', 'lantern', 'laptop', 'letter', 'letterboxed', 'lifebuoy', 'lipstick', 'liquid', 'lock', 'lotion', '_machine', 'map', 'marker', 'model_kit', 'money', 'monitor', 'mop', 'mug', 'needle', 'newspaper', 'nintendo', 'nintendo_switch', 'notebook', '(object)', 'ofuda', 'orb', 'origami', '(playing_card)', 'pack', 'paddle', 'paintbrush', 'pan', 'paper', 'parasol', 'patch', 'pc', 'pen', 'pencil', 'pencil', 'pendant_watch', 'phone', 'pill', 'pinwheel', 'plate', 'playstation', 'pocket_watch', 'pointer', 'poke_ball', 'pole', 'quill', 'racket', 'randoseru', 'remote_control', 'ring', 'rope', 'sack', 'saddle', 'sakazuki', 'satchel', 'saucer', 'scissors', 'scroll', 'seashell', 'seatbelt', 'shell', 'shide', 'shopping_cart', 'shovel', 'shower_head', 'silk', 'sketchbook', 'smartphone', 'soap', 'sparkler', 'spatula', 'speaker', 'spoon', 'statue', 'stethoscope', 'stick', 'sticker', 'stopwatch', 'string', 'stuffed_', 'stylus', 'suction_cups', 'suitcase', 'surfboard', 'syringe', 'talisman', 'tanzaku', 'tape', 'teacup', 'teapot', 'teddy_bear', 'television', 'test_tube', 'tiles', 'tokkuri', 'tombstone', 'torch', 'towel', 'toy', 'traffic_cone', 'tray', 'treasure_chest', 'uchiwa', 'umbrella', 'vase', 'vial', 'video_game', 'viewfinder', 'volleyball', 'wallet', 'watch', 'watch', 'whisk', 'whiteboard', 'wreath', 'wrench', 'wristwatch', 'yunomi', 'ace_of_hearts', 'inkwell', 'compass', 'ipod', 'sunscreen', 'rocket', 'cobblestone'],
33
+ 'Character Design':['+boys', '+girls', '1other', '39', '_boys', '_challenge', '_connection', '_female', '_fur', '_girls', '_interface', '_male', '_man', '_person', 'abyssal_ship', 'age_difference', 'aged_down', 'aged_up', 'albino', 'alien', 'alternate_muscle_size', 'ambiguous_gender', 'amputee', 'androgynous', 'angel', 'animalization', 'ass-to-ass', 'assault_visor', 'au_ra', 'baby', 'bartender', 'beak', 'bishounen', 'borrowed_character', 'boxers', 'boy', 'breast_envy', 'breathing_fire', 'bride', 'broken', 'brother_and_sister', 'brothers', 'camouflage', 'cheating_(relationship)', 'cheerleader', 'chibi', 'child', 'clone', 'command_spell', 'comparison', 'contemporary', 'corpse', 'corruption', 'cosplay', 'couple', 'creature_and_personification', 'crossdressing', 'crossover', 'cyberpunk', 'cyborg', 'cyclops', 'damaged', 'dancer', 'danmaku', 'darkness', 'death', 'defeat', 'demon', 'disembodied_', 'draph', 'drone', 'duel', 'dwarf', 'egyptian', 'electricity', 'elezen', 'elf', 'enmaided', 'erune', 'everyone', 'evolutionary_line', 'expressions', 'fairy', 'family', 'fangs', 'fantasy', 'fashion', 'fat', 'father_and_daughter', 'father_and_son', 'fewer_digits', 'fins', 'flashback', 'fluffy', 'fumo_(doll)', 'furry', 'fusion', 'fuuin_no_tsue', 'gameplay_mechanics', 'genderswap', 'ghost', 'giant', 'giantess', 'gibson_les_paul', 'girl', 'goblin', 'groom', 'guro', 'gyaru', 'habit', 'harem', 'harpy', 'harvin', 'heads_together', 'health_bar', 'height_difference', 'hitodama', 'horror_(theme)', 'humanization', 'husband_and_wife', 'hydrokinesis', 'hypnosis', 'hyur', 'idol', 'insignia', 'instant_loss', 'interracial', 'interspecies', 'japari_bun', 'jeweled_branch_of_hourai', 'jiangshi', 'jirai_kei', 'joints', 'karakasa_obake', 'keyhole', 'kitsune', 'knight', 'kodona', 'kogal', 'kyuubi', 'lamia', 'left-handed', 'loli', 'lolita', 'look-alike', 'machinery', 'magic', 'male_focus', 'manly', 'matching_outfits', 'mature_female', 'mecha', 'mermaid', 'meta', 'miko', 'milestone_celebration', 'military', 'mind_control', 'miniboy', 'minigirl',"miqo'te",'monster', 'monsterification', 'mother_and_daughter', 'mother_and_son', 'multiple_others', 'muscular', 'nanodesu_(phrase)', 'narrow_waist', 'nekomata', 'netorare', 'ninja', 'no_humans', 'nontraditional', 'nun', 'nurse', 'object_namesake', 'obliques', 'office_lady', 'old', 'on_body', 'onee-shota', 'oni', 'orc', 'others', 'otoko_no_ko', 'oversized_object', 'paint_splatter', 'pantyshot', 'pawpads', 'persona', 'personality', 'personification', 'pet_play', 'petite', 'pirate', 'playboy_bunny', 'player_2', 'plugsuit', 'plump', 'poi', 'pokemon', 'police', 'policewoman', 'pom_pom_(cheerleading)', 'princess', 'prosthesis', 'pun', 'puppet', 'race_queen', 'radio_antenna', 'real_life_insert', 'redesign', 'reverse_trap', 'rigging', 'robot', 'rod_of_remorse', 'sailor', 'salaryman', 'samurai', 'sangvis_ferri', 'scales', 'scene_reference', 'school', 'sheikah', 'shota', 'shrine', 'siblings', 'side-by-side', 'sidesaddle', 'sisters', 'size_difference', 'skeleton', 'skinny', 'slave', 'slime_(substance)', 'soldier', 'spiked_shell', 'spokencharacter', 'steampunk', 'streetwear', 'striker_unit', 'strongman', 'submerged', 'suggestive', 'super_saiyan', 'superhero', 'surreal', 'take_your_pick', 'tall', 'talons', 'taur', 'teacher', 'team_rocket', 'three-dimensional_maneuver_gear', 'time_paradox', 'tomboy', 'traditional_youkai', 'transformation', 'trick_or_treat', 'tusks', 'twins', 'ufo', 'under_covers', 'v-fin', 'v-fin', 'vampire', 'virtual_youtuber', 'waitress', 'watching_television', 'wedding', 'what', 'when_you_see_it', 'wife_and_wife', 'wing', 'wings', 'witch', 'world_war_ii', 'yandere', 'year_of', 'yes', 'yin_yang', 'yordle',"you're_doing_it_wrong",'you_gonna_get_raped', 'yukkuri_shiteitte_ne', 'yuri', 'zombie', '(alice_in_wonderland)', '(arknights)', '(blue_archive)', '(cosplay)', '(creature)', '(emblem)', '(evangelion)', '(fate)', '(fate/stay_night)', '(ff11)', '(fire_emblem)', '(genshin_impact)', '(grimm)', '(houseki_no_kuni)', '(hyouka)', '(idolmaster)', '(jojo)', '(kancolle)', '(kantai_collection)', '(kill_la_kill)', '(league_of_legends)', '(legends)', '(lyomsnpmp)', '(machimazo)', '(madoka_magica)', '(mecha)', '(meme)', '(nier:automata)', '(organ)', '(overwatch)', '(pokemon)', '(project_moon)', '(project_sekai)', '(sao)', '(senran_kagura)', '(splatoon)', '(touhou)', '(tsukumo_sana)', '(youkai_watch)', '(yu-gi-oh!_gx)', '(zelda)', 'sextuplets', 'imperial_japanese_army', 'extra_faces', '_miku'],
34
+ 'Composition':['abstract', 'anime_coloring', 'animification', 'back-to-back', 'bad_anatomy', 'blurry', 'border', 'bound', 'cameo', 'cheek-to-cheek', 'chromatic_aberration', 'close-up', 'collage', 'color_guide', 'colorful', 'comic', 'contrapposto', 'cover', 'cowboy_shot', 'crosshatching', 'depth_of_field', 'dominatrix', 'dutch_angle', '_focus', 'face-to-face', 'fake_screenshot', 'film_grain', 'fisheye', 'flat_color', 'foreshortening', 'from_above', 'from_behind', 'from_below', 'from_side', 'full_body', 'glitch', 'greyscale', 'halftone', 'head_only', 'heads-up_display', 'high_contrast', 'horizon', '_inset', 'inset', 'jaggy_lines', '1koma', '2koma', '3koma', '4koma', '5koma', 'leaning', 'leaning_forward', 'leaning_to_the_side', 'left-to-right_manga', 'lens_flare', 'limited_palette', 'lineart', 'lineup', 'lower_body', '(medium)', 'marker_(medium)', 'meme', 'mixed_media', 'monochrome', 'multiple_views', 'muted_color', 'oekaki', 'on_side', 'out_of_frame', 'outline', 'painting', 'parody', 'partially_colored', 'partially_underwater_shot', 'perspective', 'photorealistic', 'picture_frame', 'pillarboxed', 'portrait', 'poster_(object)', 'product_placement', 'profile', 'realistic', 'recording', 'retro_artstyle', '(style)', '_style', 'sandwiched', 'science_fiction', 'sepia', 'shikishi', 'side-by-side', 'sideways', 'sideways_glance', 'silhouette', 'sketch', 'spot_color', 'still_life', 'straight-on', 'symmetry', '(texture)', 'tachi-e', 'taking_picture', 'tegaki', 'too_many', 'traditional_media', 'turnaround', 'underwater', 'upper_body', 'upside-down', 'upskirt', 'variations', 'wide_shot', '_design', 'symbolism', 'rounded_corners', 'surrounded'],
35
+ 'Season':['akeome', 'anniversary', 'autumn', 'birthday', 'christmas', '_day', 'festival', 'halloween', 'kotoyoro', 'nengajou', 'new_year', 'spring_(season)', 'summer', 'tanabata', 'valentine', 'winter'],
36
+ 'Background':['_background', 'backlighting', 'bloom', 'bokeh', 'brick_wall', 'bubble', 'cable', 'caustics', 'cityscape', 'cloud', 'confetti', 'constellation', 'contrail', 'crowd', 'crystal', 'dark', 'debris', 'dusk', 'dust', 'egasumi', 'embers', 'emphasis_lines', 'energy', 'evening', 'explosion', 'fireworks', 'fog', 'footprints', 'glint', 'graffiti', 'ice', 'industrial_pipe', 'landscape', 'light', 'light_particles', 'light_rays', 'lightning', 'lights', 'moonlight', 'motion_blur', 'motion_lines', 'mountainous_horizon', 'nature', '(planet)', 'pagoda', 'people', 'pillar', 'planet', 'power_lines', 'puddle', 'rain', 'rainbow', 'reflection', 'ripples', 'rubble', 'ruins', 'scenery', 'shade', 'shooting_star', 'sidelighting', 'smoke', 'snowflakes', 'snowing', 'space', 'sparkle', 'sparks', 'speed_lines', 'spider_web', 'spotlight', 'star_(sky)', 'stone_wall', 'sunbeam', 'sunburst', 'sunrise', '_theme', 'tile_wall', 'twilight', 'wall_clock', 'wall_of_text', 'water', 'waves', 'wind', 'wire', 'wooden_wall', 'lighthouse'],
37
+ 'Patterns':['arrow', 'bass_clef', 'blank_censor', 'circle', 'cube', 'heart', 'hexagon', 'hexagram', 'light_censor', '(pattern)', 'pattern', 'pentagram', 'roman_numeral', '(shape)', '(symbol)', 'shape', 'sign', 'symbol', 'tally', 'treble_clef', 'triangle', 'tube', 'yagasuri'],
38
+ 'Censorship':['blur_censor', '_censor', '_censoring', 'censored', 'character_censor', 'convenient', 'hair_censor', 'heart_censor', 'identity_censor', 'maebari', 'novelty_censor', 'soap_censor', 'steam_censor', 'tail_censor', 'uncensored'],
39
+ 'Others':['2007', '2008', '2009', '2010', '2011', '2012', '2013', '2014', '2015', '2016', '2017', '2018', '2019', '2020', '2021', '2022', '2023', '2024', 'artist', 'artist_name', 'artistic_error', 'asian', '(company)', 'character_name', 'content_rating', 'copyright', 'cover_page', 'dated', 'english_text', 'japan', 'layer', 'logo', 'name', 'numbered', 'page_number', 'pixiv_id', 'language', 'reference_sheet', 'signature', 'speech_bubble', 'subtitled', 'text', 'thank_you', 'typo', 'username', 'wallpaper', 'watermark', 'web_address', 'screwdriver', 'translated'],
40
+ 'Quality Tags':['masterpiece', '_quality', 'highres', 'absurdres', 'ultra-detailed', 'lowres']}
41
+
42
+ # Create reversed_categories with escaped versions for pattern matching
43
+ reversed_categories = {}
44
+ for key, values in categories.items():
45
+ for value in values:
46
+ # Store both the original and escaped version for matching
47
+ reversed_categories[value] = key
48
+ if '(' in value or ')' in value:
49
+ escaped_value = value.replace('(', '\\(').replace(')', '\\)')
50
+ reversed_categories[escaped_value] = key
51
+
52
+ # Precompute keyword lengths
53
+ keyword_lengths = {keyword: len(keyword) for keyword in reversed_categories}
54
+
55
+ # Trie for efficient keyword matching
56
+ class TrieNode:
57
+ def __init__(self):
58
+ self.children = {}
59
+ self.category = None
60
+
61
+ def build_trie(keywords):
62
+ root = TrieNode()
63
+ for keyword, category in reversed_categories.items():
64
+ node = root
65
+ for char in keyword:
66
+ if char not in node.children:
67
+ node.children[char] = TrieNode()
68
+ node = node.children[char]
69
+ node.category = category
70
+ return root
71
+
72
+ trie_root = build_trie(reversed_categories)
73
+
74
+ def find_category(trie_root, tag):
75
+ node = trie_root
76
+ for char in tag:
77
+ if char in node.children:
78
+ node = node.children[char]
79
+ if node.category:
80
+ return node.category
81
+ else:
82
+ break
83
+ return None
84
+
85
+ def classify_tags(tags: list[str], local_test: bool = False):
86
+ # Dictionary for automatic classification
87
+ classified_tags: defaultdict[str, list] = defaultdict(list)
88
+ unclassified_tags: list[str] = []
89
+
90
+ # Logic for automatic grouping
91
+ for tag in tags:
92
+ classified = False
93
+
94
+ # Keep original tag for storage, create normalized version for matching
95
+ tag_normalized = tag.replace(" ", "_").replace("-", "_")
96
+
97
+ # Try exact match with normalized tag
98
+ category = find_category(trie_root, tag_normalized)
99
+
100
+ # Also try with escaped parentheses if tag contains parentheses?
101
+ if not category and ('(' in tag_normalized or ')' in tag_normalized):
102
+ tag_escaped = tag_normalized.replace("(", "\\(").replace(")", "\\)")
103
+ category = find_category(trie_root, tag_escaped)
104
+
105
+ if category:
106
+ classified = True
107
+ if tag not in classified_tags[category]: # Avoid duplicates
108
+ classified_tags[category].append(tag)
109
+ else:
110
+ # Fuzzy match
111
+ tag_parts = tag_normalized.split("_")
112
+ found_category = None
113
+ for keyword, keyword_length in keyword_lengths.items():
114
+ if keyword in tag_normalized and keyword_length > 3:
115
+ found_category = reversed_categories[keyword]
116
+ break
117
+
118
+ if found_category and tag not in classified_tags[found_category]:
119
+ classified_tags[found_category].append(tag)
120
+ classified = True
121
+
122
+ if not classified and tag not in unclassified_tags:
123
+ unclassified_tags.append(tag)
124
+
125
+ if local_test:
126
+ # Output the grouping result
127
+ for category, tags_in_category in classified_tags.items():
128
+ print(f"{category}:")
129
+ print(", ".join(tags_in_category))
130
+ print()
131
+
132
+ if len(unclassified_tags) > 0:
133
+ print(f"\nUnclassified tags: {len(unclassified_tags)}")
134
+ print(f"{unclassified_tags[:200]}") # Display some unclassified tags
135
+
136
+ return classified_tags, unclassified_tags
137
+
138
+ # Code for "Tag Categorizer" tab
139
+ def process_tags(input_tags: str):
140
+ # Split tags using regex to handle both commas and question marks
141
+ tags = []
142
+ for tag in re.split(r'[\?.,\n]+', input_tags): # Fixed regex pattern
143
+ tag = tag.strip()
144
+ if tag:
145
+ tag = tag.replace('_', ' ')
146
+ if tag: # Only add if tag is not empty after processing
147
+ tags.append(tag)
148
+
149
+ # Classify the cleaned tags
150
+ classified_tags, unclassified_tags = classify_tags(tags)
151
+
152
+ # Create the outputs
153
+ categorized_string = ', '.join([tag for category in classified_tags.values() for tag in category]).replace("(", "\\(").replace(")", "\\)")
154
+ categorized_json = {category: tags for category, tags in classified_tags.items()}
155
+
156
+ return categorized_string, categorized_json
 
modules/florence2.py CHANGED
@@ -1,91 +1,91 @@
1
- import os,io,copy,subprocess,spaces,matplotlib.pyplot as plt,matplotlib.patches as patches,random,numpy as np
2
- from PIL import Image,ImageDraw,ImageFont
3
- from unittest.mock import patch
4
- from transformers.dynamic_module_utils import get_imports
5
- from transformers import AutoProcessor,AutoModelForCausalLM
6
- import gradio as gr
7
-
8
- def fixed_get_imports(filename:str|os.PathLike)->list[str]:
9
- if not str(filename).endswith('/modeling_florence2.py'):return get_imports(filename)
10
- imports=get_imports(filename)
11
- if'flash_attn'in imports:imports.remove('flash_attn')
12
- return imports
13
- @spaces.GPU
14
- def get_device_type():
15
- import torch
16
- if torch.cuda.is_available():return'cuda'
17
- elif torch.backends.mps.is_available()and torch.backends.mps.is_built():return'mps'
18
- else:return'cpu'
19
- device = get_device_type()
20
- if (device == "cuda"):
21
- subprocess.run('pip install flash-attn --no-build-isolation', env={'FLASH_ATTENTION_SKIP_CUDA_BUILD': "TRUE"}, shell=True)
22
- model = AutoModelForCausalLM.from_pretrained("MiaoshouAI/Florence-2-base-PromptGen-v2.0", trust_remote_code=True)
23
- processor = AutoProcessor.from_pretrained("MiaoshouAI/Florence-2-base-PromptGen-v2.0", trust_remote_code=True)
24
- model.to(device)
25
- else:
26
- with patch("transformers.dynamic_module_utils.get_imports", fixed_get_imports):
27
- model = AutoModelForCausalLM.from_pretrained("MiaoshouAI/Florence-2-base-PromptGen-v2.0", trust_remote_code=True)
28
- processor = AutoProcessor.from_pretrained("MiaoshouAI/Florence-2-base-PromptGen-v2.0", trust_remote_code=True)
29
- model.to(device)
30
-
31
- colormap=['blue','orange','green','purple','brown','pink','gray','olive','cyan','red','lime','indigo','violet','aqua','magenta','coral','gold','tan','skyblue']
32
-
33
- def fig_to_pil(fig):buf=io.BytesIO();fig.savefig(buf,format='png');buf.seek(0);return Image.open(buf)
34
- @spaces.GPU
35
- def run_example(task_prompt,image,text_input=None):
36
- if text_input is None:prompt=task_prompt
37
- else:prompt=task_prompt+text_input
38
- inputs=processor(text=prompt,images=image,return_tensors='pt').to(device);generated_ids=model.generate(input_ids=inputs['input_ids'],pixel_values=inputs['pixel_values'],max_new_tokens=1024,early_stopping=False,do_sample=False,num_beams=3);generated_text=processor.batch_decode(generated_ids,skip_special_tokens=False)[0];parsed_answer=processor.post_process_generation(generated_text,task=task_prompt,image_size=(image.width,image.height));return parsed_answer
39
- def plot_bbox(image,data):
40
- fig,ax=plt.subplots();ax.imshow(image)
41
- for(bbox,label)in zip(data['bboxes'],data['labels']):x1,y1,x2,y2=bbox;rect=patches.Rectangle((x1,y1),x2-x1,y2-y1,linewidth=1,edgecolor='r',facecolor='none');ax.add_patch(rect);plt.text(x1,y1,label,color='white',fontsize=8,bbox=dict(facecolor='red',alpha=.5))
42
- ax.axis('off');return fig
43
- def draw_polygons(image,prediction,fill_mask=False):
44
- draw=ImageDraw.Draw(image);scale=1
45
- for(polygons,label)in zip(prediction['polygons'],prediction['labels']):
46
- color=random.choice(colormap);fill_color=random.choice(colormap)if fill_mask else None
47
- for _polygon in polygons:
48
- _polygon=np.array(_polygon).reshape(-1,2)
49
- if len(_polygon)<3:print('Invalid polygon:',_polygon);continue
50
- _polygon=(_polygon*scale).reshape(-1).tolist()
51
- if fill_mask:draw.polygon(_polygon,outline=color,fill=fill_color)
52
- else:draw.polygon(_polygon,outline=color)
53
- draw.text((_polygon[0]+8,_polygon[1]+2),label,fill=color)
54
- return image
55
-
56
- def draw_ocr_bboxes(image,prediction):
57
- scale=1;draw=ImageDraw.Draw(image);bboxes,labels=prediction['quad_boxes'],prediction['labels']
58
- for(box,label)in zip(bboxes,labels):color=random.choice(colormap);new_box=(np.array(box)*scale).tolist();draw.polygon(new_box,width=3,outline=color);draw.text((new_box[0]+8,new_box[1]+2),'{}'.format(label),align='right',fill=color)
59
- return image
60
- def convert_to_od_format(data):bboxes=data.get('bboxes',[]);labels=data.get('bboxes_labels',[]);od_results={'bboxes':bboxes,'labels':labels};return od_results
61
-
62
- def process_image(image,task_prompt,text_input=None):
63
- if isinstance(image,str):image=Image.open(image)
64
- else:image=Image.fromarray(image)
65
- if task_prompt=='Caption':task_prompt='<CAPTION>';results=run_example(task_prompt,image);return results[task_prompt],None
66
- elif task_prompt=='Detailed Caption':task_prompt='<DETAILED_CAPTION>';results=run_example(task_prompt,image);return results[task_prompt],None
67
- elif task_prompt=='More Detailed Caption':task_prompt='<MORE_DETAILED_CAPTION>';results=run_example(task_prompt,image);return results,None
68
- elif task_prompt=='Caption + Grounding':task_prompt='<CAPTION>';results=run_example(task_prompt,image);text_input=results[task_prompt];task_prompt='<CAPTION_TO_PHRASE_GROUNDING>';results=run_example(task_prompt,image,text_input);results['<CAPTION>']=text_input;fig=plot_bbox(image,results['<CAPTION_TO_PHRASE_GROUNDING>']);return results,fig_to_pil(fig)
69
- elif task_prompt=='Detailed Caption + Grounding':task_prompt='<DETAILED_CAPTION>';results=run_example(task_prompt,image);text_input=results[task_prompt];task_prompt='<CAPTION_TO_PHRASE_GROUNDING>';results=run_example(task_prompt,image,text_input);results['<DETAILED_CAPTION>']=text_input;fig=plot_bbox(image,results['<CAPTION_TO_PHRASE_GROUNDING>']);return results,fig_to_pil(fig)
70
- elif task_prompt=='More Detailed Caption + Grounding':task_prompt='<MORE_DETAILED_CAPTION>';results=run_example(task_prompt,image);text_input=results[task_prompt];task_prompt='<CAPTION_TO_PHRASE_GROUNDING>';results=run_example(task_prompt,image,text_input);results['<MORE_DETAILED_CAPTION>']=text_input;fig=plot_bbox(image,results['<CAPTION_TO_PHRASE_GROUNDING>']);return results,fig_to_pil(fig)
71
- elif task_prompt=='Object Detection':task_prompt='<OD>';results=run_example(task_prompt,image);fig=plot_bbox(image,results['<OD>']);return results,fig_to_pil(fig)
72
- elif task_prompt=='Dense Region Caption':task_prompt='<DENSE_REGION_CAPTION>';results=run_example(task_prompt,image);fig=plot_bbox(image,results['<DENSE_REGION_CAPTION>']);return results,fig_to_pil(fig)
73
- elif task_prompt=='Region Proposal':task_prompt='<REGION_PROPOSAL>';results=run_example(task_prompt,image);fig=plot_bbox(image,results['<REGION_PROPOSAL>']);return results,fig_to_pil(fig)
74
- elif task_prompt=='Caption to Phrase Grounding':task_prompt='<CAPTION_TO_PHRASE_GROUNDING>';results=run_example(task_prompt,image,text_input);fig=plot_bbox(image,results['<CAPTION_TO_PHRASE_GROUNDING>']);return results,fig_to_pil(fig)
75
- elif task_prompt=='Referring Expression Segmentation':task_prompt='<REFERRING_EXPRESSION_SEGMENTATION>';results=run_example(task_prompt,image,text_input);output_image=copy.deepcopy(image);output_image=draw_polygons(output_image,results['<REFERRING_EXPRESSION_SEGMENTATION>'],fill_mask=True);return results,output_image
76
- elif task_prompt=='Region to Segmentation':task_prompt='<REGION_TO_SEGMENTATION>';results=run_example(task_prompt,image,text_input);output_image=copy.deepcopy(image);output_image=draw_polygons(output_image,results['<REGION_TO_SEGMENTATION>'],fill_mask=True);return results,output_image
77
- elif task_prompt=='Open Vocabulary Detection':task_prompt='<OPEN_VOCABULARY_DETECTION>';results=run_example(task_prompt,image,text_input);bbox_results=convert_to_od_format(results['<OPEN_VOCABULARY_DETECTION>']);fig=plot_bbox(image,bbox_results);return results,fig_to_pil(fig)
78
- elif task_prompt=='Region to Category':task_prompt='<REGION_TO_CATEGORY>';results=run_example(task_prompt,image,text_input);return results,None
79
- elif task_prompt=='Region to Description':task_prompt='<REGION_TO_DESCRIPTION>';results=run_example(task_prompt,image,text_input);return results,None
80
- elif task_prompt=='OCR':task_prompt='<OCR>';results=run_example(task_prompt,image);return results,None
81
- elif task_prompt=='OCR with Region':task_prompt='<OCR_WITH_REGION>';results=run_example(task_prompt,image);output_image=copy.deepcopy(image);output_image=draw_ocr_bboxes(output_image,results['<OCR_WITH_REGION>']);return results,output_image
82
- else:return'',None # Return empty string and None for unknown task prompts
83
-
84
- single_task_list=['Caption','Detailed Caption','More Detailed Caption','Object Detection','Dense Region Caption','Region Proposal','Caption to Phrase Grounding','Referring Expression Segmentation','Region to Segmentation','Open Vocabulary Detection','Region to Category','Region to Description','OCR','OCR with Region']
85
- cascaded_task_list=['Caption + Grounding','Detailed Caption + Grounding','More Detailed Caption + Grounding']
86
-
87
- def update_task_dropdown(choice):
88
- if choice == 'Cascaded task':
89
- return gr.Dropdown(choices=cascaded_task_list, value='Caption + Grounding')
90
- else:
91
  return gr.Dropdown(choices=single_task_list, value='Caption')
 
1
+ import os,io,copy,subprocess,spaces,matplotlib.pyplot as plt,matplotlib.patches as patches,random,numpy as np
2
+ from PIL import Image,ImageDraw,ImageFont
3
+ from unittest.mock import patch
4
+ from transformers.dynamic_module_utils import get_imports
5
+ from transformers import AutoProcessor,AutoModelForCausalLM
6
+ import gradio as gr
7
+
8
+ def fixed_get_imports(filename:str|os.PathLike)->list[str]:
9
+ if not str(filename).endswith('/modeling_florence2.py'):return get_imports(filename)
10
+ imports=get_imports(filename)
11
+ if'flash_attn'in imports:imports.remove('flash_attn')
12
+ return imports
13
+ @spaces.GPU
14
+ def get_device_type():
15
+ import torch
16
+ if torch.cuda.is_available():return'cuda'
17
+ elif torch.backends.mps.is_available()and torch.backends.mps.is_built():return'mps'
18
+ else:return'cpu'
19
+ device = get_device_type()
20
+ if (device == "cuda"):
21
+ subprocess.run('pip install flash-attn --no-build-isolation', env={'FLASH_ATTENTION_SKIP_CUDA_BUILD': "TRUE"}, shell=True)
22
+ model = AutoModelForCausalLM.from_pretrained("MiaoshouAI/Florence-2-base-PromptGen-v2.0", trust_remote_code=True)
23
+ processor = AutoProcessor.from_pretrained("MiaoshouAI/Florence-2-base-PromptGen-v2.0", trust_remote_code=True)
24
+ model.to(device)
25
+ else:
26
+ with patch("transformers.dynamic_module_utils.get_imports", fixed_get_imports):
27
+ model = AutoModelForCausalLM.from_pretrained("MiaoshouAI/Florence-2-base-PromptGen-v2.0", trust_remote_code=True)
28
+ processor = AutoProcessor.from_pretrained("MiaoshouAI/Florence-2-base-PromptGen-v2.0", trust_remote_code=True)
29
+ model.to(device)
30
+
31
+ colormap=['blue','orange','green','purple','brown','pink','gray','olive','cyan','red','lime','indigo','violet','aqua','magenta','coral','gold','tan','skyblue']
32
+
33
+ def fig_to_pil(fig):buf=io.BytesIO();fig.savefig(buf,format='png');buf.seek(0);return Image.open(buf)
34
+ @spaces.GPU
35
+ def run_example(task_prompt,image,text_input=None):
36
+ if text_input is None:prompt=task_prompt
37
+ else:prompt=task_prompt+text_input
38
+ inputs=processor(text=prompt,images=image,return_tensors='pt').to(device);generated_ids=model.generate(input_ids=inputs['input_ids'],pixel_values=inputs['pixel_values'],max_new_tokens=1024,early_stopping=False,do_sample=False,num_beams=3);generated_text=processor.batch_decode(generated_ids,skip_special_tokens=False)[0];parsed_answer=processor.post_process_generation(generated_text,task=task_prompt,image_size=(image.width,image.height));return parsed_answer
39
+ def plot_bbox(image,data):
40
+ fig,ax=plt.subplots();ax.imshow(image)
41
+ for(bbox,label)in zip(data['bboxes'],data['labels']):x1,y1,x2,y2=bbox;rect=patches.Rectangle((x1,y1),x2-x1,y2-y1,linewidth=1,edgecolor='r',facecolor='none');ax.add_patch(rect);plt.text(x1,y1,label,color='white',fontsize=8,bbox=dict(facecolor='red',alpha=.5))
42
+ ax.axis('off');return fig
43
+ def draw_polygons(image,prediction,fill_mask=False):
44
+ draw=ImageDraw.Draw(image);scale=1
45
+ for(polygons,label)in zip(prediction['polygons'],prediction['labels']):
46
+ color=random.choice(colormap);fill_color=random.choice(colormap)if fill_mask else None
47
+ for _polygon in polygons:
48
+ _polygon=np.array(_polygon).reshape(-1,2)
49
+ if len(_polygon)<3:print('Invalid polygon:',_polygon);continue
50
+ _polygon=(_polygon*scale).reshape(-1).tolist()
51
+ if fill_mask:draw.polygon(_polygon,outline=color,fill=fill_color)
52
+ else:draw.polygon(_polygon,outline=color)
53
+ draw.text((_polygon[0]+8,_polygon[1]+2),label,fill=color)
54
+ return image
55
+
56
+ def draw_ocr_bboxes(image,prediction):
57
+ scale=1;draw=ImageDraw.Draw(image);bboxes,labels=prediction['quad_boxes'],prediction['labels']
58
+ for(box,label)in zip(bboxes,labels):color=random.choice(colormap);new_box=(np.array(box)*scale).tolist();draw.polygon(new_box,width=3,outline=color);draw.text((new_box[0]+8,new_box[1]+2),'{}'.format(label),align='right',fill=color)
59
+ return image
60
+ def convert_to_od_format(data):bboxes=data.get('bboxes',[]);labels=data.get('bboxes_labels',[]);od_results={'bboxes':bboxes,'labels':labels};return od_results
61
+
62
+ def process_image(image,task_prompt,text_input=None):
63
+ if isinstance(image,str):image=Image.open(image)
64
+ else:image=Image.fromarray(image)
65
+ if task_prompt=='Caption':task_prompt='<CAPTION>';results=run_example(task_prompt,image);return results[task_prompt],None
66
+ elif task_prompt=='Detailed Caption':task_prompt='<DETAILED_CAPTION>';results=run_example(task_prompt,image);return results[task_prompt],None
67
+ elif task_prompt=='More Detailed Caption':task_prompt='<MORE_DETAILED_CAPTION>';results=run_example(task_prompt,image);return results,None
68
+ elif task_prompt=='Caption + Grounding':task_prompt='<CAPTION>';results=run_example(task_prompt,image);text_input=results[task_prompt];task_prompt='<CAPTION_TO_PHRASE_GROUNDING>';results=run_example(task_prompt,image,text_input);results['<CAPTION>']=text_input;fig=plot_bbox(image,results['<CAPTION_TO_PHRASE_GROUNDING>']);return results,fig_to_pil(fig)
69
+ elif task_prompt=='Detailed Caption + Grounding':task_prompt='<DETAILED_CAPTION>';results=run_example(task_prompt,image);text_input=results[task_prompt];task_prompt='<CAPTION_TO_PHRASE_GROUNDING>';results=run_example(task_prompt,image,text_input);results['<DETAILED_CAPTION>']=text_input;fig=plot_bbox(image,results['<CAPTION_TO_PHRASE_GROUNDING>']);return results,fig_to_pil(fig)
70
+ elif task_prompt=='More Detailed Caption + Grounding':task_prompt='<MORE_DETAILED_CAPTION>';results=run_example(task_prompt,image);text_input=results[task_prompt];task_prompt='<CAPTION_TO_PHRASE_GROUNDING>';results=run_example(task_prompt,image,text_input);results['<MORE_DETAILED_CAPTION>']=text_input;fig=plot_bbox(image,results['<CAPTION_TO_PHRASE_GROUNDING>']);return results,fig_to_pil(fig)
71
+ elif task_prompt=='Object Detection':task_prompt='<OD>';results=run_example(task_prompt,image);fig=plot_bbox(image,results['<OD>']);return results,fig_to_pil(fig)
72
+ elif task_prompt=='Dense Region Caption':task_prompt='<DENSE_REGION_CAPTION>';results=run_example(task_prompt,image);fig=plot_bbox(image,results['<DENSE_REGION_CAPTION>']);return results,fig_to_pil(fig)
73
+ elif task_prompt=='Region Proposal':task_prompt='<REGION_PROPOSAL>';results=run_example(task_prompt,image);fig=plot_bbox(image,results['<REGION_PROPOSAL>']);return results,fig_to_pil(fig)
74
+ elif task_prompt=='Caption to Phrase Grounding':task_prompt='<CAPTION_TO_PHRASE_GROUNDING>';results=run_example(task_prompt,image,text_input);fig=plot_bbox(image,results['<CAPTION_TO_PHRASE_GROUNDING>']);return results,fig_to_pil(fig)
75
+ elif task_prompt=='Referring Expression Segmentation':task_prompt='<REFERRING_EXPRESSION_SEGMENTATION>';results=run_example(task_prompt,image,text_input);output_image=copy.deepcopy(image);output_image=draw_polygons(output_image,results['<REFERRING_EXPRESSION_SEGMENTATION>'],fill_mask=True);return results,output_image
76
+ elif task_prompt=='Region to Segmentation':task_prompt='<REGION_TO_SEGMENTATION>';results=run_example(task_prompt,image,text_input);output_image=copy.deepcopy(image);output_image=draw_polygons(output_image,results['<REGION_TO_SEGMENTATION>'],fill_mask=True);return results,output_image
77
+ elif task_prompt=='Open Vocabulary Detection':task_prompt='<OPEN_VOCABULARY_DETECTION>';results=run_example(task_prompt,image,text_input);bbox_results=convert_to_od_format(results['<OPEN_VOCABULARY_DETECTION>']);fig=plot_bbox(image,bbox_results);return results,fig_to_pil(fig)
78
+ elif task_prompt=='Region to Category':task_prompt='<REGION_TO_CATEGORY>';results=run_example(task_prompt,image,text_input);return results,None
79
+ elif task_prompt=='Region to Description':task_prompt='<REGION_TO_DESCRIPTION>';results=run_example(task_prompt,image,text_input);return results,None
80
+ elif task_prompt=='OCR':task_prompt='<OCR>';results=run_example(task_prompt,image);return results,None
81
+ elif task_prompt=='OCR with Region':task_prompt='<OCR_WITH_REGION>';results=run_example(task_prompt,image);output_image=copy.deepcopy(image);output_image=draw_ocr_bboxes(output_image,results['<OCR_WITH_REGION>']);return results,output_image
82
+ else:return'',None # Return empty string and None for unknown task prompts
83
+
84
+ single_task_list=['Caption','Detailed Caption','More Detailed Caption','Object Detection','Dense Region Caption','Region Proposal','Caption to Phrase Grounding','Referring Expression Segmentation','Region to Segmentation','Open Vocabulary Detection','Region to Category','Region to Description','OCR','OCR with Region']
85
+ cascaded_task_list=['Caption + Grounding','Detailed Caption + Grounding','More Detailed Caption + Grounding']
86
+
87
+ def update_task_dropdown(choice):
88
+ if choice == 'Cascaded task':
89
+ return gr.Dropdown(choices=cascaded_task_list, value='Caption + Grounding')
90
+ else:
91
  return gr.Dropdown(choices=single_task_list, value='Caption')
modules/tag_enhancer.py CHANGED
@@ -2,7 +2,7 @@ import gradio as gr
2
  import re,torch
3
  from transformers import pipeline,AutoTokenizer,AutoModelForSeq2SeqLM
4
 
5
- device = "cuda" if torch.cuda.is_available() else "cpu"
6
 
7
  def load_models():
8
  try:
@@ -16,38 +16,37 @@ def load_models():
16
  print(e)
17
  enhancer_medium = enhancer_long = enhancer_flux = None
18
  return enhancer_medium, enhancer_long, enhancer_flux
19
-
20
  enhancer_medium, enhancer_long, enhancer_flux = load_models()
21
 
22
- def enhance_prompt(input_prompt, model_choice):
23
  if model_choice == "Medium":
24
  result = enhancer_medium("Enhance the description: " + input_prompt)
25
- enhanced_text = result[0]['summary_text']
26
 
27
  pattern = r'^.*?of\s+(.*?(?:\.|$))'
28
- match = re.match(pattern, enhanced_text, re.IGNORECASE | re.DOTALL)
29
 
30
  if match:
31
- remaining_text = enhanced_text[match.end():].strip()
32
  modified_sentence = match.group(1).capitalize()
33
- enhanced_text = modified_sentence + ' ' + remaining_text
34
  elif model_choice == "Flux":
35
- result = enhancer_flux("enhance prompt: " + input_prompt, max_length=256)
36
- enhanced_text = result[0]['generated_text']
37
  else: # Long
38
  result = enhancer_long("Enhance the description: " + input_prompt)
39
- enhanced_text = result[0]['summary_text']
40
 
41
- return enhanced_text
42
 
43
- def prompt_enhancer(character: str, series: str, general: str, model_choice: str):
44
  characters = character.split(",") if character else []
45
  serieses = series.split(",") if series else []
46
  generals = general.split(",") if general else []
47
  tags = characters + serieses + generals
48
  cprompt = ",".join(tags) if tags else ""
49
 
50
- output = enhance_prompt(cprompt, model_choice)
51
  prompt = cprompt + ", " + output
52
 
53
  return prompt, gr.update(interactive=True), gr.update(interactive=True)
 
2
  import re,torch
3
  from transformers import pipeline,AutoTokenizer,AutoModelForSeq2SeqLM
4
 
5
+ device = "cpu" if torch.cuda.is_available() else "cpu" # Switched to CPU since we are using HF with no GPU
6
 
7
  def load_models():
8
  try:
 
16
  print(e)
17
  enhancer_medium = enhancer_long = enhancer_flux = None
18
  return enhancer_medium, enhancer_long, enhancer_flux
 
19
  enhancer_medium, enhancer_long, enhancer_flux = load_models()
20
 
21
+ def summarize_prompt(input_prompt, model_choice):
22
  if model_choice == "Medium":
23
  result = enhancer_medium("Enhance the description: " + input_prompt)
24
+ summarized_text = result[0]['summary_text']
25
 
26
  pattern = r'^.*?of\s+(.*?(?:\.|$))'
27
+ match = re.match(pattern, summarized_text, re.IGNORECASE | re.DOTALL)
28
 
29
  if match:
30
+ remaining_text = summarized_text[match.end():].strip()
31
  modified_sentence = match.group(1).capitalize()
32
+ summarized_text = modified_sentence + ' ' + remaining_text
33
  elif model_choice == "Flux":
34
+ result = enhancer_flux("Enhance prompt: " + input_prompt, max_length=256)
35
+ summarized_text = result[0]['generated_text']
36
  else: # Long
37
  result = enhancer_long("Enhance the description: " + input_prompt)
38
+ summarized_text = result[0]['summary_text']
39
 
40
+ return summarized_text
41
 
42
+ def prompt_summarizer(character: str, series: str, general: str, model_choice: str):
43
  characters = character.split(",") if character else []
44
  serieses = series.split(",") if series else []
45
  generals = general.split(",") if general else []
46
  tags = characters + serieses + generals
47
  cprompt = ",".join(tags) if tags else ""
48
 
49
+ output = summarize_prompt(cprompt, model_choice)
50
  prompt = cprompt + ", " + output
51
 
52
  return prompt, gr.update(interactive=True), gr.update(interactive=True)
pre-requirements.txt CHANGED
@@ -1 +1,6 @@
1
- pip>=23.0.0
 
 
 
 
 
 
1
+ pip>=23.0.0
2
+ transformers==4.51.0
3
+ huggingface-hub
4
+ apscheduler
5
+ spaces
6
+ pandas==2.1.2
requirements.txt CHANGED
@@ -1,25 +1,21 @@
1
- huggingface-hub
2
- apscheduler
3
-
4
- --extra-index-url https://download.pytorch.org/whl/cu124
5
- torch==2.5.0+cu124; sys_platform != 'darwin'
6
- torchvision==0.20.0+cu124; sys_platform != 'darwin'
7
- torch==2.5.0; sys_platform == 'darwin'
8
- torchvision==0.20.0; sys_platform == 'darwin'
9
-
10
- gradio
11
- transformers==4.51.0
12
- safetensors
13
- sentencepiece
14
- spaces
15
- pillow
16
- requests
17
- numpy
18
- timm
19
- einops
20
- optimum
21
- accelerate
22
- opencv-python
23
- onnxruntime>=1.12.0
24
- pandas==2.1.2
25
- matplotlib
 
1
+ --extra-index-url https://download.pytorch.org/whl/cu124
2
+ torch==2.5.0+cu124; sys_platform != 'darwin'
3
+ torchvision==0.20.0+cu124; sys_platform != 'darwin'
4
+ torch==2.5.0; sys_platform == 'darwin'
5
+ torchvision==0.20.0; sys_platform == 'darwin'
6
+
7
+ gradio
8
+ safetensors
9
+ sentencepiece
10
+ pillow
11
+ requests
12
+ numpy
13
+ timm
14
+ einops
15
+ optimum
16
+ accelerate
17
+ opencv-python
18
+ onnxruntime>=1.12.0
19
+ matplotlib
20
+
21
+ https://github.com/kingbri1/flash-attention/releases/download/v2.7.1.post1/flash_attn-2.7.1.post1+cu124torch2.5.1cxx11abiFALSE-cp310-cp310-win_amd64.whl
 
 
 
 
themes/theme_schema@0.0.1.json CHANGED
@@ -1 +1 @@
1
- {"theme": {"_font": [{"__gradio_font__": true, "name": "Montserrat", "class": "google"}, {"__gradio_font__": true, "name": "ui-sans-serif", "class": "font"}, {"__gradio_font__": true, "name": "system-ui", "class": "font"}, {"__gradio_font__": true, "name": "sans-serif", "class": "font"}], "_font_mono": [{"__gradio_font__": true, "name": "IBM Plex Mono", "class": "google"}, {"__gradio_font__": true, "name": "ui-monospace", "class": "font"}, {"__gradio_font__": true, "name": "Consolas", "class": "font"}, {"__gradio_font__": true, "name": "monospace", "class": "font"}], "_stylesheets": ["https://fonts.googleapis.com/css2?family=Montserrat:wght@400;600&display=swap", "https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;600&display=swap"], "background_fill_primary": "white", "background_fill_primary_dark": "*neutral_950", "background_fill_secondary": "*neutral_50", "background_fill_secondary_dark": "*neutral_900", "block_background_fill": "white", "block_background_fill_dark": "*neutral_800", "block_border_color": "*border_color_primary", "block_border_color_dark": "*border_color_primary", "block_border_width": "0px", "block_info_text_color": "*body_text_color_subdued", "block_info_text_color_dark": "*body_text_color_subdued", "block_info_text_size": "*text_sm", "block_info_text_weight": "400", "block_label_background_fill": "*primary_100", "block_label_background_fill_dark": "*primary_600", "block_label_border_color": "*border_color_primary", "block_label_border_color_dark": "*border_color_primary", "block_label_border_width": "1px", "block_label_margin": "*spacing_md", "block_label_padding": "*spacing_sm *spacing_md", "block_label_radius": "*radius_md", "block_label_right_radius": "0 calc(*radius_lg - 1px) 0 calc(*radius_lg - 1px)", "block_label_text_color": "*primary_500", "block_label_text_color_dark": "*white", "block_label_text_size": "*text_md", "block_label_text_weight": "600", "block_padding": "*spacing_xl calc(*spacing_xl + 2px)", "block_radius": "*radius_lg", "block_shadow": "none", "block_title_background_fill": "*primary_100", "block_title_border_color": "none", "block_title_border_width": "0px", "block_title_padding": "*block_label_padding", "block_title_radius": "*block_label_radius", "block_title_text_color": "*primary_500", "block_title_text_color_dark": "*white", "block_title_text_size": "*text_md", "block_title_text_weight": "600", "body_background_fill": "*background_fill_primary", "body_background_fill_dark": "*background_fill_primary", "body_text_color": "*neutral_800", "body_text_color_dark": "*neutral_100", "body_text_color_subdued": "*neutral_400", "body_text_color_subdued_dark": "*neutral_400", "body_text_size": "*text_md", "body_text_weight": "400", "border_color_accent": "*primary_300", "border_color_accent_dark": "*neutral_600", "border_color_primary": "*neutral_200", "border_color_primary_dark": "*neutral_700", "button_border_width": "*input_border_width", "button_border_width_dark": "*input_border_width", "button_cancel_background_fill": "*button_secondary_background_fill", "button_cancel_background_fill_dark": "*button_secondary_background_fill", "button_cancel_background_fill_hover": "*button_secondary_background_fill_hover", "button_cancel_background_fill_hover_dark": "*button_secondary_background_fill_hover", "button_cancel_border_color": "*button_secondary_border_color", "button_cancel_border_color_dark": "*button_secondary_border_color", "button_cancel_border_color_hover": "*button_cancel_border_color", "button_cancel_border_color_hover_dark": "*button_cancel_border_color", "button_cancel_text_color": "*button_secondary_text_color", "button_cancel_text_color_dark": "*button_secondary_text_color", "button_cancel_text_color_hover": "*button_cancel_text_color", "button_cancel_text_color_hover_dark": "*button_cancel_text_color", "button_large_padding": "*spacing_lg calc(2 * *spacing_lg)", "button_large_radius": "*radius_lg", "button_large_text_size": "*text_lg", "button_large_text_weight": "600", "button_primary_background_fill": "#06AE56", "button_primary_background_fill_dark": "#06AE56", "button_primary_background_fill_hover": "#07C863", "button_primary_background_fill_hover_dark": "*primary_500", "button_primary_border_color": "#06AE56", "button_primary_border_color_dark": "#06AE56", "button_primary_border_color_hover": "*button_primary_border_color", "button_primary_border_color_hover_dark": "*button_primary_border_color", "button_primary_text_color": "#FFFFFF", "button_primary_text_color_dark": "#FFFFFF", "button_primary_text_color_hover": "*button_primary_text_color", "button_primary_text_color_hover_dark": "*button_primary_text_color", "button_secondary_background_fill": "#F2F2F2", "button_secondary_background_fill_dark": "#2B2B2B", "button_secondary_background_fill_hover": "*neutral_100", "button_secondary_background_fill_hover_dark": "*primary_500", "button_secondary_border_color": "*neutral_200", "button_secondary_border_color_dark": "*neutral_600", "button_secondary_border_color_hover": "*button_secondary_border_color", "button_secondary_border_color_hover_dark": "*button_secondary_border_color", "button_secondary_text_color": "#393939", "button_secondary_text_color_dark": "#FFFFFF", "button_secondary_text_color_hover": "*button_secondary_text_color", "button_secondary_text_color_hover_dark": "*button_secondary_text_color", "button_shadow": "*shadow_drop_lg", "button_shadow_active": "*shadow_inset", "button_shadow_hover": "*shadow_drop_lg", "button_small_padding": "*spacing_sm calc(2 * *spacing_sm)", "button_small_radius": "*radius_lg", "button_small_text_size": "*text_md", "button_small_text_weight": "400", "button_transition": "background-color 0.2s ease", "checkbox_background_color": "*background_fill_primary", "checkbox_background_color_dark": "*neutral_800", "checkbox_background_color_focus": "*checkbox_background_color", "checkbox_background_color_focus_dark": "*checkbox_background_color", "checkbox_background_color_hover": "*checkbox_background_color", "checkbox_background_color_hover_dark": "*checkbox_background_color", "checkbox_background_color_selected": "*primary_600", "checkbox_background_color_selected_dark": "*primary_700", "checkbox_border_color": "*neutral_100", "checkbox_border_color_dark": "*neutral_600", "checkbox_border_color_focus": "*primary_500", "checkbox_border_color_focus_dark": "*primary_600", "checkbox_border_color_hover": "*neutral_300", "checkbox_border_color_hover_dark": "*neutral_600", "checkbox_border_color_selected": "*primary_600", "checkbox_border_color_selected_dark": "*primary_700", "checkbox_border_radius": "*radius_sm", "checkbox_border_width": "1px", "checkbox_border_width_dark": "*input_border_width", "checkbox_check": "url(\"data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M12.207 4.793a1 1 0 010 1.414l-5 5a1 1 0 01-1.414 0l-2-2a1 1 0 011.414-1.414L6.5 9.086l4.293-4.293a1 1 0 011.414 0z'/%3e%3c/svg%3e\")", "checkbox_label_background_fill": "*button_secondary_background_fill", "checkbox_label_background_fill_dark": "*button_secondary_background_fill", "checkbox_label_background_fill_hover": "*button_secondary_background_fill_hover", "checkbox_label_background_fill_hover_dark": "*button_secondary_background_fill_hover", "checkbox_label_background_fill_selected": "*primary_500", "checkbox_label_background_fill_selected_dark": "*primary_600", "checkbox_label_border_color": "*border_color_primary", "checkbox_label_border_color_dark": "*border_color_primary", "checkbox_label_border_color_hover": "*checkbox_label_border_color", "checkbox_label_border_color_hover_dark": "*checkbox_label_border_color", "checkbox_label_border_width": "*input_border_width", "checkbox_label_border_width_dark": "*input_border_width", "checkbox_label_gap": "*spacing_lg", "checkbox_label_padding": "*spacing_md calc(2 * *spacing_md)", "checkbox_label_shadow": "*shadow_drop_lg", "checkbox_label_text_color": "*body_text_color", "checkbox_label_text_color_dark": "*body_text_color", "checkbox_label_text_color_selected": "white", "checkbox_label_text_color_selected_dark": "*checkbox_label_text_color", "checkbox_label_text_size": "*text_md", "checkbox_label_text_weight": "400", "checkbox_shadow": "none", "color_accent": "*primary_500", "color_accent_soft": "*primary_50", "color_accent_soft_dark": "*neutral_700", "container_radius": "*radius_lg", "embed_radius": "*radius_lg", "error_background_fill": "#fee2e2", "error_background_fill_dark": "*background_fill_primary", "error_border_color": "#fecaca", "error_border_color_dark": "*border_color_primary", "error_border_width": "1px", "error_text_color": "#ef4444", "error_text_color_dark": "#ef4444", "font": "'Montserrat', 'ui-sans-serif', 'system-ui', sans-serif", "font_mono": "'IBM Plex Mono', 'ui-monospace', 'Consolas', monospace", "form_gap_width": "0px", "input_background_fill": "#F6F6F6", "input_background_fill_dark": "*neutral_700", "input_background_fill_focus": "*secondary_500", "input_background_fill_focus_dark": "*secondary_600", "input_background_fill_hover": "*input_background_fill", "input_background_fill_hover_dark": "*input_background_fill", "input_border_color": "*neutral_50", "input_border_color_dark": "*border_color_primary", "input_border_color_focus": "*secondary_300", "input_border_color_focus_dark": "*neutral_700", "input_border_color_hover": "*input_border_color", "input_border_color_hover_dark": "*input_border_color", "input_border_width": "0px", "input_padding": "*spacing_xl", "input_placeholder_color": "*neutral_400", "input_placeholder_color_dark": "*neutral_500", "input_radius": "*radius_lg", "input_shadow": "*shadow_drop", "input_shadow_focus": "*shadow_drop_lg", "input_text_size": "*text_md", "input_text_weight": "400", "layout_gap": "*spacing_xxl", "link_text_color": "*secondary_600", "link_text_color_active": "*secondary_600", "link_text_color_active_dark": "*secondary_500", "link_text_color_dark": "*secondary_500", "link_text_color_hover": "*secondary_700", "link_text_color_hover_dark": "*secondary_400", "link_text_color_visited": "*secondary_500", "link_text_color_visited_dark": "*secondary_600", "loader_color": "*color_accent", "neutral_100": "#f3f4f6", "neutral_200": "#e5e7eb", "neutral_300": "#d1d5db", "neutral_400": "#B2B2B2", "neutral_50": "#f9fafb", "neutral_500": "#808080", "neutral_600": "#636363", "neutral_700": "#515151", "neutral_800": "#393939", "neutral_900": "#272727", "neutral_950": "#171717", "panel_background_fill": "*background_fill_secondary", "panel_background_fill_dark": "*background_fill_secondary", "panel_border_color": "*border_color_primary", "panel_border_color_dark": "*border_color_primary", "panel_border_width": "1px", "primary_100": "rgba(2, 193, 96, 0.2)", "primary_200": "#02C160", "primary_300": "rgba(2, 193, 96, 0.32)", "primary_400": "rgba(2, 193, 96, 0.32)", "primary_50": "#02C160", "primary_500": "rgba(2, 193, 96, 1.0)", "primary_600": "rgba(2, 193, 96, 1.0)", "primary_700": "rgba(2, 193, 96, 0.32)", "primary_800": "rgba(2, 193, 96, 0.32)", "primary_900": "#02C160", "primary_950": "#02C160", "prose_header_text_weight": "600", "prose_text_size": "*text_md", "prose_text_weight": "400", "radio_circle": "url(\"data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3ccircle cx='8' cy='8' r='3'/%3e%3c/svg%3e\")", "radius_lg": "6px", "radius_md": "4px", "radius_sm": "2px", "radius_xl": "8px", "radius_xs": "1px", "radius_xxl": "12px", "radius_xxs": "1px", "secondary_100": "#576b95", "secondary_200": "#576b95", "secondary_300": "#576b95", "secondary_400": "#576b95", "secondary_50": "#576b95", "secondary_500": "#576b95", "secondary_600": "#576b95", "secondary_700": "#576b95", "secondary_800": "#576b95", "secondary_900": "#576b95", "secondary_950": "#576b95", "section_header_text_size": "*text_md", "section_header_text_weight": "400", "shadow_drop": "0 1px 4px 0 rgb(0 0 0 / 0.1)", "shadow_drop_lg": "0 2px 5px 0 rgb(0 0 0 / 0.1)", "shadow_inset": "rgba(0,0,0,0.05) 0px 2px 4px 0px inset", "shadow_spread": "6px", "shadow_spread_dark": "1px", "slider_color": "*primary_500", "slider_color_dark": "*primary_600", "spacing_lg": "8px", "spacing_md": "6px", "spacing_sm": "4px", "spacing_xl": "10px", "spacing_xs": "2px", "spacing_xxl": "16px", "spacing_xxs": "1px", "stat_background_fill": "*primary_300", "stat_background_fill_dark": "*primary_500", "table_border_color": "*neutral_300", "table_border_color_dark": "*neutral_700", "table_even_background_fill": "white", "table_even_background_fill_dark": "*neutral_950", "table_odd_background_fill": "*neutral_50", "table_odd_background_fill_dark": "*neutral_900", "table_radius": "*radius_lg", "table_row_focus": "*color_accent_soft", "table_row_focus_dark": "*color_accent_soft", "text_lg": "16px", "text_md": "14px", "text_sm": "12px", "text_xl": "22px", "text_xs": "10px", "text_xxl": "26px", "text_xxs": "9px"}, "version": "1.0.0"}
 
1
+ {"theme": {"_font": [{"__gradio_font__": true, "name": "BIZ UDMincho Medium", "class": "font"}, {"__gradio_font__": true, "name": "BIZ UDMincho Medium", "class": "font"}, {"__gradio_font__": true, "name": "system-ui", "class": "font"}, {"__gradio_font__": true, "name": "sans-serif", "class": "font"}], "_font_mono": [{"__gradio_font__": true, "name": "BIZ UDMincho Medium", "class": "font"}, {"__gradio_font__": true, "name": "ui-monospace", "class": "font"}, {"__gradio_font__": true, "name": "BIZ UDMincho Medium", "class": "font"}, {"__gradio_font__": true, "name": "monospace", "class": "font"}], "_stylesheets": ["https://fonts.googleapis.com/css2?family=Source+Sans+Pro:wght@400;600&display=swap", "https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;600&display=swap"], "background_fill_primary": "#bebebef2", "background_fill_primary_dark": "*neutral_950", "background_fill_secondary": "*neutral_50", "background_fill_secondary_dark": "#160d0dad", "block_background_fill": "#e3e3e3", "block_background_fill_dark": "#160d0dad", "block_border_color": "#ffffff", "block_border_color_dark": "*border_color_primary", "block_border_width": "1px", "block_border_width_dark": "1px", "block_info_text_color": "*body_text_color_subdued", "block_info_text_color_dark": "*body_text_color_subdued", "block_info_text_size": "*text_sm", "block_info_text_weight": "400", "block_label_background_fill": "#160d0d00", "block_label_background_fill_dark": "#160d0d00", "block_label_border_color": "*border_color_primary", "block_label_border_color_dark": "*border_color_primary", "block_label_border_width": "1px", "block_label_border_width_dark": "1px", "block_label_margin": "0", "block_label_padding": "*spacing_sm *spacing_lg", "block_label_radius": "calc(*radius_lg - 1px) 0 calc(*radius_lg - 1px) 0", "block_label_right_radius": "0 calc(*radius_lg - 1px) 0 calc(*radius_lg - 1px)", "block_label_text_color": "*neutral_500", "block_label_text_color_dark": "*neutral_200", "block_label_text_size": "*text_sm", "block_label_text_weight": "400", "block_padding": "*spacing_xl calc(*spacing_xl + 2px)", "block_radius": "*radius_lg", "block_shadow": "none", "block_shadow_dark": "none", "block_title_background_fill": "none", "block_title_background_fill_dark": "none", "block_title_border_color": "none", "block_title_border_color_dark": "none", "block_title_border_width": "0px", "block_title_border_width_dark": "0px", "block_title_padding": "0", "block_title_radius": "none", "block_title_text_color": "*neutral_500", "block_title_text_color_dark": "*neutral_200", "block_title_text_size": "*text_md", "block_title_text_weight": "400", "body_background_fill": "white", "body_background_fill_dark": "#391633", "body_text_color": "#000000", "body_text_color_dark": "*neutral_100", "body_text_color_subdued": "#000000", "body_text_color_subdued_dark": "*neutral_400", "body_text_size": "*text_md", "body_text_weight": "400", "border_color_accent": "*primary_300", "border_color_accent_dark": "*neutral_600", "border_color_primary": "#ffffff00", "border_color_primary_dark": "*neutral_700", "button_border_width": "*input_border_width", "button_border_width_dark": "*input_border_width", "button_cancel_background_fill": "*button_secondary_background_fill", "button_cancel_background_fill_dark": "*secondary_300", "button_cancel_background_fill_hover": "*button_cancel_background_fill", "button_cancel_background_fill_hover_dark": "*secondary_200", "button_cancel_border_color": "*button_secondary_border_color", "button_cancel_border_color_dark": "*button_secondary_border_color", "button_cancel_border_color_hover": "*button_cancel_border_color", "button_cancel_border_color_hover_dark": "*button_cancel_border_color", "button_cancel_text_color": "*button_secondary_text_color", "button_cancel_text_color_dark": "*button_secondary_text_color", "button_cancel_text_color_hover": "*button_cancel_text_color", "button_cancel_text_color_hover_dark": "*button_cancel_text_color", "button_large_padding": "*spacing_lg calc(2 * *spacing_lg)", "button_large_radius": "*radius_lg", "button_large_text_size": "*text_lg", "button_large_text_weight": "600", "button_primary_background_fill": "#e7e7e7", "button_primary_background_fill_dark": "*primary_700", "button_primary_background_fill_hover": "#6400c5", "button_primary_background_fill_hover_dark": "*neutral_700", "button_primary_border_color": "*primary_200", "button_primary_border_color_dark": "*primary_600", "button_primary_border_color_hover": "*button_primary_border_color", "button_primary_border_color_hover_dark": "*button_primary_border_color", "button_primary_text_color": "#000000", "button_primary_text_color_dark": "white", "button_primary_text_color_hover": "#000000", "button_primary_text_color_hover_dark": "*button_primary_text_color", "button_secondary_background_fill": "#e7e7e7", "button_secondary_background_fill_dark": "*primary_700", "button_secondary_background_fill_hover": "*button_secondary_background_fill", "button_secondary_background_fill_hover_dark": "*neutral_700", "button_secondary_border_color": "*neutral_200", "button_secondary_border_color_dark": "*neutral_700", "button_secondary_border_color_hover": "*button_secondary_border_color", "button_secondary_border_color_hover_dark": "*button_secondary_border_color", "button_secondary_text_color": "#000000", "button_secondary_text_color_dark": "white", "button_secondary_text_color_hover": "*button_secondary_text_color", "button_secondary_text_color_hover_dark": "*button_secondary_text_color", "button_shadow": "none", "button_shadow_active": "none", "button_shadow_hover": "none", "button_small_padding": "*spacing_sm calc(2 * *spacing_sm)", "button_small_radius": "*radius_lg", "button_small_text_size": "*text_md", "button_small_text_weight": "400", "button_transition": "background-color 0.2s ease", "checkbox_background_color": "*background_fill_primary", "checkbox_background_color_dark": "*neutral_800", "checkbox_background_color_focus": "*checkbox_background_color", "checkbox_background_color_focus_dark": "*primary_400", "checkbox_background_color_hover": "*checkbox_background_color", "checkbox_background_color_hover_dark": "*primary_700", "checkbox_background_color_selected": "*secondary_600", "checkbox_background_color_selected_dark": "*primary_600", "checkbox_border_color": "*neutral_300", "checkbox_border_color_dark": "*neutral_700", "checkbox_border_color_focus": "*secondary_500", "checkbox_border_color_focus_dark": "*secondary_500", "checkbox_border_color_hover": "*neutral_300", "checkbox_border_color_hover_dark": "*neutral_600", "checkbox_border_color_selected": "*secondary_600", "checkbox_border_color_selected_dark": "*secondary_600", "checkbox_border_radius": "*radius_sm", "checkbox_border_width": "*input_border_width", "checkbox_border_width_dark": "*input_border_width", "checkbox_check": "url(\"data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M12.207 4.793a1 1 0 010 1.414l-5 5a1 1 0 01-1.414 0l-2-2a1 1 0 011.414-1.414L6.5 9.086l4.293-4.293a1 1 0 011.414 0z'/%3e%3c/svg%3e\")", "checkbox_label_background_fill": "*button_secondary_background_fill", "checkbox_label_background_fill_dark": "*primary_700", "checkbox_label_background_fill_hover": "*button_secondary_background_fill_hover", "checkbox_label_background_fill_hover_dark": "*button_secondary_background_fill_hover", "checkbox_label_background_fill_selected": "*checkbox_label_background_fill", "checkbox_label_background_fill_selected_dark": "*checkbox_label_background_fill", "checkbox_label_border_color": "*border_color_primary", "checkbox_label_border_color_dark": "*border_color_primary", "checkbox_label_border_color_hover": "*checkbox_label_border_color", "checkbox_label_border_color_hover_dark": "*checkbox_label_border_color", "checkbox_label_border_width": "*input_border_width", "checkbox_label_border_width_dark": "*input_border_width", "checkbox_label_gap": "*spacing_lg", "checkbox_label_padding": "*spacing_md calc(2 * *spacing_md)", "checkbox_label_shadow": "none", "checkbox_label_text_color": "*body_text_color", "checkbox_label_text_color_dark": "*body_text_color", "checkbox_label_text_color_selected": "*checkbox_label_text_color", "checkbox_label_text_color_selected_dark": "*checkbox_label_text_color", "checkbox_label_text_size": "*text_md", "checkbox_label_text_weight": "400", "checkbox_shadow": "*input_shadow", "color_accent": "#000000", "color_accent_soft": "*primary_50", "color_accent_soft_dark": "*neutral_700", "container_radius": "*radius_md", "embed_radius": "*radius_lg", "error_background_fill": "#fee2e2", "error_background_fill_dark": "*background_fill_primary", "error_border_color": "#fecaca", "error_border_color_dark": "*border_color_primary", "error_border_width": "1px", "error_border_width_dark": "1px", "error_text_color": "#ef4444", "error_text_color_dark": "#ef4444", "font": "'BIZ UDMincho Medium', 'ui-sans-serif', 'system-ui', sans-serif", "font_mono": "'IBM Plex Mono', 'ui-monospace', 'Consolas', monospace", "form_gap_width": "*spacing_md", "input_background_fill": "#191919cf", "input_background_fill_dark": "*neutral_700", "input_background_fill_focus": "#3b3b3b", "input_background_fill_focus_dark": "#0a0808", "input_background_fill_hover": "*input_background_fill", "input_background_fill_hover_dark": "*input_background_fill", "input_border_color": "*border_color_primary", "input_border_color_dark": "*border_color_primary", "input_border_color_focus": "*secondary_300", "input_border_color_focus_dark": "*neutral_700", "input_border_color_hover": "*input_border_color", "input_border_color_hover_dark": "*input_border_color", "input_border_width": "0px", "input_border_width_dark": "0px", "input_padding": "*spacing_xl", "input_placeholder_color": "*neutral_400", "input_placeholder_color_dark": "*neutral_500", "input_radius": "*radius_lg", "input_shadow": "none", "input_shadow_dark": "none", "input_shadow_focus": "*input_shadow", "input_shadow_focus_dark": "*input_shadow", "input_text_size": "*text_md", "input_text_weight": "400", "layout_gap": "*spacing_xxl", "link_text_color": "*secondary_600", "link_text_color_active": "*secondary_600", "link_text_color_active_dark": "*secondary_500", "link_text_color_dark": "*secondary_500", "link_text_color_hover": "*secondary_700", "link_text_color_hover_dark": "*secondary_400", "link_text_color_visited": "*secondary_500", "link_text_color_visited_dark": "*secondary_600", "loader_color": "*color_accent", "loader_color_dark": "*color_accent", "name": "base", "neutral_100": "#f4f4f5", "neutral_200": "#e4e4e7", "neutral_300": "#d4d4d8", "neutral_400": "#a1a1aa", "neutral_50": "#fafafa", "neutral_500": "#71717a", "neutral_600": "#52525b", "neutral_700": "#1a1a1d", "neutral_800": "#101012", "neutral_900": "#0b0b0d", "neutral_950": "#070708", "panel_background_fill": "#d3d3d3", "panel_background_fill_dark": "*background_fill_secondary", "panel_border_color": "*border_color_primary", "panel_border_color_dark": "*border_color_primary", "panel_border_width": "0", "panel_border_width_dark": "0", "primary_100": "#ede9fe", "primary_200": "#ddd6fe", "primary_300": "#c4b5fd", "primary_400": "#a78bfa", "primary_50": "#f5f3ff", "primary_500": "#8b5cf6", "primary_600": "#7c3aed", "primary_700": "#6d28d9", "primary_800": "#120625", "primary_900": "#090313", "primary_950": "#0a0414", "prose_header_text_weight": "600", "prose_text_size": "*text_md", "prose_text_weight": "400", "radio_circle": "url(\"data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3ccircle cx='8' cy='8' r='3'/%3e%3c/svg%3e\")", "radius_lg": "6px", "radius_md": "4px", "radius_sm": "2px", "radius_xl": "8px", "radius_xs": "1px", "radius_xxl": "12px", "radius_xxs": "1px", "secondary_100": "#f3e8ff", "secondary_200": "#a72d85", "secondary_300": "#72174f", "secondary_400": "#561743", "secondary_50": "#faf5ff", "secondary_500": "#a855f7", "secondary_600": "#9333ea", "secondary_700": "#7e22ce", "secondary_800": "#14061f", "secondary_900": "#0d0415", "secondary_950": "#0e0516", "section_header_text_size": "*text_md", "section_header_text_weight": "400", "shadow_drop": "rgba(0,0,0,0.05) 0px 1px 2px 0px", "shadow_drop_lg": "0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)", "shadow_inset": "rgba(0,0,0,0.05) 0px 2px 4px 0px inset", "shadow_spread": "3px", "shadow_spread_dark": "1px", "slider_color": "#b7b7b7", "slider_color_dark": "#3e1a1a", "spacing_lg": "6px", "spacing_md": "4px", "spacing_sm": "2px", "spacing_xl": "9px", "spacing_xs": "1px", "spacing_xxl": "12px", "spacing_xxs": "1px", "stat_background_fill": "*primary_300", "stat_background_fill_dark": "*primary_500", "table_border_color": "*neutral_300", "table_border_color_dark": "*neutral_600", "table_even_background_fill": "white", "table_even_background_fill_dark": "*neutral_950", "table_odd_background_fill": "*neutral_50", "table_odd_background_fill_dark": "*neutral_900", "table_radius": "*radius_lg", "table_row_focus": "*color_accent_soft", "table_row_focus_dark": "*color_accent_soft", "text_lg": "16px", "text_md": "13px", "text_sm": "11px", "text_xl": "20px", "text_xs": "9px", "text_xxl": "24px", "text_xxs": "8px"}, "version": "0.0.1"}