コード例 #1
0
 def __init__(self):
     # Properties for main scenedetect command options (-i, -s, etc...) and CliContext logic.
     self.options_processed = False  # True when CLI option parsing is complete.
     self.scene_manager = None  # detect-content, detect-threshold, etc...
     self.video_manager = None  # -i/--input, -d/--downscale
     self.base_timecode = None  # -f/--framerate
     self.start_frame = 0  # time -s/--start [start_frame]
     self.stats_manager = StatsManager()  # -s/--stats
     self.stats_file_path = None  # -s/--stats [stats_file_path]
     self.output_directory = None  # -o/--output [output_directory]
     self.quiet_mode = False  # -q/--quiet or -v/--verbosity quiet
     self.frame_skip = 0  # -fs/--frame-skip [frame_skip]
     # Properties for save-images command.
     self.save_images = False  # save-images command
     self.image_extension = 'jpg'  # save-images -j/--jpeg, -w/--webp, -p/--png
     self.image_directory = None  # save-images -o/--output [image_directory]
     self.image_param = None  # save-images -q/--quality if -j/-w, -c/--compression if -p
     self.num_images = 2  # save-images -n/--num-images
     self.imwrite_params = get_cv2_imwrite_params()
     # Properties for split-video command.
     self.split_video = False  # split-video command
     self.split_mkvmerge = False  # split-video -m/--mkvmerge (or split-video without ffmpeg)
     self.split_args = None  # split-video -f/--ffmpeg-args [split_args]
     self.split_directory = None  # split-video -o/--output [split_directory]
     self.split_quiet = False  # split-video -q/--quiet
     # Properties for list-scenes command.
     self.list_scenes = False  # list-scenes command
     self.print_scene_list = False  # list-scenes --quiet/-q
     self.scene_list_path = None  # list-scenes -o [scene_list_path]
コード例 #2
0
 def __init__(self):
     # Properties for main scenedetect command options (-i, -s, etc...) and CliContext logic.
     self.options_processed = False  # True when CLI option parsing is complete.
     self.scene_manager = None  # detect-content, detect-threshold, etc...
     self.video_manager = None  # -i/--input, -d/--downscale
     self.base_timecode = None  # -f/--framerate
     self.start_frame = 0  # time -s/--start
     self.stats_manager = StatsManager()  # -s/--stats
     self.stats_file_path = None  # -s/--stats
     self.output_directory = None  # -o/--output
     self.quiet_mode = False  # -q/--quiet or -v/--verbosity quiet
     self.frame_skip = 0  # -fs/--frame-skip
     # Properties for save-images command.
     self.save_images = False  # save-images command
     self.image_extension = 'jpg'  # save-images -j/--jpeg, -w/--webp, -p/--png
     self.image_directory = None  # save-images -o/--output
     self.image_param = None  # save-images -q/--quality if -j/-w, -c/--compression if -p
     self.image_name_format = '$VIDEO_NAME-Scene-$SCENE_NUMBER-$IMAGE_NUMBER'  # save-images -f/--name-format
     self.num_images = 2  # save-images -n/--num-images
     self.imwrite_params = get_cv2_imwrite_params()
     # Properties for split-video command.
     self.split_video = False  # split-video command
     self.split_mkvmerge = False  # split-video -c/--copy
     self.split_args = None  # split-video -a/--override-args
     self.split_directory = None  # split-video -o/--output
     self.split_name_format = '$VIDEO_NAME-Scene-$SCENE_NUMBER'  # split-video -f/--filename
     self.split_quiet = False  # split-video -q/--quiet
     # Properties for list-scenes command.
     self.list_scenes = False  # list-scenes command
     self.print_scene_list = False  # list-scenes --quiet/-q
     self.scene_list_directory = None  # list-scenes -o/--output
     self.scene_list_name_format = None  # list-scenes -f/--filename
     self.scene_list_output = False  # list-scenes -n/--no-output
