Skip to content

Commit 54d4c7b

Browse files
authored
Merge pull request #393 from aiplayuser/main
Add controlnet model loader
2 parents fc5c5c4 + db20839 commit 54d4c7b

File tree

3 files changed

+257
-10
lines changed

3 files changed

+257
-10
lines changed

__init__.py

Lines changed: 48 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -116,25 +116,63 @@ def execute(self, preprocessor, image, resolution=512):
116116

117117
return getattr(aux_class(), aux_class.FUNCTION)(**params)
118118

119+
##########################################################################################################################
120+
WEB_DIRECTORY = "./web"
121+
from server import PromptServer
122+
from aiohttp import web
123+
import folder_paths, comfy.controlnet
124+
@PromptServer.instance.routes.get("/Preprocessor")
125+
async def getStylesList(request):
126+
cnmodelname = request.rel_url.query["name"]
127+
return web.json_response([{"name":i} for i in preprocessor_options()])
119128

120129
class ControlNetPreprocessorSelector:
121130
@classmethod
122131
def INPUT_TYPES(s):
123-
return {
124-
"required": {
125-
"preprocessor": (PREPROCESSOR_OPTIONS,),
126-
}
127-
}
132+
return { "required": { "cn": ( ["none"]+folder_paths.get_filename_list("controlnet"), ),
133+
"image": ("IMAGE",), },
134+
"hidden": { "prompt": "PROMPT", "my_unique_id": "UNIQUE_ID" },
135+
"optional": { "resolution": ("INT", {"default": 512, "min": 64, "max": 4096, "step": 64 } ) } }
128136

129-
RETURN_TYPES = (PREPROCESSOR_OPTIONS,)
130-
RETURN_NAMES = ("preprocessor",)
137+
RETURN_TYPES = ("CONTROL_NET","IMAGE")
131138
FUNCTION = "get_preprocessor"
132-
133139
CATEGORY = "ControlNet Preprocessors"
140+
OUTPUT_NODE = True
134141

135-
def get_preprocessor(self, preprocessor: str):
136-
return (preprocessor,)
142+
def get_preprocessor(self, cn, image, resolution=512, prompt=None, my_unique_id=None):
143+
controlnet = comfy.controlnet.load_controlnet( folder_paths.get_full_path("controlnet", cn) )
144+
pre = prompt[my_unique_id]["inputs"]['select_styles']
145+
print(prompt)
146+
if pre == "": return (controlnet, image )
147+
else:
148+
aux_class = AUX_NODE_MAPPINGS[pre]
149+
input_types = aux_class.INPUT_TYPES()
150+
input_types = {
151+
**input_types["required"],
152+
**(input_types["optional"] if "optional" in input_types else {})
153+
}
154+
params = {}
155+
for name, input_type in input_types.items():
156+
if name == "image":
157+
params[name] = image
158+
continue
159+
160+
if name == "resolution":
161+
params[name] = resolution
162+
continue
163+
164+
if len(input_type) == 2 and ("default" in input_type[1]):
165+
params[name] = input_type[1]["default"]
166+
continue
167+
168+
default_values = { "INT": 0, "FLOAT": 0.0 }
169+
if input_type[0] in default_values: params[name] = default_values[input_type[0]]
170+
171+
predict = getattr(aux_class(), aux_class.FUNCTION)(**params)
137172

173+
if isinstance(predict, dict): return (controlnet,) + predict["result"]
174+
else: return (controlnet,) + predict
175+
##########################################################################################################################
138176

