Exemplo n.º 1
0
def skip_frames_ffmpeg(filename, skip=0):
    if skip == 0:
        return

    import os

    of, fex = os.path.splitext(filename)

    pts_ratio = 1 / (skip + 1)
    atempo_ratio = skip + 1

    outname = of + '_skip' + fex

    if has_audio(filename):
        cmd = [
            'ffmpeg', '-y', '-i', filename, '-filter_complex',
            f'[0:v]setpts={pts_ratio}*PTS[v];[0:a]atempo={atempo_ratio}[a]',
            '-map', '[v]', '-map', '[a]', '-q:v', '3', '-shortest', outname
        ]
    else:
        cmd = [
            'ffmpeg', '-y', '-i', filename, '-filter_complex',
            f'[0:v]setpts={pts_ratio}*PTS[v]', '-map', '[v]', '-q:v', '3',
            outname
        ]

    ffmpeg_cmd(cmd, get_length(filename), pb_prefix='Skipping frames:')
Exemplo n.º 2
0
def contrast_brightness_ffmpeg(filename, contrast=0, brightness=0):
    """
    Applies contrast and brightness adjustments on the source video using ffmpeg.

    Args:
        filename (str): Path to the video to process.
        contrast (int or float, optional): Increase or decrease contrast. Values range from -100 to 100. Defaults to 0.
        brightness (int or float, optional): Increase or decrease brightness. Values range from -100 to 100. Defaults to 0.

    Outputs:
        `filename`_cb.<file extension>
    """
    if contrast == 0 and brightness == 0:
        return

    import os
    import numpy as np

    of, fex = os.path.splitext(filename)

    # keeping values in sensible range
    contrast = np.clip(contrast, -100.0, 100.0)
    brightness = np.clip(brightness, -100.0, 100.0)

    # ranges are "handpicked" so that the results are close to the results of contrast_brightness_cv2 (deprecated)
    if contrast == 0:
        p_saturation, p_contrast, p_brightness = 0, 0, 0
    elif contrast > 0:
        p_saturation = scale_num(contrast, 0, 100, 1, 1.9)
        p_contrast = scale_num(contrast, 0, 100, 1, 2.3)
        p_brightness = scale_num(contrast, 0, 100, 0, 0.04)
    elif contrast < 0:
        p_saturation = scale_num(contrast, 0, -100, 1, 0)
        p_contrast = scale_num(contrast, 0, -100, 1, 0)
        p_brightness = 0

    if brightness != 0:
        p_brightness += brightness / 100

    outname = of + '_cb' + fex

    cmd = [
        'ffmpeg', '-y', '-i', filename, '-vf',
        f'eq=saturation={p_saturation}:contrast={p_contrast}:brightness={p_brightness}',
        '-q:v', '3', "-c:a", "copy", outname
    ]

    ffmpeg_cmd(cmd,
               get_length(filename),
               pb_prefix='Adjusting contrast and brightness:')
Exemplo n.º 3
0
def contrast_brightness_ffmpeg(filename, contrast=0, brightness=0):
    if contrast == 0 and brightness == 0:
        return

    import os
    import numpy as np

    of, fex = os.path.splitext(filename)

    # keeping values in sensible range
    contrast = np.clip(contrast, -100.0, 100.0)
    brightness = np.clip(brightness, -100.0, 100.0)

    # ranges are "handpicked" so that the results are close to the results of mg_contrast_brightness
    if contrast == 0:
        p_saturation, p_contrast, p_brightness = 0, 0, 0
    elif contrast > 0:
        p_saturation = scale_num(contrast, 0, 100, 1, 1.9)
        p_contrast = scale_num(contrast, 0, 100, 1, 2.3)
        p_brightness = scale_num(contrast, 0, 100, 0, 0.04)
    elif contrast < 0:
        p_saturation = scale_num(contrast, 0, -100, 1, 0)
        p_contrast = scale_num(contrast, 0, -100, 1, 0)
        p_brightness = 0

    if brightness != 0:
        p_brightness += brightness / 100

    outname = of + '_cb' + fex

    cmd = [
        'ffmpeg', '-y', '-i', filename, '-vf',
        f'eq=saturation={p_saturation}:contrast={p_contrast}:brightness={p_brightness}',
        '-q:v', '3', "-c:a", "copy", outname
    ]

    ffmpeg_cmd(cmd,
               get_length(filename),
               pb_prefix='Adjusting contrast and brightness:')
