Source code for stable_datasets.images.olivetti_faces

import numpy as np
from PIL import Image as PILImage
from scipy.io import loadmat

from stable_datasets.schema import (
    ClassLabel,
    DatasetInfo,
    DatasetSource,
    DownloadInfo,
    Features,
    Image,
    Version,
)
from stable_datasets.utils import BaseDatasetBuilder


[docs] class OlivettiFaces(BaseDatasetBuilder): """Build the Olivetti Faces dataset for identity classification. The dataset contains 400 grayscale 64x64 images of 40 individuals, with 10 images per individual. This builder defines a deterministic split of 320 training images and 80 test images, using 8 training and 2 test images per identity. This split is not an official split provided by the original source. """ VERSION = Version("1.0.0") SOURCE = DatasetSource( license="Unknown", homepage="https://scikit-learn.org/stable/datasets/real_world.html#olivetti-faces-dataset", citation="""@misc{olivettifaces, title={The Olivetti Faces Dataset}, howpublished={AT&T Laboratories Cambridge} }""", assets={ "train": DownloadInfo(url="https://ndownloader.figshare.com/files/5976027"), "test": DownloadInfo(url="https://ndownloader.figshare.com/files/5976027"), }, ) def _info(self): return DatasetInfo( description=( "The Olivetti Faces dataset contains 400 grayscale images " "of 40 individuals, with 10 images per individual. " "Each image has a resolution of 64x64 pixels. " "A deterministic stratified split assigns 8 images per " "individual to training and 2 to testing." ), features=Features( { "image": Image(), "label": ClassLabel(num_classes=40), } ), supervised_keys=("image", "label"), homepage=self.SOURCE["homepage"], citation=self.SOURCE["citation"], ) def _generate_examples(self, data_path, split): """Generate grayscale images and identity labels.""" raw_faces = loadmat(data_path)["faces"] if raw_faces.shape != (4096, 400): raise ValueError(f"Unexpected Olivetti Faces shape: {raw_faces.shape}") # Reproduce the official scikit-learn image orientation. images = raw_faces.T.copy().reshape((400, 64, 64)).transpose(0, 2, 1) labels = np.repeat(np.arange(40), 10) for idx, (image, label) in enumerate(zip(images, labels)): # Keep a deterministic 8/2 split for each identity. position = idx % 10 if split == "train" and position >= 8: continue if split == "test" and position < 8: continue pil_image = PILImage.fromarray(image) yield ( idx, { "image": pil_image, "label": int(label), }, )