Files
Nlar-ComfyUI_CartoonSegment…/segmentation.py
T

192 lines
6.6 KiB
Python

import os
import PIL
import numpy as np
import torch
from PIL import ImageSequence, ImageOps
from PIL.Image import Image
import mmcv
import cv2
import folder_paths as folder_paths
from custom_nodes.ComfyUI_CartoonSegmentation.CartoonSegmentation.anime_3dkenburns.kenburns_effect import build_kenburns_cfg, KenBurnsPipeline, npyframes2video
from custom_nodes.ComfyUI_CartoonSegmentation.CartoonSegmentation.animeinsseg import AnimeInsSeg, AnimeInstances
from custom_nodes.ComfyUI_CartoonSegmentation.CartoonSegmentation.utils.constants import get_color
def pil_image_to_image(input_image):
output_images = []
for i in ImageSequence.Iterator(input_image):
i = ImageOps.exif_transpose(i)
image = i.convert("RGB")
image = np.array(image).astype(np.float32) / 255.0
image = torch.from_numpy(image)[None,]
if 'A' in i.getbands():
mask = np.array(i.getchannel('A')).astype(np.float32) / 255.0
mask = 1. - torch.from_numpy(mask)
else:
mask = torch.zeros((64, 64), dtype=torch.float32, device="cpu")
output_images.append(image)
if len(output_images) > 1:
output_image = torch.cat(output_images, dim=0)
else:
output_image = output_images[0]
return output_image
def nparray_to_image(nparr):
pil_img = PIL.Image.fromarray(nparr)
# pil_img.save("output/testing_image.png")
return pil_image_to_image(pil_img)
def get_drawed(drawed, img, instances, target_mask_count):
im_h, im_w = img.shape[:2]
mask_array = None
target_mask = None
counter = 1
try:
for ii, (xywh, mask) in enumerate(zip(instances.bboxes, instances.masks)):
color = get_color(ii)
mask_alpha = 0.5
linewidth = max(round(sum(img.shape) / 2 * 0.003), 2)
# draw bbox
p1, p2 = (int(xywh[0]), int(xywh[1])), (int(xywh[2] + xywh[0]), int(xywh[3] + xywh[1]))
cv2.rectangle(drawed, p1, p2, color, thickness=linewidth, lineType=cv2.LINE_AA)
# draw mask
p = mask.astype(np.float32)
mask_tensor = torch.from_numpy(p)
img_tensor = mask_tensor.reshape((-1, 1, mask.shape[-2], mask.shape[-1])).movedim(1, -1).expand(-1, -1, -1, 3)
if mask_array is None:
mask_array = img_tensor
else:
mask_array = torch.cat((mask_array, img_tensor), 0)
if counter == target_mask_count:
target_mask = img_tensor
counter += 1
# torch.from_numpy(p).resize(1, im_h, im_w))
blend_mask = np.full((im_h, im_w, 3), color, dtype=np.float32)
alpha_msk = (mask_alpha * p)[..., None]
alpha_ori = 1 - alpha_msk
drawed = drawed * alpha_ori + alpha_msk * blend_mask
drawed = drawed.astype(np.uint8)
except Exception as e:
print("Nothing found to segment")
return drawed, mask_array, target_mask
class AnimeSegmentation:
@classmethod
def INPUT_TYPES(cls):
return {"required": {
"model": (folder_paths.get_filename_list("cartoon_seg_ckpt"), {"default": "rtmdetl_e60.ckpt"},),
"image": ("IMAGE", {}),
"mask_thres": ("FLOAT", {"default": 0.3, "min": 0.0, "max": 4.0, "step": 0.01}),
"instance_thres": ("FLOAT", {"default": 0.3, "min": 0.0, "max": 4.0, "step": 0.01}),
"character_output": ("INT", {"default": 1, "min": 1, "max": 1000}),
},
}
RETURN_TYPES = ('IMAGE', "IMAGE", "IMAGE")
# OUTPUT_IS_LIST = (False, False, True,)
RETURN_NAMES = ('display image', 'character image mask', 'all image masks')
FUNCTION = 'ani_segmentation'
CATEGORY = "CartoonSegmentation"
def ani_segmentation(self, model, image, mask_thres, instance_thres, character_output):
ckpt = os.path.join(folder_paths.folder_names_and_paths["cartoon_seg_ckpt"][0][0], model)
refine_kwargs = {'refine_method': 'refinenet_isnet'} # set to None if not using refinenet
num_img = np.flip((image.squeeze(0).numpy() * 255), axis=2)
net = AnimeInsSeg(ckpt, mask_thr=mask_thres, refine_kwargs=refine_kwargs)
instances: AnimeInstances = net.infer(
num_img,
output_type='numpy',
pred_score_thr=instance_thres
)
drawed, mask_images, character_mask = get_drawed(num_img.copy(), num_img, instances, character_output)
# img = PIL.Image.fromarray(drawed[..., ::-1])
if drawed is None:
drawed = image
seg_img = pil_image_to_image(PIL.Image.fromarray(drawed[..., ::-1]))
if mask_images is None:
mask_images = seg_img
targeted_image = seg_img
return seg_img, character_mask, mask_images
class KenBurns_Processor:
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"kb_config": ("kb_config", {}),
"output_video_name": ("STRING", {"default": "kenburns.mp4"}),
}
}
FUNCTION = "perform_3dkb"
CATEGORY = "CartoonSegmentation"
RETURN_TYPES = ("IMAGE", )
RETURN_NAME = ("Output Preview", )
OUTPUT_NODE = True
def perform_3dkb(self, kb_config, output_video_name):
(cfg_full_path, det_ckpt, image_path, verbose, ) = kb_config
video_path_output = f"{folder_paths.output_directory.rstrip('/')}/{output_video_name}"
kbc = build_kenburns_cfg(cfg_full_path)
kbc.det_ckpt = det_ckpt
kpipe = KenBurnsPipeline(kbc)
img = mmcv.imread(image_path)
kcfg = kpipe.generate_kenburns_config(img, verbose=verbose, savep=video_path_output)
if verbose:
stage_instance_vis = kcfg.instances.draw_instances(img, draw_tags=False)
cv2.imwrite('tmp_stage_instance.png', stage_instance_vis)
cv2.imwrite('tmp_stage_depth_coarse.png', kcfg.stage_depth_coarse)
cv2.imwrite('tmp_stage_depth_adjusted.png', kcfg.stage_depth_adjusted)
cv2.imwrite('tmp_stage_depth_final.png', kcfg.stage_depth_final)
npy_frame_list = kpipe.autozoom(kcfg, verbose=verbose)
if verbose:
for ii, inpainted_img in enumerate(kcfg.stage_inpainted_imgs):
cv2.imwrite(f'tmp_stage_inpaint_{ii}.png', inpainted_img)
for ii, mask in enumerate(kcfg.stage_inpainted_masks):
cv2.imwrite(f'tmp_stage_inpaint_mask_{ii}.png', mask)
npyframes2video(npy_frame_list, video_path_output)
np_arry_sample = npy_frame_list[int(len(npy_frame_list) / 2)]
img = nparray_to_image(np_arry_sample)
return (img, )