Exemplo n.º 4
0
def skip_frames_ffmpeg(filename, skip=0):
    """
    Time-shrinks the video by skipping (discarding) every n frames determined by `skip`. To discard half of the frames (ie. double the speed of the video) use `skip=1`.

    Args:
        filename (str): Path to the video to process.
        skip (int, optional): Discard `skip` frames before keeping one. Defaults to 0.

    Outputs:
        `filename`_skip.<file extension>
    """
    if skip == 0:
        return

    import os

    of, fex = os.path.splitext(filename)

    pts_ratio = 1 / (skip + 1)
    atempo_ratio = skip + 1

    outname = of + '_skip' + fex

    if has_audio(filename):
        cmd = [
            'ffmpeg', '-y', '-i', filename, '-filter_complex',
            f'[0:v]setpts={pts_ratio}*PTS[v];[0:a]atempo={atempo_ratio}[a]',
            '-map', '[v]', '-map', '[a]', '-q:v', '3', '-shortest', outname
        ]
    else:
        cmd = [
            'ffmpeg', '-y', '-i', filename, '-filter_complex',
            f'[0:v]setpts={pts_ratio}*PTS[v]', '-map', '[v]', '-q:v', '3',
            outname
        ]

    ffmpeg_cmd(cmd, get_length(filename), pb_prefix='Skipping frames:')
Exemplo n.º 5
0
def videograms_ffmpeg(self):
    """
    Usees FFMPEG as backend. Averages videoframes by axes, and creates two images of the horizontal-axis and vertical-axis stacks.
    In these stacks, a single row or column corresponds to a frame from the source video, and the index
    of the row or column corresponds to the index of the source frame.

    Outputs
    -------
    - `filename`_vgx.png

        A horizontal videogram of the source video.
    - `filename`_vgy.png

        A vertical videogram of the source video.

    Returns
    -------
    - list(MgImage, MgImage)

        A tuple with the string paths to the horizontal and vertical videograms respectively. 
    """

    width, height = get_widthheight(self.filename)
    framecount = get_framecount(self.filename)
    length = get_length(self.filename)

    outname = self.of + '_vgy.png'
    cmd = ['ffmpeg', '-y', '-i', self.filename, '-frames', '1', '-vf',
           f'scale=1:{height}:sws_flags=area,normalize,tile={framecount}x1', outname]
    ffmpeg_cmd(cmd, length, pb_prefix="Rendering horizontal videogram:")

    outname = self.of + '_vgx.png'
    cmd = ['ffmpeg', '-y', '-i', self.filename, '-frames', '1', '-vf',
           f'scale={width}:1:sws_flags=area,normalize,tile=1x{framecount}', outname]
    ffmpeg_cmd(cmd, length, pb_prefix="Rendering vertical videogram:")

    return MgList([MgImage(self.of+'_vgx.png'), MgImage(self.of+'_vgy.png')])
