Refacto of the scene cut detection, make it generic and add another algorythm as an option
This commit is contained in:
@@ -0,0 +1,148 @@
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
|
||||
import bpy
|
||||
|
||||
from vse_toolbox import bl_utils
|
||||
|
||||
|
||||
def detect_scene_change(strip, movie_type="ANIMATED", threshold=0.5, frame_start=None, frame_end=None, crop=None):
|
||||
"""Launch ffmpeg command to detect changing frames from a movie strip.
|
||||
|
||||
Args:
|
||||
movie_strip (bpy.types.Sequence): blender sequence strip to detect changes.
|
||||
threshold (float): value of the detection factor (from 0 to 1).
|
||||
frame_start (int, optional): first frame to detect.
|
||||
Defaults to None.
|
||||
frame_end (int, optional): last frame to detect.
|
||||
Defaults to None.
|
||||
|
||||
Returns:
|
||||
str: ffmpeg command log.
|
||||
"""
|
||||
|
||||
path = bl_utils.abspath(strip.filepath)
|
||||
fps = bpy.context.scene.render.fps
|
||||
|
||||
if frame_start is None:
|
||||
frame_start = 0
|
||||
if frame_end is None:
|
||||
frame_end = strip.frame_duration
|
||||
|
||||
frame_start = frame_start - strip.frame_start
|
||||
|
||||
start_time = frame_start/fps
|
||||
end_time = frame_end/fps
|
||||
|
||||
#frame_start += strip.frame_offset_start
|
||||
#frame_end -= strip.frame_offset_end
|
||||
|
||||
if movie_type == 'ANIMATED':
|
||||
return select_generator(path, start_time=start_time, end_time=end_time, threshold=threshold, crop=crop)
|
||||
|
||||
elif movie_type == 'STILL':
|
||||
return freeze_detect_generator(path, start_time=start_time, end_time=end_time, threshold=threshold, crop=crop)
|
||||
|
||||
else:
|
||||
raise Exception(f'movie_type: {movie_type} not implemented')
|
||||
|
||||
|
||||
def freeze_detect_generator(path, start_time, end_time, threshold=0.005, crop=None):
|
||||
"""Generate the ffmpeg command which detect change from a movie.
|
||||
|
||||
Args:
|
||||
path (_type_): path to detect changes.
|
||||
threshold (_type_): value of the detection factor (from 0 to 1).
|
||||
frame_start (_type_): first frame to detect.
|
||||
frame_end (_type_): last frame to detect.
|
||||
fps (_type_): framerate of the movie.
|
||||
|
||||
Returns:
|
||||
list: ffmpeg command as list for subprocess module.
|
||||
"""
|
||||
|
||||
if crop is None:
|
||||
crop = [0, 0, 0, 0]
|
||||
|
||||
crop_expr = f"crop=iw-{crop[0]}-{crop[1]}:ih-{crop[2]}-{crop[3]}:{crop[0]}:{crop[-1]}"
|
||||
|
||||
command = [
|
||||
'ffmpeg',
|
||||
'-nostats',
|
||||
'-i',
|
||||
str(path),
|
||||
'-ss', str(start_time),
|
||||
'-t', str(end_time),
|
||||
'-vf',
|
||||
f"{crop_expr}, freezedetect=n={threshold}:d=0.01,metadata=print", #,
|
||||
'-f',
|
||||
'null',
|
||||
'-'
|
||||
]
|
||||
|
||||
print(command)
|
||||
|
||||
process = subprocess.Popen(
|
||||
command,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT,
|
||||
universal_newlines=True)
|
||||
|
||||
def parse_split_time(log):
|
||||
print(log)
|
||||
timecodes = re.findall(r'freeze_end: ([\d.]+)', log)
|
||||
if timecodes:
|
||||
return round(float(timecodes[0]) * bpy.context.scene.render.fps)
|
||||
|
||||
return filter(None, (parse_split_time(x) for x in process.stdout))
|
||||
|
||||
def select_generator(path, start_time, end_time, threshold, crop=None):
|
||||
"""Generate the ffmpeg command which detect change from a movie.
|
||||
|
||||
Args:
|
||||
path (_type_): path to detect changes.
|
||||
threshold (_type_): value of the detection factor (from 0 to 1).
|
||||
frame_start (_type_): first frame to detect.
|
||||
frame_end (_type_): last frame to detect.
|
||||
fps (_type_): framerate of the movie.
|
||||
|
||||
Returns:
|
||||
list: ffmpeg command as list for subprocess module.
|
||||
"""
|
||||
|
||||
if crop is None:
|
||||
crop = [0, 0, 0, 0]
|
||||
|
||||
crop_expr = f"crop=iw-{crop[0]}-{crop[1]}:ih-{crop[2]}-{crop[3]}:{crop[0]}:{crop[-1]}"
|
||||
|
||||
command = [
|
||||
'ffmpeg',
|
||||
'-nostats',
|
||||
'-i',
|
||||
str(path),
|
||||
'-ss', str(start_time),
|
||||
'-t', str(end_time),
|
||||
'-vf',
|
||||
f"{crop_expr}, select='gt(scene, {threshold})', showinfo",
|
||||
'-f',
|
||||
'null',
|
||||
'-'
|
||||
]
|
||||
|
||||
print(command)
|
||||
|
||||
process = subprocess.Popen(
|
||||
command,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT,
|
||||
universal_newlines=True)
|
||||
|
||||
def parse_split_time(log):
|
||||
print(log)
|
||||
timecodes = re.findall(r'pts_time:([\d.]+)', log)
|
||||
if timecodes:
|
||||
return round(float(timecodes[0]) * bpy.context.scene.render.fps)
|
||||
|
||||
return filter(None, (parse_split_time(x) for x in process.stdout))
|
||||
|
||||
Reference in New Issue
Block a user