[WIP] Port image tests from WAI (#3365)
* Copy image tests from ADO * wip * Port tests to googletest * Add FNS-Candy license * Add missing collaterals * Remove brand images * Fix typos * Use PrepareModelSessionBinding in MnistImageTest * Fix typos
|
|
@ -83,6 +83,17 @@ function(get_winml_test_concurrency_src
|
|||
set(${output_winml_test_concurrency_src} ${winml_test_concurrency_src} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(get_winml_test_image_src
|
||||
winml_test_src_path
|
||||
output_winml_test_image_src
|
||||
)
|
||||
if (onnxruntime_USE_DML)
|
||||
set(${output_winml_test_scenario_libs} "onnxruntime_providers_dml" PARENT_SCOPE)
|
||||
endif()
|
||||
file(GLOB winml_test_image_src CONFIGURE_DEPENDS "${winml_test_src_path}/image/*.cpp")
|
||||
set(${output_winml_test_image_src} ${winml_test_image_src} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
file(GLOB winml_test_common_src CONFIGURE_DEPENDS "${WINML_TEST_SRC_DIR}/common/*.cpp")
|
||||
add_library(winml_test_common STATIC ${winml_test_common_src})
|
||||
add_dependencies(winml_test_common
|
||||
|
|
@ -127,6 +138,18 @@ endif()
|
|||
# on dev machines but not on the aiinfra agent pool
|
||||
target_link_options(winml_test_scenario PRIVATE /ignore:4199)
|
||||
|
||||
get_winml_test_image_src(${WINML_TEST_SRC_DIR} winml_test_image_src winml_test_image_libs)
|
||||
add_winml_test(
|
||||
TARGET winml_test_image
|
||||
SOURCES ${winml_test_image_src}
|
||||
LIBS winml_test_common delayimp.lib ${winml_test_image_libs}
|
||||
)
|
||||
target_precompiled_header(winml_test_image testPch.h)
|
||||
|
||||
target_link_options(winml_test_image PRIVATE /DELAYLOAD:d3d12.dll /DELAYLOAD:api-ms-win-core-file-l1-2-2.dll /DELAYLOAD:api-ms-win-core-synch-l1-2-1.dll)
|
||||
if (EXISTS ${dxcore_header})
|
||||
target_link_options(winml_test_image PRIVATE /DELAYLOAD:ext-ms-win-dxcore-l1-*.dll)
|
||||
endif()
|
||||
|
||||
get_winml_test_concurrency_src(${WINML_TEST_SRC_DIR} winml_test_concurrency_src)
|
||||
add_winml_test(
|
||||
|
|
@ -155,9 +178,15 @@ function(add_winml_collateral source)
|
|||
endfunction()
|
||||
|
||||
add_winml_collateral("${WINML_TEST_SRC_DIR}/api/models/*.onnx")
|
||||
add_winml_collateral("${WINML_TEST_SRC_DIR}/collateral/images/*.jpg")
|
||||
add_winml_collateral("${WINML_TEST_SRC_DIR}/collateral/images/*.png")
|
||||
add_winml_collateral("${WINML_TEST_SRC_DIR}/collateral/models/*.onnx")
|
||||
add_winml_collateral("${WINML_TEST_SRC_DIR}/common/testdata/squeezenet/*")
|
||||
add_winml_collateral("${WINML_TEST_SRC_DIR}/image/images/*.jpg")
|
||||
add_winml_collateral("${WINML_TEST_SRC_DIR}/image/images/*.png")
|
||||
add_winml_collateral("${WINML_TEST_SRC_DIR}/image/groundTruth/*.jpg")
|
||||
add_winml_collateral("${WINML_TEST_SRC_DIR}/image/groundTruth/*.png")
|
||||
add_winml_collateral("${WINML_TEST_SRC_DIR}/image/models/*.onnx")
|
||||
add_winml_collateral("${WINML_TEST_SRC_DIR}/scenario/cppwinrt/*.onnx")
|
||||
add_winml_collateral("${WINML_TEST_SRC_DIR}/scenario/models/*.onnx")
|
||||
add_winml_collateral("${REPO_ROOT}/onnxruntime/test/testdata/sequence_length.onnx")
|
||||
|
|
|
|||
BIN
winml/test/collateral/images/1080.jpg
Normal file
|
After Width: | Height: | Size: 262 KiB |
BIN
winml/test/collateral/images/big.png
Normal file
|
After Width: | Height: | Size: 7.9 KiB |
BIN
winml/test/collateral/images/horizontal-crop.png
Normal file
|
After Width: | Height: | Size: 470 B |
BIN
winml/test/collateral/images/small.png
Normal file
|
After Width: | Height: | Size: 201 B |
BIN
winml/test/collateral/images/vertical-crop.png
Normal file
|
After Width: | Height: | Size: 226 B |
4
winml/test/collateral/models/README.md
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
The FNS_Candy machine learning models here are used with permission from Justin Johnson.
|
||||
For additional information on these models, refer to:
|
||||
* [Fast-neural-style GitHub repo](https://github.com/jcjohnson/fast-neural-style)
|
||||
* [Perceptual Losses for Real-Time Style Transfer and Super-Resolution](https://cs.stanford.edu/people/jcjohns/papers/eccv16/JohnsonECCV16.pdf)
|
||||
BIN
winml/test/collateral/models/fns-candy.onnx
Normal file
127
winml/test/image/README.md
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
|
||||
# Overview
|
||||
|
||||
ImageTests are to test all kinds of scenarios that are related to images.
|
||||
|
||||
# Purpose
|
||||
|
||||
To make image related tests data driven and increase test code coverage.
|
||||
|
||||
# Image Test Data Table
|
||||
|
||||
|Table Name | Device |EvaluationStrategies|Models |Input Files |Output Binding Strategies|Input Image Sources |InputPixelFormats|
|
||||
|--------------|--------------|--------------------|---------------------------|-----------------|-------------------------|---------------------|-----------------|
|
||||
|Table Rows |GPU |Async |fns-candy_Bgr8_freeDimInput|fish_720.png |Bound |FromImageFeatureValue|Bgra8
|
||||
| |CPU |Sync |fns-candy_Bgr8 |fish_720_Gray.png|Unbound |FromVideoFrame |Rgba8
|
||||
| | | |fns-candy_Rgb8 | | |FromCPUResource |Gray8
|
||||
| | | |fns-candy_tensor | | |FromGPUResource |
|
||||
|
||||
# Test Coverage
|
||||
|
||||
### 1. Devices
|
||||
|
||||
> Devices the Test uses.
|
||||
* GPU
|
||||
* CPU
|
||||
|
||||
### 2. Evaluation Strategy
|
||||
> The strategy to use during evalution.
|
||||
|
||||
* EvalutateAsync
|
||||
* EvaluateSync
|
||||
* MultipleEvaluations (**TODO**)
|
||||
* SwapChain (**TODO**)
|
||||
* Chain (**TODO**)
|
||||
|
||||
### 3. Output Binding Strategy
|
||||
|
||||
* Bound
|
||||
```
|
||||
VideoFrame outputimage(BitmapPixelFormat::Rgba8, outputtensorshape.GetAt(3), outputtensorshape.GetAt(2));
|
||||
ImageFeatureValue outputTensor = ImageFeatureValue::CreateFromVideoFrame(outputimage);
|
||||
```
|
||||
|
||||
* Unbound
|
||||
|
||||
### 4. Models
|
||||
> Des: Currently, we are using four models, FNS-candy_Bgr8_freeDimInput, FNS-candy_Bgr8, FNS-candy_Rgb8, and FNS-candy_Tensor. We would try to get more models to cover the following listed cases.
|
||||
|
||||
a. [Input Image Metadata](https://github.com/onnx/onnx/blob/master/docs/MetadataProps.md)
|
||||
|
||||
1. No metadata provided (**TODO**)
|
||||
2. BitmapPixelFormat
|
||||
|
||||
* Bgr8
|
||||
* Rgb8
|
||||
* Gray8
|
||||
* Bgra8 (**TODO**, not supported in RS5)
|
||||
* Rgba8 (**TODO**, not supported in RS5)
|
||||
* yuv (**TODO**, not in ONNX1.2.2 spec)
|
||||
|
||||
* ColorSpaceGamma
|
||||
* sRGB
|
||||
* Linear (**TODO**, not supported in RS5)
|
||||
|
||||
* NominalPixelRange
|
||||
* NominalRange_0_255
|
||||
* Normalized_0_1 (No such model)
|
||||
* Normalized_1_1 (No such model)
|
||||
* NominalRange_16_235 (No such model)
|
||||
|
||||
b. Model Input Output Type
|
||||
* free dimension
|
||||
* image : corresponding to ImageFeatureDescriptor
|
||||
* tensor : corresponding to TensorFeatureDescriptor
|
||||
|
||||
c. Models we are using.
|
||||
|
||||
* Model takes Gray8 image as input and also output image (**TODO**, MNIST outputs tensor)
|
||||
* FNS-candy_Bgr8_freeDimInput
|
||||
* FNS-candy_Bgr8
|
||||
* FNS-candy_Rgb8
|
||||
* FNS-candy_Tensor
|
||||
|
||||
|
||||
### 5. Input Image Files
|
||||
|
||||
* fish_720.png
|
||||
* fish_720_gray.png
|
||||
|
||||
### 6. Input
|
||||
|
||||
a. input image source
|
||||
|
||||
* FromImageFeatureValue
|
||||
```
|
||||
VideoFrame frame = VideoFrame::CreateWithSoftwareBitmap(softwareBitmap);
|
||||
VERIFY_NO_THROW(m_modelBinding.Bind(inputFeature.Current().Name(), frame));
|
||||
```
|
||||
|
||||
* FromVideoFrame
|
||||
```
|
||||
VideoFrame frame = VideoFrame::CreateWithSoftwareBitmap(softwareBitmap);
|
||||
VERIFY_NO_THROW(m_modelBinding.Bind(inputFeature.Current().Name(), frame));
|
||||
```
|
||||
|
||||
* FromGPUResource
|
||||
> Bind input from GPU Resources as described in this [PR](https://mscodehub.visualstudio.com/_git/WindowsAI/pullrequest/4542?_a=overview)
|
||||
|
||||
* FromCPUResource
|
||||
> Bind input from GPU Resources as described in this [PR](https://mscodehub.visualstudio.com/_git/WindowsAI/pullrequest/4507?_a=overview)
|
||||
|
||||
b. Multiple inputs (**TODO**)
|
||||
1. test the case where 2 inputs requiring GPU processing are bound back to back forcing bind calls that potentially overlap GPU work from previous bind
|
||||
2. Test models require multiple inputs
|
||||
|
||||
c. Bind with Property (**TODO**)
|
||||
* _If bind with Property, what properties should be tested? And how to make properties as parameters?_
|
||||
|
||||
### 7. InputPixelFormats
|
||||
> We could convert the input image to the following PixelFormats, and then used as input of model. The pixel format expected from model input can be different from the pixel format of real input. But WinML can handle the conversion between them.
|
||||
|
||||
* Bgra8
|
||||
* Rgba8
|
||||
* Gray8
|
||||
|
||||
# Implementation
|
||||
Googletest's [value parameterized tests](https://github.com/google/googletest/blob/master/googletest/docs/advanced.md#value-parameterized-tests) are used.
|
||||
BIN
winml/test/image/batchGroundTruth/1080.jpg
Normal file
|
After Width: | Height: | Size: 230 KiB |
BIN
winml/test/image/batchGroundTruth/fish_720.png
Normal file
|
After Width: | Height: | Size: 237 KiB |
BIN
winml/test/image/batchGroundTruth/fish_720_Gray.png
Normal file
|
After Width: | Height: | Size: 237 KiB |
BIN
winml/test/image/batchGroundTruth/kitten_224.png
Normal file
|
After Width: | Height: | Size: 180 KiB |
BIN
winml/test/image/groundTruth/groundTruth/Bgr8_Bgra8_1080.jpg
Normal file
|
After Width: | Height: | Size: 230 KiB |
BIN
winml/test/image/groundTruth/groundTruth/Bgr8_Bgra8_fish_720.png
Normal file
|
After Width: | Height: | Size: 237 KiB |
|
After Width: | Height: | Size: 237 KiB |
|
After Width: | Height: | Size: 180 KiB |
BIN
winml/test/image/groundTruth/groundTruth/Bgr8_Gray8_1080.jpg
Normal file
|
After Width: | Height: | Size: 229 KiB |
BIN
winml/test/image/groundTruth/groundTruth/Bgr8_Gray8_fish_720.png
Normal file
|
After Width: | Height: | Size: 237 KiB |
|
After Width: | Height: | Size: 237 KiB |
|
After Width: | Height: | Size: 179 KiB |
BIN
winml/test/image/groundTruth/groundTruth/Bgr8_Rgba8_1080.jpg
Normal file
|
After Width: | Height: | Size: 230 KiB |
BIN
winml/test/image/groundTruth/groundTruth/Bgr8_Rgba8_fish_720.png
Normal file
|
After Width: | Height: | Size: 237 KiB |
|
After Width: | Height: | Size: 237 KiB |
|
After Width: | Height: | Size: 180 KiB |
BIN
winml/test/image/groundTruth/groundTruth/Rgb8_Bgra8_1080.jpg
Normal file
|
After Width: | Height: | Size: 227 KiB |
BIN
winml/test/image/groundTruth/groundTruth/Rgb8_Bgra8_fish_720.png
Normal file
|
After Width: | Height: | Size: 235 KiB |
|
After Width: | Height: | Size: 235 KiB |
|
After Width: | Height: | Size: 178 KiB |
BIN
winml/test/image/groundTruth/groundTruth/Rgb8_Gray8_1080.jpg
Normal file
|
After Width: | Height: | Size: 227 KiB |
BIN
winml/test/image/groundTruth/groundTruth/Rgb8_Gray8_fish_720.png
Normal file
|
After Width: | Height: | Size: 235 KiB |
|
After Width: | Height: | Size: 235 KiB |
|
After Width: | Height: | Size: 178 KiB |
BIN
winml/test/image/groundTruth/groundTruth/Rgb8_Rgba8_1080.jpg
Normal file
|
After Width: | Height: | Size: 227 KiB |
BIN
winml/test/image/groundTruth/groundTruth/Rgb8_Rgba8_fish_720.png
Normal file
|
After Width: | Height: | Size: 235 KiB |
|
After Width: | Height: | Size: 235 KiB |
|
After Width: | Height: | Size: 178 KiB |
284
winml/test/image/imageTestHelper.cpp
Normal file
|
|
@ -0,0 +1,284 @@
|
|||
#include "testPch.h"
|
||||
|
||||
#include "imageTestHelper.h"
|
||||
#include "robuffer.h"
|
||||
#include "winrt/Windows.Storage.h"
|
||||
#include "winrt/Windows.Storage.Streams.h"
|
||||
|
||||
#include <d3dx12.h>
|
||||
#include <MemoryBuffer.h>
|
||||
#include <wil\Resource.h>
|
||||
|
||||
#define FENCE_SIGNAL_VALUE 1
|
||||
|
||||
using namespace winrt;
|
||||
using namespace winrt::Windows::AI::MachineLearning;
|
||||
using namespace winrt::Windows::Foundation::Collections;
|
||||
using namespace winrt::Windows::Media;
|
||||
using namespace winrt::Windows::Graphics::Imaging;
|
||||
|
||||
namespace ImageTestHelper {
|
||||
BitmapPixelFormat GetPixelFormat(const std::wstring& inputPixelFormat) {
|
||||
// Return corresponding BitmapPixelFormat according to input string
|
||||
if (L"Bgra8" == inputPixelFormat || L"Bgr8" == inputPixelFormat) {
|
||||
return BitmapPixelFormat::Bgra8;
|
||||
} else if (L"Rgba8" == inputPixelFormat || L"Rgb8" == inputPixelFormat) {
|
||||
return BitmapPixelFormat::Rgba8;
|
||||
} else if (L"Gray8" == inputPixelFormat) {
|
||||
return BitmapPixelFormat::Gray8;
|
||||
} else {
|
||||
throw std::invalid_argument("Unsupported pixelFormat");
|
||||
}
|
||||
}
|
||||
|
||||
TensorFloat LoadInputImageFromCPU(
|
||||
SoftwareBitmap softwareBitmap,
|
||||
const std::wstring& modelPixelFormat) {
|
||||
softwareBitmap = SoftwareBitmap::Convert(softwareBitmap, BitmapPixelFormat::Bgra8);
|
||||
BYTE* pData = nullptr;
|
||||
UINT32 size = 0;
|
||||
winrt::Windows::Graphics::Imaging::BitmapBuffer spBitmapBuffer(softwareBitmap.LockBuffer(winrt::Windows::Graphics::Imaging::BitmapBufferAccessMode::Read));
|
||||
winrt::Windows::Foundation::IMemoryBufferReference reference = spBitmapBuffer.CreateReference();
|
||||
auto spByteAccess = reference.as<::Windows::Foundation::IMemoryBufferByteAccess>();
|
||||
spByteAccess->GetBuffer(&pData, &size);
|
||||
|
||||
uint32_t height = softwareBitmap.PixelHeight();
|
||||
uint32_t width = softwareBitmap.PixelWidth();
|
||||
|
||||
// TODO: Need modification for Gray8
|
||||
std::vector<int64_t> shape = { 1, 3, height , width };
|
||||
float* pCPUTensor;
|
||||
uint32_t uCapacity;
|
||||
TensorFloat tf = TensorFloat::Create(shape);
|
||||
com_ptr<ITensorNative> itn = tf.as<ITensorNative>();
|
||||
itn->GetBuffer(reinterpret_cast<BYTE**>(&pCPUTensor), &uCapacity);
|
||||
if (BitmapPixelFormat::Bgra8 == GetPixelFormat(modelPixelFormat)) {
|
||||
for (UINT32 i = 0; i < size; i += 4) {
|
||||
UINT32 pixelInd = i / 4;
|
||||
pCPUTensor[pixelInd] = (float)pData[i];
|
||||
pCPUTensor[(height * width) + pixelInd] = (float)pData[i + 1];
|
||||
pCPUTensor[(height * width * 2) + pixelInd] = (float)pData[i + 2];
|
||||
}
|
||||
} else if (BitmapPixelFormat::Rgba8 == GetPixelFormat(modelPixelFormat)) {
|
||||
for (UINT32 i = 0; i < size; i += 4) {
|
||||
UINT32 pixelInd = i / 4;
|
||||
pCPUTensor[pixelInd] = (float)pData[i + 2];
|
||||
pCPUTensor[(height * width) + pixelInd] = (float)pData[i + 1];
|
||||
pCPUTensor[(height * width * 2) + pixelInd] = (float)pData[i];
|
||||
}
|
||||
}
|
||||
// else if()
|
||||
// TODO: for Gray8
|
||||
else {
|
||||
std::cerr << "Unsupportted pixelFormat";
|
||||
}
|
||||
return tf;
|
||||
}
|
||||
|
||||
TensorFloat LoadInputImageFromGPU(
|
||||
SoftwareBitmap softwareBitmap,
|
||||
const std::wstring& modelPixelFormat) {
|
||||
|
||||
softwareBitmap = SoftwareBitmap::Convert(softwareBitmap, BitmapPixelFormat::Bgra8);
|
||||
BYTE* pData = nullptr;
|
||||
UINT32 size = 0;
|
||||
BitmapBuffer spBitmapBuffer(softwareBitmap.LockBuffer(winrt::Windows::Graphics::Imaging::BitmapBufferAccessMode::Read));
|
||||
winrt::Windows::Foundation::IMemoryBufferReference reference = spBitmapBuffer.CreateReference();
|
||||
com_ptr<::Windows::Foundation::IMemoryBufferByteAccess> spByteAccess = reference.as<::Windows::Foundation::IMemoryBufferByteAccess>();
|
||||
spByteAccess->GetBuffer(&pData, &size);
|
||||
|
||||
std::vector<int64_t> shape = { 1, 3, softwareBitmap.PixelHeight() , softwareBitmap.PixelWidth() };
|
||||
float* pCPUTensor;
|
||||
uint32_t uCapacity;
|
||||
|
||||
// CPU tensorization
|
||||
TensorFloat tf = TensorFloat::Create(shape);
|
||||
com_ptr<ITensorNative> itn = tf.as<ITensorNative>();
|
||||
itn->GetBuffer(reinterpret_cast<BYTE**>(&pCPUTensor), &uCapacity);
|
||||
|
||||
uint32_t height = softwareBitmap.PixelHeight();
|
||||
uint32_t width = softwareBitmap.PixelWidth();
|
||||
if (BitmapPixelFormat::Bgra8 == GetPixelFormat(modelPixelFormat)) {
|
||||
for (UINT32 i = 0; i < size; i += 4) {
|
||||
UINT32 pixelInd = i / 4;
|
||||
pCPUTensor[pixelInd] = (float)pData[i];
|
||||
pCPUTensor[(height * width) + pixelInd] = (float)pData[i + 1];
|
||||
pCPUTensor[(height * width * 2) + pixelInd] = (float)pData[i + 2];
|
||||
}
|
||||
} else if (BitmapPixelFormat::Rgba8 == GetPixelFormat(modelPixelFormat)) {
|
||||
for (UINT32 i = 0; i < size; i += 4) {
|
||||
UINT32 pixelInd = i / 4;
|
||||
pCPUTensor[pixelInd] = (float)pData[i + 2];
|
||||
pCPUTensor[(height * width) + pixelInd] = (float)pData[i + 1];
|
||||
pCPUTensor[(height * width * 2) + pixelInd] = (float)pData[i];
|
||||
}
|
||||
}
|
||||
// else if()
|
||||
// TODO: for Gray8
|
||||
else {
|
||||
std::cerr << "unsupported pixelFormat";
|
||||
}
|
||||
|
||||
// create the d3d device.
|
||||
com_ptr<ID3D12Device> pD3D12Device = nullptr;
|
||||
WINML_EXPECT_NO_THROW(D3D12CreateDevice(nullptr, D3D_FEATURE_LEVEL::D3D_FEATURE_LEVEL_11_0, __uuidof(ID3D12Device), reinterpret_cast<void**>(&pD3D12Device)));
|
||||
|
||||
// create the command queue.
|
||||
com_ptr<ID3D12CommandQueue> dxQueue = nullptr;
|
||||
D3D12_COMMAND_QUEUE_DESC commandQueueDesc = {};
|
||||
commandQueueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
|
||||
pD3D12Device->CreateCommandQueue(&commandQueueDesc, __uuidof(ID3D12CommandQueue), reinterpret_cast<void**>(&dxQueue));
|
||||
com_ptr<ILearningModelDeviceFactoryNative> devicefactory = get_activation_factory<LearningModelDevice, ILearningModelDeviceFactoryNative>();
|
||||
com_ptr<ITensorStaticsNative> tensorfactory = get_activation_factory<TensorFloat, ITensorStaticsNative>();
|
||||
com_ptr<::IUnknown> spUnk;
|
||||
devicefactory->CreateFromD3D12CommandQueue(dxQueue.get(), spUnk.put());
|
||||
|
||||
// Create ID3D12GraphicsCommandList and Allocator
|
||||
D3D12_COMMAND_LIST_TYPE queuetype = dxQueue->GetDesc().Type;
|
||||
com_ptr<ID3D12CommandAllocator> alloctor;
|
||||
com_ptr<ID3D12GraphicsCommandList> cmdList;
|
||||
|
||||
pD3D12Device->CreateCommandAllocator(
|
||||
queuetype,
|
||||
winrt::guid_of<ID3D12CommandAllocator>(),
|
||||
alloctor.put_void());
|
||||
|
||||
pD3D12Device->CreateCommandList(
|
||||
0,
|
||||
queuetype,
|
||||
alloctor.get(),
|
||||
nullptr,
|
||||
winrt::guid_of<ID3D12CommandList>(),
|
||||
cmdList.put_void());
|
||||
|
||||
// Create Committed Resource
|
||||
// 3 is number of channels we use. R G B without alpha.
|
||||
UINT64 bufferbytesize = 3 * sizeof(float) * softwareBitmap.PixelWidth()*softwareBitmap.PixelHeight();
|
||||
D3D12_HEAP_PROPERTIES heapProperties = {
|
||||
D3D12_HEAP_TYPE_DEFAULT,
|
||||
D3D12_CPU_PAGE_PROPERTY_UNKNOWN,
|
||||
D3D12_MEMORY_POOL_UNKNOWN,
|
||||
0,
|
||||
0
|
||||
};
|
||||
D3D12_RESOURCE_DESC resourceDesc = {
|
||||
D3D12_RESOURCE_DIMENSION_BUFFER,
|
||||
0,
|
||||
bufferbytesize,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
DXGI_FORMAT_UNKNOWN,
|
||||
{ 1, 0 },
|
||||
D3D12_TEXTURE_LAYOUT_ROW_MAJOR,
|
||||
D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS
|
||||
};
|
||||
|
||||
com_ptr<ID3D12Resource> pGPUResource = nullptr;
|
||||
com_ptr<ID3D12Resource> imageUploadHeap;
|
||||
pD3D12Device->CreateCommittedResource(
|
||||
&heapProperties,
|
||||
D3D12_HEAP_FLAG_NONE,
|
||||
&resourceDesc,
|
||||
D3D12_RESOURCE_STATE_COMMON,
|
||||
nullptr,
|
||||
__uuidof(ID3D12Resource),
|
||||
pGPUResource.put_void()
|
||||
);
|
||||
|
||||
// Create the GPU upload buffer.
|
||||
auto heap_properties = CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD);
|
||||
WINML_EXPECT_NO_THROW(pD3D12Device->CreateCommittedResource(
|
||||
&heap_properties,
|
||||
D3D12_HEAP_FLAG_NONE,
|
||||
&CD3DX12_RESOURCE_DESC::Buffer(bufferbytesize),
|
||||
D3D12_RESOURCE_STATE_GENERIC_READ,
|
||||
nullptr,
|
||||
__uuidof(ID3D12Resource),
|
||||
imageUploadHeap.put_void()));
|
||||
|
||||
// Copy from Cpu to GPU
|
||||
D3D12_SUBRESOURCE_DATA CPUData = {};
|
||||
CPUData.pData = reinterpret_cast<BYTE*>(pCPUTensor);
|
||||
CPUData.RowPitch = bufferbytesize;
|
||||
CPUData.SlicePitch = bufferbytesize;
|
||||
UpdateSubresources(cmdList.get(), pGPUResource.get(), imageUploadHeap.get(), 0, 0, 1, &CPUData);
|
||||
|
||||
// Close the command list and execute it to begin the initial GPU setup.
|
||||
WINML_EXPECT_NO_THROW(cmdList->Close());
|
||||
ID3D12CommandList* ppCommandLists[] = { cmdList.get() };
|
||||
dxQueue->ExecuteCommandLists(_countof(ppCommandLists), ppCommandLists);
|
||||
|
||||
//Create Event
|
||||
HANDLE directEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
|
||||
wil::unique_event hDirectEvent(directEvent);
|
||||
|
||||
//Create Fence
|
||||
Microsoft::WRL::ComPtr<ID3D12Fence> spDirectFence = nullptr;
|
||||
WINML_EXPECT_HRESULT_SUCCEEDED(pD3D12Device->CreateFence(
|
||||
0,
|
||||
D3D12_FENCE_FLAG_NONE,
|
||||
IID_PPV_ARGS(spDirectFence.ReleaseAndGetAddressOf())));
|
||||
//Adds fence to queue
|
||||
WINML_EXPECT_HRESULT_SUCCEEDED(dxQueue->Signal(spDirectFence.Get(), FENCE_SIGNAL_VALUE));
|
||||
WINML_EXPECT_HRESULT_SUCCEEDED(spDirectFence->SetEventOnCompletion(FENCE_SIGNAL_VALUE, hDirectEvent.get()));
|
||||
|
||||
//Wait for signal
|
||||
DWORD retVal = WaitForSingleObject(hDirectEvent.get(), INFINITE);
|
||||
if (retVal != WAIT_OBJECT_0) {
|
||||
WINML_EXPECT_HRESULT_SUCCEEDED(E_UNEXPECTED);
|
||||
}
|
||||
|
||||
// GPU tensorize
|
||||
com_ptr<::IUnknown> spUnkTensor;
|
||||
TensorFloat input1imagetensor(nullptr);
|
||||
int64_t shapes[4] = { 1,3, softwareBitmap.PixelWidth(), softwareBitmap.PixelHeight() };
|
||||
tensorfactory->CreateFromD3D12Resource(pGPUResource.get(), shapes, 4, spUnkTensor.put());
|
||||
spUnkTensor.try_as(input1imagetensor);
|
||||
|
||||
return input1imagetensor;
|
||||
}
|
||||
|
||||
bool VerifyHelper(
|
||||
VideoFrame actual,
|
||||
VideoFrame expected) {
|
||||
// Verify two input ImageFeatureValues are identified.
|
||||
auto softwareBitmapActual = actual.SoftwareBitmap();
|
||||
auto softwareBitmapExpected = expected.SoftwareBitmap();
|
||||
WINML_EXPECT_TRUE(softwareBitmapActual.PixelHeight() == softwareBitmapExpected.PixelHeight());
|
||||
WINML_EXPECT_TRUE(softwareBitmapActual.PixelWidth() == softwareBitmapExpected.PixelWidth());
|
||||
WINML_EXPECT_TRUE(softwareBitmapActual.BitmapPixelFormat() == softwareBitmapExpected.BitmapPixelFormat());
|
||||
|
||||
uint32_t size = 4 * softwareBitmapActual.PixelHeight() * softwareBitmapActual.PixelWidth();
|
||||
|
||||
winrt::Windows::Storage::Streams::Buffer actualOutputBuffer(size);
|
||||
winrt::Windows::Storage::Streams::Buffer expectedOutputBuffer(size);
|
||||
|
||||
softwareBitmapActual.CopyToBuffer(actualOutputBuffer);
|
||||
softwareBitmapExpected.CopyToBuffer(expectedOutputBuffer);
|
||||
|
||||
byte* actualBytes;
|
||||
actualOutputBuffer.try_as<::Windows::Storage::Streams::IBufferByteAccess>()->Buffer(&actualBytes);
|
||||
byte* expectedBytes;
|
||||
expectedOutputBuffer.try_as<::Windows::Storage::Streams::IBufferByteAccess>()->Buffer(&expectedBytes);
|
||||
|
||||
byte* pActualByte = actualBytes;
|
||||
byte* pExpectedByte = expectedBytes;
|
||||
|
||||
// hard code, might need to be modified later.
|
||||
const float cMaxErrorRate = 0.06f;
|
||||
int8_t epsilon = 20;
|
||||
|
||||
// Even given two same ImageFeatureValues, the comparison cannot exactly match.
|
||||
// So we use error rate.
|
||||
UINT errors = 0;
|
||||
for (uint32_t i = 0; i < size; i++, pActualByte++, pExpectedByte++) {
|
||||
auto diff = (*pActualByte - *pExpectedByte);
|
||||
if (diff > epsilon) {
|
||||
errors++;
|
||||
}
|
||||
}
|
||||
std::cerr << "total errors is " << errors << "/" << size << ", errors rate is " << (float)errors / size;
|
||||
return (float)errors / size < cMaxErrorRate;
|
||||
}
|
||||
}
|
||||
34
winml/test/image/imageTestHelper.h
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <winrt/Windows.Media.h>
|
||||
#include <winrt/Windows.Graphics.Imaging.h>
|
||||
|
||||
enum OutputBindingStrategy { Bound, Unbound, Empty };
|
||||
enum EvaluationStrategy { Async, Sync };
|
||||
enum ModelInputOutputType { Image, Tensor };
|
||||
enum InputImageSource { FromVideoFrame, FromImageFeatureValue, FromCPUResource, FromGPUResource };
|
||||
enum VideoFrameSource { FromSoftwareBitmap, FromDirect3DSurface, FromUnsupportedD3DSurface };
|
||||
|
||||
namespace ImageTestHelper
|
||||
{
|
||||
winrt::Windows::Graphics::Imaging::BitmapPixelFormat GetPixelFormat(const std::wstring& inputPixelFormat);
|
||||
|
||||
winrt::Windows::AI::MachineLearning::TensorFloat LoadInputImageFromCPU(
|
||||
winrt::Windows::Graphics::Imaging::SoftwareBitmap softwareBitmap,
|
||||
const std::wstring& modelPixelFormat);
|
||||
|
||||
winrt::Windows::AI::MachineLearning::TensorFloat LoadInputImageFromGPU(
|
||||
winrt::Windows::Graphics::Imaging::SoftwareBitmap softwareBitmap,
|
||||
const std::wstring& modelPixelFormat);
|
||||
|
||||
bool VerifyHelper(
|
||||
winrt::Windows::Media::VideoFrame actual,
|
||||
winrt::Windows::Media::VideoFrame expected);
|
||||
|
||||
}
|
||||
BIN
winml/test/image/images/1080.jpg
Normal file
|
After Width: | Height: | Size: 262 KiB |
BIN
winml/test/image/images/RGB_5.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
winml/test/image/images/fish_720.png
Normal file
|
After Width: | Height: | Size: 1.2 MiB |
BIN
winml/test/image/images/fish_720_gray.png
Normal file
|
After Width: | Height: | Size: 421 KiB |
BIN
winml/test/image/images/horizontal-crop.png
Normal file
|
After Width: | Height: | Size: 470 B |
BIN
winml/test/image/images/kitten_224.png
Normal file
|
After Width: | Height: | Size: 55 KiB |
BIN
winml/test/image/images/small.png
Normal file
|
After Width: | Height: | Size: 201 B |
BIN
winml/test/image/images/vertical-crop.png
Normal file
|
After Width: | Height: | Size: 226 B |
1029
winml/test/image/imagetests.cpp
Normal file
4
winml/test/image/models/README.md
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
The FNS_Candy machine learning models here are used with permission from Justin Johnson.
|
||||
For additional information on these models, refer to:
|
||||
* [Fast-neural-style GitHub repo](https://github.com/jcjohnson/fast-neural-style)
|
||||
* [Perceptual Losses for Real-Time Style Transfer and Super-Resolution](https://cs.stanford.edu/people/jcjohns/papers/eccv16/JohnsonECCV16.pdf)
|
||||