Skip to content

Commit 09f9380

Browse files
authored
Rewrite the fns_candy_style_transfer example so that it doesn't depends on libpng on Windows (#163)
1 parent 4999a7f commit 09f9380

File tree

12 files changed

+290
-160
lines changed

12 files changed

+290
-160
lines changed

.pipelines/OneBranch.Official.yml

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -83,20 +83,17 @@ extends:
8383
minor: '0'
8484
exclude_commit: true
8585

86-
- script: |
87-
set -e -x
88-
apt-get update
89-
apt-get install -y cmake gcc g++ libpng-dev libjpeg-turbo8-dev
90-
curl -O -L https://github.com/microsoft/onnxruntime/releases/download/v{{ parameters.ortversion }}/onnxruntime-linux-x64-{{ parameters.ortversion }}.tgz
91-
mkdir onnxruntimebin
92-
cd onnxruntimebin
93-
tar --strip=1 -zxvf ../onnxruntime-linux-x64-{{ parameters.ortversion }}.tgz
94-
displayName: Download onnxruntime
95-
workingDirectory: '$(Build.BinariesDirectory)'
86+
- task: Bash@3
87+
displayName: 'Download ONNX Runtime Binaries'
88+
inputs:
89+
targetType: filePath
90+
filePath: ci_build/download_ort_release_and_install_deps.sh
91+
arguments: '-i ${{ parameters.ortversion }}'
92+
workingDirectory: '$(Build.BinariesDirectory)'
9693

9794
- script: |
9895
set -e -x
99-
cmake $(Build.SourcesDirectory)/c_cxx -DCMAKE_BUILD_TYPE=Release -DONNXRUNTIME_ROOTDIR=$(Build.BinariesDirectory)/onnxruntimebin
96+
CFLAGS="-O2 -Wall -Wextra -Werror" CXXFLAGS="-O2 -Wall -Wextra -Werror" cmake $(Build.SourcesDirectory)/c_cxx -DCMAKE_BUILD_TYPE=Release -DONNXRUNTIME_ROOTDIR=$(Build.BinariesDirectory)/onnxruntimebin
10097
make -j$(nproc)
10198
displayName: build
10299
workingDirectory: '$(Build.BinariesDirectory)'
@@ -130,12 +127,15 @@ extends:
130127
@echo ##vso[task.setvariable variable=vslatest]%vslatest%
131128
@echo ##vso[task.setvariable variable=vsdevcmd]%vsdevcmd%
132129
displayName: 'locate vsdevcmd via vswhere'
133-
- script: |
134-
curl -L -o onnxruntime.zip https://github.com/microsoft/onnxruntime/releases/download/v{{ parameters.ortversion }}/onnxruntime-win-x64-{{ parameters.ortversion }}.zip
135-
7z x onnxruntime.zip
136-
move onnxruntime-win-x64-{{ parameters.ortversion }} onnxruntimebin
137-
workingDirectory: '$(Build.BinariesDirectory)'
138-
displayName: 'Install python modules'
130+
131+
- task: PowerShell@2
132+
displayName: 'Download ONNX Runtime'
133+
inputs:
134+
targetType: filePath
135+
filePath: ci_build/download_ort_release.ps1
136+
arguments: '${{ parameters.ortversion }}'
137+
workingDirectory: '$(Build.BinariesDirectory)'
138+
139139
#TODO: use the vsdevcmd variable
140140
- script: |
141141
call $(vsdevcmd)

.pipelines/OneBranch.PullRequest.yml

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -83,20 +83,17 @@ extends:
8383
minor: '0'
8484
exclude_commit: true
8585

86-
- script: |
87-
set -e -x
88-
apt-get update
89-
apt-get install -y cmake gcc g++ libpng-dev libjpeg-turbo8-dev
90-
curl -O -L https://github.com/microsoft/onnxruntime/releases/download/v{{ parameters.ortversion }}/onnxruntime-linux-x64-{{ parameters.ortversion }}.tgz
91-
mkdir onnxruntimebin
92-
cd onnxruntimebin
93-
tar --strip=1 -zxvf ../onnxruntime-linux-x64-{{ parameters.ortversion }}.tgz
94-
displayName: Download onnxruntime
95-
workingDirectory: '$(Build.BinariesDirectory)'
86+
- task: Bash@3
87+
displayName: 'Download ONNX Runtime Binaries'
88+
inputs:
89+
targetType: filePath
90+
filePath: ci_build/download_ort_release_and_install_deps.sh
91+
arguments: '-i ${{ parameters.ortversion }}'
92+
workingDirectory: '$(Build.BinariesDirectory)'
9693

