IIIF (the International Image Interoperability Framework) has defined a set of standards for publishing and using image collections. The State Library of Victoria makes many of its images available from an IIIF-compliant server. This means you can access and manipulate the images in standard ways set out by the IIIF Image API. This notebook demonstrates some of the possibilities.
There are more details about the default image used in this notebook on the SLV site. If you would like to try these examples with another image, just go to the image in the SLV viewer. Underneath the image, you'll see a box like this. Select the IIIF tab, copy the url, then paste it in the cell below.
# Replace with another IIIF link if you want!
manifest_url = 'https://rosetta.slv.vic.gov.au/delivery/iiif/presentation/2.1/IE1164978/manifest'
If you haven't used one of these notebooks before, they're basically web pages in which you can write, edit, and run live code. They're meant to encourage experimentation, so don't feel nervous. Just try running a few cells and see what happens!
Some tips:
Is this thing on? If you can't edit or run any of the code cells, you might be viewing a static (read only) version of this notebook. Click here to load a live version running on Binder.
In order to access an image from the SLV's IIIF server, you need to have set a cookie in your browser by loading the IIIF manifest (that's the link provided by the image viewer). That's fine if you're just playing around in your browser, although it means you can't share a link to a derivative image, or embed it in another page. It also means that if you're building some sort of processing pipeline, you need to create a session to store the cookie value in and load the manifest to set the cookie, before requesting any images. This notebook uses requests.Session()
to handle this.
If they want people to make full use of their IIIF service, the SLV should really change this.
import requests
import re
import ipywidgets as widgets
from pathlib import Path
from IPython.display import display, HTML
def get_image_ids(manifest):
'''
Extract a list of image @ids from an IIIF manifest
'''
image_ids = []
# There can be multiple images in a record
# So we loop through the canvases to get each one.
for canvas in manifest['sequences'][0]['canvases']:
image_ids.append(canvas['images'][0]['resource']['service']['@id'])
return image_ids
def display_image(image_url, feature):
'''
Download and display the requested derivative image
'''
# Get the pid from the link
pid = re.search(r'\/IE(\d+):', image_url).group(1)
# Construct a filename
filename = Path(f'images/slv-{pid}-{feature}.jpg')
# Download and save the image
response = s.get(image_url)
filename.write_bytes(response.content)
# Display the image
display(HTML(f'<a href="{filename}"><img src="{filename}"></a>'))
Create a default session to use for image requests, and load the manifest to set the required cookie.
# Here we're creating a session and loading the manifest to set the cookie we'll need to access images
# All the requests below will use this session
s = requests.Session()
response = s.get(manifest_url)
manifest = response.json()
# Get the id of the first image in the manifest
# We'll use this id in the examples
image_id = get_image_ids(manifest)[0]
print(image_id)
https://rosetta.slv.vic.gov.au:443/cantaloupe/iiif/2/IE1164978:FL15631103.tif
A request for an image from an IIIF-compliant server includes the following parameters: region, size, rotation, quality, and format. The downloading images notebook let you select the size and format, but here we'll explore the options in more depth.
The parameters are supplied in the url you use to request an image. Here's a IIIF url with the default parameters.
https://rosetta.slv.vic.gov.au:443/cantaloupe/iiif/2/IE1164978:FL15631103.tif/full/max/0/default.jpg
Let's break it down:
full
– supply the complete image (not cropped or sliced)max
– supply the image at its largest possible size0
– supply the image with a rotation of 0 degreesdefault
– supply the image in its current quality (no colour changes)jpg
– supply the image in JPEG formatAll of these can be changed.
Note that the transformations are applied in the order they're supplied in the url. So if you specify a region and a size, the region will be selected from the full image and then resized.
# For easy display we're asking for a maximum width of 300 pixels
image_url = f'{image_id}/full/300,/0/default.jpg'
display_image(image_url, 'full')
We can ask for just part of the image to be returned.
If we want a square, centered image, we can just use the square
value.
# For easy display we're asking for a maximum width of 300 pixels
image_url = f'{image_id}/square/300,/0/default.jpg'
display_image(image_url, 'square')
For more control, we can specify a region using coordinates. Let's ask for the top left corner of the image.
Here we specify the x
and y
coordinates of the top left-hand corner of our selection, followed by the required width
and height
. So to get a 1000 x 200 pixel slice, starting at the top left of the image, we want 0,0,1000,200
.
Note that the manifest includes the full image dimensions, so you could retrieve and use them in your selections.
We can also specify a region using percentages rather than pixel numbers. So if we want the centre of the image we could try pct:46,46,8,8
, which will select a region that's 8% of the width and height of the original, starting at a point that's 46% of the distance from the top and left.
image_url = f'{image_id}/pct:46,46,8,8/max/0/default.jpg'
display_image(image_url, 'region-pct')
We've already seen this in action. Just specify a width, a height, or both (in pixels). If you supply both, the aspect ratio will probably get screwed up. You can prevent this by adding a !
before the parameters – this will maintain the aspect ratio, while returning an image within the prescribed dimension.
So to to create a thumbnail with a maximum dimension of 200 px, we can use !200,200
.
We can also supply a percentage. So for an image that's 10% of the original, we'd specify pct:10
.
The third parameter is just the number of degrees the image should be rotated by. Let's rotate by 45 degrees.
# For easy display we're asking for a maximum width of 300 pixels
# We're also asking for the result as a PNG, so that the background is transparent
image_url = f'{image_id}/full/300,/45/default.png'
display_image(image_url, 'rotate')
If we add a !
at the start the image is also reflected. So to just reverse the image we can use !0
.
# For easy display we're asking for a maximum width of 300 pixels
image_url = f'{image_id}/full/300,/!0/default.jpg'
display_image(image_url, 'reflect')
Back to front and upside down!
# For easy display we're asking for a maximum width of 300 pixels
image_url = f'{image_id}/full/300,/!180/default.jpg'
display_image(image_url, 'rotate-reflect')
The quality parameter specifies whether the image is delivered as color, grayscale or black and white. Let's try converting to a bitonal (black and white) image.
# For easy display we're asking for a maximum width of 300 pixels
image_url = f'{image_id}/full/300,/0/bitonal.jpg'
display_image(image_url, 'bitonal')
To change the format of the image, just change the file extension at the end of the url. So to get a JPEG:
https://rosetta.slv.vic.gov.au:443/cantaloupe/iiif/2/IE1164978:FL15631103.tif/full/max/0/default.jpg
or a TIFF:
https://rosetta.slv.vic.gov.au:443/cantaloupe/iiif/2/IE1164978:FL15631103.tif/full/max/0/default.tif
Created by Tim Sherratt for the GLAM Workbench.
If you find this useful, please consider supporting my work on Patreon.