139177
NODE_CLASS_MAPPINGS = {
140178
**AUX_NODE_MAPPINGS,

web/index.css

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
.Preprocessor{
2+
overflow: auto;
3+
}
4+
.Preprocessor .tools{
5+
display:flex;
6+
justify-content:space-between;
7+
height:20px;
8+
padding-bottom:5px;
9+
border-bottom:2px solid var(--border-color);
10+
}
11+
.Preprocessor .tools button.delete{
12+
height:20px;
13+
border-radius: 8px;
14+
border: 2px solid var(--border-color);
15+
font-size:11px;
16+
background:var(--comfy-input-bg);
17+
color:var(--error-text);
18+
box-shadow:none;
19+
cursor:pointer;
20+
}
21+
.Preprocessor .tools button.delete:hover{
22+
filter: brightness(1.2);
23+
}
24+
.Preprocessor .tools textarea.search{
25+
flex:1;
26+
margin-left:10px;
27+
height:10px;
28+
line-height:8px;
29+
border-radius: 8px;
30+
border: 2px solid var(--border-color);
31+
font-size:15px;
32+
background:var(--comfy-input-bg);
33+
color:var(--input-text);
34+
box-shadow:none;
35+
padding:4px 10px;
36+
outline: none;
37+
resize: none;
38+
appearance:none;
39+
}
40+
.Preprocessor-list{
41+
list-style: none;
42+
padding: 0;
43+
margin: 0;
44+
min-height: 150px;
45+
height: calc(100% - 30px);
46+
overflow: auto;
47+
/*display: flex;*/
48+
/*flex-wrap: wrap;*/
49+
}
50+
.Preprocessor-list.no-top{
51+
height: auto;
52+
}
53+
54+
.Preprocessor-tag{
55+
display: inline-block;
56+
vertical-align: middle;
57+
margin-top: 0px;
58+
margin-right: 0px;
59+
padding:0px;
60+
color: var(--input-text);
61+
background-color: var(--comfy-input-bg);
62+
border-radius: 8px;
63+
border: 2px solid var(--border-color);
64+
font-size:11px;
65+
cursor:pointer;
66+
}
67+
.Preprocessor-tag.hide{
68+
display:none;
69+
}
70+
.Preprocessor-tag:hover{
71+
filter: brightness(1.2);
72+
}
73+
.Preprocessor-tag input{
74+
--ring-color: transparent;
75+
position: relative;
76+
box-shadow: none;
77+
border: 2px solid var(--border-color);
78+
border-radius: 2px;
79+
background: linear-gradient(135deg, var(--comfy-menu-bg) 0%, var(--comfy-input-bg) 60%);
80+
}
81+
.Preprocessor-tag input[type=checkbox]:checked{
82+
border: 1px solid var(--theme-color-light);
83+
background-color: var(--theme-color-light);
84+
background-image: 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");
85+
}
86+
.Preprocessor-tag input[type=checkbox]{
87+
color-adjust: exact;
88+
display: inline-block;
89+
flex-shrink: 0;
90+
vertical-align: middle;
91+
appearance: none;
92+
border: 2px solid var(--border-color);
93+
background-origin: border-box;
94+
padding: 0;
95+
width: 1rem;
96+
height: 1rem;
97+
border-radius:4px;
98+
color:var(--theme-color-light);
99+
user-select: none;
100+
}
101+
.Preprocessor-tag span{
102+
margin:0 4px;
103+
vertical-align: middle;
104+
}

web/index.js

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
2+
import { app } from "../../../scripts/app.js";
3+
import { api } from "../../../scripts/api.js";
4+
import { $el } from "../../../scripts/ui.js";
5+
6+
const link = document.createElement("link"); link.rel = "stylesheet";
7+
link.href = "extensions/comfyui_controlnet_aux_pre/index.css";
8+
document.head.appendChild(link);
9+
10+
let preprolist = {}; let controlnet = 'control_v11p_sd15_canny.pth';
11+
12+
function listsort(listdata,valuestr) { listdata.sort((a,b)=> valuestr.includes(b.name) - valuestr.includes(a.name)); };
13+
14+
function addhide(el,searchValue) { el.classList.toggle('hide', !(
15+
el.dataset.name.toLowerCase().includes(searchValue.toLowerCase()) ||
16+
el.dataset.tag.toLowerCase().includes(searchValue.toLowerCase()) ||
17+
el.classList.contains('Preprocessor-tag-selected') ) ) };
18+
19+
function getTagList() { return preprolist[controlnet].map((tag, index) => {
20+
return $el('label.Preprocessor-tag',
21+
{ dataset: { tag: tag.name, name: tag.name, index: index },
22+
$: (el) => { el.firstChild.onclick = () => { el.classList.toggle("Preprocessor-tag-selected"); }; }, },
23+
[ $el("input", { type: 'checkbox', name: tag.name }), $el("span", { textContent: tag.name }) ] ); } ); };
24+
25+
async function getprepro(el){ const resp = await api.fetchApi(`/Preprocessor?name=${controlnet}`);
26+
if (resp.status === 200) { let data = await resp.json(); let mlist = ["","none"];
27+
28+
if(controlnet.includes("canny")){ mlist=["canny", "CannyEdgePreprocessor"] }
29+
else if(controlnet.includes("depth")){ mlist=["depth", "MiDaS-DepthMapPreprocessor"] }
30+
else if(controlnet.includes("lineart")){ mlist=["lineart", "LineArtPreprocessor"] }
31+
else if(controlnet.includes("tile")){ mlist=["tile", "TilePreprocessor"] }
32+
else if(controlnet.includes("scrib")){ mlist=["scrib", "FakeScribblePreprocessor"] }
33+
else if(controlnet.includes("soft")){ mlist=["soft", "HEDPreprocessor"] }
34+
else if(controlnet.includes("pose")){ mlist=["pose", "DWPreprocessor"] }
35+
else if(controlnet.includes("normal")){ mlist=["normal", "BAE-NormalMapPreprocessor"] }
36+
else if(controlnet.includes("semseg")){ mlist=["semseg", "OneFormer-ADE20K-SemSegPreprocessor"] }
37+
else if(controlnet.includes("shuffle")){ mlist=["shuffle", "ShufflePreprocessor"] }
38+
else if(controlnet.includes("ioclab_sd15_recolor")){ mlist=["image", "ImageLuminanceDetector"] }
39+
else if(controlnet.includes("t2iadapter_color")){ mlist=["color", "ColorPreprocessor"] }
40+
else if(controlnet.includes("sketch")){ mlist=["scrib", "FakeScribblePreprocessor"] }
41+
42+
document.querySelector('.search').value = mlist[0]; listsort(data,mlist[1]);
43+
44+
preprolist[controlnet] = data; el.innerHTML = ''; el.append(...getTagList());
45+
el.children[0].classList.add("Preprocessor-tag-selected"); el.children[0].children[0].checked = true;
46+
} };
47+
48+
app.registerExtension({
49+
name: 'comfy.ControlNet Preprocessors.Preprocessor Selector',
50+
async beforeRegisterNodeDef(nodeType, nodeData, app) {
51+
if(nodeData.name == 'ControlNetPreprocessorSelector'){ const onNodeCreated = nodeType.prototype.onNodeCreated;
52+
nodeType.prototype.onNodeCreated = function() { const styles_id = this.widgets.findIndex((w) => w.name == 'cn');
53+
this.setProperty("values",[]); this.setSize([300, 350]);
54+
55+
const toolsElement = $el('div.tools', [ //添加清空按钮搜索框
56+
$el('button.delete',{ textContent: 'Empty',
57+
onclick:()=>{ selectorlist[0].querySelectorAll(".search").forEach(el=>{ el.value = '' });
58+
selectorlist[1].querySelectorAll(".Preprocessor-tag").forEach(el => {
59+
el.classList.remove("Preprocessor-tag-selected");
60+
el.classList.remove("hide"); el.children[0].checked = false }) } }),
61+
62+
$el('textarea.search',{ placeholder:"🔎 search",
63+
oninput:(e)=>{ let searchValue = e.target.value;
64+
selectorlist[1].querySelectorAll(".Preprocessor-tag").forEach(el => { addhide(el,searchValue); }) } })
65+
]);
66+
const stylesList = $el("ul.Preprocessor-list", []);
67+
let selector = this.addDOMWidget( 'select_styles', "btn", $el('div.Preprocessor', [toolsElement, stylesList] ) );
68+
let selectorlist = selector.element.children;
69+
70+
// 监听鼠标离开事件
71+
selectorlist[1].addEventListener('mouseleave', function(e) { const searchValue = document.querySelector('.search').value;
72+
const selectedTags = Array.from(this.querySelectorAll('.Preprocessor-tag-selected')).map(el => el.dataset.tag); // 当前选中的标签值
73+
listsort(preprolist[controlnet],selectedTags); this.innerHTML = ''; this.append(...getTagList()); // 重新排序
74+
this.querySelectorAll('.Preprocessor-tag').forEach(el => { // 遍历所有标签
75+
const isSelected = selectedTags.includes(el.dataset.tag); //标签的选中状态
76+
if (isSelected) { el.classList.add("Preprocessor-tag-selected"); el.children[0].checked = true; } //更新样式标签的选中状态
77+
addhide(el,searchValue); }); // 同时处理搜索和隐藏逻辑
78+
});
79+
80+
//根据controlnet模型返回预处理器列表
81+
Object.defineProperty( this.widgets[styles_id], 'value', { get:()=>{ return controlnet },
82+
set:(value)=>{ controlnet = value; getprepro(selectorlist[1]) } })
83+
84+
//根据选中状态返回预处理器
85+
let style_select_values = ''
86+
Object.defineProperty(selector, "value", {
87+
set: (value) => {
88+
selectorlist[1].querySelectorAll(".Preprocessor-tag").forEach(el => {
89+
if (value.split(',').includes(el.dataset.tag)) {
90+
el.classList.add("Preprocessor-tag-selected"); el.children[0].checked = true } }) },
91+
get: () => {
92+
selectorlist[1].querySelectorAll(".Preprocessor-tag").forEach(el => {
93+
if(el.classList.value.indexOf("Preprocessor-tag-selected")>=0){
94+
if(!this.properties["values"].includes(el.dataset.tag)){
95+
this.properties["values"].push(el.dataset.tag); }}
96+
else{ if(this.properties["values"].includes(el.dataset.tag)){
97+
this.properties["values"]= this.properties["values"].filter(v=>v!=el.dataset.tag); } } });
98+
style_select_values = this.properties["values"].join(',');
99+
return style_select_values; } });
100+
101+
getprepro(selectorlist[1]); return onNodeCreated;
102+
}
103+
}
104+
}
105+
})

0 commit comments

Comments
 (0)