コード例 #3
0
ファイル: context.py プロジェクト: Breakthrough/PySceneDetect
    def __init__(self):
        # Properties for main scenedetect command options (-i, -s, etc...) and CliContext logic.
        self.options_processed = False          # True when CLI option parsing is complete.
        self.scene_manager = None               # detect-content, detect-threshold, etc...
        self.video_manager = None               # -i/--input, -d/--downscale
        self.base_timecode = None               # -f/--framerate
        self.start_frame = 0                    # time -s/--start
        self.stats_manager = None               # -s/--stats
        self.stats_file_path = None             # -s/--stats
        self.output_directory = None            # -o/--output
        self.quiet_mode = False                 # -q/--quiet or -v/--verbosity quiet
        self.frame_skip = 0                     # -fs/--frame-skip
        # Properties for save-images command.
        self.save_images = False                # save-images command
        self.image_extension = 'jpg'            # save-images -j/--jpeg, -w/--webp, -p/--png
        self.image_directory = None             # save-images -o/--output

        self.image_param = None                 # save-images -q/--quality if -j/-w,
                                                #   -c/--compression if -p


        self.image_name_format = (              # save-images -f/--name-format
            '$VIDEO_NAME-Scene-$SCENE_NUMBER-$IMAGE_NUMBER')
        self.num_images = 2                     # save-images -n/--num-images
        self.imwrite_params = get_cv2_imwrite_params()
        # Properties for split-video command.
        self.split_video = False                # split-video command
        self.split_mkvmerge = False             # split-video -c/--copy
        self.split_args = None                  # split-video -a/--override-args
        self.split_directory = None             # split-video -o/--output
        self.split_name_format = '$VIDEO_NAME-Scene-$SCENE_NUMBER'  # split-video -f/--filename
        self.split_quiet = False                # split-video -q/--quiet
        # Properties for list-scenes command.
        self.list_scenes = False                # list-scenes command
        self.print_scene_list = False           # list-scenes --quiet/-q
        self.scene_list_directory = None        # list-scenes -o/--output
        self.scene_list_name_format = None      # list-scenes -f/--filename
        self.scene_list_output = False          # list-scenes -n/--no-output

        self.export_html = False                # export-html command
        self.html_name_format = None            # export-html -f/--filename
        self.html_include_images = True         # export-html --no-images
        self.image_filenames = None             # export-html used for embedding images
        self.image_width = None                 # export-html -w/--image-width
        self.image_height = None                # export-html -h/--image-height
コード例 #4
0
ファイル: context.py プロジェクト: surensn/PySceneDetect
    def save_images_command(self, num_images, output, name_format, jpeg, webp,
                            quality, png, compression, frame_margin, scale,
                            height, width):
        # type: (int, str, str, bool, bool, int, bool, int, float, int, int) -> None
        """ Save Images Command: Parses all options/arguments passed to the save-images command,
        or with respect to the CLI, this function processes [save-images options] when calling:
        scenedetect [global options] save-images [save-images options] [other commands...].

        Raises:
            click.BadParameter
        """
        self.check_input_open()

        if contains_sequence_or_url(self.video_manager.get_video_paths()):
            self.options_processed = False
            error_str = '\nThe save-images command is incompatible with image sequences/URLs.'
            self.logger.error(error_str)
            raise click.BadParameter(error_str, param_hint='save-images')

        num_flags = sum([1 if flag else 0 for flag in [jpeg, webp, png]])
        if num_flags <= 1:

            # Ensure the format exists.
            extension = 'jpg'  # Default is jpg.
            if png:
                extension = 'png'
            elif webp:
                extension = 'webp'
            valid_params = get_cv2_imwrite_params()
            if not extension in valid_params or valid_params[extension] is None:
                error_strs = [
                    'Image encoder type %s not supported.' % extension.upper(),
                    'The specified encoder type could not be found in the current OpenCV module.',
                    'To enable this output format, please update the installed version of OpenCV.',
                    'If you build OpenCV, ensure the the proper dependencies are enabled. '
                ]
                self.logger.debug('\n'.join(error_strs))
                raise click.BadParameter('\n'.join(error_strs),
                                         param_hint='save-images')

            self.save_images = True
            self.image_directory = output
            self.image_extension = extension
            self.image_param = compression if png else quality
            self.image_name_format = name_format
            self.num_images = num_images
            self.frame_margin = frame_margin
            self.scale = scale
            self.height = height
            self.width = width

            image_type = 'JPEG' if self.image_extension == 'jpg' else self.image_extension.upper(
            )
            image_param_type = ''
            if self.image_param:
                image_param_type = 'Compression' if image_type == 'PNG' else 'Quality'
                image_param_type = ' [%s: %d]' % (image_param_type,
                                                  self.image_param)
            self.logger.info('Image output format set: %s%s', image_type,
                             image_param_type)
            if self.image_directory is not None:
                self.logger.info('Image output directory set:\n  %s',
                                 os.path.abspath(self.image_directory))
        else:
            self.options_processed = False
            self.logger.error(
                'Multiple image type flags set for save-images command.')
            raise click.BadParameter(
                'Only one image type (JPG/PNG/WEBP) can be specified.',
                param_hint='save-images')
