Skip to content

Commit

Permalink
Merge pull request #393 from aiplayuser/main
Browse files Browse the repository at this point in the history
Add controlnet model loader
  • Loading branch information
Fannovel16 authored Jul 30, 2024
2 parents fc5c5c4 + db20839 commit 54d4c7b
Show file tree
Hide file tree
Showing 3 changed files with 257 additions and 10 deletions.
58 changes: 48 additions & 10 deletions __init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,25 +116,63 @@ def execute(self, preprocessor, image, resolution=512):

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

##########################################################################################################################
WEB_DIRECTORY = "./web"
from server import PromptServer
from aiohttp import web
import folder_paths, comfy.controlnet
@PromptServer.instance.routes.get("/Preprocessor")
async def getStylesList(request):
cnmodelname = request.rel_url.query["name"]
return web.json_response([{"name":i} for i in preprocessor_options()])

class ControlNetPreprocessorSelector:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"preprocessor": (PREPROCESSOR_OPTIONS,),
}
}
return { "required": { "cn": ( ["none"]+folder_paths.get_filename_list("controlnet"), ),
"image": ("IMAGE",), },
"hidden": { "prompt": "PROMPT", "my_unique_id": "UNIQUE_ID" },
"optional": { "resolution": ("INT", {"default": 512, "min": 64, "max": 4096, "step": 64 } ) } }

RETURN_TYPES = (PREPROCESSOR_OPTIONS,)
RETURN_NAMES = ("preprocessor",)
RETURN_TYPES = ("CONTROL_NET","IMAGE")
FUNCTION = "get_preprocessor"

CATEGORY = "ControlNet Preprocessors"
OUTPUT_NODE = True

def get_preprocessor(self, preprocessor: str):
return (preprocessor,)
def get_preprocessor(self, cn, image, resolution=512, prompt=None, my_unique_id=None):
controlnet = comfy.controlnet.load_controlnet( folder_paths.get_full_path("controlnet", cn) )
pre = prompt[my_unique_id]["inputs"]['select_styles']
print(prompt)
if pre == "": return (controlnet, image )
else:
aux_class = AUX_NODE_MAPPINGS[pre]
input_types = aux_class.INPUT_TYPES()
input_types = {
**input_types["required"],
**(input_types["optional"] if "optional" in input_types else {})
}
params = {}
for name, input_type in input_types.items():
if name == "image":
params[name] = image
continue

if name == "resolution":
params[name] = resolution
continue

if len(input_type) == 2 and ("default" in input_type[1]):
params[name] = input_type[1]["default"]
continue

default_values = { "INT": 0, "FLOAT": 0.0 }
if input_type[0] in default_values: params[name] = default_values[input_type[0]]

predict = getattr(aux_class(), aux_class.FUNCTION)(**params)

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