Exemplo n.º 6
0
def history_ffmpeg(self,
                   filename='',
                   history_length=10,
                   weights=1,
                   normalize=False,
                   norm_strength=1,
                   norm_smooth=0):
    """
    This function  creates a video where each frame is the average of the 
    n previous frames, where n is determined by `history_length`.
    The history frames are summed up and normalized, and added to the 
    current frame to show the history. Based on ffmpeg.

    Parameters
    ----------
    - filename : str, optional

        Path to the input video file. If not specified the video file pointed to by the MgObject is used.
    - history_length : int, optional

        Default is 10. Number of frames to be saved in the history tail.

    - weights: int, float, str, list, optional

        Default is 1. Defines the weight or weights applied to the frames in the history tail. If given as list
        the first element in the list will correspond to the weight of the newest frame in the tail. If given as
        a str - like "3 1.2 1" - it will be automatically converted to a list - like [3, 1.2, 1].

    - normalize: bool, optional

        Default is `False` (no normalization). If `True`, the history video will be normalized. This can be useful
        when processing motion (frame difference) videos.

    - norm_strength: int, float, optional

        Default is 1. Defines the strength of the normalization where 1 represents full strength.

    - norm_smooth: int, optional

        Default is 0 (no smoothing). Defines the number of previous frames to use for temporal smoothing. The input 
        range of each channel is smoothed using a rolling average over the current frame and the `norm_smooth` previous frames.

    Outputs
    -------
    - `filename`_history.avi

    Returns
    -------
    - MgObject 
        A new MgObject pointing to the output '_history' video file.
    """

    if filename == '':
        filename = self.filename

    of, fex = os.path.splitext(filename)

    if type(weights) in [int, float]:
        weights_map = np.ones(history_length)
        weights_map[-1] = weights
        str_weights = ' '.join([str(weight) for weight in weights_map])
    elif type(weights) == list:
        typecheck_list = [type(item) in [int, float] for item in weights]
        if False in typecheck_list:
            raise ParameterError(
                'Found wrong type(s) in the list of weights. Use ints and floats.'
            )
        elif len(weights) > history_length:
            raise ParameterError(
                'history_length must be greater than or equal to the number of weights specified in weights.'
            )
        else:
            weights_map = np.ones(history_length - len(weights))
            weights.reverse()
            weights_map = list(weights_map)
            weights_map += weights
            str_weights = ' '.join([str(weight) for weight in weights_map])
    elif type(weights) == str:
        weights_as_list = weights.split()
        typecheck_list = [
            type(item) in [int, float] for item in weights_as_list
        ]
        if False in typecheck_list:
            raise ParameterError(
                'Found wrong type(s) in the list of weights. Use ints and floats.'
            )
        elif len(weights) > history_length:
            raise ParameterError(
                'history_length must be greater than or equal to the number of weights specified in weights.'
            )
        else:
            weights_map = np.ones(history_length - len(weights_as_list))
            weights_as_list.reverse()
            weights_map += weights_as_list
            str_weights = ' '.join([str(weight) for weight in weights_map])
    else:
        raise ParameterError(
            'Wrong type used for weights. Use int, float, str, or list.')

    if type(normalize) != bool:
        raise ParameterError('Wrong type used for normalize. Use only bool.')

    if normalize:
        if type(norm_strength) not in [int, float]:
            raise ParameterError(
                'Wrong type used for norm_strength. Use int or float.')
        if type(norm_smooth) != int:
            raise ParameterError(
                'Wrong type used for norm_smooth. Use only int.')

    outname = of + '_history' + fex
    if normalize:
        if norm_smooth != 0:
            cmd = [
                'ffmpeg', '-y', '-i', filename, '-filter_complex',
                f'tmix=frames={history_length}:weights={str_weights},normalize=independence=0:strength={norm_strength}:smoothing={norm_smooth}',
                '-q:v', '3', '-c:a', 'copy', outname
            ]
        else:
            cmd = [
                'ffmpeg', '-y', '-i', filename, '-filter_complex',
                f'tmix=frames={history_length}:weights={str_weights},normalize=independence=0:strength={norm_strength}',
                '-q:v', '3', '-c:a', 'copy', outname
            ]
    else:
        cmd = [
            'ffmpeg', '-y', '-i', filename, '-vf',
            f'tmix=frames={history_length}:weights={str_weights}', '-q:v', '3',
            '-c:a', 'copy', outname
        ]

    # success = ffmpeg_cmd(cmd, get_length(filename),
    #                      pb_prefix='Rendering history video:')

    ffmpeg_cmd(cmd, get_length(filename), pb_prefix='Rendering history video:')

    # if success:
    #     destination_video = self.of + '_history' + self.fex
    #     return musicalgestures.MgObject(destination_video, color=self.color, returned_by_process=True)

    destination_video = self.of + '_history' + self.fex
    return musicalgestures.MgObject(destination_video,
                                    color=self.color,
                                    returned_by_process=True)
