blender-mask-peoples/core/compositor_setup.py

169 lines
4.9 KiB
Python

"""
Compositor Node Tree Setup for mask-based blur effect.
Creates and manages compositing node trees that apply blur
only to masked regions of a video strip.
"""
def create_mask_blur_node_tree(
name: str = "FaceMaskBlur",
blur_size: int = 50,
) -> "bpy.types.NodeTree": # noqa: F821
"""
Create a compositing node tree for mask-based blur.
Node structure:
[Render Layers] ──┬──────────────────────────→ [Mix] → [Composite]
│ ↑
└→ [Blur] → [Mix Factor Mask] ↗
Args:
name: Name for the node tree
blur_size: Blur radius in pixels
Returns:
The created NodeTree
"""
import bpy
# Create new node tree or get existing
if name in bpy.data.node_groups:
# Return existing tree
return bpy.data.node_groups[name]
# Create compositing scene if needed
tree = bpy.data.node_groups.new(name=name, type='CompositorNodeTree')
tree.use_fake_user = True # Prevent deletion
nodes = tree.nodes
links = tree.links
# Clear default nodes
nodes.clear()
# Create nodes
# Input: Image and Mask
input_node = nodes.new('NodeGroupInput')
input_node.location = (-400, 0)
# Output
output_node = nodes.new('NodeGroupOutput')
output_node.location = (600, 0)
# Blur node
blur_node = nodes.new('CompositorNodeBlur')
blur_node.location = (0, -150)
blur_node.filter_type = 'GAUSS'
blur_node.label = "Face Blur"
# Note: Blender 5.0 uses 'Size' input socket instead of size_x/size_y properties
# We'll set default_value on the socket after linking
# Mix node (combines original with blurred using mask)
mix_node = nodes.new('CompositorNodeMixRGB')
mix_node.location = (300, 0)
mix_node.blend_type = 'MIX'
mix_node.label = "Mask Mix"
# Set blur size via input socket (Blender 5.0 API)
if 'Size' in blur_node.inputs:
blur_node.inputs['Size'].default_value = blur_size / 100.0 # Size is 0-1 range
elif 'size' in blur_node.inputs:
blur_node.inputs['size'].default_value = blur_size / 100.0
# Define interface sockets
tree.interface.new_socket(
name="Image",
in_out='INPUT',
socket_type='NodeSocketColor',
)
tree.interface.new_socket(
name="Mask",
in_out='INPUT',
socket_type='NodeSocketFloat',
)
tree.interface.new_socket(
name="Image",
in_out='OUTPUT',
socket_type='NodeSocketColor',
)
# Link nodes
# Input Image → Blur
links.new(input_node.outputs[0], blur_node.inputs['Image'])
# Input Image → Mix (first color)
links.new(input_node.outputs[0], mix_node.inputs[1])
# Blur → Mix (second color)
links.new(blur_node.outputs[0], mix_node.inputs[2])
# Input Mask → Mix (factor)
links.new(input_node.outputs[1], mix_node.inputs[0])
# Mix → Output
links.new(mix_node.outputs[0], output_node.inputs[0])
return tree
def setup_strip_compositor_modifier(
strip: "bpy.types.Strip", # noqa: F821
mask_strip: "bpy.types.Strip", # noqa: F821
node_tree: "bpy.types.NodeTree", # noqa: F821
) -> "bpy.types.SequenceModifier": # noqa: F821
"""
Add a Compositor modifier to a strip using the mask-blur node tree.
Args:
strip: The video strip to add the modifier to
mask_strip: The mask image sequence strip
node_tree: The compositing node tree to use
Returns:
The created modifier
"""
# Add compositor modifier
modifier = strip.modifiers.new(
name="FaceMaskBlur",
type='COMPOSITOR',
)
# Set the node tree/group (Blender 5.0 uses node_group instead of node_tree)
if hasattr(modifier, 'node_group'):
# Blender 5.0+
modifier.node_group = node_tree
elif hasattr(modifier, 'node_tree'):
# Blender 4.x
modifier.node_tree = node_tree
else:
raise AttributeError("Compositor modifier has neither 'node_group' nor 'node_tree' attribute")
# Configure input mapping
# The modifier automatically maps strip image to first input
# We need to configure the mask input
# TODO: Blender 5.0 may have different API for this
# This is a placeholder for the actual implementation
return modifier
def get_or_create_blur_node_tree(blur_size: int = 50) -> "bpy.types.NodeTree": # noqa: F821
"""
Get existing or create new blur node tree with specified blur size.
Args:
blur_size: Blur radius in pixels
Returns:
The node tree
"""
import bpy
name = f"FaceMaskBlur_{blur_size}"
if name in bpy.data.node_groups:
return bpy.data.node_groups[name]
return create_mask_blur_node_tree(name=name, blur_size=blur_size)