Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bff2760c3d | ||
|
|
a0f8848367 | ||
|
|
5b1cbcd8d5 | ||
|
|
05857a92d5 | ||
|
|
6bdc811286 | ||
|
|
469d50a5b8 | ||
|
|
ef86904bfb | ||
|
|
d4181ea67c | ||
|
|
1c6d17309f | ||
|
|
db293ec41d |
+70
-1
@@ -8,6 +8,8 @@ import hashlib
|
||||
import datetime
|
||||
import folder_paths
|
||||
import logging
|
||||
import base64,io,re
|
||||
from PIL import Image
|
||||
from comfy.cli_args import args
|
||||
python = sys.executable
|
||||
|
||||
@@ -30,6 +32,8 @@ except:
|
||||
print("##nodes.ChatGPT ImportError")
|
||||
|
||||
|
||||
from .nodes.RembgNode import get_rembg_models,U2NET_HOME,run_briarmbg,run_rembg
|
||||
|
||||
from server import PromptServer
|
||||
|
||||
try:
|
||||
@@ -98,6 +102,26 @@ install_openai()
|
||||
current_path = os.path.abspath(os.path.dirname(__file__))
|
||||
|
||||
|
||||
def remove_base64_prefix(base64_str):
|
||||
"""
|
||||
去除 base64 字符串中的 data:image/*;base64, 前缀
|
||||
|
||||
Args:
|
||||
base64_str: base64 编码的字符串
|
||||
|
||||
Returns:
|
||||
去除前缀后的 base64 字符串
|
||||
"""
|
||||
|
||||
# 使用正则表达式匹配常见的前缀
|
||||
pattern = r'^data:image\/(.*);base64,(.+)$'
|
||||
match = re.match(pattern, base64_str)
|
||||
if match:
|
||||
# 如果匹配到常见的前缀,则去除前缀并返回
|
||||
return match.group(2)
|
||||
else:
|
||||
# 如果不匹配到常见的前缀,则直接返回
|
||||
return base64_str
|
||||
|
||||
def calculate_md5(string):
|
||||
encoded_string = string.encode()
|
||||
@@ -629,10 +653,53 @@ async def get_checkpoints(request):
|
||||
names=get_llama_models()
|
||||
except:
|
||||
print("llamafile none")
|
||||
|
||||
try:
|
||||
if data['type']=='rembg':
|
||||
names=get_rembg_models(U2NET_HOME)
|
||||
except:
|
||||
print("rembg none")
|
||||
|
||||
return web.json_response({"names":names,"types":list(folder_paths.folder_names_and_paths.keys())})
|
||||
|
||||
|
||||
@routes.post('/mixlab/rembg')
|
||||
async def rembg_hander(request):
|
||||
data = await request.json()
|
||||
model=data['model']
|
||||
result={}
|
||||
|
||||
data_base64=remove_base64_prefix(data['base64'])
|
||||
image_data = base64.b64decode(data_base64)
|
||||
|
||||
# 创建一个BytesIO对象
|
||||
image_stream = io.BytesIO(image_data)
|
||||
|
||||
# 使用PIL Image模块读取图像
|
||||
image = Image.open(image_stream)
|
||||
|
||||
if model=='briarmbg':
|
||||
_,rgba_images,_=run_briarmbg([image])
|
||||
else:
|
||||
_,rgba_images,_=run_rembg(model,[image])
|
||||
|
||||
with io.BytesIO() as buf:
|
||||
rgba_images[0].save(buf, format='PNG')
|
||||
img_bytes = buf.getvalue()
|
||||
img_base64 = base64.b64encode(img_bytes).decode('utf-8')
|
||||
|
||||
try:
|
||||
result={
|
||||
'data':img_base64,
|
||||
'model':model,
|
||||
'status':'success',
|
||||
}
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
return web.json_response(result)
|
||||
|
||||
|
||||
@routes.post("/mixlab/prompt_result")
|
||||
async def post_prompt_result(request):
|
||||
data = await request.json()
|
||||
@@ -711,12 +778,14 @@ async def start_local_llm(data):
|
||||
|
||||
server_settings=ServerSettings(host=address,port=port)
|
||||
|
||||
name, ext = os.path.splitext(os.path.basename(model))
|
||||
print('#model',name)
|
||||
app = create_app(
|
||||
server_settings=server_settings,
|
||||
model_settings=[
|
||||
ModelSettings(
|
||||
model=model,
|
||||
model_alias=os.path.basename(model),
|
||||
model_alias=name,
|
||||
n_gpu_layers=n_gpu_layers,
|
||||
n_ctx=4098,
|
||||
chat_format=chat_format,
|
||||
|
||||
+28
-18
@@ -476,6 +476,24 @@ os.environ["U2NET_HOME"] = U2NET_HOME
|
||||
global _available
|
||||
_available=False
|
||||
|
||||
|
||||
def get_rembg_models(path):
|
||||
"""从目录中获取文件并提取文件名
|
||||
Args:
|
||||
path: 目录路径
|
||||
Returns:
|
||||
文件名列表
|
||||
"""
|
||||
filenames = []
|
||||
for root, _, files in os.walk(path):
|
||||
for filename in files:
|
||||
# 过滤隐藏文件
|
||||
if not filename.startswith('.'):
|
||||
name, ext = os.path.splitext(os.path.basename(filename))
|
||||
filenames.append(name)
|
||||
return filenames
|
||||
|
||||
|
||||
def is_installed(package):
|
||||
try:
|
||||
spec = importlib.util.find_spec(package)
|
||||
@@ -509,7 +527,7 @@ except:
|
||||
_available=False
|
||||
|
||||
|
||||
def briarmbg_run(images=[]):
|
||||
def run_briarmbg(images=[]):
|
||||
mroot=os.path.join(folder_paths.models_dir, "rembg")
|
||||
m=os.path.join(mroot,'briarmbg.pth')
|
||||
if os.path.exists(m)==False:
|
||||
@@ -573,14 +591,15 @@ def briarmbg_run(images=[]):
|
||||
return (masks,rgba_images,rgb_images)
|
||||
|
||||
|
||||
def run_bg(model_name= "unet",images=[]):
|
||||
def run_rembg(model_name= "unet",images=[],callback=None):
|
||||
# model_name = "unet" # "isnet-general-use"
|
||||
# print('#run_rembg',model_name)
|
||||
rembg_session = new_session(model_name)
|
||||
masks=[]
|
||||
rgba_images=[]
|
||||
rgb_images=[]
|
||||
# 进度条
|
||||
pbar = comfy.utils.ProgressBar(len(images) )
|
||||
pbar=callback
|
||||
for img in images:
|
||||
# use the post_process_mask argument to post process the mask to get better results.
|
||||
mask = remove(img, session=rembg_session,only_mask=True,post_process_mask=True)
|
||||
@@ -620,8 +639,9 @@ def run_bg(model_name= "unet",images=[]):
|
||||
rgb_image = Image.new("RGB", image_rgba.size, (0, 0, 0))
|
||||
rgb_image.paste(image_rgba, mask=image_rgba.split()[3])
|
||||
rgb_images.append(rgb_image)
|
||||
|
||||
pbar.update(1)
|
||||
|
||||
if pbar:
|
||||
pbar.update(1)
|
||||
return (masks,rgba_images,rgb_images)
|
||||
|
||||
|
||||
@@ -643,17 +663,7 @@ class RembgNode_:
|
||||
def INPUT_TYPES(s):
|
||||
return {"required": {
|
||||
"image": ("IMAGE",),
|
||||
"model_name": ([
|
||||
"briarmbg",
|
||||
"u2net",
|
||||
"u2netp",
|
||||
"u2net_human_seg",
|
||||
"u2net_cloth_seg",
|
||||
"silueta",
|
||||
"isnet-general-use",
|
||||
"isnet-anime",
|
||||
|
||||
],),
|
||||
"model_name": (get_rembg_models(U2NET_HOME),),
|
||||
|
||||
},
|
||||
}
|
||||
@@ -681,9 +691,9 @@ class RembgNode_:
|
||||
images.append(im)
|
||||
|
||||
if model_name=='briarmbg':
|
||||
masks,rgba_images,rgb_images=briarmbg_run(images)
|
||||
masks,rgba_images,rgb_images=run_briarmbg(images)
|
||||
else:
|
||||
masks,rgba_images,rgb_images=run_bg(model_name,images)
|
||||
masks,rgba_images,rgb_images=run_rembg(model_name,images, comfy.utils.ProgressBar(len(images) ))
|
||||
|
||||
masks=[pil2tensor(m) for m in masks]
|
||||
|
||||
|
||||
+247
-62
@@ -247,7 +247,7 @@
|
||||
|
||||
.card textarea {
|
||||
width: 100%;
|
||||
height:'fit-content';
|
||||
height: 'fit-content';
|
||||
/* min-width: 300px; */
|
||||
margin-top: 12px;
|
||||
resize: none;
|
||||
@@ -452,7 +452,7 @@
|
||||
<link href="/extensions/comfyui-mixlab-nodes/lib/photoswipe.min.css" rel="stylesheet">
|
||||
<link href="/extensions/comfyui-mixlab-nodes/lib/classic.min.css" rel="stylesheet">
|
||||
<script src="/extensions/comfyui-mixlab-nodes/lib/pickr.min.js"></script>
|
||||
<script src="/extensions/comfyui-mixlab-nodes/lib/filerobot-image-editor.min.js"></script>
|
||||
<!-- <script src="/extensions/comfyui-mixlab-nodes/lib/filerobot-image-editor.min.js"></script> -->
|
||||
<script type="module" src="/extensions/comfyui-mixlab-nodes/lib/model-viewer.min.js"></script>
|
||||
<link rel="stylesheet" href="/extensions/comfyui-mixlab-nodes/lib/login.css">
|
||||
</head>
|
||||
@@ -474,15 +474,14 @@
|
||||
<a class="link" href="https://www.mixcomfy.com" target="_blank">ComfyUI中文爱好者社区推荐</a>
|
||||
</div>
|
||||
|
||||
<a target="_blank" id="login_btn" href="https://www.mixcomfy.com/blog/" style="text-decoration: none;
|
||||
<a id="login_btn" target="_blank" href="https://discord.gg/xbP2GZF6gn" style="text-decoration: none;
|
||||
color: black;font-size:12px">
|
||||
<svg height="32" aria-hidden="true" viewBox="0 0 16 16" version="1.1" width="32" data-view-component="true"
|
||||
class="octicon octicon-mark-github v-align-middle color-fg-default">
|
||||
<path
|
||||
d="M8 0c4.42 0 8 3.58 8 8a8.013 8.013 0 0 1-5.45 7.59c-.4.08-.55-.17-.55-.38 0-.27.01-1.13.01-2.2 0-.75-.25-1.23-.54-1.48 1.78-.2 3.65-.88 3.65-3.95 0-.88-.31-1.59-.82-2.15.08-.2.36-1.02-.08-2.12 0 0-.67-.22-2.2.82-.64-.18-1.32-.27-2-.27-.68 0-1.36.09-2 .27-1.53-1.03-2.2-.82-2.2-.82-.44 1.1-.16 1.92-.08 2.12-.51.56-.82 1.28-.82 2.15 0 3.06 1.86 3.75 3.64 3.95-.23.2-.44.55-.51 1.07-.46.21-1.61.55-2.33-.66-.15-.24-.6-.83-1.23-.82-.67.01-.27.38.01.53.34.19.73.9.82 1.13.16.45.68 1.31 2.69.94 0 .67.01 1.3.01 1.49 0 .21-.15.45-.55.38A7.995 7.995 0 0 1 0 8c0-4.42 3.58-8 8-8Z">
|
||||
</path>
|
||||
</svg> Community</a>
|
||||
|
||||
</svg> HELP/帮助</a>
|
||||
|
||||
</div>
|
||||
<a id="author"></a>
|
||||
@@ -680,11 +679,89 @@
|
||||
}
|
||||
|
||||
|
||||
// 获取 rembg 模型
|
||||
async function get_rembg_models() {
|
||||
try {
|
||||
const response = await fetch(`${get_url()}/mixlab/folder_paths`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
type: 'rembg'
|
||||
})
|
||||
})
|
||||
|
||||
const data = await response.json()
|
||||
// console.log(data)
|
||||
return data.names
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
}
|
||||
|
||||
//自动抠图
|
||||
async function run_rembg(model, base64) {
|
||||
try {
|
||||
const response = await fetch(`${get_url()}/mixlab/rembg`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
model,
|
||||
base64
|
||||
})
|
||||
})
|
||||
|
||||
const data = await response.json()
|
||||
// console.log(data)
|
||||
return data.data
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
}
|
||||
|
||||
function convertImageToBlackBasedOnAlpha(image) {
|
||||
const canvas = document.createElement('canvas');
|
||||
const ctx = canvas.getContext('2d');
|
||||
|
||||
// Draw the image onto the canvas
|
||||
canvas.width = image.width;
|
||||
canvas.height = image.height;
|
||||
ctx.drawImage(image, 0, 0);
|
||||
|
||||
// Get the image data from the canvas
|
||||
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
||||
const pixels = imageData.data;
|
||||
|
||||
// Modify the RGB values based on the alpha channel
|
||||
for (let i = 0; i < pixels.length; i += 4) {
|
||||
const alpha = pixels[i + 3];
|
||||
if (alpha !== 0) {
|
||||
// Set non-transparent pixels to black
|
||||
pixels[i] = 0; // Red
|
||||
pixels[i + 1] = 0; // Green
|
||||
pixels[i + 2] = 0; // Blue
|
||||
}
|
||||
}
|
||||
|
||||
// Put the modified image data back onto the canvas
|
||||
ctx.putImageData(imageData, 0, 0);
|
||||
|
||||
// Convert the modified canvas to base64 data URL
|
||||
const base64ImageData = canvas.toDataURL('image/png'); // Replace 'png' with your desired image format
|
||||
|
||||
return base64ImageData;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 图像编辑
|
||||
async function editImage(image, data) {
|
||||
//判断mask是否有输出
|
||||
let isMask = data.options.hasMask;
|
||||
console.log(data)
|
||||
|
||||
//app
|
||||
document.body.querySelector('.app').style.display = 'none'
|
||||
document.body.querySelector('#author').style.display = 'none'
|
||||
@@ -693,21 +770,139 @@
|
||||
editor.style.display = 'block';
|
||||
const iframe = editor.querySelector('iframe');
|
||||
|
||||
const sleep = (t = 1000) => {
|
||||
return new Promise((res, rej) => {
|
||||
setTimeout(() => {
|
||||
res(true)
|
||||
}, t)
|
||||
})
|
||||
}
|
||||
|
||||
//清空 图层
|
||||
const removeAllLayer = () => {
|
||||
let Layers = iframe.contentWindow.Layers;
|
||||
//清空
|
||||
Layers.reset_layers()
|
||||
Layers.refresh_gui()
|
||||
}
|
||||
|
||||
// 复原
|
||||
const resetLayer = () => {
|
||||
let Layers = iframe.contentWindow.Layers;
|
||||
for (const layer of Layers.get_layers()) {
|
||||
layer.visible = true;
|
||||
}
|
||||
Layers.refresh_gui()
|
||||
}
|
||||
//取image
|
||||
const getImageBase64FromLayer = () => {
|
||||
let Layers = iframe.contentWindow.Layers;
|
||||
let tempCanvas = document.createElement("canvas");
|
||||
let tempCtx = tempCanvas.getContext("2d");
|
||||
let dim = Layers.get_dimensions();
|
||||
tempCanvas.width = dim.width;
|
||||
tempCanvas.height = dim.height;
|
||||
for (const layer of Layers.get_layers()) {
|
||||
if (layer.name === 'Image_' + data.id) {
|
||||
layer.visible = true;
|
||||
} else {
|
||||
layer.visible = false;
|
||||
}
|
||||
}
|
||||
Layers.refresh_gui()
|
||||
|
||||
Layers.convert_layers_to_canvas(tempCtx);
|
||||
return tempCanvas.toDataURL()
|
||||
}
|
||||
|
||||
//add mask
|
||||
const addMask = (id, name, image) => {
|
||||
let Layers = iframe.contentWindow.Layers;
|
||||
var new_mask_layer = {
|
||||
id,
|
||||
name,
|
||||
type: 'brush',
|
||||
data: [],
|
||||
render_function: ['brush', 'render'],
|
||||
width: image.naturalWidth || image.width,
|
||||
height: image.naturalHeight || image.height,
|
||||
|
||||
};
|
||||
Layers.insert(new_mask_layer);
|
||||
}
|
||||
|
||||
//add image
|
||||
const addImage = (id, name, image) => {
|
||||
let Layers = iframe.contentWindow.Layers;
|
||||
var new_mask_layer = {
|
||||
id,
|
||||
name,
|
||||
type: 'image',
|
||||
data: image,
|
||||
width: image.naturalWidth || image.width,
|
||||
height: image.naturalHeight || image.height,
|
||||
width_original: image.naturalWidth || image.width,
|
||||
height_original: image.naturalHeight || image.height,
|
||||
};
|
||||
Layers.insert(new_mask_layer);
|
||||
}
|
||||
|
||||
//默认的画笔size设置大
|
||||
// let inputSize = (iframe.contentDocument.getElementById('size')).querySelector('input');
|
||||
// inputSize.value=50;
|
||||
// (iframe.contentDocument.getElementById('size')).querySelector('.increase_number').click()
|
||||
|
||||
//自动抠图
|
||||
let autoMaskSelect = iframe.contentDocument.getElementById('automask_image_mixlab');
|
||||
const select = iframe.contentDocument.getElementById('automask_models_mixlab');
|
||||
if (select.children.length === 0) {
|
||||
let rembgModels = await get_rembg_models()
|
||||
// 遍历模型列表并创建选项
|
||||
for (const model of rembgModels) {
|
||||
const option = document.createElement('option');
|
||||
option.value = model;
|
||||
option.textContent = model;
|
||||
select.appendChild(option);
|
||||
}
|
||||
}
|
||||
|
||||
let autoMaskBtn = iframe.contentDocument.getElementById('automask_image_mixlab');
|
||||
if (!autoMaskBtn.getAttribute('init')) autoMaskBtn.addEventListener('click', async e => {
|
||||
//api请求
|
||||
let base64 = getImageBase64FromLayer()
|
||||
resetLayer()
|
||||
|
||||
let res = await run_rembg(select.value, base64)
|
||||
const match = res.match(/^data:image\/(\w+);base64,/);
|
||||
if (!match) {
|
||||
res = 'data:image/png;base64,' + res
|
||||
}
|
||||
|
||||
let image = await createImage(res)
|
||||
|
||||
let mb = convertImageToBlackBasedOnAlpha(image)
|
||||
let mask = await createImage(mb)
|
||||
let id = Layers.auto_increment;
|
||||
addImage(id, 'Mask_' + data.id + id, mask)
|
||||
})
|
||||
|
||||
autoMaskBtn.setAttribute('init', 1)
|
||||
let cancelImageBtn = iframe.contentDocument.getElementById('cancel_image_mixlab');
|
||||
cancelImageBtn.addEventListener('click', e => {
|
||||
if (!cancelImageBtn.getAttribute('init')) cancelImageBtn.addEventListener('click', e => {
|
||||
editor.style.display = 'none';
|
||||
document.body.querySelector('.app').style.display = 'flex'
|
||||
document.body.querySelector('#author').style.display = 'block'
|
||||
})
|
||||
|
||||
cancelImageBtn.setAttribute('init', 1)
|
||||
// 获取 id 为 "mix" 的 button 元素
|
||||
let saveImageBtn = iframe.contentDocument.getElementById('save_image_mixlab');
|
||||
saveImageBtn.addEventListener('click', async e => {
|
||||
saveImageBtn.style = `width: 98px;
|
||||
height: 36px;
|
||||
margin: 0 12px;
|
||||
background-color: var(--background-color-active);
|
||||
color: var(--text-color-active);`
|
||||
if (!saveImageBtn.getAttribute('init')) saveImageBtn.addEventListener('click', async e => {
|
||||
//保存,并更新图片
|
||||
e.preventDefault();
|
||||
|
||||
@@ -734,13 +929,7 @@
|
||||
Layers.convert_layers_to_canvas(tempCtx);
|
||||
|
||||
// 复原
|
||||
for (const layer of Layers.get_layers()) {
|
||||
layer.visible = true;
|
||||
}
|
||||
Layers.refresh_gui()
|
||||
|
||||
|
||||
|
||||
resetLayer()
|
||||
|
||||
// 获取图像数据
|
||||
const imageData = tempCtx.getImageData(0, 0, dim.width, dim.height);
|
||||
@@ -780,28 +969,22 @@
|
||||
|
||||
|
||||
//更新图片
|
||||
|
||||
const canvas = document.createElement("canvas");
|
||||
canvas.width = dim.width;
|
||||
canvas.height = dim.height;
|
||||
|
||||
const ctx = canvas.getContext('2d');
|
||||
|
||||
const defaultImage = new Image();
|
||||
defaultImage.src = data.options.defaultImage;
|
||||
defaultImage.onload = function () {
|
||||
ctx.drawImage(defaultImage, 0, 0, dim.width, dim.height);
|
||||
|
||||
// 绘制base64图片
|
||||
const base64Image = base64
|
||||
const base64ImageObj = new Image();
|
||||
base64ImageObj.onload = function () {
|
||||
ctx.globalCompositeOperation = 'destination-in';
|
||||
ctx.drawImage(base64ImageObj, 0, 0, dim.width, dim.height);
|
||||
image.src = canvas.toDataURL();
|
||||
};
|
||||
base64ImageObj.src = base64;
|
||||
};
|
||||
const defaultImage = await createImage(data.options.defaultImage)
|
||||
ctx.drawImage(defaultImage, 0, 0, dim.width, dim.height);
|
||||
|
||||
// 绘制base64图片
|
||||
// const base64Image = base64
|
||||
const base64ImageObj = await createImage(base64)
|
||||
ctx.globalCompositeOperation = 'destination-in';
|
||||
ctx.drawImage(base64ImageObj, 0, 0, dim.width, dim.height);
|
||||
image.src = canvas.toDataURL();
|
||||
|
||||
}
|
||||
|
||||
@@ -809,46 +992,32 @@
|
||||
document.body.querySelector('#author').style.display = 'block'
|
||||
})
|
||||
|
||||
saveImageBtn.setAttribute('init', 1)
|
||||
|
||||
var Layers = iframe.contentWindow.Layers;
|
||||
// console.log(Layers)
|
||||
|
||||
//判断是否已经存在
|
||||
let layers1 = Layers.get_layers()
|
||||
let layers1 = Layers.get_layers();
|
||||
|
||||
//通过layer.link.src 判断是否图片更新
|
||||
console.log(layers1.filter(l => l.name == 'Image_' + data.id)[0]?.link?.currentSrc !== image.src)
|
||||
if (layers1.filter(l => l.name == 'Image_' + data.id)[0]
|
||||
&& layers1.filter(l => l.name == 'Image_' + data.id)[0].link.src !== image.src) {
|
||||
//清空图层
|
||||
removeAllLayer();
|
||||
layers1 = [];
|
||||
await sleep()
|
||||
}
|
||||
console.log(layers1, layers1.length)
|
||||
if (!layers1.filter(l => l.name == 'Image_' + data.id)[0]) {
|
||||
var new_layer = {
|
||||
id: (Layers.get_layers()).length,
|
||||
name: 'Image_' + data.id,
|
||||
type: 'image',
|
||||
data: image,
|
||||
width: image.naturalWidth || image.width,
|
||||
height: image.naturalHeight || image.height,
|
||||
width_original: image.naturalWidth || image.width,
|
||||
height_original: image.naturalHeight || image.height,
|
||||
};
|
||||
|
||||
Layers.insert(new_layer);
|
||||
addImage(0, 'Image_' + data.id, image)
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (isMask) {
|
||||
if (!layers1.filter(l => l.name == 'Mask_' + data.id)[0]) {
|
||||
var new_mask_layer = {
|
||||
id: (Layers.get_layers()).length,
|
||||
name: 'Mask_' + data.id,
|
||||
type: 'brush',
|
||||
data: [],
|
||||
render_function: ['brush', 'render'],
|
||||
width: image.naturalWidth || image.width,
|
||||
height: image.naturalHeight || image.height,
|
||||
|
||||
};
|
||||
|
||||
Layers.insert(new_mask_layer);
|
||||
addMask(1, 'Mask_' + data.id, image)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1297,8 +1466,9 @@
|
||||
|
||||
if (hashId == window._appData.data[data.id].hashId) return
|
||||
|
||||
let base64 = await blobToBase64(fileBlob)
|
||||
|
||||
if (data.class_type === 'LoadImagesToBatch') {
|
||||
let base64 = await blobToBase64(fileBlob)
|
||||
createBase64ImageForLoadImageToBatch(imageElement, data.id, base64)
|
||||
} else {
|
||||
let { url, name } = await uploadImage(fileBlob);
|
||||
@@ -1307,6 +1477,14 @@
|
||||
window._appData.data[data.id].inputs.image = name;
|
||||
window._appData.data[data.id].hashId = hashId;
|
||||
console.log("上传的文件:", url, data.id, name);
|
||||
|
||||
//更换option里的default image
|
||||
window._appData.input = Array.from(window._appData.input, inp => {
|
||||
if (inp.id === data.id) {
|
||||
inp.options.defaultImage = base64;
|
||||
}
|
||||
return inp
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1608,9 +1786,9 @@
|
||||
//上传,返回url
|
||||
let { url, name } = await uploadImage(fileBlob, '.' + file.type.split('/')[1])
|
||||
|
||||
let base64 = await parseImageToBase64(url);
|
||||
|
||||
if (data.class_type === 'ImagesPrompt_') {
|
||||
//
|
||||
let base64 = await parseImageToBase64(url);
|
||||
uploadContainer.querySelector('.images_prompt_main').src = base64
|
||||
window._appData.data[data.id].inputs.image_base64 = base64;
|
||||
} else {
|
||||
@@ -1620,10 +1798,17 @@
|
||||
// 在这里可以对 Blob 对象进行进一步处理
|
||||
imageElement.src = url;
|
||||
|
||||
|
||||
if (isVideoUpload) {
|
||||
window._appData.data[data.id].inputs.video = name;
|
||||
} else {
|
||||
//更换option里的default image
|
||||
window._appData.input = Array.from(window._appData.input, inp => {
|
||||
if (inp.id === data.id) {
|
||||
inp.options.defaultImage = base64;
|
||||
}
|
||||
return inp
|
||||
})
|
||||
|
||||
window._appData.data[data.id].inputs.image = name;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import { app } from '../../../scripts/app.js'
|
||||
const repoOwner = 'shadowcz007' // 替换为仓库的所有者
|
||||
const repoName = 'comfyui-mixlab-nodes' // 替换为仓库的名称
|
||||
|
||||
const version = 'v0.27.0'
|
||||
const version = 'v0.28.1'
|
||||
|
||||
fetch(`https://api.github.com/repos/${repoOwner}/${repoName}/releases/latest`)
|
||||
.then(response => response.json())
|
||||
|
||||
@@ -46,7 +46,7 @@ async function get_llamafile_models () {
|
||||
})
|
||||
|
||||
const data = await response.json()
|
||||
console.log(data)
|
||||
// console.log(data)
|
||||
return data.names
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
@@ -539,10 +539,6 @@ injectCSS(`::-webkit-scrollbar {
|
||||
border-left: 2px solid var(--input-text);
|
||||
}
|
||||
|
||||
.litegraph{
|
||||
background: var(--bg-color)!important;
|
||||
}
|
||||
|
||||
`)
|
||||
|
||||
async function getCustomnodeMappings (mode = 'url') {
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"presets": ["@babel/preset-env"],
|
||||
"plugins": [
|
||||
["@babel/plugin-transform-runtime", {
|
||||
"regenerator": true
|
||||
}]
|
||||
]
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
# OS generated files #
|
||||
.DS_Store
|
||||
.DS_Store?
|
||||
._*
|
||||
.Trashes
|
||||
ehthumbs.db
|
||||
Thumbs.db
|
||||
nbproject/
|
||||
.idea/
|
||||
.git/
|
||||
.vscode
|
||||
/.project
|
||||
*.log
|
||||
|
||||
/node_modules/
|
||||
*.js.ignore
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
Copyright (c) ViliusL
|
||||
https://github.com/viliusle
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
@@ -1,63 +0,0 @@
|
||||
# miniPaint
|
||||
|
||||
Online image editor lets you create, edit images using HTML5 technologies.
|
||||
No need to buy, download, install or have obsolete flash. No ads.
|
||||
Key features: layers, filters, HTML5, open source, Photoshop alternative.
|
||||
|
||||
miniPaint operates directly in the browser. You can create images, paste from the clipboard (ctrl+v)
|
||||
or upload from the computer (using menu or drag & drop). Nothing will be sent to any server. Everything stays in your
|
||||
browser.
|
||||
|
||||
## URL:
|
||||
**https://viliusle.github.io/miniPaint/**
|
||||
|
||||
## Preview:
|
||||

|
||||
(generated using miniPaint)
|
||||
|
||||
**Change log:** [/miniPaint/releases](https://github.com/viliusle/miniPaint/releases)
|
||||
|
||||
## Browser Support
|
||||
- Chrome
|
||||
- Firefox
|
||||
- Opera
|
||||
- Edge
|
||||
- Safari
|
||||
|
||||
## Features
|
||||
|
||||
- **Files**: open images, directories, URL, data URL, drag and drop, save (PNG, JPG, BMP, WEBP, animated GIF, TIFF, JSON
|
||||
(layers data), print.
|
||||
- **Edit**: Undo, cut, copy, paste, selection, paste from clipboard.
|
||||
- **Image**: information, EXIF, trim, zoom, resize (Hermite resample, default resize), rotate, flip,
|
||||
color corrections (brightness, contrast, hue, saturation, luminance), auto adjust colors, grid, histogram, negative.
|
||||
- **Layers**: multiple layers system, differences, merge, flatten, Transparency support.
|
||||
- **Effects**: Black and White, Blur (box, Gaussian, stack, zoom), Bulge/Pinch, Denoise, Desaturate, Dither, Dot Screen,
|
||||
Edge, Emboss, Enrich, Gamma, Grains, GrayScale, Heatmap, JPG Compression, Mosaic, Oil, Sepia, Sharpen, Solarize,
|
||||
Tilt Shift, Vignette, Vibrance, Vintage, Blueprint, Night Vision, Pencil, also Instagram Filters: 1977, Aden, Clarendon,
|
||||
Gingham, Inkwell, Lo-fi, Toaster, Valencia, X-Pro II.
|
||||
- **Tools**: pencil, brush, magic wand, erase, fill, color picker, letters, crop, blur, sharpen, desaturate, clone,
|
||||
borders, sprites, key-points, color zoom, replace color, restore alpha, content fill.
|
||||
- **Help**: keyboard shortcuts, translations.
|
||||
|
||||
## Embed
|
||||
To embed this app in the other page, use this HTML code:
|
||||
|
||||
<iframe style="width:100%; height:1000px;" id="miniPaint" src="https://viliusle.github.io/miniPaint/" allow="camera"></iframe>
|
||||
|
||||
## Build instructions
|
||||
See [Wiki > Build instructions](https://github.com/viliusle/miniPaint/wiki/Build-instructions)
|
||||
|
||||
## Wiki
|
||||
See [Wiki](https://github.com/viliusle/miniPaint/wiki)
|
||||
|
||||
## Contributors
|
||||
<a align="center" href="https://github.com/viliusle/miniPaint/graphs/contributors">
|
||||
<img src="https://contrib.rocks/image?repo=viliusle/miniPaint" />
|
||||
</a>
|
||||
|
||||
## License
|
||||
MIT License
|
||||
|
||||
## Support
|
||||
Please use the GitHub issues for support, features, issues or use mail www.viliusl@gmail.com for contacts.
|
||||
@@ -1,15 +0,0 @@
|
||||
# Security Policy
|
||||
|
||||
## Supported Versions
|
||||
|
||||
Use this section to tell people about which versions of your project are
|
||||
currently being supported with security updates.
|
||||
|
||||
| Version | Supported |
|
||||
| ------- | ------------------ |
|
||||
| latest | :white_check_mark: |
|
||||
| < latest | :x: |
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
Please send details to www.viliusl@gmail.com
|
||||
@@ -34,7 +34,7 @@
|
||||
content="miniPaint is free online image editor using HTML5. Edit, adjust your images, add effects online in your browser, without installing anything..." />
|
||||
<meta property="og:site_name" content="miniPaint" />
|
||||
|
||||
<script src="dist/bundle.js"></script>
|
||||
<script src="dist/bundle.ejs"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
@@ -45,6 +45,15 @@
|
||||
<div class="submenu">
|
||||
<!-- <a class="logo" href="#">miniPaint</a> -->
|
||||
<div class="block attributes" id="action_attributes"></div>
|
||||
|
||||
<select id="automask_models_mixlab"></select>
|
||||
|
||||
<button id="automask_image_mixlab" type="button" style="width: 98px;
|
||||
height: 36px;margin-right: 18px;
|
||||
color: white;">
|
||||
RemoveBg
|
||||
</button>
|
||||
|
||||
<button id="cancel_image_mixlab" type="button" style="width: 98px;
|
||||
height: 36px;
|
||||
color: white;">
|
||||
@@ -55,6 +64,13 @@
|
||||
color: white;">
|
||||
Save
|
||||
</button>
|
||||
|
||||
<a
|
||||
target="_blank" href="https://discord.gg/xbP2GZF6gn"
|
||||
style="width: 98px;
|
||||
color: white;
|
||||
text-decoration: none;"> Help/帮助 </a>
|
||||
|
||||
<button class="undo_button" id="undo_button" type="button">
|
||||
<span class="sr_only">Undo</span>
|
||||
</button>
|
||||
@@ -82,7 +98,7 @@
|
||||
</div>
|
||||
|
||||
<div class="sidebar_right">
|
||||
<div class="preview block">
|
||||
<div class="preview block" style="display: none;">
|
||||
<h2 class="trn toggle" data-target="toggle_preview">Preview</h2>
|
||||
<div id="toggle_preview"></div>
|
||||
</div>
|
||||
@@ -92,7 +108,7 @@
|
||||
<div class="content" id="toggle_colors"></div>
|
||||
</div>
|
||||
|
||||
<div class="block" id="info_base">
|
||||
<div class="block" id="info_base" style="display: none;">
|
||||
<h2 class="trn toggle toggle-full" data-target="toggle_info">Information</h2>
|
||||
<div class="content" id="toggle_info"></div>
|
||||
</div>
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
{
|
||||
"name": "miniPaint",
|
||||
"short_name": "miniPaint",
|
||||
"start_url": "/",
|
||||
"display": "standalone",
|
||||
"orientation": "landscape",
|
||||
"background_color": "#666d6f",
|
||||
"description": "miniPaint is free online image editor using HTML5.",
|
||||
"icons": [
|
||||
{
|
||||
"src": "images/manifest/48x48.png",
|
||||
"sizes": "48x48",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "images/manifest/72x72.png",
|
||||
"sizes": "72x72",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "images/manifest/96x96.png",
|
||||
"sizes": "96x96",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "images/manifest/144x144.png",
|
||||
"sizes": "144x144",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "images/manifest/168x168.png",
|
||||
"sizes": "168x168",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "images/manifest/192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png"
|
||||
}
|
||||
]
|
||||
}
|
||||
-10750
File diff suppressed because it is too large
Load Diff
@@ -1,51 +0,0 @@
|
||||
{
|
||||
"name": "miniPaint",
|
||||
"version": "4.14.2",
|
||||
"author": "Vilius L.",
|
||||
"description": "Online graphics editing tool lets create, edit images using HTML5 technologies.",
|
||||
"keywords": [
|
||||
"canvas",
|
||||
"drawing",
|
||||
"paint",
|
||||
"layers",
|
||||
"effects"
|
||||
],
|
||||
"scripts": {
|
||||
"server": "webpack serve --mode development --env development --open",
|
||||
"dev": "webpack --mode development",
|
||||
"build": "webpack --mode production"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/viliusle/miniPaint"
|
||||
},
|
||||
"homepage": "https://github.com/viliusle/miniPaint",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.14.5",
|
||||
"@babel/plugin-transform-runtime": "^7.14.5",
|
||||
"@babel/preset-env": "^7.14.5",
|
||||
"babel-loader": "^8.2.2",
|
||||
"css-loader": "^5.2.6",
|
||||
"source-map-loader": "^3.0.0",
|
||||
"style-loader": "^2.0.0",
|
||||
"webpack": "^5.76.0",
|
||||
"webpack-cli": "^4.7.2",
|
||||
"webpack-dev-server": "^4.3.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.14.5",
|
||||
"alertifyjs": "^1.13.1",
|
||||
"blueimp-canvas-to-blob": "^3.28.0",
|
||||
"exif-js": "^2.3.0",
|
||||
"file-saver": "^2.0.5",
|
||||
"fuzzysort": "^1.1.4",
|
||||
"gif.js.optimized": "^1.0.1",
|
||||
"hermite-resize": "git+https://github.com/viliusle/Hermite-resize.git",
|
||||
"jquery": "^3.5.1",
|
||||
"pica": "^7.0.0",
|
||||
"semver-compare": "^1.0.0",
|
||||
"uuid": "^8.3.2",
|
||||
"webfontloader": "^1.6.28"
|
||||
}
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
|
||||
//IMPORTANT - this file is not used !!!
|
||||
|
||||
|
||||
// use a cacheName for cache versioning
|
||||
var cacheName = 'v1:static';
|
||||
|
||||
// during the install phase you usually want to cache static assets
|
||||
self.addEventListener('install', function(e) {
|
||||
// once the SW is installed, go ahead and fetch the resources to make this work offline
|
||||
e.waitUntil(
|
||||
caches.open(cacheName).then(function(cache) {
|
||||
return cache.addAll([
|
||||
'./',
|
||||
'./dist/bundle.js',
|
||||
'./images/favicon.png',
|
||||
'./images/logo.svg',
|
||||
'./images/logo-colors.png',
|
||||
'./images/icons/animation.svg',
|
||||
'./images/icons/blur.svg',
|
||||
'./images/icons/bold.svg',
|
||||
'./images/icons/brush.svg',
|
||||
'./images/icons/bulge_pinch.svg',
|
||||
'./images/icons/clone.svg',
|
||||
'./images/icons/crop.svg',
|
||||
'./images/icons/delete.svg',
|
||||
'./images/icons/desaturate.svg',
|
||||
'./images/icons/erase.svg',
|
||||
'./images/icons/external.png',
|
||||
'./images/icons/fill.svg',
|
||||
'./images/icons/gradient.png',
|
||||
'./images/icons/grid.png',
|
||||
'./images/icons/italic.svg',
|
||||
'./images/icons/magic_erase.svg',
|
||||
'./images/icons/media.svg',
|
||||
'./images/icons/menu.svg',
|
||||
'./images/icons/pencil.svg',
|
||||
'./images/icons/pick_color.svg',
|
||||
'./images/icons/refresh.svg',
|
||||
'./images/icons/select.svg',
|
||||
'./images/icons/selection.svg',
|
||||
'./images/icons/shape.svg',
|
||||
'./images/icons/sharpen.svg',
|
||||
'./images/icons/strikethrough.svg',
|
||||
'./images/icons/text.svg',
|
||||
'./images/icons/underline.svg',
|
||||
'./images/icons/view.svg'
|
||||
]).then(function() {
|
||||
self.skipWaiting();
|
||||
});
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
// when the browser fetches a url
|
||||
self.addEventListener('fetch', function(event) {
|
||||
// either respond with the cached object or go ahead and fetch the actual url
|
||||
event.respondWith(
|
||||
caches.match(event.request).then(function(response) {
|
||||
if (response) {
|
||||
// retrieve from cache
|
||||
return response;
|
||||
}
|
||||
// fetch as normal
|
||||
return fetch(event.request);
|
||||
})
|
||||
);
|
||||
});
|
||||
@@ -1,56 +0,0 @@
|
||||
var webpack = require('webpack');
|
||||
var path = require('path');
|
||||
|
||||
module.exports = {
|
||||
entry: [
|
||||
'./src/js/main.js',
|
||||
],
|
||||
output: {
|
||||
path: path.resolve(__dirname, 'dist'),
|
||||
filename: 'bundle.js',
|
||||
publicPath: '/dist/'
|
||||
},
|
||||
resolve: {
|
||||
extensions: ['.js', '.css'],
|
||||
alias: {
|
||||
Utilities: path.resolve(__dirname, './../node_modules/')
|
||||
}
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.css$/,
|
||||
use: [
|
||||
'style-loader',
|
||||
{
|
||||
loader: 'css-loader',
|
||||
options: {url: false}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
test: /\.js$/,
|
||||
exclude: /(node_modules|bower_components)/,
|
||||
use: ['babel-loader']
|
||||
},
|
||||
]
|
||||
},
|
||||
plugins: [
|
||||
new webpack.ProvidePlugin({
|
||||
$: "jquery",
|
||||
jQuery: "jquery",
|
||||
"window.jQuery": "jquery"
|
||||
}),
|
||||
new webpack.DefinePlugin({
|
||||
VERSION: JSON.stringify(require("./package.json").version)
|
||||
}),
|
||||
],
|
||||
devtool: "cheap-module-source-map",
|
||||
devServer: {
|
||||
// host: '0.0.0.0',
|
||||
//contentBase: "./",
|
||||
static: {
|
||||
directory: path.resolve(__dirname, "./"),
|
||||
},
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user