Skip to content

Media inputs and outputs

Every component takes X as a container of samples and returns one output per sample, in input order. The class name fixes the type of every sample, so a string is a text in a Text class and a path or URL in a media class.

Sample containers

X can be a list, a tuple, a one-dimensional NumPy array, a pandas Series, or a single-column two-dimensional array or DataFrame. Each element is one sample. An input with more than one column raises ValueError, except in Multimodal classes, which read every row of a DataFrame as one sample.

import pandas as pd
from skai.generation import TextToTextGenerator

summarizer = TextToTextGenerator(instructions='Summarize the text.', max_length=2, backend='dummy').fit()
print(summarizer.predict(pd.DataFrame({'text': ['First long text.', 'Second long text.']})))
['First long' 'Second long']

Accepted samples

Input Accepted forms
Text str
Image local path, http(s) URL, encoded bytes, PIL image, NumPy array of shape (H, W) or (H, W, C)
Audio local path, http(s) URL, encoded bytes, skai.core.Audio, (samples, sample_rate) tuple
Video local path, http(s) URL, encoded bytes, skai.core.Video
Multimodal a dict of named texts, images, audio, videos or transcripts, or a DataFrame row of them

A text sample that is not a string raises skai.core.InvalidSampleError, a TypeError. A media sample that cannot be read or decoded raises skai.core.MediaReadError, a ValueError. Both name the index of the sample. With on_error='ignore', an unreadable media sample gives a missing output and a warning instead.

import numpy as np
from PIL import Image
from skai.generation import ImageToTextGenerator

describer = ImageToTextGenerator(instructions='Describe the image.', backend='dummy').fit()
print(describer.predict([Image.new('RGB', (64, 48)), np.zeros((16, 16), dtype=np.uint8)]))
['A 64x48 image.' 'A 16x16 image.']

Video classes sample n_frames frames evenly over every video, and send the audio track to backends that accept audio.

Multimodal samples

A Multimodal class reads every value of a sample by its own kind: strings are texts unless they are URLs or paths of existing media files, and the other values are read as images, audio or videos. The backend receives every part in one request, each named by its key. An operation that needs specific keys, such as video and audio for a lip sync, raises InvalidSampleError naming the missing key and the sample index.

import pandas as pd
from PIL import Image
from skai.classification import MultimodalClassifier
from skai.generation import MultimodalToTextGenerator

posts = pd.DataFrame({'image': [Image.new('RGB', (8, 8)), Image.new('RGB', (8, 8), 'red')], 'caption': ['hi', 'sale']})
print(MultimodalClassifier(labels=['safe', 'unsafe'], backend='dummy').fit().predict(posts))
captioner = MultimodalToTextGenerator(instructions='Write a caption.', backend='dummy').fit()
print(captioner.predict([{'photo': Image.new('RGB', (8, 8)), 'notes': 'taken in Crete'}]))
['unsafe' 'safe']
['photo: A 8x8 image. notes: taken in Crete']

MediaUnion builds multimodal samples inside a pipeline, as Pipelines shows.

List-valued samples

Segmenters return one list of regions per sample. Only ForEach, TopK and the list operations of generators, collage and concatenate, accept list-valued samples. Any other component raises InvalidSampleError and suggests wrapping the step in ForEach.

from skai.core import InvalidSampleError
from skai.generation import TextToTextGenerator
from skai.pipeline import ForEach

generator = TextToTextGenerator(instructions='Shorten the text.', max_length=1, backend='dummy')
try:
    generator.fit().predict([['First passage', 'Second passage']])
except InvalidSampleError as error:
    print(error)
print(ForEach(generator).fit_transform([['First passage', 'Second passage']]))
Sample 0 is a list, which TextToTextGenerator does not accept. Wrap the step in `ForEach` to apply it to every item.
[list(['First', 'Second'])]

Outputs

Text outputs are one-dimensional NumPy arrays of dtype=object that hold strings. Classifiers return labels that follow the targets seen in fit, embedders return float arrays of shape (n_samples, n_dimensions), extractors return one dict per sample, and segmenters return one list of region dicts per sample.

Components that generate images, audio or video take output_format:

output_format Images Audio Video
'object' (default) PIL image skai.core.Audio skai.core.Video
'bytes' PNG bytes WAV bytes MP4 bytes
'path' PNG file WAV file MP4 file

With output_format='path', every output is written to a new file in output_dir, or in a new temporary directory when output_dir is None. Existing files are never overwritten. Encoding video needs the video extra.

import tempfile
from skai.generation import TextToImageGenerator

generator = TextToImageGenerator(size=(64, 48), output_format='path', output_dir=tempfile.mkdtemp(), backend='dummy')
paths = generator.fit().predict(['a red square', 'a blue square'])
print(len(paths), paths[0].endswith('.png'))
2 True

Audio holds the samples array and the sample_rate, and Video holds the frames, the fps, an optional audio track and optional subtitles. Both have save and to_bytes methods, and both are accepted back as inputs by any component.

from skai.generation import AudioToTextGenerator, TextToAudioGenerator

speech = TextToAudioGenerator(backend='dummy').fit().predict(['Hello there'])
print(speech[0], AudioToTextGenerator(backend='dummy').fit().predict(speech))
Audio(duration=0.40s, sample_rate=16000) ['Speech of 0.40 seconds.']