Exemplo n.º 7
0
def videograms_ffmpeg(self):
    """
    Renders horizontal and vertical videograms of the source video using ffmpeg. Averages videoframes by axes, and creates two images of the horizontal-axis and vertical-axis stacks. In these stacks, a single row or column corresponds to a frame from the source video, and the index of the row or column corresponds to the index of the source frame.

    Outputs:
        `self.filename`_vgx.png
        `self.filename`_vgy.png

    Returns:
        MgList(MgImage, MgImage): An MgList with the MgImage objects referring to the horizontal and vertical videograms respectively. 
    """

    width, height = get_widthheight(self.filename)
    framecount = get_framecount(self.filename)

    def calc_skipfactor(width, height, framecount):
        """
        Helper function to calculate the necessary frame-skipping to avoid integer overflow. This makes sure that we can succesfully create videograms even on many-hours-long videos as well.

        Args:
            width (int): The width of the video.
            height (int): The height of the video.
            framecount (int): The number of frames in the video.

        Returns:
            list(int, int): The necessary dilation factors to apply on the video for the horizontal and vertical videograms, respectively.
        """

        intmax = 2147483647
        skipfactor_x = int(
            math.ceil(framecount * 8 / (intmax / (height + 128) - 1024)))
        skipfactor_y = int(
            math.ceil(framecount / (intmax / ((width * 8) + 1024) - 128)))
        return skipfactor_x, skipfactor_y

    testx, testy = calc_skipfactor(width, height, framecount)

    if testx > 1 or testy > 1:
        necessary_skipfactor = max([testx, testy])
        print(
            f'{os.path.basename(self.filename)} is too large to process. Applying minimal skipping necessary...'
        )

        skip_frames_ffmpeg(self.filename, skip=necessary_skipfactor - 1)

        shortened_file = self.of + '_skip' + self.fex
        framecount = get_framecount(shortened_file)
        length = get_length(shortened_file)

        outname = self.of + '_skip_vgy.png'
        cmd = [
            'ffmpeg', '-y', '-i', shortened_file, '-vf',
            f'scale=1:{height}:sws_flags=area,normalize,tile={framecount}x1',
            '-aspect', f'{framecount}:{height}', '-frames', '1', outname
        ]
        ffmpeg_cmd(cmd,
                   length,
                   stream=False,
                   pb_prefix="Rendering horizontal videogram:")

        outname = self.of + '_skip_vgx.png'
        cmd = [
            'ffmpeg', '-y', '-i', shortened_file, '-vf',
            f'scale={width}:1:sws_flags=area,normalize,tile=1x{framecount}',
            '-aspect', f'{width}:{framecount}', '-frames', '1', outname
        ]
        ffmpeg_cmd(cmd,
                   length,
                   stream=False,
                   pb_prefix="Rendering vertical videogram:")

        return MgList([
            MgImage(self.of + '_skip_vgx.png'),
            MgImage(self.of + '_skip_vgy.png')
        ])

    else:
        length = get_length(self.filename)

        outname = self.of + '_vgy.png'
        cmd = [
            'ffmpeg', '-y', '-i', self.filename, '-frames', '1', '-vf',
            f'scale=1:{height}:sws_flags=area,normalize,tile={framecount}x1',
            '-aspect', f'{framecount}:{height}', outname
        ]
        ffmpeg_cmd(cmd,
                   length,
                   stream=False,
                   pb_prefix="Rendering horizontal videogram:")

        outname = self.of + '_vgx.png'
        cmd = [
            'ffmpeg', '-y', '-i', self.filename, '-frames', '1', '-vf',
            f'scale={width}:1:sws_flags=area,normalize,tile=1x{framecount}',
            '-aspect', f'{width}:{framecount}', outname
        ]
        ffmpeg_cmd(cmd,
                   length,
                   stream=False,
                   pb_prefix="Rendering vertical videogram:")

        return MgList(
            [MgImage(self.of + '_vgx.png'),
             MgImage(self.of + '_vgy.png')])