Skip to content

Commit 3d2e468

Browse files
Ryan LaiRyan Lai
andauthored
Check in sample showcasing Rust projection of Winrt (#342)
* Init rust squeezenet project * Add winrt-rs submodule * update .gitmodules * Add squeezenet code * More efficient sorting * refactoring * Make pretty * add comments * Add README.md * nit * Use macro to clean up code * actually use winml nuget 1.4.0 * PR comments * Update to latest winrt-rs master Co-authored-by: Ryan Lai <[email protected]>
1 parent 27fd18e commit 3d2e468

File tree

6 files changed

+196
-0
lines changed

6 files changed

+196
-0
lines changed

.gitmodules

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
[submodule "Tools/WinML-Dashboard/deps/Netron"]
22
path = Tools/WinMLDashboard/deps/Netron
33
url = https://github.com/lutzroeder/Netron.git
4+
[submodule "Samples/RustSqueezenet/winrt-rs"]
5+
path = Samples/RustSqueezenet/winrt-rs
6+
url = https://github.com/microsoft/winrt-rs.git

Samples/RustSqueezenet/Cargo.toml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
[package]
2+
name = "rust_squeezenet"
3+
version = "0.1.0"
4+
authors = ["Microsoft"]
5+
edition = "2018"
6+
7+
[build]
8+
target-dir = "target"
9+
10+
[dependencies]
11+
winrt = { path = "./winrt-rs" }
12+
# NOTE: winrt_macros is needed as a dependency because Rust 1.46 is needed and hasn't been released yet.
13+
winrt_macros = { git = "https://github.com/microsoft/winrt-rs", version = "0.7.2" }
14+
15+
[build-dependencies]
16+
winrt = { path = "./winrt-rs" }
17+
18+
# Nuget packages
19+
[package.metadata.winrt.dependencies]
20+
"Microsoft.Windows.SDK.Contracts" = "10.0.19041.1"
21+
"Microsoft.AI.MachineLearning" = "1.4.0"

Samples/RustSqueezenet/README.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# SqueezeNet Rust sample
2+
This is a desktop application that uses SqueezeNet, a pre-trained machine learning model, to detect the predominant object in an image selected by the user from a file.
3+
4+
Note: SqueezeNet was trained to work with image sizes of 224x224, so you must provide an image of size 224X224.
5+
6+
## Prerequisites
7+
- [Install Rustup](https://www.rust-lang.org/tools/install)
8+
- Install cargo-winrt through command prompt. Until Rust 1.46 is released, cargo-winrt should be installed through the winrt-rs git repository.
9+
- ```cargo install --git https://github.com/microsoft/winrt-rs cargo-winrt```
10+
11+
## Build and Run the sample
12+
1. This project requires Rust 1.46, which is currently in Beta. Rust release dates can be found [here](https://forge.rust-lang.org/). Rust Beta features can be enabled by running the following commands through command prompt in this current project directory after installation of Rustup :
13+
- ``` rustup install beta ```
14+
- ``` rustup override set beta ```
15+
2. Install the WinRT nuget dependencies with this command: ``` cargo winrt install ```
16+
3. Build the project by running ```cargo build``` for debug and ```cargo build --release``` for release.
17+
4. Run the sample by running this command through the command prompt. ``` cargo winrt run ```
18+
- Another option would be to run the executable directly. Should be ```<git enlistment>\Samples\RustSqueezeNet\target\debug\rust_squeezenet.exe```
19+
20+
## Sample output
21+
```
22+
C:\Repos\Windows-Machine-Learning\Samples\RustSqueezeNet> cargo winrt run
23+
Finished installing WinRT dependencies in 0.47s
24+
Finished dev [unoptimized + debuginfo] target(s) in 0.12s
25+
Running `target\debug\rust_squeezenet.exe`
26+
Loading model C:\Repos\Windows-Machine-Learning\RustSqueezeNet\target\debug\Squeezenet.onnx
27+
Creating session
28+
Loading image file C:\Repos\Windows-Machine-Learning\RustSqueezeNet\target\debug\kitten_224.png
29+
Evaluating
30+
Results:
31+
tabby tabby cat 0.9314611
32+
Egyptian cat 0.06530659
33+
tiger cat 0.0029267797
34+
```

Samples/RustSqueezenet/build.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
macro_rules! copy_file {
2+
($file:expr, $destination:expr) => {
3+
match fs::copy($file,
4+
$destination) {
5+
Ok(file) => file,
6+
Err(error) => panic!("Problem copying the file {} to {}: {:?}", $file, $destination, error),
7+
};
8+
}
9+
}
10+
11+
fn copy_resources() {
12+
use std::fs;
13+
let profile = std::env::var("PROFILE").unwrap();
14+
if profile == "debug" {
15+
copy_file!("..\\..\\SharedContent\\media\\fish.png",".\\target\\debug\\fish.png");
16+
copy_file!("..\\..\\SharedContent\\media\\fish.png",".\\target\\debug\\kitten_224.png");
17+
copy_file!("..\\..\\SharedContent\\models\\SqueezeNet.onnx",".\\target\\debug\\SqueezeNet.onnx");
18+
copy_file!("..\\SqueezeNetObjectDetection\\Desktop\\cpp\\Labels.txt",".\\target\\debug\\Labels.txt");
19+
}
20+
else if profile == "release" {
21+
copy_file!("..\\..\\SharedContent\\media\\fish.png",".\\target\\release\\fish.png");
22+
copy_file!("..\\..\\SharedContent\\media\\fish.png",".\\target\\release\\kitten_224.png");
23+
copy_file!("..\\..\\SharedContent\\models\\SqueezeNet.onnx",".\\target\\release\\SqueezeNet.onnx");
24+
copy_file!("..\\SqueezeNetObjectDetection\\Desktop\\cpp\\Labels.txt",".\\target\\release\\Labels.txt");
25+
}
26+
}
27+
28+
fn main() {
29+
winrt::build!(
30+
types
31+
microsoft::ai::machine_learning::*
32+
windows::graphics::imaging::*
33+
);
34+
copy_resources();
35+
}

Samples/RustSqueezenet/src/main.rs

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
include!(concat!(env!("OUT_DIR"), "/winrt.rs"));
2+
3+
macro_rules! handle_io_error_as_winrt_error {
4+
($expression:expr, $error_message:expr) => {
5+
match $expression {
6+
Ok(val) => val,
7+
Err(_err) => return Err(winrt::Error::new(winrt::ErrorCode(Error::last_os_error().raw_os_error().unwrap() as u32), $error_message)),
8+
}
9+
}
10+
}
11+
12+
fn main() -> winrt::Result<()> {
13+
use microsoft::ai::machine_learning::*;
14+
use winrt::ComInterface;
15+
16+
let model_path = get_current_dir()? + "\\Squeezenet.onnx";
17+
println!("Loading model {}", model_path);
18+
let learning_model = LearningModel::load_from_file_path(model_path)?;
19+
20+
let device = LearningModelDevice::create(LearningModelDeviceKind::Cpu)?;
21+
22+
println!("Creating session");
23+
let session = LearningModelSession::create_from_model_on_device(learning_model, device)?;
24+
25+
let image_file_path = get_current_dir()? + "\\kitten_224.png";
26+
println!("Loading image file {}", image_file_path);
27+
let input_image_videoframe = load_image_file(image_file_path)?;
28+
let input_image_feature_value = ImageFeatureValue::create_from_video_frame(input_image_videoframe)?;
29+
let binding = LearningModelBinding::create_from_session(&session)?;
30+
binding.bind("data_0", input_image_feature_value)?;
31+
32+
println!("Evaluating");
33+
let results = LearningModelSession::evaluate(&session,binding, "RunId")?;
34+
35+
let result_lookup = results.outputs()?.lookup("softmaxout_1")?;
36+
let result_itensor_float : ITensorFloat = result_lookup.try_query()?;
37+
let result_vector_view = result_itensor_float.get_as_vector_view()?;
38+
println!("Results:");
39+
print_results(result_vector_view)?;
40+
Ok(())
41+
}
42+
43+
// Print the evaluation results.
44+
fn print_results(results: windows::foundation::collections::IVectorView<f32>) -> winrt::Result<()> {
45+
let labels = load_labels()?;
46+
let mut sorted_results : std::vec::Vec<(f32,u32)> = Vec::new();
47+
for i in 0..results.size()? {
48+
let result = (results.get_at(i)?, i);
49+
sorted_results.push(result);
50+
}
51+
sorted_results.sort_by(|a, b| b.0.partial_cmp(&a.0).unwrap());
52+
53+
// Display the top results
54+
for i in 0..3 {
55+
println!(" {} {}", labels[sorted_results[i].1 as usize], sorted_results[i].0)
56+
}
57+
Ok(())
58+
}
59+
60+
// Return the path of the current directory of the executable
61+
fn get_current_dir() -> winrt::Result<String> {
62+
use std::env;
63+
use std::io::Error;
64+
let current_exe = handle_io_error_as_winrt_error!(env::current_exe(), "Failed to get current directory of executable.");
65+
let current_dir = current_exe.parent().unwrap();
66+
Ok(current_dir.display().to_string())
67+
}
68+
69+
// Load all the SqueezeNet labeels and return in a vector of Strings.
70+
fn load_labels() -> winrt::Result<std::vec::Vec<String>> {
71+
use std::io::Error;
72+
use std::fs::File;
73+
use std::io::{prelude::*, BufReader};
74+
75+
let mut labels : std::vec::Vec<String> = Vec::new();
76+
let labels_file_path = get_current_dir()? + "\\Labels.txt";
77+
let file = handle_io_error_as_winrt_error!(File::open(labels_file_path), "Failed to load labels.");
78+
let reader = BufReader::new(file);
79+
for line in reader.lines() {
80+
let line_str = handle_io_error_as_winrt_error!(line,"Failed to read lines.");
81+
let mut tokenized_line: Vec<&str> = line_str.split(',').collect();
82+
let index = tokenized_line[0].parse::<usize>().unwrap();
83+
labels.resize(index+1, "".to_string());
84+
tokenized_line.remove(0);
85+
labels[index] = tokenized_line.join("");
86+
}
87+
Ok(labels)
88+
}
89+
90+
// load image file given a path and return Videoframe
91+
fn load_image_file(image_file_path: String) -> winrt::Result<windows::media::VideoFrame> {
92+
use windows::graphics::imaging::*;
93+
use windows::media::*;
94+
use windows::storage::*;
95+
96+
let file = StorageFile::get_file_from_path_async(image_file_path)?.get()?;
97+
let stream = file.open_async(FileAccessMode::Read)?.get()?;
98+
let decoder = BitmapDecoder::create_async(&stream)?.get()?;
99+
let software_bitmap = decoder.get_software_bitmap_async()?.get()?;
100+
let image_videoframe = VideoFrame::create_with_software_bitmap(software_bitmap)?;
101+
Ok(image_videoframe)
102+
}

Samples/RustSqueezenet/winrt-rs

Submodule winrt-rs added at 2edcccd

0 commit comments

Comments
 (0)