9794
- script: |
9895
set -e -x
99-
cmake $(Build.SourcesDirectory)/c_cxx -DCMAKE_BUILD_TYPE=Release -DONNXRUNTIME_ROOTDIR=$(Build.BinariesDirectory)/onnxruntimebin
96+
CFLAGS="-O2 -Wall -Wextra -Werror" CXXFLAGS="-O2 -Wall -Wextra -Werror" cmake $(Build.SourcesDirectory)/c_cxx -DCMAKE_BUILD_TYPE=Release -DONNXRUNTIME_ROOTDIR=$(Build.BinariesDirectory)/onnxruntimebin
10097
make -j$(nproc)
10198
displayName: build
10299
workingDirectory: '$(Build.BinariesDirectory)'
@@ -130,12 +127,15 @@ extends:
130127
@echo ##vso[task.setvariable variable=vslatest]%vslatest%
131128
@echo ##vso[task.setvariable variable=vsdevcmd]%vsdevcmd%
132129
displayName: 'locate vsdevcmd via vswhere'
133-
- script: |
134-
curl -L -o onnxruntime.zip https://github.com/microsoft/onnxruntime/releases/download/v{{ parameters.ortversion }}/onnxruntime-win-x64-{{ parameters.ortversion }}.zip
135-
7z x onnxruntime.zip
136-
move onnxruntime-win-x64-{{ parameters.ortversion }} onnxruntimebin
137-
workingDirectory: '$(Build.BinariesDirectory)'
138-
displayName: 'Download onnxruntime binary'
130+
131+
- task: PowerShell@2
132+
displayName: 'Download ONNX Runtime'
133+
inputs:
134+
targetType: filePath
135+
filePath: ci_build/download_ort_release.ps1
136+
arguments: '${{ parameters.ortversion }}'
137+
workingDirectory: '$(Build.BinariesDirectory)'
138+
139139
#TODO: use the vsdevcmd variable
140140
- script: |
141141
call $(vsdevcmd)

c_cxx/CMakeLists.txt

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ option(onnxruntime_USE_NUPHAR "Build with Nuphar" OFF)
2121
option(onnxruntime_USE_TENSORRT "Build with TensorRT support" OFF)
2222
option(LIBPNG_ROOTDIR "libpng root dir")
2323
option(ONNXRUNTIME_ROOTDIR "onnxruntime root dir")
24+
include(FetchContent)
2425

2526
set(CMAKE_CXX_STANDARD 17)
2627

@@ -32,23 +33,35 @@ if(NOT ONNXRUNTIME_ROOTDIR)
3233
endif()
3334
endif()
3435

36+
if(WIN32)
37+
add_library(wil INTERFACE)
38+
39+
40+
FetchContent_Declare(
41+
microsoft_wil
42+
URL https://github.com/microsoft/wil/archive/refs/tags/v1.0.220914.1.zip
43+
)
44+
FetchContent_Populate(microsoft_wil)
45+
target_include_directories(wil INTERFACE ${microsoft_wil_SOURCE_DIR}/include)
46+
set(WIL_LIB wil)
47+
endif()
48+
3549
#TODO: we should only need one of them.
3650
include_directories("${ONNXRUNTIME_ROOTDIR}/include" "${ONNXRUNTIME_ROOTDIR}/include/onnxruntime/core/session")
3751
link_directories("${ONNXRUNTIME_ROOTDIR}/lib")
3852

