1. ISO 19794-4 Images (Print)¶
1.1. Reading¶
The open()
method sets the following info
properties
version
- Fixed version (
020
) nb_representation
- The number of representations, i.e. the number of frames
nb_position
- The number of position, i.e. the different finger ranks present in the frames
certification_flag
- A flag indicating if the certification blocks are included or not
In addition, each frame has the following additional attributes:
header
The representation header (specific to each frame), containing:
capture_datetime
capture_device_technology_id
capture_device_vendor_id
capture_device_type_id
quality_records
: a list of quality records (score
,algo_vendor_id
,algo_id
)certification_records
: a list of certification records (authority_id
,scheme_id
)position
: the finger/plam position as a textnumber
: the image numberscale_units
: the scale unit as a texthorizontal_scan_sampling_rate
vertical_scan_sampling_rate
horizontal_image_sampling_rate
vertical_image_sampling_rate
image_compression_algo
: the compression algo as a textimpression_type
: the impression type as text
When reading an image the fields
position
,scale_units
,image_compression_algo
andimpression_type
are converted to readable text.
1.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.
1.3. Usage¶
First, let’s create a sample image looking like a fingerprint:
>>> from PIL import Image, ImageDraw
>>> sample = Image.new("L",(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(
... capture_datetime = datetime.datetime.now(),
... capture_device_technology_id=b'\x00', # unknown
... capture_device_vendor_id=b'\xab\xcd',
... capture_device_type_id=b'\x12\x34',
... quality_records=[],
... certification_records=[],
... position='LEFT_INDEX_FINGER',
... number=1,
... scale_units='PPI',
... horizontal_scan_sampling_rate=500,
... vertical_scan_sampling_rate=500,
... horizontal_image_sampling_rate=500,
... vertical_image_sampling_rate=500,
... image_compression_algo='RAW',
... impression_type='LIVESCAN_ROLLED'
... )
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(image_compression_algo='RAW')
>>> buffer = io.BytesIO()
>>> sample.save(buffer,"FIR")
Using a fully defined header:
>>> sample.header = header
>>> buffer = io.BytesIO()
>>> sample.save(buffer,"FIR")
>>> print(buffer.getvalue()[0:3])
b'FIR'
>>> print(buffer.getvalue()[4:7])
b'020'
Multi-frames image is generated with the save_all
option:
>>> buffer_multi = io.BytesIO()
>>> sample.save(buffer_multi,"FIR",save_all=True,append_images=[sample])
To read an image, just use the standard open function:
>>> nsample = Image.open(buffer_multi)
>>> nsample.info['nb_representation']
2
>>> nsample.info['nb_position']
1
>>> nsample.seek(1)
To specify the compression algorithm:
>>> nsample = Image.open(buffer_multi)
>>> buffer = io.BytesIO()
>>> nsample.header['image_compression_algo'] ='JPEG'
>>> nsample.seek(1)
>>> nsample.header['image_compression_algo'] ='JPEG'
>>> nsample.save(buffer,"FIR",save_all=True)
Jpeg2000 is also supported (see https://pillow.readthedocs.io/en/stable/handbook/image-file-formats.html#jpeg-2000
for the prerequisites), use JPEG2000_LOSSY
>>> buffer = io.BytesIO()
>>> sample.header['image_compression_algo'] ='JPEG2000_LOSSY'
>>> sample.save(buffer,"FIR")
or JPEG2000_LOSSLESS
>>> buffer = io.BytesIO()
>>> sample.header['image_compression_algo'] ='JPEG2000_LOSSLESS'
>>> sample.save(buffer,"FIR")