Skip to content

Commit f6d66f5

Browse files
authored
[Examples] OpenVINO mobilenet taking image as input (second-state#2)
Signed-off-by: Jianbai Ye <jianbaiye@outlook.com>
1 parent e0f6001 commit f6d66f5

File tree

15 files changed

+1253
-17
lines changed

15 files changed

+1253
-17
lines changed

‎.github/workflows/build.yaml‎

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,15 @@ jobs:
5252
source /opt/intel/openvino_2021/bin/setupvars.sh;\
5353
ldconfig;\
5454
cd WasmEdge && cmake --install build && cd ..;\
55-
cd mobilenet;\
55+
cd openvino-mobilenet-raw;\
5656
./download_mobilenet.sh;\
5757
wasmedge --dir .:. wasmedge-wasinn-example-mobilenet.wasm mobilenet.xml mobilenet.bin tensor-1x224x224x3-f32.bgr"
58+
- name: Build and run Rust example - Mobilenet Image
59+
run: |
60+
bash -c "\
61+
source /opt/intel/openvino_2021/bin/setupvars.sh;\
62+
ldconfig;\
63+
cd WasmEdge && cmake --install build && cd ..;\
64+
cd openvino-mobilenet-image;\
65+
./download_mobilenet.sh;\
66+
wasmedge --dir .:. wasmedge-wasinn-example-mobilenet-image.wasm mobilenet.xml mobilenet.bin input.jpg"

‎.gitignore‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
**/build
22
**/target
33

4+
openvino-mobilenet-image/mobilenet.bin
5+
openvino-mobilenet-image/mobilenet.xml
6+
openvino-mobilenet-image/input.jpg
7+
8+
openvino-mobilenet-raw/mobilenet.bin
9+
openvino-mobilenet-raw/mobilenet.xml
10+
openvino-mobilenet-raw/tensor-1x224x224x3-f32.bgr
11+
412
.DS_Store
513
Cargo.lock

‎mobilenet/rust/Cargo.lock‎

Lines changed: 0 additions & 16 deletions
This file was deleted.

‎openvino-mobilenet-image/README.md‎

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# Mobilenet example for WASI-NN
2+
3+
This package is a high-level Rust bindings for [wasi-nn] example of Mobilenet.
4+
5+
[wasi-nn]: https://github.com/WebAssembly/wasi-nn
6+
7+
## Dependencies
8+
9+
This crate depends on the `wasi-nn` in the `Cargo.toml`:
10+
11+
```toml
12+
[dependencies]
13+
wasi-nn = "0.1.0"
14+
```
15+
16+
## Build
17+
18+
Compile the application to WebAssembly:
19+
20+
```bash
21+
cargo build --target=wasm32-wasi --release
22+
```
23+
24+
The output WASM file will be at `target/wasm32-wasi/release/wasmedge-wasinn-example-mobilenet-image.wasm`.
25+
To speed up the image processing, we can enable the AOT mode in WasmEdge with:
26+
27+
```bash
28+
wasmedgec rust/target/wasm32-wasi/release/wasmedge-wasinn-example-mobilenet-image.wasm wasmedge-wasinn-example-mobilenet-image.wasm
29+
```
30+
31+
## Run
32+
33+
First download the fixture files with the script:
34+
35+
```bash
36+
./download_mobilenet.sh
37+
```
38+
39+
it will also download a testing image `input.jpg`
40+
41+
![](https://github.com/bytecodealliance/wasi-nn/raw/main/rust/images/1.jpg)
42+
43+
And execute the WASM with the `wasmedge` with OpenVINO supporting:
44+
45+
```bash
46+
wasmedge --dir .:. wasmedge-wasinn-example-mobilenet-image.wasm mobilenet.xml mobilenet.bin input.jpg
47+
```
48+
49+
You will get the output:
50+
51+
```bash
52+
Read graph XML, size in bytes: 143525
53+
Read graph weights, size in bytes: 13956476
54+
Loaded graph into wasi-nn with ID: 0
55+
Created wasi-nn execution context with ID: 0
56+
Read input tensor, size in bytes: 602112
57+
Executed graph inference
58+
1.) [954](0.9789)banana
59+
2.) [940](0.0074)spaghetti squash
60+
3.) [951](0.0014)lemon
61+
4.) [969](0.0005)eggnog
62+
5.) [942](0.0005)butternut squash
63+
```
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
FIXTURE=https://github.com/intel/openvino-rs/raw/v0.3.3/crates/openvino/tests/fixtures/mobilenet
2+
TODIR=$1
3+
4+
if [ ! -f $TODIR/mobilenet.bin ]; then
5+
wget --no-clobber --directory-prefix=$TODIR $FIXTURE/mobilenet.bin
6+
fi
7+
if [ ! -f $TODIR/mobilenet.xml ]; then
8+
wget --no-clobber --directory-prefix=$TODIR $FIXTURE/mobilenet.xml
9+
fi
10+
if [ ! -f $TODIR/input.jpg ]; then
11+
wget --no-clobber --directory-prefix=$TODIR https://github.com/bytecodealliance/wasi-nn/raw/main/rust/images/1.jpg -O input.jpg
12+
fi
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
[package]
2+
name = "wasmedge-wasinn-example-mobilenet-image"
3+
version = "0.19.0"
4+
authors = ["Second-State"]
5+
readme = "README.md"
6+
edition = "2018"
7+
publish = false
8+
9+
[dependencies]
10+
image = { version = "0.23.14", default-features = false, features = ["gif", "jpeg", "ico", "png", "pnm", "tga", "tiff", "webp", "bmp", "hdr", "dxt", "dds", "farbfeld"] }
11+
wasi-nn = { version = "0.1.0" }
12+
13+
# This crate is built with the wasm32-wasi target, so it's separate
14+
# from the main Wasmtime build, so use this directive to exclude it
15+
# from the parent directory's workspace.
16+
[workspace]
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
use image::io::Reader;
2+
use image::DynamicImage;
3+
use std::convert::TryInto;
4+
use std::env;
5+
use std::fs;
6+
use wasi_nn;
7+
mod imagenet_classes;
8+
9+
pub fn main() {
10+
main_entry();
11+
}
12+
13+
#[no_mangle]
14+
fn main_entry() {
15+
infer_image();
16+
}
17+
18+
fn infer_image() {
19+
let args: Vec<String> = env::args().collect();
20+
let model_xml_name: &str = &args[1];
21+
let model_bin_name: &str = &args[2];
22+
let image_name: &str = &args[3];
23+
24+
let xml = fs::read_to_string(model_xml_name).unwrap();
25+
println!("Read graph XML, size in bytes: {}", xml.len());
26+
27+
let weights = fs::read(model_bin_name).unwrap();
28+
println!("Read graph weights, size in bytes: {}", weights.len());
29+
30+
let graph = unsafe {
31+
wasi_nn::load(
32+
&[&xml.into_bytes(), &weights],
33+
wasi_nn::GRAPH_ENCODING_OPENVINO,
34+
wasi_nn::EXECUTION_TARGET_CPU,
35+
)
36+
.unwrap()
37+
};
38+
println!("Loaded graph into wasi-nn with ID: {}", graph);
39+
40+
let context = unsafe { wasi_nn::init_execution_context(graph).unwrap() };
41+
println!("Created wasi-nn execution context with ID: {}", context);
42+
43+
// Load a tensor that precisely matches the graph input tensor (see
44+
let tensor_data = image_to_tensor(image_name.to_string(), 224, 224);
45+
println!("Read input tensor, size in bytes: {}", tensor_data.len());
46+
let tensor = wasi_nn::Tensor {
47+
dimensions: &[1, 3, 224, 224],
48+
r#type: wasi_nn::TENSOR_TYPE_F32,
49+
data: &tensor_data,
50+
};
51+
unsafe {
52+
wasi_nn::set_input(context, 0, tensor).unwrap();
53+
}
54+
// Execute the inference.
55+
unsafe {
56+
wasi_nn::compute(context).unwrap();
57+
}
58+
println!("Executed graph inference");
59+
// Retrieve the output.
60+
let mut output_buffer = vec![0f32; 1001];
61+
unsafe {
62+
wasi_nn::get_output(
63+
context,
64+
0,
65+
&mut output_buffer[..] as *mut [f32] as *mut u8,
66+
(output_buffer.len() * 4).try_into().unwrap(),
67+
)
68+
.unwrap();
69+
}
70+
71+
let results = sort_results(&output_buffer);
72+
for i in 0..5 {
73+
println!(
74+
" {}.) [{}]({:.4}){}",
75+
i + 1,
76+
results[i].0,
77+
results[i].1,
78+
imagenet_classes::IMAGENET_CLASSES[results[i].0]
79+
);
80+
}
81+
}
82+
83+
// Sort the buffer of probabilities. The graph places the match probability for each class at the
84+
// index for that class (e.g. the probability of class 42 is placed at buffer[42]). Here we convert
85+
// to a wrapping InferenceResult and sort the results.
86+
fn sort_results(buffer: &[f32]) -> Vec<InferenceResult> {
87+
let mut results: Vec<InferenceResult> = buffer
88+
.iter()
89+
.skip(1)
90+
.enumerate()
91+
.map(|(c, p)| InferenceResult(c, *p))
92+
.collect();
93+
results.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap());
94+
results
95+
}
96+
97+
// Take the image located at 'path', open it, resize it to height x width, and then converts
98+
// the pixel precision to FP32. The resulting BGR pixel vector is then returned.
99+
fn image_to_tensor(path: String, height: u32, width: u32) -> Vec<u8> {
100+
let pixels = Reader::open(path).unwrap().decode().unwrap();
101+
let dyn_img: DynamicImage = pixels.resize_exact(width, height, image::imageops::Triangle);
102+
let bgr_img = dyn_img.to_bgr8();
103+
// Get an array of the pixel values
104+
let raw_u8_arr: &[u8] = &bgr_img.as_raw()[..];
105+
// Create an array to hold the f32 value of those pixels
106+
let bytes_required = raw_u8_arr.len() * 4;
107+
let mut u8_f32_arr: Vec<u8> = vec![0; bytes_required];
108+
109+
for i in 0..raw_u8_arr.len() {
110+
// Read the number as a f32 and break it into u8 bytes
111+
let u8_f32: f32 = raw_u8_arr[i] as f32;
112+
let u8_bytes = u8_f32.to_ne_bytes();
113+
114+
for j in 0..4 {
115+
u8_f32_arr[(i * 4) + j] = u8_bytes[j];
116+
}
117+
}
118+
return u8_f32_arr;
119+
}
120+
121+
// A wrapper for class ID and match probabilities.
122+
#[derive(Debug, PartialEq)]
123+
struct InferenceResult(usize, f32);
Binary file not shown.
File renamed without changes.

0 commit comments

Comments
 (0)