Files

185 lines
8.0 KiB
Python

import torch
from PIL import Image, ImageChops
from .imagefunc import log, tensor2pil, pil2tensor, image2mask
from .imagefunc import get_gray_average, calculate_shadow_highlight_level, luminance_keyer
def norm_value(value):
if value < 0.01:
value = 0.01
if value > 0.99:
value = 0.99
return value
class ShadowAndHighlightMask:
def __init__(self):
self.NODE_NAME = 'Shadow & Highlight Mask'
@classmethod
def INPUT_TYPES(self):
return {
"required": {
"image": ("IMAGE", ),
"shadow_level_offset": ("INT", {"default": 0, "min": -99, "max": 99, "step": 1}),
"shadow_range": ("FLOAT", {"default": 0.25, "min": 0.01, "max": 0.99, "step": 0.01}),
"highlight_level_offset": ("INT", {"default": 0, "min": -99, "max": 99, "step": 1}),
"highlight_range": ("FLOAT", {"default": 0.25, "min": 0.01, "max": 0.99, "step": 0.01}),
},
"optional": {
"mask": ("MASK",), #
}
}
RETURN_TYPES = ("MASK", "MASK")
RETURN_NAMES = ("shadow_mask", "highlight_mask")
FUNCTION = 'shadow_and_highlight_mask'
CATEGORY = '😺dzNodes/LayerMask'
def shadow_and_highlight_mask(self, image,
shadow_level_offset, shadow_range,
highlight_level_offset, highlight_range,
mask=None
):
ret_shadow_masks = []
ret_highlight_masks = []
input_images = []
input_masks = []
for i in image:
input_images.append(torch.unsqueeze(i, 0))
m = tensor2pil(i)
if m.mode == 'RGBA':
input_masks.append(m.split()[-1])
else:
input_masks.append(Image.new('L', size=m.size, color='white'))
if mask is not None:
if mask.dim() == 2:
mask = torch.unsqueeze(mask, 0)
input_masks = []
for m in mask:
input_masks.append(tensor2pil(torch.unsqueeze(m, 0)).convert('L'))
max_batch = max(len(input_images), len(input_masks))
for i in range(max_batch):
_image = input_images[i] if i < len(input_images) else input_images[-1]
_image = tensor2pil(_image).convert('RGB')
_mask = input_masks[i] if i < len(input_masks) else input_masks[-1]
avg_gray = get_gray_average(_image, _mask)
shadow_level, highlight_level = calculate_shadow_highlight_level(avg_gray)
shadow_low_threshold = (shadow_level + shadow_level_offset) / 100 + shadow_range / 2
shadow_low_threshold = norm_value(shadow_low_threshold)
shadow_high_threshold = (shadow_level + shadow_level_offset) / 100 - shadow_range / 2
shadow_high_threshold = norm_value(shadow_high_threshold)
_shadow_mask = luminance_keyer(_image, shadow_low_threshold, shadow_high_threshold)
highlight_low_threshold = (highlight_level + highlight_level_offset) / 100 - highlight_range / 2
highlight_low_threshold = norm_value(highlight_low_threshold)
highlight_high_threshold = (highlight_level + highlight_level_offset) / 100 + highlight_range / 2
highlight_high_threshold = norm_value(highlight_high_threshold)
_highlight_mask = luminance_keyer(_image, highlight_low_threshold, highlight_high_threshold)
black = Image.new('L', size=_image.size, color='black')
_mask = ImageChops.invert(_mask)
_shadow_mask.paste(black, mask=_mask)
_highlight_mask.paste(black, mask=_mask)
ret_shadow_masks.append(image2mask(_shadow_mask))
ret_highlight_masks.append(image2mask(_highlight_mask))
log(f"{self.NODE_NAME} Processed {len(ret_shadow_masks)} image(s).", message_type='finish')
return (torch.cat(ret_shadow_masks, dim=0),torch.cat(ret_highlight_masks, dim=0),)
class LS_ShadowAndHighlightMaskV2:
def __init__(self):
self.NODE_NAME = 'Shadow Highlight Mask V2'
@classmethod
def INPUT_TYPES(self):
return {
"required": {
"image": ("IMAGE", ),
"shadow_level_offset": ("INT", {"default": 0, "min": -99, "max": 99, "step": 1}),
"shadow_range": ("FLOAT", {"default": 0.25, "min": 0.01, "max": 0.99, "step": 0.01}),
"highlight_level_offset": ("INT", {"default": 0, "min": -99, "max": 99, "step": 1}),
"highlight_range": ("FLOAT", {"default": 0.25, "min": 0.01, "max": 0.99, "step": 0.01}),
},
"optional": {
"mask": ("MASK",), #
}
}
RETURN_TYPES = ("MASK", "MASK")
RETURN_NAMES = ("shadow_mask", "highlight_mask")
FUNCTION = 'shadow_and_highlight_mask_v2'
CATEGORY = '😺dzNodes/LayerMask'
def shadow_and_highlight_mask_v2(self, image,
shadow_level_offset, shadow_range,
highlight_level_offset, highlight_range,
mask=None
):
ret_shadow_masks = []
ret_highlight_masks = []
input_images = []
input_masks = []
for i in image:
input_images.append(torch.unsqueeze(i, 0))
m = tensor2pil(i)
if m.mode == 'RGBA':
input_masks.append(m.split()[-1])
else:
input_masks.append(Image.new('L', size=m.size, color='white'))
if mask is not None:
if mask.dim() == 2:
mask = torch.unsqueeze(mask, 0)
input_masks = []
for m in mask:
input_masks.append(tensor2pil(torch.unsqueeze(m, 0)).convert('L'))
max_batch = max(len(input_images), len(input_masks))
for i in range(max_batch):
_image = input_images[i] if i < len(input_images) else input_images[-1]
_image = tensor2pil(_image).convert('RGB')
_mask = input_masks[i] if i < len(input_masks) else input_masks[-1]
avg_gray = get_gray_average(_image, _mask)
shadow_level, highlight_level = calculate_shadow_highlight_level(avg_gray)
shadow_low_threshold = (shadow_level + shadow_level_offset) / 100 + shadow_range / 2
shadow_low_threshold = norm_value(shadow_low_threshold)
shadow_high_threshold = (shadow_level + shadow_level_offset) / 100 - shadow_range / 2
shadow_high_threshold = norm_value(shadow_high_threshold)
_shadow_mask = luminance_keyer(_image, shadow_low_threshold, shadow_high_threshold)
highlight_low_threshold = (highlight_level + highlight_level_offset) / 100 - highlight_range / 2
highlight_low_threshold = norm_value(highlight_low_threshold)
highlight_high_threshold = (highlight_level + highlight_level_offset) / 100 + highlight_range / 2
highlight_high_threshold = norm_value(highlight_high_threshold)
_highlight_mask = luminance_keyer(_image, highlight_low_threshold, highlight_high_threshold)
black = Image.new('L', size=_image.size, color='black')
_mask = ImageChops.invert(_mask)
_shadow_mask.paste(black, mask=_mask)
_highlight_mask.paste(black, mask=_mask)
ret_shadow_masks.append(image2mask(_shadow_mask))
ret_highlight_masks.append(image2mask(_highlight_mask))
log(f"{self.NODE_NAME} Processed {len(ret_shadow_masks)} image(s).", message_type='finish')
return (torch.cat(ret_shadow_masks, dim=0),torch.cat(ret_highlight_masks, dim=0),)
NODE_CLASS_MAPPINGS = {
"LayerMask: Shadow & Highlight Mask": ShadowAndHighlightMask,
"LayerMask: ShadowHighlightMaskV2": LS_ShadowAndHighlightMaskV2
}
NODE_DISPLAY_NAME_MAPPINGS = {
"LayerMask: Shadow & Highlight Mask": "LayerMask: Shadow & Highlight Mask",
"LayerMask: ShadowHighlightMaskV2": "LayerMask: Shadow Highlight Mask V2"
}