Skip to content

Commit c81d440

Browse files
committed
moved deepdanbooru to pure pytorch implementation
1 parent 47a44c7 commit c81d440

File tree

8 files changed

+777
-195
lines changed

8 files changed

+777
-195
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ Check the [custom scripts](https://github.com/AUTOMATIC1111/stable-diffusion-web
7070
- separate prompts using uppercase `AND`
7171
- also supports weights for prompts: `a cat :1.2 AND a dog AND a penguin :2.2`
7272
- No token limit for prompts (original stable diffusion lets you use up to 75 tokens)
73-
- DeepDanbooru integration, creates danbooru style tags for anime prompts (add --deepdanbooru to commandline args)
73+
- DeepDanbooru integration, creates danbooru style tags for anime prompts
7474
- [xformers](https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki/Xformers), major speed increase for select cards: (add --xformers to commandline args)
7575
- via extension: [History tab](https://github.com/yfszzx/stable-diffusion-webui-images-browser): view, direct and delete images conveniently within the UI
7676
- Generate forever option

launch.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,6 @@ def prepare_enviroment():
134134

135135
gfpgan_package = os.environ.get('GFPGAN_PACKAGE', "git+https://github.com/TencentARC/GFPGAN.git@8d2447a2d918f8eba5a4a01463fd48e45126a379")
136136
clip_package = os.environ.get('CLIP_PACKAGE', "git+https://github.com/openai/CLIP.git@d50d76daa670286dd6cacf3bcd80b5e4823fc8e1")
137-
deepdanbooru_package = os.environ.get('DEEPDANBOORU_PACKAGE', "git+https://github.com/KichangKim/DeepDanbooru.git@d91a2963bf87c6a770d74894667e9ffa9f6de7ff")
138137

139138
xformers_windows_package = os.environ.get('XFORMERS_WINDOWS_PACKAGE', 'https://github.com/C43H66N12O12S2/stable-diffusion-webui/releases/download/f/xformers-0.0.14.dev0-cp310-cp310-win_amd64.whl')
140139

@@ -158,7 +157,6 @@ def prepare_enviroment():
158157
sys.argv, update_check = extract_arg(sys.argv, '--update-check')
159158
sys.argv, run_tests = extract_arg(sys.argv, '--tests')
160159
xformers = '--xformers' in sys.argv
161-
deepdanbooru = '--deepdanbooru' in sys.argv
162160
ngrok = '--ngrok' in sys.argv
163161

164162
try:
@@ -193,9 +191,6 @@ def prepare_enviroment():
193191
elif platform.system() == "Linux":
194192
run_pip("install xformers", "xformers")
195193

196-
if not is_installed("deepdanbooru") and deepdanbooru:
197-
run_pip(f"install {deepdanbooru_package}#egg=deepdanbooru[tensorflow] tensorflow==2.10.0 tensorflow-io==0.27.0", "deepdanbooru")
198-
199194
if not is_installed("pyngrok") and ngrok:
200195
run_pip("install pyngrok", "ngrok")
201196

modules/api/api.py

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from secrets import compare_digest
1010

1111
import modules.shared as shared
12-
from modules import sd_samplers
12+
from modules import sd_samplers, deepbooru
1313
from modules.api.models import *
1414
from modules.processing import StableDiffusionProcessingTxt2Img, StableDiffusionProcessingImg2Img, process_images
1515
from modules.extras import run_extras, run_pnginfo
@@ -18,9 +18,6 @@
1818
from modules.realesrgan_model import get_realesrgan_models
1919
from typing import List
2020

21-
if shared.cmd_opts.deepdanbooru:
22-
from modules.deepbooru import get_deepbooru_tags
23-
2421
def upscaler_to_index(name: str):
2522
try:
2623
return [x.name.lower() for x in shared.sd_upscalers].index(name.lower())
@@ -245,10 +242,7 @@ def interrogateapi(self, interrogatereq: InterrogateRequest):
245242
if interrogatereq.model == "clip":
246243
processed = shared.interrogator.interrogate(img)
247244
elif interrogatereq.model == "deepdanbooru":
248-
if shared.cmd_opts.deepdanbooru:
249-
processed = get_deepbooru_tags(img)
250-
else:
251-
raise HTTPException(status_code=404, detail="Model not found. Add --deepdanbooru when launching for using the model.")
245+
processed = deepbooru.model.tag(img)
252246
else:
253247
raise HTTPException(status_code=404, detail="Model not found")
254248

modules/deepbooru.py

Lines changed: 91 additions & 167 deletions
Original file line numberDiff line numberDiff line change
@@ -1,173 +1,97 @@
1-
import os.path
2-
from concurrent.futures import ProcessPoolExecutor
3-
import multiprocessing
4-
import time
1+
import os
52
import re
63

4+
import torch
5+
from PIL import Image
6+
import numpy as np
7+
8+
from modules import modelloader, paths, deepbooru_model, devices, images, shared
9+
710
re_special = re.compile(r'([\\()])')
811

9-
def get_deepbooru_tags(pil_image):
10-
"""
11-
This method is for running only one image at a time for simple use. Used to the img2img interrogate.
12-
"""
13-
from modules import shared # prevents circular reference
14-
15-
try:
16-
create_deepbooru_process(shared.opts.interrogate_deepbooru_score_threshold, create_deepbooru_opts())
17-
return get_tags_from_process(pil_image)
18-
finally:
19-
release_process()
20-
21-
22-
OPT_INCLUDE_RANKS = "include_ranks"
23-
def create_deepbooru_opts():
24-
from modules import shared
25-
26-
return {
27-
"use_spaces": shared.opts.deepbooru_use_spaces,
28-
"use_escape": shared.opts.deepbooru_escape,
29-
"alpha_sort": shared.opts.deepbooru_sort_alpha,
30-
OPT_INCLUDE_RANKS: shared.opts.interrogate_return_ranks,
31-
}
32-
33-
34-
def deepbooru_process(queue, deepbooru_process_return, threshold, deepbooru_opts):
35-
model, tags = get_deepbooru_tags_model()
36-
while True: # while process is running, keep monitoring queue for new image
37-
pil_image = queue.get()
38-
if pil_image == "QUIT":
39-
break
40-
else:
41-
deepbooru_process_return["value"] = get_deepbooru_tags_from_model(model, tags, pil_image, threshold, deepbooru_opts)
42-
43-
44-
def create_deepbooru_process(threshold, deepbooru_opts):
45-
"""
46-
Creates deepbooru process. A queue is created to send images into the process. This enables multiple images
47-
to be processed in a row without reloading the model or creating a new process. To return the data, a shared
48-
dictionary is created to hold the tags created. To wait for tags to be returned, a value of -1 is assigned
49-
to the dictionary and the method adding the image to the queue should wait for this value to be updated with
50-
the tags.
51-
"""
52-
from modules import shared # prevents circular reference
53-
context = multiprocessing.get_context("spawn")
54-
shared.deepbooru_process_manager = context.Manager()
55-
shared.deepbooru_process_queue = shared.deepbooru_process_manager.Queue()
56-
shared.deepbooru_process_return = shared.deepbooru_process_manager.dict()
57-
shared.deepbooru_process_return["value"] = -1
58-
shared.deepbooru_process = context.Process(target=deepbooru_process, args=(shared.deepbooru_process_queue, shared.deepbooru_process_return, threshold, deepbooru_opts))
59-
shared.deepbooru_process.start()
60-
61-
62-
def get_tags_from_process(image):
63-
from modules import shared
64-
65-
shared.deepbooru_process_return["value"] = -1
66-
shared.deepbooru_process_queue.put(image)
67-
while shared.deepbooru_process_return["value"] == -1:
68-
time.sleep(0.2)
69-
caption = shared.deepbooru_process_return["value"]
70-
shared.deepbooru_process_return["value"] = -1
71-
72-
return caption
73-
74-
75-
def release_process():
76-
"""
77-
Stops the deepbooru process to return used memory
78-
"""
79-
from modules import shared # prevents circular reference
80-
shared.deepbooru_process_queue.put("QUIT")
81-
shared.deepbooru_process.join()
82-
shared.deepbooru_process_queue = None
83-
shared.deepbooru_process = None
84-
shared.deepbooru_process_return = None
85-
shared.deepbooru_process_manager = None
86-
87-
def get_deepbooru_tags_model():
88-
import deepdanbooru as dd
89-
import tensorflow as tf
90-
import numpy as np
91-
this_folder = os.path.dirname(__file__)
92-
model_path = os.path.abspath(os.path.join(this_folder, '..', 'models', 'deepbooru'))
93-
if not os.path.exists(os.path.join(model_path, 'project.json')):
94-
# there is no point importing these every time
95-
import zipfile
96-
from basicsr.utils.download_util import load_file_from_url
97-
load_file_from_url(
98-
r"https://github.com/KichangKim/DeepDanbooru/releases/download/v3-20211112-sgd-e28/deepdanbooru-v3-20211112-sgd-e28.zip",
99-
model_path)
100-
with zipfile.ZipFile(os.path.join(model_path, "deepdanbooru-v3-20211112-sgd-e28.zip"), "r") as zip_ref:
101-
zip_ref.extractall(model_path)
102-
os.remove(os.path.join(model_path, "deepdanbooru-v3-20211112-sgd-e28.zip"))
103-
104-
tags = dd.project.load_tags_from_project(model_path)
105-
model = dd.project.load_model_from_project(
106-
model_path, compile_model=False
107-
)
108-
return model, tags
109-
110-
111-
def get_deepbooru_tags_from_model(model, tags, pil_image, threshold, deepbooru_opts):
112-
import deepdanbooru as dd
113-
import tensorflow as tf
114-
import numpy as np
115-
116-
alpha_sort = deepbooru_opts['alpha_sort']
117-
use_spaces = deepbooru_opts['use_spaces']
118-
use_escape = deepbooru_opts['use_escape']
119-
include_ranks = deepbooru_opts['include_ranks']
120-
121-
width = model.input_shape[2]
122-
height = model.input_shape[1]
123-
image = np.array(pil_image)
124-
image = tf.image.resize(
125-
image,
126-
size=(height, width),
127-
method=tf.image.ResizeMethod.AREA,
128-
preserve_aspect_ratio=True,
129-
)
130-
image = image.numpy() # EagerTensor to np.array
131-
image = dd.image.transform_and_pad_image(image, width, height)
132-
image = image / 255.0
133-
image_shape = image.shape
134-
image = image.reshape((1, image_shape[0], image_shape[1], image_shape[2]))
135-
136-
y = model.predict(image)[0]
137-
138-
result_dict = {}
139-
140-
for i, tag in enumerate(tags):
141-
result_dict[tag] = y[i]
142-
143-
unsorted_tags_in_theshold = []
144-
result_tags_print = []
145-
for tag in tags:
146-
if result_dict[tag] >= threshold:
12+
13+
class DeepDanbooru:
14+
def __init__(self):
15+
self.model = None
16+
17+
def load(self):
18+
if self.model is not None:
19+
return
20+
21+
files = modelloader.load_models(
22+
model_path=os.path.join(paths.models_path, "torch_deepdanbooru"),
23+
model_url='https://github.com/AUTOMATIC1111/TorchDeepDanbooru/releases/download/v1/model-resnet_custom_v3.pt',
24+
ext_filter=".pt",
25+
download_name='model-resnet_custom_v3.pt',
26+
)
27+
28+
self.model = deepbooru_model.DeepDanbooruModel()
29+
self.model.load_state_dict(torch.load(files[0], map_location="cpu"))
30+
31+
self.model.eval()
32+
self.model.to(devices.cpu, devices.dtype)
33+
34+
def start(self):
35+
self.load()
36+
self.model.to(devices.device)
37+
38+
def stop(self):
39+
if not shared.opts.interrogate_keep_models_in_memory:
40+
self.model.to(devices.cpu)
41+
devices.torch_gc()
42+
43+
def tag(self, pil_image):
44+
self.start()
45+
res = self.tag_multi(pil_image)
46+
self.stop()
47+
48+
return res
49+
50+
def tag_multi(self, pil_image, force_disable_ranks=False):
51+
threshold = shared.opts.interrogate_deepbooru_score_threshold
52+
use_spaces = shared.opts.deepbooru_use_spaces
53+
use_escape = shared.opts.deepbooru_escape
54+
alpha_sort = shared.opts.deepbooru_sort_alpha
55+
include_ranks = shared.opts.interrogate_return_ranks and not force_disable_ranks
56+
57+
pic = images.resize_image(2, pil_image.convert("RGB"), 512, 512)
58+
a = np.expand_dims(np.array(pic, dtype=np.float32), 0) / 255
59+
60+
with torch.no_grad(), devices.autocast():
61+
x = torch.from_numpy(a).cuda()
62+
y = self.model(x)[0].detach().cpu().numpy()
63+
64+
probability_dict = {}
65+
66+
for tag, probability in zip(self.model.tags, y):
67+
if probability < threshold:
68+
continue
69+
14770
if tag.startswith("rating:"):
14871
continue
149-
unsorted_tags_in_theshold.append((result_dict[tag], tag))
150-
result_tags_print.append(f'{result_dict[tag]} {tag}')
151-
152-
# sort tags
153-
result_tags_out = []
154-
sort_ndx = 0
155-
if alpha_sort:
156-
sort_ndx = 1
157-
158-
# sort by reverse by likelihood and normal for alpha, and format tag text as requested
159-
unsorted_tags_in_theshold.sort(key=lambda y: y[sort_ndx], reverse=(not alpha_sort))
160-
for weight, tag in unsorted_tags_in_theshold:
161-
tag_outformat = tag
162-
if use_spaces:
163-
tag_outformat = tag_outformat.replace('_', ' ')
164-
if use_escape:
165-
tag_outformat = re.sub(re_special, r'\\\1', tag_outformat)
166-
if include_ranks:
167-
tag_outformat = f"({tag_outformat}:{weight:.3f})"
168-
169-
result_tags_out.append(tag_outformat)
170-
171-
print('\n'.join(sorted(result_tags_print, reverse=True)))
172-
173-
return ', '.join(result_tags_out)
72+
73+
probability_dict[tag] = probability
74+
75+
if alpha_sort:
76+
tags = sorted(probability_dict)
77+
else:
78+
tags = [tag for tag, _ in sorted(probability_dict.items(), key=lambda x: -x[1])]
79+
80+
res = []
81+
82+
for tag in tags:
83+
probability = probability_dict[tag]
84+
tag_outformat = tag
85+
if use_spaces:
86+
tag_outformat = tag_outformat.replace('_', ' ')
87+
if use_escape:
88+
tag_outformat = re.sub(re_special, r'\\\1', tag_outformat)
89+
if include_ranks:
90+
tag_outformat = f"({tag_outformat}:{probability:.3f})"
91+
92+
res.append(tag_outformat)
93+
94+
return ", ".join(res)
95+
96+
97+
model = DeepDanbooru()

0 commit comments

Comments
 (0)