Move the samples to a new repo (#8374)
Move the samples to a new repo https://github.com/microsoft/onnxruntime-inference-examples
This commit is contained in:
Родитель
4931ef666d
Коммит
4e1c5f6ef4
|
@ -33,14 +33,3 @@ target_include_directories(ort_opschema_lib PRIVATE ${ONNXRUNTIME_ROOT} ${ORTTRA
|
|||
onnxruntime_add_include_to_target(ort_opschema_lib onnxruntime_common onnx onnx_proto protobuf::libprotobuf flatbuffers)
|
||||
add_dependencies(ort_opschema_lib ${OPSCHEMA_LIB_DEPENDENCIES})
|
||||
|
||||
# Test schema library using toy application
|
||||
|
||||
set(OPSCHEMA_LIB_TEST ${REPO_ROOT}/samples/c_cxx/opschema_lib_use)
|
||||
|
||||
file(GLOB_RECURSE opschema_lib_test_src "${OPSCHEMA_LIB_TEST}/main.cc")
|
||||
|
||||
add_executable(opschema_lib_test ${opschema_lib_test_src})
|
||||
|
||||
target_include_directories(opschema_lib_test PRIVATE ${ORTTRAINING_ROOT})
|
||||
|
||||
target_link_libraries(opschema_lib_test ort_opschema_lib ${OPSCHEMA_LIB_DEPENDENCIES})
|
||||
|
|
|
@ -1,82 +0,0 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
cmake_minimum_required(VERSION 3.13)
|
||||
|
||||
# Project
|
||||
project(onnxruntime_samples C CXX)
|
||||
if (WIN32)
|
||||
string(APPEND CMAKE_CXX_FLAGS " /W4")
|
||||
else()
|
||||
string(APPEND CMAKE_CXX_FLAGS " -Wall -Wextra")
|
||||
string(APPEND CMAKE_C_FLAGS " -Wall -Wextra")
|
||||
endif()
|
||||
|
||||
#onnxruntime providers
|
||||
option(onnxruntime_USE_CUDA "Build with CUDA support" OFF)
|
||||
option(onnxruntime_USE_OPENVINO "Build with OpenVINO support" OFF)
|
||||
option(onnxruntime_USE_NNAPI_BUILTIN "Build with builtin NNAPI lib for Android NNAPI support" OFF)
|
||||
option(onnxruntime_USE_DNNL "Build with DNNL support" OFF)
|
||||
option(onnxruntime_USE_NUPHAR "Build with Nuphar" OFF)
|
||||
option(onnxruntime_USE_TENSORRT "Build with TensorRT support" OFF)
|
||||
option(LIBPNG_ROOTDIR "libpng root dir")
|
||||
option(ONNXRUNTIME_ROOTDIR "onnxruntime root dir")
|
||||
|
||||
if(NOT ONNXRUNTIME_ROOTDIR)
|
||||
if(WIN32)
|
||||
set(ONNXRUNTIME_ROOTDIR "C:/Program Files (x86)/onnxruntime")
|
||||
else()
|
||||
include_directories("/usr/local/include/onnxruntime")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
include_directories("${ONNXRUNTIME_ROOTDIR}/include" "${ONNXRUNTIME_ROOTDIR}/include/onnxruntime/core/session")
|
||||
link_directories("${ONNXRUNTIME_ROOTDIR}/lib")
|
||||
|
||||
#if JPEG lib is available, we'll use it for image decoding, otherwise we'll use WIC
|
||||
find_package(JPEG)
|
||||
if(LIBPNG_ROOTDIR)
|
||||
set(PNG_FOUND true)
|
||||
if(WIN32)
|
||||
set(PNG_LIBRARIES debug libpng16_d optimized libpng16)
|
||||
else()
|
||||
set(PNG_LIBRARIES png16)
|
||||
endif()
|
||||
set(PNG_INCLUDE_DIRS "${LIBPNG_ROOTDIR}/include")
|
||||
set(PNG_LIBDIR "${LIBPNG_ROOTDIR}/lib")
|
||||
else()
|
||||
find_package(PNG)
|
||||
endif()
|
||||
|
||||
if(onnxruntime_USE_CUDA)
|
||||
add_definitions(-DUSE_CUDA)
|
||||
endif()
|
||||
if(onnxruntime_USE_OPENVINO)
|
||||
add_definitions(-DUSE_OPENVINO)
|
||||
endif()
|
||||
if(onnxruntime_USE_NNAPI_BUILTIN)
|
||||
add_definitions(-DUSE_NNAPI)
|
||||
endif()
|
||||
if(onnxruntime_USE_DNNL)
|
||||
add_definitions(-DUSE_DNNL)
|
||||
endif()
|
||||
if(onnxruntime_USE_NUPHAR)
|
||||
add_definitions(-DUSE_NUPHAR)
|
||||
endif()
|
||||
if(onnxruntime_USE_TENSORRT)
|
||||
add_definitions(-DUSE_TENSORRT)
|
||||
endif()
|
||||
if(onnxruntime_USE_DML)
|
||||
message("Enabling DML")
|
||||
add_definitions(-DUSE_DML)
|
||||
endif()
|
||||
|
||||
# some examples require a Windows build environment
|
||||
if(WIN32)
|
||||
add_subdirectory(imagenet)
|
||||
add_subdirectory(MNIST)
|
||||
endif()
|
||||
if(PNG_FOUND)
|
||||
add_subdirectory(fns_candy_style_transfer)
|
||||
endif()
|
||||
add_subdirectory(model-explorer)
|
|
@ -1,8 +0,0 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
cmake_minimum_required(VERSION 3.13)
|
||||
|
||||
add_executable(mnist MNIST.cpp)
|
||||
|
||||
target_link_options(mnist PRIVATE "/SUBSYSTEM:WINDOWS")
|
|
@ -1,262 +0,0 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
#define UNICODE
|
||||
#include <windows.h>
|
||||
#include <windowsx.h>
|
||||
#include <onnxruntime_cxx_api.h>
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <algorithm>
|
||||
|
||||
#pragma comment(lib, "user32.lib")
|
||||
#pragma comment(lib, "gdi32.lib")
|
||||
#pragma comment(lib, "onnxruntime.lib")
|
||||
|
||||
template <typename T>
|
||||
static void softmax(T& input) {
|
||||
float rowmax = *std::max_element(input.begin(), input.end());
|
||||
std::vector<float> y(input.size());
|
||||
float sum = 0.0f;
|
||||
for (size_t i = 0; i != input.size(); ++i) {
|
||||
sum += y[i] = std::exp(input[i] - rowmax);
|
||||
}
|
||||
for (size_t i = 0; i != input.size(); ++i) {
|
||||
input[i] = y[i] / sum;
|
||||
}
|
||||
}
|
||||
|
||||
// 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 memory_info = Ort::MemoryInfo::CreateCpu(OrtDeviceAllocator, OrtMemTypeCPU);
|
||||
input_tensor_ = Ort::Value::CreateTensor<float>(memory_info, input_image_.data(), input_image_.size(), input_shape_.data(), input_shape_.size());
|
||||
output_tensor_ = Ort::Value::CreateTensor<float>(memory_info, results_.data(), results_.size(), output_shape_.data(), output_shape_.size());
|
||||
}
|
||||
|
||||
std::ptrdiff_t 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);
|
||||
softmax(results_);
|
||||
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<float, width_ * height_> input_image_{};
|
||||
std::array<float, 10> results_{};
|
||||
int64_t result_{0};
|
||||
|
||||
private:
|
||||
Ort::Env env;
|
||||
Ort::Session session_{env, L"model.onnx", Ort::SessionOptions{nullptr}};
|
||||
|
||||
Ort::Value input_tensor_{nullptr};
|
||||
std::array<int64_t, 4> input_shape_{1, 1, width_, height_};
|
||||
|
||||
Ort::Value output_tensor_{nullptr};
|
||||
std::array<int64_t, 2> 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_};
|
||||
|
||||
std::unique_ptr<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<const DWORD*>(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<const DWORD*>(reinterpret_cast<const BYTE*>(input) + info.Pitch());
|
||||
output += MNIST::width_;
|
||||
}
|
||||
}
|
||||
|
||||
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
|
||||
|
||||
// The Windows entry point function
|
||||
int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE /*hPrevInstance*/, _In_ LPTSTR /*lpCmdLine*/,
|
||||
_In_ int nCmdShow) {
|
||||
try {
|
||||
mnist_ = std::make_unique<MNIST>();
|
||||
} catch (const Ort::Exception& exception) {
|
||||
MessageBoxA(nullptr, exception.what(), "Error:", MB_OK);
|
||||
return 0;
|
||||
}
|
||||
|
||||
{
|
||||
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);
|
||||
}
|
||||
if (dib_ == nullptr) return -1;
|
||||
hdc_dib_ = CreateCompatibleDC(nullptr);
|
||||
SelectObject(hdc_dib_, dib_);
|
||||
SelectObject(hdc_dib_, CreatePen(PS_SOLID, 2, RGB(0, 0, 0)));
|
||||
RECT rect{0, 0, MNIST::width_, MNIST::height_};
|
||||
FillRect(hdc_dib_, &rect, (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);
|
||||
}
|
||||
|
||||
DeleteObject(dib_);
|
||||
DeleteDC(hdc_dib_);
|
||||
|
||||
DeleteObject(brush_winner_);
|
||||
DeleteObject(brush_bars_);
|
||||
|
||||
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;
|
||||
|
||||
int graphs_zero = static_cast<int>(graphs_left - least * graph_width / range);
|
||||
|
||||
// Hilight the winner
|
||||
RECT rc{graphs_left, static_cast<LONG>(mnist_->result_) * 16, graphs_left + graph_width + 128, static_cast<LONG>(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, static_cast<int>(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
|
||||
{
|
||||
RECT rect{0, 0, MNIST::width_, MNIST::height_};
|
||||
FillRect(hdc_dib_, &rect, (HBRUSH)GetStockObject(WHITE_BRUSH));
|
||||
InvalidateRect(hWnd, nullptr, false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
case WM_DESTROY:
|
||||
PostQuitMessage(0);
|
||||
return 0;
|
||||
}
|
||||
return DefWindowProc(hWnd, message, wParam, lParam);
|
||||
}
|
Двоичные данные
samples/c_cxx/MNIST/Screenshot.png
Двоичные данные
samples/c_cxx/MNIST/Screenshot.png
Двоичный файл не отображается.
До Ширина: | Высота: | Размер: 6.1 KiB |
|
@ -1 +0,0 @@
|
|||
cl MNIST.cpp /Zi /EHsc /I..\..\..\include\onnxruntime\core\session /link /LIBPATH:..\..\..\build\Windows\Debug\Debug
|
|
@ -1,384 +0,0 @@
|
|||
/*
|
||||
Copyright (C) 2021, Intel Corporation
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
Portions of this software are copyright of their respective authors and released under the MIT license:
|
||||
- ONNX-Runtime-Inference, Copyright 2020 Lei Mao. For licensing see https://github.com/leimao/ONNX-Runtime-Inference/blob/main/LICENSE.md
|
||||
*/
|
||||
|
||||
#include <onnxruntime_cxx_api.h>
|
||||
#include <opencv2/dnn/dnn.hpp>
|
||||
#include <opencv2/imgcodecs.hpp>
|
||||
#include <opencv2/imgproc.hpp>
|
||||
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
#include <exception>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
#include <numeric>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <stdexcept> // To use runtime_error
|
||||
|
||||
template <typename T>
|
||||
T vectorProduct(const std::vector<T>& v)
|
||||
{
|
||||
return accumulate(v.begin(), v.end(), 1, std::multiplies<T>());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Operator overloading for printing vectors
|
||||
* @tparam T
|
||||
* @param os
|
||||
* @param v
|
||||
* @return std::ostream&
|
||||
*/
|
||||
|
||||
template <typename T>
|
||||
std::ostream& operator<<(std::ostream& os, const std::vector<T>& v)
|
||||
{
|
||||
os << "[";
|
||||
for (int i = 0; i < v.size(); ++i)
|
||||
{
|
||||
os << v[i];
|
||||
if (i != v.size() - 1)
|
||||
{
|
||||
os << ", ";
|
||||
}
|
||||
}
|
||||
os << "]";
|
||||
return os;
|
||||
}
|
||||
|
||||
// Function to validate the input image file extension.
|
||||
bool imageFileExtension(std::string str)
|
||||
{
|
||||
// is empty throw error
|
||||
if (str.empty())
|
||||
throw std::runtime_error("[ ERROR ] The image File path is empty");
|
||||
|
||||
size_t pos = str.rfind('.');
|
||||
if (pos == std::string::npos)
|
||||
return false;
|
||||
|
||||
std::string ext = str.substr(pos+1);
|
||||
|
||||
if (ext == "jpg" || ext == "jpeg" || ext == "gif" || ext == "png" || ext == "jfif" ||
|
||||
ext == "JPG" || ext == "JPEG" || ext == "GIF" || ext == "PNG" || ext == "JFIF") {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Function to read the labels from the labelFilepath.
|
||||
std::vector<std::string> readLabels(std::string& labelFilepath)
|
||||
{
|
||||
std::vector<std::string> labels;
|
||||
std::string line;
|
||||
std::ifstream fp(labelFilepath);
|
||||
while (std::getline(fp, line))
|
||||
{
|
||||
labels.push_back(line);
|
||||
}
|
||||
return labels;
|
||||
}
|
||||
|
||||
// Function to validate the input model file extension.
|
||||
bool checkModelExtension(const std::string& filename)
|
||||
{
|
||||
if(filename.empty())
|
||||
{
|
||||
throw std::runtime_error("[ ERROR ] The Model file path is empty");
|
||||
}
|
||||
size_t pos = filename.rfind('.');
|
||||
if (pos == std::string::npos)
|
||||
return false;
|
||||
std::string ext = filename.substr(pos+1);
|
||||
if (ext == "onnx")
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Function to validate the Label file extension.
|
||||
bool checkLabelFileExtension(const std::string& filename)
|
||||
{
|
||||
size_t pos = filename.rfind('.');
|
||||
if (filename.empty())
|
||||
{
|
||||
throw std::runtime_error("[ ERROR ] The Label file path is empty");
|
||||
}
|
||||
if (pos == std::string::npos)
|
||||
return false;
|
||||
std::string ext = filename.substr(pos+1);
|
||||
if (ext == "txt") {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//Handling divide by zero
|
||||
float division(float num, float den){
|
||||
if (den == 0) {
|
||||
throw std::runtime_error("[ ERROR ] Math error: Attempted to divide by Zero\n");
|
||||
}
|
||||
return (num / den);
|
||||
}
|
||||
|
||||
void printHelp() {
|
||||
std::cout << "To run the model, use the following command:\n";
|
||||
std::cout << "Example: ./run_squeezenet --use_openvino <path_to_the_model> <path_to_the_image> <path_to_the_classes_file>" << std::endl;
|
||||
std::cout << "\n To Run using OpenVINO EP.\nExample: ./run_squeezenet --use_openvino squeezenet1.1-7.onnx demo.jpeg synset.txt \n" << std::endl;
|
||||
std::cout << "\n To Run on Default CPU.\n Example: ./run_squeezenet --use_cpu squeezenet1.1-7.onnx demo.jpeg synset.txt \n" << std::endl;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
bool useOPENVINO{true};
|
||||
const char* useOPENVINOFlag = "--use_openvino";
|
||||
const char* useCPUFlag = "--use_cpu";
|
||||
|
||||
if(argc == 2) {
|
||||
std::string option = argv[1];
|
||||
if (option == "--help" || option == "-help" || option == "--h" || option == "-h") {
|
||||
printHelp();
|
||||
}
|
||||
return 0;
|
||||
} else if(argc != 5) {
|
||||
std::cout << "[ ERROR ] you have used the wrong command to run your program." << std::endl;
|
||||
printHelp();
|
||||
return 0;
|
||||
} else if (strcmp(argv[1], useOPENVINOFlag) == 0) {
|
||||
useOPENVINO = true;
|
||||
} else if (strcmp(argv[1], useCPUFlag) == 0) {
|
||||
useOPENVINO = false;
|
||||
}
|
||||
|
||||
if (useOPENVINO)
|
||||
{
|
||||
std::cout << "Inference Execution Provider: OPENVINO" << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Inference Execution Provider: CPU" << std::endl;
|
||||
}
|
||||
|
||||
std::string instanceName{"image-classification-inference"};
|
||||
|
||||
std::string modelFilepath = argv[2]; // .onnx file
|
||||
|
||||
//validate ModelFilePath
|
||||
checkModelExtension(modelFilepath);
|
||||
if(!checkModelExtension(modelFilepath)) {
|
||||
throw std::runtime_error("[ ERROR ] The ModelFilepath is not correct. Make sure you are setting the path to an onnx model file (.onnx)");
|
||||
}
|
||||
std::string imageFilepath = argv[3];
|
||||
|
||||
// Validate ImageFilePath
|
||||
imageFileExtension(imageFilepath);
|
||||
if(!imageFileExtension(imageFilepath)) {
|
||||
throw std::runtime_error("[ ERROR ] The imageFilepath doesn't have correct image extension. Choose from jpeg, jpg, gif, png, PNG, jfif");
|
||||
}
|
||||
std::ifstream f(imageFilepath.c_str());
|
||||
if(!f.good()) {
|
||||
throw std::runtime_error("[ ERROR ] The imageFilepath is not set correctly or doesn't exist");
|
||||
}
|
||||
|
||||
// Validate LabelFilePath
|
||||
std::string labelFilepath = argv[4];
|
||||
if(!checkLabelFileExtension(labelFilepath)) {
|
||||
throw std::runtime_error("[ ERROR ] The LabelFilepath is not set correctly and the labels file should end with extension .txt");
|
||||
}
|
||||
|
||||
std::vector<std::string> labels{readLabels(labelFilepath)};
|
||||
|
||||
Ort::Env env(OrtLoggingLevel::ORT_LOGGING_LEVEL_WARNING,
|
||||
instanceName.c_str());
|
||||
Ort::SessionOptions sessionOptions;
|
||||
sessionOptions.SetIntraOpNumThreads(1);
|
||||
|
||||
//Appending OpenVINO Execution Provider API
|
||||
if (useOPENVINO) {
|
||||
// Using OPENVINO backend
|
||||
OrtOpenVINOProviderOptions options;
|
||||
options.device_type = "CPU_FP32"; //Other options are: GPU_FP32, GPU_FP16, MYRIAD_FP16
|
||||
std::cout << "OpenVINO device type is set to: " << options.device_type << std::endl;
|
||||
sessionOptions.AppendExecutionProvider_OpenVINO(options);
|
||||
}
|
||||
|
||||
// Sets graph optimization level
|
||||
// Available levels are
|
||||
// ORT_DISABLE_ALL -> To disable all optimizations
|
||||
// ORT_ENABLE_BASIC -> To enable basic optimizations (Such as redundant node
|
||||
// removals) ORT_ENABLE_EXTENDED -> To enable extended optimizations
|
||||
// (Includes level 1 + more complex optimizations like node fusions)
|
||||
// ORT_ENABLE_ALL -> To Enable All possible optimizations
|
||||
sessionOptions.SetGraphOptimizationLevel(
|
||||
GraphOptimizationLevel::ORT_DISABLE_ALL);
|
||||
|
||||
//Creation: The Ort::Session is created here
|
||||
Ort::Session session(env, modelFilepath.c_str(), sessionOptions);
|
||||
|
||||
Ort::AllocatorWithDefaultOptions allocator;
|
||||
|
||||
size_t numInputNodes = session.GetInputCount();
|
||||
size_t numOutputNodes = session.GetOutputCount();
|
||||
|
||||
std::cout << "Number of Input Nodes: " << numInputNodes << std::endl;
|
||||
std::cout << "Number of Output Nodes: " << numOutputNodes << std::endl;
|
||||
|
||||
const char* inputName = session.GetInputName(0, allocator);
|
||||
std::cout << "Input Name: " << inputName << std::endl;
|
||||
|
||||
Ort::TypeInfo inputTypeInfo = session.GetInputTypeInfo(0);
|
||||
auto inputTensorInfo = inputTypeInfo.GetTensorTypeAndShapeInfo();
|
||||
|
||||
ONNXTensorElementDataType inputType = inputTensorInfo.GetElementType();
|
||||
std::cout << "Input Type: " << inputType << std::endl;
|
||||
|
||||
std::vector<int64_t> inputDims = inputTensorInfo.GetShape();
|
||||
std::cout << "Input Dimensions: " << inputDims << std::endl;
|
||||
|
||||
const char* outputName = session.GetOutputName(0, allocator);
|
||||
std::cout << "Output Name: " << outputName << std::endl;
|
||||
|
||||
Ort::TypeInfo outputTypeInfo = session.GetOutputTypeInfo(0);
|
||||
auto outputTensorInfo = outputTypeInfo.GetTensorTypeAndShapeInfo();
|
||||
|
||||
ONNXTensorElementDataType outputType = outputTensorInfo.GetElementType();
|
||||
std::cout << "Output Type: " << outputType << std::endl;
|
||||
|
||||
std::vector<int64_t> outputDims = outputTensorInfo.GetShape();
|
||||
std::cout << "Output Dimensions: " << outputDims << std::endl;
|
||||
//pre-processing the Image
|
||||
// step 1: Read an image in HWC BGR UINT8 format.
|
||||
cv::Mat imageBGR = cv::imread(imageFilepath, cv::ImreadModes::IMREAD_COLOR);
|
||||
|
||||
// step 2: Resize the image.
|
||||
cv::Mat resizedImageBGR, resizedImageRGB, resizedImage, preprocessedImage;
|
||||
cv::resize(imageBGR, resizedImageBGR,
|
||||
cv::Size(inputDims.at(2), inputDims.at(3)),
|
||||
cv::InterpolationFlags::INTER_CUBIC);
|
||||
|
||||
// step 3: Convert the image to HWC RGB UINT8 format.
|
||||
cv::cvtColor(resizedImageBGR, resizedImageRGB,
|
||||
cv::ColorConversionCodes::COLOR_BGR2RGB);
|
||||
// step 4: Convert the image to HWC RGB float format by dividing each pixel by 255.
|
||||
resizedImageRGB.convertTo(resizedImage, CV_32F, 1.0 / 255);
|
||||
|
||||
// step 5: Split the RGB channels from the image.
|
||||
cv::Mat channels[3];
|
||||
cv::split(resizedImage, channels);
|
||||
|
||||
//step 6: Normalize each channel.
|
||||
// Normalization per channel
|
||||
// Normalization parameters obtained from
|
||||
// https://github.com/onnx/models/tree/master/vision/classification/squeezenet
|
||||
channels[0] = (channels[0] - 0.485) / 0.229;
|
||||
channels[1] = (channels[1] - 0.456) / 0.224;
|
||||
channels[2] = (channels[2] - 0.406) / 0.225;
|
||||
|
||||
//step 7: Merge the RGB channels back to the image.
|
||||
cv::merge(channels, 3, resizedImage);
|
||||
|
||||
// step 8: Convert the image to CHW RGB float format.
|
||||
// HWC to CHW
|
||||
cv::dnn::blobFromImage(resizedImage, preprocessedImage);
|
||||
|
||||
|
||||
//Run Inference
|
||||
|
||||
/* To run inference using ONNX Runtime, the user is responsible for creating and managing the
|
||||
input and output buffers. These buffers could be created and managed via std::vector.
|
||||
The linear-format input data should be copied to the buffer for ONNX Runtime inference. */
|
||||
|
||||
size_t inputTensorSize = vectorProduct(inputDims);
|
||||
std::vector<float> inputTensorValues(inputTensorSize);
|
||||
inputTensorValues.assign(preprocessedImage.begin<float>(),
|
||||
preprocessedImage.end<float>());
|
||||
|
||||
size_t outputTensorSize = vectorProduct(outputDims);
|
||||
assert(("Output tensor size should equal to the label set size.",
|
||||
labels.size() == outputTensorSize));
|
||||
std::vector<float> outputTensorValues(outputTensorSize);
|
||||
|
||||
|
||||
/* Once the buffers were created, they would be used for creating instances of Ort::Value
|
||||
which is the tensor format for ONNX Runtime. There could be multiple inputs for a neural network,
|
||||
so we have to prepare an array of Ort::Value instances for inputs and outputs respectively even if
|
||||
we only have one input and one output. */
|
||||
|
||||
std::vector<const char*> inputNames{inputName};
|
||||
std::vector<const char*> outputNames{outputName};
|
||||
std::vector<Ort::Value> inputTensors;
|
||||
std::vector<Ort::Value> outputTensors;
|
||||
|
||||
/*
|
||||
Creating ONNX Runtime inference sessions, querying input and output names,
|
||||
dimensions, and types are trivial.
|
||||
Setup inputs & outputs: The input & output tensors are created here. */
|
||||
|
||||
Ort::MemoryInfo memoryInfo = Ort::MemoryInfo::CreateCpu(
|
||||
OrtAllocatorType::OrtArenaAllocator, OrtMemType::OrtMemTypeDefault);
|
||||
inputTensors.push_back(Ort::Value::CreateTensor<float>(
|
||||
memoryInfo, inputTensorValues.data(), inputTensorSize, inputDims.data(),
|
||||
inputDims.size()));
|
||||
outputTensors.push_back(Ort::Value::CreateTensor<float>(
|
||||
memoryInfo, outputTensorValues.data(), outputTensorSize,
|
||||
outputDims.data(), outputDims.size()));
|
||||
|
||||
/* To run inference, we provide the run options, an array of input names corresponding to the
|
||||
inputs in the input tensor, an array of input tensor, number of inputs, an array of output names
|
||||
corresponding to the the outputs in the output tensor, an array of output tensor, number of outputs. */
|
||||
|
||||
session.Run(Ort::RunOptions{nullptr}, inputNames.data(),
|
||||
inputTensors.data(), 1, outputNames.data(),
|
||||
outputTensors.data(), 1);
|
||||
|
||||
int predId = 0;
|
||||
float activation = 0;
|
||||
float maxActivation = std::numeric_limits<float>::lowest();
|
||||
float expSum = 0;
|
||||
/* The inference result could be found in the buffer for the output tensors,
|
||||
which are usually the buffer from std::vector instances. */
|
||||
for (int i = 0; i < labels.size(); i++) {
|
||||
activation = outputTensorValues.at(i);
|
||||
expSum += std::exp(activation);
|
||||
if (activation > maxActivation)
|
||||
{
|
||||
predId = i;
|
||||
maxActivation = activation;
|
||||
}
|
||||
}
|
||||
std::cout << "Predicted Label ID: " << predId << std::endl;
|
||||
std::cout << "Predicted Label: " << labels.at(predId) << std::endl;
|
||||
float result;
|
||||
try {
|
||||
result = division(std::exp(maxActivation), expSum);
|
||||
std::cout << "Uncalibrated Confidence: " << result << std::endl;
|
||||
}
|
||||
catch (std::runtime_error& e) {
|
||||
std::cout << "Exception occurred" << std::endl << e.what();
|
||||
}
|
||||
|
||||
// Measure latency
|
||||
int numTests{100};
|
||||
std::chrono::steady_clock::time_point begin =
|
||||
std::chrono::steady_clock::now();
|
||||
|
||||
//Run: Running the session is done in the Run() method:
|
||||
for (int i = 0; i < numTests; i++) {
|
||||
session.Run(Ort::RunOptions{nullptr}, inputNames.data(),
|
||||
inputTensors.data(), 1, outputNames.data(),
|
||||
outputTensors.data(), 1);
|
||||
}
|
||||
std::chrono::steady_clock::time_point end =
|
||||
std::chrono::steady_clock::now();
|
||||
std::cout << "Minimum Inference Latency: "
|
||||
<< std::chrono::duration_cast<std::chrono::milliseconds>(end - begin).count() / static_cast<float>(numTests)
|
||||
<< " ms" << std::endl;
|
||||
return 0;
|
||||
}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,63 +0,0 @@
|
|||
This directory contains a few C/C++ sample applications for demoing onnxruntime usage:
|
||||
|
||||
1. (Windows only) fns_candy_style_transfer: A C application that uses the FNS-Candy style transfer model to re-style images.
|
||||
2. (Windows only) MNIST: A windows GUI application for doing handwriting recognition
|
||||
3. (Windows only) imagenet: An end-to-end sample for the [ImageNet Large Scale Visual Recognition Challenge 2012](http://www.image-net.org/challenges/LSVRC/2012/) - requires ATL libraries to be installed as a part of the VS Studio installation.
|
||||
4. model-explorer: A commandline C++ application that generates random data and performs model inference. A second C++ application demonstrates how to perform batch processing.
|
||||
|
||||
# How to build
|
||||
|
||||
## Prerequisites
|
||||
1. Visual Studio 2015/2017/2019
|
||||
2. cmake(version >=3.13)
|
||||
3. (optional) [libpng 1.6](http://www.libpng.org/pub/png/libpng.html)
|
||||
|
||||
You may get a precompiled libpng library from [https://onnxruntimetestdata.blob.core.windows.net/models/libpng.zip](https://onnxruntimetestdata.blob.core.windows.net/models/libpng.zip)
|
||||
|
||||
## Install ONNX Runtime
|
||||
You may either get a prebuit onnxruntime from nuget.org, or build it from source by following the [build instructions](https://www.onnxruntime.ai/docs/how-to/build.html).
|
||||
If you build it by yourself, you must append the "--build_shared_lib" flag to your build command.
|
||||
Open Developer Command Prompt for Visual Studio version you are going to use. This will setup necessary environment for the compiler and other things to be found.
|
||||
```
|
||||
build.bat --config RelWithDebInfo --build_shared_lib --parallel
|
||||
```
|
||||
|
||||
By default this will build a project with "C:\Program Files (x86)\onnxruntime" install destination. This is a protected folder on Windows. If you do not want to run installation with elevated priviliges you will need to override the default installation location by passing extra CMake arguments. For example:
|
||||
|
||||
```
|
||||
build.bat --config RelWithDebInfo --build_shared_lib --parallel --cmake_extra_defines CMAKE_INSTALL_PREFIX=c:\dev\ort_install
|
||||
```
|
||||
|
||||
By default products of the build on Windows go to .\build\Windows\<config> folder. In the case above it would be .\build\Windows\RelWithDebInfo.
|
||||
If you did not specify alternative installation location above you would need to open an elevated command prompt to install onnxruntime.
|
||||
Run the following commands.
|
||||
|
||||
```
|
||||
cd .\Windows\RelWithDebInfo
|
||||
msbuild INSTALL.vcxproj /p:Configuration=RelWithDebInfo
|
||||
```
|
||||
|
||||
## Build the samples
|
||||
|
||||
Open Developer Command Prompt for Visual Studio version you are going to use, change your current directory to samples\c_cxx, then run
|
||||
```bat
|
||||
mkdir build && cd build
|
||||
cmake .. -A x64 -T host=x64 -DLIBPNG_ROOTDIR=C:\path\to\your\libpng\binary -DONNXRUNTIME_ROOTDIR=c:\dev\ort_install
|
||||
```
|
||||
You may omit the "-DLIBPNG_ROOTDIR=..." argument if you don't have the libpng library.
|
||||
You may omit "-DONNXRUNTIME_ROOTDIR=..." if you installed to a default location.
|
||||
|
||||
You may append "-Donnxruntime_USE_CUDA=ON" or "-Donnxruntime_USE_DML=ON" to the last command args if your onnxruntime binary was built with CUDA or DirectML support respectively.
|
||||
|
||||
You can then either open the solution in a Visual Studio and build it from there
|
||||
```bat
|
||||
devenv onnxruntime_samples.sln
|
||||
```
|
||||
Or build it using msbuild
|
||||
|
||||
```bat
|
||||
msbuild onnxruntime_samples.sln /p:Configuration=Debug|Release
|
||||
```
|
||||
|
||||
To run the samples make sure that your Install Folder Bin is in the path so your sample executable can find onnxruntime dll and libpng if you used it.
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
add_executable(fns_candy_style_transfer "fns_candy_style_transfer.c")
|
||||
target_include_directories(fns_candy_style_transfer PRIVATE ${PROJECT_SOURCE_DIR}/include ${PNG_INCLUDE_DIRS})
|
||||
target_link_libraries(fns_candy_style_transfer PRIVATE onnxruntime ${PNG_LIBRARIES})
|
||||
if(PNG_LIBDIR)
|
||||
target_link_directories(fns_candy_style_transfer PRIVATE ${PNG_LIBDIR})
|
||||
endif()
|
|
@ -1,20 +0,0 @@
|
|||
# FNS Candy
|
||||
FNS Candy is a style transfer model. In this sample application, we use the ONNX Runtime C API to process an image using the FNS Candy model in ONNX format.
|
||||
|
||||
# Build Instructions
|
||||
See [../README.md](../README.md)
|
||||
|
||||
# Prepare data
|
||||
First, download the FNS Candy ONNX model from [here](https://raw.githubusercontent.com/microsoft/Windows-Machine-Learning/master/Samples/FNSCandyStyleTransfer/UWP/cs/Assets/candy.onnx).
|
||||
|
||||
Then, prepare an image:
|
||||
1. PNG format
|
||||
2. Dimension of 720x720
|
||||
|
||||
# Run
|
||||
Command to run the application:
|
||||
```
|
||||
fns_candy_style_transfer.exe <model_path> <input_image_path> <output_image_path> [cpu|cuda|dml]
|
||||
```
|
||||
|
||||
To use the CUDA or DirectML execution providers, specify `cuda` or `dml` on the command line. `cpu` is the default.
|
|
@ -1,301 +0,0 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
#include "onnxruntime_c_api.h"
|
||||
#include "providers.h"
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <png.h>
|
||||
#ifdef _WIN32
|
||||
#include <objbase.h>
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#define tcscmp wcscmp
|
||||
#else
|
||||
#define tcscmp strcmp
|
||||
#endif
|
||||
|
||||
const OrtApi* g_ort = NULL;
|
||||
|
||||
#define ORT_ABORT_ON_ERROR(expr) \
|
||||
do { \
|
||||
OrtStatus* onnx_status = (expr); \
|
||||
if (onnx_status != NULL) { \
|
||||
const char* msg = g_ort->GetErrorMessage(onnx_status); \
|
||||
fprintf(stderr, "%s\n", msg); \
|
||||
g_ort->ReleaseStatus(onnx_status); \
|
||||
abort(); \
|
||||
} \
|
||||
} while (0);
|
||||
|
||||
/**
|
||||
* convert input from HWC format to CHW format
|
||||
* \param input A single image. The byte array has length of 3*h*w
|
||||
* \param h image height
|
||||
* \param w image width
|
||||
* \param output A float array. should be freed by caller after use
|
||||
* \param output_count Array length of the `output` param
|
||||
*/
|
||||
static void hwc_to_chw(const png_byte* input, size_t h, size_t w, float** output, size_t* output_count) {
|
||||
size_t stride = h * w;
|
||||
*output_count = stride * 3;
|
||||
float* output_data = (float*)malloc(*output_count * sizeof(float));
|
||||
for (size_t i = 0; i != stride; ++i) {
|
||||
for (size_t c = 0; c != 3; ++c) {
|
||||
output_data[c * stride + i] = input[i * 3 + c];
|
||||
}
|
||||
}
|
||||
*output = output_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* convert input from CHW format to HWC format
|
||||
* \param input A single image. This float array has length of 3*h*w
|
||||
* \param h image height
|
||||
* \param w image width
|
||||
* \param output A byte array. should be freed by caller after use
|
||||
*/
|
||||
static void chw_to_hwc(const float* input, size_t h, size_t w, png_bytep* output) {
|
||||
size_t stride = h * w;
|
||||
png_bytep output_data = (png_bytep)malloc(stride * 3);
|
||||
for (int c = 0; c != 3; ++c) {
|
||||
size_t t = c * stride;
|
||||
for (size_t i = 0; i != stride; ++i) {
|
||||
float f = input[t + i];
|
||||
if (f < 0.f || f > 255.0f) f = 0;
|
||||
output_data[i * 3 + c] = (png_byte)f;
|
||||
}
|
||||
}
|
||||
*output = output_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* \param out should be freed by caller after use
|
||||
* \param output_count Array length of the `out` param
|
||||
*/
|
||||
static int read_png_file(const char* input_file, size_t* height, size_t* width, float** out, size_t* output_count) {
|
||||
png_image image; /* The control structure used by libpng */
|
||||
/* Initialize the 'png_image' structure. */
|
||||
memset(&image, 0, (sizeof image));
|
||||
image.version = PNG_IMAGE_VERSION;
|
||||
if (png_image_begin_read_from_file(&image, input_file) == 0) {
|
||||
return -1;
|
||||
}
|
||||
png_bytep buffer;
|
||||
image.format = PNG_FORMAT_BGR;
|
||||
size_t input_data_length = PNG_IMAGE_SIZE(image);
|
||||
if (input_data_length != 720 * 720 * 3) {
|
||||
printf("input_data_length:%zd\n", input_data_length);
|
||||
return -1;
|
||||
}
|
||||
buffer = (png_bytep)malloc(input_data_length);
|
||||
memset(buffer, 0, input_data_length);
|
||||
if (png_image_finish_read(&image, NULL /*background*/, buffer, 0 /*row_stride*/, NULL /*colormap*/) == 0) {
|
||||
return -1;
|
||||
}
|
||||
hwc_to_chw(buffer, image.height, image.width, out, output_count);
|
||||
free(buffer);
|
||||
*width = image.width;
|
||||
*height = image.height;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* \param tensor should be a float tensor in [N,C,H,W] format
|
||||
*/
|
||||
static int write_tensor_to_png_file(OrtValue* tensor, const char* output_file) {
|
||||
struct OrtTensorTypeAndShapeInfo* shape_info;
|
||||
ORT_ABORT_ON_ERROR(g_ort->GetTensorTypeAndShape(tensor, &shape_info));
|
||||
size_t dim_count;
|
||||
ORT_ABORT_ON_ERROR(g_ort->GetDimensionsCount(shape_info, &dim_count));
|
||||
if (dim_count != 4) {
|
||||
printf("output tensor must have 4 dimensions");
|
||||
return -1;
|
||||
}
|
||||
int64_t dims[4];
|
||||
ORT_ABORT_ON_ERROR(g_ort->GetDimensions(shape_info, dims, sizeof(dims) / sizeof(dims[0])));
|
||||
if (dims[0] != 1 || dims[1] != 3) {
|
||||
printf("output tensor shape error");
|
||||
return -1;
|
||||
}
|
||||
float* f;
|
||||
ORT_ABORT_ON_ERROR(g_ort->GetTensorMutableData(tensor, (void**)&f));
|
||||
png_bytep model_output_bytes;
|
||||
png_image image;
|
||||
memset(&image, 0, (sizeof image));
|
||||
image.version = PNG_IMAGE_VERSION;
|
||||
image.format = PNG_FORMAT_BGR;
|
||||
image.height = (png_uint_32)dims[2];
|
||||
image.width = (png_uint_32)dims[3];
|
||||
chw_to_hwc(f, image.height, image.width, &model_output_bytes);
|
||||
int ret = 0;
|
||||
if (png_image_write_to_file(&image, output_file, 0 /*convert_to_8bit*/, model_output_bytes, 0 /*row_stride*/,
|
||||
NULL /*colormap*/) == 0) {
|
||||
printf("write to '%s' failed:%s\n", output_file, image.message);
|
||||
ret = -1;
|
||||
}
|
||||
free(model_output_bytes);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void usage() { printf("usage: <model_path> <input_file> <output_file> [cpu|cuda|dml] \n"); }
|
||||
|
||||
#ifdef _WIN32
|
||||
static char* convert_string(const wchar_t* input) {
|
||||
size_t src_len = wcslen(input) + 1;
|
||||
if (src_len > INT_MAX) {
|
||||
printf("size overflow\n");
|
||||
abort();
|
||||
}
|
||||
const int len = WideCharToMultiByte(CP_ACP, 0, input, (int)src_len, NULL, 0, NULL, NULL);
|
||||
assert(len > 0);
|
||||
char* ret = (char*)malloc(len);
|
||||
assert(ret != NULL);
|
||||
const int r = WideCharToMultiByte(CP_ACP, 0, input, (int)src_len, ret, len, NULL, NULL);
|
||||
assert(len == r);
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
int run_inference(OrtSession* session, const ORTCHAR_T* input_file, const ORTCHAR_T* output_file) {
|
||||
size_t input_height;
|
||||
size_t input_width;
|
||||
float* model_input;
|
||||
size_t model_input_ele_count;
|
||||
#ifdef _WIN32
|
||||
char* output_file_p = convert_string(output_file);
|
||||
char* input_file_p = convert_string(input_file);
|
||||
#else
|
||||
char* output_file_p = output_file;
|
||||
char* input_file_p = input_file;
|
||||
#endif
|
||||
if (read_png_file(input_file_p, &input_height, &input_width, &model_input, &model_input_ele_count) != 0) {
|
||||
return -1;
|
||||
}
|
||||
if (input_height != 720 || input_width != 720) {
|
||||
printf("please resize to image to 720x720\n");
|
||||
free(model_input);
|
||||
return -1;
|
||||
}
|
||||
OrtMemoryInfo* memory_info;
|
||||
ORT_ABORT_ON_ERROR(g_ort->CreateCpuMemoryInfo(OrtArenaAllocator, OrtMemTypeDefault, &memory_info));
|
||||
const int64_t input_shape[] = {1, 3, 720, 720};
|
||||
const size_t input_shape_len = sizeof(input_shape) / sizeof(input_shape[0]);
|
||||
const size_t model_input_len = model_input_ele_count * sizeof(float);
|
||||
|
||||
OrtValue* input_tensor = NULL;
|
||||
ORT_ABORT_ON_ERROR(g_ort->CreateTensorWithDataAsOrtValue(memory_info, model_input, model_input_len, input_shape,
|
||||
input_shape_len, ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT,
|
||||
&input_tensor));
|
||||
assert(input_tensor != NULL);
|
||||
int is_tensor;
|
||||
ORT_ABORT_ON_ERROR(g_ort->IsTensor(input_tensor, &is_tensor));
|
||||
assert(is_tensor);
|
||||
g_ort->ReleaseMemoryInfo(memory_info);
|
||||
const char* input_names[] = {"inputImage"};
|
||||
const char* output_names[] = {"outputImage"};
|
||||
OrtValue* output_tensor = NULL;
|
||||
ORT_ABORT_ON_ERROR(
|
||||
g_ort->Run(session, NULL, input_names, (const OrtValue* const*)&input_tensor, 1, output_names, 1, &output_tensor));
|
||||
assert(output_tensor != NULL);
|
||||
ORT_ABORT_ON_ERROR(g_ort->IsTensor(output_tensor, &is_tensor));
|
||||
assert(is_tensor);
|
||||
int ret = 0;
|
||||
if (write_tensor_to_png_file(output_tensor, output_file_p) != 0) {
|
||||
ret = -1;
|
||||
}
|
||||
g_ort->ReleaseValue(output_tensor);
|
||||
g_ort->ReleaseValue(input_tensor);
|
||||
free(model_input);
|
||||
#ifdef _WIN32
|
||||
free(input_file_p);
|
||||
free(output_file_p);
|
||||
#endif // _WIN32
|
||||
return ret;
|
||||
}
|
||||
|
||||
void verify_input_output_count(OrtSession* session) {
|
||||
size_t count;
|
||||
ORT_ABORT_ON_ERROR(g_ort->SessionGetInputCount(session, &count));
|
||||
assert(count == 1);
|
||||
ORT_ABORT_ON_ERROR(g_ort->SessionGetOutputCount(session, &count));
|
||||
assert(count == 1);
|
||||
}
|
||||
|
||||
#ifdef USE_CUDA
|
||||
void enable_cuda(OrtSessionOptions* session_options) {
|
||||
ORT_ABORT_ON_ERROR(OrtSessionOptionsAppendExecutionProvider_CUDA(session_options, 0));
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_DML
|
||||
void enable_dml(OrtSessionOptions* session_options) {
|
||||
ORT_ABORT_ON_ERROR(OrtSessionOptionsAppendExecutionProvider_DML(session_options, 0));
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
int wmain(int argc, wchar_t* argv[]) {
|
||||
#else
|
||||
int main(int argc, char* argv[]) {
|
||||
#endif
|
||||
if (argc < 4) {
|
||||
usage();
|
||||
return -1;
|
||||
}
|
||||
|
||||
g_ort = OrtGetApiBase()->GetApi(ORT_API_VERSION);
|
||||
#ifdef _WIN32
|
||||
//CoInitializeEx is only needed if Windows Image Component will be used in this program for image loading/saving.
|
||||
HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
|
||||
if (!SUCCEEDED(hr)) return -1;
|
||||
#endif
|
||||
ORTCHAR_T* model_path = argv[1];
|
||||
ORTCHAR_T* input_file = argv[2];
|
||||
ORTCHAR_T* output_file = argv[3];
|
||||
ORTCHAR_T* execution_provider = (argc >= 5) ? argv[4] : NULL;
|
||||
OrtEnv* env;
|
||||
ORT_ABORT_ON_ERROR(g_ort->CreateEnv(ORT_LOGGING_LEVEL_WARNING, "test", &env));
|
||||
OrtSessionOptions* session_options;
|
||||
ORT_ABORT_ON_ERROR(g_ort->CreateSessionOptions(&session_options));
|
||||
|
||||
if (execution_provider)
|
||||
{
|
||||
if (tcscmp(execution_provider, ORT_TSTR("cpu")) == 0) {
|
||||
// Nothing; this is the default
|
||||
} else if (tcscmp(execution_provider, ORT_TSTR("cuda")) == 0) {
|
||||
#ifdef USE_CUDA
|
||||
enable_cuda(session_options);
|
||||
#else
|
||||
puts("CUDA is not enabled in this build.");
|
||||
return -1;
|
||||
#endif
|
||||
} else if (tcscmp(execution_provider, ORT_TSTR("dml")) == 0) {
|
||||
#ifdef USE_DML
|
||||
enable_dml(session_options);
|
||||
#else
|
||||
puts("DirectML is not enabled in this build.");
|
||||
return -1;
|
||||
#endif
|
||||
} else {
|
||||
usage();
|
||||
puts("Invalid execution provider option.");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
OrtSession* session;
|
||||
ORT_ABORT_ON_ERROR(g_ort->CreateSession(env, model_path, session_options, &session));
|
||||
verify_input_output_count(session);
|
||||
int ret = run_inference(session, input_file, output_file);
|
||||
g_ort->ReleaseSessionOptions(session_options);
|
||||
g_ort->ReleaseSession(session);
|
||||
g_ort->ReleaseEnv(env);
|
||||
if (ret != 0) {
|
||||
fprintf(stderr, "fail\n");
|
||||
}
|
||||
#ifdef _WIN32
|
||||
CoUninitialize();
|
||||
#endif
|
||||
return ret;
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
set(FS_SOURCES local_filesystem.h sync_api.h controller.h controller.cc)
|
||||
if(WIN32)
|
||||
LIST(APPEND FS_SOURCES local_filesystem_win.cc sync_api_win.cc)
|
||||
else()
|
||||
LIST(APPEND FS_SOURCES local_filesystem_posix.cc sync_api_posix.cc)
|
||||
endif()
|
||||
add_library(slim_fs_lib ${FS_SOURCES})
|
||||
if(WIN32)
|
||||
target_compile_definitions(slim_fs_lib PRIVATE WIN32_LEAN_AND_MEAN NOMINMAX)
|
||||
endif()
|
||||
|
||||
if(JPEG_FOUND)
|
||||
SET(IMAGE_SRC jpeg_handle.cc jpeg_handle.h jpeg_mem.cc jpeg_mem.h image_loader_libjpeg.cc)
|
||||
elseif(WIN32)
|
||||
SET(IMAGE_SRC image_loader_wic.cc)
|
||||
endif()
|
||||
add_executable(image_classifier main.cc runnable_task.h data_processing.h ${IMAGE_SRC}
|
||||
async_ring_buffer.h image_loader.cc image_loader.h cached_interpolation.h single_consumer.h)
|
||||
if(JPEG_FOUND)
|
||||
target_compile_definitions(image_classifier PRIVATE HAVE_JPEG)
|
||||
SET(IMAGE_HEADERS ${JPEG_INCLUDE_DIR})
|
||||
SET(IMAGE_LIBS ${JPEG_LIBRARIES})
|
||||
endif()
|
||||
target_include_directories(image_classifier PRIVATE ${PROJECT_SOURCE_DIR}/include ${IMAGE_HEADERS})
|
||||
if(WIN32)
|
||||
target_compile_definitions(image_classifier PRIVATE WIN32_LEAN_AND_MEAN NOMINMAX)
|
||||
endif()
|
||||
target_link_libraries(image_classifier PRIVATE onnxruntime slim_fs_lib ${IMAGE_LIBS})
|
||||
|
||||
|
||||
|
|
@ -1,74 +0,0 @@
|
|||
# Overview
|
||||
|
||||
|
||||
|
||||
![taskflow](taskflow.png)
|
||||
|
||||
WARNING: If you want to train the model by yourself, you need at least 500GB disk space and a powerful NVIDIA GPU.
|
||||
|
||||
# Install tensorflow
|
||||
Install Python 3.x from [python.org](https://www.python.org/), then execute
|
||||
```
|
||||
pip install --upgrade tensorflow==1.14
|
||||
```
|
||||
For more information, see [Install Tensorflow](https://www.tensorflow.org/install)
|
||||
|
||||
# Get the Imagenet dataset
|
||||
We need the [ILSVRC-2012-CLS](http://www.image-net.org/challenges/LSVRC/2012/) image classification dataset from http://www.image-net.org/.
|
||||
|
||||
If you're going to train the model by yourself, then you need the full dataset, which is about 500GB. Otherwise, you only need the
|
||||
validation data set, which is just about 3GB.
|
||||
|
||||
For how to get the data, see [ImageNet Download faq](http://image-net.org/download-faq). Once you get an account, visit http://www.image-net.org/download-images. You will find "Download links to ILSVRC2012 image data" on that page
|
||||
|
||||
And also, please download the "[imagenet_lsvrc_2015_synsets.txt](https://raw.githubusercontent.com/tensorflow/models/master/research/slim/datasets/imagenet_lsvrc_2015_synsets.txt)" and "[imagenet_2012_validation_synset_labels.txt](https://raw.githubusercontent.com/tensorflow/models/master/research/slim/datasets/imagenet_2012_validation_synset_labels.txt)" from tensorflow models repo.
|
||||
|
||||
# Get the model
|
||||
Please check [https://github.com/tensorflow/models/tree/master/research/slim/](https://github.com/tensorflow/models/tree/master/research/slim/).
|
||||
You may either train the model by yourself, or just download a pretrained model provided by Google.
|
||||
If you don't know which one to download and try, we suggest you choose the [Inception V4](http://download.tensorflow.org/models/inception_v4_2016_09_09.tar.gz) model as a starting point.
|
||||
|
||||
After downloading, please uncompress it.
|
||||
```
|
||||
tar -zxvf inception_v4_2016_09_09.tar.gz
|
||||
```
|
||||
|
||||
The [Inception V4] zip file only contains a single checkpoint file: inception_v4.ckpt. It can't be directly used for inferencing.
|
||||
You need to combine the network definition and the checkpoint. Please follow the steps below:
|
||||
|
||||
1. Export the graph.
|
||||
```
|
||||
git clone https://github.com/tensorflow/models
|
||||
# Copy inception_v4.ckpt into models
|
||||
cd models
|
||||
# Ignore deprecation warnings
|
||||
python research\slim\export_inference_graph.py --model_name=inception_v4 --output_file=graph.pb
|
||||
```
|
||||
|
||||
2. Freeze the graph
|
||||
Run
|
||||
```
|
||||
freeze_graph.exe --input_graph=graph.pb --input_checkpoint=inception_v4.ckpt --output_graph=inception_v4.pb --output_node_names=InceptionV4/Logits/Predictions --input_binary=true
|
||||
```
|
||||
|
||||
# Convert the model to ONNX
|
||||
|
||||
```
|
||||
pip install --upgrade tf2onnx
|
||||
python -m tf2onnx.convert --input inception_v4.pb --inputs input:0 --outputs InceptionV4/Logits/Predictions:0 --opset 10 --output inception_v4.onnx
|
||||
```
|
||||
|
||||
You should see messages like these:
|
||||
|
||||
INFO - Successfully converted TensorFlow model inception_v4.pb to ONNX
|
||||
|
||||
INFO - ONNX model is saved at inception_v4.onnx
|
||||
|
||||
# Run the inferencing
|
||||
In your build dir of onnxruntime_samples, search for "image_classifier.exe" and run
|
||||
```
|
||||
image_classifier.exe C:\tools\imagnet_validation_data inception_v4.onnx imagenet_lsvrc_2015_synsets.txt imagenet_2012_validation_synset_labels.txt 32
|
||||
```
|
||||
Please replace the file names with the corresponding file paths.
|
||||
|
||||
The last parameter is batch size, you may need to adjust it according to your GPU memory size.
|
|
@ -1,309 +0,0 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
#include <iostream>
|
||||
#include <cstddef>
|
||||
#include <mutex>
|
||||
#include "controller.h"
|
||||
#include "onnxruntime/core/session/onnxruntime_cxx_api.h"
|
||||
#include "single_consumer.h"
|
||||
#include "runnable_task.h"
|
||||
|
||||
template <typename InputIterator>
|
||||
class AsyncRingBuffer {
|
||||
private:
|
||||
static VOID NTAPI ThreadPoolEntry(_Inout_ PTP_CALLBACK_INSTANCE pci, _Inout_opt_ PVOID data, _Inout_ PTP_WORK work) {
|
||||
CloseThreadpoolWork(work);
|
||||
(*(RunnableTask*)data)(pci);
|
||||
}
|
||||
|
||||
template <typename T = float>
|
||||
static size_t CalcItemSize(const std::vector<int64_t>& tensor_shape) {
|
||||
int64_t r = 1;
|
||||
for (int64_t i : tensor_shape) r *= i;
|
||||
return static_cast<size_t>(r) * sizeof(T);
|
||||
}
|
||||
|
||||
enum class BufferState { EMPTY,
|
||||
FILLING,
|
||||
FULL,
|
||||
TAKEN };
|
||||
const size_t batch_size_;
|
||||
using InputType = typename InputIterator::value_type;
|
||||
DataProcessing* p_;
|
||||
OutputCollector<InputType>* c_;
|
||||
size_t capacity_;
|
||||
struct QueueItem {
|
||||
Ort::Value value{nullptr};
|
||||
std::vector<InputType> taskid_list;
|
||||
|
||||
QueueItem() = default;
|
||||
QueueItem(const QueueItem&) = delete;
|
||||
QueueItem& operator=(const QueueItem&) = delete;
|
||||
};
|
||||
//A list of tensors with equal tensor shape
|
||||
SingleConsumerFIFO<QueueItem> queue_;
|
||||
using TensorListEntry = typename SingleConsumerFIFO<QueueItem>::ListEntry;
|
||||
Controller& threadpool_;
|
||||
std::vector<int64_t> CreateTensorShapeWithBatchSize(const std::vector<int64_t>& input, size_t batch_size) {
|
||||
std::vector<int64_t> shape(input.size() + 1);
|
||||
shape[0] = batch_size;
|
||||
size_t len = shape.size();
|
||||
for (size_t i = 1; i != len; ++i) {
|
||||
shape[i] = input[i - 1];
|
||||
}
|
||||
return shape;
|
||||
}
|
||||
std::mutex m;
|
||||
|
||||
/**
|
||||
* A collection of buffers with equal size.
|
||||
*/
|
||||
struct BufferManager {
|
||||
size_t capacity_;
|
||||
size_t item_size_in_bytes_;
|
||||
size_t write_index_ = 0;
|
||||
std::vector<BufferState> buffer_state;
|
||||
std::vector<InputType> input_task_id_for_buffers_;
|
||||
|
||||
// TODO: if there is an alignment requirement, this buffer need do padding between the tensors.
|
||||
std::vector<uint8_t> buffer_;
|
||||
|
||||
BufferManager(size_t capacity, size_t item_size_in_bytes)
|
||||
: capacity_(capacity),
|
||||
item_size_in_bytes_(item_size_in_bytes),
|
||||
buffer_state(capacity, BufferState::EMPTY),
|
||||
input_task_id_for_buffers_(capacity),
|
||||
buffer_(item_size_in_bytes * capacity) {}
|
||||
|
||||
size_t GetId(_In_ const uint8_t* p) const { return (p - buffer_.data()) / item_size_in_bytes_; }
|
||||
size_t GetItemSizeInBytes() const { return item_size_in_bytes_; }
|
||||
bool CompareAndSet(size_t i, BufferState old, BufferState new_state) {
|
||||
if (buffer_state[i] != old) return false;
|
||||
buffer_state[i] = new_state;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CompareAndSet(size_t index, size_t index_end, BufferState old, BufferState new_state) {
|
||||
assert(index_end >= index);
|
||||
for (size_t i = index; i != index_end; ++i) {
|
||||
if (buffer_state[i] != old) return false;
|
||||
}
|
||||
for (size_t i = index; i != index_end; ++i) {
|
||||
buffer_state[i] = new_state;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TakeRange(size_t index, size_t index_end, std::vector<InputType>& task_id_list) {
|
||||
assert(index_end >= index);
|
||||
if (!CompareAndSet(index, index_end, BufferState::FULL, BufferState::TAKEN)) {
|
||||
return false;
|
||||
}
|
||||
auto* p = &input_task_id_for_buffers_[index];
|
||||
auto* p_end = p + (index_end - index);
|
||||
task_id_list.assign(p, p_end);
|
||||
return true;
|
||||
}
|
||||
|
||||
_Success_(return ) bool TakeAllRemain(_Out_ uint8_t** begin, std::vector<InputType>& task_id_list) {
|
||||
auto iter =
|
||||
std::find_if(buffer_state.begin(), buffer_state.end(), [](BufferState s) { return s == BufferState::FULL; });
|
||||
if (iter == buffer_state.end()) return false;
|
||||
auto iter_end = std::find_if(iter, buffer_state.end(), [](BufferState s) { return s != BufferState::FULL; });
|
||||
|
||||
*begin = &buffer_[iter - buffer_state.begin()];
|
||||
if (!TakeRange(iter - buffer_state.begin(), iter_end - buffer_state.begin(), task_id_list)) {
|
||||
throw std::runtime_error("internal error");
|
||||
}
|
||||
size_t remain = std::count_if(buffer_state.begin(), buffer_state.end(),
|
||||
[](BufferState s) { return s != BufferState::TAKEN && s != BufferState::EMPTY; });
|
||||
if (remain != 0) {
|
||||
throw std::runtime_error("the buffer contains multiple non-contiguous region");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t* Begin() { return buffer_.data(); }
|
||||
|
||||
/*
|
||||
* Get a buffer pointer and set its state to FILLING
|
||||
* \param taskid
|
||||
* \return Pointer to the buffer
|
||||
*/
|
||||
uint8_t* Next(InputType taskid) {
|
||||
for (size_t i = 0; i != capacity_; ++i) {
|
||||
size_t index = (write_index_ + i) % capacity_;
|
||||
if (buffer_state[i] == BufferState::EMPTY) {
|
||||
buffer_state[i] = BufferState::FILLING;
|
||||
input_task_id_for_buffers_[i] = taskid;
|
||||
return &buffer_[index * item_size_in_bytes_];
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
BufferManager buffer_;
|
||||
InputIterator input_begin_;
|
||||
const InputIterator input_end_;
|
||||
// unsafe
|
||||
bool is_input_eof() const { return input_end_ == input_begin_; }
|
||||
size_t parallelism = 8;
|
||||
size_t current_running_downloders = 0;
|
||||
|
||||
void ReturnAndTake(TensorListEntry*& input_tensor) {
|
||||
std::lock_guard<std::mutex> g(m);
|
||||
if (input_tensor != nullptr) {
|
||||
size_t tensor_id = queue_.Return(input_tensor);
|
||||
size_t buffer_id = tensor_id * batch_size_;
|
||||
if (!buffer_.CompareAndSet(buffer_id, buffer_id + batch_size_, BufferState::TAKEN, BufferState::EMPTY)) {
|
||||
throw std::runtime_error("ReturnAndTake: internal state error");
|
||||
}
|
||||
}
|
||||
input_tensor = queue_.Take();
|
||||
}
|
||||
|
||||
void OnDownloadFinished(_Inout_opt_ ONNXRUNTIME_CALLBACK_INSTANCE pci, const uint8_t* dest) {
|
||||
size_t buffer_id = buffer_.GetId(dest);
|
||||
TensorListEntry* input_tensor = nullptr;
|
||||
{
|
||||
std::lock_guard<std::mutex> g(m);
|
||||
--current_running_downloders;
|
||||
if (!buffer_.CompareAndSet(buffer_id, BufferState::FILLING, BufferState::FULL)) {
|
||||
throw std::runtime_error("ReturnAndTake: internal state error");
|
||||
}
|
||||
size_t tensor_id = buffer_id / batch_size_;
|
||||
std::vector<InputType> task_id_list;
|
||||
buffer_id = tensor_id * batch_size_;
|
||||
if (buffer_.TakeRange(buffer_id, buffer_id + batch_size_, task_id_list)) {
|
||||
queue_.Put(tensor_id, [&task_id_list](QueueItem& i) {
|
||||
i.taskid_list = task_id_list;
|
||||
});
|
||||
input_tensor = queue_.Take();
|
||||
}
|
||||
}
|
||||
|
||||
bool eof = false;
|
||||
while (threadpool_.IsRunning()) {
|
||||
if (!eof) {
|
||||
int tasks = StartDownloadTasks();
|
||||
if (tasks < 0) {
|
||||
threadpool_.SetFailBit(pci, "Schedule download task failed");
|
||||
return;
|
||||
}
|
||||
if (tasks == 0) {
|
||||
threadpool_.SetEof(pci);
|
||||
eof = true;
|
||||
}
|
||||
}
|
||||
if (input_tensor == nullptr) {
|
||||
break;
|
||||
}
|
||||
(*c_)(input_tensor->value.taskid_list, input_tensor->value.value);
|
||||
ReturnAndTake(input_tensor);
|
||||
}
|
||||
}
|
||||
|
||||
void Fail(_Inout_opt_ ONNXRUNTIME_CALLBACK_INSTANCE pci, const char* errmsg) {
|
||||
threadpool_.SetFailBit(pci, errmsg);
|
||||
}
|
||||
|
||||
public:
|
||||
AsyncRingBuffer(size_t batch_size, size_t capacity, Controller& threadpool, const InputIterator& input_begin,
|
||||
const InputIterator& input_end, DataProcessing* p, OutputCollector<InputType>* c)
|
||||
: batch_size_(batch_size),
|
||||
p_(p),
|
||||
c_(c),
|
||||
capacity_((capacity + batch_size_ - 1) / batch_size_ * batch_size_),
|
||||
queue_(capacity_ / batch_size_),
|
||||
threadpool_(threadpool),
|
||||
buffer_(capacity_, CalcItemSize(p->GetOutputShape(1))),
|
||||
input_begin_(input_begin),
|
||||
input_end_(input_end) {
|
||||
Ort::MemoryInfo memory_info = Ort::MemoryInfo::CreateCpu(OrtArenaAllocator, OrtMemTypeDefault);
|
||||
uint8_t* output_data = buffer_.Begin();
|
||||
std::vector<int64_t> input_shape = p_->GetOutputShape(batch_size_);
|
||||
size_t off = CalcItemSize(input_shape);
|
||||
queue_.Init([&memory_info, off, &output_data, &input_shape](QueueItem& e) {
|
||||
e.value = Ort::Value::CreateTensor(memory_info, reinterpret_cast<float*>(output_data), off, input_shape.data(), input_shape.size());
|
||||
output_data += off;
|
||||
});
|
||||
}
|
||||
|
||||
void ProcessRemain() {
|
||||
queue_.Release();
|
||||
c_->ResetCache();
|
||||
|
||||
uint8_t* output_data;
|
||||
std::vector<InputType> task_id_list;
|
||||
if (!buffer_.TakeAllRemain(&output_data, task_id_list)) return;
|
||||
Ort::MemoryInfo memory_info = Ort::MemoryInfo::CreateCpu(OrtArenaAllocator, OrtMemTypeDefault);
|
||||
size_t count = task_id_list.size();
|
||||
assert(count != 0);
|
||||
std::vector<int64_t> input_shape = p_->GetOutputShape(count);
|
||||
size_t len = CalcItemSize(input_shape);
|
||||
Ort::Value input_tensor = Ort::Value::CreateTensor(memory_info, reinterpret_cast<float*>(output_data), len, input_shape.data(), input_shape.size());
|
||||
(*c_)(task_id_list, input_tensor);
|
||||
}
|
||||
|
||||
/**
|
||||
* call this function when a download task is just finished or any buffer became FREE.
|
||||
* \return 0 EOF. No more download task to schedule
|
||||
* 1 OK
|
||||
* -1 ERROR
|
||||
*/
|
||||
int StartDownloadTasks() {
|
||||
class DownloadTask : public RunnableTask {
|
||||
public:
|
||||
AsyncRingBuffer* requester;
|
||||
InputType source;
|
||||
uint8_t* dest;
|
||||
DownloadTask(AsyncRingBuffer* r, const InputType& s, uint8_t* d) : requester(r), source(s), dest(d) {}
|
||||
|
||||
void operator()(_In_opt_ ONNXRUNTIME_CALLBACK_INSTANCE pci) noexcept override {
|
||||
AsyncRingBuffer* r = requester;
|
||||
InputType s = source;
|
||||
uint8_t* d = dest;
|
||||
delete this;
|
||||
try {
|
||||
(*r->p_)(&s, d, r->buffer_.GetItemSizeInBytes());
|
||||
r->OnDownloadFinished(pci, d);
|
||||
} catch (const std::exception& ex) {
|
||||
fprintf(stderr, "%s\n", ex.what());
|
||||
r->Fail(pci, ex.what());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// search empty slots, launch a download task for each of them
|
||||
std::vector<DownloadTask*> tasks_to_launch;
|
||||
bool is_eof = false;
|
||||
{
|
||||
std::lock_guard<std::mutex> g(m);
|
||||
// if we have
|
||||
// 1. cpu (current_running_downloders < parallelism)
|
||||
// 2. memory (buffer available)
|
||||
// 3. input_task
|
||||
// then schedule a download task to the thread pool
|
||||
for (; current_running_downloders + tasks_to_launch.size() < parallelism && !is_input_eof();
|
||||
++input_begin_, ++current_running_downloders) {
|
||||
uint8_t* b = buffer_.Next(*input_begin_);
|
||||
if (b == nullptr) break; // no empty buffer
|
||||
tasks_to_launch.push_back(new DownloadTask(this, *input_begin_, b));
|
||||
}
|
||||
is_eof = is_input_eof();
|
||||
}
|
||||
|
||||
for (DownloadTask* p : tasks_to_launch) {
|
||||
if (!threadpool_.RunAsync(ThreadPoolEntry, p)) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_eof) {
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
};
|
|
@ -1,26 +0,0 @@
|
|||
/* Copyright 2015 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
// Compute the interpolation indices only once.
|
||||
struct CachedInterpolation {
|
||||
int64_t lower; // Lower source index used in the interpolation
|
||||
int64_t upper; // Upper source index used in the interpolation
|
||||
// 1-D linear interpolation scale (see:
|
||||
// https://en.wikipedia.org/wiki/Bilinear_interpolation)
|
||||
float lerp;
|
||||
};
|
|
@ -1,48 +0,0 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#include "controller.h"
|
||||
|
||||
Controller::Controller() : cleanup_group_(CreateThreadpoolCleanupGroup()), event_(CreateOnnxRuntimeEvent()) {
|
||||
InitializeThreadpoolEnvironment(&env_);
|
||||
SetThreadpoolCallbackPool(&env_, nullptr);
|
||||
SetThreadpoolCallbackCleanupGroup(&env_, cleanup_group_, nullptr);
|
||||
}
|
||||
|
||||
Controller::~Controller() noexcept { free(errmsg_); }
|
||||
|
||||
bool Controller::RunAsync(_Inout_ ONNXRUNTIME_CALLBACK_FUNCTION callback, _In_ void* data) {
|
||||
std::lock_guard<std::mutex> g(m_);
|
||||
if (state_ == State::RUNNING) {
|
||||
::CreateAndSubmitThreadpoolWork(callback, data, &env_);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string Controller::Wait() {
|
||||
WaitAndCloseEvent(event_);
|
||||
CloseThreadpoolCleanupGroupMembers(cleanup_group_, errmsg_ == nullptr ? FALSE : TRUE, nullptr);
|
||||
CloseThreadpoolCleanupGroup(cleanup_group_);
|
||||
return errmsg_ == nullptr ? std::string() : errmsg_;
|
||||
}
|
||||
|
||||
void Controller::SetFailBit(_Inout_opt_ ONNXRUNTIME_CALLBACK_INSTANCE pci, _In_ const char* err_msg) {
|
||||
std::lock_guard<std::mutex> g(m_);
|
||||
if (state_ == State::RUNNING || state_ == State::SHUTDOWN) {
|
||||
state_ = State::STOPPED;
|
||||
is_running_ = false;
|
||||
errmsg_ = my_strdup(err_msg);
|
||||
::OnnxRuntimeSetEventWhenCallbackReturns(pci, event_);
|
||||
}
|
||||
}
|
||||
|
||||
bool Controller::SetEof(ONNXRUNTIME_CALLBACK_INSTANCE pci) {
|
||||
std::lock_guard<std::mutex> g(m_);
|
||||
if (state_ == State::RUNNING) {
|
||||
state_ = State::SHUTDOWN;
|
||||
::OnnxRuntimeSetEventWhenCallbackReturns(pci, event_);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "sync_api.h"
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
|
||||
class Controller {
|
||||
private:
|
||||
PTP_CLEANUP_GROUP const cleanup_group_;
|
||||
TP_CALLBACK_ENVIRON env_;
|
||||
ONNXRUNTIME_EVENT event_;
|
||||
std::atomic<bool> is_running_ = true;
|
||||
std::mutex m_;
|
||||
enum class State { RUNNING, SHUTDOWN, STOPPED } state_ = State::RUNNING;
|
||||
char* errmsg_ = nullptr;
|
||||
|
||||
public:
|
||||
Controller();
|
||||
~Controller() noexcept;
|
||||
Controller(const Controller&) = delete;
|
||||
Controller& operator=(const Controller&) = delete;
|
||||
// return true if SetFailBit has not been called
|
||||
bool IsRunning() const { return is_running_; }
|
||||
|
||||
void SetFailBit(_Inout_opt_ ONNXRUNTIME_CALLBACK_INSTANCE pci, _In_ const char* err_msg);
|
||||
bool SetEof(_Inout_opt_ ONNXRUNTIME_CALLBACK_INSTANCE pci);
|
||||
|
||||
// Wait the state becoming stopped, and all the submitted work has been finished(or cancelled if error happened)
|
||||
std::string Wait();
|
||||
bool RunAsync(_Inout_ ONNXRUNTIME_CALLBACK_FUNCTION callback, _In_ void* data);
|
||||
};
|
|
@ -1,13 +0,0 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
#include <vector>
|
||||
#include <sal.h>
|
||||
|
||||
class DataProcessing {
|
||||
public:
|
||||
virtual void operator()(_In_ const void* input_data, _Out_writes_bytes_all_(output_len) void* output_data, size_t output_len) const = 0;
|
||||
virtual std::vector<int64_t> GetOutputShape(size_t batch_size) const = 0;
|
||||
virtual ~DataProcessing() = default;
|
||||
};
|
|
@ -1,189 +0,0 @@
|
|||
/* Copyright 2015 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <assert.h>
|
||||
#include "image_loader.h"
|
||||
#include "cached_interpolation.h"
|
||||
#include "local_filesystem.h"
|
||||
|
||||
namespace {
|
||||
/**
|
||||
* CalculateResizeScale determines the float scaling factor.
|
||||
* @param in_size
|
||||
* @param out_size
|
||||
* @param align_corners If true, the centers of the 4 corner pixels of the input and output tensors are aligned,
|
||||
* preserving the values at the corner pixels
|
||||
* @return
|
||||
*/
|
||||
inline float CalculateResizeScale(int64_t in_size, int64_t out_size, bool align_corners) {
|
||||
return (align_corners && out_size > 1) ? (in_size - 1) / static_cast<float>(out_size - 1)
|
||||
: in_size / static_cast<float>(out_size);
|
||||
}
|
||||
|
||||
inline void compute_interpolation_weights(const int64_t out_size, const int64_t in_size, const float scale,
|
||||
CachedInterpolation* interpolation) {
|
||||
interpolation[out_size].lower = 0;
|
||||
interpolation[out_size].upper = 0;
|
||||
for (int64_t i = out_size - 1; i >= 0; --i) {
|
||||
const float in = i * scale;
|
||||
interpolation[i].lower = static_cast<int64_t>(in);
|
||||
interpolation[i].upper = std::min(interpolation[i].lower + 1, in_size - 1);
|
||||
interpolation[i].lerp = in - interpolation[i].lower;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the bilinear interpolation from the appropriate 4 float points
|
||||
* and the linear interpolation weights.
|
||||
*/
|
||||
inline float compute_lerp(const float top_left, const float top_right, const float bottom_left,
|
||||
const float bottom_right, const float x_lerp, const float y_lerp) {
|
||||
const float top = top_left + (top_right - top_left) * x_lerp;
|
||||
const float bottom = bottom_left + (bottom_right - bottom_left) * x_lerp;
|
||||
return top + (bottom - top) * y_lerp;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
template <typename T>
|
||||
void ResizeImageInMemory(const T* input_data, float* output_data, int in_height, int in_width, int out_height,
|
||||
int out_width, int channels) {
|
||||
float height_scale = CalculateResizeScale(in_height, out_height, false);
|
||||
float width_scale = CalculateResizeScale(in_width, out_width, false);
|
||||
|
||||
std::vector<CachedInterpolation> ys(out_height + 1);
|
||||
std::vector<CachedInterpolation> xs(out_width + 1);
|
||||
|
||||
// Compute the cached interpolation weights on the x and y dimensions.
|
||||
compute_interpolation_weights(out_height, in_height, height_scale, ys.data());
|
||||
compute_interpolation_weights(out_width, in_width, width_scale, xs.data());
|
||||
|
||||
// Scale x interpolation weights to avoid a multiplication during iteration.
|
||||
for (int i = 0; i < xs.size(); ++i) {
|
||||
xs[i].lower *= channels;
|
||||
xs[i].upper *= channels;
|
||||
}
|
||||
|
||||
const int64_t in_row_size = in_width * channels;
|
||||
const int64_t in_batch_num_values = in_height * in_row_size;
|
||||
const int64_t out_row_size = out_width * channels;
|
||||
|
||||
const T* input_b_ptr = input_data;
|
||||
float* output_y_ptr = output_data;
|
||||
const int batch_size = 1;
|
||||
|
||||
if (channels == 3) {
|
||||
for (int b = 0; b < batch_size; ++b) {
|
||||
for (int64_t y = 0; y < out_height; ++y) {
|
||||
const T* ys_input_lower_ptr = input_b_ptr + ys[y].lower * in_row_size;
|
||||
const T* ys_input_upper_ptr = input_b_ptr + ys[y].upper * in_row_size;
|
||||
const float ys_lerp = ys[y].lerp;
|
||||
for (int64_t x = 0; x < out_width; ++x) {
|
||||
const int64_t xs_lower = xs[x].lower;
|
||||
const int64_t xs_upper = xs[x].upper;
|
||||
const float xs_lerp = xs[x].lerp;
|
||||
|
||||
// Read channel 0.
|
||||
const float top_left0(ys_input_lower_ptr[xs_lower + 0]);
|
||||
const float top_right0(ys_input_lower_ptr[xs_upper + 0]);
|
||||
const float bottom_left0(ys_input_upper_ptr[xs_lower + 0]);
|
||||
const float bottom_right0(ys_input_upper_ptr[xs_upper + 0]);
|
||||
|
||||
// Read channel 1.
|
||||
const float top_left1(ys_input_lower_ptr[xs_lower + 1]);
|
||||
const float top_right1(ys_input_lower_ptr[xs_upper + 1]);
|
||||
const float bottom_left1(ys_input_upper_ptr[xs_lower + 1]);
|
||||
const float bottom_right1(ys_input_upper_ptr[xs_upper + 1]);
|
||||
|
||||
// Read channel 2.
|
||||
const float top_left2(ys_input_lower_ptr[xs_lower + 2]);
|
||||
const float top_right2(ys_input_lower_ptr[xs_upper + 2]);
|
||||
const float bottom_left2(ys_input_upper_ptr[xs_lower + 2]);
|
||||
const float bottom_right2(ys_input_upper_ptr[xs_upper + 2]);
|
||||
|
||||
// Compute output.
|
||||
output_y_ptr[x * channels + 0] =
|
||||
compute_lerp(top_left0, top_right0, bottom_left0, bottom_right0, xs_lerp, ys_lerp);
|
||||
output_y_ptr[x * channels + 1] =
|
||||
compute_lerp(top_left1, top_right1, bottom_left1, bottom_right1, xs_lerp, ys_lerp);
|
||||
output_y_ptr[x * channels + 2] =
|
||||
compute_lerp(top_left2, top_right2, bottom_left2, bottom_right2, xs_lerp, ys_lerp);
|
||||
}
|
||||
output_y_ptr += out_row_size;
|
||||
}
|
||||
input_b_ptr += in_batch_num_values;
|
||||
}
|
||||
} else {
|
||||
for (int b = 0; b < batch_size; ++b) {
|
||||
for (int64_t y = 0; y < out_height; ++y) {
|
||||
const T* ys_input_lower_ptr = input_b_ptr + ys[y].lower * in_row_size;
|
||||
const T* ys_input_upper_ptr = input_b_ptr + ys[y].upper * in_row_size;
|
||||
const float ys_lerp = ys[y].lerp;
|
||||
for (int64_t x = 0; x < out_width; ++x) {
|
||||
auto xs_lower = xs[x].lower;
|
||||
auto xs_upper = xs[x].upper;
|
||||
auto xs_lerp = xs[x].lerp;
|
||||
for (int c = 0; c < channels; ++c) {
|
||||
const float top_left(ys_input_lower_ptr[xs_lower + c]);
|
||||
const float top_right(ys_input_lower_ptr[xs_upper + c]);
|
||||
const float bottom_left(ys_input_upper_ptr[xs_lower + c]);
|
||||
const float bottom_right(ys_input_upper_ptr[xs_upper + c]);
|
||||
output_y_ptr[x * channels + c] =
|
||||
compute_lerp(top_left, top_right, bottom_left, bottom_right, xs_lerp, ys_lerp);
|
||||
}
|
||||
}
|
||||
output_y_ptr += out_row_size;
|
||||
}
|
||||
input_b_ptr += in_batch_num_values;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template void ResizeImageInMemory(const float* input_data, float* output_data, int in_height, int in_width,
|
||||
int out_height, int out_width, int channels);
|
||||
|
||||
template void ResizeImageInMemory(const uint8_t* input_data, float* output_data, int in_height, int in_width,
|
||||
int out_height, int out_width, int channels);
|
||||
|
||||
InceptionPreprocessing::InceptionPreprocessing(int out_height, int out_width, int channels)
|
||||
: out_height_(out_height), out_width_(out_width), channels_(channels) {
|
||||
if (!CreateImageLoader(&image_loader_)) {
|
||||
throw std::runtime_error("create image loader failed");
|
||||
}
|
||||
}
|
||||
|
||||
// see: https://github.com/tensorflow/models/blob/master/research/slim/preprocessing/inception_preprocessing.py
|
||||
// function: preprocess_for_eval
|
||||
void InceptionPreprocessing::operator()(_In_ const void* input_data,
|
||||
_Out_writes_bytes_all_(output_len) void* output_data, size_t output_len) const {
|
||||
const TCharString& file_name = *reinterpret_cast<const TCharString*>(input_data);
|
||||
size_t output_count = channels_ * out_height_ * out_width_;
|
||||
if (output_len < output_count * sizeof(float)) {
|
||||
throw std::runtime_error("buffer is too small");
|
||||
}
|
||||
float* float_file_data_pointer;
|
||||
int bbox_h_size, bbox_w_size;
|
||||
Ort::ThrowOnError(LoadImageFromFileAndCrop(image_loader_, file_name.c_str(), central_fraction_,
|
||||
&float_file_data_pointer, &bbox_w_size, &bbox_h_size));
|
||||
auto output_data_ = reinterpret_cast<float*>(output_data);
|
||||
ResizeImageInMemory(float_file_data_pointer, output_data_, bbox_h_size, bbox_w_size, out_height_, out_width_,
|
||||
channels_);
|
||||
free(float_file_data_pointer);
|
||||
|
||||
for (size_t i = 0; i != output_count; ++i) {
|
||||
output_data_[i] = (output_data_[i] - 0.5f) * 2.f;
|
||||
}
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include "cached_interpolation.h"
|
||||
#include "sync_api.h"
|
||||
#include "data_processing.h"
|
||||
#include <onnxruntime/core/session/onnxruntime_c_api.h>
|
||||
|
||||
template <typename T>
|
||||
void ResizeImageInMemory(const T* input_data, float* output_data, int in_height, int in_width, int out_height,
|
||||
int out_width, int channels);
|
||||
|
||||
template <typename InputType>
|
||||
class OutputCollector {
|
||||
public:
|
||||
virtual void operator()(const std::vector<InputType>& task_id_list, const Ort::Value& tensor) = 0;
|
||||
// Release the internal cache. It need be called whenever batchsize is changed
|
||||
virtual void ResetCache() = 0;
|
||||
virtual ~OutputCollector() = default;
|
||||
};
|
||||
|
||||
bool CreateImageLoader(void** out);
|
||||
OrtStatus* LoadImageFromFileAndCrop(void* loader, const ORTCHAR_T* filename, double central_crop_fraction, float** out,
|
||||
int* out_width, int* out_height);
|
||||
|
||||
void ReleaseImageLoader(void* p);
|
||||
|
||||
class InceptionPreprocessing : public DataProcessing {
|
||||
private:
|
||||
const int out_height_;
|
||||
const int out_width_;
|
||||
const int channels_;
|
||||
const double central_fraction_ = 0.875;
|
||||
void* image_loader_;
|
||||
|
||||
public:
|
||||
InceptionPreprocessing(int out_height, int out_width, int channels);
|
||||
|
||||
void operator()(_In_ const void* input_data, _Out_writes_bytes_all_(output_len) void* output_data, size_t output_len) const override;
|
||||
|
||||
// output data from this class is in NWHC format
|
||||
std::vector<int64_t> GetOutputShape(size_t batch_size) const override {
|
||||
return {(int64_t)batch_size, out_height_, out_width_, channels_};
|
||||
}
|
||||
};
|
|
@ -1,85 +0,0 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#include "image_loader.h"
|
||||
#include <jpeglib.h>
|
||||
#include "jpeg_mem.h"
|
||||
#include "local_filesystem.h"
|
||||
#include <assert.h>
|
||||
|
||||
bool CreateImageLoader(void** out) {
|
||||
*out = nullptr;
|
||||
return true;
|
||||
}
|
||||
|
||||
void ReleaseImageLoader(void*) {}
|
||||
|
||||
OrtStatus* LoadImageFromFileAndCrop(void*, const ORTCHAR_T* filename, double central_crop_fraction, float** out,
|
||||
int* out_width, int* out_height) {
|
||||
const int channels_ = 3;
|
||||
UncompressFlags flags;
|
||||
flags.components = channels_;
|
||||
// The TensorFlow-chosen default for jpeg decoding is IFAST, sacrificing
|
||||
// image quality for speed.
|
||||
flags.dct_method = JDCT_IFAST;
|
||||
size_t file_len;
|
||||
void* file_data;
|
||||
ReadFileAsString(filename, file_data, file_len);
|
||||
int width;
|
||||
int height;
|
||||
int channels;
|
||||
std::unique_ptr<uint8_t[]> decompressed_image(
|
||||
Uncompress(file_data, static_cast<int>(file_len), flags, &width, &height, &channels, nullptr));
|
||||
free(file_data);
|
||||
|
||||
if (decompressed_image == nullptr) {
|
||||
std::ostringstream oss;
|
||||
oss << "decompress '" << filename << "' failed";
|
||||
return OrtCreateStatus(ORT_FAIL, oss.str().c_str());
|
||||
}
|
||||
|
||||
if (channels != channels_) {
|
||||
std::ostringstream oss;
|
||||
oss << "input format error, expect 3 channels, got " << channels;
|
||||
return OrtCreateStatus(ORT_FAIL, oss.str().c_str());
|
||||
}
|
||||
|
||||
// cast uint8 to float
|
||||
// See: https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/ops/image_ops_impl.py of
|
||||
// tf.image.convert_image_dtype
|
||||
|
||||
// crop it, and cast each pixel value from uint8 to float in range of [0,1]
|
||||
// TODO: should the result be in range of [0,1) or [0,1]?
|
||||
|
||||
int bbox_h_start =
|
||||
static_cast<int>((static_cast<double>(height) - static_cast<double>(height) * central_crop_fraction) / 2);
|
||||
int bbox_w_start =
|
||||
static_cast<int>((static_cast<double>(width) - static_cast<double>(width) * central_crop_fraction) / 2);
|
||||
int bbox_h_size = height - bbox_h_start * 2;
|
||||
int bbox_w_size = width - bbox_w_start * 2;
|
||||
const size_t ele_count = bbox_h_size * bbox_w_size * channels;
|
||||
float* float_file_data = (float*)malloc(ele_count * sizeof(float));
|
||||
if (float_file_data == nullptr) {
|
||||
return OrtCreateStatus(ORT_FAIL, "out of memory");
|
||||
}
|
||||
|
||||
{
|
||||
auto p = decompressed_image.get() + (bbox_h_start * width + bbox_w_start) * channels;
|
||||
|
||||
size_t len = bbox_w_size * channels;
|
||||
float* wptr = float_file_data;
|
||||
for (int i = 0; i != bbox_h_size; ++i) {
|
||||
for (int j = 0; j != len; ++j) {
|
||||
// TODO: should it be divided by 255 or 256?
|
||||
*wptr++ = static_cast<float>(p[j]) / 255;
|
||||
}
|
||||
p += width * channels;
|
||||
}
|
||||
assert(wptr == float_file_data + ele_count);
|
||||
}
|
||||
|
||||
*out = float_file_data;
|
||||
*out_width = bbox_w_size;
|
||||
*out_height = bbox_h_size;
|
||||
return nullptr;
|
||||
}
|
|
@ -1,105 +0,0 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#include "image_loader.h"
|
||||
#include <sstream>
|
||||
#include <wincodec.h>
|
||||
#include <wincodecsdk.h>
|
||||
#include <atlbase.h>
|
||||
|
||||
bool CreateImageLoader(void** out) {
|
||||
IWICImagingFactory* piFactory;
|
||||
auto hr = CoCreateInstance(CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&piFactory));
|
||||
if (!SUCCEEDED(hr)) return false;
|
||||
*out = piFactory;
|
||||
return true;
|
||||
}
|
||||
|
||||
void ReleaseImageLoader(void* p) {
|
||||
auto piFactory = reinterpret_cast<IWICImagingFactory*>(p);
|
||||
piFactory->Release();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void PrintErrorDescription(HRESULT hr, std::basic_ostringstream<T>& oss) {
|
||||
if (FACILITY_WINDOWS == HRESULT_FACILITY(hr)) hr = HRESULT_CODE(hr);
|
||||
TCHAR* szErrMsg;
|
||||
|
||||
if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, hr,
|
||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&szErrMsg, 0, NULL) != 0) {
|
||||
oss << szErrMsg;
|
||||
LocalFree(szErrMsg);
|
||||
} else {
|
||||
oss << TEXT("[Could not find a description for error # ") << hr;
|
||||
}
|
||||
}
|
||||
|
||||
OrtStatus* LoadImageFromFileAndCrop(void* loader, const ORTCHAR_T* filename, double central_crop_fraction, float** out,
|
||||
int* out_width, int* out_height) {
|
||||
auto piFactory = reinterpret_cast<IWICImagingFactory*>(loader);
|
||||
const int channels = 3;
|
||||
try {
|
||||
CComPtr<IWICBitmapDecoder> piDecoder;
|
||||
ATLENSURE_SUCCEEDED(
|
||||
piFactory->CreateDecoderFromFilename(filename, NULL, GENERIC_READ,
|
||||
WICDecodeMetadataCacheOnDemand, // defer parsing non-critical metadata
|
||||
&piDecoder));
|
||||
|
||||
UINT count = 0;
|
||||
ATLENSURE_SUCCEEDED(piDecoder->GetFrameCount(&count));
|
||||
if (count != 1) {
|
||||
return Ort::GetApi().CreateStatus(ORT_FAIL, "The image has multiple frames, I don't know which to choose");
|
||||
}
|
||||
|
||||
CComPtr<IWICBitmapFrameDecode> piFrameDecode;
|
||||
ATLENSURE_SUCCEEDED(piDecoder->GetFrame(0, &piFrameDecode));
|
||||
UINT width, height;
|
||||
ATLENSURE_SUCCEEDED(piFrameDecode->GetSize(&width, &height));
|
||||
CComPtr<IWICFormatConverter> ppIFormatConverter;
|
||||
ATLENSURE_SUCCEEDED(piFactory->CreateFormatConverter(&ppIFormatConverter));
|
||||
ATLENSURE_SUCCEEDED(ppIFormatConverter->Initialize(piFrameDecode, // Source frame to convert
|
||||
GUID_WICPixelFormat24bppRGB, // The desired pixel format
|
||||
WICBitmapDitherTypeNone, // The desired dither pattern
|
||||
NULL, // The desired palette
|
||||
0.f, // The desired alpha threshold
|
||||
WICBitmapPaletteTypeCustom // Palette translation type
|
||||
));
|
||||
int bbox_h_start =
|
||||
static_cast<int>((static_cast<double>(height) - static_cast<double>(height) * central_crop_fraction) / 2);
|
||||
int bbox_w_start =
|
||||
static_cast<int>((static_cast<double>(width) - static_cast<double>(width) * central_crop_fraction) / 2);
|
||||
int bbox_h_size = height - bbox_h_start * 2;
|
||||
int bbox_w_size = width - bbox_w_start * 2;
|
||||
UINT stride = bbox_w_size * channels;
|
||||
UINT result_buffer_size = bbox_h_size * bbox_w_size * channels;
|
||||
// TODO: check result_buffer_size <= UNIT_MAX
|
||||
std::vector<uint8_t> data(result_buffer_size);
|
||||
WICRect rect;
|
||||
memset(&rect, 0, sizeof(WICRect));
|
||||
rect.X = bbox_w_start;
|
||||
rect.Y = bbox_h_start;
|
||||
rect.Height = bbox_h_size;
|
||||
rect.Width = bbox_w_size;
|
||||
|
||||
ATLENSURE_SUCCEEDED(ppIFormatConverter->CopyPixels(&rect, stride, static_cast<UINT>(data.size()), data.data()));
|
||||
float* float_file_data = (float*)malloc(data.size() * sizeof(float));
|
||||
size_t len = data.size();
|
||||
for (size_t i = 0; i != len; ++i) {
|
||||
float_file_data[i] = static_cast<float>(data[i]) / 255;
|
||||
}
|
||||
|
||||
*out = float_file_data;
|
||||
*out_width = bbox_w_size;
|
||||
*out_height = bbox_h_size;
|
||||
return nullptr;
|
||||
} catch (const std::exception& ex) {
|
||||
std::ostringstream oss;
|
||||
oss << "Load " << filename << " failed:" << ex.what();
|
||||
return Ort::GetApi().CreateStatus(ORT_FAIL, oss.str().c_str());
|
||||
} catch (const CAtlException& ex) {
|
||||
std::ostringstream oss;
|
||||
oss << "Load " << filename << " failed:";
|
||||
PrintErrorDescription(ex.m_hr, oss);
|
||||
return Ort::GetApi().CreateStatus(ORT_FAIL, oss.str().c_str());
|
||||
}
|
||||
}
|
|
@ -1,170 +0,0 @@
|
|||
/* Copyright 2015 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
// This file implements a memory destination for libjpeg
|
||||
// The design is very similar to jdatadst.c in libjpeg
|
||||
// These functions are not meant to be used directly, see jpeg_mem.h instead.
|
||||
// We are filling out stubs required by jpeglib, those stubs are private to
|
||||
// the implementation, we are just making available JPGMemSrc, JPGMemDest
|
||||
|
||||
#include "jpeg_handle.h"
|
||||
|
||||
#include <setjmp.h>
|
||||
#include <stddef.h>
|
||||
#include <iostream>
|
||||
|
||||
void CatchError(j_common_ptr cinfo) {
|
||||
(*cinfo->err->output_message)(cinfo);
|
||||
jmp_buf* jpeg_jmpbuf = reinterpret_cast<jmp_buf*>(cinfo->client_data);
|
||||
jpeg_destroy(cinfo);
|
||||
longjmp(*jpeg_jmpbuf, 1);
|
||||
}
|
||||
|
||||
// *****************************************************************************
|
||||
// *****************************************************************************
|
||||
// *****************************************************************************
|
||||
// Destination functions
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
void MemInitDestination(j_compress_ptr cinfo) {
|
||||
MemDestMgr* dest = reinterpret_cast<MemDestMgr*>(cinfo->dest);
|
||||
std::cout << "Initializing buffer=" << dest->bufsize << " bytes";
|
||||
dest->pub.next_output_byte = dest->buffer;
|
||||
dest->pub.free_in_buffer = dest->bufsize;
|
||||
dest->datacount = 0;
|
||||
if (dest->dest) {
|
||||
dest->dest->clear();
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
boolean MemEmptyOutputBuffer(j_compress_ptr cinfo) {
|
||||
MemDestMgr* dest = reinterpret_cast<MemDestMgr*>(cinfo->dest);
|
||||
std::cout << "Writing " << dest->bufsize << " bytes";
|
||||
if (dest->dest) {
|
||||
dest->dest->append(reinterpret_cast<char*>(dest->buffer), dest->bufsize);
|
||||
}
|
||||
dest->pub.next_output_byte = dest->buffer;
|
||||
dest->pub.free_in_buffer = dest->bufsize;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
void MemTermDestination(j_compress_ptr cinfo) {
|
||||
MemDestMgr* dest = reinterpret_cast<MemDestMgr*>(cinfo->dest);
|
||||
std::cout << "Writing " << dest->bufsize - dest->pub.free_in_buffer << " bytes";
|
||||
if (dest->dest) {
|
||||
dest->dest->append(reinterpret_cast<char*>(dest->buffer), dest->bufsize - dest->pub.free_in_buffer);
|
||||
std::cout << "Total size= " << dest->dest->size();
|
||||
}
|
||||
dest->datacount = dest->bufsize - dest->pub.free_in_buffer;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
void SetDest(j_compress_ptr cinfo, void* buffer, int bufsize) { SetDest(cinfo, buffer, bufsize, nullptr); }
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
void SetDest(j_compress_ptr cinfo, void* buffer, int bufsize, std::string* destination) {
|
||||
MemDestMgr* dest;
|
||||
if (cinfo->dest == nullptr) {
|
||||
cinfo->dest = reinterpret_cast<struct jpeg_destination_mgr*>(
|
||||
(*cinfo->mem->alloc_small)(reinterpret_cast<j_common_ptr>(cinfo), JPOOL_PERMANENT, sizeof(MemDestMgr)));
|
||||
}
|
||||
|
||||
dest = reinterpret_cast<MemDestMgr*>(cinfo->dest);
|
||||
dest->bufsize = bufsize;
|
||||
dest->buffer = static_cast<JOCTET*>(buffer);
|
||||
dest->dest = destination;
|
||||
dest->pub.init_destination = MemInitDestination;
|
||||
dest->pub.empty_output_buffer = MemEmptyOutputBuffer;
|
||||
dest->pub.term_destination = MemTermDestination;
|
||||
}
|
||||
|
||||
// *****************************************************************************
|
||||
// *****************************************************************************
|
||||
// *****************************************************************************
|
||||
// Source functions
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
void MemInitSource(j_decompress_ptr cinfo) {
|
||||
MemSourceMgr* src = reinterpret_cast<MemSourceMgr*>(cinfo->src);
|
||||
src->pub.next_input_byte = src->data;
|
||||
src->pub.bytes_in_buffer = src->datasize;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// We emulate the same error-handling as fill_input_buffer() from jdatasrc.c,
|
||||
// for coherency's sake.
|
||||
boolean MemFillInputBuffer(j_decompress_ptr cinfo) {
|
||||
static const JOCTET kEOIBuffer[2] = {0xff, JPEG_EOI};
|
||||
MemSourceMgr* src = reinterpret_cast<MemSourceMgr*>(cinfo->src);
|
||||
if (src->pub.bytes_in_buffer == 0 && src->pub.next_input_byte == src->data) {
|
||||
// empty file -> treated as an error.
|
||||
ERREXIT(cinfo, JERR_INPUT_EMPTY);
|
||||
return FALSE;
|
||||
} else if (src->pub.bytes_in_buffer) {
|
||||
// if there's still some data left, it's probably corrupted
|
||||
return src->try_recover_truncated_jpeg ? TRUE : FALSE;
|
||||
} else if (src->pub.next_input_byte != kEOIBuffer && src->try_recover_truncated_jpeg) {
|
||||
// In an attempt to recover truncated files, we insert a fake EOI
|
||||
WARNMS(cinfo, JWRN_JPEG_EOF);
|
||||
src->pub.next_input_byte = kEOIBuffer;
|
||||
src->pub.bytes_in_buffer = 2;
|
||||
return TRUE;
|
||||
} else {
|
||||
// We already inserted a fake EOI and it wasn't enough, so this time
|
||||
// it's really an error.
|
||||
ERREXIT(cinfo, JERR_FILE_READ);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
void MemTermSource(j_decompress_ptr) {}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
void MemSkipInputData(j_decompress_ptr cinfo, long jump) {
|
||||
MemSourceMgr* src = reinterpret_cast<MemSourceMgr*>(cinfo->src);
|
||||
if (jump < 0) {
|
||||
return;
|
||||
}
|
||||
if (jump > src->pub.bytes_in_buffer) {
|
||||
src->pub.bytes_in_buffer = 0;
|
||||
(void)MemFillInputBuffer(cinfo); // warn with a fake EOI or error
|
||||
} else {
|
||||
src->pub.bytes_in_buffer -= jump;
|
||||
src->pub.next_input_byte += jump;
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
void SetSrc(j_decompress_ptr cinfo, const void* data, unsigned long int datasize, bool try_recover_truncated_jpeg) {
|
||||
MemSourceMgr* src;
|
||||
|
||||
cinfo->src = reinterpret_cast<struct jpeg_source_mgr*>(
|
||||
(*cinfo->mem->alloc_small)(reinterpret_cast<j_common_ptr>(cinfo), JPOOL_PERMANENT, sizeof(MemSourceMgr)));
|
||||
|
||||
src = reinterpret_cast<MemSourceMgr*>(cinfo->src);
|
||||
src->pub.init_source = MemInitSource;
|
||||
src->pub.fill_input_buffer = MemFillInputBuffer;
|
||||
src->pub.skip_input_data = MemSkipInputData;
|
||||
src->pub.resync_to_restart = jpeg_resync_to_restart;
|
||||
src->pub.term_source = MemTermSource;
|
||||
src->data = reinterpret_cast<const unsigned char*>(data);
|
||||
src->datasize = datasize;
|
||||
src->pub.bytes_in_buffer = 0;
|
||||
src->pub.next_input_byte = nullptr;
|
||||
src->try_recover_truncated_jpeg = try_recover_truncated_jpeg;
|
||||
}
|
|
@ -1,56 +0,0 @@
|
|||
/* Copyright 2015 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
// This file declares the functions and structures for memory I/O with libjpeg
|
||||
// These functions are not meant to be used directly, see jpeg_mem.h instead.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string>
|
||||
#include <sys/types.h>
|
||||
extern "C" {
|
||||
#include "jerror.h"
|
||||
#include "jpeglib.h"
|
||||
}
|
||||
|
||||
// Handler for fatal JPEG library errors: clean up & return
|
||||
void CatchError(j_common_ptr cinfo);
|
||||
|
||||
typedef struct {
|
||||
struct jpeg_destination_mgr pub;
|
||||
JOCTET* buffer;
|
||||
int bufsize;
|
||||
int datacount;
|
||||
std::string* dest;
|
||||
} MemDestMgr;
|
||||
|
||||
typedef struct {
|
||||
struct jpeg_source_mgr pub;
|
||||
const unsigned char* data;
|
||||
unsigned long int datasize;
|
||||
bool try_recover_truncated_jpeg;
|
||||
} MemSourceMgr;
|
||||
|
||||
void SetSrc(j_decompress_ptr cinfo, const void* data, unsigned long int datasize, bool try_recover_truncated_jpeg);
|
||||
|
||||
// JPEG destination: we will store all the data in a buffer "buffer" of total
|
||||
// size "bufsize", if the buffer overflows, we will be in trouble.
|
||||
void SetDest(j_compress_ptr cinfo, void* buffer, int bufsize);
|
||||
// Same as above, except that buffer is only used as a temporary structure and
|
||||
// is emptied into "destination" as soon as it fills up.
|
||||
void SetDest(j_compress_ptr cinfo, void* buffer, int bufsize, std::string* destination);
|
|
@ -1,403 +0,0 @@
|
|||
/* Copyright 2015 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
// This file defines functions to compress and uncompress JPEG data
|
||||
// to and from memory, as well as some direct manipulations of JPEG string
|
||||
|
||||
#include "jpeg_mem.h"
|
||||
|
||||
#include <setjmp.h>
|
||||
#include <string.h>
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "jpeg_handle.h"
|
||||
#include <iostream>
|
||||
#include <assert.h>
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Decompression
|
||||
|
||||
namespace {
|
||||
|
||||
enum JPEGErrors { JPEGERRORS_OK, JPEGERRORS_UNEXPECTED_END_OF_DATA, JPEGERRORS_BAD_PARAM };
|
||||
|
||||
// Prevent bad compiler behavior in ASAN mode by wrapping most of the
|
||||
// arguments in a struct struct.
|
||||
class FewerArgsForCompiler {
|
||||
public:
|
||||
FewerArgsForCompiler(int datasize, const UncompressFlags& flags, int64* nwarn,
|
||||
std::function<uint8*(int, int, int)> allocate_output)
|
||||
: datasize_(datasize),
|
||||
flags_(flags),
|
||||
pnwarn_(nwarn),
|
||||
allocate_output_(std::move(allocate_output)),
|
||||
height_read_(0),
|
||||
height_(0),
|
||||
stride_(0) {
|
||||
if (pnwarn_ != nullptr) *pnwarn_ = 0;
|
||||
}
|
||||
|
||||
const int datasize_;
|
||||
const UncompressFlags flags_;
|
||||
int64* const pnwarn_;
|
||||
std::function<uint8*(int, int, int)> allocate_output_;
|
||||
int height_read_; // number of scanline lines successfully read
|
||||
int height_;
|
||||
int stride_;
|
||||
};
|
||||
|
||||
uint8* UncompressLow(const void* srcdata, FewerArgsForCompiler* argball) {
|
||||
// unpack the argball
|
||||
const int datasize = argball->datasize_;
|
||||
const auto& flags = argball->flags_;
|
||||
const int ratio = flags.ratio;
|
||||
int components = flags.components;
|
||||
int stride = flags.stride; // may be 0
|
||||
int64* const nwarn = argball->pnwarn_; // may be NULL
|
||||
|
||||
// Can't decode if the ratio is not recognized by libjpeg
|
||||
if ((ratio != 1) && (ratio != 2) && (ratio != 4) && (ratio != 8)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Channels must be autodetect, grayscale, or rgb.
|
||||
if (!(components == 0 || components == 1 || components == 3)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// if empty image, return
|
||||
if (datasize == 0 || srcdata == nullptr) return nullptr;
|
||||
|
||||
// Declare temporary buffer pointer here so that we can free on error paths
|
||||
JSAMPLE* tempdata = nullptr;
|
||||
|
||||
// Initialize libjpeg structures to have a memory source
|
||||
// Modify the usual jpeg error manager to catch fatal errors.
|
||||
JPEGErrors error = JPEGERRORS_OK;
|
||||
struct jpeg_decompress_struct cinfo;
|
||||
struct jpeg_error_mgr jerr;
|
||||
cinfo.err = jpeg_std_error(&jerr);
|
||||
jerr.error_exit = CatchError;
|
||||
|
||||
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
|
||||
jerr.output_message = no_print;
|
||||
#endif
|
||||
|
||||
jmp_buf jpeg_jmpbuf;
|
||||
cinfo.client_data = &jpeg_jmpbuf;
|
||||
if (setjmp(jpeg_jmpbuf)) {
|
||||
delete[] tempdata;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
jpeg_create_decompress(&cinfo);
|
||||
SetSrc(&cinfo, srcdata, datasize, flags.try_recover_truncated_jpeg);
|
||||
jpeg_read_header(&cinfo, TRUE);
|
||||
|
||||
// Set components automatically if desired, autoconverting cmyk to rgb.
|
||||
if (components == 0) components = std::min(cinfo.num_components, 3);
|
||||
|
||||
// set grayscale and ratio parameters
|
||||
switch (components) {
|
||||
case 1:
|
||||
cinfo.out_color_space = JCS_GRAYSCALE;
|
||||
break;
|
||||
case 3:
|
||||
if (cinfo.jpeg_color_space == JCS_CMYK || cinfo.jpeg_color_space == JCS_YCCK) {
|
||||
// Always use cmyk for output in a 4 channel jpeg. libjpeg has a builtin
|
||||
// decoder. We will further convert to rgb below.
|
||||
cinfo.out_color_space = JCS_CMYK;
|
||||
} else {
|
||||
cinfo.out_color_space = JCS_RGB;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
std::cout << " Invalid components value " << components << std::endl;
|
||||
jpeg_destroy_decompress(&cinfo);
|
||||
return nullptr;
|
||||
}
|
||||
cinfo.do_fancy_upsampling = boolean(flags.fancy_upscaling);
|
||||
cinfo.scale_num = 1;
|
||||
cinfo.scale_denom = ratio;
|
||||
cinfo.dct_method = flags.dct_method;
|
||||
|
||||
// Determine the output image size before attempting decompress to prevent
|
||||
// OOM'ing doing the decompress
|
||||
jpeg_calc_output_dimensions(&cinfo);
|
||||
|
||||
int64 total_size = static_cast<int64>(cinfo.output_height) * static_cast<int64>(cinfo.output_width) *
|
||||
static_cast<int64>(cinfo.num_components);
|
||||
// Some of the internal routines do not gracefully handle ridiculously
|
||||
// large images, so fail fast.
|
||||
if (cinfo.output_width <= 0 || cinfo.output_height <= 0) {
|
||||
std::cout << "Invalid image size: " << cinfo.output_width << " x " << cinfo.output_height;
|
||||
jpeg_destroy_decompress(&cinfo);
|
||||
return nullptr;
|
||||
}
|
||||
if (total_size >= (1LL << 29)) {
|
||||
std::cout << "Image too large: " << total_size;
|
||||
jpeg_destroy_decompress(&cinfo);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
jpeg_start_decompress(&cinfo);
|
||||
|
||||
JDIMENSION target_output_width = cinfo.output_width;
|
||||
JDIMENSION target_output_height = cinfo.output_height;
|
||||
JDIMENSION skipped_scanlines = 0;
|
||||
|
||||
// check for compatible stride
|
||||
const int min_stride = target_output_width * components * sizeof(JSAMPLE);
|
||||
if (stride == 0) {
|
||||
stride = min_stride;
|
||||
} else if (stride < min_stride) {
|
||||
std::cout << "Incompatible stride: " << stride << " < " << min_stride;
|
||||
jpeg_destroy_decompress(&cinfo);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Remember stride and height for use in Uncompress
|
||||
argball->height_ = target_output_height;
|
||||
argball->stride_ = stride;
|
||||
|
||||
uint8* dstdata = argball->allocate_output_(target_output_width, target_output_height, components);
|
||||
|
||||
if (dstdata == nullptr) {
|
||||
jpeg_destroy_decompress(&cinfo);
|
||||
return nullptr;
|
||||
}
|
||||
JSAMPLE* output_line = static_cast<JSAMPLE*>(dstdata);
|
||||
|
||||
// jpeg_read_scanlines requires the buffers to be allocated based on
|
||||
// cinfo.output_width, but the target image width might be different if crop
|
||||
// is enabled and crop_width is not MCU aligned. In this case, we need to
|
||||
// realign the scanline output to achieve the exact cropping. Notably, only
|
||||
// cinfo.output_width needs to fall on MCU boundary, while cinfo.output_height
|
||||
// has no such constraint.
|
||||
const bool need_realign_cropped_scanline = (target_output_width != cinfo.output_width);
|
||||
const bool use_cmyk = (cinfo.out_color_space == JCS_CMYK);
|
||||
|
||||
if (use_cmyk) {
|
||||
// Temporary buffer used for CMYK -> RGB conversion.
|
||||
tempdata = new JSAMPLE[cinfo.output_width * 4];
|
||||
} else if (need_realign_cropped_scanline) {
|
||||
// Temporary buffer used for MCU-aligned scanline data.
|
||||
tempdata = new JSAMPLE[cinfo.output_width * components];
|
||||
}
|
||||
|
||||
// If there is an error reading a line, this aborts the reading.
|
||||
// Save the fraction of the image that has been read.
|
||||
argball->height_read_ = target_output_height;
|
||||
|
||||
// These variables are just to avoid repeated computation in the loop.
|
||||
const int max_scanlines_to_read = skipped_scanlines + target_output_height;
|
||||
const int mcu_align_offset = (cinfo.output_width - target_output_width) * (use_cmyk ? 4 : components);
|
||||
while (cinfo.output_scanline < max_scanlines_to_read) {
|
||||
int num_lines_read = 0;
|
||||
if (use_cmyk) {
|
||||
num_lines_read = jpeg_read_scanlines(&cinfo, &tempdata, 1);
|
||||
if (num_lines_read > 0) {
|
||||
// Convert CMYK to RGB if scanline read succeeded.
|
||||
for (size_t i = 0; i < target_output_width; ++i) {
|
||||
int offset = 4 * i;
|
||||
if (need_realign_cropped_scanline) {
|
||||
// Align the offset for MCU boundary.
|
||||
offset += mcu_align_offset;
|
||||
}
|
||||
const int c = tempdata[offset + 0];
|
||||
const int m = tempdata[offset + 1];
|
||||
const int y = tempdata[offset + 2];
|
||||
const int k = tempdata[offset + 3];
|
||||
int r, g, b;
|
||||
if (cinfo.saw_Adobe_marker) {
|
||||
r = (k * c) / 255;
|
||||
g = (k * m) / 255;
|
||||
b = (k * y) / 255;
|
||||
} else {
|
||||
r = (255 - k) * (255 - c) / 255;
|
||||
g = (255 - k) * (255 - m) / 255;
|
||||
b = (255 - k) * (255 - y) / 255;
|
||||
}
|
||||
output_line[3 * i + 0] = r;
|
||||
output_line[3 * i + 1] = g;
|
||||
output_line[3 * i + 2] = b;
|
||||
}
|
||||
}
|
||||
} else if (need_realign_cropped_scanline) {
|
||||
num_lines_read = jpeg_read_scanlines(&cinfo, &tempdata, 1);
|
||||
if (num_lines_read > 0) {
|
||||
memcpy(output_line, tempdata + mcu_align_offset, min_stride);
|
||||
}
|
||||
} else {
|
||||
num_lines_read = jpeg_read_scanlines(&cinfo, &output_line, 1);
|
||||
}
|
||||
// Handle error cases
|
||||
if (num_lines_read == 0) {
|
||||
std::cout << "Premature end of JPEG data. Stopped at line " << cinfo.output_scanline - skipped_scanlines << "/"
|
||||
<< target_output_height;
|
||||
if (!flags.try_recover_truncated_jpeg) {
|
||||
argball->height_read_ = cinfo.output_scanline - skipped_scanlines;
|
||||
error = JPEGERRORS_UNEXPECTED_END_OF_DATA;
|
||||
} else {
|
||||
for (size_t line = cinfo.output_scanline; line < max_scanlines_to_read; ++line) {
|
||||
if (line == 0) {
|
||||
// If even the first line is missing, fill with black color
|
||||
memset(output_line, 0, min_stride);
|
||||
} else {
|
||||
// else, just replicate the line above.
|
||||
memcpy(output_line, output_line - stride, min_stride);
|
||||
}
|
||||
output_line += stride;
|
||||
}
|
||||
argball->height_read_ = target_output_height; // consider all lines as read
|
||||
// prevent error-on-exit in libjpeg:
|
||||
cinfo.output_scanline = max_scanlines_to_read;
|
||||
}
|
||||
break;
|
||||
}
|
||||
assert(num_lines_read == 1);
|
||||
output_line += stride;
|
||||
}
|
||||
delete[] tempdata;
|
||||
tempdata = nullptr;
|
||||
|
||||
|
||||
|
||||
// Convert the RGB data to RGBA, with alpha set to 0xFF to indicate
|
||||
// opacity.
|
||||
// RGBRGBRGB... --> RGBARGBARGBA...
|
||||
if (components == 4) {
|
||||
// Start on the last line.
|
||||
JSAMPLE* scanlineptr = static_cast<JSAMPLE*>(dstdata + static_cast<int64>(target_output_height - 1) * stride);
|
||||
const JSAMPLE kOpaque = -1; // All ones appropriate for JSAMPLE.
|
||||
const int right_rgb = (target_output_width - 1) * 3;
|
||||
const int right_rgba = (target_output_width - 1) * 4;
|
||||
|
||||
for (int y = target_output_height; y-- > 0;) {
|
||||
// We do all the transformations in place, going backwards for each row.
|
||||
const JSAMPLE* rgb_pixel = scanlineptr + right_rgb;
|
||||
JSAMPLE* rgba_pixel = scanlineptr + right_rgba;
|
||||
scanlineptr -= stride;
|
||||
for (int x = target_output_width; x-- > 0; rgba_pixel -= 4, rgb_pixel -= 3) {
|
||||
// We copy the 3 bytes at rgb_pixel into the 4 bytes at rgba_pixel
|
||||
// The "a" channel is set to be opaque.
|
||||
rgba_pixel[3] = kOpaque;
|
||||
rgba_pixel[2] = rgb_pixel[2];
|
||||
rgba_pixel[1] = rgb_pixel[1];
|
||||
rgba_pixel[0] = rgb_pixel[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch (components) {
|
||||
case 1:
|
||||
if (cinfo.output_components != 1) {
|
||||
error = JPEGERRORS_BAD_PARAM;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
case 4:
|
||||
if (cinfo.out_color_space == JCS_CMYK) {
|
||||
if (cinfo.output_components != 4) {
|
||||
error = JPEGERRORS_BAD_PARAM;
|
||||
}
|
||||
} else {
|
||||
if (cinfo.output_components != 3) {
|
||||
error = JPEGERRORS_BAD_PARAM;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// will never happen, should be caught by the previous switch
|
||||
std::cout << "Invalid components value " << components << std::endl;
|
||||
jpeg_destroy_decompress(&cinfo);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// save number of warnings if requested
|
||||
if (nwarn != nullptr) {
|
||||
*nwarn = cinfo.err->num_warnings;
|
||||
}
|
||||
|
||||
// Handle errors in JPEG
|
||||
switch (error) {
|
||||
case JPEGERRORS_OK:
|
||||
jpeg_finish_decompress(&cinfo);
|
||||
break;
|
||||
case JPEGERRORS_UNEXPECTED_END_OF_DATA:
|
||||
case JPEGERRORS_BAD_PARAM:
|
||||
jpeg_abort(reinterpret_cast<j_common_ptr>(&cinfo));
|
||||
break;
|
||||
default:
|
||||
std::cout << "Unhandled case " << error;
|
||||
break;
|
||||
}
|
||||
|
||||
jpeg_destroy_decompress(&cinfo);
|
||||
return dstdata;
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// We do the apparently silly thing of packing 5 of the arguments
|
||||
// into a structure that is then passed to another routine
|
||||
// that does all the work. The reason is that we want to catch
|
||||
// fatal JPEG library errors with setjmp/longjmp, and g++ and
|
||||
// associated libraries aren't good enough to guarantee that 7
|
||||
// parameters won't get clobbered by the longjmp. So we help
|
||||
// it out a little.
|
||||
uint8* Uncompress(const void* srcdata, int datasize, const UncompressFlags& flags, int64* nwarn,
|
||||
std::function<uint8*(int, int, int)> allocate_output) {
|
||||
FewerArgsForCompiler argball(datasize, flags, nwarn, std::move(allocate_output));
|
||||
uint8* const dstdata = UncompressLow(srcdata, &argball);
|
||||
|
||||
const float fraction_read =
|
||||
argball.height_ == 0 ? 1.0f : (static_cast<float>(argball.height_read_) / argball.height_);
|
||||
if (dstdata == nullptr || fraction_read < std::min(1.0f, flags.min_acceptable_fraction)) {
|
||||
// Major failure, none or too-partial read returned; get out
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// If there was an error in reading the jpeg data,
|
||||
// set the unread pixels to black
|
||||
if (argball.height_read_ != argball.height_) {
|
||||
const int first_bad_line = argball.height_read_;
|
||||
uint8* start = dstdata + first_bad_line * argball.stride_;
|
||||
const int nbytes = (argball.height_ - first_bad_line) * argball.stride_;
|
||||
memset(static_cast<void*>(start), 0, nbytes);
|
||||
}
|
||||
|
||||
return dstdata;
|
||||
}
|
||||
|
||||
uint8* Uncompress(const void* srcdata, int datasize, const UncompressFlags& flags, int* pwidth, int* pheight,
|
||||
int* pcomponents, int64* nwarn) {
|
||||
uint8* buffer = nullptr;
|
||||
uint8* result = Uncompress(srcdata, datasize, flags, nwarn, [=, &buffer](int width, int height, int components) {
|
||||
if (pwidth != nullptr) *pwidth = width;
|
||||
if (pheight != nullptr) *pheight = height;
|
||||
if (pcomponents != nullptr) *pcomponents = components;
|
||||
buffer = new uint8[height * width * components];
|
||||
return buffer;
|
||||
});
|
||||
if (!result) delete[] buffer;
|
||||
return result;
|
||||
}
|
|
@ -1,93 +0,0 @@
|
|||
/* Copyright 2015 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
// This file defines functions to compress and uncompress JPEG files
|
||||
// to and from memory. It provides interfaces for raw images
|
||||
// (data array and size fields).
|
||||
// Direct manipulation of JPEG strings are supplied: Flip, Rotate, Crop..
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string>
|
||||
#include <sys/types.h>
|
||||
extern "C" {
|
||||
#include "jerror.h"
|
||||
#include "jpeglib.h"
|
||||
}
|
||||
|
||||
using uint8 = std::uint8_t;
|
||||
using int64 = std::int64_t;
|
||||
|
||||
// Flags for Uncompress
|
||||
struct UncompressFlags {
|
||||
// ratio can be 1, 2, 4, or 8 and represent the denominator for the scaling
|
||||
// factor (eg ratio = 4 means that the resulting image will be at 1/4 original
|
||||
// size in both directions).
|
||||
int ratio = 1;
|
||||
|
||||
// The number of bytes per pixel (1, 3 or 4), or 0 for autodetect.
|
||||
int components = 0;
|
||||
|
||||
// If true, decoder will use a slower but nicer upscaling of the chroma
|
||||
// planes (yuv420/422 only).
|
||||
bool fancy_upscaling = true;
|
||||
|
||||
// If true, will attempt to fill in missing lines of truncated files
|
||||
bool try_recover_truncated_jpeg = false;
|
||||
|
||||
// The minimum required fraction of lines read before the image is accepted.
|
||||
float min_acceptable_fraction = 1.0;
|
||||
|
||||
// The distance in bytes from one scanline to the other. Should be at least
|
||||
// equal to width*components*sizeof(JSAMPLE). If 0 is passed, the stride
|
||||
// used will be this minimal value.
|
||||
int stride = 0;
|
||||
|
||||
// Setting of J_DCT_METHOD enum in jpeglib.h, for choosing which
|
||||
// algorithm to use for DCT/IDCT.
|
||||
//
|
||||
// Setting this has a quality/speed trade-off implication.
|
||||
J_DCT_METHOD dct_method = JDCT_DEFAULT;
|
||||
};
|
||||
|
||||
// Uncompress some raw JPEG data given by the pointer srcdata and the length
|
||||
// datasize.
|
||||
// - width and height are the address where to store the size of the
|
||||
// uncompressed image in pixels. May be nullptr.
|
||||
// - components is the address where the number of read components are
|
||||
// stored. This is *output only*: to request a specific number of
|
||||
// components use flags.components. May be nullptr.
|
||||
// - nwarn is the address in which to store the number of warnings.
|
||||
// May be nullptr.
|
||||
// The function returns a pointer to the raw uncompressed data or NULL if
|
||||
// there was an error. The caller of the function is responsible for
|
||||
// freeing the memory (using delete []).
|
||||
uint8* Uncompress(const void* srcdata, int datasize, const UncompressFlags& flags, int* width, int* height,
|
||||
int* components, // Output only: useful with autodetect
|
||||
int64* nwarn);
|
||||
|
||||
// Version of Uncompress that allocates memory via a callback. The callback
|
||||
// arguments are (width, height, components). If the size is known ahead of
|
||||
// time this function can return an existing buffer; passing a callback allows
|
||||
// the buffer to be shaped based on the JPEG header. The caller is responsible
|
||||
// for freeing the memory *even along error paths*.
|
||||
uint8* Uncompress(const void* srcdata, int datasize, const UncompressFlags& flags, int64* nwarn,
|
||||
std::function<uint8*(int, int, int)> allocate_output);
|
|
@ -1,142 +0,0 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <stdint.h>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#ifdef _WIN32
|
||||
#include <Windows.h>
|
||||
#else
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <dirent.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/mman.h>
|
||||
#endif
|
||||
#include <onnxruntime/core/session/onnxruntime_c_api.h>
|
||||
void ReadFileAsString(const ORTCHAR_T* fname, void*& p, size_t& len);
|
||||
|
||||
enum class OrtFileType { TYPE_BLK, TYPE_CHR, TYPE_DIR, TYPE_FIFO, TYPE_LNK, TYPE_REG, TYPE_SOCK, TYPE_UNKNOWN };
|
||||
using TCharString = std::basic_string<ORTCHAR_T>;
|
||||
|
||||
#ifdef _WIN32
|
||||
inline OrtFileType DTToFileType(DWORD dwFileAttributes) {
|
||||
if (dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
||||
return OrtFileType::TYPE_DIR;
|
||||
}
|
||||
// TODO: test if it is reg
|
||||
return OrtFileType::TYPE_REG;
|
||||
}
|
||||
|
||||
inline std::string FormatErrorCode(DWORD dw) {
|
||||
char* lpMsgBuf;
|
||||
FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dw,
|
||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&lpMsgBuf, 0, NULL);
|
||||
std::string s(lpMsgBuf);
|
||||
LocalFree(lpMsgBuf);
|
||||
return s;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void LoopDir(const std::wstring& dir_name, T func) {
|
||||
std::wstring pattern = dir_name + L"\\*";
|
||||
WIN32_FIND_DATAW ffd;
|
||||
std::unique_ptr<void, decltype(&FindClose)> hFind(FindFirstFileW(pattern.c_str(), &ffd), FindClose);
|
||||
if (hFind.get() == INVALID_HANDLE_VALUE) {
|
||||
DWORD dw = GetLastError();
|
||||
std::string s = FormatErrorCode(dw);
|
||||
throw std::runtime_error(s);
|
||||
}
|
||||
do {
|
||||
if (!func(ffd.cFileName, DTToFileType(ffd.dwFileAttributes))) return;
|
||||
} while (FindNextFileW(hFind.get(), &ffd) != 0);
|
||||
DWORD dwError = GetLastError();
|
||||
if (dwError != ERROR_NO_MORE_FILES) {
|
||||
DWORD dw = GetLastError();
|
||||
std::string s = FormatErrorCode(dw);
|
||||
throw std::runtime_error(s);
|
||||
}
|
||||
}
|
||||
#else
|
||||
|
||||
inline void ReportSystemError(const char* operation_name, const TCharString& path) {
|
||||
auto e = errno;
|
||||
char buf[1024];
|
||||
const char* msg = "";
|
||||
if (e > 0) {
|
||||
#if defined(__GLIBC__) && defined(_GNU_SOURCE) && !defined(__ANDROID__)
|
||||
msg = strerror_r(e, buf, sizeof(buf));
|
||||
#else
|
||||
// for Mac OS X and Android lower than API 23
|
||||
if (strerror_r(e, buf, sizeof(buf)) != 0) {
|
||||
buf[0] = '\0';
|
||||
}
|
||||
msg = buf;
|
||||
#endif
|
||||
}
|
||||
std::ostringstream oss;
|
||||
oss << operation_name << " file \"" << path << "\" failed: " << msg;
|
||||
throw std::runtime_error(oss.str());
|
||||
}
|
||||
|
||||
inline OrtFileType DTToFileType(unsigned char t) {
|
||||
switch (t) {
|
||||
case DT_BLK:
|
||||
return OrtFileType::TYPE_BLK;
|
||||
case DT_CHR:
|
||||
return OrtFileType::TYPE_CHR;
|
||||
case DT_DIR:
|
||||
return OrtFileType::TYPE_DIR;
|
||||
case DT_FIFO:
|
||||
return OrtFileType::TYPE_FIFO;
|
||||
case DT_LNK:
|
||||
return OrtFileType::TYPE_LNK;
|
||||
case DT_REG:
|
||||
return OrtFileType::TYPE_REG;
|
||||
case DT_SOCK:
|
||||
return OrtFileType::TYPE_SOCK;
|
||||
default:
|
||||
return OrtFileType::TYPE_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void LoopDir(const TCharString& dir_name, T func) {
|
||||
DIR* dir = opendir(dir_name.c_str());
|
||||
if (dir == nullptr) {
|
||||
auto e = errno;
|
||||
char buf[1024];
|
||||
char* msg;
|
||||
#if defined(__GLIBC__) && defined(_GNU_SOURCE) && !defined(__ANDROID__)
|
||||
msg = strerror_r(e, buf, sizeof(buf));
|
||||
#else
|
||||
if (strerror_r(e, buf, sizeof(buf)) != 0) {
|
||||
buf[0] = '\0';
|
||||
}
|
||||
msg = buf;
|
||||
#endif
|
||||
std::ostringstream oss;
|
||||
oss << "couldn't open '" << dir_name << "':" << msg;
|
||||
std::string s = oss.str();
|
||||
throw std::runtime_error(s);
|
||||
}
|
||||
try {
|
||||
struct dirent* dp;
|
||||
while ((dp = readdir(dir)) != nullptr) {
|
||||
if (!func(dp->d_name, DTToFileType(dp->d_type))) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (std::exception& ex) {
|
||||
closedir(dir);
|
||||
throw;
|
||||
}
|
||||
closedir(dir);
|
||||
}
|
||||
#endif
|
|
@ -1,50 +0,0 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#include "local_filesystem.h"
|
||||
#include <assert.h>
|
||||
#include <mutex>
|
||||
|
||||
static std::mutex m;
|
||||
|
||||
void ReadFileAsString(const ORTCHAR_T* fname, void*& p, size_t& len) {
|
||||
std::lock_guard<std::mutex> g(m);
|
||||
if (!fname) {
|
||||
throw std::runtime_error("ReadFileAsString: 'fname' cannot be NULL");
|
||||
}
|
||||
int fd = open(fname, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
return ReportSystemError("open", fname);
|
||||
}
|
||||
struct stat stbuf;
|
||||
if (fstat(fd, &stbuf) != 0) {
|
||||
return ReportSystemError("fstat", fname);
|
||||
}
|
||||
|
||||
if (!S_ISREG(stbuf.st_mode)) {
|
||||
throw std::runtime_error("ReadFileAsString: input is not a regular file");
|
||||
}
|
||||
// TODO:check overflow
|
||||
len = static_cast<size_t>(stbuf.st_size);
|
||||
|
||||
if (len == 0) {
|
||||
p = nullptr;
|
||||
} else {
|
||||
char* buffer = reinterpret_cast<char*>(malloc(len));
|
||||
char* wptr = reinterpret_cast<char*>(buffer);
|
||||
auto length_remain = len;
|
||||
do {
|
||||
size_t bytes_to_read = length_remain;
|
||||
ssize_t bytes_read;
|
||||
TEMP_FAILURE_RETRY(bytes_read = read(fd, wptr, bytes_to_read));
|
||||
if (bytes_read <= 0) {
|
||||
return ReportSystemError("read", fname);
|
||||
}
|
||||
assert(static_cast<size_t>(bytes_read) <= bytes_to_read);
|
||||
wptr += bytes_read;
|
||||
length_remain -= bytes_read;
|
||||
} while (length_remain > 0);
|
||||
p = buffer;
|
||||
}
|
||||
close(fd);
|
||||
}
|
|
@ -1,70 +0,0 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#include "local_filesystem.h"
|
||||
#include <assert.h>
|
||||
#include <mutex>
|
||||
|
||||
static std::mutex m;
|
||||
|
||||
void ReadFileAsString(const ORTCHAR_T* fname, void*& p, size_t& len) {
|
||||
if (!fname) {
|
||||
throw std::runtime_error("ReadFileAsString: 'fname' cannot be NULL");
|
||||
}
|
||||
|
||||
HANDLE hFile = CreateFileW(fname, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (hFile == INVALID_HANDLE_VALUE) {
|
||||
int err = GetLastError();
|
||||
std::ostringstream oss;
|
||||
oss << "open file " << fname << " fail, errcode =" << err;
|
||||
throw std::runtime_error(oss.str().c_str());
|
||||
}
|
||||
std::unique_ptr<void, decltype(&CloseHandle)> handler_holder(hFile, CloseHandle);
|
||||
LARGE_INTEGER filesize;
|
||||
if (!GetFileSizeEx(hFile, &filesize)) {
|
||||
int err = GetLastError();
|
||||
std::ostringstream oss;
|
||||
oss << "GetFileSizeEx file " << fname << " fail, errcode =" << err;
|
||||
throw std::runtime_error(oss.str().c_str());
|
||||
}
|
||||
if (static_cast<ULONGLONG>(filesize.QuadPart) > std::numeric_limits<size_t>::max()) {
|
||||
throw std::runtime_error("ReadFileAsString: File is too large");
|
||||
}
|
||||
len = static_cast<size_t>(filesize.QuadPart);
|
||||
// check the file file for avoiding allocating a zero length buffer
|
||||
if (len == 0) { // empty file
|
||||
p = nullptr;
|
||||
len = 0;
|
||||
return;
|
||||
}
|
||||
std::unique_ptr<char[]> buffer(reinterpret_cast<char*>(malloc(len)));
|
||||
char* wptr = reinterpret_cast<char*>(buffer.get());
|
||||
size_t length_remain = len;
|
||||
DWORD bytes_read = 0;
|
||||
for (; length_remain > 0; wptr += bytes_read, length_remain -= bytes_read) {
|
||||
// read at most 1GB each time
|
||||
DWORD bytes_to_read;
|
||||
if (length_remain > (1 << 30)) {
|
||||
bytes_to_read = 1 << 30;
|
||||
} else {
|
||||
bytes_to_read = static_cast<DWORD>(length_remain);
|
||||
}
|
||||
if (ReadFile(hFile, wptr, bytes_to_read, &bytes_read, nullptr) != TRUE) {
|
||||
int err = GetLastError();
|
||||
p = nullptr;
|
||||
len = 0;
|
||||
std::ostringstream oss;
|
||||
oss << "ReadFile " << fname << " fail, errcode =" << err;
|
||||
throw std::runtime_error(oss.str().c_str());
|
||||
}
|
||||
if (bytes_read != bytes_to_read) {
|
||||
p = nullptr;
|
||||
len = 0;
|
||||
std::ostringstream oss;
|
||||
oss << "ReadFile " << fname << " fail: unexpected end";
|
||||
throw std::runtime_error(oss.str().c_str());
|
||||
}
|
||||
}
|
||||
p = buffer.release();
|
||||
return;
|
||||
}
|
|
@ -1,249 +0,0 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#include <string>
|
||||
#include <string.h>
|
||||
#include <sstream>
|
||||
#include <stdint.h>
|
||||
#include <assert.h>
|
||||
#include <stdexcept>
|
||||
#include <setjmp.h>
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <atomic>
|
||||
|
||||
#include "providers.h"
|
||||
#include "local_filesystem.h"
|
||||
#include "sync_api.h"
|
||||
|
||||
#include <onnxruntime/core/session/onnxruntime_cxx_api.h>
|
||||
|
||||
#include "image_loader.h"
|
||||
#include "async_ring_buffer.h"
|
||||
#include <fstream>
|
||||
#include <condition_variable>
|
||||
#ifdef _WIN32
|
||||
#include <atlbase.h>
|
||||
#endif
|
||||
using namespace std::chrono;
|
||||
|
||||
class Validator : public OutputCollector<TCharString> {
|
||||
private:
|
||||
static std::vector<std::string> ReadFileToVec(const TCharString& file_path, size_t expected_line_count) {
|
||||
std::ifstream ifs(file_path);
|
||||
if (!ifs) {
|
||||
throw std::runtime_error("open file failed");
|
||||
}
|
||||
std::string line;
|
||||
std::vector<std::string> labels;
|
||||
while (std::getline(ifs, line)) {
|
||||
if (!line.empty()) labels.push_back(line);
|
||||
}
|
||||
if (labels.size() != expected_line_count) {
|
||||
std::ostringstream oss;
|
||||
oss << "line count mismatch, expect " << expected_line_count << " from " << file_path.c_str() << ", got "
|
||||
<< labels.size();
|
||||
throw std::runtime_error(oss.str());
|
||||
}
|
||||
return labels;
|
||||
}
|
||||
|
||||
// input file name has pattern like:
|
||||
//"C:\tools\imagnet_validation_data\ILSVRC2012_val_00000001.JPEG"
|
||||
//"C:\tools\imagnet_validation_data\ILSVRC2012_val_00000002.JPEG"
|
||||
static int ExtractImageNumberFromFileName(const TCharString& image_file) {
|
||||
size_t s = image_file.rfind('.');
|
||||
if (s == std::string::npos) throw std::runtime_error("illegal filename");
|
||||
size_t s2 = image_file.rfind('_');
|
||||
if (s2 == std::string::npos) throw std::runtime_error("illegal filename");
|
||||
|
||||
const ORTCHAR_T* start_ptr = image_file.c_str() + s2 + 1;
|
||||
const ORTCHAR_T* endptr = nullptr;
|
||||
long value = my_strtol(start_ptr, (ORTCHAR_T**)&endptr, 10);
|
||||
if (start_ptr == endptr || value > INT32_MAX || value <= 0) throw std::runtime_error("illegal filename");
|
||||
return static_cast<int>(value);
|
||||
}
|
||||
|
||||
static void VerifyInputOutputCount(Ort::Session& session) {
|
||||
size_t count = session.GetInputCount();
|
||||
assert(count == 1);
|
||||
count = session.GetOutputCount();
|
||||
assert(count == 1);
|
||||
}
|
||||
|
||||
Ort::Session session_{nullptr};
|
||||
const int output_class_count_ = 1001;
|
||||
std::vector<std::string> labels_;
|
||||
std::vector<std::string> validation_data_;
|
||||
std::atomic<int> top_1_correct_count_;
|
||||
std::atomic<int> finished_count_;
|
||||
int image_size_;
|
||||
|
||||
std::mutex m_;
|
||||
char* input_name_ = nullptr;
|
||||
char* output_name_ = nullptr;
|
||||
Ort::Env& env_;
|
||||
const TCharString model_path_;
|
||||
system_clock::time_point start_time_;
|
||||
|
||||
public:
|
||||
int GetImageSize() const { return image_size_; }
|
||||
|
||||
~Validator() {
|
||||
free(input_name_);
|
||||
free(output_name_);
|
||||
}
|
||||
|
||||
void PrintResult() {
|
||||
if (finished_count_ == 0) return;
|
||||
printf("Top-1 Accuracy %f\n", ((float)top_1_correct_count_.load() / finished_count_));
|
||||
}
|
||||
|
||||
void ResetCache() override {
|
||||
CreateSession();
|
||||
}
|
||||
|
||||
void CreateSession() {
|
||||
Ort::SessionOptions session_options;
|
||||
#ifdef USE_CUDA
|
||||
Ort::ThrowOnError(OrtSessionOptionsAppendExecutionProvider_CUDA(session_options, 0));
|
||||
#endif
|
||||
session_ = Ort::Session(env_, model_path_.c_str(), session_options);
|
||||
}
|
||||
|
||||
Validator(Ort::Env& env, const TCharString& model_path, const TCharString& label_file_path,
|
||||
const TCharString& validation_file_path, size_t input_image_count)
|
||||
: labels_(ReadFileToVec(label_file_path, 1000)),
|
||||
validation_data_(ReadFileToVec(validation_file_path, input_image_count)),
|
||||
top_1_correct_count_(0),
|
||||
finished_count_(0),
|
||||
env_(env),
|
||||
model_path_(model_path) {
|
||||
CreateSession();
|
||||
VerifyInputOutputCount(session_);
|
||||
Ort::AllocatorWithDefaultOptions ort_alloc;
|
||||
{
|
||||
char* t = session_.GetInputName(0, ort_alloc);
|
||||
input_name_ = my_strdup(t);
|
||||
ort_alloc.Free(t);
|
||||
t = session_.GetOutputName(0, ort_alloc);
|
||||
output_name_ = my_strdup(t);
|
||||
ort_alloc.Free(t);
|
||||
}
|
||||
|
||||
Ort::TypeInfo info = session_.GetInputTypeInfo(0);
|
||||
auto tensor_info = info.GetTensorTypeAndShapeInfo();
|
||||
size_t dim_count = tensor_info.GetDimensionsCount();
|
||||
assert(dim_count == 4);
|
||||
std::vector<int64_t> dims(dim_count);
|
||||
tensor_info.GetDimensions(dims.data(), dims.size());
|
||||
if (dims[1] != dims[2] || dims[3] != 3) {
|
||||
throw std::runtime_error("This model is not supported by this program. input tensor need be in NHWC format");
|
||||
}
|
||||
|
||||
image_size_ = static_cast<int>(dims[1]);
|
||||
start_time_ = system_clock::now();
|
||||
}
|
||||
|
||||
void operator()(const std::vector<TCharString>& task_id_list, const Ort::Value& input_tensor) override {
|
||||
{
|
||||
std::lock_guard<std::mutex> l(m_);
|
||||
const size_t remain = task_id_list.size();
|
||||
Ort::Value output_tensor{nullptr};
|
||||
session_.Run(Ort::RunOptions{nullptr}, &input_name_, &input_tensor, 1, &output_name_, &output_tensor, 1);
|
||||
float* probs = output_tensor.GetTensorMutableData<float>();
|
||||
for (const auto& s : task_id_list) {
|
||||
float* end = probs + output_class_count_;
|
||||
float* max_p = std::max_element(probs + 1, end);
|
||||
auto max_prob_index = std::distance(probs, max_p);
|
||||
assert(max_prob_index >= 1);
|
||||
int test_data_id = ExtractImageNumberFromFileName(s);
|
||||
assert(test_data_id >= 1);
|
||||
if (labels_[max_prob_index - 1] == validation_data_[test_data_id - 1]) {
|
||||
++top_1_correct_count_;
|
||||
}
|
||||
probs = end;
|
||||
}
|
||||
size_t finished = finished_count_ += static_cast<int>(remain);
|
||||
float progress = static_cast<float>(finished) / validation_data_.size();
|
||||
auto elapsed = system_clock::now() - start_time_;
|
||||
auto eta = progress > 0 ? duration_cast<minutes>(elapsed * (1 - progress) / progress).count() : 9999999;
|
||||
float accuracy = finished > 0 ? top_1_correct_count_ / static_cast<float>(finished) : 0;
|
||||
printf("accuracy = %.2f, progress %.2f%%, expect to be finished in %d minutes\n", accuracy, progress * 100, eta);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
int real_main(int argc, ORTCHAR_T* argv[]) {
|
||||
if (argc < 6) return -1;
|
||||
std::vector<TCharString> image_file_paths;
|
||||
TCharString data_dir = argv[1];
|
||||
TCharString model_path = argv[2];
|
||||
// imagenet_lsvrc_2015_synsets.txt
|
||||
TCharString label_file_path = argv[3];
|
||||
TCharString validation_file_path = argv[4];
|
||||
const int batch_size = std::stoi(argv[5]);
|
||||
|
||||
// TODO: remove the slash at the end of data_dir string
|
||||
LoopDir(data_dir, [&data_dir, &image_file_paths](const ORTCHAR_T* filename, OrtFileType filetype) -> bool {
|
||||
if (filetype != OrtFileType::TYPE_REG) return true;
|
||||
if (filename[0] == '.') return true;
|
||||
const ORTCHAR_T* p = my_strrchr(filename, '.');
|
||||
if (p == nullptr) return true;
|
||||
// as we tested filename[0] is not '.', p should larger than filename
|
||||
assert(p > filename);
|
||||
if (my_strcasecmp(p, ORT_TSTR(".JPEG")) != 0 && my_strcasecmp(p, ORT_TSTR(".JPG")) != 0) return true;
|
||||
TCharString v(data_dir);
|
||||
#ifdef _WIN32
|
||||
v.append(1, '\\');
|
||||
#else
|
||||
v.append(1, '/');
|
||||
#endif
|
||||
v.append(filename);
|
||||
image_file_paths.emplace_back(v);
|
||||
return true;
|
||||
});
|
||||
|
||||
std::vector<uint8_t> data;
|
||||
Ort::Env env(ORT_LOGGING_LEVEL_WARNING, "Default");
|
||||
|
||||
Validator v(env, model_path, label_file_path, validation_file_path, image_file_paths.size());
|
||||
|
||||
//Which image size does the model expect? 224, 299, or ...?
|
||||
int image_size = v.GetImageSize();
|
||||
const int channels = 3;
|
||||
std::atomic<int> finished(0);
|
||||
|
||||
InceptionPreprocessing prepro(image_size, image_size, channels);
|
||||
Controller c;
|
||||
AsyncRingBuffer<std::vector<TCharString>::iterator> buffer(batch_size, 160, c, image_file_paths.begin(),
|
||||
image_file_paths.end(), &prepro, &v);
|
||||
buffer.StartDownloadTasks();
|
||||
std::string err = c.Wait();
|
||||
if (err.empty()) {
|
||||
buffer.ProcessRemain();
|
||||
v.PrintResult();
|
||||
return 0;
|
||||
}
|
||||
fprintf(stderr, "%s\n", err.c_str());
|
||||
return -1;
|
||||
}
|
||||
#ifdef _WIN32
|
||||
int wmain(int argc, ORTCHAR_T* argv[]) {
|
||||
HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
|
||||
if (!SUCCEEDED(hr)) return -1;
|
||||
#else
|
||||
int main(int argc, ORTCHAR_T* argv[]) {
|
||||
#endif
|
||||
int ret = -1;
|
||||
try {
|
||||
ret = real_main(argc, argv);
|
||||
} catch (const std::exception& ex) {
|
||||
fprintf(stderr, "%s\n", ex.what());
|
||||
}
|
||||
#ifdef _WIN32
|
||||
CoUninitialize();
|
||||
#endif
|
||||
return ret;
|
||||
}
|
|
@ -1,65 +0,0 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
// A simple tool to test if the image resizing code works
|
||||
|
||||
#include "image_loader.h"
|
||||
#include "CachedInterpolation.h"
|
||||
#include <jpeglib.h>
|
||||
#include "local_filesystem.h"
|
||||
#include "jpeg_mem.h"
|
||||
|
||||
#include <png.h>
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
std::string file_name(argv[1]);
|
||||
std::string output_file_name(argv[2]);
|
||||
int out_width = 299;
|
||||
int out_height = 299;
|
||||
|
||||
int width;
|
||||
int height;
|
||||
int channels;
|
||||
|
||||
UncompressFlags flags;
|
||||
flags.components = 3;
|
||||
// The TensorFlow-chosen default for jpeg decoding is IFAST, sacrificing
|
||||
// image quality for speed.
|
||||
flags.dct_method = JDCT_IFAST;
|
||||
size_t file_len;
|
||||
void* file_data;
|
||||
ReadFileAsString(file_name.c_str(), file_data, file_len);
|
||||
uint8_t* image_data = Uncompress(file_data, file_len, flags, &width, &height, &channels, nullptr);
|
||||
free(file_data);
|
||||
|
||||
if (channels != 3) {
|
||||
std::ostringstream oss;
|
||||
oss << "input format error, expect 3 channels, got " << channels;
|
||||
throw std::runtime_error(oss.str());
|
||||
}
|
||||
|
||||
std::vector<float> output_data(height * width * channels);
|
||||
|
||||
ResizeImageInMemory((uint8_t*)image_data, output_data.data(), height, width, out_height, out_width, channels);
|
||||
delete[](uint8*) image_data;
|
||||
|
||||
std::vector<png_byte> model_output_bytes(output_data.size());
|
||||
for (size_t i = 0; i != output_data.size(); ++i) {
|
||||
model_output_bytes[i] = (png_byte)(output_data[i]);
|
||||
}
|
||||
|
||||
png_image image;
|
||||
memset(&image, 0, (sizeof image));
|
||||
image.version = PNG_IMAGE_VERSION;
|
||||
image.format = PNG_FORMAT_RGB;
|
||||
image.height = out_height;
|
||||
image.width = out_width;
|
||||
|
||||
if (png_image_write_to_file(&image, output_file_name.c_str(), 0 /*convert_to_8bit*/, model_output_bytes.data(),
|
||||
0 /*row_stride*/, nullptr /*colormap*/) == 0) {
|
||||
printf("write to '%s' failed:%s\n", output_file_name.c_str(), image.message);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
#include <functional>
|
||||
#include "sync_api.h"
|
||||
|
||||
class RunnableTask : public std::unary_function<void, void> {
|
||||
public:
|
||||
virtual void operator()(_Inout_opt_ ONNXRUNTIME_CALLBACK_INSTANCE pci) noexcept = 0;
|
||||
virtual ~RunnableTask() = default;
|
||||
};
|
|
@ -1,90 +0,0 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
|
||||
/**
|
||||
* A special FIFO that is restricted to have only one consumer
|
||||
* The consumer must return the previous borrowed item before taking the next
|
||||
*/
|
||||
template <typename ValueType>
|
||||
class SingleConsumerFIFO {
|
||||
public:
|
||||
struct ListEntry {
|
||||
ValueType value;
|
||||
ListEntry* next = nullptr;
|
||||
};
|
||||
|
||||
private:
|
||||
// fixed size
|
||||
ListEntry* values_;
|
||||
ListEntry* free_list_ = nullptr;
|
||||
// whenever free_list_ is nullptr, free_list_tail_ should equal to &free_list_;
|
||||
ListEntry** free_list_tail_ = &free_list_;
|
||||
bool is_consumer_running_ = false;
|
||||
size_t len_;
|
||||
#ifndef NDEBUG
|
||||
size_t count_ = 0;
|
||||
#endif
|
||||
public:
|
||||
explicit SingleConsumerFIFO(size_t len) : values_(new ListEntry[len]), len_(len) {}
|
||||
|
||||
// destruct values earlier
|
||||
void Release() {
|
||||
delete[] values_;
|
||||
values_ = nullptr;
|
||||
}
|
||||
~SingleConsumerFIFO() noexcept { delete[] values_; }
|
||||
|
||||
template <typename T>
|
||||
void Init(const T& t) {
|
||||
for (size_t i = 0; i != len_; ++i) {
|
||||
t(values_[i].value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a borrowed item
|
||||
* @param e a pointer returned from the Take() function
|
||||
* @return ID of the entry, in [0,len)
|
||||
*/
|
||||
size_t Return(ListEntry* e) {
|
||||
is_consumer_running_ = false;
|
||||
return e - values_;
|
||||
}
|
||||
|
||||
template <typename FUNC>
|
||||
void Put(size_t element_id, const FUNC& f) {
|
||||
assert(element_id < len_);
|
||||
#ifndef NDEBUG
|
||||
++count_;
|
||||
#endif
|
||||
|
||||
// printf("Append %zd to the free list\n", element_id);
|
||||
ListEntry* t = &values_[element_id];
|
||||
t->next = nullptr;
|
||||
(*free_list_tail_) = t;
|
||||
free_list_tail_ = &t->next;
|
||||
f(t->value);
|
||||
}
|
||||
|
||||
ListEntry* Take() {
|
||||
if (is_consumer_running_) return nullptr;
|
||||
if (free_list_ == nullptr) {
|
||||
is_consumer_running_ = false;
|
||||
assert(count_ == 0);
|
||||
return nullptr;
|
||||
}
|
||||
auto input_tensor = free_list_;
|
||||
is_consumer_running_ = true;
|
||||
if ((free_list_ = free_list_->next) == nullptr) free_list_tail_ = &free_list_;
|
||||
#ifndef NDEBUG
|
||||
--count_;
|
||||
assert(free_list_ != nullptr || count_ == 0);
|
||||
#endif
|
||||
return input_tensor;
|
||||
}
|
||||
};
|
|
@ -1,62 +0,0 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <Windows.h>
|
||||
#else
|
||||
#include <vector>
|
||||
#endif
|
||||
#include <onnxruntime/core/session/onnxruntime_c_api.h>
|
||||
#include <onnxruntime/core/session/onnxruntime_cxx_api.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#define my_strtol wcstol
|
||||
#define my_strrchr wcsrchr
|
||||
#define my_strcasecmp _wcsicmp
|
||||
#define my_strdup _strdup
|
||||
#else
|
||||
#define my_strtol strtol
|
||||
#define my_strrchr strrchr
|
||||
#define my_strcasecmp strcasecmp
|
||||
#define my_strdup strdup
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
using ONNXRUNTIME_CALLBACK_INSTANCE = PTP_CALLBACK_INSTANCE;
|
||||
using ONNXRUNTIME_EVENT = HANDLE;
|
||||
#define ONNXRUNTIME_CALLBACK __stdcall
|
||||
using ONNXRUNTIME_WORK = PTP_WORK;
|
||||
using PThreadPoolCallbackEnv = PTP_CALLBACK_ENVIRON;
|
||||
using ONNXRUNTIME_CALLBACK_FUNCTION = PTP_WORK_CALLBACK;
|
||||
#define OnnxRuntimeCloseThreadpoolWork CloseThreadpoolWork
|
||||
inline PThreadPoolCallbackEnv GetDefaultThreadPool() { return nullptr; }
|
||||
#else
|
||||
#define ONNXRUNTIME_CALLBACK
|
||||
namespace Eigen {
|
||||
class ThreadPoolInterface;
|
||||
}
|
||||
using PThreadPoolCallbackEnv = Eigen::ThreadPoolInterface*;
|
||||
#define ONNXRUNTIME_WORK void*
|
||||
struct OnnxRuntimeEvent;
|
||||
using ONNXRUNTIME_EVENT = OnnxRuntimeEvent*;
|
||||
|
||||
class OnnxRuntimeCallbackInstance;
|
||||
using ONNXRUNTIME_CALLBACK_INSTANCE = OnnxRuntimeCallbackInstance*;
|
||||
using ONNXRUNTIME_CALLBACK_FUNCTION = void ONNXRUNTIME_CALLBACK (*)(ONNXRUNTIME_CALLBACK_INSTANCE pci, void* context,
|
||||
ONNXRUNTIME_WORK work);
|
||||
#endif
|
||||
|
||||
// The returned value will be used with CreateAndSubmitThreadpoolWork function
|
||||
PThreadPoolCallbackEnv GetDefaultThreadPool();
|
||||
// On Windows, the last parameter can be null, in that case it will use the default thread pool.
|
||||
// On Linux, there is no per process default thread pool. You have to pass a non-null pointer.
|
||||
// Caller must delete the data pointer if this function returns a non-ok status. Otherwise, the ownership is transferred
|
||||
void CreateAndSubmitThreadpoolWork(_In_ ONNXRUNTIME_CALLBACK_FUNCTION callback, _In_ void* data,
|
||||
_In_opt_ PThreadPoolCallbackEnv pool);
|
||||
ONNXRUNTIME_EVENT CreateOnnxRuntimeEvent();
|
||||
// pci is a pointer, can be NULL. If pci is NULL, signal the event immediately
|
||||
void OnnxRuntimeSetEventWhenCallbackReturns(_Inout_opt_ ONNXRUNTIME_CALLBACK_INSTANCE pci,
|
||||
_In_ ONNXRUNTIME_EVENT finish_event);
|
||||
void WaitAndCloseEvent(_In_ ONNXRUNTIME_EVENT finish_event);
|
|
@ -1,109 +0,0 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#include "sync_api.h"
|
||||
#include <mutex>
|
||||
#include <unsupported/Eigen/CXX11/ThreadPool>
|
||||
#include <core/common/common.h>
|
||||
#include <core/common/logging/logging.h>
|
||||
#include "simple_thread_pool.h"
|
||||
#include "onnxruntime_event.h"
|
||||
|
||||
using onnxruntime::common::Status;
|
||||
|
||||
// this can be passed to one of the following functions:
|
||||
// OnnxRuntimeSetEventWhenCallbackReturns
|
||||
class OnnxRuntimeCallbackInstance {
|
||||
private:
|
||||
std::vector<ONNXRUNTIME_EVENT> events_to_signal_;
|
||||
|
||||
public:
|
||||
void AddEvent(ONNXRUNTIME_EVENT event);
|
||||
onnxruntime::common::Status SignalAllEvents();
|
||||
};
|
||||
|
||||
Status WaitAndCloseEvent(ONNXRUNTIME_EVENT finish_event) {
|
||||
if (finish_event == nullptr)
|
||||
return Status(onnxruntime::common::ONNXRUNTIME, onnxruntime::common::INVALID_ARGUMENT, "");
|
||||
pthread_mutex_lock(&finish_event->finish_event_mutex);
|
||||
while (!finish_event->finished) {
|
||||
pthread_cond_wait(&finish_event->finish_event_data, &finish_event->finish_event_mutex);
|
||||
}
|
||||
pthread_mutex_unlock(&finish_event->finish_event_mutex);
|
||||
delete finish_event;
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status CreateAndSubmitThreadpoolWork(ONNXRUNTIME_CALLBACK_FUNCTION callback, void* data, PThreadPool pool) {
|
||||
if (callback == nullptr)
|
||||
return Status(onnxruntime::common::ONNXRUNTIME, onnxruntime::common::INVALID_ARGUMENT, "callback cannot be NULL");
|
||||
if (pool == nullptr)
|
||||
return Status(onnxruntime::common::ONNXRUNTIME, onnxruntime::common::INVALID_ARGUMENT, "pool cannot be NULL");
|
||||
pool->Schedule([=]() {
|
||||
OnnxRuntimeCallbackInstance instance;
|
||||
callback(&instance, data, nullptr);
|
||||
Status st = instance.SignalAllEvents();
|
||||
if (!st.IsOK()) {
|
||||
LOGF_DEFAULT(ERROR, "SignalAllEvents failed:%s. aborting...\n", st.ErrorMessage().c_str());
|
||||
abort();
|
||||
}
|
||||
});
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
using DefaultThreadPoolType = onnxruntime::SimpleThreadPoolTempl<onnxruntime::Env>;
|
||||
static std::unique_ptr<DefaultThreadPoolType> default_pool;
|
||||
static std::once_flag default_pool_init;
|
||||
|
||||
PThreadPool GetDefaultThreadPool(const onnxruntime::Env& env) {
|
||||
std::call_once(default_pool_init, [&env] {
|
||||
int core_num = env.GetNumCpuCores();
|
||||
default_pool.reset(new DefaultThreadPoolType(core_num, env));
|
||||
});
|
||||
return default_pool.get();
|
||||
}
|
||||
|
||||
Status OnnxRuntimeSetEventWhenCallbackReturns(ONNXRUNTIME_CALLBACK_INSTANCE pci, ONNXRUNTIME_EVENT finish_event) {
|
||||
if (finish_event == nullptr)
|
||||
return Status(onnxruntime::common::ONNXRUNTIME, onnxruntime::common::INVALID_ARGUMENT, "");
|
||||
|
||||
if (pci == nullptr) {
|
||||
if (pthread_mutex_lock(&finish_event->finish_event_mutex)) {
|
||||
return ONNXRUNTIME_MAKE_STATUS(ONNXRUNTIME, FAIL, "lock failed");
|
||||
}
|
||||
finish_event->finished = true;
|
||||
if (pthread_mutex_unlock(&finish_event->finish_event_mutex))
|
||||
return ONNXRUNTIME_MAKE_STATUS(ONNXRUNTIME, FAIL, "unlock failed");
|
||||
if (!pthread_cond_broadcast(&finish_event->finish_event_data))
|
||||
return Status::OK();
|
||||
else
|
||||
return ONNXRUNTIME_MAKE_STATUS(ONNXRUNTIME, FAIL, "pthread_cond_broadcast failed");
|
||||
} else {
|
||||
pci->AddEvent(finish_event);
|
||||
return Status::OK();
|
||||
}
|
||||
}
|
||||
|
||||
void OnnxRuntimeCallbackInstance::AddEvent(ONNXRUNTIME_EVENT event) { events_to_signal_.push_back(event); }
|
||||
|
||||
Status OnnxRuntimeCallbackInstance::SignalAllEvents() {
|
||||
for (ONNXRUNTIME_EVENT finish_event : events_to_signal_) {
|
||||
if (pthread_mutex_lock(&finish_event->finish_event_mutex)) {
|
||||
return ONNXRUNTIME_MAKE_STATUS(ONNXRUNTIME, FAIL, "lock failed");
|
||||
}
|
||||
finish_event->finished = true;
|
||||
if (pthread_mutex_unlock(&finish_event->finish_event_mutex))
|
||||
return ONNXRUNTIME_MAKE_STATUS(ONNXRUNTIME, FAIL, "unlock failed");
|
||||
if (pthread_cond_broadcast(&finish_event->finish_event_data))
|
||||
return ONNXRUNTIME_MAKE_STATUS(ONNXRUNTIME, FAIL, "pthread_cond_broadcast failed");
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status CreateOnnxRuntimeEvent(ONNXRUNTIME_EVENT* out) {
|
||||
if (out == nullptr) return Status(onnxruntime::common::ONNXRUNTIME, onnxruntime::common::INVALID_ARGUMENT, "");
|
||||
*out = new OnnxRuntimeEvent();
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
void ONNXRuntimeCloseEvent(ONNXRUNTIME_EVENT finish_event) { delete finish_event; }
|
|
@ -1,41 +0,0 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#include "sync_api.h"
|
||||
|
||||
void CreateAndSubmitThreadpoolWork(_In_ ONNXRUNTIME_CALLBACK_FUNCTION callback, _In_ void* data,
|
||||
_In_opt_ PThreadPoolCallbackEnv pool) {
|
||||
PTP_WORK work = CreateThreadpoolWork(callback, data, pool);
|
||||
if (!work) {
|
||||
throw std::runtime_error("create thread pool task failed");
|
||||
}
|
||||
SubmitThreadpoolWork(work);
|
||||
}
|
||||
|
||||
void WaitAndCloseEvent(_In_ ONNXRUNTIME_EVENT finish_event) {
|
||||
DWORD dwWaitResult = WaitForSingleObject(finish_event, INFINITE);
|
||||
(void)CloseHandle(finish_event);
|
||||
if (dwWaitResult != WAIT_OBJECT_0) {
|
||||
throw std::runtime_error("WaitForSingleObject failed");
|
||||
}
|
||||
}
|
||||
|
||||
ONNXRUNTIME_EVENT CreateOnnxRuntimeEvent() {
|
||||
HANDLE finish_event = CreateEvent(NULL, // default security attributes
|
||||
TRUE, // manual-reset event
|
||||
FALSE, // initial state is nonsignaled
|
||||
NULL);
|
||||
if (finish_event == NULL) {
|
||||
throw std::runtime_error("unable to create finish event");
|
||||
}
|
||||
return finish_event;
|
||||
}
|
||||
|
||||
void OnnxRuntimeSetEventWhenCallbackReturns(_Inout_opt_ ONNXRUNTIME_CALLBACK_INSTANCE pci,
|
||||
_In_ ONNXRUNTIME_EVENT finish_event) {
|
||||
if (pci)
|
||||
SetEventWhenCallbackReturns(pci, finish_event);
|
||||
else if (!SetEvent(finish_event)) {
|
||||
throw std::runtime_error("SetEvent failed");
|
||||
}
|
||||
}
|
Двоичные данные
samples/c_cxx/imagenet/taskflow.png
Двоичные данные
samples/c_cxx/imagenet/taskflow.png
Двоичный файл не отображается.
До Ширина: | Высота: | Размер: 71 KiB |
|
@ -1,24 +0,0 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
#include "onnxruntime/core/providers/cpu/cpu_provider_factory.h"
|
||||
|
||||
#ifdef USE_CUDA
|
||||
#include "onnxruntime/core/providers/cuda/cuda_provider_factory.h"
|
||||
#endif
|
||||
#ifdef USE_DNNL
|
||||
#include "onnxruntime/core/providers/dnnl/dnnl_provider_factory.h"
|
||||
#endif
|
||||
#ifdef USE_NUPHAR
|
||||
#include "onnxruntime/core/providers/nuphar/nuphar_provider_factory.h"
|
||||
#endif
|
||||
#ifdef USE_TENSORRT
|
||||
#include "onnxruntime/core/providers/tensorrt/tensorrt_provider_factory.h"
|
||||
#endif
|
||||
#ifdef USE_DML
|
||||
#include "onnxruntime/core/providers/dml/dml_provider_factory.h"
|
||||
#endif
|
||||
#ifdef USE_MIGRAPHX
|
||||
#include "onnxruntime/core/providers/migraphx/migraphx_provider_factory.h"
|
||||
#endif
|
|
@ -1,8 +0,0 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
add_executable(model-explorer model-explorer.cpp)
|
||||
target_link_libraries(model-explorer PRIVATE onnxruntime)
|
||||
|
||||
add_executable(batch-model-explorer batch-model-explorer.cpp)
|
||||
target_link_libraries(batch-model-explorer PRIVATE onnxruntime)
|
|
@ -1,133 +0,0 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
/**
|
||||
* This example demonstrates how to batch process data using the experimental C++ API.
|
||||
*
|
||||
* This example is based on the model-explorer.cpp example except it demonstrates how to
|
||||
* batch process data. Please start by checking out model-explorer.cpp first.
|
||||
*
|
||||
* This example is best run with one of the ResNet models (i.e. ResNet18) from the onnx model zoo at
|
||||
* https://github.com/onnx/models
|
||||
*
|
||||
* Assumptions made in this example:
|
||||
* 1) The onnx model has 1 input node and 1 output node
|
||||
* 2) The onnx model has a symbolic first dimension (i.e. -1x3x224x224)
|
||||
*
|
||||
*
|
||||
* In this example, we do the following:
|
||||
* 1) read in an onnx model
|
||||
* 2) print out some metadata information about inputs and outputs that the model expects
|
||||
* 3) create tensors by generating 3 random batches of data (with batch_size = 5) for input to the model
|
||||
* 4) pass each batch through the model and check the resulting output
|
||||
*
|
||||
*
|
||||
* NOTE: Some onnx models may not have a symbolic first dimension. To prepare the onnx model, see the python code snippet below.
|
||||
* ============= Python Example ======================
|
||||
* import onnx
|
||||
* model = onnx.load_model('model.onnx')
|
||||
* model.graph.input[0].type.tensor_type.shape.dim[0].dim_param = 'None'
|
||||
* onnx.save_model(model, 'model-symbolic.onnx')
|
||||
*
|
||||
*/
|
||||
|
||||
#include <algorithm> // std::generate
|
||||
#include <assert.h>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
#include <experimental_onnxruntime_cxx_api.h>
|
||||
|
||||
// pretty prints a shape dimension vector
|
||||
std::string print_shape(const std::vector<int64_t>& v) {
|
||||
std::stringstream ss("");
|
||||
for (size_t i = 0; i < v.size() - 1; i++)
|
||||
ss << v[i] << "x";
|
||||
ss << v[v.size() - 1];
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
int calculate_product(const std::vector<int64_t>& v) {
|
||||
int total = 1;
|
||||
for (auto& i : v) total *= i;
|
||||
return total;
|
||||
}
|
||||
|
||||
using namespace std;
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
if (argc != 2) {
|
||||
cout << "Usage: ./onnx-api-example <onnx_model.onnx>" << endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
std::string str = argv[1];
|
||||
std::wstring wide_string = std::wstring(str.begin(), str.end());
|
||||
std::basic_string<ORTCHAR_T> model_file = std::basic_string<ORTCHAR_T>(wide_string);
|
||||
#else
|
||||
std::string model_file = argv[1];
|
||||
#endif
|
||||
|
||||
// onnxruntime setup
|
||||
Ort::Env env(ORT_LOGGING_LEVEL_WARNING, "batch-model-explorer");
|
||||
Ort::SessionOptions session_options;
|
||||
Ort::Experimental::Session session = Ort::Experimental::Session(env, model_file, session_options);
|
||||
|
||||
// print name/shape of inputs
|
||||
auto input_names = session.GetInputNames();
|
||||
auto input_shapes = session.GetInputShapes();
|
||||
cout << "Input Node Name/Shape (" << input_names.size() << "):" << endl;
|
||||
for (size_t i = 0; i < input_names.size(); i++) {
|
||||
cout << "\t" << input_names[i] << " : " << print_shape(input_shapes[i]) << endl;
|
||||
}
|
||||
|
||||
// print name/shape of outputs
|
||||
auto output_names = session.GetOutputNames();
|
||||
auto output_shapes = session.GetOutputShapes();
|
||||
cout << "Output Node Name/Shape (" << output_names.size() << "):" << endl;
|
||||
for (size_t i = 0; i < output_names.size(); i++) {
|
||||
cout << "\t" << output_names[i] << " : " << print_shape(output_shapes[i]) << endl;
|
||||
}
|
||||
|
||||
// Assume model has 1 input node and 1 output node.
|
||||
assert(input_names.size() == 1 && output_names.size() == 1);
|
||||
|
||||
int batch_size = 5;
|
||||
int num_batches = 3;
|
||||
auto input_shape = input_shapes[0];
|
||||
assert(input_shape[0] == -1); // symbolic dimensions are represented by a -1 value
|
||||
input_shape[0] = batch_size;
|
||||
int num_elements_per_batch = calculate_product(input_shape);
|
||||
|
||||
// process multiple batches
|
||||
for (int i = 0; i < num_batches; i++) {
|
||||
cout << "\nProcessing batch #" << i << endl;
|
||||
|
||||
// Create an Ort tensor containing random numbers
|
||||
std::vector<float> batch_input_tensor_values(num_elements_per_batch);
|
||||
std::generate(batch_input_tensor_values.begin(), batch_input_tensor_values.end(), [&] { return rand() % 255; }); // generate random numbers in the range [0, 255]
|
||||
std::vector<Ort::Value> batch_input_tensors;
|
||||
batch_input_tensors.push_back(Ort::Experimental::Value::CreateTensor<float>(batch_input_tensor_values.data(), batch_input_tensor_values.size(), input_shape));
|
||||
|
||||
// double-check the dimensions of the input tensor
|
||||
assert(batch_input_tensors[0].IsTensor() &&
|
||||
batch_input_tensors[0].GetTensorTypeAndShapeInfo().GetShape() == input_shape);
|
||||
cout << "batch_input_tensor shape: " << print_shape(batch_input_tensors[0].GetTensorTypeAndShapeInfo().GetShape()) << endl;
|
||||
|
||||
// pass data through model
|
||||
try {
|
||||
auto batch_output_tensors = session.Run(input_names, batch_input_tensors, output_names);
|
||||
// double-check the dimensions of the output tensors
|
||||
// NOTE: the number of output tensors is equal to the number of output nodes specifed in the Run() call
|
||||
assert(batch_output_tensors.size() == output_names.size() &&
|
||||
batch_output_tensors[0].IsTensor() &&
|
||||
batch_output_tensors[0].GetTensorTypeAndShapeInfo().GetShape()[0] == batch_size);
|
||||
cout << "batch_output_tensor_shape: " << print_shape(batch_output_tensors[0].GetTensorTypeAndShapeInfo().GetShape()) << endl;
|
||||
} catch (const Ort::Exception& exception) {
|
||||
cout << "ERROR running model inference: " << exception.what() << endl;
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
cout << "\nDone" << endl;
|
||||
}
|
|
@ -1,114 +0,0 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
/**
|
||||
* This sample application demonstrates how to use components of the experimental C++ API
|
||||
* to query for model inputs/outputs and how to run inferrence on a model.
|
||||
*
|
||||
* This example is best run with one of the ResNet models (i.e. ResNet18) from the onnx model zoo at
|
||||
* https://github.com/onnx/models
|
||||
*
|
||||
* Assumptions made in this example:
|
||||
* 1) The onnx model has 1 input node and 1 output node
|
||||
*
|
||||
*
|
||||
* In this example, we do the following:
|
||||
* 1) read in an onnx model
|
||||
* 2) print out some metadata information about inputs and outputs that the model expects
|
||||
* 3) generate random data for an input tensor
|
||||
* 4) pass tensor through the model and check the resulting tensor
|
||||
*
|
||||
*/
|
||||
|
||||
#include <algorithm> // std::generate
|
||||
#include <assert.h>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
#include <experimental_onnxruntime_cxx_api.h>
|
||||
|
||||
// pretty prints a shape dimension vector
|
||||
std::string print_shape(const std::vector<int64_t>& v) {
|
||||
std::stringstream ss("");
|
||||
for (size_t i = 0; i < v.size() - 1; i++)
|
||||
ss << v[i] << "x";
|
||||
ss << v[v.size() - 1];
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
int calculate_product(const std::vector<int64_t>& v) {
|
||||
int total = 1;
|
||||
for (auto& i : v) total *= i;
|
||||
return total;
|
||||
}
|
||||
|
||||
using namespace std;
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
if (argc != 2) {
|
||||
cout << "Usage: ./onnx-api-example <onnx_model.onnx>" << endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
std::string str = argv[1];
|
||||
std::wstring wide_string = std::wstring(str.begin(), str.end());
|
||||
std::basic_string<ORTCHAR_T> model_file = std::basic_string<ORTCHAR_T>(wide_string);
|
||||
#else
|
||||
std::string model_file = argv[1];
|
||||
#endif
|
||||
|
||||
// onnxruntime setup
|
||||
Ort::Env env(ORT_LOGGING_LEVEL_WARNING, "example-model-explorer");
|
||||
Ort::SessionOptions session_options;
|
||||
Ort::Experimental::Session session = Ort::Experimental::Session(env, model_file, session_options); // access experimental components via the Experimental namespace
|
||||
|
||||
// print name/shape of inputs
|
||||
std::vector<std::string> input_names = session.GetInputNames();
|
||||
std::vector<std::vector<int64_t> > input_shapes = session.GetInputShapes();
|
||||
cout << "Input Node Name/Shape (" << input_names.size() << "):" << endl;
|
||||
for (size_t i = 0; i < input_names.size(); i++) {
|
||||
cout << "\t" << input_names[i] << " : " << print_shape(input_shapes[i]) << endl;
|
||||
}
|
||||
|
||||
// print name/shape of outputs
|
||||
std::vector<std::string> output_names = session.GetOutputNames();
|
||||
std::vector<std::vector<int64_t> > output_shapes = session.GetOutputShapes();
|
||||
cout << "Output Node Name/Shape (" << output_names.size() << "):" << endl;
|
||||
for (size_t i = 0; i < output_names.size(); i++) {
|
||||
cout << "\t" << output_names[i] << " : " << print_shape(output_shapes[i]) << endl;
|
||||
}
|
||||
|
||||
// Assume model has 1 input node and 1 output node.
|
||||
assert(input_names.size() == 1 && output_names.size() == 1);
|
||||
|
||||
// Create a single Ort tensor of random numbers
|
||||
auto input_shape = input_shapes[0];
|
||||
int total_number_elements = calculate_product(input_shape);
|
||||
std::vector<float> input_tensor_values(total_number_elements);
|
||||
std::generate(input_tensor_values.begin(), input_tensor_values.end(), [&] { return rand() % 255; }); // generate random numbers in the range [0, 255]
|
||||
std::vector<Ort::Value> input_tensors;
|
||||
input_tensors.push_back(Ort::Experimental::Value::CreateTensor<float>(input_tensor_values.data(), input_tensor_values.size(), input_shape));
|
||||
|
||||
// double-check the dimensions of the input tensor
|
||||
assert(input_tensors[0].IsTensor() &&
|
||||
input_tensors[0].GetTensorTypeAndShapeInfo().GetShape() == input_shape);
|
||||
cout << "\ninput_tensor shape: " << print_shape(input_tensors[0].GetTensorTypeAndShapeInfo().GetShape()) << endl;
|
||||
|
||||
// pass data through model
|
||||
cout << "Running model...";
|
||||
try {
|
||||
auto output_tensors = session.Run(session.GetInputNames(), input_tensors, session.GetOutputNames());
|
||||
cout << "done" << endl;
|
||||
|
||||
// double-check the dimensions of the output tensors
|
||||
// NOTE: the number of output tensors is equal to the number of output nodes specifed in the Run() call
|
||||
assert(output_tensors.size() == session.GetOutputNames().size() &&
|
||||
output_tensors[0].IsTensor());
|
||||
cout << "output_tensor_shape: " << print_shape(output_tensors[0].GetTensorTypeAndShapeInfo().GetShape()) << endl;
|
||||
|
||||
} catch (const Ort::Exception& exception) {
|
||||
cout << "ERROR running model inference: " << exception.what() << endl;
|
||||
exit(-1);
|
||||
}
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
// A sample/test application using the opschema library of ORT's custom ops.
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4100)
|
||||
|
||||
#include "onnx/defs/schema.h"
|
||||
|
||||
#pragma warning(pop)
|
||||
|
||||
//
|
||||
// #include "orttraining/core/graph/training_op_defs.h"
|
||||
|
||||
namespace onnxruntime {
|
||||
extern void RegisterOrtOpSchemas();
|
||||
}
|
||||
|
||||
void Check(const char* domain, int version, const char* opname) {
|
||||
const onnx::OpSchema* schema = onnx::OpSchemaRegistry::Schema(opname, version, domain);
|
||||
std::cout << opname << ": " << ((schema != nullptr) ? "Found schema.\n" : "Error: Schema not found.\n");
|
||||
}
|
||||
|
||||
int main() {
|
||||
onnxruntime::RegisterOrtOpSchemas();
|
||||
|
||||
constexpr const char* kMSDomain = "com.microsoft";
|
||||
|
||||
Check(kMSDomain, 1, "ReluGrad");
|
||||
}
|
Двоичные данные
samples/c_cxx/vs.png
Двоичные данные
samples/c_cxx/vs.png
Двоичный файл не отображается.
До Ширина: | Высота: | Размер: 153 KiB |
|
@ -1,91 +0,0 @@
|
|||
/*
|
||||
Copyright (C) 2021, Intel Corporation
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
namespace yolov3
|
||||
{
|
||||
public class LabelMap
|
||||
{
|
||||
public static readonly string[] Labels = new[] {"person",
|
||||
"bicycle",
|
||||
"car",
|
||||
"motorcycle",
|
||||
"airplane",
|
||||
"bus",
|
||||
"train",
|
||||
"truck",
|
||||
"boat",
|
||||
"traffic light",
|
||||
"fire hydrant",
|
||||
"stop sign",
|
||||
"parking meter",
|
||||
"bench",
|
||||
"bird",
|
||||
"cat",
|
||||
"dog",
|
||||
"horse",
|
||||
"sheep",
|
||||
"cow",
|
||||
"elephant",
|
||||
"bear",
|
||||
"zebra",
|
||||
"giraffe",
|
||||
"backpack",
|
||||
"umbrella",
|
||||
"handbag",
|
||||
"tie",
|
||||
"suitcase",
|
||||
"frisbee",
|
||||
"skis",
|
||||
"snowboard",
|
||||
"sports ball",
|
||||
"kite",
|
||||
"baseball bat",
|
||||
"baseball glove",
|
||||
"skateboard",
|
||||
"surfboard",
|
||||
"tennis racket",
|
||||
"bottle",
|
||||
"wine glass",
|
||||
"cup",
|
||||
"fork",
|
||||
"knife",
|
||||
"spoon",
|
||||
"bowl",
|
||||
"banana",
|
||||
"apple",
|
||||
"sandwich",
|
||||
"orange",
|
||||
"broccoli",
|
||||
"carrot",
|
||||
"hot dog",
|
||||
"pizza",
|
||||
"donut",
|
||||
"cake",
|
||||
"chair",
|
||||
"couch",
|
||||
"potted plant",
|
||||
"bed",
|
||||
"dining table",
|
||||
"toilet",
|
||||
"tv",
|
||||
"laptop",
|
||||
"mouse",
|
||||
"remote",
|
||||
"keyboard",
|
||||
"cell phone",
|
||||
"microwave",
|
||||
"oven",
|
||||
"toaster",
|
||||
"sink",
|
||||
"refrigerator",
|
||||
"book",
|
||||
"clock",
|
||||
"vase",
|
||||
"scissors",
|
||||
"teddy bear",
|
||||
"hair drier",
|
||||
"toothbrush"};
|
||||
}
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
/*
|
||||
Copyright (C) 2021, Intel Corporation
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
namespace yolov3
|
||||
{
|
||||
public class Prediction
|
||||
{
|
||||
public Box Box { get; set; }
|
||||
public string Class { get; set; }
|
||||
public float Score { get; set; }
|
||||
}
|
||||
|
||||
public class Box
|
||||
{
|
||||
public float Xmin { get; set; }
|
||||
public float Ymin { get; set; }
|
||||
public float Xmax { get; set; }
|
||||
public float Ymax { get; set; }
|
||||
|
||||
public Box(float xmin, float ymin, float xmax, float ymax)
|
||||
{
|
||||
Xmin = xmin;
|
||||
Ymin = ymin;
|
||||
Xmax = xmax;
|
||||
Ymax = ymax;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,173 +0,0 @@
|
|||
/*
|
||||
Copyright (C) 2021, Intel Corporation
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Microsoft.ML.OnnxRuntime.Tensors;
|
||||
using Microsoft.ML.OnnxRuntime;
|
||||
using SixLabors.ImageSharp;
|
||||
using SixLabors.ImageSharp.PixelFormats;
|
||||
using SixLabors.ImageSharp.Processing;
|
||||
using SixLabors.ImageSharp.Formats;
|
||||
using SixLabors.ImageSharp.Drawing.Processing;
|
||||
using SixLabors.Fonts;
|
||||
|
||||
namespace yolov3
|
||||
{
|
||||
class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
// string is null or empty
|
||||
if (args == null || args.Length < 3)
|
||||
{
|
||||
Console.WriteLine("Usage information: dotnet run model.onnx input.jpg output.jpg");
|
||||
return;
|
||||
} else
|
||||
{
|
||||
if(!(File.Exists(args[0])))
|
||||
{
|
||||
Console.WriteLine("Model Path does not exist");
|
||||
return;
|
||||
}
|
||||
if (!(File.Exists(args[1])))
|
||||
{
|
||||
Console.WriteLine("Input Path does not exist");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Read paths
|
||||
string modelFilePath = args[0];
|
||||
string imageFilePath = args[1];
|
||||
string outImageFilePath = args[2];
|
||||
|
||||
using Image imageOrg = Image.Load(imageFilePath, out IImageFormat format);
|
||||
|
||||
//Letterbox image
|
||||
var iw = imageOrg.Width;
|
||||
var ih = imageOrg.Height;
|
||||
var w = 416;
|
||||
var h = 416;
|
||||
|
||||
if ((iw == 0) || (ih == 0))
|
||||
{
|
||||
Console.WriteLine("Math error: Attempted to divide by Zero");
|
||||
return;
|
||||
}
|
||||
|
||||
float width = (float)w / iw;
|
||||
float height = (float)h / ih;
|
||||
|
||||
float scale = Math.Min(width, height);
|
||||
|
||||
var nw = (int)(iw * scale);
|
||||
var nh = (int)(ih * scale);
|
||||
|
||||
var pad_dims_w = (w - nw) / 2;
|
||||
var pad_dims_h = (h - nh) / 2;
|
||||
|
||||
// Resize image using default bicubic sampler
|
||||
var image = imageOrg.Clone(x => x.Resize((nw), (nh)));
|
||||
|
||||
var clone = new Image<Rgb24>(w, h);
|
||||
clone.Mutate(i => i.Fill(Color.Gray));
|
||||
clone.Mutate(o => o.DrawImage(image, new Point(pad_dims_w, pad_dims_h), 1f)); // draw the first one top left
|
||||
|
||||
//Preprocessing image
|
||||
Tensor<float> input = new DenseTensor<float>(new[] { 1, 3, h, w });
|
||||
for (int y = 0; y < clone.Height; y++)
|
||||
{
|
||||
Span<Rgb24> pixelSpan = clone.GetPixelRowSpan(y);
|
||||
for (int x = 0; x < clone.Width; x++)
|
||||
{
|
||||
input[0, 0, y, x] = pixelSpan[x].B / 255f;
|
||||
input[0, 1, y, x] = pixelSpan[x].G / 255f;
|
||||
input[0, 2, y, x] = pixelSpan[x].R / 255f;
|
||||
}
|
||||
}
|
||||
|
||||
//Get the Image Shape
|
||||
var image_shape = new DenseTensor<float>(new[] { 1, 2 });
|
||||
image_shape[0, 0] = ih;
|
||||
image_shape[0, 1] = iw;
|
||||
|
||||
// Setup inputs and outputs
|
||||
var container = new List<NamedOnnxValue>();
|
||||
container.Add(NamedOnnxValue.CreateFromTensor("input_1", input));
|
||||
container.Add(NamedOnnxValue.CreateFromTensor("image_shape", image_shape));
|
||||
|
||||
// Session Options
|
||||
SessionOptions options = new SessionOptions();
|
||||
options.LogSeverityLevel = OrtLoggingLevel.ORT_LOGGING_LEVEL_INFO;
|
||||
options.AppendExecutionProvider_OpenVINO(@"MYRIAD_FP16");
|
||||
options.AppendExecutionProvider_CPU(1);
|
||||
|
||||
// Run inference
|
||||
using var session = new InferenceSession(modelFilePath,options);
|
||||
|
||||
using IDisposableReadOnlyCollection<DisposableNamedOnnxValue> results = session.Run(container);
|
||||
|
||||
Console.WriteLine("Inference done");
|
||||
|
||||
//Post Processing Steps
|
||||
var resultsArray = results.ToArray();
|
||||
Tensor<float> boxes = resultsArray[0].AsTensor<float>();
|
||||
Tensor<float> scores = resultsArray[1].AsTensor<float>();
|
||||
int[] indices = resultsArray[2].AsTensor<int>().ToArray();
|
||||
|
||||
var len = indices.Length / 3;
|
||||
var out_classes = new int[len];
|
||||
float[] out_scores = new float[len];
|
||||
|
||||
var predictions = new List<Prediction>();
|
||||
var count = 0;
|
||||
for (int i = 0; i < indices.Length; i = i + 3)
|
||||
{
|
||||
out_classes[count] = indices[i + 1];
|
||||
out_scores[count] = scores[indices[i], indices[i + 1], indices[i + 2]];
|
||||
predictions.Add(new Prediction
|
||||
{
|
||||
Box = new Box(boxes[indices[i], indices[i + 2], 1],
|
||||
boxes[indices[i], indices[i + 2], 0],
|
||||
boxes[indices[i], indices[i + 2], 3],
|
||||
boxes[indices[i], indices[i + 2], 2]),
|
||||
Class = LabelMap.Labels[out_classes[count]],
|
||||
Score = out_scores[count]
|
||||
});
|
||||
count++;
|
||||
}
|
||||
|
||||
// Put boxes, labels and confidence on image and save for viewing
|
||||
using var outputImage = File.OpenWrite(outImageFilePath);
|
||||
Font font = SystemFonts.CreateFont("Arial", 16);
|
||||
foreach (var p in predictions)
|
||||
{
|
||||
imageOrg.Mutate(x =>
|
||||
{
|
||||
x.DrawLines(Color.Red, 2f, new PointF[] {
|
||||
|
||||
new PointF(p.Box.Xmin, p.Box.Ymin),
|
||||
new PointF(p.Box.Xmax, p.Box.Ymin),
|
||||
|
||||
new PointF(p.Box.Xmax, p.Box.Ymin),
|
||||
new PointF(p.Box.Xmax, p.Box.Ymax),
|
||||
|
||||
new PointF(p.Box.Xmax, p.Box.Ymax),
|
||||
new PointF(p.Box.Xmin, p.Box.Ymax),
|
||||
|
||||
new PointF(p.Box.Xmin, p.Box.Ymax),
|
||||
new PointF(p.Box.Xmin, p.Box.Ymin)
|
||||
});
|
||||
x.DrawText($"{p.Class}, {p.Score:0.00}", font, Color.White, new PointF(p.Box.Xmin, p.Box.Ymin));
|
||||
});
|
||||
}
|
||||
imageOrg.Save(outputImage, format);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface AppDelegate : UIResponder <UIApplicationDelegate>
|
||||
|
||||
@property(nonatomic) UIWindow *window;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -1,11 +0,0 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#import "AppDelegate.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@implementation AppDelegate
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -1,98 +0,0 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "20x20",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "20x20",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "29x29",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "29x29",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "40x40",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "40x40",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "60x60",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "60x60",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "20x20",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "20x20",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "29x29",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "29x29",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "40x40",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "40x40",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "76x76",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "76x76",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "83.5x83.5",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ios-marketing",
|
||||
"size" : "1024x1024",
|
||||
"scale" : "1x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
{
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="16097.2" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
|
||||
<device id="retina4_7" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16087"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--View Controller-->
|
||||
<scene sceneID="EHf-IW-A2E">
|
||||
<objects>
|
||||
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
|
||||
<layoutGuides>
|
||||
<viewControllerLayoutGuide type="top" id="Y0Z-8F-bB8"/>
|
||||
<viewControllerLayoutGuide type="bottom" id="TqJ-Hq-gHs"/>
|
||||
</layoutGuides>
|
||||
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="ONNXRuntime Test" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumScaleFactor="0.25" translatesAutoresizingMaskIntoConstraints="NO" id="zIC-MS-HeK">
|
||||
<rect key="frame" x="16" y="314" width="343" height="39"/>
|
||||
<fontDescription key="fontDescription" type="boldSystem" pointSize="32"/>
|
||||
<color key="textColor" red="1" green="0.50329624702372611" blue="0.013296667412401542" alpha="0.84705882352941175" colorSpace="custom" customColorSpace="displayP3"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="trailing" secondItem="zIC-MS-HeK" secondAttribute="trailing" constant="16" id="1Wp-jb-ol6"/>
|
||||
<constraint firstItem="zIC-MS-HeK" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="centerY" id="R2T-Hp-TBa"/>
|
||||
<constraint firstItem="zIC-MS-HeK" firstAttribute="leading" secondItem="Ze5-6b-2t3" secondAttribute="leading" constant="16" id="alE-O6-WL6"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="52" y="374.66266866566718"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
</document>
|
|
@ -1,94 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="16097.2" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
|
||||
<device id="retina4_7" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment version="2304" identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16087"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--View Controller-->
|
||||
<scene sceneID="tne-QT-ifu">
|
||||
<objects>
|
||||
<viewController id="BYZ-38-t0r" customClass="ViewController" sceneMemberID="viewController">
|
||||
<layoutGuides>
|
||||
<viewControllerLayoutGuide type="top" id="5Qd-iD-SiH"/>
|
||||
<viewControllerLayoutGuide type="bottom" id="zTS-de-uge"/>
|
||||
</layoutGuides>
|
||||
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Select the ONNX model below" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Jnm-tY-m15">
|
||||
<rect key="frame" x="71.5" y="20" width="232" height="20.5"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<toolbar opaque="NO" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="UWb-3E-O5r" userLabel="Bottom Invoke Toolbar">
|
||||
<rect key="frame" x="0.0" y="154.5" width="375" height="44"/>
|
||||
<items>
|
||||
<barButtonItem title="OrtSession.Run" width="374" id="He4-7G-biW">
|
||||
<connections>
|
||||
<action selector="invokeRuntime:" destination="BYZ-38-t0r" id="Ycs-E9-Vul"/>
|
||||
</connections>
|
||||
</barButtonItem>
|
||||
</items>
|
||||
</toolbar>
|
||||
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" editable="NO" adjustsFontForContentSizeCategory="YES" selectable="NO" translatesAutoresizingMaskIntoConstraints="NO" id="7Ws-3t-76I">
|
||||
<rect key="frame" x="0.0" y="198.5" width="375" height="468.5"/>
|
||||
<color key="backgroundColor" red="0.12820077356385221" green="0.40366933178860925" blue="0.96080166101455688" alpha="1" colorSpace="custom" customColorSpace="displayP3"/>
|
||||
<color key="textColor" cocoaTouchSystemColor="tableCellGroupedBackgroundColor"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
|
||||
</textView>
|
||||
<toolbar opaque="NO" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="ahi-i4-2FP" userLabel="Top Model Toolbar">
|
||||
<rect key="frame" x="0.0" y="80.5" width="375" height="44"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="44" id="nwy-nk-0wZ"/>
|
||||
</constraints>
|
||||
<items>
|
||||
<barButtonItem style="plain" id="Ywd-KS-s96">
|
||||
<segmentedControl key="customView" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="bar" selectedSegmentIndex="0" id="8kc-88-CHj" userLabel="Model Control">
|
||||
<rect key="frame" x="16" y="6" width="343" height="32"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<segments>
|
||||
<segment title="MobileNet"/>
|
||||
<segment title="Quantized"/>
|
||||
<segment title="NLP"/>
|
||||
</segments>
|
||||
<connections>
|
||||
<action selector="modelChanged:" destination="BYZ-38-t0r" eventType="valueChanged" id="z13-8K-EwC"/>
|
||||
</connections>
|
||||
</segmentedControl>
|
||||
</barButtonItem>
|
||||
</items>
|
||||
</toolbar>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="trailing" secondItem="ahi-i4-2FP" secondAttribute="trailing" id="1D2-FC-OQ0"/>
|
||||
<constraint firstItem="UWb-3E-O5r" firstAttribute="leading" secondItem="8bC-Xf-vdC" secondAttribute="leading" id="1To-8n-Knb"/>
|
||||
<constraint firstItem="7Ws-3t-76I" firstAttribute="leading" secondItem="8bC-Xf-vdC" secondAttribute="leading" id="3Et-px-WCV"/>
|
||||
<constraint firstItem="zTS-de-uge" firstAttribute="top" secondItem="7Ws-3t-76I" secondAttribute="bottom" id="Lkb-XF-ldX"/>
|
||||
<constraint firstItem="Jnm-tY-m15" firstAttribute="top" secondItem="5Qd-iD-SiH" secondAttribute="bottom" constant="20" id="bKw-8D-j4F"/>
|
||||
<constraint firstItem="7Ws-3t-76I" firstAttribute="top" secondItem="UWb-3E-O5r" secondAttribute="bottom" id="bXr-pF-Ld2"/>
|
||||
<constraint firstItem="ahi-i4-2FP" firstAttribute="leading" secondItem="8bC-Xf-vdC" secondAttribute="leading" id="c98-HO-3m5"/>
|
||||
<constraint firstItem="ahi-i4-2FP" firstAttribute="top" secondItem="Jnm-tY-m15" secondAttribute="bottom" constant="40" id="fHc-5w-lzl"/>
|
||||
<constraint firstAttribute="trailing" secondItem="UWb-3E-O5r" secondAttribute="trailing" id="oJz-hf-dJa"/>
|
||||
<constraint firstAttribute="trailing" secondItem="7Ws-3t-76I" secondAttribute="trailing" id="oyy-C7-mUJ"/>
|
||||
<constraint firstItem="UWb-3E-O5r" firstAttribute="top" secondItem="ahi-i4-2FP" secondAttribute="bottom" constant="30" id="uFg-MF-aJz"/>
|
||||
<constraint firstItem="Jnm-tY-m15" firstAttribute="centerX" secondItem="8bC-Xf-vdC" secondAttribute="centerX" id="vl6-uY-kDj"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<connections>
|
||||
<outlet property="invokeButton" destination="He4-7G-biW" id="kpj-CS-Fss"/>
|
||||
<outlet property="modelControl" destination="8kc-88-CHj" id="GTB-WG-ozW"/>
|
||||
<outlet property="resultsTextView" destination="7Ws-3t-76I" id="fnd-i5-Pdh"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="136.80000000000001" y="132.68365817091455"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
</document>
|
|
@ -1,42 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>LaunchScreen</string>
|
||||
<key>UIMainStoryboardFile</key>
|
||||
<string>Main</string>
|
||||
<key>UIRequiredDeviceCapabilities</key>
|
||||
<array>
|
||||
<string>armv7</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
|
@ -1,28 +0,0 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#ifndef ort_session_h
|
||||
#define ort_session_h
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
// @class TFLInterpreterOptions;
|
||||
// @class TFLTensor;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
|
||||
/**
|
||||
* An OnnxRuntime Session
|
||||
*/
|
||||
@interface OrtMobileSession : NSObject
|
||||
|
||||
- (nullable instancetype)initWithModelPath:(NSString *)modelPath error:(NSError **)error;
|
||||
- (NSString*)run: (NSMutableData *)buff mname:(NSString*)mname error:(NSError **)error;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
|
||||
#endif /* ort_session_h */
|
|
@ -1,143 +0,0 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#import "OrtSession.h"
|
||||
|
||||
#include <vector>
|
||||
#include <chrono>
|
||||
#include <sstream>
|
||||
|
||||
#include <core/session/onnxruntime_cxx_api.h>
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
|
||||
static std::string run_mobilenet(Ort::Session* session) {
|
||||
static const int width_ = 224;
|
||||
static const int height_ = 224;
|
||||
static const int classes = 1000;
|
||||
|
||||
auto& input_image_ = *(new std::array<float, 3 * width_ * height_>());
|
||||
auto& results_ = *(new std::array<float, classes>);
|
||||
|
||||
std::array<int64_t, 4> input_shape_{1, 3, width_, height_};
|
||||
std::array<int64_t, 2> output_shape_{1, classes};
|
||||
|
||||
auto memory_info = Ort::MemoryInfo::CreateCpu(OrtDeviceAllocator, OrtMemTypeCPU);
|
||||
|
||||
auto input_tensor = Ort::Value::CreateTensor<float>(memory_info, input_image_.data(), input_image_.size(), input_shape_.data(), input_shape_.size());
|
||||
auto output_tensor = Ort::Value::CreateTensor<float>(memory_info, results_.data(), results_.size(), output_shape_.data(), output_shape_.size());
|
||||
|
||||
const char* input_names[] = {"data"};
|
||||
const char* output_names[] = {"mobilenetv20_output_flatten0_reshape0"};
|
||||
|
||||
// Start measuring time
|
||||
auto begin = std::chrono::high_resolution_clock::now();
|
||||
session->Run(Ort::RunOptions{nullptr}, input_names, &input_tensor, 1, output_names, &output_tensor, 1);
|
||||
|
||||
// Stop measuring time and calculate the elapsed time
|
||||
auto end = std::chrono::high_resolution_clock::now();
|
||||
auto elapsed = std::chrono::duration_cast<std::chrono::nanoseconds>(end - begin);
|
||||
|
||||
Ort::OrtRelease(input_tensor.release());
|
||||
Ort::OrtRelease(output_tensor.release());
|
||||
delete &input_image_;
|
||||
delete &results_;
|
||||
|
||||
std::ostringstream output;
|
||||
output << "Total time: " << static_cast<double>(elapsed.count() * 1e-3) << std::endl;
|
||||
return output.str();
|
||||
}
|
||||
|
||||
static std::string run_nlp(Ort::Session* session) {
|
||||
auto memory_info = Ort::MemoryInfo::CreateCpu(OrtDeviceAllocator, OrtMemTypeCPU);
|
||||
|
||||
auto input_shape = std::vector<int64_t>({5, 6});
|
||||
auto input = new std::array<float, 5 * 6>();
|
||||
std::generate(input->begin(), input->end(), []{return std::rand() % 109;});
|
||||
|
||||
auto output_shape = std::vector<int64_t>({5, 26});
|
||||
auto result = new std::array<float, 5 * 26>();
|
||||
|
||||
auto input_tensor = Ort::Value::CreateTensor<float>(memory_info, input->data(), input->size(), input_shape.data(), input_shape.size());
|
||||
auto output_tensor = Ort::Value::CreateTensor<float>(memory_info, result->data(), result->size(), output_shape.data(), output_shape.size());
|
||||
|
||||
const char* input_names[] = {"input_1"};
|
||||
const char* output_names[] = {"dense_1"};
|
||||
|
||||
// Start measuring time
|
||||
auto begin = std::chrono::high_resolution_clock::now();
|
||||
session->Run(Ort::RunOptions{nullptr}, input_names, &input_tensor, 1, output_names, &output_tensor, 1);
|
||||
|
||||
// Stop measuring time and calculate the elapsed time
|
||||
auto end = std::chrono::high_resolution_clock::now();
|
||||
auto elapsed = std::chrono::duration_cast<std::chrono::nanoseconds>(end - begin);
|
||||
|
||||
delete input;
|
||||
delete result;
|
||||
|
||||
std::ostringstream output;
|
||||
output << "Total time: " << static_cast<double>(elapsed.count() * 1e-3) << std::endl;
|
||||
return output.str();
|
||||
}
|
||||
|
||||
@interface OrtMobileSession ()
|
||||
|
||||
@property(nonatomic, nullable) Ort::Session* pOrtApiSession;
|
||||
|
||||
@property(nonatomic, nullable) Ort::Value* input_tensor;
|
||||
|
||||
@property(nonatomic, nullable) Ort::Value* output_tensor;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation OrtMobileSession
|
||||
|
||||
#pragma mark - NSObject
|
||||
|
||||
- (void)dealloc {
|
||||
if (_pOrtApiSession != nullptr) {
|
||||
delete _pOrtApiSession;
|
||||
_pOrtApiSession = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Public
|
||||
|
||||
static std::unique_ptr<Ort::Env> ort_env;
|
||||
|
||||
- (nullable instancetype)initWithModelPath:(NSString *)modelPath error:(NSError **)error {
|
||||
|
||||
self = [super init];
|
||||
if (_pOrtApiSession != nullptr) {
|
||||
delete _pOrtApiSession;
|
||||
_pOrtApiSession = nullptr;
|
||||
}
|
||||
ort_env.reset();
|
||||
|
||||
ort_env.reset(new Ort::Env(ORT_LOGGING_LEVEL_INFO, "Default"));
|
||||
Ort::SessionOptions so(nullptr);
|
||||
const char* model_path = [modelPath UTF8String];
|
||||
_pOrtApiSession = new Ort::Session(*ort_env, model_path, so);
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSString* )run:(nonnull NSMutableData *)buff mname:(NSString*)mname error:(NSError *__autoreleasing _Nullable * _Nullable)error {
|
||||
std::string sinfo;
|
||||
if ([mname isEqualToString:@"mobilenet"]) {
|
||||
sinfo = run_mobilenet(_pOrtApiSession);
|
||||
}
|
||||
else if ([mname isEqualToString:@"nlp"]) {
|
||||
sinfo = run_nlp(_pOrtApiSession);
|
||||
}
|
||||
|
||||
NSString *resultMsg = [NSString stringWithCString:sinfo.c_str()
|
||||
encoding:[NSString defaultCStringEncoding]];
|
||||
return resultMsg;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -1,11 +0,0 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface ViewController : UIViewController
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -1,192 +0,0 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#import "ViewController.h"
|
||||
|
||||
#import "OrtSession.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
* Safely dispatches the given `block` on the main thread. If already on the main thread, the given
|
||||
* block is executed immediately; otherwise, dispatches the block asynchronously on the main thread.
|
||||
*
|
||||
* @param block The block to dispatch on the main thread.
|
||||
*/
|
||||
void TLTSafeDispatchOnMain(dispatch_block_t block) {
|
||||
if (block == nil) return;
|
||||
if (NSThread.isMainThread) {
|
||||
block();
|
||||
} else {
|
||||
dispatch_async(dispatch_get_main_queue(), block);
|
||||
}
|
||||
}
|
||||
|
||||
static NSString *const kModelNameMobileNet = @"mobilenetv2-7";
|
||||
|
||||
static NSString *const kModelNameQuantized = @"mobilenetv2-7";
|
||||
|
||||
static NSString *const kModelNameNLP = @"nlp";
|
||||
|
||||
/** Model resource type. */
|
||||
static NSString *const kModelType = @"ort";
|
||||
|
||||
/** The label for the serial queue for synchronizing runtime calls. */
|
||||
static const char *kRuntimeSerialQueueLabel = "com.onnxruntime.testapp";
|
||||
|
||||
static NSString *const kNilRuntimeError =
|
||||
@"Failed to invoke the runtime because the runtime was nil.";
|
||||
static NSString *const kInvokeRuntimeError = @"Failed to invoke ONNX Runtime due to error: %@.";
|
||||
|
||||
/** Model paths. */
|
||||
static NSArray *arrModelPaths;
|
||||
|
||||
@interface ViewController ()
|
||||
|
||||
/** Serial queue for synchronizing runtime calls. */
|
||||
@property(nonatomic) dispatch_queue_t runtimeSerialQueue;
|
||||
|
||||
/** ONNXRuntime for the currently selected model. */
|
||||
@property(nonatomic) OrtMobileSession *runtime;
|
||||
|
||||
@property(weak, nonatomic) IBOutlet UISegmentedControl *modelControl;
|
||||
@property(weak, nonatomic) IBOutlet UIBarButtonItem *invokeButton;
|
||||
@property(weak, nonatomic) IBOutlet UITextView *resultsTextView;
|
||||
|
||||
@end
|
||||
|
||||
@implementation ViewController
|
||||
|
||||
#pragma mark - NSObject
|
||||
|
||||
+ (void)initialize {
|
||||
if (self == [ViewController self]) {
|
||||
arrModelPaths = @[
|
||||
[NSBundle.mainBundle pathForResource:kModelNameMobileNet ofType:kModelType],
|
||||
[NSBundle.mainBundle pathForResource:kModelNameQuantized ofType:kModelType],
|
||||
[NSBundle.mainBundle pathForResource:kModelNameNLP ofType:kModelType],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - UIViewController
|
||||
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
self.runtimeSerialQueue =
|
||||
dispatch_queue_create(kRuntimeSerialQueueLabel, DISPATCH_QUEUE_SERIAL);
|
||||
self.invokeButton.enabled = NO;
|
||||
[self updateResultsText:[NSString stringWithFormat:@"Using ONNXRuntime runtime version %@.", @"1.5.0"]];
|
||||
[self loadModel];
|
||||
}
|
||||
|
||||
#pragma mark - IBActions
|
||||
|
||||
- (IBAction)modelChanged:(id)sender {
|
||||
self.invokeButton.enabled = NO;
|
||||
NSString *results = [NSString
|
||||
stringWithFormat:@"Switched to the %@ model.",
|
||||
[self.modelControl
|
||||
titleForSegmentAtIndex:self.modelControl.selectedSegmentIndex]];
|
||||
[self updateResultsText:results];
|
||||
[self loadModel];
|
||||
|
||||
}
|
||||
|
||||
- (IBAction)invokeRuntime:(id)sender {
|
||||
switch (self.modelControl.selectedSegmentIndex) {
|
||||
case 0:
|
||||
[self invokeMobileNet];
|
||||
break;
|
||||
case 1:
|
||||
[self invokeQuantized];
|
||||
break;
|
||||
case 2:
|
||||
[self invokeNLP];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Private
|
||||
|
||||
/** Path of the currently selected model. */
|
||||
- (nullable NSString *)currentModelPath {
|
||||
return self.modelControl.selectedSegmentIndex == UISegmentedControlNoSegment
|
||||
? nil
|
||||
: arrModelPaths[self.modelControl.selectedSegmentIndex];
|
||||
}
|
||||
|
||||
- (void)loadModel {
|
||||
NSString *modelPath = [self currentModelPath];
|
||||
if (modelPath.length == 0) {
|
||||
[self updateResultsText:@"No model is selected."];
|
||||
return;
|
||||
}
|
||||
|
||||
__weak typeof(self) weakSelf = self;
|
||||
dispatch_async(self.runtimeSerialQueue, ^{
|
||||
NSError *error;
|
||||
weakSelf.runtime = [[OrtMobileSession alloc] initWithModelPath:modelPath
|
||||
error:&error];
|
||||
if (weakSelf.runtime == nil || error != nil) {
|
||||
NSString *results =
|
||||
[NSString stringWithFormat:@"Failed to create the runtime due to error:%@",
|
||||
error.localizedDescription];
|
||||
[weakSelf updateResultsText:results];
|
||||
} else {
|
||||
TLTSafeDispatchOnMain(^{
|
||||
weakSelf.invokeButton.enabled = YES;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
- (void)invokeMobileNet {
|
||||
__weak typeof(self) weakSelf = self;
|
||||
dispatch_async(self.runtimeSerialQueue, ^{
|
||||
if (weakSelf.runtime == nil) {
|
||||
[weakSelf updateResultsText:kNilRuntimeError];
|
||||
return;
|
||||
}
|
||||
|
||||
NSError* error;
|
||||
NSMutableData* data = [NSMutableData alloc];
|
||||
NSString *resultMsg = [weakSelf.runtime run:data mname:@"mobilenet" error:&error];
|
||||
[weakSelf updateResultsText:resultMsg];
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
- (void)invokeQuantized {
|
||||
__weak typeof(self) weakSelf = self;
|
||||
dispatch_async(self.runtimeSerialQueue, ^{
|
||||
if (weakSelf.runtime == nil) {
|
||||
[weakSelf updateResultsText:kNilRuntimeError];
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
- (void)invokeNLP {
|
||||
__weak typeof(self) weakSelf = self;
|
||||
dispatch_async(self.runtimeSerialQueue, ^{
|
||||
if (weakSelf.runtime == nil) {
|
||||
[weakSelf updateResultsText:kNilRuntimeError];
|
||||
return;
|
||||
}
|
||||
NSError* error;
|
||||
NSMutableData* data = [NSMutableData alloc];
|
||||
NSString *resultMsg = [weakSelf.runtime run:data mname:@"nlp" error:&error];
|
||||
[weakSelf updateResultsText:resultMsg];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)updateResultsText:(NSString *)text {
|
||||
__weak typeof(self) weakSelf = self;
|
||||
TLTSafeDispatchOnMain(^{
|
||||
weakSelf.resultsTextView.text = text;
|
||||
});
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -1,11 +0,0 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "AppDelegate.h"
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
@autoreleasepool {
|
||||
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
|
||||
}
|
||||
}
|
|
@ -1,405 +0,0 @@
|
|||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 50;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
61202FC924F7357100DCF250 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 61202FC624F7357100DCF250 /* ViewController.m */; };
|
||||
61202FCA24F7357100DCF250 /* OrtSession.mm in Sources */ = {isa = PBXBuildFile; fileRef = 61202FC824F7357100DCF250 /* OrtSession.mm */; };
|
||||
614B6546250C2DFF00570B69 /* nlp.ort in Resources */ = {isa = PBXBuildFile; fileRef = 614B6545250C2DFF00570B69 /* nlp.ort */; };
|
||||
614B6548250C2E4900570B69 /* mobilenetv2-7.ort in Resources */ = {isa = PBXBuildFile; fileRef = 614B6547250C2E4900570B69 /* mobilenetv2-7.ort */; };
|
||||
61B7341324F9AAD8009C4F8C /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B210BD962291D78D00572163 /* Main.storyboard */; };
|
||||
61B7341424F9AAD8009C4F8C /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B210BD9B2291D78F00572163 /* LaunchScreen.storyboard */; };
|
||||
61DFBA262519190800114A5C /* libonnxruntime.1.5.0.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 61DFBA252519190800114A5C /* libonnxruntime.1.5.0.dylib */; };
|
||||
61DFBA2825191A4900114A5C /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = 61DFBA2725191A4800114A5C /* README.md */; };
|
||||
61DFBA2925191BD900114A5C /* libonnxruntime.1.5.0.dylib in Copy Files */ = {isa = PBXBuildFile; fileRef = 61DFBA252519190800114A5C /* libonnxruntime.1.5.0.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
|
||||
B210BD922291D78D00572163 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = B210BD912291D78D00572163 /* AppDelegate.m */; };
|
||||
B210BD9A2291D78E00572163 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B210BD992291D78E00572163 /* Assets.xcassets */; };
|
||||
B210BDA02291D78F00572163 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = B210BD9F2291D78F00572163 /* main.m */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
614954D424F784EA00605437 /* Copy Files */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 12;
|
||||
dstPath = "";
|
||||
dstSubfolderSpec = 10;
|
||||
files = (
|
||||
61DFBA2925191BD900114A5C /* libonnxruntime.1.5.0.dylib in Copy Files */,
|
||||
);
|
||||
name = "Copy Files";
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXCopyFilesBuildPhase section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
61202FC624F7357100DCF250 /* ViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = "<group>"; };
|
||||
61202FC724F7357100DCF250 /* OrtSession.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OrtSession.h; sourceTree = "<group>"; };
|
||||
61202FC824F7357100DCF250 /* OrtSession.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = OrtSession.mm; sourceTree = "<group>"; };
|
||||
614B6545250C2DFF00570B69 /* nlp.ort */ = {isa = PBXFileReference; lastKnownFileType = file; name = nlp.ort; path = Models/nlp.ort; sourceTree = "<group>"; };
|
||||
614B6547250C2E4900570B69 /* mobilenetv2-7.ort */ = {isa = PBXFileReference; lastKnownFileType = file; name = "mobilenetv2-7.ort"; path = "Models/mobilenetv2-7.ort"; sourceTree = "<group>"; };
|
||||
61DFBA252519190800114A5C /* libonnxruntime.1.5.0.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libonnxruntime.1.5.0.dylib; path = "../../build/iOS/MinSizeRel/MinSizeRel-iphoneos/libonnxruntime.1.5.0.dylib"; sourceTree = "<group>"; };
|
||||
61DFBA2725191A4800114A5C /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
|
||||
B210BD8D2291D78D00572163 /* ONNXModelRunner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ONNXModelRunner.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
B210BD902291D78D00572163 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
|
||||
B210BD912291D78D00572163 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
|
||||
B210BD932291D78D00572163 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = "<group>"; };
|
||||
B210BD972291D78D00572163 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
|
||||
B210BD992291D78E00572163 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
B210BD9C2291D78F00572163 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||
B210BD9E2291D78F00572163 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
B210BD9F2291D78F00572163 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
B210BD8A2291D78D00572163 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
61DFBA262519190800114A5C /* libonnxruntime.1.5.0.dylib in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
614954C424F76DF100605437 /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
61DFBA252519190800114A5C /* libonnxruntime.1.5.0.dylib */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
61B7341524F9B775009C4F8C /* Models */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
614B6547250C2E4900570B69 /* mobilenetv2-7.ort */,
|
||||
614B6545250C2DFF00570B69 /* nlp.ort */,
|
||||
);
|
||||
name = Models;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B210BD842291D78D00572163 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
61DFBA2725191A4800114A5C /* README.md */,
|
||||
61B7341524F9B775009C4F8C /* Models */,
|
||||
B210BD8F2291D78D00572163 /* ModelRunner */,
|
||||
B210BD8E2291D78D00572163 /* Products */,
|
||||
614954C424F76DF100605437 /* Frameworks */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B210BD8E2291D78D00572163 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B210BD8D2291D78D00572163 /* ONNXModelRunner.app */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B210BD8F2291D78D00572163 /* ModelRunner */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
61202FC724F7357100DCF250 /* OrtSession.h */,
|
||||
61202FC824F7357100DCF250 /* OrtSession.mm */,
|
||||
61202FC624F7357100DCF250 /* ViewController.m */,
|
||||
B210BD902291D78D00572163 /* AppDelegate.h */,
|
||||
B210BD912291D78D00572163 /* AppDelegate.m */,
|
||||
B210BD932291D78D00572163 /* ViewController.h */,
|
||||
B210BD962291D78D00572163 /* Main.storyboard */,
|
||||
B210BD992291D78E00572163 /* Assets.xcassets */,
|
||||
B210BD9B2291D78F00572163 /* LaunchScreen.storyboard */,
|
||||
B210BD9E2291D78F00572163 /* Info.plist */,
|
||||
B210BD9F2291D78F00572163 /* main.m */,
|
||||
);
|
||||
path = ModelRunner;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
B210BD8C2291D78D00572163 /* ONNXModelRunner */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = B210BDA32291D78F00572163 /* Build configuration list for PBXNativeTarget "ONNXModelRunner" */;
|
||||
buildPhases = (
|
||||
B210BD892291D78D00572163 /* Sources */,
|
||||
B210BD8A2291D78D00572163 /* Frameworks */,
|
||||
B210BD8B2291D78D00572163 /* Resources */,
|
||||
614954D424F784EA00605437 /* Copy Files */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = ONNXModelRunner;
|
||||
productName = TestApp;
|
||||
productReference = B210BD8D2291D78D00572163 /* ONNXModelRunner.app */;
|
||||
productType = "com.apple.product-type.application";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
B210BD852291D78D00572163 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 1010;
|
||||
ORGANIZATIONNAME = Microsoft;
|
||||
TargetAttributes = {
|
||||
B210BD8C2291D78D00572163 = {
|
||||
CreatedOnToolsVersion = 10.1;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = B210BD882291D78D00572163 /* Build configuration list for PBXProject "ONNXModelRunner" */;
|
||||
compatibilityVersion = "Xcode 9.3";
|
||||
developmentRegion = en;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
Base,
|
||||
);
|
||||
mainGroup = B210BD842291D78D00572163;
|
||||
productRefGroup = B210BD8E2291D78D00572163 /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
B210BD8C2291D78D00572163 /* ONNXModelRunner */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
B210BD8B2291D78D00572163 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
61DFBA2825191A4900114A5C /* README.md in Resources */,
|
||||
614B6548250C2E4900570B69 /* mobilenetv2-7.ort in Resources */,
|
||||
614B6546250C2DFF00570B69 /* nlp.ort in Resources */,
|
||||
61B7341324F9AAD8009C4F8C /* Main.storyboard in Resources */,
|
||||
61B7341424F9AAD8009C4F8C /* LaunchScreen.storyboard in Resources */,
|
||||
B210BD9A2291D78E00572163 /* Assets.xcassets in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
B210BD892291D78D00572163 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
61202FC924F7357100DCF250 /* ViewController.m in Sources */,
|
||||
B210BDA02291D78F00572163 /* main.m in Sources */,
|
||||
61202FCA24F7357100DCF250 /* OrtSession.mm in Sources */,
|
||||
B210BD922291D78D00572163 /* AppDelegate.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXVariantGroup section */
|
||||
B210BD962291D78D00572163 /* Main.storyboard */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
B210BD972291D78D00572163 /* Base */,
|
||||
);
|
||||
name = Main.storyboard;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B210BD9B2291D78F00572163 /* LaunchScreen.storyboard */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
B210BD9C2291D78F00572163 /* Base */,
|
||||
);
|
||||
name = LaunchScreen.storyboard;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXVariantGroup section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
B210BDA12291D78F00572163 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 12.1;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
B210BDA22291D78F00572163 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 12.1;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
MTL_FAST_MATH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
VALIDATE_PRODUCT = YES;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
B210BDA42291D78F00572163 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 0.0.2;
|
||||
DEVELOPMENT_TEAM = QBMN2BBW3K;
|
||||
HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/../../include/onnxruntime";
|
||||
INFOPLIST_FILE = "$(SRCROOT)/ModelRunner/Info.plist";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
LIBRARY_SEARCH_PATHS = "$(PROJECT_DIR)/Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.onnxruntime.TestApp;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
TARGETED_DEVICE_FAMILY = 1;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
B210BDA52291D78F00572163 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 0.0.2;
|
||||
DEVELOPMENT_TEAM = QBMN2BBW3K;
|
||||
FRAMEWORK_SEARCH_PATHS = "";
|
||||
HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/../../include/onnxruntime";
|
||||
INFOPLIST_FILE = "$(SRCROOT)/ModelRunner/Info.plist";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
LIBRARY_SEARCH_PATHS = "$(PROJECT_DIR)/Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.onnxruntime.TestApp;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
TARGETED_DEVICE_FAMILY = 1;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
B210BD882291D78D00572163 /* Build configuration list for PBXProject "ONNXModelRunner" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
B210BDA12291D78F00572163 /* Debug */,
|
||||
B210BDA22291D78F00572163 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
B210BDA32291D78F00572163 /* Build configuration list for PBXNativeTarget "ONNXModelRunner" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
B210BDA42291D78F00572163 /* Debug */,
|
||||
B210BDA52291D78F00572163 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = B210BD852291D78D00572163 /* Project object */;
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "self:/Users/wenbingl/Project/iOSTestApp/ONNXModelRunner.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
|
@ -1,8 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>IDEDidComputeMac32BitWarning</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
|
@ -1,78 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1160"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "B210BD8C2291D78D00572163"
|
||||
BuildableName = "ONNXModelRunner.app"
|
||||
BlueprintName = "ONNXModelRunner"
|
||||
ReferencedContainer = "container:ONNXModelRunner.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Release"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "B210BD8C2291D78D00572163"
|
||||
BuildableName = "ONNXModelRunner.app"
|
||||
BlueprintName = "ONNXModelRunner"
|
||||
ReferencedContainer = "container:ONNXModelRunner.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "B210BD8C2291D78D00572163"
|
||||
BuildableName = "ONNXModelRunner.app"
|
||||
BlueprintName = "ONNXModelRunner"
|
||||
ReferencedContainer = "container:ONNXModelRunner.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
|
@ -1,10 +0,0 @@
|
|||
## ONNXRuntime iOS Sample
|
||||
|
||||
|
||||
**Before build the app**
|
||||
|
||||
1. create a soft link to the libonnxruntime.1.4.0.dylib to Framework directory.
|
||||
2. Cook the reduced size ORT model by following the manual. And the model file names could be found are "mobilenetv2-7.ort" and "nlp.ort" which are temporary.
|
||||
|
||||
|
||||
**open it as the xcode project, build and run the app**
|
|
@ -1,195 +0,0 @@
|
|||
'''
|
||||
Copyright (C) 2021, Intel Corporation
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
'''
|
||||
|
||||
import numpy as np
|
||||
import onnxruntime as rt
|
||||
import cv2
|
||||
import time
|
||||
import os
|
||||
|
||||
def sigmoid(x, derivative=False):
|
||||
return x*(1-x) if derivative else 1/(1+np.exp(-x))
|
||||
|
||||
def softmax(x):
|
||||
scoreMatExp = np.exp(np.asarray(x))
|
||||
return scoreMatExp / scoreMatExp.sum(0)
|
||||
|
||||
def checkModelExtension(fp):
|
||||
# Split the extension from the path and normalise it to lowercase.
|
||||
ext = os.path.splitext(fp)[-1].lower()
|
||||
|
||||
# Now we can simply use != to check for inequality, no need for wildcards.
|
||||
if(ext != ".onnx"):
|
||||
raise Exception(fp, "is an unknown file format. Use the model ending with .onnx format")
|
||||
|
||||
if not os.path.exists(fp):
|
||||
raise Exception("[ ERROR ] Path of the onnx model file is Invalid")
|
||||
|
||||
def checkVideoFileExtension(fp):
|
||||
# Split the extension from the path and normalise it to lowercase.
|
||||
ext = os.path.splitext(fp)[-1].lower()
|
||||
# Now we can simply use != to check for inequality, no need for wildcards.
|
||||
|
||||
if(ext == ".mp4" or ext == ".avi" or ext == ".mov"):
|
||||
pass
|
||||
else:
|
||||
raise Exception(fp, "is an unknown file format. Use the video file ending with .mp4 or .avi or .mov formats")
|
||||
|
||||
if not os.path.exists(fp):
|
||||
raise Exception("[ ERROR ] Path of the video file is Invalid")
|
||||
|
||||
# color look up table for different classes for object detection sample
|
||||
clut = [(0,0,0),(255,0,0),(255,0,255),(0,0,255),(0,255,0),(0,255,128),
|
||||
(128,255,0),(128,128,0),(0,128,255),(128,0,128),
|
||||
(255,0,128),(128,0,255),(255,128,128),(128,255,128),(255,255,0),
|
||||
(255,128,128),(128,128,255),(255,128,128),(128,255,128),(128,255,128)]
|
||||
|
||||
# 20 labels that the tiny-yolov2 model can do the object_detection on
|
||||
label = ["aeroplane","bicycle","bird","boat","bottle",
|
||||
"bus","car","cat","chair","cow","diningtable",
|
||||
"dog","horse","motorbike","person","pottedplant",
|
||||
"sheep","sofa","train","tvmonitor"]
|
||||
|
||||
model_file_path = "tiny_yolo_v2_zoo_model.onnx"
|
||||
# TODO: You need to modify the path to the input onnx model based on where it is located on your device after downloading it from ONNX Model zoo.
|
||||
|
||||
# Validate model file path
|
||||
checkModelExtension(model_file_path)
|
||||
|
||||
# Load the model
|
||||
sess = rt.InferenceSession(model_file_path)
|
||||
|
||||
# Get the input name of the model
|
||||
input_name = sess.get_inputs()[0].name
|
||||
|
||||
device = 'CPU_FP32'
|
||||
# Set OpenVINO as the Execution provider to infer this model
|
||||
sess.set_providers(['OpenVINOExecutionProvider'], [{'device_type' : device}])
|
||||
'''
|
||||
other 'device_type' options are: (Any hardware target can be assigned if you have the access to it)
|
||||
|
||||
'CPU_FP32', 'GPU_FP32', 'GPU_FP16', 'MYRIAD_FP16', 'VAD-M_FP16', 'VAD-F_FP32',
|
||||
'HETERO:MYRIAD,CPU', 'MULTI:MYRIAD,GPU,CPU'
|
||||
|
||||
'''
|
||||
|
||||
#Path to video file has to be provided
|
||||
video_file_path = "sample_demo_video.mp4"
|
||||
# TODO: You need to specify the path to your own sample video based on where it is located on your device.
|
||||
|
||||
#validate video file input path
|
||||
checkVideoFileExtension(video_file_path)
|
||||
|
||||
#Path to video file has to be provided
|
||||
cap = cv2.VideoCapture(video_file_path)
|
||||
|
||||
# capturing different metrics of the image from the video
|
||||
fps = cap.get(cv2.CAP_PROP_FPS)
|
||||
width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
|
||||
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
|
||||
x_scale = float(width)/416.0 #In the document of tino-yolo-v2, input shape of this network is (1,3,416,416).
|
||||
y_scale = float(height)/416.0
|
||||
|
||||
# writing the inferencing output as a video to the local disk
|
||||
fourcc = cv2.VideoWriter_fourcc(*'XVID')
|
||||
output_video_name = device + "_output.avi"
|
||||
output_video = cv2.VideoWriter(output_video_name,fourcc, float(17.0), (640,360))
|
||||
|
||||
# capturing one frame at a time from the video feed and performing the inference
|
||||
i = 0
|
||||
while cap.isOpened():
|
||||
l_start = time.time()
|
||||
ret, frame = cap.read()
|
||||
if not ret:
|
||||
break
|
||||
initial_w = cap.get(3)
|
||||
initial_h = cap.get(4)
|
||||
|
||||
# preprocessing the input frame and reshaping it.
|
||||
#In the document of tino-yolo-v2, input shape of this network is (1,3,416,416). so we resize the model frame w.r.t that size.
|
||||
in_frame = cv2.resize(frame, (416, 416))
|
||||
X = np.asarray(in_frame)
|
||||
X = X.astype(np.float32)
|
||||
X = X.transpose(2,0,1)
|
||||
# Reshaping the input array to align with the input shape of the model
|
||||
X = X.reshape(1,3,416,416)
|
||||
|
||||
start = time.time()
|
||||
#Running the session by passing in the input data of the model
|
||||
out = sess.run(None, {input_name: X})
|
||||
end = time.time()
|
||||
inference_time = end - start
|
||||
out = out[0][0]
|
||||
|
||||
numClasses = 20
|
||||
anchors = [1.08, 1.19, 3.42, 4.41, 6.63, 11.38, 9.42, 5.11, 16.62, 10.52]
|
||||
|
||||
existingLabels = {l: [] for l in label}
|
||||
|
||||
#Inside this loop we compute the bounding box b for grid cell (cy, cx)
|
||||
for cy in range(0,13):
|
||||
for cx in range(0,13):
|
||||
for b in range(0,5):
|
||||
# First we read the tx, ty, width(tw), and height(th) for the bounding box from the out array, as well as the confidence score
|
||||
channel = b*(numClasses+5)
|
||||
tx = out[channel ][cy][cx]
|
||||
ty = out[channel+1][cy][cx]
|
||||
tw = out[channel+2][cy][cx]
|
||||
th = out[channel+3][cy][cx]
|
||||
tc = out[channel+4][cy][cx]
|
||||
|
||||
x = (float(cx) + sigmoid(tx))*32
|
||||
y = (float(cy) + sigmoid(ty))*32
|
||||
|
||||
w = np.exp(tw) * 32 * anchors[2*b ]
|
||||
h = np.exp(th) * 32 * anchors[2*b+1]
|
||||
|
||||
#calculating the confidence score
|
||||
confidence = sigmoid(tc) # The confidence value for the bounding box is given by tc
|
||||
|
||||
classes = np.zeros(numClasses)
|
||||
for c in range(0,numClasses):
|
||||
classes[c] = out[channel + 5 +c][cy][cx]
|
||||
# we take the softmax to turn the array into a probability distribution. And then we pick the class with the largest score as the winner.
|
||||
classes = softmax(classes)
|
||||
detectedClass = classes.argmax()
|
||||
|
||||
# Now we can compute the final score for this bounding box and we only want to keep the ones whose combined score is over a certain threshold
|
||||
if 0.45< classes[detectedClass]*confidence:
|
||||
color =clut[detectedClass]
|
||||
x = (x - w/2)*x_scale
|
||||
y = (y - h/2)*y_scale
|
||||
w *= x_scale
|
||||
h *= y_scale
|
||||
|
||||
labelX = int((x+x+w)/2)
|
||||
labelY = int((y+y+h)/2)
|
||||
addLabel = True
|
||||
labThreshold = 40
|
||||
for point in existingLabels[label[detectedClass]]:
|
||||
if labelX < point[0] + labThreshold and labelX > point[0] - labThreshold and \
|
||||
labelY < point[1] + labThreshold and labelY > point[1] - labThreshold:
|
||||
addLabel = False
|
||||
#Adding class labels to the output of the frame and also drawing a rectangular bounding box around the object detected.
|
||||
if addLabel:
|
||||
cv2.rectangle(frame, (int(x),int(y)),(int(x+w),int(y+h)),color,2)
|
||||
cv2.rectangle(frame, (int(x),int(y-13)),(int(x)+9*len(label[detectedClass]),int(y)),color,-1)
|
||||
cv2.putText(frame,label[detectedClass],(int(x)+2,int(y)-3),cv2.FONT_HERSHEY_COMPLEX,0.4,(255,255,255),1)
|
||||
existingLabels[label[detectedClass]].append((labelX,labelY))
|
||||
print('{} detected in frame {}'.format(label[detectedClass],i))
|
||||
output_video.write(frame)
|
||||
cv2.putText(frame,device,(10,20),cv2.FONT_HERSHEY_COMPLEX,0.5,(255,255,255),1)
|
||||
cv2.putText(frame,'FPS: {}'.format(1.0/inference_time),(10,40),cv2.FONT_HERSHEY_COMPLEX,0.5,(255,255,255),1)
|
||||
cv2.imshow('frame',frame)
|
||||
|
||||
#Press 'q' to quit the process
|
||||
if cv2.waitKey(1) & 0xFF == ord('q'):
|
||||
break
|
||||
print('Processed Frame {}'.format(i))
|
||||
i += 1
|
||||
l_end = time.time()
|
||||
print('Loop Time = {}'.format(l_end - l_start))
|
||||
output_video.release()
|
||||
cv2.destroyAllWindows()
|
|
@ -1,54 +0,0 @@
|
|||
# MNIST Sample - Number recognition
|
||||
|
||||
This sample uses the MNIST model from the Model Zoo: https://github.com/onnx/models/tree/master/vision/classification/mnist
|
||||
|
||||
![Screenshot](Screenshot.png)
|
||||
|
||||
## Requirements
|
||||
|
||||
* MacOS Catalina
|
||||
* Xcode 11
|
||||
* Compiled libonnxruntime.dll / lib
|
||||
|
||||
|
||||
## Build
|
||||
|
||||
Command line:
|
||||
|
||||
```
|
||||
$ xcodebuild -project SwiftMnist.xcodeproj
|
||||
```
|
||||
|
||||
From Xcode, open `SwiftMnist.xcodeproj` and run with Command-R.
|
||||
|
||||
## How to use it
|
||||
|
||||
Just draw a number on the surface, when you lift your finger from the
|
||||
mouse or the trackpad, the guess 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).
|
||||
|
||||
## How it works
|
||||
|
||||
(Add once it is added)
|
||||
|
||||
### 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).
|
||||
|
||||
### 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
|
||||
|
|
@ -1,396 +0,0 @@
|
|||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 50;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
49F074202485756B00A83CDC /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49F0741F2485756B00A83CDC /* AppDelegate.swift */; };
|
||||
49F074222485756B00A83CDC /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49F074212485756B00A83CDC /* ContentView.swift */; };
|
||||
49F074242485756C00A83CDC /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 49F074232485756C00A83CDC /* Assets.xcassets */; };
|
||||
49F074272485756C00A83CDC /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 49F074262485756C00A83CDC /* Preview Assets.xcassets */; };
|
||||
49F0742A2485756C00A83CDC /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 49F074282485756C00A83CDC /* Main.storyboard */; };
|
||||
49F07434248575A600A83CDC /* libonnxruntime.1.3.0.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 49F07433248575A600A83CDC /* libonnxruntime.1.3.0.dylib */; };
|
||||
49F07435248575A600A83CDC /* libonnxruntime.1.3.0.dylib in Embed Libraries */ = {isa = PBXBuildFile; fileRef = 49F07433248575A600A83CDC /* libonnxruntime.1.3.0.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
|
||||
49F0743A248575CE00A83CDC /* OnnxInterop.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 49F07438248575CE00A83CDC /* OnnxInterop.cpp */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
49F07436248575A600A83CDC /* Embed Libraries */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
dstPath = "";
|
||||
dstSubfolderSpec = 10;
|
||||
files = (
|
||||
49F07435248575A600A83CDC /* libonnxruntime.1.3.0.dylib in Embed Libraries */,
|
||||
);
|
||||
name = "Embed Libraries";
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXCopyFilesBuildPhase section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
49F0741C2485756B00A83CDC /* SwiftMnist.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SwiftMnist.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
49F0741F2485756B00A83CDC /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||
49F074212485756B00A83CDC /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
|
||||
49F074232485756C00A83CDC /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
49F074262485756C00A83CDC /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
|
||||
49F074292485756C00A83CDC /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
|
||||
49F0742B2485756C00A83CDC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
49F0742C2485756C00A83CDC /* SwiftMnist.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = SwiftMnist.entitlements; sourceTree = "<group>"; };
|
||||
49F07433248575A600A83CDC /* libonnxruntime.1.3.0.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libonnxruntime.1.3.0.dylib; path = ../../build/Linux/RelWithDebInfo/libonnxruntime.1.3.0.dylib; sourceTree = "<group>"; };
|
||||
49F07437248575CE00A83CDC /* SwiftMnist-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SwiftMnist-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||
49F07438248575CE00A83CDC /* OnnxInterop.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = OnnxInterop.cpp; sourceTree = "<group>"; };
|
||||
49F07439248575CE00A83CDC /* OnnxInterop.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = OnnxInterop.hpp; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
49F074192485756B00A83CDC /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
49F07434248575A600A83CDC /* libonnxruntime.1.3.0.dylib in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
49F074132485756B00A83CDC = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
49F0741E2485756B00A83CDC /* SwiftMnist */,
|
||||
49F0741D2485756B00A83CDC /* Products */,
|
||||
49F07432248575A600A83CDC /* Frameworks */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
49F0741D2485756B00A83CDC /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
49F0741C2485756B00A83CDC /* SwiftMnist.app */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
49F0741E2485756B00A83CDC /* SwiftMnist */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
49F0741F2485756B00A83CDC /* AppDelegate.swift */,
|
||||
49F074212485756B00A83CDC /* ContentView.swift */,
|
||||
49F074232485756C00A83CDC /* Assets.xcassets */,
|
||||
49F074282485756C00A83CDC /* Main.storyboard */,
|
||||
49F0742B2485756C00A83CDC /* Info.plist */,
|
||||
49F0742C2485756C00A83CDC /* SwiftMnist.entitlements */,
|
||||
49F074252485756C00A83CDC /* Preview Content */,
|
||||
49F07438248575CE00A83CDC /* OnnxInterop.cpp */,
|
||||
49F07439248575CE00A83CDC /* OnnxInterop.hpp */,
|
||||
49F07437248575CE00A83CDC /* SwiftMnist-Bridging-Header.h */,
|
||||
);
|
||||
path = SwiftMnist;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
49F074252485756C00A83CDC /* Preview Content */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
49F074262485756C00A83CDC /* Preview Assets.xcassets */,
|
||||
);
|
||||
path = "Preview Content";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
49F07432248575A600A83CDC /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
49F07433248575A600A83CDC /* libonnxruntime.1.3.0.dylib */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
49F0741B2485756B00A83CDC /* SwiftMnist */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 49F0742F2485756C00A83CDC /* Build configuration list for PBXNativeTarget "SwiftMnist" */;
|
||||
buildPhases = (
|
||||
49F074182485756B00A83CDC /* Sources */,
|
||||
49F074192485756B00A83CDC /* Frameworks */,
|
||||
49F0741A2485756B00A83CDC /* Resources */,
|
||||
49F07436248575A600A83CDC /* Embed Libraries */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = SwiftMnist;
|
||||
productName = SwiftMnist;
|
||||
productReference = 49F0741C2485756B00A83CDC /* SwiftMnist.app */;
|
||||
productType = "com.apple.product-type.application";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
49F074142485756B00A83CDC /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastSwiftUpdateCheck = 1150;
|
||||
LastUpgradeCheck = 1150;
|
||||
ORGANIZATIONNAME = "Miguel de Icaza";
|
||||
TargetAttributes = {
|
||||
49F0741B2485756B00A83CDC = {
|
||||
CreatedOnToolsVersion = 11.5;
|
||||
LastSwiftMigration = 1150;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 49F074172485756B00A83CDC /* Build configuration list for PBXProject "SwiftMnist" */;
|
||||
compatibilityVersion = "Xcode 9.3";
|
||||
developmentRegion = en;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
Base,
|
||||
);
|
||||
mainGroup = 49F074132485756B00A83CDC;
|
||||
productRefGroup = 49F0741D2485756B00A83CDC /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
49F0741B2485756B00A83CDC /* SwiftMnist */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
49F0741A2485756B00A83CDC /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
49F0742A2485756C00A83CDC /* Main.storyboard in Resources */,
|
||||
49F074272485756C00A83CDC /* Preview Assets.xcassets in Resources */,
|
||||
49F074242485756C00A83CDC /* Assets.xcassets in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
49F074182485756B00A83CDC /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
49F074222485756B00A83CDC /* ContentView.swift in Sources */,
|
||||
49F074202485756B00A83CDC /* AppDelegate.swift in Sources */,
|
||||
49F0743A248575CE00A83CDC /* OnnxInterop.cpp in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXVariantGroup section */
|
||||
49F074282485756C00A83CDC /* Main.storyboard */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
49F074292485756C00A83CDC /* Base */,
|
||||
);
|
||||
name = Main.storyboard;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXVariantGroup section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
49F0742D2485756C00A83CDC /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.15;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = macosx;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
49F0742E2485756C00A83CDC /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.15;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
MTL_FAST_MATH = YES;
|
||||
SDKROOT = macosx;
|
||||
SWIFT_COMPILATION_MODE = wholemodule;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-O";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
49F074302485756C00A83CDC /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = SwiftMnist/SwiftMnist.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"SwiftMnist/Preview Content\"";
|
||||
DEVELOPMENT_TEAM = PJQC57N853;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/../../include/onnxruntime/core/session";
|
||||
INFOPLIST_FILE = SwiftMnist/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
LIBRARY_SEARCH_PATHS = "$(PROJECT_DIR)/../../build/Linux/RelWithDebInfo";
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.15;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.tirania.SwiftMnist;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "SwiftMnist/SwiftMnist-Bridging-Header.h";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
49F074312485756C00A83CDC /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = SwiftMnist/SwiftMnist.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"SwiftMnist/Preview Content\"";
|
||||
DEVELOPMENT_TEAM = PJQC57N853;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/../../include/onnxruntime/core/session";
|
||||
INFOPLIST_FILE = SwiftMnist/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
LIBRARY_SEARCH_PATHS = "$(PROJECT_DIR)/../../build/Linux/RelWithDebInfo";
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.15;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.tirania.SwiftMnist;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "SwiftMnist/SwiftMnist-Bridging-Header.h";
|
||||
SWIFT_VERSION = 5.0;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
49F074172485756B00A83CDC /* Build configuration list for PBXProject "SwiftMnist" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
49F0742D2485756C00A83CDC /* Debug */,
|
||||
49F0742E2485756C00A83CDC /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
49F0742F2485756C00A83CDC /* Build configuration list for PBXNativeTarget "SwiftMnist" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
49F074302485756C00A83CDC /* Debug */,
|
||||
49F074312485756C00A83CDC /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = 49F074142485756B00A83CDC /* Project object */;
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "self:SwiftMnist.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
|
@ -1,8 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>IDEDidComputeMac32BitWarning</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
|
@ -1,85 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1150"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "49F0741B2485756B00A83CDC"
|
||||
BuildableName = "SwiftMnist.app"
|
||||
BlueprintName = "SwiftMnist"
|
||||
ReferencedContainer = "container:SwiftMnist.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "49F0741B2485756B00A83CDC"
|
||||
BuildableName = "SwiftMnist.app"
|
||||
BlueprintName = "SwiftMnist"
|
||||
ReferencedContainer = "container:SwiftMnist.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<EnvironmentVariables>
|
||||
<EnvironmentVariable
|
||||
key = "CG_CONTEXT_SHOW_BACKTRACE"
|
||||
value = "1"
|
||||
isEnabled = "YES">
|
||||
</EnvironmentVariable>
|
||||
</EnvironmentVariables>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "49F0741B2485756B00A83CDC"
|
||||
BuildableName = "SwiftMnist.app"
|
||||
BlueprintName = "SwiftMnist"
|
||||
ReferencedContainer = "container:SwiftMnist.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
|
@ -1,41 +0,0 @@
|
|||
//
|
||||
// AppDelegate.swift
|
||||
// SwiftMnist
|
||||
//
|
||||
// Created by Miguel de Icaza on 6/1/20.
|
||||
// Copyright © 2020 Miguel de Icaza. All rights reserved.
|
||||
//
|
||||
|
||||
import Cocoa
|
||||
import SwiftUI
|
||||
|
||||
var mnist = mnist_new()
|
||||
@NSApplicationMain
|
||||
class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
|
||||
var window: NSWindow!
|
||||
|
||||
|
||||
func applicationDidFinishLaunching(_ aNotification: Notification) {
|
||||
// Create the SwiftUI view that provides the window contents.
|
||||
let contentView = ContentView()
|
||||
|
||||
|
||||
// Create the window and set the content view.
|
||||
window = NSWindow(
|
||||
contentRect: NSRect(x: 0, y: 0, width: 540, height: 300),
|
||||
styleMask: [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView],
|
||||
backing: .buffered, defer: false)
|
||||
window.center()
|
||||
window.setFrameAutosaveName("Main Window")
|
||||
window.contentView = NSHostingView(rootView: contentView)
|
||||
window.makeKeyAndOrderFront(nil)
|
||||
}
|
||||
|
||||
func applicationWillTerminate(_ aNotification: Notification) {
|
||||
// Insert code here to tear down your application
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "mac",
|
||||
"scale" : "1x",
|
||||
"size" : "16x16"
|
||||
},
|
||||
{
|
||||
"idiom" : "mac",
|
||||
"scale" : "2x",
|
||||
"size" : "16x16"
|
||||
},
|
||||
{
|
||||
"idiom" : "mac",
|
||||
"scale" : "1x",
|
||||
"size" : "32x32"
|
||||
},
|
||||
{
|
||||
"idiom" : "mac",
|
||||
"scale" : "2x",
|
||||
"size" : "32x32"
|
||||
},
|
||||
{
|
||||
"idiom" : "mac",
|
||||
"scale" : "1x",
|
||||
"size" : "128x128"
|
||||
},
|
||||
{
|
||||
"idiom" : "mac",
|
||||
"scale" : "2x",
|
||||
"size" : "128x128"
|
||||
},
|
||||
{
|
||||
"idiom" : "mac",
|
||||
"scale" : "1x",
|
||||
"size" : "256x256"
|
||||
},
|
||||
{
|
||||
"idiom" : "mac",
|
||||
"scale" : "2x",
|
||||
"size" : "256x256"
|
||||
},
|
||||
{
|
||||
"idiom" : "mac",
|
||||
"scale" : "1x",
|
||||
"size" : "512x512"
|
||||
},
|
||||
{
|
||||
"idiom" : "mac",
|
||||
"scale" : "2x",
|
||||
"size" : "512x512"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -1,683 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="14814" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14814"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--Application-->
|
||||
<scene sceneID="JPo-4y-FX3">
|
||||
<objects>
|
||||
<application id="hnw-xV-0zn" sceneMemberID="viewController">
|
||||
<menu key="mainMenu" title="Main Menu" systemMenu="main" id="AYu-sK-qS6">
|
||||
<items>
|
||||
<menuItem title="SwiftMnist" id="1Xt-HY-uBw">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="SwiftMnist" systemMenu="apple" id="uQy-DD-JDr">
|
||||
<items>
|
||||
<menuItem title="About SwiftMnist" id="5kV-Vb-QxS">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="orderFrontStandardAboutPanel:" target="Ady-hI-5gd" id="Exp-CZ-Vem"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="VOq-y0-SEH"/>
|
||||
<menuItem title="Preferences…" keyEquivalent="," id="BOF-NM-1cW"/>
|
||||
<menuItem isSeparatorItem="YES" id="wFC-TO-SCJ"/>
|
||||
<menuItem title="Services" id="NMo-om-nkz">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Services" systemMenu="services" id="hz9-B4-Xy5"/>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="4je-JR-u6R"/>
|
||||
<menuItem title="Hide SwiftMnist" keyEquivalent="h" id="Olw-nP-bQN">
|
||||
<connections>
|
||||
<action selector="hide:" target="Ady-hI-5gd" id="PnN-Uc-m68"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Hide Others" keyEquivalent="h" id="Vdr-fp-XzO">
|
||||
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
|
||||
<connections>
|
||||
<action selector="hideOtherApplications:" target="Ady-hI-5gd" id="VT4-aY-XCT"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Show All" id="Kd2-mp-pUS">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="unhideAllApplications:" target="Ady-hI-5gd" id="Dhg-Le-xox"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="kCx-OE-vgT"/>
|
||||
<menuItem title="Quit SwiftMnist" keyEquivalent="q" id="4sb-4s-VLi">
|
||||
<connections>
|
||||
<action selector="terminate:" target="Ady-hI-5gd" id="Te7-pn-YzF"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem title="File" id="dMs-cI-mzQ">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="File" id="bib-Uj-vzu">
|
||||
<items>
|
||||
<menuItem title="New" keyEquivalent="n" id="Was-JA-tGl">
|
||||
<connections>
|
||||
<action selector="newDocument:" target="Ady-hI-5gd" id="4Si-XN-c54"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Open…" keyEquivalent="o" id="IAo-SY-fd9">
|
||||
<connections>
|
||||
<action selector="openDocument:" target="Ady-hI-5gd" id="bVn-NM-KNZ"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Open Recent" id="tXI-mr-wws">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Open Recent" systemMenu="recentDocuments" id="oas-Oc-fiZ">
|
||||
<items>
|
||||
<menuItem title="Clear Menu" id="vNY-rz-j42">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="clearRecentDocuments:" target="Ady-hI-5gd" id="Daa-9d-B3U"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="m54-Is-iLE"/>
|
||||
<menuItem title="Close" keyEquivalent="w" id="DVo-aG-piG">
|
||||
<connections>
|
||||
<action selector="performClose:" target="Ady-hI-5gd" id="HmO-Ls-i7Q"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Save…" keyEquivalent="s" id="pxx-59-PXV">
|
||||
<connections>
|
||||
<action selector="saveDocument:" target="Ady-hI-5gd" id="teZ-XB-qJY"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Save As…" keyEquivalent="S" id="Bw7-FT-i3A">
|
||||
<connections>
|
||||
<action selector="saveDocumentAs:" target="Ady-hI-5gd" id="mDf-zr-I0C"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Revert to Saved" keyEquivalent="r" id="KaW-ft-85H">
|
||||
<connections>
|
||||
<action selector="revertDocumentToSaved:" target="Ady-hI-5gd" id="iJ3-Pv-kwq"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="aJh-i4-bef"/>
|
||||
<menuItem title="Page Setup…" keyEquivalent="P" id="qIS-W8-SiK">
|
||||
<modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
|
||||
<connections>
|
||||
<action selector="runPageLayout:" target="Ady-hI-5gd" id="Din-rz-gC5"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Print…" keyEquivalent="p" id="aTl-1u-JFS">
|
||||
<connections>
|
||||
<action selector="print:" target="Ady-hI-5gd" id="qaZ-4w-aoO"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem title="Edit" id="5QF-Oa-p0T">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Edit" id="W48-6f-4Dl">
|
||||
<items>
|
||||
<menuItem title="Undo" keyEquivalent="z" id="dRJ-4n-Yzg">
|
||||
<connections>
|
||||
<action selector="undo:" target="Ady-hI-5gd" id="M6e-cu-g7V"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Redo" keyEquivalent="Z" id="6dh-zS-Vam">
|
||||
<connections>
|
||||
<action selector="redo:" target="Ady-hI-5gd" id="oIA-Rs-6OD"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="WRV-NI-Exz"/>
|
||||
<menuItem title="Cut" keyEquivalent="x" id="uRl-iY-unG">
|
||||
<connections>
|
||||
<action selector="cut:" target="Ady-hI-5gd" id="YJe-68-I9s"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Copy" keyEquivalent="c" id="x3v-GG-iWU">
|
||||
<connections>
|
||||
<action selector="copy:" target="Ady-hI-5gd" id="G1f-GL-Joy"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Paste" keyEquivalent="v" id="gVA-U4-sdL">
|
||||
<connections>
|
||||
<action selector="paste:" target="Ady-hI-5gd" id="UvS-8e-Qdg"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Paste and Match Style" keyEquivalent="V" id="WeT-3V-zwk">
|
||||
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
|
||||
<connections>
|
||||
<action selector="pasteAsPlainText:" target="Ady-hI-5gd" id="cEh-KX-wJQ"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Delete" id="pa3-QI-u2k">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="delete:" target="Ady-hI-5gd" id="0Mk-Ml-PaM"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Select All" keyEquivalent="a" id="Ruw-6m-B2m">
|
||||
<connections>
|
||||
<action selector="selectAll:" target="Ady-hI-5gd" id="VNm-Mi-diN"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="uyl-h8-XO2"/>
|
||||
<menuItem title="Find" id="4EN-yA-p0u">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Find" id="1b7-l0-nxx">
|
||||
<items>
|
||||
<menuItem title="Find…" tag="1" keyEquivalent="f" id="Xz5-n4-O0W">
|
||||
<connections>
|
||||
<action selector="performFindPanelAction:" target="Ady-hI-5gd" id="cD7-Qs-BN4"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Find and Replace…" tag="12" keyEquivalent="f" id="YEy-JH-Tfz">
|
||||
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
|
||||
<connections>
|
||||
<action selector="performFindPanelAction:" target="Ady-hI-5gd" id="WD3-Gg-5AJ"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Find Next" tag="2" keyEquivalent="g" id="q09-fT-Sye">
|
||||
<connections>
|
||||
<action selector="performFindPanelAction:" target="Ady-hI-5gd" id="NDo-RZ-v9R"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Find Previous" tag="3" keyEquivalent="G" id="OwM-mh-QMV">
|
||||
<connections>
|
||||
<action selector="performFindPanelAction:" target="Ady-hI-5gd" id="HOh-sY-3ay"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Use Selection for Find" tag="7" keyEquivalent="e" id="buJ-ug-pKt">
|
||||
<connections>
|
||||
<action selector="performFindPanelAction:" target="Ady-hI-5gd" id="U76-nv-p5D"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Jump to Selection" keyEquivalent="j" id="S0p-oC-mLd">
|
||||
<connections>
|
||||
<action selector="centerSelectionInVisibleArea:" target="Ady-hI-5gd" id="IOG-6D-g5B"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem title="Spelling and Grammar" id="Dv1-io-Yv7">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Spelling" id="3IN-sU-3Bg">
|
||||
<items>
|
||||
<menuItem title="Show Spelling and Grammar" keyEquivalent=":" id="HFo-cy-zxI">
|
||||
<connections>
|
||||
<action selector="showGuessPanel:" target="Ady-hI-5gd" id="vFj-Ks-hy3"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Check Document Now" keyEquivalent=";" id="hz2-CU-CR7">
|
||||
<connections>
|
||||
<action selector="checkSpelling:" target="Ady-hI-5gd" id="fz7-VC-reM"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="bNw-od-mp5"/>
|
||||
<menuItem title="Check Spelling While Typing" id="rbD-Rh-wIN">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="toggleContinuousSpellChecking:" target="Ady-hI-5gd" id="7w6-Qz-0kB"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Check Grammar With Spelling" id="mK6-2p-4JG">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="toggleGrammarChecking:" target="Ady-hI-5gd" id="muD-Qn-j4w"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Correct Spelling Automatically" id="78Y-hA-62v">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="toggleAutomaticSpellingCorrection:" target="Ady-hI-5gd" id="2lM-Qi-WAP"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem title="Substitutions" id="9ic-FL-obx">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Substitutions" id="FeM-D8-WVr">
|
||||
<items>
|
||||
<menuItem title="Show Substitutions" id="z6F-FW-3nz">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="orderFrontSubstitutionsPanel:" target="Ady-hI-5gd" id="oku-mr-iSq"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="gPx-C9-uUO"/>
|
||||
<menuItem title="Smart Copy/Paste" id="9yt-4B-nSM">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="toggleSmartInsertDelete:" target="Ady-hI-5gd" id="3IJ-Se-DZD"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Smart Quotes" id="hQb-2v-fYv">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="toggleAutomaticQuoteSubstitution:" target="Ady-hI-5gd" id="ptq-xd-QOA"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Smart Dashes" id="rgM-f4-ycn">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="toggleAutomaticDashSubstitution:" target="Ady-hI-5gd" id="oCt-pO-9gS"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Smart Links" id="cwL-P1-jid">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="toggleAutomaticLinkDetection:" target="Ady-hI-5gd" id="Gip-E3-Fov"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Data Detectors" id="tRr-pd-1PS">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="toggleAutomaticDataDetection:" target="Ady-hI-5gd" id="R1I-Nq-Kbl"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Text Replacement" id="HFQ-gK-NFA">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="toggleAutomaticTextReplacement:" target="Ady-hI-5gd" id="DvP-Fe-Py6"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem title="Transformations" id="2oI-Rn-ZJC">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Transformations" id="c8a-y6-VQd">
|
||||
<items>
|
||||
<menuItem title="Make Upper Case" id="vmV-6d-7jI">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="uppercaseWord:" target="Ady-hI-5gd" id="sPh-Tk-edu"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Make Lower Case" id="d9M-CD-aMd">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="lowercaseWord:" target="Ady-hI-5gd" id="iUZ-b5-hil"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Capitalize" id="UEZ-Bs-lqG">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="capitalizeWord:" target="Ady-hI-5gd" id="26H-TL-nsh"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem title="Speech" id="xrE-MZ-jX0">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Speech" id="3rS-ZA-NoH">
|
||||
<items>
|
||||
<menuItem title="Start Speaking" id="Ynk-f8-cLZ">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="startSpeaking:" target="Ady-hI-5gd" id="654-Ng-kyl"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Stop Speaking" id="Oyz-dy-DGm">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="stopSpeaking:" target="Ady-hI-5gd" id="dX8-6p-jy9"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem title="Format" id="jxT-CU-nIS">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Format" id="GEO-Iw-cKr">
|
||||
<items>
|
||||
<menuItem title="Font" id="Gi5-1S-RQB">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Font" systemMenu="font" id="aXa-aM-Jaq">
|
||||
<items>
|
||||
<menuItem title="Show Fonts" keyEquivalent="t" id="Q5e-8K-NDq">
|
||||
<connections>
|
||||
<action selector="orderFrontFontPanel:" target="YLy-65-1bz" id="WHr-nq-2xA"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Bold" tag="2" keyEquivalent="b" id="GB9-OM-e27">
|
||||
<connections>
|
||||
<action selector="addFontTrait:" target="YLy-65-1bz" id="hqk-hr-sYV"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Italic" tag="1" keyEquivalent="i" id="Vjx-xi-njq">
|
||||
<connections>
|
||||
<action selector="addFontTrait:" target="YLy-65-1bz" id="IHV-OB-c03"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Underline" keyEquivalent="u" id="WRG-CD-K1S">
|
||||
<connections>
|
||||
<action selector="underline:" target="Ady-hI-5gd" id="FYS-2b-JAY"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="5gT-KC-WSO"/>
|
||||
<menuItem title="Bigger" tag="3" keyEquivalent="+" id="Ptp-SP-VEL">
|
||||
<connections>
|
||||
<action selector="modifyFont:" target="YLy-65-1bz" id="Uc7-di-UnL"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Smaller" tag="4" keyEquivalent="-" id="i1d-Er-qST">
|
||||
<connections>
|
||||
<action selector="modifyFont:" target="YLy-65-1bz" id="HcX-Lf-eNd"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="kx3-Dk-x3B"/>
|
||||
<menuItem title="Kern" id="jBQ-r6-VK2">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Kern" id="tlD-Oa-oAM">
|
||||
<items>
|
||||
<menuItem title="Use Default" id="GUa-eO-cwY">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="useStandardKerning:" target="Ady-hI-5gd" id="6dk-9l-Ckg"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Use None" id="cDB-IK-hbR">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="turnOffKerning:" target="Ady-hI-5gd" id="U8a-gz-Maa"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Tighten" id="46P-cB-AYj">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="tightenKerning:" target="Ady-hI-5gd" id="hr7-Nz-8ro"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Loosen" id="ogc-rX-tC1">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="loosenKerning:" target="Ady-hI-5gd" id="8i4-f9-FKE"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem title="Ligatures" id="o6e-r0-MWq">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Ligatures" id="w0m-vy-SC9">
|
||||
<items>
|
||||
<menuItem title="Use Default" id="agt-UL-0e3">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="useStandardLigatures:" target="Ady-hI-5gd" id="7uR-wd-Dx6"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Use None" id="J7y-lM-qPV">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="turnOffLigatures:" target="Ady-hI-5gd" id="iX2-gA-Ilz"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Use All" id="xQD-1f-W4t">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="useAllLigatures:" target="Ady-hI-5gd" id="KcB-kA-TuK"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem title="Baseline" id="OaQ-X3-Vso">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Baseline" id="ijk-EB-dga">
|
||||
<items>
|
||||
<menuItem title="Use Default" id="3Om-Ey-2VK">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="unscript:" target="Ady-hI-5gd" id="0vZ-95-Ywn"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Superscript" id="Rqc-34-cIF">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="superscript:" target="Ady-hI-5gd" id="3qV-fo-wpU"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Subscript" id="I0S-gh-46l">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="subscript:" target="Ady-hI-5gd" id="Q6W-4W-IGz"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Raise" id="2h7-ER-AoG">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="raiseBaseline:" target="Ady-hI-5gd" id="4sk-31-7Q9"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Lower" id="1tx-W0-xDw">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="lowerBaseline:" target="Ady-hI-5gd" id="OF1-bc-KW4"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="Ndw-q3-faq"/>
|
||||
<menuItem title="Show Colors" keyEquivalent="C" id="bgn-CT-cEk">
|
||||
<connections>
|
||||
<action selector="orderFrontColorPanel:" target="Ady-hI-5gd" id="mSX-Xz-DV3"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="iMs-zA-UFJ"/>
|
||||
<menuItem title="Copy Style" keyEquivalent="c" id="5Vv-lz-BsD">
|
||||
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
|
||||
<connections>
|
||||
<action selector="copyFont:" target="Ady-hI-5gd" id="GJO-xA-L4q"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Paste Style" keyEquivalent="v" id="vKC-jM-MkH">
|
||||
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
|
||||
<connections>
|
||||
<action selector="pasteFont:" target="Ady-hI-5gd" id="JfD-CL-leO"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem title="Text" id="Fal-I4-PZk">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Text" id="d9c-me-L2H">
|
||||
<items>
|
||||
<menuItem title="Align Left" keyEquivalent="{" id="ZM1-6Q-yy1">
|
||||
<connections>
|
||||
<action selector="alignLeft:" target="Ady-hI-5gd" id="zUv-R1-uAa"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Center" keyEquivalent="|" id="VIY-Ag-zcb">
|
||||
<connections>
|
||||
<action selector="alignCenter:" target="Ady-hI-5gd" id="spX-mk-kcS"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Justify" id="J5U-5w-g23">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="alignJustified:" target="Ady-hI-5gd" id="ljL-7U-jND"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Align Right" keyEquivalent="}" id="wb2-vD-lq4">
|
||||
<connections>
|
||||
<action selector="alignRight:" target="Ady-hI-5gd" id="r48-bG-YeY"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="4s2-GY-VfK"/>
|
||||
<menuItem title="Writing Direction" id="H1b-Si-o9J">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Writing Direction" id="8mr-sm-Yjd">
|
||||
<items>
|
||||
<menuItem title="Paragraph" enabled="NO" id="ZvO-Gk-QUH">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
</menuItem>
|
||||
<menuItem id="YGs-j5-SAR">
|
||||
<string key="title"> Default</string>
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="makeBaseWritingDirectionNatural:" target="Ady-hI-5gd" id="qtV-5e-UBP"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem id="Lbh-J2-qVU">
|
||||
<string key="title"> Left to Right</string>
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="makeBaseWritingDirectionLeftToRight:" target="Ady-hI-5gd" id="S0X-9S-QSf"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem id="jFq-tB-4Kx">
|
||||
<string key="title"> Right to Left</string>
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="makeBaseWritingDirectionRightToLeft:" target="Ady-hI-5gd" id="5fk-qB-AqJ"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="swp-gr-a21"/>
|
||||
<menuItem title="Selection" enabled="NO" id="cqv-fj-IhA">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
</menuItem>
|
||||
<menuItem id="Nop-cj-93Q">
|
||||
<string key="title"> Default</string>
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="makeTextWritingDirectionNatural:" target="Ady-hI-5gd" id="lPI-Se-ZHp"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem id="BgM-ve-c93">
|
||||
<string key="title"> Left to Right</string>
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="makeTextWritingDirectionLeftToRight:" target="Ady-hI-5gd" id="caW-Bv-w94"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem id="RB4-Sm-HuC">
|
||||
<string key="title"> Right to Left</string>
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="makeTextWritingDirectionRightToLeft:" target="Ady-hI-5gd" id="EXD-6r-ZUu"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="fKy-g9-1gm"/>
|
||||
<menuItem title="Show Ruler" id="vLm-3I-IUL">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="toggleRuler:" target="Ady-hI-5gd" id="FOx-HJ-KwY"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Copy Ruler" keyEquivalent="c" id="MkV-Pr-PK5">
|
||||
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
|
||||
<connections>
|
||||
<action selector="copyRuler:" target="Ady-hI-5gd" id="71i-fW-3W2"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Paste Ruler" keyEquivalent="v" id="LVM-kO-fVI">
|
||||
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
|
||||
<connections>
|
||||
<action selector="pasteRuler:" target="Ady-hI-5gd" id="cSh-wd-qM2"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem title="View" id="H8h-7b-M4v">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="View" id="HyV-fh-RgO">
|
||||
<items>
|
||||
<menuItem title="Show Toolbar" keyEquivalent="t" id="snW-S8-Cw5">
|
||||
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
|
||||
<connections>
|
||||
<action selector="toggleToolbarShown:" target="Ady-hI-5gd" id="BXY-wc-z0C"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Customize Toolbar…" id="1UK-8n-QPP">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="runToolbarCustomizationPalette:" target="Ady-hI-5gd" id="pQI-g3-MTW"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="hB3-LF-h0Y"/>
|
||||
<menuItem title="Show Sidebar" keyEquivalent="s" id="kIP-vf-haE">
|
||||
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
|
||||
<connections>
|
||||
<action selector="toggleSidebar:" target="Ady-hI-5gd" id="iwa-gc-5KM"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Enter Full Screen" keyEquivalent="f" id="4J7-dP-txa">
|
||||
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
|
||||
<connections>
|
||||
<action selector="toggleFullScreen:" target="Ady-hI-5gd" id="dU3-MA-1Rq"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem title="Window" id="aUF-d1-5bR">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Window" systemMenu="window" id="Td7-aD-5lo">
|
||||
<items>
|
||||
<menuItem title="Minimize" keyEquivalent="m" id="OY7-WF-poV">
|
||||
<connections>
|
||||
<action selector="performMiniaturize:" target="Ady-hI-5gd" id="VwT-WD-YPe"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Zoom" id="R4o-n2-Eq4">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="performZoom:" target="Ady-hI-5gd" id="DIl-cC-cCs"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="eu3-7i-yIM"/>
|
||||
<menuItem title="Bring All to Front" id="LE2-aR-0XJ">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="arrangeInFront:" target="Ady-hI-5gd" id="DRN-fu-gQh"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem title="Help" id="wpr-3q-Mcd">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Help" systemMenu="help" id="F2S-fz-NVQ">
|
||||
<items>
|
||||
<menuItem title="SwiftMnist Help" keyEquivalent="?" id="FKE-Sm-Kum">
|
||||
<connections>
|
||||
<action selector="showHelp:" target="Ady-hI-5gd" id="y7X-2Q-9no"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
<connections>
|
||||
<outlet property="delegate" destination="Voe-Tx-rLC" id="PrD-fu-P6m"/>
|
||||
</connections>
|
||||
</application>
|
||||
<customObject id="Voe-Tx-rLC" customClass="AppDelegate" customModuleProvider="target"/>
|
||||
<customObject id="YLy-65-1bz" customClass="NSFontManager"/>
|
||||
<customObject id="Ady-hI-5gd" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="75" y="0.0"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
</document>
|
|
@ -1,173 +0,0 @@
|
|||
//
|
||||
// ContentView.swift
|
||||
// SwiftMnist
|
||||
//
|
||||
// Created by Miguel de Icaza on 6/1/20.
|
||||
// Copyright © 2020 Miguel de Icaza. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import CoreGraphics
|
||||
|
||||
|
||||
let factor:CGFloat = 6.0
|
||||
|
||||
struct Drawing {
|
||||
var points: [CGPoint] = [CGPoint]()
|
||||
}
|
||||
|
||||
struct DrawingPad: View {
|
||||
@Binding var currentDrawing: Drawing
|
||||
@Binding var drawings: [Drawing]
|
||||
@Binding var guesses: [Float]
|
||||
@Binding var result: Int
|
||||
|
||||
func updateGuess ()
|
||||
{
|
||||
// Render the vector to a bitmap of ours
|
||||
let ctx = CGContext.init(data: nil, width: 28, height: 28, bitsPerComponent: 8, bytesPerRow: 28*4, space: CGColorSpaceCreateDeviceRGB(), bitmapInfo: CGImageAlphaInfo.premultipliedFirst.rawValue)!
|
||||
ctx.translateBy(x: 0, y: 28)
|
||||
ctx.scaleBy(x: 1, y: -1)
|
||||
|
||||
ctx.setStrokeColor(NSColor.black.cgColor)
|
||||
for drawing in self.drawings {
|
||||
if drawing.points.count < 2 {
|
||||
continue
|
||||
}
|
||||
|
||||
let mapped = drawing.points.map { CGPoint (x: $0.x/factor, y: $0.y/factor) }
|
||||
ctx.beginPath()
|
||||
ctx.move(to: mapped [0])
|
||||
for i in 1..<mapped.count {
|
||||
ctx.addLine(to: mapped [i])
|
||||
}
|
||||
ctx.strokePath()
|
||||
}
|
||||
// Turn bitmap into float array
|
||||
var size: Int = 0
|
||||
let mnist_input = mnist_get_input_image(mnist, &size)!
|
||||
let ctxdata = ctx.data!.bindMemory(to: Int32.self, capacity: 28*28*4)
|
||||
var midx = 0
|
||||
print ("-------------------")
|
||||
for _ in 0..<28 {
|
||||
var r = ""
|
||||
for _ in 0..<28 {
|
||||
mnist_input [midx] = ctxdata [midx] == 0 ? 0.0 : 1.0
|
||||
r += ctxdata [midx] == 0 ? "." : "x"
|
||||
midx += 1
|
||||
}
|
||||
print ("\(r)\n")
|
||||
}
|
||||
result = mnist_run (mnist)
|
||||
print ("Result: \(result)")
|
||||
let mnist_guesses = mnist_get_results(mnist, &size)!
|
||||
for i in 0..<10 {
|
||||
guesses [i] = mnist_guesses [i]
|
||||
}
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
GeometryReader { geometry in
|
||||
Path { path in
|
||||
for drawing in self.drawings {
|
||||
self.add(drawing: drawing, toPath: &path)
|
||||
}
|
||||
self.add(drawing: self.currentDrawing, toPath: &path)
|
||||
}
|
||||
.stroke(Color.black, lineWidth: 3)
|
||||
.background(Color(white: 0.95))
|
||||
.gesture(
|
||||
DragGesture(minimumDistance: 0.1)
|
||||
.onChanged({ (value) in
|
||||
let currentPoint = value.location
|
||||
if currentPoint.y >= 0
|
||||
&& currentPoint.y < geometry.size.height {
|
||||
self.currentDrawing.points.append(currentPoint)
|
||||
}
|
||||
self.updateGuess ()
|
||||
})
|
||||
.onEnded({ (value) in
|
||||
self.drawings.append(self.currentDrawing)
|
||||
self.currentDrawing = Drawing()
|
||||
self.updateGuess ()
|
||||
})
|
||||
)
|
||||
}
|
||||
.frame(maxHeight: .infinity)
|
||||
}
|
||||
|
||||
private func add(drawing: Drawing, toPath path: inout Path) {
|
||||
let points = drawing.points
|
||||
if points.count > 1 {
|
||||
for i in 0..<points.count-1 {
|
||||
let current = points[i]
|
||||
let next = points[i+1]
|
||||
path.move(to: current)
|
||||
path.addLine(to: next)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
struct ContentView: View {
|
||||
@State private var currentDrawing: Drawing = Drawing()
|
||||
@State private var drawings: [Drawing] = [Drawing]()
|
||||
@State private var lineWidth: CGFloat = 3.0
|
||||
@State var guesses: [Float] = Array.init(repeating: 0, count: 10)
|
||||
@State var result: Int = 0
|
||||
|
||||
|
||||
func fmt (_ idx: Int)->String
|
||||
{
|
||||
let v = guesses [idx]
|
||||
return (v >= 0 ? " " : "") + String(format: "%2.2f", v)
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .center) {
|
||||
Text("Draw a digit")
|
||||
.font(.largeTitle)
|
||||
HStack {
|
||||
DrawingPad(
|
||||
currentDrawing: $currentDrawing,
|
||||
drawings: $drawings,
|
||||
guesses: $guesses,
|
||||
result: $result)
|
||||
.frame(width: 24*factor, height: 24*factor)
|
||||
.border(Color.black)
|
||||
VStack {
|
||||
ForEach (guesses.indices) { idx in
|
||||
HStack {
|
||||
Text ("x")
|
||||
Rectangle ()
|
||||
.offset (x: 10)
|
||||
.border(Color.red)
|
||||
.frame(width:40, height: 20)
|
||||
}
|
||||
}
|
||||
}.frame (width: 120)
|
||||
VStack (alignment: .leading) {
|
||||
Text ("Result: \(result)")
|
||||
ForEach (guesses.indices) { idx in
|
||||
|
||||
Text ("\(idx) -> \(self.fmt (idx))")
|
||||
|
||||
}
|
||||
}.frame(width: 120)
|
||||
}
|
||||
.font(.system(Font.TextStyle.body, design: .monospaced))
|
||||
|
||||
Button(action: {self.drawings = []; self.guesses = Array.init (repeating: 0, count: 10)}) {
|
||||
Text ("Clear")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct ContentView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
ContentView()
|
||||
}
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string></string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Copyright © 2020 Miguel de Icaza. All rights reserved.</string>
|
||||
<key>NSMainStoryboardFile</key>
|
||||
<string>Main</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string>NSApplication</string>
|
||||
<key>NSSupportsAutomaticTermination</key>
|
||||
<true/>
|
||||
<key>NSSupportsSuddenTermination</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
|
@ -1,73 +0,0 @@
|
|||
//
|
||||
// OnnxInterop.cpp
|
||||
// SwiftMnist
|
||||
//
|
||||
// Created by Miguel de Icaza on 6/1/20.
|
||||
// Copyright © 2020 Miguel de Icaza. All rights reserved.
|
||||
//
|
||||
#include <array>
|
||||
#include <onnxruntime_cxx_api.h>
|
||||
extern "C" {
|
||||
#include "SwiftMnist-Bridging-Header.h"
|
||||
}
|
||||
struct MNIST {
|
||||
MNIST() {
|
||||
auto memory_info = Ort::MemoryInfo::CreateCpu(OrtDeviceAllocator, OrtMemTypeCPU);
|
||||
input_tensor_ = Ort::Value::CreateTensor<float>(memory_info, input_image_.data(), input_image_.size(), input_shape_.data(), input_shape_.size());
|
||||
output_tensor_ = Ort::Value::CreateTensor<float>(memory_info, results_.data(), results_.size(), output_shape_.data(), output_shape_.size());
|
||||
}
|
||||
|
||||
std::ptrdiff_t 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<float, width_ * height_> input_image_{};
|
||||
std::array<float, 10> results_{};
|
||||
int64_t result_{0};
|
||||
|
||||
private:
|
||||
Ort::Env env;
|
||||
Ort::Session session_{env, "model.onnx", Ort::SessionOptions{nullptr}};
|
||||
|
||||
Ort::Value input_tensor_{nullptr};
|
||||
std::array<int64_t, 4> input_shape_{1, 1, width_, height_};
|
||||
|
||||
Ort::Value output_tensor_{nullptr};
|
||||
std::array<int64_t, 2> output_shape_{1, 10};
|
||||
};
|
||||
|
||||
mnist *mnist_new ()
|
||||
{
|
||||
return (mnist *) new MNIST();
|
||||
}
|
||||
|
||||
float *mnist_get_input_image (mnist *_mnist, size_t *out)
|
||||
{
|
||||
MNIST *mnist = (MNIST *) _mnist;
|
||||
*out = mnist->input_image_.size();
|
||||
return mnist->input_image_.data ();
|
||||
}
|
||||
|
||||
float *mnist_get_results (mnist *_mnist, size_t *out)
|
||||
{
|
||||
MNIST *mnist = (MNIST *) _mnist;
|
||||
*out = mnist->results_.size();
|
||||
return mnist->results_.data ();
|
||||
}
|
||||
|
||||
long mnist_run (mnist *_mnist)
|
||||
{
|
||||
MNIST *mnist = (MNIST *) _mnist;
|
||||
|
||||
mnist->Run();
|
||||
return mnist->result_;
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
//
|
||||
// OnnxInterop.hpp
|
||||
// SwiftMnist
|
||||
//
|
||||
// Created by Miguel de Icaza on 6/1/20.
|
||||
// Copyright © 2020 Miguel de Icaza. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef OnnxInterop_hpp
|
||||
#define OnnxInterop_hpp
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
typedef void *mnist;
|
||||
|
||||
mnist *mnist_new ();
|
||||
float *mnist_get_input_image (mnist *_mnist, size_t *out);
|
||||
float *mnist_get_results (mnist *_mnist, size_t *out);
|
||||
long mnist_run (mnist *_mnist);
|
||||
|
||||
#endif /* OnnxInterop_hpp */
|
|
@ -1,6 +0,0 @@
|
|||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
//
|
||||
// Use this file to import your target's public headers that you would like to expose to Swift.
|
||||
//
|
||||
|
||||
#include "OnnxInterop.hpp"
|
||||
typedef void *mnist;
|
||||
|
||||
|
||||
mnist *mnist_new ();
|
||||
float *mnist_get_input_image (mnist *_mnist, size_t *out);
|
||||
float *mnist_get_results (mnist *_mnist, size_t *out);
|
||||
long mnist_run (mnist *_mnist);
|
|
@ -1,10 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.app-sandbox</key>
|
||||
<true/>
|
||||
<key>com.apple.security.files.user-selected.read-only</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
Загрузка…
Ссылка в новой задаче