39-
#if JPEG lib is available, we'll use it for image decoding, otherwise we'll use WIC
40-
find_package(JPEG)
41-
if(LIBPNG_ROOTDIR)
42-
set(PNG_FOUND true)
43-
if(WIN32)
44-
set(PNG_LIBRARIES debug libpng16_d optimized libpng16)
45-
else()
46-
set(PNG_LIBRARIES png16)
47-
endif()
48-
set(PNG_INCLUDE_DIRS "${LIBPNG_ROOTDIR}/include")
49-
set(PNG_LIBDIR "${LIBPNG_ROOTDIR}/lib")
50-
else()
51-
find_package(PNG)
53+
# On Linux the samples use libjpeg and libpng for decoding images.
54+
# On Windows they use Windows Image Component(WIC)
55+
if(NOT WIN32)
56+
find_package(JPEG)
57+
if(LIBPNG_ROOTDIR)
58+
set(PNG_FOUND true)
59+
set(PNG_LIBRARIES png16)
60+
set(PNG_INCLUDE_DIRS "${LIBPNG_ROOTDIR}/include")
61+
set(PNG_LIBDIR "${LIBPNG_ROOTDIR}/lib")
62+
else()
63+
find_package(PNG)
64+
endif()
5265
endif()
5366

5467
if(onnxruntime_USE_CUDA)
@@ -80,7 +93,7 @@ if(WIN32)
8093
add_subdirectory(MNIST)
8194
endif()
8295
add_subdirectory(squeezenet)
83-
if(PNG_FOUND)
96+
if(WIN32 OR PNG_FOUND)
8497
add_subdirectory(fns_candy_style_transfer)
8598
endif()
8699
#missing experimental_onnxruntime_cxx_api.h
Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
11
# Copyright (c) Microsoft Corporation. All rights reserved.
22
# Licensed under the MIT License.
33

4-
add_executable(fns_candy_style_transfer "fns_candy_style_transfer.c")
4+
add_executable(fns_candy_style_transfer "fns_candy_style_transfer.c" "image_file.h")
5+
if(WIN32)
6+
target_sources(fns_candy_style_transfer PRIVATE image_file_wic.cc)
7+
else()
8+
target_sources(fns_candy_style_transfer PRIVATE image_file_libpng.c)
9+
endif()
510
target_include_directories(fns_candy_style_transfer PRIVATE ${PROJECT_SOURCE_DIR}/include ${PNG_INCLUDE_DIRS})
6-
target_link_libraries(fns_candy_style_transfer PRIVATE onnxruntime ${PNG_LIBRARIES})
11+
target_link_libraries(fns_candy_style_transfer PRIVATE onnxruntime ${PNG_LIBRARIES} ${WIL_LIB})
712
if(PNG_LIBDIR)
813
target_link_directories(fns_candy_style_transfer PRIVATE ${PNG_LIBDIR})
914
endif()

c_cxx/fns_candy_style_transfer/fns_candy_style_transfer.c

Lines changed: 15 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
// Copyright (c) Microsoft Corporation. All rights reserved.
22
// Licensed under the MIT License.
33
#include <assert.h>
4-
#include <png.h>
54
#include <stdio.h>
65

76
#include "onnxruntime_c_api.h"
@@ -11,6 +10,7 @@
1110
#endif
1211
#include <objbase.h>
1312
#endif
13+
#include "image_file.h"
1414

