From 0a79cfaed646a1c45701a118f64882660b1fd68f Mon Sep 17 00:00:00 2001 From: RyanUnderhill Date: Tue, 25 Jun 2019 19:36:37 -0700 Subject: [PATCH 1/7] Initial commit of sample --- samples/c_cxx/NMIST/NMIST.cpp | 229 ++++++++++++++++++++++++++++++++++ samples/c_cxx/NMIST/ReadMe.md | 1 + samples/c_cxx/NMIST/build.bat | 1 + 3 files changed, 231 insertions(+) create mode 100644 samples/c_cxx/NMIST/NMIST.cpp create mode 100644 samples/c_cxx/NMIST/ReadMe.md create mode 100644 samples/c_cxx/NMIST/build.bat diff --git a/samples/c_cxx/NMIST/NMIST.cpp b/samples/c_cxx/NMIST/NMIST.cpp new file mode 100644 index 0000000000000..010cc211d61c0 --- /dev/null +++ b/samples/c_cxx/NMIST/NMIST.cpp @@ -0,0 +1,229 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +#define UNICODE +#include +#include + +#pragma comment(lib, "user32.lib") +#pragma comment(lib, "gdi32.lib") +#pragma comment(lib, "onnxruntime.lib") + +Ort::Env env{ORT_LOGGING_LEVEL_WARNING, "test"}; + +// This is the structure to interface with the NMIST model +// After instantiation, set the input_image_ data to be the 28x28 pixel image of the number to recognize +// Then call Run() to fill in the results_ data with the probabilities of each +// result_ holds the index with highest probability (aka the number the model thinks is in the image) +struct NMIST { + NMIST() { + auto allocator_info = Ort::AllocatorInfo::CreateCpu(OrtDeviceAllocator, OrtMemTypeCPU); + input_tensor_ = Ort::Value::CreateTensor(allocator_info, input_image_.data(), input_image_.size(), input_shape_.data(), input_shape_.size()); + output_tensor_ = Ort::Value::CreateTensor(allocator_info, results_.data(), results_.size(), output_shape_.data(), output_shape_.size()); + } + + int Run() { + const char* input_names[] = {"Input3"}; + const char* output_names[] = {"Plus214_Output_0"}; + + session_.Run(Ort::RunOptions{nullptr}, input_names, &input_tensor_, 1, output_names, &output_tensor_, 1); + + result_ = std::distance(results_.begin(), std::max_element(results_.begin(), results_.end())); + return result_; + } + + static constexpr const int width_ = 28; + static constexpr const int height_ = 28; + + std::array input_image_{}; + std::array results_{}; + int result_{0}; + + private: + Ort::Session session_{env, L"mnist\\model.onnx", Ort::SessionOptions{nullptr}}; + + Ort::Value input_tensor_{nullptr}; + std::array input_shape_{1, 1, width_, height_}; + + Ort::Value output_tensor_{nullptr}; + std::array output_shape_{1, 10}; +}; + +const constexpr unsigned scale_{1}; +const constexpr unsigned scale_ui_{4}; + +NMIST nmist_; +HBITMAP dib_; +HDC hdc_dib_; +bool painting_{}; + +struct DIBInfo : DIBSECTION { + DIBInfo(HBITMAP hBitmap) noexcept { ::GetObject(hBitmap, sizeof(DIBSECTION), this); } + + int Width() const noexcept { return dsBm.bmWidth; } + int Height() const noexcept { return dsBm.bmHeight; } + + void* Bits() const noexcept { return dsBm.bmBits; } + int Pitch() const noexcept { return dsBmih.biSizeImage / abs(dsBmih.biHeight); } +}; + +// We need to convert the true-color data in the DIB into the model's floating point format +// TODO: (also scales down the image and smooths the values, but this is not working properly) +void ConvertDibToNmist() { + DIBInfo info{dib_}; + + const DWORD* input = reinterpret_cast(info.Bits()); + float* output = nmist_.input_image_.data(); + + std::fill(nmist_.input_image_.begin(), nmist_.input_image_.end(), 0.f); + + for (unsigned y = 0; y < NMIST::height_; y++) { + for (unsigned yblock = 0; yblock < scale_; yblock++) { + for (unsigned x = 0; x < NMIST::width_; x++) { + for (unsigned xblock = 0; xblock < scale_; xblock++) + output[x] += input[x * scale_ + xblock] == 0 ? 1.0f : 0.0f; + } + input = reinterpret_cast(reinterpret_cast(input) + info.Pitch()); + } + output += NMIST::width_; + } + + // Normalize the resulting sums + for (auto& v : nmist_.input_image_) + v /= scale_ * scale_; +} + +LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); + +int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { + { + WNDCLASSEX wc{}; + wc.cbSize = sizeof(WNDCLASSEX); + wc.style = CS_HREDRAW | CS_VREDRAW; + wc.lpfnWndProc = WndProc; + wc.hInstance = hInstance; + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); + wc.lpszClassName = L"ONNXTest"; + RegisterClassEx(&wc); + } + { + BITMAPINFO bmi{}; + bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader); + bmi.bmiHeader.biWidth = NMIST::width_ * scale_; + bmi.bmiHeader.biHeight = -NMIST::height_ * scale_; + bmi.bmiHeader.biPlanes = 1; + bmi.bmiHeader.biBitCount = 32; + bmi.bmiHeader.biPlanes = 1; + bmi.bmiHeader.biCompression = BI_RGB; + + void* bits; + dib_ = CreateDIBSection(nullptr, &bmi, DIB_RGB_COLORS, &bits, nullptr, 0); + } + + hdc_dib_ = CreateCompatibleDC(nullptr); + SelectObject(hdc_dib_, dib_); + SelectObject(hdc_dib_, CreatePen(PS_SOLID, 2, RGB(0, 0, 0))); + FillRect(hdc_dib_, &RECT{0, 0, NMIST::width_, NMIST::height_}, (HBRUSH)GetStockObject(WHITE_BRUSH)); + + HWND hWnd = CreateWindow(L"ONNXTest", L"ONNX Runtime Sample - NMIST", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 512, 256, nullptr, nullptr, hInstance, nullptr); + if (!hWnd) + return FALSE; + + ShowWindow(hWnd, nCmdShow); + + MSG msg; + while (GetMessage(&msg, NULL, 0, 0)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + return (int)msg.wParam; +} + +LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { + switch (message) { + case WM_PAINT: { + PAINTSTRUCT ps; + HDC hdc = BeginPaint(hWnd, &ps); + + // Draw the image + StretchBlt(hdc, 0, 0, NMIST::width_ * scale_ui_, NMIST::height_ * scale_ui_, hdc_dib_, 0, 0, NMIST::width_, NMIST::height_, SRCCOPY); + SelectObject(hdc, GetStockObject(BLACK_PEN)); + MoveToEx(hdc, NMIST::width_ * scale_ui_, 0, nullptr); + LineTo(hdc, NMIST::width_ * scale_ui_, NMIST::height_ * scale_ui_); + LineTo(hdc, 0, NMIST::height_ * scale_ui_); + + constexpr int graphs_left = NMIST::width_ * scale_ui_ + 5; + constexpr int graph_width = 64; + SelectObject(hdc, GetStockObject(GRAY_BRUSH)); + + auto least = *std::min_element(nmist_.results_.begin(), nmist_.results_.end()); + auto greatest = nmist_.results_[nmist_.result_]; + auto range = greatest - least; + + auto graphs_zero = graphs_left - least * graph_width / range; + + // Hilight the winner + RECT rc{graphs_left, nmist_.result_ * 16, graphs_left + graph_width + 128, (nmist_.result_ + 1) * 16}; + FillRect(hdc, &rc, (HBRUSH)GetStockObject(LTGRAY_BRUSH)); + + // For every entry, draw the odds and the graph for it + SetBkMode(hdc, TRANSPARENT); + wchar_t value[80]; + for (unsigned i = 0; i < 10; i++) { + int y = 16 * i; + float result = nmist_.results_[i]; + + auto length = wsprintf(value, L"%2d: %d.%02d", i, int(result), abs(int(result * 100) % 100)); + TextOut(hdc, graphs_left + graph_width + 5, y, value, length); + + Rectangle(hdc, graphs_zero, y + 1, graphs_zero + result * graph_width / range, y + 14); + } + + // Draw the zero line + MoveToEx(hdc, graphs_zero, 0, nullptr); + LineTo(hdc, graphs_zero, 16 * 10); + + EndPaint(hWnd, &ps); + return 0; + } + + case WM_LBUTTONDOWN: { + SetCapture(hWnd); + painting_ = true; + int x = LOWORD(lParam); + int y = HIWORD(lParam); + MoveToEx(hdc_dib_, x / scale_ui_, y / scale_ui_, nullptr); + return 0; + } + + case WM_MOUSEMOVE: + if (painting_) { + int x = LOWORD(lParam); + int y = HIWORD(lParam); + LineTo(hdc_dib_, x / scale_ui_, y / scale_ui_); + InvalidateRect(hWnd, nullptr, false); + } + return 0; + + case WM_CAPTURECHANGED: + painting_ = false; + return 0; + + case WM_LBUTTONUP: + ReleaseCapture(); + ConvertDibToNmist(); + nmist_.Run(); + InvalidateRect(hWnd, nullptr, true); + return 0; + + case WM_RBUTTONDOWN: // Erase the image + FillRect(hdc_dib_, &RECT{0, 0, NMIST::width_ * scale_, NMIST::height_ * scale_}, (HBRUSH)GetStockObject(WHITE_BRUSH)); + InvalidateRect(hWnd, nullptr, false); + return 0; + + case WM_DESTROY: + PostQuitMessage(0); + return 0; + } + return DefWindowProc(hWnd, message, wParam, lParam); +} diff --git a/samples/c_cxx/NMIST/ReadMe.md b/samples/c_cxx/NMIST/ReadMe.md new file mode 100644 index 0000000000000..21dd185a38abb --- /dev/null +++ b/samples/c_cxx/NMIST/ReadMe.md @@ -0,0 +1 @@ +Information on how to use the sample \ No newline at end of file diff --git a/samples/c_cxx/NMIST/build.bat b/samples/c_cxx/NMIST/build.bat new file mode 100644 index 0000000000000..a9f18295daece --- /dev/null +++ b/samples/c_cxx/NMIST/build.bat @@ -0,0 +1 @@ +cl NMIST.cpp /Zi /EHsc /I..\..\..\include\onnxruntime\core\session /link /LIBPATH:..\..\..\build\Windows\Debug\Debug \ No newline at end of file From 6fda348b1431d6a2b4a6b8fe49bec507f8d2c4a0 Mon Sep 17 00:00:00 2001 From: Ryan Hill <38674843+RyanUnderhill@users.noreply.github.com> Date: Tue, 25 Jun 2019 20:09:31 -0700 Subject: [PATCH 2/7] Update ReadMe.md --- samples/c_cxx/NMIST/ReadMe.md | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/samples/c_cxx/NMIST/ReadMe.md b/samples/c_cxx/NMIST/ReadMe.md index 21dd185a38abb..2328b18d0472e 100644 --- a/samples/c_cxx/NMIST/ReadMe.md +++ b/samples/c_cxx/NMIST/ReadMe.md @@ -1 +1,34 @@ -Information on how to use the sample \ No newline at end of file +# NMIST Sample - Number recognition + +This sample uses the NMIST model from the Model Zoo: https://github.com/onnx/models/tree/master/mnist + +(inline image of app) + +## Requirements + +Compiled Onnxruntime.dll / lib (link to instructions on how to build dll) +Windows Visual Studio Compiler (cl.exe) + +## Build + +Run 'build.bat' in this directory to call cl.exe to generate NMIST.exe +Then just run NMIST.exe + +## How to use it + +Just draw a number with the left mouse button (or use touch) in the box on the left side. After releasing the mouse button the model will be run and the outputs of the model will be displayed. + +To clear the image, click the right mouse button anywhere. + +## How it works + +A single Ort::Env is created globally to initialize the runtime. + +The NMIST structure abstracts away all of the interaction with the Onnx Runtime, creating the tensors, and running the model. + +ConvertDibToNmist converts the image data in the 32-bit Windows DIB into the NMIST model's input tensor format. + +WWinMain is the Windows entry point, it creates the main window. + +WndProc is the window procedure for the window, handling the mouse input and drawing the graphics + From 80628eccc6bb794958912651bd1a336594743be5 Mon Sep 17 00:00:00 2001 From: RyanUnderhill Date: Tue, 25 Jun 2019 20:12:35 -0700 Subject: [PATCH 3/7] Add more comments. --- samples/c_cxx/NMIST/NMIST.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/samples/c_cxx/NMIST/NMIST.cpp b/samples/c_cxx/NMIST/NMIST.cpp index 010cc211d61c0..c4b0f85936a42 100644 --- a/samples/c_cxx/NMIST/NMIST.cpp +++ b/samples/c_cxx/NMIST/NMIST.cpp @@ -94,6 +94,7 @@ void ConvertDibToNmist() { LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); +// The Windows entry point function int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { { WNDCLASSEX wc{}; From 521dc757984fbf9770d0051997178fbb9565cd52 Mon Sep 17 00:00:00 2001 From: RyanUnderhill Date: Fri, 28 Jun 2019 15:21:33 -0700 Subject: [PATCH 4/7] Prettier view, fix some drawing bugs --- samples/c_cxx/MNIST/MNIST.cpp | 228 ++++++++++++++++++++++++++++++++++ samples/c_cxx/MNIST/ReadMe.md | 34 +++++ samples/c_cxx/MNIST/build.bat | 1 + 3 files changed, 263 insertions(+) create mode 100644 samples/c_cxx/MNIST/MNIST.cpp create mode 100644 samples/c_cxx/MNIST/ReadMe.md create mode 100644 samples/c_cxx/MNIST/build.bat diff --git a/samples/c_cxx/MNIST/MNIST.cpp b/samples/c_cxx/MNIST/MNIST.cpp new file mode 100644 index 0000000000000..2ee727be18bb8 --- /dev/null +++ b/samples/c_cxx/MNIST/MNIST.cpp @@ -0,0 +1,228 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +#define UNICODE +#include +#include +#include + +#pragma comment(lib, "user32.lib") +#pragma comment(lib, "gdi32.lib") +#pragma comment(lib, "onnxruntime.lib") + +Ort::Env env{ORT_LOGGING_LEVEL_WARNING, "test"}; + +// This is the structure to interface with the MNIST model +// After instantiation, set the input_image_ data to be the 28x28 pixel image of the number to recognize +// Then call Run() to fill in the results_ data with the probabilities of each +// result_ holds the index with highest probability (aka the number the model thinks is in the image) +struct MNIST { + MNIST() { + auto allocator_info = Ort::AllocatorInfo::CreateCpu(OrtDeviceAllocator, OrtMemTypeCPU); + input_tensor_ = Ort::Value::CreateTensor(allocator_info, input_image_.data(), input_image_.size(), input_shape_.data(), input_shape_.size()); + output_tensor_ = Ort::Value::CreateTensor(allocator_info, results_.data(), results_.size(), output_shape_.data(), output_shape_.size()); + } + + int Run() { + const char* input_names[] = {"Input3"}; + const char* output_names[] = {"Plus214_Output_0"}; + + session_.Run(Ort::RunOptions{nullptr}, input_names, &input_tensor_, 1, output_names, &output_tensor_, 1); + + result_ = std::distance(results_.begin(), std::max_element(results_.begin(), results_.end())); + return result_; + } + + static constexpr const int width_ = 28; + static constexpr const int height_ = 28; + + std::array input_image_{}; + std::array results_{}; + int result_{0}; + + private: + Ort::Session session_{env, L"model.onnx", Ort::SessionOptions{nullptr}}; + + Ort::Value input_tensor_{nullptr}; + std::array input_shape_{1, 1, width_, height_}; + + Ort::Value output_tensor_{nullptr}; + std::array output_shape_{1, 10}; +}; + +const constexpr int drawing_area_inset_{4}; // Number of pixels to inset the top left of the drawing area +const constexpr int drawing_area_scale_{4}; // Number of times larger to make the drawing area compared to the shape inputs +const constexpr int drawing_area_width_{MNIST::width_ * drawing_area_scale_}; +const constexpr int drawing_area_height_{MNIST::height_ * drawing_area_scale_}; + +MNIST mnist_; +HBITMAP dib_; +HDC hdc_dib_; +bool painting_{}; + +HBRUSH brush_winner_{CreateSolidBrush(RGB(128, 255, 128))}; +HBRUSH brush_bars_{CreateSolidBrush(RGB(128, 128, 255))}; + +struct DIBInfo : DIBSECTION { + DIBInfo(HBITMAP hBitmap) noexcept { ::GetObject(hBitmap, sizeof(DIBSECTION), this); } + + int Width() const noexcept { return dsBm.bmWidth; } + int Height() const noexcept { return dsBm.bmHeight; } + + void* Bits() const noexcept { return dsBm.bmBits; } + int Pitch() const noexcept { return dsBmih.biSizeImage / abs(dsBmih.biHeight); } +}; + +// We need to convert the true-color data in the DIB into the model's floating point format +// TODO: (also scales down the image and smooths the values, but this is not working properly) +void ConvertDibToMnist() { + DIBInfo info{dib_}; + + const DWORD* input = reinterpret_cast(info.Bits()); + float* output = mnist_.input_image_.data(); + + std::fill(mnist_.input_image_.begin(), mnist_.input_image_.end(), 0.f); + + for (unsigned y = 0; y < MNIST::height_; y++) { + for (unsigned x = 0; x < MNIST::width_; x++) { + output[x] += input[x] == 0 ? 1.0f : 0.0f; + } + input = reinterpret_cast(reinterpret_cast(input) + info.Pitch()); + } + output += MNIST::width_; +} + +LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); + +// The Windows entry point function +int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { + { + WNDCLASSEX wc{}; + wc.cbSize = sizeof(WNDCLASSEX); + wc.style = CS_HREDRAW | CS_VREDRAW; + wc.lpfnWndProc = WndProc; + wc.hInstance = hInstance; + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); + wc.lpszClassName = L"ONNXTest"; + RegisterClassEx(&wc); + } + { + BITMAPINFO bmi{}; + bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader); + bmi.bmiHeader.biWidth = MNIST::width_; + bmi.bmiHeader.biHeight = -MNIST::height_; + bmi.bmiHeader.biPlanes = 1; + bmi.bmiHeader.biBitCount = 32; + bmi.bmiHeader.biPlanes = 1; + bmi.bmiHeader.biCompression = BI_RGB; + + void* bits; + dib_ = CreateDIBSection(nullptr, &bmi, DIB_RGB_COLORS, &bits, nullptr, 0); + } + + hdc_dib_ = CreateCompatibleDC(nullptr); + SelectObject(hdc_dib_, dib_); + SelectObject(hdc_dib_, CreatePen(PS_SOLID, 2, RGB(0, 0, 0))); + FillRect(hdc_dib_, &RECT{0, 0, MNIST::width_, MNIST::height_}, (HBRUSH)GetStockObject(WHITE_BRUSH)); + + HWND hWnd = CreateWindow(L"ONNXTest", L"ONNX Runtime Sample - MNIST", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 512, 256, nullptr, nullptr, hInstance, nullptr); + if (!hWnd) + return FALSE; + + ShowWindow(hWnd, nCmdShow); + + MSG msg; + while (GetMessage(&msg, NULL, 0, 0)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + return (int)msg.wParam; +} + +LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { + switch (message) { + case WM_PAINT: { + PAINTSTRUCT ps; + HDC hdc = BeginPaint(hWnd, &ps); + + // Draw the image + StretchBlt(hdc, drawing_area_inset_, drawing_area_inset_, drawing_area_width_, drawing_area_height_, hdc_dib_, 0, 0, MNIST::width_, MNIST::height_, SRCCOPY); + SelectObject(hdc, GetStockObject(BLACK_PEN)); + SelectObject(hdc, GetStockObject(NULL_BRUSH)); + Rectangle(hdc, drawing_area_inset_, drawing_area_inset_, drawing_area_inset_ + drawing_area_width_, drawing_area_inset_ + drawing_area_height_); + + constexpr int graphs_left = drawing_area_inset_ + drawing_area_width_ + 5; + constexpr int graph_width = 64; + SelectObject(hdc, brush_bars_); + + auto least = *std::min_element(mnist_.results_.begin(), mnist_.results_.end()); + auto greatest = mnist_.results_[mnist_.result_]; + auto range = greatest - least; + + auto graphs_zero = graphs_left - least * graph_width / range; + + // Hilight the winner + RECT rc{graphs_left, mnist_.result_ * 16, graphs_left + graph_width + 128, (mnist_.result_ + 1) * 16}; + FillRect(hdc, &rc, brush_winner_); + + // For every entry, draw the odds and the graph for it + SetBkMode(hdc, TRANSPARENT); + wchar_t value[80]; + for (unsigned i = 0; i < 10; i++) { + int y = 16 * i; + float result = mnist_.results_[i]; + + auto length = wsprintf(value, L"%2d: %d.%02d", i, int(result), abs(int(result * 100) % 100)); + TextOut(hdc, graphs_left + graph_width + 5, y, value, length); + + Rectangle(hdc, graphs_zero, y + 1, graphs_zero + result * graph_width / range, y + 14); + } + + // Draw the zero line + MoveToEx(hdc, graphs_zero, 0, nullptr); + LineTo(hdc, graphs_zero, 16 * 10); + + EndPaint(hWnd, &ps); + return 0; + } + + case WM_LBUTTONDOWN: { + SetCapture(hWnd); + painting_ = true; + int x = (GET_X_LPARAM(lParam) - drawing_area_inset_) / drawing_area_scale_; + int y = (GET_Y_LPARAM(lParam) - drawing_area_inset_) / drawing_area_scale_; + MoveToEx(hdc_dib_, x, y, nullptr); + return 0; + } + + case WM_MOUSEMOVE: + if (painting_) { + int x = (GET_X_LPARAM(lParam) - drawing_area_inset_) / drawing_area_scale_; + int y = (GET_Y_LPARAM(lParam) - drawing_area_inset_) / drawing_area_scale_; + LineTo(hdc_dib_, x, y); + InvalidateRect(hWnd, nullptr, false); + } + return 0; + + case WM_CAPTURECHANGED: + painting_ = false; + return 0; + + case WM_LBUTTONUP: + ReleaseCapture(); + ConvertDibToMnist(); + mnist_.Run(); + InvalidateRect(hWnd, nullptr, true); + return 0; + + case WM_RBUTTONDOWN: // Erase the image + FillRect(hdc_dib_, &RECT{0, 0, MNIST::width_, MNIST::height_}, (HBRUSH)GetStockObject(WHITE_BRUSH)); + InvalidateRect(hWnd, nullptr, false); + return 0; + + case WM_DESTROY: + PostQuitMessage(0); + return 0; + } + return DefWindowProc(hWnd, message, wParam, lParam); +} diff --git a/samples/c_cxx/MNIST/ReadMe.md b/samples/c_cxx/MNIST/ReadMe.md new file mode 100644 index 0000000000000..48c9c25eb4219 --- /dev/null +++ b/samples/c_cxx/MNIST/ReadMe.md @@ -0,0 +1,34 @@ +# MNIST Sample - Number recognition + +This sample uses the MNIST model from the Model Zoo: https://github.com/onnx/models/tree/master/mnist + +(inline image of app) + +## Requirements + +Compiled Onnxruntime.dll / lib (link to instructions on how to build dll) +Windows Visual Studio Compiler (cl.exe) + +## Build + +Run 'build.bat' in this directory to call cl.exe to generate MNIST.exe +Then just run MNIST.exe + +## How to use it + +Just draw a number with the left mouse button (or use touch) in the box on the left side. After releasing the mouse button the model will be run and the outputs of the model will be displayed. + +To clear the image, click the right mouse button anywhere. + +## How it works + +A single Ort::Env is created globally to initialize the runtime. + +The MNIST structure abstracts away all of the interaction with the Onnx Runtime, creating the tensors, and running the model. + +ConvertDibToMnist converts the image data in the 32-bit Windows DIB into the MNIST model's input tensor format. + +WWinMain is the Windows entry point, it creates the main window. + +WndProc is the window procedure for the window, handling the mouse input and drawing the graphics + diff --git a/samples/c_cxx/MNIST/build.bat b/samples/c_cxx/MNIST/build.bat new file mode 100644 index 0000000000000..eba19ffbd1926 --- /dev/null +++ b/samples/c_cxx/MNIST/build.bat @@ -0,0 +1 @@ +cl MNIST.cpp /Zi /EHsc /I..\..\..\include\onnxruntime\core\session /link /LIBPATH:..\..\..\build\Windows\Debug\Debug \ No newline at end of file From 1444de9a42543f8c250801984a79349a995c5e5a Mon Sep 17 00:00:00 2001 From: Ryan Hill <38674843+RyanUnderhill@users.noreply.github.com> Date: Mon, 1 Jul 2019 17:37:17 -0700 Subject: [PATCH 5/7] Update ReadMe.md --- samples/c_cxx/MNIST/ReadMe.md | 38 ++++++++++++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/samples/c_cxx/MNIST/ReadMe.md b/samples/c_cxx/MNIST/ReadMe.md index 48c9c25eb4219..148c0a1e4da8a 100644 --- a/samples/c_cxx/MNIST/ReadMe.md +++ b/samples/c_cxx/MNIST/ReadMe.md @@ -16,19 +16,51 @@ Then just run MNIST.exe ## How to use it -Just draw a number with the left mouse button (or use touch) in the box on the left side. After releasing the mouse button the model will be run and the outputs of the model will be displayed. +Just draw a number with the left mouse button (or use touch) in the box on the left side. After releasing the mouse button the model will be run and the outputs of the model will be displayed. Note that when drawing numbers requiring multiple drawing strokes, the model will be run at the end of each stroke with probably wrong predictions (but it's amusing to see and avoids needing to press a 'run model' button). To clear the image, click the right mouse button anywhere. ## How it works A single Ort::Env is created globally to initialize the runtime. +https://github.com/microsoft/onnxruntime/blob/521dc757984fbf9770d0051997178fbb9565cd52/samples/c_cxx/MNIST/MNIST.cpp#L12 The MNIST structure abstracts away all of the interaction with the Onnx Runtime, creating the tensors, and running the model. -ConvertDibToMnist converts the image data in the 32-bit Windows DIB into the MNIST model's input tensor format. - WWinMain is the Windows entry point, it creates the main window. WndProc is the window procedure for the window, handling the mouse input and drawing the graphics +### Preprocessing the data + +MNIST's input is a {1,1,28,28} shaped float tensor, which is basically a 28x28 floating point grayscale image (0.0 = background, 1.0 = foreground). + +The sample stores the image in a 32-bit per pixel windows DIB section, since that's easy to draw into and draw to the screen for windows. The DIB is created here: +https://github.com/microsoft/onnxruntime/blob/521dc757984fbf9770d0051997178fbb9565cd52/samples/c_cxx/MNIST/MNIST.cpp#L109-L121 + +The function to convert the DIB data and writ it into the model's input tensor: +https://github.com/microsoft/onnxruntime/blob/521dc757984fbf9770d0051997178fbb9565cd52/samples/c_cxx/MNIST/MNIST.cpp#L77-L92 + +### Postprocessing the output + +MNIST's output is a simple {1,10} float tensor that holds the likelihood weights per number. The number with the highest value is the model's best guess. + +The MNIST structure uses std::max_element to do this and stores it in result_: +https://github.com/microsoft/onnxruntime/blob/521dc757984fbf9770d0051997178fbb9565cd52/samples/c_cxx/MNIST/MNIST.cpp#L31 + +To make things more interesting, the window painting handler graphs the probabilities and shows the weights here: +https://github.com/microsoft/onnxruntime/blob/521dc757984fbf9770d0051997178fbb9565cd52/samples/c_cxx/MNIST/MNIST.cpp#L164-L183 + +### The Ort::Session + +1. Creation: The Ort::Session is created inside the MNIST structure here: +https://github.com/microsoft/onnxruntime/blob/521dc757984fbf9770d0051997178fbb9565cd52/samples/c_cxx/MNIST/MNIST.cpp#L43 + +2. Setup inputs & outputs: The input & output tensors are created here: +https://github.com/microsoft/onnxruntime/blob/521dc757984fbf9770d0051997178fbb9565cd52/samples/c_cxx/MNIST/MNIST.cpp#L19-L23 +In this usage, we're providing the memory location for the data instead of having Ort allocate the buffers. This is simpler in this case since the buffers are small and can just be fixed members of the MNIST struct. + +3. Run: Running the session is done in the Run() method: +https://github.com/microsoft/onnxruntime/blob/521dc757984fbf9770d0051997178fbb9565cd52/samples/c_cxx/MNIST/MNIST.cpp#L25-L33 + + From ed7ca97ba2da299747fe0e734356748c9701909c Mon Sep 17 00:00:00 2001 From: RyanUnderhill Date: Mon, 1 Jul 2019 17:41:55 -0700 Subject: [PATCH 6/7] Updates + screenshot --- samples/c_cxx/MNIST/MNIST.cpp | 2 +- samples/c_cxx/MNIST/Screenshot.png | Bin 0 -> 6281 bytes samples/c_cxx/NMIST/NMIST.cpp | 230 ----------------------------- samples/c_cxx/NMIST/ReadMe.md | 34 ----- samples/c_cxx/NMIST/build.bat | 1 - 5 files changed, 1 insertion(+), 266 deletions(-) create mode 100644 samples/c_cxx/MNIST/Screenshot.png delete mode 100644 samples/c_cxx/NMIST/NMIST.cpp delete mode 100644 samples/c_cxx/NMIST/ReadMe.md delete mode 100644 samples/c_cxx/NMIST/build.bat diff --git a/samples/c_cxx/MNIST/MNIST.cpp b/samples/c_cxx/MNIST/MNIST.cpp index 2ee727be18bb8..0f3c98c96a825 100644 --- a/samples/c_cxx/MNIST/MNIST.cpp +++ b/samples/c_cxx/MNIST/MNIST.cpp @@ -87,8 +87,8 @@ void ConvertDibToMnist() { output[x] += input[x] == 0 ? 1.0f : 0.0f; } input = reinterpret_cast(reinterpret_cast(input) + info.Pitch()); + output += MNIST::width_; } - output += MNIST::width_; } LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); diff --git a/samples/c_cxx/MNIST/Screenshot.png b/samples/c_cxx/MNIST/Screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..4c4ea23007e54b4c411a0efecfd2fc0d5315a0e4 GIT binary patch literal 6281 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∨ssT&WMwE2sl#wgiM$T$=k2{RcYc7C)T^PN{xSP)ie;1ZDCnZMgTw~ajtEF zd{e1+!Gv|p@kO*QyMpob5vHg9RPy3>po)m2Tg|AX0g?u01v31mq8O9?Hjdmv@6XU4;%d=Qx;eH_qZM^RjX0TSFfv0)6;dg8Tvu^Dd#Yk|0eEz zV(E5r=6cJ9MgpR@xeRV@lVr4F9r{zhZVN3vUV>lOm*ShADvd0euHr@t&Wk+WOU)sB z>RGSsgboo3YN{B%K$-cw&9%y~aMY|4t)SdBOVoGoKOM4F%WIKpTRGBj7#?}-)XwmmKtbI( zx^Y_LM;)MEbicvuSkgXqY6n5=nJf??f(cVO>Yz`8*lZSOwM~6V`*WzVEM$+MkT!j# zCSAla-$jm95$P8WXdrzDM&B$KDa0C5({mRc>t;r}N|wG!JXNmfprE3qy~Ur!bzQ&g zk=tgoT9CoLU#h-B9N5M@iTBfIeWKgOQPXT}grx5Mwc<&MH{EQLS1M(ob2-qV*Xmd z0Jtsp@Tft$=~kP1x=d%5i4#Fk==;nhi?I$ z0Av{VUlR!BlN^0ESQz4jJp@S{b)oR~A;ZXh_7|eW-4j(`g=C#h1Tx@j>>~kl^(v*6 zLU~JjXuLNg3x{8}1e0FvlmE0Hm9qG!w|Iv`QW3VY9-!=uvV|yht_B#aw+Wdc(KX!ZBj2p3XJ(f}9B}b~PQ_|l_2%x26 z(wsqC(rurHp-T1Gjqmd|CqoO+eX@e6gsO~EWo{Ep#VIr zvH(N&NHc1YXDU&*$w;m?{W0(=R%m$cv)RvHkTw7kx3**WFDuWqZ&sBGDBGmk^%*2)nH$y$-& z`}a^AdInu1;q`6CKd#etdWX;l00ziWg~oOe9Hf8-p&3xI{v8sH6c2X=4ge^VR@~|s z_wl5znUiOai>z>4m)1d&&o+Ab)e(?pf3ytz*(IjwAyK8l>~^5iY*r#*reL=u-vt)P z9_`UDDCo8yTDOdB@Yio|wb!x|0-PPYPa0wwI^2_ysdRVLi#c-qF{bDKcn%N^hXDTt z;^xV~D#iOOJMGJ*5f|-!W$g{yENuz=-yeu7i2#1T4$N%TV@LeSW?zqfre6_Mi+u_5(-s<%l2IMbOo8!5j@C+R=*l%;*e)KO_v6=#dx2sOCd-8p~(qOkI5o%oZ;)=ZW+JujFJlDi3+rpzFF6PGoywgm8 z`3Oi!19zYSxGk^tf#wImS(l-L!26?>g*0k;vkx>I{+fk_fle+nbzfmxIV;o=iUVs0 zm;!X=<5V@%TdanYT+e$PN>WMGH)+gl5pwFj$7bxU2Eq4G6`~<)AD+co0IlnuD8O%= zWlxI{bd77Z!go1`Jq9qICSboSNJ=fw5}<7=SIs+zUj}=<yRAu6(EO-R_`9wvsJ4Z~4BAf%`IsWMnsejt7`sCj%{th>d9J>La@W zrJryAS!tl@^Iz`K!Av8JU8XfCJR0`M94GuH^nc!BMG0b{5`AC__is~1af|DKO*3z? z|J3sT!z%zV1%T?%DT%68Ak)+sIPgF)bmw2+4E~wGveZAlEd1B5DFW)_epZ{{fw}zr zum$k63|`o>=6`#f_%9Ow@PyF{$!Wyd!&?n3Bh=1Ujui(&`!KsT{f#^9j@cv`%HZrn b2bP57PLx4pJv{{fVgMN%n4c@qce?!_iKpIc literal 0 HcmV?d00001 diff --git a/samples/c_cxx/NMIST/NMIST.cpp b/samples/c_cxx/NMIST/NMIST.cpp deleted file mode 100644 index c4b0f85936a42..0000000000000 --- a/samples/c_cxx/NMIST/NMIST.cpp +++ /dev/null @@ -1,230 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -#define UNICODE -#include -#include - -#pragma comment(lib, "user32.lib") -#pragma comment(lib, "gdi32.lib") -#pragma comment(lib, "onnxruntime.lib") - -Ort::Env env{ORT_LOGGING_LEVEL_WARNING, "test"}; - -// This is the structure to interface with the NMIST model -// After instantiation, set the input_image_ data to be the 28x28 pixel image of the number to recognize -// Then call Run() to fill in the results_ data with the probabilities of each -// result_ holds the index with highest probability (aka the number the model thinks is in the image) -struct NMIST { - NMIST() { - auto allocator_info = Ort::AllocatorInfo::CreateCpu(OrtDeviceAllocator, OrtMemTypeCPU); - input_tensor_ = Ort::Value::CreateTensor(allocator_info, input_image_.data(), input_image_.size(), input_shape_.data(), input_shape_.size()); - output_tensor_ = Ort::Value::CreateTensor(allocator_info, results_.data(), results_.size(), output_shape_.data(), output_shape_.size()); - } - - int Run() { - const char* input_names[] = {"Input3"}; - const char* output_names[] = {"Plus214_Output_0"}; - - session_.Run(Ort::RunOptions{nullptr}, input_names, &input_tensor_, 1, output_names, &output_tensor_, 1); - - result_ = std::distance(results_.begin(), std::max_element(results_.begin(), results_.end())); - return result_; - } - - static constexpr const int width_ = 28; - static constexpr const int height_ = 28; - - std::array input_image_{}; - std::array results_{}; - int result_{0}; - - private: - Ort::Session session_{env, L"mnist\\model.onnx", Ort::SessionOptions{nullptr}}; - - Ort::Value input_tensor_{nullptr}; - std::array input_shape_{1, 1, width_, height_}; - - Ort::Value output_tensor_{nullptr}; - std::array output_shape_{1, 10}; -}; - -const constexpr unsigned scale_{1}; -const constexpr unsigned scale_ui_{4}; - -NMIST nmist_; -HBITMAP dib_; -HDC hdc_dib_; -bool painting_{}; - -struct DIBInfo : DIBSECTION { - DIBInfo(HBITMAP hBitmap) noexcept { ::GetObject(hBitmap, sizeof(DIBSECTION), this); } - - int Width() const noexcept { return dsBm.bmWidth; } - int Height() const noexcept { return dsBm.bmHeight; } - - void* Bits() const noexcept { return dsBm.bmBits; } - int Pitch() const noexcept { return dsBmih.biSizeImage / abs(dsBmih.biHeight); } -}; - -// We need to convert the true-color data in the DIB into the model's floating point format -// TODO: (also scales down the image and smooths the values, but this is not working properly) -void ConvertDibToNmist() { - DIBInfo info{dib_}; - - const DWORD* input = reinterpret_cast(info.Bits()); - float* output = nmist_.input_image_.data(); - - std::fill(nmist_.input_image_.begin(), nmist_.input_image_.end(), 0.f); - - for (unsigned y = 0; y < NMIST::height_; y++) { - for (unsigned yblock = 0; yblock < scale_; yblock++) { - for (unsigned x = 0; x < NMIST::width_; x++) { - for (unsigned xblock = 0; xblock < scale_; xblock++) - output[x] += input[x * scale_ + xblock] == 0 ? 1.0f : 0.0f; - } - input = reinterpret_cast(reinterpret_cast(input) + info.Pitch()); - } - output += NMIST::width_; - } - - // Normalize the resulting sums - for (auto& v : nmist_.input_image_) - v /= scale_ * scale_; -} - -LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); - -// The Windows entry point function -int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { - { - WNDCLASSEX wc{}; - wc.cbSize = sizeof(WNDCLASSEX); - wc.style = CS_HREDRAW | CS_VREDRAW; - wc.lpfnWndProc = WndProc; - wc.hInstance = hInstance; - wc.hCursor = LoadCursor(NULL, IDC_ARROW); - wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); - wc.lpszClassName = L"ONNXTest"; - RegisterClassEx(&wc); - } - { - BITMAPINFO bmi{}; - bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader); - bmi.bmiHeader.biWidth = NMIST::width_ * scale_; - bmi.bmiHeader.biHeight = -NMIST::height_ * scale_; - bmi.bmiHeader.biPlanes = 1; - bmi.bmiHeader.biBitCount = 32; - bmi.bmiHeader.biPlanes = 1; - bmi.bmiHeader.biCompression = BI_RGB; - - void* bits; - dib_ = CreateDIBSection(nullptr, &bmi, DIB_RGB_COLORS, &bits, nullptr, 0); - } - - hdc_dib_ = CreateCompatibleDC(nullptr); - SelectObject(hdc_dib_, dib_); - SelectObject(hdc_dib_, CreatePen(PS_SOLID, 2, RGB(0, 0, 0))); - FillRect(hdc_dib_, &RECT{0, 0, NMIST::width_, NMIST::height_}, (HBRUSH)GetStockObject(WHITE_BRUSH)); - - HWND hWnd = CreateWindow(L"ONNXTest", L"ONNX Runtime Sample - NMIST", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 512, 256, nullptr, nullptr, hInstance, nullptr); - if (!hWnd) - return FALSE; - - ShowWindow(hWnd, nCmdShow); - - MSG msg; - while (GetMessage(&msg, NULL, 0, 0)) { - TranslateMessage(&msg); - DispatchMessage(&msg); - } - return (int)msg.wParam; -} - -LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { - switch (message) { - case WM_PAINT: { - PAINTSTRUCT ps; - HDC hdc = BeginPaint(hWnd, &ps); - - // Draw the image - StretchBlt(hdc, 0, 0, NMIST::width_ * scale_ui_, NMIST::height_ * scale_ui_, hdc_dib_, 0, 0, NMIST::width_, NMIST::height_, SRCCOPY); - SelectObject(hdc, GetStockObject(BLACK_PEN)); - MoveToEx(hdc, NMIST::width_ * scale_ui_, 0, nullptr); - LineTo(hdc, NMIST::width_ * scale_ui_, NMIST::height_ * scale_ui_); - LineTo(hdc, 0, NMIST::height_ * scale_ui_); - - constexpr int graphs_left = NMIST::width_ * scale_ui_ + 5; - constexpr int graph_width = 64; - SelectObject(hdc, GetStockObject(GRAY_BRUSH)); - - auto least = *std::min_element(nmist_.results_.begin(), nmist_.results_.end()); - auto greatest = nmist_.results_[nmist_.result_]; - auto range = greatest - least; - - auto graphs_zero = graphs_left - least * graph_width / range; - - // Hilight the winner - RECT rc{graphs_left, nmist_.result_ * 16, graphs_left + graph_width + 128, (nmist_.result_ + 1) * 16}; - FillRect(hdc, &rc, (HBRUSH)GetStockObject(LTGRAY_BRUSH)); - - // For every entry, draw the odds and the graph for it - SetBkMode(hdc, TRANSPARENT); - wchar_t value[80]; - for (unsigned i = 0; i < 10; i++) { - int y = 16 * i; - float result = nmist_.results_[i]; - - auto length = wsprintf(value, L"%2d: %d.%02d", i, int(result), abs(int(result * 100) % 100)); - TextOut(hdc, graphs_left + graph_width + 5, y, value, length); - - Rectangle(hdc, graphs_zero, y + 1, graphs_zero + result * graph_width / range, y + 14); - } - - // Draw the zero line - MoveToEx(hdc, graphs_zero, 0, nullptr); - LineTo(hdc, graphs_zero, 16 * 10); - - EndPaint(hWnd, &ps); - return 0; - } - - case WM_LBUTTONDOWN: { - SetCapture(hWnd); - painting_ = true; - int x = LOWORD(lParam); - int y = HIWORD(lParam); - MoveToEx(hdc_dib_, x / scale_ui_, y / scale_ui_, nullptr); - return 0; - } - - case WM_MOUSEMOVE: - if (painting_) { - int x = LOWORD(lParam); - int y = HIWORD(lParam); - LineTo(hdc_dib_, x / scale_ui_, y / scale_ui_); - InvalidateRect(hWnd, nullptr, false); - } - return 0; - - case WM_CAPTURECHANGED: - painting_ = false; - return 0; - - case WM_LBUTTONUP: - ReleaseCapture(); - ConvertDibToNmist(); - nmist_.Run(); - InvalidateRect(hWnd, nullptr, true); - return 0; - - case WM_RBUTTONDOWN: // Erase the image - FillRect(hdc_dib_, &RECT{0, 0, NMIST::width_ * scale_, NMIST::height_ * scale_}, (HBRUSH)GetStockObject(WHITE_BRUSH)); - InvalidateRect(hWnd, nullptr, false); - return 0; - - case WM_DESTROY: - PostQuitMessage(0); - return 0; - } - return DefWindowProc(hWnd, message, wParam, lParam); -} diff --git a/samples/c_cxx/NMIST/ReadMe.md b/samples/c_cxx/NMIST/ReadMe.md deleted file mode 100644 index 2328b18d0472e..0000000000000 --- a/samples/c_cxx/NMIST/ReadMe.md +++ /dev/null @@ -1,34 +0,0 @@ -# NMIST Sample - Number recognition - -This sample uses the NMIST model from the Model Zoo: https://github.com/onnx/models/tree/master/mnist - -(inline image of app) - -## Requirements - -Compiled Onnxruntime.dll / lib (link to instructions on how to build dll) -Windows Visual Studio Compiler (cl.exe) - -## Build - -Run 'build.bat' in this directory to call cl.exe to generate NMIST.exe -Then just run NMIST.exe - -## How to use it - -Just draw a number with the left mouse button (or use touch) in the box on the left side. After releasing the mouse button the model will be run and the outputs of the model will be displayed. - -To clear the image, click the right mouse button anywhere. - -## How it works - -A single Ort::Env is created globally to initialize the runtime. - -The NMIST structure abstracts away all of the interaction with the Onnx Runtime, creating the tensors, and running the model. - -ConvertDibToNmist converts the image data in the 32-bit Windows DIB into the NMIST model's input tensor format. - -WWinMain is the Windows entry point, it creates the main window. - -WndProc is the window procedure for the window, handling the mouse input and drawing the graphics - diff --git a/samples/c_cxx/NMIST/build.bat b/samples/c_cxx/NMIST/build.bat deleted file mode 100644 index a9f18295daece..0000000000000 --- a/samples/c_cxx/NMIST/build.bat +++ /dev/null @@ -1 +0,0 @@ -cl NMIST.cpp /Zi /EHsc /I..\..\..\include\onnxruntime\core\session /link /LIBPATH:..\..\..\build\Windows\Debug\Debug \ No newline at end of file From 5501d862ef3bc4bfae90da60c30e224eac8d22be Mon Sep 17 00:00:00 2001 From: Ryan Hill <38674843+RyanUnderhill@users.noreply.github.com> Date: Mon, 1 Jul 2019 17:43:55 -0700 Subject: [PATCH 7/7] Update ReadMe.md --- samples/c_cxx/MNIST/ReadMe.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/c_cxx/MNIST/ReadMe.md b/samples/c_cxx/MNIST/ReadMe.md index 148c0a1e4da8a..043a63dbc9e17 100644 --- a/samples/c_cxx/MNIST/ReadMe.md +++ b/samples/c_cxx/MNIST/ReadMe.md @@ -2,7 +2,7 @@ This sample uses the MNIST model from the Model Zoo: https://github.com/onnx/models/tree/master/mnist -(inline image of app) +![Screenshot](Screenshot.png) ## Requirements