2. ISO 19794-5 Images (Face)

2.1. Reading

The open() method sets the following info properties

version
Version (010, 020 or 030)
nb_facial_images
The number of representations, i.e. the number of frames

In addition, each frame has the following additional attributes:

header

The representation header (specific to each frame), containing:

  • For version 010:

    • landmark_points
    • gender
    • eye_colour
    • hair_colour
    • property_mask
    • expression
    • pose_yaw
    • pose_pitch
    • pose_roll
    • pose_uncertainty_yaw
    • pose_uncertainty_pitch
    • pose_uncertainty_roll
    • face_image_type
    • image_data_type
    • source_type
    • device_type
    • quality

    When reading an image the fields gender, eye_colour, hair_colour, property_mask, expression, face_image_type, image_data_type and source_type are converted to readable text.

2.2. Writing

The save() method can take the following keyword arguments:

save_all
If true, Pillow will save all frames of the image to a multirepresentation file.
append_images
A list of images to append as additional frames. Each of the images in the list can be a single or multiframe image.
version
The version of the format to use, one of 010, 020 or 030. If not provided and if the image was loaded from an ISO 19794 image, the same version will be used.

2.3. Usage

First, let’s create a sample image:

>>> from PIL import Image, ImageDraw
>>> sample = Image.new("RGB",(200,300),255)
>>> draw = ImageDraw.Draw(sample)
>>> for i in range(20,100,10):
...     for n in range(5):
...         draw.ellipse( (i+n,i+n,200-i-n,300-i-n),outline=0)

To build a single frame image, we first need a representation header. This can be built from a list of key/value.

>>> import datetime
>>> header = dict(
...     landmark_points=[],
...     gender='M',
...     eye_colour='BLUE',
...     hair_colour='BLACK',
...     property_mask=['GLASSES'],
...     expression='NEUTRAL',
...     pose_yaw=0,
...     pose_pitch=0,
...     pose_roll=0,
...     pose_uncertainty_yaw=0,
...     pose_uncertainty_pitch=0,
...     pose_uncertainty_roll=0,
...     face_image_type='FULL_FRONTAL',
...     image_data_type='JPEG',
...     source_type='STATIC_CAMERA',
...     device_type=b'\x00\x00',
...     quality=b'\x00\x00',
...    )

Header must be defined on the image for the save operation to work correctly, but a minimal header is also possible (default values will be provided)

>>> sample.header = dict()
>>> buffer = io.BytesIO()
>>> sample.save(buffer,"FAC", version='010')

Using a fully defined header:

>>> sample.header = header
>>> buffer = io.BytesIO()
>>> sample.save(buffer,"FAC", version='010')
>>> print(buffer.getvalue()[0:3])
b'FAC'
>>> print(buffer.getvalue()[4:7])
b'010'

Multi-frames image is generated with the save_all option:

>>> buffer_multi = io.BytesIO()
>>> sample.save(buffer_multi,"FAC",save_all=True,append_images=[sample], version='010')

To read an image, just use the standard open function:

>>> nsample = Image.open(buffer_multi)
>>> nsample.info['nb_facial_images']
2

Access the second frame:

>>> nsample.seek(1)
>>> nsample.mode
'RGB'
>>> nsample.size
(200, 300)