<p>Using the encoder's scenecut to detect scenes is not advisable, since its purpose is efficient GOP splitting taking into account other encoding parameters. It is not a "pure" scene detector. Its propensity to mark a "scene change" increases as it gets closer to the the next forced keyframe target (set by <code>keyint</code>).</p>

<p>Consider using <a href="https://github.com/Breakthrough/PySceneDetect" rel="noreferrer">https://github.com/Breakthrough/PySceneDetect</a> instead.</p>

CC BY-SA 4.0
2019-10-02T15:30:11.927Z
Yes I was using that library before. But much slower than ffmpeg. So was looking for a ffmpeg-based solution.
angel_30
CC BY-SA 4.0
2019-10-02T08:58:14.997Z
2019-10-02T08:58:14.997Z
Gyan
<p>This script makes it simple, better results than other posts around town:
<a href="https://gist.github.com/achesco/4dc2ebf13378a0a61fc26c7fe01f539e" rel="nofollow noreferrer">https://gist.github.com/achesco/4dc2ebf13378a0a61fc26c7fe01f539e</a></p>
<blockquote>
<p>Splits file.mp4 file to scenes with change more than 0.5 and saves
output parts to /tmp/parts folder</p>
</blockquote>
<blockquote>
<p>split.sh -d 0.5 -o /tmp/parts -f file.mp4</p>
</blockquote>
<p>Including the script here for reference (note the version below has some differences, primarily a bug fix):</p>
<pre><code># Splits video to separate scenes files
# Inspired by https://stackoverflow.com/a/38205105

#!/bin/bash

file=&quot;&quot;
out=&quot;./&quot;
diff=0.4
bitrate=&quot;&quot;
trim=0
stripaudio=&quot;&quot;

usage () {
  echo &quot;Usage: $(basename $0) [[[-o folder] [-d ratio]] | [-h]] -f file.mp4&quot;
  echo
  echo &quot;Options:&quot;
  echo &quot;-f, --file          Input file&quot;
  echo &quot;-o, --out           Outpup files folder path, default&quot;
  echo &quot;                    to current folder&quot;
  echo &quot;-d, --diff          Number from 0 to 1, default to 0.4.&quot;
  echo &quot;                    Scene change difference factor&quot;
  echo &quot;-b, --bitrate       Bitrate to encode parts&quot;
  echo &quot;-t, --trim          Trim last given seconds number, default 0&quot;
  echo &quot;-sa, --strip-audio  Strip audio&quot;
  echo &quot;-h, --help          Display this help message&quot;
  echo
  echo &quot;Example: split.sh -d 0.5 -o /tmp/parts -f file.mp4&quot;
  echo &quot;Splits file.mp4 file to scenes with change more than 0.5&quot;
  echo &quot;and saves output parts to /tmp/parts folder&quot;
}

if [ &quot;$1&quot; = &quot;&quot; ]; then
  usage
fi

while [ &quot;$1&quot; != &quot;&quot; ]; do
  case $1 in
    -f | --file )
      shift
      file=$1
      ;;
    -d | --diff )
      shift
      diff=$1
      ;;
    -o | --out )
      shift
      out=$1
      ;;
    -b | --bitrate )
      shift
      bitrate=&quot;-b $1&quot;
      ;;
    -t | --trim )
      shift
      trim=$1
      ;;
    -sa | --strip-audio )
      stripaudio=&quot;-an&quot;
      ;;
    -h | --help )
      usage
      exit
      ;;
    * )
      usage
      exit 1
  esac
  shift
done

cut_part () {
  duration_flag=&quot;&quot;
  if [ &quot;$3&quot; != &quot;&quot; ]; then
    duration_flag=&quot;-t&quot;
  fi
  ffmpeg -loglevel error -hide_banner -ss $1 -i $file $duration_flag $3 \
    -vcodec libx264 -movflags faststart $bitrate $stripaudio \
    -y $out/`printf &quot;%04d_%s&quot; $2 $filename` &lt; /dev/null
}

filename=`basename $file`
mkdir -p $out
timefrom=0
i=1
while read -r timestamp; do
  duration=`bc &lt;&lt;&lt; &quot;$timestamp-$timefrom-$trim&quot; | awk '{printf &quot;%f&quot;, $0}'`
  cut_part $timefrom $i $duration
  timefrom=$timestamp
  i=`expr $i + 1`
done &lt; &lt;(
  ffmpeg -i $file -filter:v &quot;select='gt(scene,$diff)',showinfo&quot; -f null - 2&gt;&amp;1 | \
    grep Parsed_showinfo | grep pts_time:[0-9.]* -o | grep &quot;[0-9]*\.[0-9]*&quot; -o
)

if [ $timefrom != 0 ]; then
  cut_part $timefrom $i
fi
</code></pre>

CC BY-SA 4.0
2024-07-31T11:26:31.880Z
Instead of all that parsing, you can use this command: `ffprobe -show_entries frame=pts_time -of compact=nk=1:p=0 -f lavfi "movie=$file,select=gt(scene\,$diff)"`
OrangeDog
CC BY-SA 4.0
2020-06-27T19:16:18.343Z
2020-07-01T04:21:39.103Z
2020-07-01T04:21:39.103Z
dtmland
<p>I'm trying to detect scenes and save them as individual files with <code>FFmpeg</code>. I found this long command somewhere, but it fails with a good detection. I change the scenecut value, but I don't see any effects.</p>

