Source code for stable_datasets.images.usps
import bz2
import numpy as np
from stable_datasets.schema import ClassLabel, DatasetInfo, DatasetSource, DownloadInfo, Features, Image, Version
from stable_datasets.utils import BaseDatasetBuilder
[docs]
class USPS(BaseDatasetBuilder):
"""USPS handwritten digits from the LIBSVM distribution."""
VERSION = Version("1.0.0")
SOURCE = DatasetSource(
homepage="https://www.csie.ntu.edu.tw/~cjlin/libsvmtools/datasets/multiclass.html#usps",
citation="""@article{hull1994database,
author={Jonathan J. Hull},
title={A Database for Handwritten Text Recognition Research},
journal={IEEE Transactions on Pattern Analysis and Machine Intelligence},
volume={16},
number={5},
pages={550--554},
year={1994},
doi={10.1109/34.291440}
}""",
assets={
"train": DownloadInfo(url="https://www.csie.ntu.edu.tw/~cjlin/libsvmtools/datasets/multiclass/usps.bz2"),
"test": DownloadInfo(url="https://www.csie.ntu.edu.tw/~cjlin/libsvmtools/datasets/multiclass/usps.t.bz2"),
},
license="Unknown",
)
def _info(self):
return DatasetInfo(
description=(
"USPS contains 7,291 training and 2,007 test images of handwritten digits "
"in 10 classes. Images are 16x16 grayscale uint8 arrays. "
"LIBSVM labels 1 through 10 are mapped to class indices 0 through 9, "
"following torchvision.datasets.USPS."
),
features=Features({"image": Image(), "label": ClassLabel(num_classes=10)}),
supervised_keys=("image", "label"),
homepage=self.SOURCE["homepage"],
license=self.SOURCE["license"],
citation=self.SOURCE["citation"],
)
def _generate_examples(self, data_path, split):
with bz2.open(data_path, "rt", encoding="ascii") as stream:
for index, line in enumerate(stream):
fields = line.split()
try:
if not fields:
raise ValueError("empty sample")
label = int(fields[0])
if not 1 <= label <= 10:
raise ValueError("label must be between 1 and 10")
# LIBSVM omits zero-valued features in sparse representations.
pixels = np.zeros(256, dtype=np.float32)
seen = set()
for field in fields[1:]:
feature, value = field.split(":")
feature = int(feature)
value = float(value)
if not 1 <= feature <= 256 or feature in seen:
raise ValueError("invalid or duplicate pixel index")
if not np.isfinite(value) or not -1.0 <= value <= 1.0:
raise ValueError("pixel value must be finite and in [-1, 1]")
pixels[feature - 1] = value
seen.add(feature)
except (ValueError, OverflowError) as error:
raise ValueError(f"Invalid USPS sample at line {index + 1}: {error}") from error
# Preserve the torchvision USPS conversion, including uint8 truncation.
image = ((pixels.reshape(16, 16) + 1) / 2 * 255).astype(np.uint8)
yield index, {"image": image, "label": label - 1}