Files

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, )