Class: FFMPEG::SceneDetector

Inherits:
Object
  • Object
show all
Defined in:
lib/ffmpeg/scene_detector.rb

Overview

Detects scene changes in video using FFmpeg’s scene detection filter

Examples:

Basic usage

detector = SceneDetector.new(media)
scenes = detector.detect(threshold: 0.3)
# => [
#   { timestamp: 0.0, score: 1.0 },
#   { timestamp: 5.23, score: 0.45 },
#   { timestamp: 12.8, score: 0.38 }
# ]

With custom options

scenes = detector.detect(
  threshold: 0.4,
  min_scene_length: 2.0,
  max_scenes: 20
)

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(media) ⇒ SceneDetector

Create a new SceneDetector

Parameters:

  • media (Media)

    source media



28
29
30
31
# File 'lib/ffmpeg/scene_detector.rb', line 28

def initialize(media)
  @media = media
  validate_media!
end

Instance Attribute Details

#mediaMedia (readonly)

Returns source media.

Returns:

  • (Media)

    source media



24
25
26
# File 'lib/ffmpeg/scene_detector.rb', line 24

def media
  @media
end

Instance Method Details

#detect(threshold: 0.3, min_scene_length: nil, max_scenes: nil) ⇒ Array<Hash>

Detect scene changes

Parameters:

  • threshold (Float) (defaults to: 0.3)

    scene detection threshold (0.0-1.0) Lower values detect more scene changes

  • min_scene_length (Float, nil) (defaults to: nil)

    minimum time between scenes in seconds

  • max_scenes (Integer, nil) (defaults to: nil)

    maximum number of scenes to detect

Returns:

  • (Array<Hash>)

    detected scenes with :timestamp and :score

Raises:



40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/ffmpeg/scene_detector.rb', line 40

def detect(threshold: 0.3, min_scene_length: nil, max_scenes: nil)
  scenes = run_detection(threshold)

  # Always include first frame
  scenes.unshift({ timestamp: 0.0, score: 1.0 }) unless scenes.any? { |s| s[:timestamp].zero? }

  # Filter by minimum scene length
  if min_scene_length
    scenes = filter_by_min_length(scenes, min_scene_length)
  end

  # Limit number of scenes
  if max_scenes && scenes.length > max_scenes
    scenes = select_best_scenes(scenes, max_scenes)
  end

  scenes.sort_by { |s| s[:timestamp] }
end

#detect_with_keyframes(threshold: 0.3, output_dir:, format: "jpg") ⇒ Array<Hash>

Detect scenes and extract a keyframe for each

Parameters:

  • threshold (Float) (defaults to: 0.3)

    scene detection threshold

  • output_dir (String)

    directory for extracted frames

  • format (String) (defaults to: "jpg")

    image format (jpg, png)

Returns:

  • (Array<Hash>)

    scenes with :timestamp, :score, and :keyframe_path



64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/ffmpeg/scene_detector.rb', line 64

def detect_with_keyframes(threshold: 0.3, output_dir:, format: "jpg")
  scenes = detect(threshold: threshold)

  FileUtils.mkdir_p(output_dir)

  scenes.each_with_index do |scene, index|
    output_path = File.join(output_dir, "scene_%04d.#{format}" % index)
    extract_frame(scene[:timestamp], output_path)
    scene[:keyframe_path] = output_path
  end

  scenes
end