NODE_CLASS_MAPPINGS = {
**AUX_NODE_MAPPINGS,
Expand Down
104 changes: 104 additions & 0 deletions web/index.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
.Preprocessor{
overflow: auto;
}
.Preprocessor .tools{
display:flex;
justify-content:space-between;
height:20px;
padding-bottom:5px;
border-bottom:2px solid var(--border-color);
}
.Preprocessor .tools button.delete{
height:20px;
border-radius: 8px;
border: 2px solid var(--border-color);
font-size:11px;
background:var(--comfy-input-bg);
color:var(--error-text);
box-shadow:none;
cursor:pointer;
}
.Preprocessor .tools button.delete:hover{
filter: brightness(1.2);
}
.Preprocessor .tools textarea.search{
flex:1;
margin-left:10px;
height:10px;
line-height:8px;
border-radius: 8px;
border: 2px solid var(--border-color);
font-size:15px;
background:var(--comfy-input-bg);
color:var(--input-text);
box-shadow:none;
padding:4px 10px;
outline: none;
resize: none;
appearance:none;
}
.Preprocessor-list{
list-style: none;
padding: 0;
margin: 0;
min-height: 150px;
height: calc(100% - 30px);
overflow: auto;
/*display: flex;*/
/*flex-wrap: wrap;*/
}
.Preprocessor-list.no-top{
height: auto;
}

.Preprocessor-tag{
display: inline-block;
vertical-align: middle;
margin-top: 0px;
margin-right: 0px;
padding:0px;
color: var(--input-text);
background-color: var(--comfy-input-bg);
border-radius: 8px;
border: 2px solid var(--border-color);
font-size:11px;
cursor:pointer;
}
.Preprocessor-tag.hide{
display:none;
}
.Preprocessor-tag:hover{
filter: brightness(1.2);
}
.Preprocessor-tag input{
--ring-color: transparent;
position: relative;
box-shadow: none;
border: 2px solid var(--border-color);
border-radius: 2px;
background: linear-gradient(135deg, var(--comfy-menu-bg) 0%, var(--comfy-input-bg) 60%);
}
.Preprocessor-tag input[type=checkbox]:checked{
border: 1px solid var(--theme-color-light);
background-color: var(--theme-color-light);
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");
}
.Preprocessor-tag input[type=checkbox]{
color-adjust: exact;
display: inline-block;
flex-shrink: 0;
vertical-align: middle;
appearance: none;
border: 2px solid var(--border-color);
background-origin: border-box;
padding: 0;
width: 1rem;
height: 1rem;
border-radius:4px;
color:var(--theme-color-light);
user-select: none;
}
.Preprocessor-tag span{
margin:0 4px;
vertical-align: middle;
}
105 changes: 105 additions & 0 deletions web/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@

import { app } from "../../../scripts/app.js";
import { api } from "../../../scripts/api.js";
import { $el } from "../../../scripts/ui.js";

const link = document.createElement("link"); link.rel = "stylesheet";
link.href = "extensions/comfyui_controlnet_aux_pre/index.css";
document.head.appendChild(link);

let preprolist = {}; let controlnet = 'control_v11p_sd15_canny.pth';

function listsort(listdata,valuestr) { listdata.sort((a,b)=> valuestr.includes(b.name) - valuestr.includes(a.name)); };

function addhide(el,searchValue) { el.classList.toggle('hide', !(
el.dataset.name.toLowerCase().includes(searchValue.toLowerCase()) ||
el.dataset.tag.toLowerCase().includes(searchValue.toLowerCase()) ||
el.classList.contains('Preprocessor-tag-selected') ) ) };

function getTagList() { return preprolist[controlnet].map((tag, index) => {
return $el('label.Preprocessor-tag',
{ dataset: { tag: tag.name, name: tag.name, index: index },
$: (el) => { el.firstChild.onclick = () => { el.classList.toggle("Preprocessor-tag-selected"); }; }, },
[ $el("input", { type: 'checkbox', name: tag.name }), $el("span", { textContent: tag.name }) ] ); } ); };

async function getprepro(el){ const resp = await api.fetchApi(`/Preprocessor?name=${controlnet}`);
if (resp.status === 200) { let data = await resp.json(); let mlist = ["","none"];

if(controlnet.includes("canny")){ mlist=["canny", "CannyEdgePreprocessor"] }
else if(controlnet.includes("depth")){ mlist=["depth", "MiDaS-DepthMapPreprocessor"] }
else if(controlnet.includes("lineart")){ mlist=["lineart", "LineArtPreprocessor"] }
else if(controlnet.includes("tile")){ mlist=["tile", "TilePreprocessor"] }
else if(controlnet.includes("scrib")){ mlist=["scrib", "FakeScribblePreprocessor"] }
else if(controlnet.includes("soft")){ mlist=["soft", "HEDPreprocessor"] }
else if(controlnet.includes("pose")){ mlist=["pose", "DWPreprocessor"] }
else if(controlnet.includes("normal")){ mlist=["normal", "BAE-NormalMapPreprocessor"] }
else if(controlnet.includes("semseg")){ mlist=["semseg", "OneFormer-ADE20K-SemSegPreprocessor"] }
else if(controlnet.includes("shuffle")){ mlist=["shuffle", "ShufflePreprocessor"] }
else if(controlnet.includes("ioclab_sd15_recolor")){ mlist=["image", "ImageLuminanceDetector"] }
else if(controlnet.includes("t2iadapter_color")){ mlist=["color", "ColorPreprocessor"] }
else if(controlnet.includes("sketch")){ mlist=["scrib", "FakeScribblePreprocessor"] }

document.querySelector('.search').value = mlist[0]; listsort(data,mlist[1]);

preprolist[controlnet] = data; el.innerHTML = ''; el.append(...getTagList());
el.children[0].classList.add("Preprocessor-tag-selected"); el.children[0].children[0].checked = true;
} };

app.registerExtension({
name: 'comfy.ControlNet Preprocessors.Preprocessor Selector',
async beforeRegisterNodeDef(nodeType, nodeData, app) {
if(nodeData.name == 'ControlNetPreprocessorSelector'){ const onNodeCreated = nodeType.prototype.onNodeCreated;
nodeType.prototype.onNodeCreated = function() { const styles_id = this.widgets.findIndex((w) => w.name == 'cn');
this.setProperty("values",[]); this.setSize([300, 350]);

const toolsElement = $el('div.tools', [ //添加清空按钮搜索框
$el('button.delete',{ textContent: 'Empty',
onclick:()=>{ selectorlist[0].querySelectorAll(".search").forEach(el=>{ el.value = '' });
selectorlist[1].querySelectorAll(".Preprocessor-tag").forEach(el => {
el.classList.remove("Preprocessor-tag-selected");
el.classList.remove("hide"); el.children[0].checked = false }) } }),

$el('textarea.search',{ placeholder:"🔎 search",
oninput:(e)=>{ let searchValue = e.target.value;
selectorlist[1].querySelectorAll(".Preprocessor-tag").forEach(el => { addhide(el,searchValue); }) } })
]);
const stylesList = $el("ul.Preprocessor-list", []);
let selector = this.addDOMWidget( 'select_styles', "btn", $el('div.Preprocessor', [toolsElement, stylesList] ) );
let selectorlist = selector.element.children;

// 监听鼠标离开事件
selectorlist[1].addEventListener('mouseleave', function(e) { const searchValue = document.querySelector('.search').value;
const selectedTags = Array.from(this.querySelectorAll('.Preprocessor-tag-selected')).map(el => el.dataset.tag); // 当前选中的标签值
listsort(preprolist[controlnet],selectedTags); this.innerHTML = ''; this.append(...getTagList()); // 重新排序
this.querySelectorAll('.Preprocessor-tag').forEach(el => { // 遍历所有标签
const isSelected = selectedTags.includes(el.dataset.tag); //标签的选中状态
if (isSelected) { el.classList.add("Preprocessor-tag-selected"); el.children[0].checked = true; } //更新样式标签的选中状态
addhide(el,searchValue); }); // 同时处理搜索和隐藏逻辑
});

//根据controlnet模型返回预处理器列表
Object.defineProperty( this.widgets[styles_id], 'value', { get:()=>{ return controlnet },
set:(value)=>{ controlnet = value; getprepro(selectorlist[1]) } })

//根据选中状态返回预处理器
let style_select_values = ''
Object.defineProperty(selector, "value", {
set: (value) => {
selectorlist[1].querySelectorAll(".Preprocessor-tag").forEach(el => {
if (value.split(',').includes(el.dataset.tag)) {
el.classList.add("Preprocessor-tag-selected"); el.children[0].checked = true } }) },
get: () => {
selectorlist[1].querySelectorAll(".Preprocessor-tag").forEach(el => {
if(el.classList.value.indexOf("Preprocessor-tag-selected")>=0){
if(!this.properties["values"].includes(el.dataset.tag)){
this.properties["values"].push(el.dataset.tag); }}
else{ if(this.properties["values"].includes(el.dataset.tag)){
this.properties["values"]= this.properties["values"].filter(v=>v!=el.dataset.tag); } } });
style_select_values = this.properties["values"].join(',');
return style_select_values; } });

getprepro(selectorlist[1]); return onNodeCreated;
}
}
}
})

0 comments on commit 54d4c7b

Please sign in to comment.