Skip to content

Frameaverage

mg_pixelarray

mg_pixelarray(self, width=640, target_name=None, overwrite=True)

Creates a 'Frame-Averaged Pixel Array' of a video by reducing each frame to a single pixel and arranging all frames into a single image. This is equivalent to the bash script that scales each frame to 1x1 pixel and then tiles them into a grid.

Based on the original bash script concept: - Each frame is reduced to a single pixel (average color of the frame) - All pixel values are arranged in a grid with specified width - Height is calculated automatically based on total frames and width

Parameters:

Name Type Description Default
width int

Width of the output image in pixels (number of frame-pixels per row). Defaults to 640.

640
target_name str

The name of the output image file. If None, uses input filename with 'framearray' suffix. Defaults to None.

None
overwrite bool

Whether to allow overwriting existing files or to automatically increment target filenames to avoid overwriting. Defaults to True.

True

Returns:

Name Type Description
MgImage

A new MgImage pointing to the output frame-averaged pixel array image file.

Source code in musicalgestures/_frameaverage.py
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
def mg_pixelarray(self, width=640, target_name=None, overwrite=True):
    """
    Creates a 'Frame-Averaged Pixel Array' of a video by reducing each frame to a single pixel
    and arranging all frames into a single image. This is equivalent to the bash script that
    scales each frame to 1x1 pixel and then tiles them into a grid.

    Based on the original bash script concept:
    - Each frame is reduced to a single pixel (average color of the frame)
    - All pixel values are arranged in a grid with specified width
    - Height is calculated automatically based on total frames and width

    Args:
        width (int, optional): Width of the output image in pixels (number of frame-pixels per row). 
                              Defaults to 640.
        target_name (str, optional): The name of the output image file. If None, uses input filename 
                                   with '_framearray_<width>' suffix. Defaults to None.
        overwrite (bool, optional): Whether to allow overwriting existing files or to automatically 
                                  increment target filenames to avoid overwriting. Defaults to True.

    Returns:
        MgImage: A new MgImage pointing to the output frame-averaged pixel array image file.
    """

    target_name = resolve_filename(self.of, f"_pixelarray_{width}.png", target_name, overwrite)

    # Get video properties
    frames = get_framecount(self.filename)
    height = int(np.ceil(frames / width))
    video_length = get_length(self.filename)

    # Method 1: Using FFmpeg (similar to the bash script)
    # This directly replicates the bash script functionality
    cmd = [
        'ffmpeg', '-y', '-i', self.filename,
        '-vf', f'scale=1:1,tile={width}x{height}',
        '-frames:v', '1',
        target_name
    ]

    ffmpeg_cmd(cmd, video_length, pb_prefix='Creating frame-averaged pixel array:')

    # Save result as the pixelarray for parent MgVideo
    self.pixelarray = MgImage(target_name)

    return self.pixelarray

mg_pixelarray_cv2

mg_pixelarray_cv2(self, width=640, target_name=None, overwrite=True)

Alternative implementation using OpenCV for more control over the process. Creates a 'Frame-Averaged Pixel Array' by reading each frame, calculating its average color, and arranging these average colors in a grid.

Parameters:

Name Type Description Default
width int

Width of the output image in pixels. Defaults to 640.

640
target_name str

The name of the output image file. Defaults to None.

None
overwrite bool

Whether to allow overwriting existing files. Defaults to True.

True

Returns:

Name Type Description
MgImage

A new MgImage pointing to the output frame-averaged pixel array image file.

Source code in musicalgestures/_frameaverage.py
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
def mg_pixelarray_cv2(self, width=640, target_name=None, overwrite=True):
    """
    Alternative implementation using OpenCV for more control over the process.
    Creates a 'Frame-Averaged Pixel Array' by reading each frame, calculating its average color,
    and arranging these average colors in a grid.

    Args:
        width (int, optional): Width of the output image in pixels. Defaults to 640.
        target_name (str, optional): The name of the output image file. Defaults to None.
        overwrite (bool, optional): Whether to allow overwriting existing files. Defaults to True.

    Returns:
        MgImage: A new MgImage pointing to the output frame-averaged pixel array image file.
    """

    target_name = resolve_filename(self.of, f"_pixelarray_cv2_{width}.png", target_name, overwrite)

    # Open video
    cap = cv2.VideoCapture(self.filename)
    total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))

    # Calculate output dimensions
    height = int(np.ceil(total_frames / width))

    pb = MgProgressbar(total=total_frames, prefix='Creating frame-averaged pixel array (cv2):')

    # Create output array
    if self.color:
        output_array = np.zeros((height, width, 3), dtype=np.uint8)
    else:
        output_array = np.zeros((height, width), dtype=np.uint8)

    frame_count = 0

    try:
        while True:
            ret, frame = cap.read()
            if not ret:
                break

            # Convert to grayscale if needed
            if not self.color:
                frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
                average_color = np.mean(frame)
            else:
                # Calculate average color for each channel
                average_color = np.mean(frame, axis=(0, 1))

            # Calculate position in output grid
            row = frame_count // width
            col = frame_count % width

            # Only process if within our output bounds
            if row < height:
                output_array[row, col] = average_color.astype(np.uint8)

            frame_count += 1
            pb.progress(frame_count)

    finally:
        cap.release()

    # Save the image
    cv2.imwrite(target_name, output_array)

    # Save result as the pixelarray_cv2 for parent MgVideo
    self.pixelarray_cv2 = MgImage(target_name)

    return self.pixelarray_cv2

mg_pixelarray_stats

mg_pixelarray_stats(self, width=640, include_stats=True)

Creates a frame-averaged pixel array and optionally returns statistics about the video. This function provides additional information similar to the bash script's output.

Parameters:

Name Type Description Default
width int

Width of the output image in pixels. Defaults to 640.

640
include_stats bool

Whether to return detailed statistics. Defaults to True.

True

Returns:

Name Type Description
dict

Dictionary containing the generated MgImage and optional statistics.

Source code in musicalgestures/_frameaverage.py
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
def mg_pixelarray_stats(self, width=640, include_stats=True):
    """
    Creates a frame-averaged pixel array and optionally returns statistics about the video.
    This function provides additional information similar to the bash script's output.

    Args:
        width (int, optional): Width of the output image in pixels. Defaults to 640.
        include_stats (bool, optional): Whether to return detailed statistics. Defaults to True.

    Returns:
        dict: Dictionary containing the generated MgImage and optional statistics.
    """

    # Get video properties for statistics (similar to bash script)
    cap = cv2.VideoCapture(self.filename)
    total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
    fps = cap.get(cv2.CAP_PROP_FPS)
    duration_seconds = total_frames / fps if fps > 0 else 0
    cap.release()

    # Calculate dimensions
    height = int(np.ceil(total_frames / width))

    # Create the frame-averaged pixel array
    result_image = mg_pixelarray(self, width=width)

    result = {
        'image': result_image,
        'filename': os.path.abspath(self.filename)
    }

    if include_stats:
        # Format duration as HH:MM:SS.ms
        hours = int(duration_seconds // 3600)
        minutes = int((duration_seconds % 3600) // 60)
        seconds = duration_seconds % 60
        duration_str = f"{hours:02d}:{minutes:02d}:{seconds:06.3f}"

        result.update({
            'duration': duration_str,
            'duration_seconds': int(duration_seconds),
            'fps': int(fps + 0.5),
            'total_frames': total_frames,
            'output_width': width,
            'output_height': height,
            'filter_description': f"scale=1:1,tile={width}x{height}"
        })

    return result