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:
headerThe representation header (specific to each frame), containing:
capture_datetimecapture_device_technology_idcapture_device_vendor_idcapture_device_type_idquality_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_ratevertical_scan_sampling_ratehorizontal_image_sampling_ratevertical_image_sampling_rateimage_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_algoandimpression_typeare 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")