コード例 #5
0
def save_images(scene_list, video_manager, num_images=3, frame_margin=1,
                image_extension='jpg', encoder_param=95,
                image_name_template='$VIDEO_NAME-Scene-$SCENE_NUMBER-$IMAGE_NUMBER',
                output_dir=None, downscale_factor=1, show_progress=False,
                scale=None, height=None, width=None):
    # type: (List[Tuple[FrameTimecode, FrameTimecode]], VideoManager,
    #        Optional[int], Optional[int], Optional[str], Optional[int],
    #        Optional[str], Optional[str], Optional[int], Optional[bool],
    #        Optional[float], Optional[int], Optional[int])
    #       -> Dict[List[str]]
    """ Saves a set number of images from each scene, given a list of scenes
    and the associated video/frame source.

    Arguments:
        scene_list: A list of scenes (pairs of FrameTimecode objects) returned
            from calling a SceneManager's detect_scenes() method.
        video_manager: A VideoManager object corresponding to the scene list.
            Note that the video will be closed/re-opened and seeked through.
        num_images: Number of images to generate for each scene.  Minimum is 1.
        frame_margin: Number of frames to pad each scene around the beginning
            and end (e.g. moves the first/last image into the scene by N frames).
            Can set to 0, but will result in some video files failing to extract
            the very last frame.
        image_extension: Type of image to save (must be one of 'jpg', 'png', or 'webp').
        encoder_param: Quality/compression efficiency, based on type of image:
            'jpg' / 'webp':  Quality 0-100, higher is better quality.  100 is lossless for webp.
            'png': Compression from 1-9, where 9 achieves best filesize but is slower to encode.
        image_name_template: Template to use when creating the images on disk. Can
            use the macros $VIDEO_NAME, $SCENE_NUMBER, and $IMAGE_NUMBER. The image
            extension is applied automatically as per the argument image_extension.
        output_dir: Directory to output the images into.  If not set, the output
            is created in the working directory.
        downscale_factor: Integer factor to downscale images by.  No filtering
            is currently done, only downsampling (thus requiring an integer).
        show_progress: If True, shows a progress bar if tqdm is installed.
        scale: Optional factor by which to rescale saved images.A scaling factor of 1 would
            not result in rescaling. A value <1 results in a smaller saved image, while a
            value >1 results in an image larger than the original. This value is ignored if
            either the height or width values are specified.
        height: Optional value for the height of the saved images. Specifying both the height
            and width will resize images to an exact size, regardless of aspect ratio.
            Specifying only height will rescale the image to that number of pixels in height
            while preserving the aspect ratio.
        width: Optional value for the width of the saved images. Specifying both the width
            and height will resize images to an exact size, regardless of aspect ratio.
            Specifying only width will rescale the image to that number of pixels wide
            while preserving the aspect ratio.


    Returns:
        Dict[List[str]]: Dictionary of the format { scene_num : [image_paths] },
        where scene_num is the number of the scene in scene_list (starting from 1),
        and image_paths is a list of the paths to the newly saved/created images.

    Raises:
        ValueError: Raised if any arguments are invalid or out of range (e.g.
        if num_images is negative).
    """

    if not scene_list:
        return {}
    if num_images <= 0 or frame_margin < 0:
        raise ValueError()

    # TODO: Validate that encoder_param is within the proper range.
    # Should be between 0 and 100 (inclusive) for jpg/webp, and 1-9 for png.
    imwrite_param = [get_cv2_imwrite_params()[image_extension],
                     encoder_param] if encoder_param is not None else []

    video_name = video_manager.get_video_name()

    # Reset video manager and downscale factor.
    video_manager.release()
    video_manager.reset()
    video_manager.set_downscale_factor(downscale_factor)
    video_manager.start()

    # Setup flags and init progress bar if available.
    completed = True
    logging.info('Generating output images (%d per scene)...', num_images)
    progress_bar = None
    if show_progress and tqdm:
        progress_bar = tqdm(
            total=len(scene_list) * num_images,
            unit='images',
            dynamic_ncols=True)

    filename_template = Template(image_name_template)

    scene_num_format = '%0'
    scene_num_format += str(max(3, math.floor(math.log(len(scene_list), 10)) + 1)) + 'd'
    image_num_format = '%0'
    image_num_format += str(math.floor(math.log(num_images, 10)) + 2) + 'd'

    timecode_list = dict()

    fps = scene_list[0][0].framerate

    timecode_list = [
        [
            FrameTimecode(int(f), fps=fps) for f in [
                # middle frames
                a[len(a)//2] if (0 < j < num_images-1) or num_images == 1

                # first frame
                else min(a[0] + frame_margin, a[-1]) if j == 0

                # last frame
                else max(a[-1] - frame_margin, a[0])

                # for each evenly-split array of frames in the scene list
                for j, a in enumerate(np.array_split(r, num_images))
            ]
        ]
        for i, r in enumerate([
            # pad ranges to number of images
            r
            if 1+r[-1]-r[0] >= num_images
            else list(r) + [r[-1]] * (num_images - len(r))
            # create range of frames in scene
            for r in (
                range(start.get_frames(), end.get_frames())
                # for each scene in scene list
                for start, end in scene_list
                )
        ])
    ]

    image_filenames = {i: [] for i in range(len(timecode_list))}
    aspect_ratio = get_aspect_ratio(video_manager)
    if abs(aspect_ratio - 1.0) < 0.01:
        aspect_ratio = None

    for i, scene_timecodes in enumerate(timecode_list):
        for j, image_timecode in enumerate(scene_timecodes):
            video_manager.seek(image_timecode)
            ret_val, frame_im = video_manager.read()
            if ret_val:
                file_path = '%s.%s' % (
                    filename_template.safe_substitute(
                        VIDEO_NAME=video_name,
                        SCENE_NUMBER=scene_num_format % (i + 1),
                        IMAGE_NUMBER=image_num_format % (j + 1),
                        FRAME_NUMBER=image_timecode.get_frames()),
                    image_extension)
                image_filenames[i].append(file_path)
                if aspect_ratio is not None:
                    frame_im = cv2.resize(
                        frame_im, (0, 0), fx=aspect_ratio, fy=1.0,
                        interpolation=cv2.INTER_CUBIC)
                
                # Get frame dimensions prior to resizing or scaling
                frame_height = frame_im.shape[0]
                frame_width = frame_im.shape[1]

                # Figure out what kind of resizing needs to be done
                if height and width:
                    frame_im = cv2.resize(
                        frame_im, (width, height), interpolation=cv2.INTER_CUBIC)
                elif height and not width:
                    factor = height / float(frame_height)
                    width = int(factor * frame_width)
                    frame_im = cv2.resize(
                        frame_im, (width, height), interpolation=cv2.INTER_CUBIC)
                elif width and not height:
                    factor = width / float(frame_width)
                    height = int(factor * frame_height)
                    frame_im = cv2.resize(
                        frame_im, (width, height), interpolation=cv2.INTER_CUBIC)
                elif scale:
                    frame_im = cv2.resize(
                        frame_im, (0, 0), fx=scale, fy=scale,
                        interpolation=cv2.INTER_CUBIC)

                cv2.imwrite(
                    get_and_create_path(file_path, output_dir),
                    frame_im, imwrite_param)
            else:
                completed = False
                break
            if progress_bar:
                progress_bar.update(1)

    if not completed:
        logging.error('Could not generate all output images.')

    return image_filenames
コード例 #6
0
def generate_images(
        scene_list,
        video_manager,
        video_name,
        num_images=2,
        image_extension='jpg',
        quality_or_compression=95,
        image_name_template='$VIDEO_NAME-Scene-$SCENE_NUMBER-$IMAGE_NUMBER',
        output_dir=None,
        downscale_factor=1,
        show_progress=False):
    # type: (...) -> bool
    """

    TODO: Documentation.

    Arguments:
        quality_or_compression: For image_extension=jpg or webp, represents encoding quality,
        from 0-100 (higher indicates better quality). For WebP, 100 indicates lossless.
        Default value in the CLI is 95 for JPEG, and 100 for WebP.

        If image_extension=png, represents the compression rate, from 0-9. Higher values
        produce smaller files but result in longer compression time. This setting does not
        affect image quality (lossless PNG), only file size. Default value in the CLI is 3.

        [default: 95]

    Returns:
        True if all requested images were generated & saved successfully, False otherwise.

    """

    if not scene_list:
        return True
    if num_images <= 0:
        raise ValueError()

    imwrite_param = []
    available_extensions = get_cv2_imwrite_params()
    if quality_or_compression is not None:
        if image_extension in available_extensions:
            imwrite_param = [
                available_extensions[image_extension], quality_or_compression
            ]
        else:
            valid_extensions = str(list(available_extensions.keys()))
            raise RuntimeError(
                'Invalid image extension, must be one of (case-sensitive): %s'
                % valid_extensions)

    # Reset video manager and downscale factor.
    video_manager.release()
    video_manager.reset()
    video_manager.set_downscale_factor(downscale_factor)
    video_manager.start()

    # Setup flags and init progress bar if available.
    completed = True
    progress_bar = None
    if tqdm and show_progress:
        progress_bar = tqdm(total=len(scene_list) * num_images, unit='images')

    filename_template = Template(image_name_template)

    scene_num_format = '%0'
    scene_num_format += str(
        max(3,
            math.floor(math.log(len(scene_list), 10)) + 1)) + 'd'
    image_num_format = '%0'
    image_num_format += str(math.floor(math.log(num_images, 10)) + 2) + 'd'

    timecode_list = dict()

    for i in range(len(scene_list)):
        timecode_list[i] = []

    if num_images == 1:
        for i, (start_time, end_time) in enumerate(scene_list):
            duration = end_time - start_time
            timecode_list[i].append(start_time +
                                    int(duration.get_frames() / 2))
    else:
        middle_images = num_images - 2
        for i, (start_time, end_time) in enumerate(scene_list):
            timecode_list[i].append(start_time)

            if middle_images > 0:
                duration = (end_time.get_frames() -
                            1) - start_time.get_frames()
                duration_increment = None
                duration_increment = int(duration / (middle_images + 1))
                for j in range(middle_images):
                    timecode_list[i].append(start_time +
                                            ((j + 1) * duration_increment))
            # End FrameTimecode is always the same frame as the next scene's start_time
            # (one frame past the end), so we need to subtract 1 here.
            timecode_list[i].append(end_time - 1)

    for i in timecode_list:
        for j, image_timecode in enumerate(timecode_list[i]):
            video_manager.seek(image_timecode)
            video_manager.grab()
            ret_val, frame_im = video_manager.retrieve()
            if ret_val:
                file_path = '%s.%s' % (filename_template.safe_substitute(
                    VIDEO_NAME=video_name,
                    SCENE_NUMBER=scene_num_format % (i + 1),
                    IMAGE_NUMBER=image_num_format % (j + 1)), image_extension)
                cv2.imwrite(get_and_create_path(file_path, output_dir),
                            frame_im, imwrite_param)
            else:
                completed = False
                break
            if progress_bar:
                progress_bar.update(1)

    return completed
コード例 #7
0
def generate_images(cap, scene_list, num_images=1, video_name="testvid"):
    # type: (List[Tuple[FrameTimecode, FrameTimecode]) -> None

    if not scene_list:
        return

    imwrite_param = []
    imwrite_param = [get_cv2_imwrite_params()['jpg'], None]

    #video_manager = VideoManager([url])
    # Reset video manager and downscale factor.
    #video_manager.release()
    #video_manager.reset()
    #video_manager.set_downscale_factor(1)
    #video_manager.start()

    # Setup flags and init progress bar if available.
    completed = True

    scene_num_format = '%0'
    scene_num_format += str(
        max(3,
            math.floor(math.log(len(scene_list), 10)) + 1)) + 'd'
    image_num_format = '%0'
    image_num_format += str(math.floor(math.log(num_images, 10)) + 2) + 'd'

    timecode_list = dict()
    image_filenames = dict()

    for i in range(len(scene_list)):
        timecode_list[i] = []
        image_filenames[i] = []

    if num_images == 1:
        for i, (start_time, end_time) in enumerate(scene_list):
            duration = end_time - start_time
            timecode_list[i].append(start_time +
                                    int(duration.get_frames() / 2))

    else:
        middle_images = num_images - 2
        for i, (start_time, end_time) in enumerate(scene_list):
            timecode_list[i].append(start_time)

            if middle_images > 0:
                duration = (end_time.get_frames() -
                            1) - start_time.get_frames()
                duration_increment = None
                duration_increment = int(duration / (middle_images + 1))
                for j in range(middle_images):
                    timecode_list[i].append(start_time +
                                            ((j + 1) * duration_increment))

            # End FrameTimecode is always the same frame as the next scene's start_time
            # (one frame past the end), so we need to subtract 1 here.
            timecode_list[i].append(end_time - 1)

    urls = []

    #image_timecode = "00:00:06.480"

    #dt_obj = datetime.strptime(str(image_timecode),'%H:%M:%S.%f')
    #millisec = dt_obj.timestamp() * 1000
    #print(str(image_timecode) + str(millisec))

    # Let's use Amazon S3
    s3 = boto3.client('s3',
                      aws_access_key_id=os.getenv("AWS_ACCESS_KEY_ID"),
                      aws_secret_access_key=os.getenv("AWS_SECRET_ACCESS_KEY"))

    #for key in s3.list_objects(Bucket='motion-snapshots')['Contents']:
    #print(key['Key'])
    #urls.append(str(key['Key']))

    try:

        for i in timecode_list:
            for j, image_timecode in enumerate(timecode_list[i]):

                cap.set(cv2.CAP_PROP_POS_MSEC,
                        get_timestamp_to_milliseconds(str(image_timecode)))
                #cap.set(cv2.CAP_PROP_POS_MSEC,image_timecode.get_frames())

                #urls.append(str(image_timecode))
                #video_manager.seek(image_timecode)
                #video_manager.grab()
                #ret_val, frame_im = video_manager.retrieve()
                ret, frame = cap.read(
                )  # Retrieves the frame at the specified second

                if ret:

                    imageName = randomword(10) + ".jpg"
                    image_string = cv2.imencode('.jpg', frame)[1].tostring()
                    s3.put_object(Bucket="motion-snapshots",
                                  Key="images/" + imageName,
                                  Body=image_string,
                                  ACL='public-read',
                                  ContentType='image/jpeg')
                    #location = boto3.client('s3').get_bucket_location(Bucket="motion-snapshots")['LocationConstraint']
                    url = "https://s3-%s.amazonaws.com/%s/%s" % (
                        "us-east-2", "motion-snapshots", "images/" + imageName)
                    #url = "https://s3-%s.amazonaws.com/%s/%s" % (location, "motion-snapshots", "images/" + imageName)
                    print("Saved Image: " + str(url))
                    urls.append(url)

                else:
                    completed = False
                    break

        #if not completed:
        #logging.error('Could not generate all output images.')
    #except WritingError as er:
    #logging.error(er)

    finally:
        return urls
コード例 #8
0
def find_scenes(video_path, generate_images=False):
    """
    This method slicing a video to a list of scenes, each scene will have a similar color distributions.
    This function allows to generate images for each scene.
    :param video_path: The path to the video for finding scenes
    :param generate_images: whether to generate images or not
    :return: a list of scenes
    """
    video_manager = VideoManager([video_path])
    stats_manager = StatsManager()
    scene_manager = SceneManager(stats_manager)

    scene_manager.add_detector(ContentDetector())
    base_timecode = video_manager.get_base_timecode()

    stats_file_path = '%s.stats.csv' % video_path

    scene_list = []
    output = []

    try:
        if os.path.exists(stats_file_path):
            with open(stats_file_path, 'r') as stats_file:
                stats_manager.load_from_csv(stats_file, base_timecode)

        video_manager.set_downscale_factor()
        video_manager.start()
        scene_manager.detect_scenes(frame_source=video_manager)
        scene_list = scene_manager.get_scene_list(base_timecode)

        print("Starting to generate images from scenelist")
        num_images = 2

        if not scene_list:
            return

        available_extensions = get_cv2_imwrite_params()
        image_extension = "jpg"

        imwrite_param = [available_extensions[image_extension], 100]

        video_manager.release()
        video_manager.reset()
        video_manager.set_downscale_factor(1)
        video_manager.start()

        completed = True
        print('Generating output images (%d per scene)...', num_images)

        filename_template = Template(
            "$VIDEO_NAME-Scene-$SCENE_NUMBER-$IMAGE_NUMBER")

        scene_num_format = '%0'
        scene_num_format += str(
            max(3,
                math.floor(math.log(len(scene_list), 10)) + 1)) + 'd'
        image_num_format = '%0'
        image_num_format += str(math.floor(math.log(num_images, 10)) + 2) + 'd'

        timecode_list = dict()

        fps = scene_list[0][0].framerate

        timecode_list = [[
            FrameTimecode(int(f), fps=fps) for f in [
                a[len(a) // 2] if (0 < j < num_images - 1) or num_images == 1
                else min(a[0] + 0, a[-1]) if j == 0 else max(a[-1] - 0, a[0])
                for j, a in enumerate(np.array_split(r, num_images))
            ]
        ] for i, r in enumerate([
            r if r.stop - r.start >= num_images else list(r) + [r.stop - 1] *
            (num_images - len(r))
            for r in (range(start.get_frames(), end.get_frames())
                      for start, end in scene_list)
        ])]

        image_filenames = {i: [] for i in range(len(timecode_list))}

        for i, tl in enumerate(timecode_list):
            for j, image_timecode in enumerate(tl):
                video_manager.seek(image_timecode)
                video_manager.grab()
                ret_val, frame_im = video_manager.retrieve()
                if ret_val:
                    file_path = '%s.%s' % (filename_template.safe_substitute(
                        VIDEO_NAME=video_path,
                        SCENE_NUMBER=scene_num_format % (i + 1),
                        IMAGE_NUMBER=image_num_format % (j + 1),
                        FRAME_NUMBER=image_timecode.get_frames()),
                                           image_extension)
                    image_filenames[i].append(file_path)
                    abs_file_path = get_and_create_path(file_path, "output")
                    output.append(frame_im)
                    if generate_images:
                        print(abs_file_path)
                        cv2.imwrite(abs_file_path, frame_im, imwrite_param)
                else:
                    completed = False
                    break

        if not completed:
            print('Could not generate all output images.')

    finally:
        video_manager.release()

    return output