<pre><code>ffmpeg -y -i test.mp4 -max_muxing_queue_size 1024 -vf yadif -preset:v fast -x264opts scenecut=20 -f segment -segment_format mp4 -segment_format_options movflags=faststart %05d.mp4
</code></pre>

<p>Is there a better command than this? There should be a shorter, more efficient command I guess.</p>

<video><ffmpeg><encoding><mp4><decoding>
How to extract each video scene with FFmpeg
angel_30
# How to extract each video scene with FFmpeg

(Asked by: username_0 on 2019-10-01)

I'm trying to detect scenes and save them as individual files with FFmpeg. I found this long command somewhere, but it fails with a good detection. I change the scenecut value, but I don't see any effects.
ffmpeg -y -i test.mp4 -max_muxing_queue_size 1024 -vf yadif -preset:v fast -x264opts scenecut=20 -f segment -segment_format mp4 -segment_format_options movflags=faststart %05d.mp4

Is there a better command than this? There should be a shorter, more efficient command I guess.

**Tags:** video, ffmpeg, encoding, mp4, decoding


---

## 2 answers

### Answer by username_1 on 2019-10-02. Score: 5

Using the encoder's scenecut to detect scenes is not advisable, since its purpose is efficient GOP splitting taking into account other encoding parameters. It is not a "pure" scene detector. Its propensity to mark a "scene change" increases as it gets closer to the the next forced keyframe target (set by `keyint`).

Consider using <https://github.com/Breakthrough/PySceneDetect> instead.

> **Comments:**
> **username_0**: Yes I was using that library before. But much slower than ffmpeg. So was looking for a ffmpeg-based solution.  

---

### Answer by username_2 on 2020-06-27. Score: 1

This script makes it simple, better results than other posts around town:
https://gist.github.com/achesco/4dc2ebf13378a0a61fc26c7fe01f539e

Splits file.mp4 file to scenes with change more than 0.5 and saves
output parts to /tmp/parts folder


split.sh -d 0.5 -o /tmp/parts -f file.mp4

Including the script here for reference (note the version below has some differences, primarily a bug fix):
# Splits video to separate scenes files
# Inspired by https://stackoverflow.com/a/38205105

#!/bin/bash

file=""
out="./"
diff=0.4
bitrate=""
trim=0
stripaudio=""

usage () {
  echo "Usage: $(basename $0) [[[-o folder] [-d ratio]] | [-h]] -f file.mp4"
  echo
  echo "Options:"
  echo "-f, --file          Input file"
  echo "-o, --out           Outpup files folder path, default"
  echo "                    to current folder"
  echo "-d, --diff          Number from 0 to 1, default to 0.4."
  echo "                    Scene change difference factor"
  echo "-b, --bitrate       Bitrate to encode parts"
  echo "-t, --trim          Trim last given seconds number, default 0"
  echo "-sa, --strip-audio  Strip audio"
  echo "-h, --help          Display this help message"
  echo
  echo "Example: split.sh -d 0.5 -o /tmp/parts -f file.mp4"
  echo "Splits file.mp4 file to scenes with change more than 0.5"
  echo "and saves output parts to /tmp/parts folder"
}

if [ "$1" = "" ]; then
  usage
fi

while [ "$1" != "" ]; do
  case $1 in
    -f | --file )
      shift
      file=$1
      ;;
    -d | --diff )
      shift
      diff=$1
      ;;
    -o | --out )
      shift
      out=$1
      ;;
    -b | --bitrate )
      shift
      bitrate="-b $1"
      ;;
    -t | --trim )
      shift
      trim=$1
      ;;
    -sa | --strip-audio )
      stripaudio="-an"
      ;;
    -h | --help )
      usage
      exit
      ;;
    * )
      usage
      exit 1
  esac
  shift
done

cut_part () {
  duration_flag=""
  if [ "$3" != "" ]; then
    duration_flag="-t"
  fi
  ffmpeg -loglevel error -hide_banner -ss $1 -i $file $duration_flag $3 \
    -vcodec libx264 -movflags faststart $bitrate $stripaudio \
    -y $out/`printf "%04d_%s" $2 $filename` < /dev/null
}

filename=`basename $file`
mkdir -p $out
timefrom=0
i=1
while read -r timestamp; do
  duration=`bc <<< "$timestamp-$timefrom-$trim" | awk '{printf "%f", $0}'`
  cut_part $timefrom $i $duration
  timefrom=$timestamp
  i=`expr $i + 1`
done < <(
  ffmpeg -i $file -filter:v "select='gt(scene,$diff)',showinfo" -f null - 2>&1 | \
    grep Parsed_showinfo | grep pts_time:[0-9.]* -o | grep "[0-9]*\.[0-9]*" -o
)

if [ $timefrom != 0 ]; then
  cut_part $timefrom $i
fi

> **Comments:**
> **username_3**: Instead of all that parsing, you can use this command: `ffprobe -show_entries frame=pts_time -of compact=nk=1:p=0 -f lavfi "movie=$file,select=gt(scene\,$diff)"`  

---

