181 lines
5.9 KiB
Python
181 lines
5.9 KiB
Python
import torch
|
|
|
|
from PIL import Image
|
|
from PIL import ImageDraw
|
|
from PIL import ImageFilter
|
|
|
|
import comfy
|
|
import nodes
|
|
import folder_paths
|
|
|
|
from bmab import utils
|
|
from bmab.nodes import BMABBind
|
|
from bmab.utils.color import apply_color_correction
|
|
|
|
|
|
def process_img2img(bind: BMABBind, image, params):
|
|
steps, cfg, sampler_name, scheduler, denoise = params['steps'], params['cfg_scale'], params['sampler_name'], params['scheduler'], params['denoise']
|
|
pixels = utils.pil2tensor(image.convert('RGB'))
|
|
latent = dict(samples=bind.vae.encode(pixels))
|
|
samples = nodes.common_ksampler(bind.model, bind.seed, steps, cfg, sampler_name, scheduler, bind.positive, bind.negative, latent, denoise=denoise)[0]
|
|
latent = bind.vae.decode(samples["samples"])
|
|
result = utils.tensor2pil(latent)
|
|
return result
|
|
|
|
|
|
def process_img2img_with_mask(bind: BMABBind, image, params, mask=None, box=None):
|
|
width, height, padding, dilation = params['width'], params['height'], params['padding'], params['dilation']
|
|
|
|
if box is None:
|
|
box = mask.getbbox()
|
|
if box is None:
|
|
return image
|
|
|
|
if mask is None:
|
|
mask = Image.new('L', image.size, 0)
|
|
dr = ImageDraw.Draw(mask, 'L')
|
|
dr.rectangle(box, fill=255)
|
|
|
|
x1, y1, x2, y2 = tuple(int(x) for x in box)
|
|
|
|
cbx = utils.get_box_with_padding(image, (x1, y1, x2, y2), padding)
|
|
cropped = image.crop(cbx)
|
|
resized = utils.resize_and_fill(cropped, width, height)
|
|
processed = process_img2img(bind, resized, params)
|
|
processed = apply_color_correction(resized, processed)
|
|
|
|
iratio = width / height
|
|
cratio = cropped.width / cropped.height
|
|
if iratio < cratio:
|
|
ratio = cropped.width / width
|
|
processed = processed.resize((int(processed.width * ratio), int(processed.height * ratio)))
|
|
y0 = (processed.height - cropped.height) // 2
|
|
processed = processed.crop((0, y0, cropped.width, y0 + cropped.height))
|
|
else:
|
|
ratio = cropped.height / height
|
|
processed = processed.resize((int(processed.width * ratio), int(processed.height * ratio)))
|
|
x0 = (processed.width - cropped.width) // 2
|
|
processed = processed.crop((x0, 0, x0 + cropped.width, cropped.height))
|
|
|
|
img = image.copy()
|
|
img.paste(processed, (cbx[0], cbx[1]))
|
|
|
|
pil_mask = utils.dilate_mask(mask, dilation)
|
|
blur = ImageFilter.GaussianBlur(dilation)
|
|
blur_mask = pil_mask.filter(blur)
|
|
|
|
image.paste(img, (0, 0), mask=blur_mask)
|
|
return image
|
|
|
|
|
|
def load_controlnet(control_net_name):
|
|
controlnet_path = folder_paths.get_full_path('controlnet', control_net_name)
|
|
controlnet = comfy.controlnet.load_controlnet(controlnet_path)
|
|
return controlnet
|
|
|
|
|
|
def apply_controlnet(control_net_name, positive, negative, strength, start_percent, end_percent, image):
|
|
control_net = load_controlnet(control_net_name)
|
|
|
|
control_hint = image.movedim(-1, 1)
|
|
cnets = {}
|
|
|
|
out = []
|
|
for conditioning in [positive, negative]:
|
|
c = []
|
|
for t in conditioning:
|
|
d = t[1].copy()
|
|
|
|
prev_cnet = d.get('control', None)
|
|
if prev_cnet in cnets:
|
|
c_net = cnets[prev_cnet]
|
|
else:
|
|
c_net = control_net.copy().set_cond_hint(control_hint, strength, (start_percent, end_percent))
|
|
c_net.set_previous_controlnet(prev_cnet)
|
|
cnets[prev_cnet] = c_net
|
|
|
|
d['control'] = c_net
|
|
d['control_apply_to_uncond'] = False
|
|
n = [t[0], d]
|
|
c.append(n)
|
|
out.append(c)
|
|
return out[0], out[1]
|
|
|
|
|
|
def preprocess(image, mask):
|
|
mask = torch.nn.functional.interpolate(mask.reshape((-1, 1, mask.shape[-2], mask.shape[-1])), size=(image.shape[1], image.shape[2]), mode="bilinear")
|
|
mask = mask.movedim(1, -1).expand((-1, -1, -1, 3))
|
|
image = image.clone()
|
|
image[mask > 0.5] = -1.0
|
|
return image
|
|
|
|
|
|
def process_img2img_with_controlnet(bind: BMABBind, image, params, controlnet_name, mask=None):
|
|
steps, cfg, sampler_name, scheduler, denoise = params['steps'], params['cfg_scale'], params['sampler_name'], params['scheduler'], params['denoise']
|
|
|
|
pixels = utils.pil2tensor(image.convert('RGB'))
|
|
latent = dict(samples=bind.vae.encode(pixels))
|
|
|
|
if mask is not None:
|
|
mask_pixels = utils.pil2tensor(mask.convert('RGB'))
|
|
cn_pixels = preprocess(pixels, mask_pixels[:, :, :, 0])
|
|
else:
|
|
cn_pixels = pixels
|
|
positive, negative = apply_controlnet(controlnet_name, bind.positive, bind.negative, 1.0, 0.0, 1.0, cn_pixels)
|
|
|
|
samples = nodes.common_ksampler(bind.model, bind.seed, steps, cfg, sampler_name, scheduler, positive, negative, latent, denoise=denoise)[0]
|
|
latent = bind.vae.decode(samples["samples"])
|
|
result = utils.tensor2pil(latent)
|
|
return result
|
|
|
|
|
|
def process_img2img_with_controlnet_mask(bind: BMABBind, controlnet_name, image, params, mask=None, box=None):
|
|
width, height, padding, dilation = params['width'], params['height'], params['padding'], params['dilation']
|
|
|
|
if box is None:
|
|
box = mask.getbbox()
|
|
if box is None:
|
|
return image
|
|
|
|
if mask is None:
|
|
mask = Image.new('L', image.size, 0)
|
|
dr = ImageDraw.Draw(mask, 'L')
|
|
dr.rectangle(box, fill=255)
|
|
|
|
x1, y1, x2, y2 = tuple(int(x) for x in box)
|
|
|
|
cbx = utils.get_box_with_padding(image, (x1, y1, x2, y2), padding)
|
|
cropped = image.crop(cbx)
|
|
resized = utils.resize_and_fill(cropped, width, height)
|
|
processed = process_img2img_with_controlnet(bind, resized, params, controlnet_name, mask=mask)
|
|
processed = apply_color_correction(resized, processed)
|
|
|
|
iratio = width / height
|
|
cratio = cropped.width / cropped.height
|
|
if iratio < cratio:
|
|
ratio = cropped.width / width
|
|
processed = processed.resize((int(processed.width * ratio), int(processed.height * ratio)))
|
|
y0 = (processed.height - cropped.height) // 2
|
|
processed = processed.crop((0, y0, cropped.width, y0 + cropped.height))
|
|
else:
|
|
ratio = cropped.height / height
|
|
processed = processed.resize((int(processed.width * ratio), int(processed.height * ratio)))
|
|
x0 = (processed.width - cropped.width) // 2
|
|
processed = processed.crop((x0, 0, x0 + cropped.width, cropped.height))
|
|
|
|
img = image.copy()
|
|
img.paste(processed, (cbx[0], cbx[1]))
|
|
|
|
pil_mask = utils.dilate_mask(mask, dilation)
|
|
blur = ImageFilter.GaussianBlur(dilation)
|
|
blur_mask = pil_mask.filter(blur)
|
|
|
|
image.paste(img, (0, 0), mask=blur_mask)
|
|
return image
|
|
|
|
|
|
|
|
|
|
|
|
|