169 lines
6.0 KiB
Markdown
169 lines
6.0 KiB
Markdown
|
|
# Documentation
|
|
- Class name: ChameleonMask
|
|
- Category: Bmad/CV/C.Photography
|
|
- Output node: False
|
|
|
|
ChameleonMask节点旨在基于特定标准或输入动态调整或修改图像蒙版,实现灵活且上下文敏感的蒙版处理。
|
|
|
|
# Input types
|
|
## Required
|
|
- dst
|
|
- 目标图像,即蒙版将应用的位置,作为最终输出的画布。
|
|
- Comfy dtype: IMAGE
|
|
- Python dtype: torch.Tensor
|
|
- src
|
|
- 源图像,用于生成蒙版,是蒙版创建的主要输入。
|
|
- Comfy dtype: IMAGE
|
|
- Python dtype: torch.Tensor
|
|
- thresh_blur
|
|
- 定义蒙版模糊的阈值,影响蒙版边缘的平滑程度。
|
|
- Comfy dtype: INT
|
|
- Python dtype: int
|
|
- close_dist
|
|
- 指定蒙版闭合操作的距离,影响蒙版的紧凑性。
|
|
- Comfy dtype: INT
|
|
- Python dtype: int
|
|
- open_dist
|
|
- 确定开放操作的距离,影响蒙版的粒度。
|
|
- Comfy dtype: INT
|
|
- Python dtype: int
|
|
- size_dist
|
|
- 控制蒙版的尺寸分布调整,影响整体蒙版结构。
|
|
- Comfy dtype: INT
|
|
- Python dtype: int
|
|
- mask_blur
|
|
- 设置应用于蒙版的模糊程度,修改其柔和度和透明度。
|
|
- Comfy dtype: INT
|
|
- Python dtype: int
|
|
- contrast_adjust
|
|
- 调整蒙版的对比度,增强或减弱蒙版的视觉强度。
|
|
- Comfy dtype: FLOAT
|
|
- Python dtype: float
|
|
- mode
|
|
- 指定蒙版生成的操作模式,提供不同的蒙版创建策略。
|
|
- Comfy dtype: COMBO[STRING]
|
|
- Python dtype: str
|
|
- output_format
|
|
- 确定输出图像的格式,影响蒙版的视觉呈现。
|
|
- Comfy dtype: COMBO[STRING]
|
|
- Python dtype: str
|
|
|
|
## Optional
|
|
- optional_roi_mask
|
|
- 可选的感兴趣区域蒙版,可用于进一步优化蒙版生成过程。
|
|
- Comfy dtype: IMAGE
|
|
- Python dtype: torch.Tensor
|
|
|
|
# Output types
|
|
- image
|
|
- 输出是一个应用了蒙版的图像,展示蒙版操作的结果。
|
|
- Comfy dtype: IMAGE
|
|
- Python dtype: torch.Tensor
|
|
|
|
|
|
## Usage tips
|
|
- Infra type: `CPU`
|
|
- Common nodes: unknown
|
|
|
|
|
|
## Source code
|
|
```python
|
|
class ChameleonMask: # wtf would I name this node as?
|
|
mode_func_map = {
|
|
"GRAY": lambda i: cv.cvtColor(i, cv.COLOR_BGR2GRAY),
|
|
"VALUE": lambda i: cv.cvtColor(i, cv.COLOR_RGB2HSV)[:, :, 2],
|
|
"LIGHTNESS": lambda i: cv.cvtColor(i, cv.COLOR_RGB2HLS)[:, :, 1],
|
|
|
|
# not sure if these would be useful, but costs nothing to leave them here
|
|
"HUE": lambda i: cv.cvtColor(i, cv.COLOR_RGB2HSV)[:, :, 0],
|
|
"SATURATION (HSV)": lambda i: cv.cvtColor(i, cv.COLOR_RGB2HSV)[:, :, 1],
|
|
"SATURATION (HSL)": lambda i: cv.cvtColor(i, cv.COLOR_RGB2HLS)[:, :, 2],
|
|
}
|
|
modes = list(mode_func_map.keys())
|
|
|
|
@classmethod
|
|
def INPUT_TYPES(s):
|
|
return {
|
|
"required": {
|
|
"dst": ("IMAGE",),
|
|
"src": ("IMAGE",),
|
|
"thresh_blur": ("INT", {"default": 30, "min": 2, "step": 2}),
|
|
"close_dist": ("INT", {"default": 32, "min": 0, "step": 1}),
|
|
"open_dist": ("INT", {"default": 32, "min": 0, "step": 1}),
|
|
"size_dist": ("INT", {"default": 8, "min": -99999, "step": 1}),
|
|
"mask_blur": ("INT", {"default": 64, "min": 0, "step": 2}),
|
|
"contrast_adjust": ("FLOAT", {"default": 2.4, "min": 0, "max": 20, "step": .5}),
|
|
"mode": (s.modes, {"default": s.modes[0]}),
|
|
"output_format": (image_output_formats_options, {
|
|
"default": image_output_formats_options[0]
|
|
}),
|
|
|
|
},
|
|
"optional": {
|
|
"optional_roi_mask": ("IMAGE",)
|
|
}
|
|
}
|
|
|
|
RETURN_TYPES = ("IMAGE", "IMAGE",)
|
|
FUNCTION = "create_mask"
|
|
CATEGORY = "Bmad/CV/C.Photography"
|
|
|
|
def create_mask(self, src, dst, thresh_blur, close_dist, open_dist, size_dist, mask_blur,
|
|
contrast_adjust, mode, output_format, optional_roi_mask=None):
|
|
src = tensor2opencv(src)
|
|
dst = tensor2opencv(dst)
|
|
|
|
thresh_blur += 1
|
|
if mask_blur > 0:
|
|
mask_blur += 1
|
|
|
|
# compute the difference between images based on mode
|
|
src = self.mode_func_map[mode](src)
|
|
dst = self.mode_func_map[mode](dst)
|
|
diff = cv.absdiff(src, dst)
|
|
|
|
if mode == "HUE":
|
|
diff = np.minimum(diff, 180 - diff)
|
|
|
|
# binary thresholding
|
|
# _, mask = cv.threshold(diff, threshold, 255, cv.THRESH_BINARY)
|
|
diff = cv.GaussianBlur(diff, (thresh_blur, thresh_blur), 0)
|
|
ret3, mask = cv.threshold(diff, 0, 255, cv.THRESH_BINARY + cv.THRESH_OTSU)
|
|
if optional_roi_mask is not None:
|
|
optional_roi_mask = tensor2opencv(optional_roi_mask, 1)
|
|
mask[optional_roi_mask < 127] = 0
|
|
|
|
# morphological closing > closing > dilate/erode
|
|
if close_dist > 0:
|
|
close_kernel = cv.getStructuringElement(cv.MORPH_ELLIPSE, (close_dist, close_dist))
|
|
mask = cv.morphologyEx(mask, cv.MORPH_CLOSE, close_kernel)
|
|
if open_dist > 0:
|
|
open_kernel = cv.getStructuringElement(cv.MORPH_ELLIPSE, (open_dist, open_dist))
|
|
mask = cv.morphologyEx(mask, cv.MORPH_OPEN, open_kernel)
|
|
|
|
if size_dist > 0:
|
|
size_op = cv.MORPH_DILATE
|
|
size = size_dist
|
|
else:
|
|
size_op = cv.MORPH_ERODE
|
|
size = abs(size_dist)
|
|
if size_dist != 0:
|
|
size_kernel = cv.getStructuringElement(cv.MORPH_ELLIPSE, (size, size))
|
|
mask = cv.morphologyEx(mask, size_op, size_kernel)
|
|
|
|
# gaussian blur + contrast adjust
|
|
if mask_blur > 0:
|
|
mask = cv.GaussianBlur(mask, (mask_blur, mask_blur), 0)
|
|
mask = cv.convertScaleAbs(mask, alpha=1 + contrast_adjust, beta=0) # / 100, beta=0)
|
|
|
|
# convert to target format and output as tensor
|
|
# note: diff is only meant to be used for debug purposes
|
|
mask = maybe_convert_img(mask, 1, image_output_formats_options_map[output_format])
|
|
diff = opencv2tensor(diff)
|
|
mask = opencv2tensor(mask)
|
|
|
|
return (mask, diff,)
|
|
|
|
```
|