blender-mask-peoples/operators/apply_blur.py
2026-02-06 08:16:23 +09:00

176 lines
6.0 KiB
Python

"""
Apply Blur Operator for masked face blur in VSE.
Provides operators to apply blur effects using mask strips
generated by the face detection operators.
"""
import bpy
from bpy.props import FloatProperty, IntProperty, StringProperty
from bpy.types import Operator
from ..core.compositor_setup import get_or_create_blur_node_tree, setup_strip_compositor_modifier
class SEQUENCER_OT_apply_mask_blur(Operator):
"""Apply blur effect using mask strip."""
bl_idname = "sequencer.apply_mask_blur"
bl_label = "Apply Mask Blur"
bl_description = "Apply blur effect to video using mask strip"
bl_options = {'REGISTER', 'UNDO'}
blur_size: IntProperty(
name="Blur Size",
description="Size of the blur effect in pixels",
default=50,
min=1,
max=500,
)
mask_strip_name: StringProperty(
name="Mask Strip",
description="Name of the mask strip to use",
default="",
)
@classmethod
def poll(cls, context):
"""Check if operator can run."""
if not context.scene.sequence_editor:
return False
strip = context.scene.sequence_editor.active_strip
if not strip:
return False
return strip.type in {'MOVIE', 'IMAGE'}
def invoke(self, context, event):
"""Show dialog to select mask strip."""
return context.window_manager.invoke_props_dialog(self)
def draw(self, context):
"""Draw the operator dialog."""
layout = self.layout
layout.prop(self, "blur_size")
layout.prop_search(
self, "mask_strip_name",
context.scene.sequence_editor, "strips",
text="Mask Strip",
)
def execute(self, context):
seq_editor = context.scene.sequence_editor
video_strip = seq_editor.active_strip
# Find mask strip
if not self.mask_strip_name:
# Try to find auto-generated mask
auto_mask_name = f"{video_strip.name}_mask"
if auto_mask_name in seq_editor.strips:
self.mask_strip_name = auto_mask_name
else:
self.report({'ERROR'}, "Please select a mask strip")
return {'CANCELLED'}
mask_strip = seq_editor.strips.get(self.mask_strip_name)
if not mask_strip:
self.report({'ERROR'}, f"Mask strip not found: {self.mask_strip_name}")
return {'CANCELLED'}
try:
# Try using Compositor Modifier (preferred method)
self._apply_with_modifier(context, video_strip, mask_strip)
except Exception as e:
self.report({'ERROR'}, f"Failed to apply blur: {e}")
return {'CANCELLED'}
return {'FINISHED'}
def _apply_with_modifier(self, context, video_strip: "bpy.types.Strip", mask_strip: "bpy.types.Strip"):
"""Apply blur using Compositor Modifier."""
# Get or create the blur node tree
node_tree = get_or_create_blur_node_tree(blur_size=self.blur_size)
# Add compositor modifier to the video strip
modifier = setup_strip_compositor_modifier(
strip=video_strip,
mask_strip=mask_strip,
node_tree=node_tree,
)
self.report({'INFO'}, f"Applied blur with Compositor Modifier")
def _apply_with_meta_strip(self, context, video_strip: "bpy.types.Strip", mask_strip: "bpy.types.Strip"):
"""
Fallback method using Meta Strip and effects.
This is less elegant but works on all Blender versions.
"""
seq_editor = context.scene.sequence_editor
# Find available channels
base_channel = video_strip.channel
blur_channel = base_channel + 1
effect_channel = blur_channel + 1
# Ensure mask is in correct position
mask_strip.channel = blur_channel
mask_strip.frame_start = video_strip.frame_final_start
# Create Gaussian Blur effect on the video strip
# First, we need to duplicate the video for the blurred version
video_copy = seq_editor.strips.new_movie(
name=f"{video_strip.name}_blur",
filepath=bpy.path.abspath(video_strip.filepath) if hasattr(video_strip, 'filepath') else "",
channel=blur_channel,
frame_start=video_strip.frame_final_start,
) if video_strip.type == 'MOVIE' else None
if video_copy:
# Calculate length (Blender 5.0 uses length instead of frame_end)
strip_length = video_strip.frame_final_end - video_strip.frame_final_start
# Apply Gaussian blur effect (Blender 5.0 API)
blur_effect = seq_editor.strips.new_effect(
name=f"{video_strip.name}_gaussian",
type='GAUSSIAN_BLUR',
channel=effect_channel,
frame_start=video_strip.frame_final_start,
length=strip_length,
input1=video_copy,
)
# Set blur size (Blender 5.0 uses size property, not size_x/size_y)
if hasattr(blur_effect, 'size_x'):
blur_effect.size_x = self.blur_size
blur_effect.size_y = self.blur_size
elif hasattr(blur_effect, 'size'):
blur_effect.size = self.blur_size
# Create Alpha Over to combine original with blurred (using mask)
# Note: Full implementation would require compositing
# This is a simplified version
self.report({'INFO'}, "Created blur effect (full compositing in development)")
else:
# For image sequences, different approach needed
self.report({'WARNING'}, "Image sequence blur not yet fully implemented")
# Registration
classes = [
SEQUENCER_OT_apply_mask_blur,
]
def register():
for cls in classes:
bpy.utils.register_class(cls)
def unregister():
for cls in reversed(classes):
bpy.utils.unregister_class(cls)