1515
#ifdef _WIN32
1616
#define tcscmp wcscmp
@@ -39,10 +39,11 @@ const OrtApi* g_ort = NULL;
3939
* \param output A float array. should be freed by caller after use
4040
* \param output_count Array length of the `output` param
4141
*/
42-
static void hwc_to_chw(const png_byte* input, size_t h, size_t w, float** output, size_t* output_count) {
42+
void hwc_to_chw(const uint8_t* input, size_t h, size_t w, float** output, size_t* output_count) {
4343
size_t stride = h * w;
4444
*output_count = stride * 3;
4545
float* output_data = (float*)malloc(*output_count * sizeof(float));
46+
assert(output_data != NULL);
4647
for (size_t i = 0; i != stride; ++i) {
4748
for (size_t c = 0; c != 3; ++c) {
4849
output_data[c * stride + i] = input[i * 3 + c];
@@ -58,121 +59,30 @@ static void hwc_to_chw(const png_byte* input, size_t h, size_t w, float** output
5859
* \param w image width
5960
* \param output A byte array. should be freed by caller after use
6061
*/
61-
static void chw_to_hwc(const float* input, size_t h, size_t w, png_bytep* output) {
62+
static void chw_to_hwc(const float* input, size_t h, size_t w, uint8_t** output) {
6263
size_t stride = h * w;
63-
png_bytep output_data = (png_bytep)malloc(stride * 3);
64-
for (int c = 0; c != 3; ++c) {
64+
uint8_t* output_data = (uint8_t*)malloc(stride * 3);
65+
assert(output_data != NULL);
66+
for (size_t c = 0; c != 3; ++c) {
6567
size_t t = c * stride;
6668
for (size_t i = 0; i != stride; ++i) {
6769
float f = input[t + i];
6870
if (f < 0.f || f > 255.0f) f = 0;
69-
output_data[i * 3 + c] = (png_byte)f;
71+
output_data[i * 3 + c] = (uint8_t)f;
7072
}
7173
}
7274
*output = output_data;
7375
}
7476

75-
/**
76-
* \param out should be freed by caller after use
77-
* \param output_count Array length of the `out` param
78-
*/
79-
static int read_png_file(const char* input_file, size_t* height, size_t* width, float** out, size_t* output_count) {
80-
png_image image; /* The control structure used by libpng */
81-
/* Initialize the 'png_image' structure. */
82-
memset(&image, 0, (sizeof image));
83-
image.version = PNG_IMAGE_VERSION;
84-
if (png_image_begin_read_from_file(&image, input_file) == 0) {
85-
return -1;
86-
}
87-
png_bytep buffer;
88-
image.format = PNG_FORMAT_BGR;
89-
size_t input_data_length = PNG_IMAGE_SIZE(image);
90-
if (input_data_length != 720 * 720 * 3) {
91-
printf("input_data_length:%zd\n", input_data_length);
92-
return -1;
93-
}
94-
buffer = (png_bytep)malloc(input_data_length);
95-
memset(buffer, 0, input_data_length);
96-
if (png_image_finish_read(&image, NULL /*background*/, buffer, 0 /*row_stride*/, NULL /*colormap*/) == 0) {
97-
return -1;
98-
}
99-
hwc_to_chw(buffer, image.height, image.width, out, output_count);
100-
free(buffer);
101-
*width = image.width;
102-
*height = image.height;
103-
return 0;
104-
}
105-
106-
/**
107-
* \param tensor should be a float tensor in [N,C,H,W] format
108-
*/
109-
static int write_tensor_to_png_file(OrtValue* tensor, const char* output_file) {
110-
struct OrtTensorTypeAndShapeInfo* shape_info;
111-
ORT_ABORT_ON_ERROR(g_ort->GetTensorTypeAndShape(tensor, &shape_info));
112-
size_t dim_count;
113-
ORT_ABORT_ON_ERROR(g_ort->GetDimensionsCount(shape_info, &dim_count));
114-
if (dim_count != 4) {
115-
printf("output tensor must have 4 dimensions");
116-
return -1;
117-
}
118-
int64_t dims[4];
119-
ORT_ABORT_ON_ERROR(g_ort->GetDimensions(shape_info, dims, sizeof(dims) / sizeof(dims[0])));
120-
if (dims[0] != 1 || dims[1] != 3) {
121-
printf("output tensor shape error");
122-
return -1;
123-
}
124-
float* f;
125-
ORT_ABORT_ON_ERROR(g_ort->GetTensorMutableData(tensor, (void**)&f));
126-
png_bytep model_output_bytes;
127-
png_image image;
128-
memset(&image, 0, (sizeof image));
129-
image.version = PNG_IMAGE_VERSION;
130-
image.format = PNG_FORMAT_BGR;
131-
image.height = (png_uint_32)dims[2];
132-
image.width = (png_uint_32)dims[3];
133-
chw_to_hwc(f, image.height, image.width, &model_output_bytes);
134-
int ret = 0;
135-
if (png_image_write_to_file(&image, output_file, 0 /*convert_to_8bit*/, model_output_bytes, 0 /*row_stride*/,
136-
NULL /*colormap*/) == 0) {
137-
printf("write to '%s' failed:%s\n", output_file, image.message);
138-
ret = -1;
139-
}
140-
free(model_output_bytes);
141-
return ret;
142-
}
143-
14477
static void usage() { printf("usage: <model_path> <input_file> <output_file> [cpu|cuda|dml] \n"); }
14578

146-
#ifdef _WIN32
147-
static char* convert_string(const wchar_t* input) {
148-
size_t src_len = wcslen(input) + 1;
149-
if (src_len > INT_MAX) {
150-
printf("size overflow\n");
151-
abort();
152-
}
153-
const int len = WideCharToMultiByte(CP_ACP, 0, input, (int)src_len, NULL, 0, NULL, NULL);
154-
assert(len > 0);
155-
char* ret = (char*)malloc(len);
156-
assert(ret != NULL);
157-
const int r = WideCharToMultiByte(CP_ACP, 0, input, (int)src_len, ret, len, NULL, NULL);
158-
assert(len == r);
159-
return ret;
160-
}
161-
#endif
162-
16379
int run_inference(OrtSession* session, const ORTCHAR_T* input_file, const ORTCHAR_T* output_file) {
16480
size_t input_height;
16581
size_t input_width;
16682
float* model_input;
16783
size_t model_input_ele_count;
168-
#ifdef _WIN32
169-
const char* output_file_p = convert_string(output_file);
170-
const char* input_file_p = convert_string(input_file);
171-
#else
172-
const char* output_file_p = output_file;
173-
const char* input_file_p = input_file;
174-
#endif
175-
if (read_png_file(input_file_p, &input_height, &input_width, &model_input, &model_input_ele_count) != 0) {
84+
85+
if (read_image_file(input_file, &input_height, &input_width, &model_input, &model_input_ele_count) != 0) {
17686
return -1;
17787
}
17888
if (input_height != 720 || input_width != 720) {
@@ -204,16 +114,16 @@ int run_inference(OrtSession* session, const ORTCHAR_T* input_file, const ORTCHA
204114
ORT_ABORT_ON_ERROR(g_ort->IsTensor(output_tensor, &is_tensor));
205115
assert(is_tensor);
206116
int ret = 0;
207-
if (write_tensor_to_png_file(output_tensor, output_file_p) != 0) {
117+
float* output_tensor_data = NULL;
118+
ORT_ABORT_ON_ERROR(g_ort->GetTensorMutableData(output_tensor, (void**)&output_tensor_data));
119+
uint8_t* output_image_data = NULL;
120+
chw_to_hwc(output_tensor_data, 720, 720, &output_image_data);
121+
if (write_image_file(output_image_data, 720, 720, output_file) != 0) {
208122
ret = -1;
209123
}
210124
g_ort->ReleaseValue(output_tensor);
211125
g_ort->ReleaseValue(input_tensor);
212126
free(model_input);
213-
#ifdef _WIN32
214-
free(input_file_p);
215-
free(output_file_p);
216-
#endif // _WIN32
217127
return ret;
218128
}
219129

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#pragma once
2+
#include "onnxruntime_c_api.h"
3+
#ifdef __cplusplus
4+
extern "C" {
5+
#endif
6+
/**
7+
* \param out should be freed by caller after use
8+
* \param output_count Array length of the `out` param
9+
*/
10+
int read_image_file(_In_z_ const ORTCHAR_T* input_file, _Out_ size_t* height, _Out_ size_t* width, _Outptr_ float** out,
11+
_Out_ size_t* output_count);
12+
13+
14+
int write_image_file(_In_ uint8_t* model_output_bytes, unsigned int height,
15+
unsigned int width, _In_z_ const ORTCHAR_T* output_file);
16+
17+
/**
18+
* convert input from HWC format to CHW format
19+
* \param input A single image. The byte array has length of 3*h*w
20+
* \param h image height
21+
* \param w image width
22+
* \param output A float array. should be freed by caller after use
23+
* \param output_count Array length of the `output` param
24+
*/
25+
void hwc_to_chw(const uint8_t* input, size_t h, size_t w, float** output, size_t* output_count);
26+
#ifdef __cplusplus
27+
}
28+
#endif

0 commit comments

Comments
 (0)