Skip to content
This repository was archived by the owner on Oct 14, 2023. It is now read-only.

Commit faee653

Browse files
authored
Merge pull request #40 from snobu/onnx-sample
Added sample for inference using ONNX model
2 parents 5822254 + ceca157 commit faee653

File tree

7 files changed

+189
-1
lines changed

7 files changed

+189
-1
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ This is a collection of Python function samples on Azure Functions 2.X. For a co
2525
| [timer-trigger-cosmos-output-binding](v2functions/timer-trigger-cosmosdb-output-binding) | Azure Functions Timer Trigger Python Sample. The function gets blog RSS feed and store the results into CosmosDB using Cosmos DB output binding | Timer | NONE | CosmosDB |
2626
| [http-trigger-blob-sas-token](v2functions/http-trigger-blob-sas-token) | Azure Function HTTP Trigger Python Sample that returns a SAS token for Azure Storage for the specified container and blob name | HTTP | NONE | HTTP |
2727
| [http-trigger-dump-request](v2functions/http-trigger-dump-request) | Azure Function HTTP Trigger Python Sample that returns request dump info with JSON format | HTTP | NONE | HTTP |
28+
| [http-trigger-onnx-model](v2functions/http-trigger-onnx-model) | This function demonstrates running an inference using an ONNX model. It is triggered by an HTTP request. | HTTP | NONE | HTTP |
2829
| [blob-trigger-watermark-blob-out-binding](v2functions/blob-trigger-watermark-blob-out-binding) | Azure Function Python Sample that watermarks an image. This function triggers on an input blob (image) and adds a watermark by calling into the Pillow library. The resulting composite image is then written back to blob storage using a blob output binding. | Blob Storage | Blob Storage | Blob Storage |
2930
| [sbqueue-trigger-sbqueue-out-binding](v2functions/sbqueue-trigger-sbqueue-out-binding) | Azure Functions Service Bus Queue Trigger Python Sample. The function demonstrates reading from a Service Bus queue and placing a message into an output Service Bus queue. | Service Bus Queue | None | Service Bus Queue |
3031

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import logging
2+
import azure.functions as func
3+
import onnxruntime
4+
from PIL import Image
5+
import numpy as np
6+
import io
7+
8+
def main(req: func.HttpRequest, context: func.Context) -> func.HttpResponse:
9+
logging.info('Python HTTP trigger function processed a request.')
10+
11+
body = req.get_body()
12+
13+
try:
14+
image = Image.open(io.BytesIO(body))
15+
except IOError:
16+
return func.HttpResponse(
17+
"Bad input. Unable to cast request body to an image format.",
18+
status_code=400
19+
)
20+
21+
result = run_inference(image, context)
22+
23+
return func.HttpResponse(result)
24+
25+
def run_inference(image, context):
26+
# See https://github.com/onnx/models/tree/master/vision/style_transfer/fast_neural_style
27+
# for implementation details
28+
model_path = f'{context.function_directory}/rain_princess.onnx'
29+
session = onnxruntime.InferenceSession(model_path)
30+
metadata = session.get_modelmeta()
31+
logging.info(f'Model metadata:\n' +
32+
f' Graph name: {metadata.graph_name}\n' +
33+
f' Model version: {metadata.version}\n' +
34+
f' Producer: {metadata.producer_name}')
35+
36+
# Preprocess image
37+
original_image_size = image.size[0], image.size[1]
38+
logging.info('Preprocessing image...')
39+
# Model expects a 224x224 shape input
40+
image = image.resize((224, 224), Image.LANCZOS)
41+
bands = image.getbands()
42+
if bands == ('R', 'G', 'B'):
43+
logging.info(f'Image is RGB. No conversion necessary.')
44+
else:
45+
logging.info(f'Image is {bands}, converting to RGB...')
46+
image = image.convert('RGB')
47+
48+
x = np.array(image).astype('float32')
49+
x = np.transpose(x, [2, 0, 1])
50+
x = np.expand_dims(x, axis=0)
51+
52+
output_name = session.get_outputs()[0].name
53+
input_name = session.get_inputs()[0].name
54+
logging.info('Running inference on ONNX model...')
55+
result = session.run([output_name], {input_name: x})[0][0]
56+
57+
# Postprocess image
58+
result = np.clip(result, 0, 255)
59+
result = result.transpose(1,2,0).astype("uint8")
60+
img = Image.fromarray(result)
61+
max_width = 800
62+
height = int(max_width * original_image_size[1] / original_image_size[0])
63+
# Upsample and correct aspect ratio for final image
64+
img = img.resize((max_width, height), Image.BICUBIC)
65+
66+
# Store inferred image as in memory byte array
67+
img_byte_arr = io.BytesIO()
68+
# Convert composite to RGB so we can return JPEG
69+
img.convert('RGB').save(img_byte_arr, format='JPEG')
70+
final_image = img_byte_arr.getvalue()
71+
72+
return final_image
1.47 MB
Loading
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"scriptFile": "__init__.py",
3+
"bindings": [
4+
{
5+
"authLevel": "function",
6+
"type": "httpTrigger",
7+
"direction": "in",
8+
"name": "req",
9+
"methods": [
10+
"get",
11+
"post"
12+
]
13+
},
14+
{
15+
"type": "http",
16+
"direction": "out",
17+
"name": "$return"
18+
}
19+
]
20+
}
6.42 MB
Binary file not shown.
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
# http-trigger-onnx-model (Python)
2+
3+
| Sample | Description | Trigger | In Bindings | Out Bindings
4+
| ------------- | ------------- | ------------- | ----------- | ----------- |
5+
| `http-trigger-onnx-model` | This function demonstrates running an inference using an ONNX model. It is triggered by an HTTP request. See _[Try it out](#try-it-out)_ for usage. | HTTP | NONE | HTTP |
6+
7+
The style transfer model used in this function is called _Rain Princess_. It is downloaded from the [ONNX Model Zoo][3].
8+
9+
Artistic style transfer models mix the content of an image with the style of another image. Examples of the styles can be seen [here][4].
10+
11+
Open Neural Network Exchange (ONNX) is an open standard format for representing machine learning models. ONNX is supported by a community of partners who have implemented it in many frameworks and tools.
12+
13+
The ONNX Model Zoo is a collection of pre-trained, state-of-the-art models in the ONNX format contributed by community members like you. See https://github.com/onnx/models for more.
14+
15+
You should be able to use other ONNX models in your function by rewriting the preprocess/postprocess code and wiring the expected inputs and outputs.
16+
17+
## Sample run
18+
![Screenshot](example.png)
19+
This example is probably not going to age well. However the pun stands on its own. Shown here: [httpie][1], [imgcat][2].
20+
21+
## Dependencies
22+
```
23+
Pillow==7.0.0
24+
onnxruntime==1.1.0
25+
numpy==1.18.1
26+
```
27+
28+
## Configuration
29+
As specified in `functions.json`, this function is triggered by an HTTP request. It expects a POST request with raw image bytes (JPEG/PNG/whatever the Pillow library can open). Output is an HTTP response with the resulting style transferred image (JPEG encoded).
30+
31+
```json
32+
{
33+
"scriptFile": "__init__.py",
34+
"bindings": [
35+
{
36+
"authLevel": "function",
37+
"type": "httpTrigger",
38+
"direction": "in",
39+
"name": "req",
40+
"methods": [
41+
"post"
42+
]
43+
},
44+
{
45+
"type": "http",
46+
"direction": "out",
47+
"name": "$return"
48+
}
49+
]
50+
}
51+
```
52+
53+
## How to develop and publish the functions
54+
55+
### Local development
56+
57+
```sh
58+
func host start
59+
```
60+
61+
### Try it out
62+
```bash
63+
# Make a POST request
64+
$ curl -s --data-binary @babyyoda.jpg http://localhost:7071/api/http-trigger-onnx-model -o out.jpg
65+
66+
# Open the resulting image (on a Mac)
67+
# Use feh or xdg-open on Linux
68+
$ open out.jpg
69+
```
70+
71+
### Publish the function to the cloud
72+
73+
Publish the function to the cloud
74+
```sh
75+
FUNCTION_APP_NAME="MyFunctionApp"
76+
func azure functionapp publish $FUNCTION_APP_NAME --build-native-deps --no-bundler
77+
```
78+
79+
Add Functions App Settings
80+
```sh
81+
FUNCTION_STORAGE_CONNECTION="*************"
82+
az webapp config appsettings set \
83+
-n $FUNCTION_APP_NAME \
84+
-g $RESOURCE_GROUP \
85+
--settings \
86+
MyStorageConnectionString=$FUNCTION_STORAGE_CONNECTION
87+
```
88+
89+
90+
[1]: https://httpie.org/
91+
[2]: https://iterm2.com/documentation-images.html
92+
[3]: https://github.com/onnx/models/tree/master/vision/style_transfer/fast_neural_style
93+
[4]: https://github.com/pytorch/examples/tree/master/fast_neural_style#models

v2functions/requirements.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,6 @@ six==1.11.0
88
# Additional packages
99
requests==2.20.1
1010
feedparser==5.2.1
11-
pillow>=6.2.0
11+
Pillow==7.0.0
12+
numpy==1.18.1
13+
onnxruntime==1.1.0

0 commit comments

Comments
 (0)