blender-mask-peoples/operators/apply_blur.py
2026-02-07 05:57:39 +09:00

252 lines
8.9 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
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,
)
@classmethod
def poll(cls, context):
"""Check if operator can run."""
if not context.scene.sequence_editor:
return False
seq_editor = context.scene.sequence_editor
strip = seq_editor.active_strip
if not strip:
return False
if strip.type not in {'MOVIE', 'IMAGE'}:
return False
# Check if corresponding mask strip exists
mask_name = f"{strip.name}_mask"
return mask_name in seq_editor.strips
def execute(self, context):
seq_editor = context.scene.sequence_editor
video_strip = seq_editor.active_strip
# Auto-detect mask strip
mask_name = f"{video_strip.name}_mask"
mask_strip = seq_editor.strips.get(mask_name)
if not mask_strip:
self.report({'ERROR'}, f"Mask strip not found: {mask_name}")
return {'CANCELLED'}
try:
# Use Mask Modifier approach (Blender 5.0 compatible)
self._apply_with_mask_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_mask_modifier(self, context, video_strip: "bpy.types.Strip", mask_strip: "bpy.types.Strip"):
"""
Apply blur using Mask Modifier, grouped in a Meta Strip.
Workflow:
1. Duplicate the video strip
2. Create Gaussian Blur effect on the duplicate
3. Add Mask modifier to the blur effect (references mask strip)
4. Group all into a Meta Strip
The blur effect with mask will automatically composite over the original
video due to VSE's channel layering system.
"""
seq_editor = context.scene.sequence_editor
# Find available channels
used_channels = {s.channel for s in seq_editor.strips}
duplicate_channel = video_strip.channel + 1
while duplicate_channel in used_channels:
duplicate_channel += 1
blur_channel = duplicate_channel + 1
while blur_channel in used_channels:
blur_channel += 1
# Step 1: Duplicate the video strip
if video_strip.type == 'MOVIE':
video_copy = seq_editor.strips.new_movie(
name=f"{video_strip.name}_copy",
filepath=bpy.path.abspath(video_strip.filepath),
channel=duplicate_channel,
frame_start=video_strip.frame_final_start,
)
elif video_strip.type == 'IMAGE':
# For image sequences, duplicate differently
video_copy = seq_editor.strips.new_image(
name=f"{video_strip.name}_copy",
filepath=bpy.path.abspath(video_strip.elements[0].filename) if video_strip.elements else "",
channel=duplicate_channel,
frame_start=video_strip.frame_final_start,
)
# Copy all elements
for elem in video_strip.elements[1:]:
video_copy.elements.append(elem.filename)
else:
raise ValueError(f"Unsupported strip type: {video_strip.type}")
# Match strip length
strip_length = video_strip.frame_final_end - video_strip.frame_final_start
video_copy.frame_final_end = video_copy.frame_final_start + strip_length
# Step 2: Create Gaussian Blur effect on the duplicate
blur_effect = seq_editor.strips.new_effect(
name=f"{video_strip.name}_blur",
type='GAUSSIAN_BLUR',
channel=blur_channel,
frame_start=video_strip.frame_final_start,
length=strip_length,
input1=video_copy,
)
# Set blur size (Blender 5.0 API)
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
# Step 3: Add Mask modifier to the blur effect
mask_mod = blur_effect.modifiers.new(
name="FaceMask",
type='MASK'
)
# Set mask input (Blender 5.0 API)
if hasattr(mask_mod, 'input_mask_strip'):
mask_mod.input_mask_strip = mask_strip
elif hasattr(mask_mod, 'input_mask_id'):
mask_mod.input_mask_type = 'STRIP'
mask_mod.input_mask_id = mask_strip
# Hide the mask strip (but keep it active for the modifier)
mask_strip.mute = True
# Step 4: Create Meta Strip to group everything
# Deselect all first
for strip in seq_editor.strips:
strip.select = False
# Select the strips to group
video_copy.select = True
blur_effect.select = True
mask_strip.select = True
# Set active strip for context
seq_editor.active_strip = blur_effect
# Create meta strip using operator
bpy.ops.sequencer.meta_make()
# Find the newly created meta strip (it will be selected)
meta_strip = None
for strip in seq_editor.strips:
if strip.select and strip.type == 'META':
meta_strip = strip
break
if meta_strip:
meta_strip.name = f"{video_strip.name}_blurred_meta"
self.report({'INFO'}, f"Applied blur with Mask Modifier (grouped in Meta Strip)")
else:
self.report({'INFO'}, f"Applied blur with Mask Modifier (blur on channel {blur_channel})")
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)