This notebook shows how to setup Clipper to accept querying with image. We will deploy a function that compute the size of the image using PIL image library and query it with image data.
In [1]:
import base64
import json
import requests
An image can be read a string of raw bytes. Clipper can take any base64 encoded bytes as input and decode it to send to your model.
Below, we define the query function and the model that calculate image size.
In [2]:
raw_bytes = open('imgs/clipper-logo.png', "rb").read()
raw_bytes[:10]
Out[2]:
In [3]:
encoded_bytes = base64.b64encode(raw_bytes)
print(type(encoded_bytes))
encoded_bytes[:10]
Out[3]:
In [4]:
encoded_string = encoded_bytes.decode()
print(type(encoded_string))
encoded_string[:10]
Out[4]:
Finally our query function will look like this:
In [5]:
def query(addr, filename):
url = "http://%s/image-example/predict" % addr
req_json = json.dumps({
"input":
base64.b64encode(open(filename, "rb").read()).decode() # bytes to unicode
})
headers = {'Content-type': 'application/json'}
r = requests.post(url, headers=headers, data=req_json)
print(r.json())
The model will takes in an array of byte arrays. The bytes are already decoded for you.
In [6]:
def image_size(imgs):
"""
Input:
- imgs : (np.ndarray) of shape (n, d). n is the number of data in this batch
d is the length of the bytes as numpy int8 array.
Output:
- sizes : List[Tuple(int, int),...]
"""
import base64
import io
import os
import PIL.Image
import tempfile
num_imgs = len(imgs)
sizes = []
for i in range(num_imgs):
# Create a temp file to write to
tmp = tempfile.NamedTemporaryFile('wb', delete=False, suffix='.png')
tmp.write(io.BytesIO(imgs[i]).getvalue())
tmp.close()
# Use PIL to read in the file and compute size
size = PIL.Image.open(tmp.name, 'r').size
# Remove the temp file
os.unlink(tmp.name)
sizes.append(size)
return sizes
We can now start Clipper with Docker and deploy our function.
Be sure to add pillow
as external package by passing it in to pkgs_to_install
parameter in deployer function!
In [7]:
from clipper_admin import ClipperConnection, DockerContainerManager
from clipper_admin.deployers import python as python_deployer
clipper_conn = ClipperConnection(DockerContainerManager())
clipper_conn.start_clipper()
python_deployer.create_endpoint(
clipper_conn=clipper_conn,
name="image-example",
input_type="bytes",
func=image_size,
pkgs_to_install=['pillow']
)
In [9]:
query(clipper_conn.get_query_addr(), 'imgs/clipper-logo.png')
Sometime you need to pass in metadata about the image like the file format. In the example below, we will serialize our image data as base64 encoded string and make it part of our input query. We will input a json serialized string as input and decoded in the model function.
In [10]:
def query_json(addr, filename, image_format):
url = "http://%s/image-example-string/predict" % addr
req_json = json.dumps({
"input":
json.dumps({
'data': base64.b64encode(open(filename, "rb").read()).decode(),
'format': image_format
})
})
headers = {'Content-type': 'application/json'}
r = requests.post(url, headers=headers, data=req_json)
print(r.json())
In [11]:
def image_size_json(imgs):
"""
Input:
- imgs : an array of strings
Output:
- sizes : List[Tuple(int, int),...]
"""
import base64
import io
import os
import PIL.Image
import tempfile
import json
num_imgs = len(imgs)
sizes = []
for i in range(num_imgs):
# Deserialize the query
data = json.loads(imgs[i])
image_format = data['format']
image_bytes = data['data'].encode()
# Create a temp file to write to
tmp = tempfile.NamedTemporaryFile('wb', delete=False, suffix='.{}'.format(image_format))
tmp.write(io.BytesIO(base64.b64decode(image_bytes)).getvalue())
tmp.close()
# Use PIL to read in the file and compute size
size = PIL.Image.open(tmp.name, 'r').size
# Remove the temp file
os.unlink(tmp.name)
sizes.append(size)
return sizes
In [12]:
python_deployer.create_endpoint(
clipper_conn=clipper_conn,
name="image-example-string",
input_type="strings",
func=image_size_json,
pkgs_to_install=['pillow']
)
In [13]:
query_json(clipper_conn.get_query_addr(), 'imgs/clipper-logo.jpg', 'jpg')
In [14]:
query_json(clipper_conn.get_query_addr(), 'imgs/clipper-logo.png', 'png')
In [15]:
clipper_conn.stop_all()