brainhops.io.images.pillow
Two-dimensional raster images -- PNG, JPEG, BMP, GIF, WebP, PNM, JPEG 2000, TGA, ... -- read and written with Pillow.
This reader requires the pillow extra (pip install brainhops[pillow]).
from brainhops.io.images import load
from brainhops.io import save
image = load("photo.png") # PillowImage
image.data.shape # (width, height, 3): x, y, c
save(image, "photo.jpg", quality=95)
Data
Pillow decodes an image into rows of pixels, (rows, columns[,
samples]). The reader returns that array transposed to the brainhops
order -- a view, not a copy -- so that data[x, y] is the pixel in
column x and row y, as for every image in brainhops:
| Pillow mode | data |
|---|---|
1 (bilevel) |
bool, (x, y) |
L (grey) |
uint8, (x, y) |
LA (grey + alpha) |
uint8, (x, y, 2) |
RGB, YCbCr, LAB, HSV |
uint8, (x, y, 3) |
RGBA, CMYK |
uint8, (x, y, 4) |
I;16 (16-bit grey) |
uint16, (x, y) |
I (32-bit grey) |
int32, (x, y) |
F (floating point) |
float32, (x, y) |
P, PA (palette) |
uint8, (x, y, 3) or (x, y, 4) |
The channels of a multi-sample image lie on a channel axis c, after the
spatial axes. The colour space is the one the file stores (mode tells
which), and is not converted, except for a palette image, whose colours
are looked up (palette=False keeps the indices instead). Pillow reduces
16-bit-per-channel colour PNGs to 8 bits per channel.
Geometry
A raster file stores no origin and no orientation. The index space is
0-based, and an integer index is the centre of a pixel. The first row of
the file is the top of the picture, so y points down; this is a
convention of the format, which the reader does not encode as an
orientation. Nor is the EXIF orientation tag of a photograph applied: the
pixels are returned as stored (apply PIL.ImageOps.exif_transpose yourself
if you need them as displayed).
The image carries one transformation, a scaling from its "pixel"
coordinate system, whose axes count samples, to a "physical" one. By
default, the physical size of a pixel is unknown: the scaling is the
identity and the physical axes have no unit. Most files record a
resolution in dots per inch (info["dpi"]: the PNG pHYs chunk, the JPEG
JFIF density or EXIF resolution, the BMP header), but it describes a screen
or a printer far more often than the scene, so it is used only when the
caller asks:
load(file, dpi=True)takes the pixel size from the file's resolution,25.4 / dpimillimetres, unless it is missing or a placeholder (72 or 96 dpi), in which case the size stays unknown.load(file, dpi=300)ordpi=(300, 150)uses that resolution instead.load(file, pixel_size=0.01, unit="mm")(orpixel_size=(sx, sy)) sets the pixel size directly, and wins over the resolution.unitalone converts a size fromdpito another unit of length.
A PNG file may also record a pixel size in an sCAL chunk, which ITK
reads and writes as its pixel spacing. ITK does not convert its spacing
(conventionally in millimetres) to the unit the chunk names, so that unit
cannot be relied on, and the chunk is not applied: it is kept as
info["sCAL"] = (unit, x, y) (unit 1 is the metre, 2 the radian), and can
be passed on as load(file, pixel_size=info["sCAL"][1:], unit="mm").
When an image is written, its resolution is recorded in the formats that
store one (PNG, JPEG, BMP, TIFF) if its preferred transformation is a
scaling onto axes measured in a unit of length; a translation, which no
raster format stores, is dropped. Otherwise, a resolution the image was
read with is written back. dpi= overrides this (dpi=False records
none).
Frames
A multi-frame file (an animated GIF, PNG or WebP, a multi-picture JPEG) is
read one frame at a time: the first by default, or load(file, frame=i).
The number of frames is image.n_frames. Pillow composites the frames of
an animated GIF, so frame i is what is displayed at step i.
Metadata
What the file records besides the pixels is kept on the image, not in the
data model: image_format (Pillow's name for the format, such as
"PNG"), mode (the mode as stored, such as "P"), info (Pillow's
metadata dictionary), frame and n_frames. The ICC profile and the
EXIF block are written back when the image is saved.
Writing
The data must be (x, y) or (x, y, c) with one to four channels (more
axes are accepted only if they are singletons). The data type is stored as
it is, never converted:
data |
Pillow mode | Formats that store it |
|---|---|---|
bool, (x, y) |
1 |
PNG, BMP, GIF, TIFF, ... |
uint8, (x, y) |
L |
all |
uint8, (x, y, 2) |
LA |
PNG, WebP, ... |
uint8, (x, y, 3) |
RGB |
all |
uint8, (x, y, 4) |
RGBA |
PNG, WebP, TIFF, ... |
uint16, (x, y) |
I;16 |
PNG, TIFF |
int32 / float32, (x, y) |
I / F |
TIFF |
Anything else -- float64, int16, several channels of 16 bits, more
than four channels, a 3D volume -- raises
WriterError, as does a mode the
chosen format cannot store (16 bits in JPEG, say). Convert the data first.
The format is chosen from the file extension, or with format= (default
PNG when writing to an unnamed stream). Other keywords are passed on to
Pillow's Image.save, such as quality for JPEG. JPEG and lossy WebP do
not store the data exactly.
Limits
- Decompression bombs. Pillow refuses to decode an image of more than
twice
PIL.Image.MAX_IMAGE_PIXELSpixels (about 179 million pixels by default), raisingPIL.Image.DecompressionBombError, and warns above it. To read a larger image you trust, setPIL.Image.MAX_IMAGE_PIXELSto a larger value, or toNone. - No lazy access. Pillow decodes a whole frame at once: the data is read in full when the image is loaded.
- TIFF. Pillow reads TIFF, but TIFF files (
.tif,.tiff) are left to the dedicated TIFF reader, which reads stacks, pyramids and their metadata; this reader claims TIFF content only as a fallback, and reads it when tifffile (thetiffextra) is not installed.
Classes
PillowImage
magic
PillowImage(
image_format: str | None = None,
mode: str | None = None,
info: dict[str, Any] | None = None,
frame: int | None = None,
n_frames: int | None = None,
)
Bases: BinaryFileParserWriter, WritableFileBasedImage, SingleScaleImage
A two-dimensional raster image (PNG, JPEG, BMP, GIF, WebP, ...) read and written with Pillow.
The data is F-ordered: (x, y) for a single-component image, and
(x, y, c) when each pixel has several samples (grey + alpha, RGB,
RGBA, ...). The only transformation is a scaling from the pixel system
to a "physical" system, which is the identity, in no unit, unless a
pixel size is known (see the module documentation).
What the file records beyond the pixels is kept on the object --
image_format, mode, info, frame and n_frames -- and the ICC
profile and EXIF block in info are written back when the image is
saved.
Attributes
PREFIXES
class-attribute
PREFIXES: tuple[str, ...] = ()
Filename prefixes required by this parser, e.g. ("y_", "iy_").
An empty tuple means "no constraint". A parser that constrains the prefix is more specific than one that does not, and wins ties.
Declaring EXTENSIONS and PREFIXES separately states the
cross-product implicitly, which is how these conventions actually
work: SPM's four names are {y_, iy_} x {.nii, .nii.gz}.
PRIORITY
class-attribute
PRIORITY: int = 10
Kind precedence, used only to break ties that confidence could not.
A NIfTI file is legitimately both an image and a set of affines, so when nothing else separates them the image wins. Scoring sniffers (e.g. NIfTI intent codes) normally decide well before this matters.
grid
property
grid: CartesianField
The Cartesian field that defines the sampling grid of the image.
This is the grid of the image's geometry.
transformation
property
writable
transformation: Transformation
The preferred transformation.
It is always the last transformation in the list.
Assigning a transformation appends it as the new preferred transformation. Assigning an integer or a string selects an existing transformation by position or by output-space name and moves it to the end. Assigning a transformation that is already in the list moves it to the end instead of adding a copy.
A transformation is recognized as already present by identity (transformations compare by identity): a distinct transformation with the same parameters is appended as a new preferred transformation.
geometry
property
geometry: Geometry
A transformation that is the composition of the preferred voxel-to-world transformation and the cartesian field corresponding to the image's shape.
This transformation can be used to reslice any image onto the same grid as this image.
Methods:
sniff
classmethod
sniff(
file: FileOrContentLike,
error: bool | Type[Exception] = False,
**kwargs,
) -> type | None
On a dispatcher, identify which registered format would read
file. On a concrete format, score how confident it is that
file, in any supported form, is its own.
sniff_file
classmethod
On a dispatcher, identify which registered format would read the file (path or file-like object). On a concrete format, score how confident it is that the file is its own.
sniff_filename
classmethod
sniff_filename(
filename: FilenameLike,
error: bool | Type[Exception] = False,
**kwargs,
) -> float
Determine if the given filename is of the type that this parser can handle.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
filename
|
FilenameLike
|
The filename to sniff. |
required |
error
|
bool | type[Exception]
|
If not False, raise an error if the filename cannot be sniffed. |
False
|
**kwargs
|
Parser-specific options. |
{}
|
Returns:
| Type | Description |
|---|---|
float
|
Confidence that the filename is of this type, in |
sniff_content
classmethod
sniff_content(
content: ContentLike,
error: bool | Type[Exception] = False,
**kwargs,
) -> type | None
On a dispatcher, identify which registered format would read the content (text or bytes). On a concrete format, score how confident it is that the content is its own.
sniff_text
classmethod
On a dispatcher, identify which registered format would read the text. On a concrete format, score how confident it is that the text is its own.
sniff_lines
classmethod
sniff_lines(
lines: Iterable[str],
error: bool | Type[Exception] = False,
**kwargs,
) -> type | None
On a dispatcher, identify which registered format would read the lines. On a concrete format, score how confident it is that the lines are its own.
sniff_line
classmethod
On a dispatcher, identify which registered format would read the line. On a concrete format, score how confident it is that the line is its own.
load
classmethod
load(other: FileOrContentLike, **kwargs) -> Self
On a dispatcher, pick the best-matching registered format and
build an instance of it from other. On a concrete format,
build an instance of this class from other, in any supported
form.
from_spec
classmethod
from_spec(spec: SourceSpec, **kwargs) -> Self
Load a structured source specification through this dispatcher.
from_file
classmethod
On a dispatcher, pick the best-matching registered format and build an instance of it from the file (path or file-like object). On a concrete format, build an instance of this class from the file.
from_filename
classmethod
from_filename(filename: FilenameLike, **kwargs) -> Self
Build an object from a filename.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
filename
|
FilenameLike
|
The filename to parse. |
required |
**kwargs
|
Parser-specific options. |
{}
|
Returns:
| Type | Description |
|---|---|
obj
|
The parsed object. |
from_content
classmethod
from_content(content: ContentLike, **kwargs) -> Self
On a dispatcher, pick the best-matching registered format and build an instance of it from the content (text or bytes). On a concrete format, build an instance of this class from the content.
from_text
classmethod
On a dispatcher, pick the best-matching registered format and build an instance of it from the text. On a concrete format, build an instance of this class from the text.
from_lines
classmethod
On a dispatcher, pick the best-matching registered format and build an instance of it from the lines. On a concrete format, build an instance of this class from the lines.
from_line
classmethod
On a dispatcher, pick the best-matching registered format and build an instance of it from the line. On a concrete format, build an instance of this class from the line.
save
save(file: FileLike, **kwargs) -> None
Write the object to a file (path or file-like object).
This is the generic front door to the to_* family. It is named
save rather than to because to already means something else
on the data models these parsers are mixed into: Transformation.to
converts an object to another type. A writer's to was shadowed
by it on every writable transformation.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
file
|
FileLike
|
The file to write to. |
required |
**kwargs
|
Parser-specific options. |
{}
|
to_file
to_file(file: FileLike, **kwargs) -> None
Write the object to a file (path or file-like object).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
file
|
FileLike
|
The file to write to. |
required |
**kwargs
|
Parser-specific options. |
{}
|
to_text
to_text(**kwargs) -> str
Return a text version of the file.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
**kwargs
|
Parser-specific options. |
{}
|
Returns:
| Type | Description |
|---|---|
str
|
A text version of the file. |
to_lines
Return a text version of the file as an iterable of lines.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
**kwargs
|
Parser-specific options. |
{}
|
Returns:
| Type | Description |
|---|---|
Iterator[str]
|
An iterable of lines representing the object. |
to_line
to_line(**kwargs) -> str
Return a line representing the object.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
**kwargs
|
Parser-specific options. |
{}
|
Returns:
| Type | Description |
|---|---|
str
|
A line representing the object. |
from_dict
classmethod
Create an instance of the class from a dictionary-like object.
Only keys in the dictionary that match keyword-like fields of
this class, or the keywords its constructor takes without
storing them (its InitVars, such as the matrix= of an
Affine), will be used. Other keys are ignored, but see
from_other,
which refuses them.
Additional positional and/or keyword arguments can be provided, and will take precedence over the values in the dictionary.
A key naming a field that this class fixes (a field that cannot
be passed to its constructor) is checked instead of used: a
dictionary that sets it to anything other than None or the
value of this class is refused with a ValueError.
from_instance
classmethod
Create an instance from an instance of a similar class.
The data model copies the fields both classes share, by name.
A field that a file format declares for its own use -- such as
the nibabel image and header of the NIfTI and MGH formats
-- is only copied from an object of that same format: from any
other object, a field of the same name holds something else
(a NIfTI image is no MGH image), so this class's default is
kept instead. Saving a NIfTI image to MGH, or the converse,
therefore converts the data model only, and the format-specific
state is rebuilt by the writer.
from_other
classmethod
Create an instance from a file, or from anything the data model reads.
A path (str or os.PathLike), an open file, bytes or a
structured source (SourceSpec)
is read with load: on a dispatcher such as FileBasedImage,
the best-matching registered format reads it, and on a concrete
format, that format does. Any other value is handed to the data
model's own from_other, which reads a mapping field by field,
copies an instance of a similar class, and passes anything else
to the constructor.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
other
|
Any
|
A file, its content, a mapping, or an instance of a similar class. |
required |
*args
|
Constructor arguments. A file is read with keyword options only. |
()
|
|
**kwargs
|
Format-specific options when reading a file, and field values otherwise. |
{}
|
Returns:
| Type | Description |
|---|---|
obj
|
The object that was built. |
Raises:
| Type | Description |
|---|---|
TypeError
|
If positional arguments come with a file to read. |
reslice
reslice(
geometry: Self
| Geometry
| Transformation
| None = None,
degree: int = 1,
bound: str = "reflect",
coeff: bool = False,
copy: bool = False,
) -> Self
Apply transformations to current data and return new image.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
geometry
|
Image | Geometry | Transformation
|
Geometry of the output image. The geometry is a voxel-to-world transformation that defines the grid onto which the image will be resliced. If it is a If it is |
None
|
degree
|
0..5
|
The spline degree. 0=nearest, 1=linear, 2=quadratic, etc. |
0..5
|
bound
|
(nearest, reflect, mirror, grid - wrap, wrap)
|
The boundary condition. If a string, one of: - 'nearest': nearest edge value (a a a a | a b c d | d d d d) - 'reflect': reflect at edge (d c b a | a b c d | d c b a) - 'mirror': mirror at edge (d c b | a b c d | c b a) - 'grid-wrap': wrap around (a b c d | a b c d | a b c d) - 'wrap': wrap around with shift (d b c d | a b c d | b c a b) If a float, the constant value to use beyond the edge. |
'nearest'
|
coeff
|
bool
|
If True, the input image is assumed to already contain spline coefficients. If False, the input image is prefiltered before interpolation. |
False
|
copy
|
bool
|
Whether the output data must be a fresh array. As with
|
False
|
Returns:
| Type | Description |
|---|---|
Image
|
The resliced image. |
__call__
__call__(transform: Transformation) -> SingleScaleImage
Apply a transformation to the image, but do not compute.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
transform
|
Transformation
|
The transformation to apply. The output space of this transformation should match
(or be compatible with) the output space of the preferred
transformation. That is, the new "voxel-to-world" transformation
is defined as |
required |
Returns:
| Type | Description |
|---|---|
Image
|
The updated (not-yet-resliced) image. |
__getitem__
__getitem__(
index: tuple[int | slice | None, ...],
) -> SingleScaleImage
Index into the image data while preserving the geometry of the image.
sniff_fileobj
classmethod
Score how confident the class is that an open file holds an image Pillow reads.
A format recognized from its magic number scores LIKELY. TIFF
scores WEAK: Pillow reads it, but the dedicated TIFF reader
reads it better, so Pillow is only a fallback.
sniff_bytes
classmethod
sniff_bytes(
content: BinaryContentLike,
error: bool | Type[Exception] = False,
**kwargs,
) -> float
Score how confident the class is that bytes hold an image Pillow reads.
from_fileobj
classmethod
from_fileobj(
file: IO,
frame: int = 0,
palette: bool = True,
dpi: _DpiLike = None,
pixel_size: Any = None,
unit: Any = None,
**kwargs,
) -> Self
Read an image from an open file.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
file
|
IO
|
A binary file object, open for reading. It is not closed. |
required |
frame
|
int
|
The frame of a multi-frame file (an animated GIF, PNG or WebP) to read. Negative values count from the end. Default: the first. |
0
|
palette
|
bool
|
Look the colours of a palette image up, giving an RGB(A) image (the default), or keep the palette indices. |
True
|
dpi
|
bool | float | (float, float)
|
Whether to take the pixel size from the resolution, in dots
per inch. By default ( |
None
|
pixel_size
|
float | Sequence[float] | Mapping[str, float]
|
The pixel size, which overrides the resolution: one size, or
|
None
|
unit
|
str | Unit
|
The unit of |
None
|
Raises:
| Type | Description |
|---|---|
ParserContentError
|
If Pillow cannot read the file. |
IndexError
|
If the file has no frame |
DecompressionBombError
|
If the image is larger than Pillow's safety limit,
|
from_bytes
classmethod
from_bytes(content: BinaryContentLike, **kwargs) -> Self
Read an image from the bytes of a file. See from_fileobj for
the options.
to_bytes
Encode the image in a raster format.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
format
|
str
|
Pillow's name for the format, such as |
None
|
dpi
|
bool | float | (float, float)
|
The resolution to record, in dots per inch, in the formats that
store one (PNG, JPEG, BMP, TIFF). By default ( |
None
|
**options
|
Passed on to Pillow's |
{}
|
Raises:
| Type | Description |
|---|---|
WriterError
|
If the data cannot be stored in the format: more than two
spatial axes, a data type Pillow does not store (e.g. |
to_fileobj
to_fileobj(file: IO, **kwargs) -> None
Write the image to an open binary file.
The format is the format keyword if given, or else the one the
file's name calls for, if it has one (see to_bytes).
to_filename
to_filename(filename: FilenameLike, **kwargs) -> None
Write the image to a file, in the format its extension calls for
(or the format keyword). See to_bytes for the options.
The image is encoded before the file is opened, so an image the format cannot store leaves no file behind.