Skip to content

Commit 99e9342

Browse files
author
Ryan Lai
authored
This change allows user to specify folder of images to bind and evaluate (#237)
* Evaluate folder of images * Evaluate folder of images * Updated usage * Test change * test changes back to test method cleanup * Fix build break * remove test * Added test * Fix comment typo * Don't print out image path if using -terse flag * Remove commitments
1 parent f5fec2f commit 99e9342

File tree

9 files changed

+185
-91
lines changed

9 files changed

+185
-91
lines changed

Testing/WinMLRunnerTest/WinMLRunnerTest.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -641,6 +641,30 @@ namespace WinMLRunnerTest
641641
// We need to expect one more line because of the header
642642
Assert::AreEqual(static_cast<size_t>(2), GetOutputCSVLineCount(tensorDataPath + L"\\PerIterationData\\Summary.csv"));
643643
}
644+
645+
TEST_METHOD(ProvidedImageInputFolder)
646+
{
647+
// Make test_folder_input folder before starting the tests
648+
std::string mkFolderCommand = "mkdir " + std::string(INPUT_FOLDER_PATH.begin(), INPUT_FOLDER_PATH.end());
649+
system(mkFolderCommand.c_str());
650+
651+
std::vector<std::string> images = { "fish.png", "kitten_224.png" };
652+
653+
// Copy images from list to test_folder_input
654+
for (auto image : images)
655+
{
656+
std::string copyCommand = "Copy ";
657+
copyCommand += image;
658+
copyCommand += ' ' + std::string(INPUT_FOLDER_PATH.begin(), INPUT_FOLDER_PATH.end());
659+
system(copyCommand.c_str());
660+
}
661+
const std::wstring command = BuildCommand({ EXE_PATH, L"-model", L"SqueezeNet.onnx", L"-InputImageFolder", INPUT_FOLDER_PATH });
662+
Assert::AreEqual(S_OK, RunProc((wchar_t*)command.c_str()));
663+
664+
std::string removeCommand = "rd /s /q ";
665+
removeCommand += std::string(INPUT_FOLDER_PATH.begin(), INPUT_FOLDER_PATH.end());
666+
system(removeCommand.c_str());
667+
}
644668
};
645669

646670
TEST_CLASS(CsvInputTest)

Tools/WinMLRunner/README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ Required command-Line arguments:
4545
-Tensor : load the input as a tensor
4646
-Perf [all]: capture performance measurements such as timing and memory usage. Specifying "all" will output all measurements
4747
-Iterations : # times perf measurements will be run/averaged. (maximum: 1024 times)
48-
-Input <fully qualified path>: binds image or CSV to model
48+
-Input <path to input file>: binds image or CSV to model
49+
-InputImageFolder <path to directory of images> : specify folder of images to bind to model" << std::endl;
4950
-TopK <number>: print top <number> values in the result. Default to 1
5051
-BaseOutputPath [<fully qualified path>] : base output directory path for results, default to cwd
5152
-PerfOutput [<path>] : fully qualified or relative path including csv filename for perf results

Tools/WinMLRunner/src/BindingUtilities.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -477,8 +477,8 @@ namespace BindingUtilities
477477
}
478478
else if (args.IsImageInput())
479479
{
480-
// Creating Tensors for Input Images haven't been added yet.
481-
throw hresult_not_implemented(L"Creating Tensors for Input Images haven't been implemented yet!");
480+
// Creating Tensors for Input Images haven't been added.
481+
throw hresult_not_implemented(L"Creating Tensors for Input Images haven't been implemented!");
482482
}
483483

484484
if (inputBindingType == InputBindingType::CPU)
@@ -605,7 +605,7 @@ namespace BindingUtilities
605605
}
606606
else
607607
{
608-
throw hresult_not_implemented(L"BitmapPixel format not yet handled by WinMLRunner.");
608+
throw hresult_not_implemented(L"BitmapPixel format not handled by WinMLRunner.");
609609
}
610610
std::vector<int64_t> shape = { 1, channels, imageFeatureDescriptor.Height(),
611611
imageFeatureDescriptor.Width() };

Tools/WinMLRunner/src/CommandLineArgs.cpp

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <ctime>
66
#include <iomanip>
77
#include <filesystem>
8+
#include "Filehelper.h"
89

910
using namespace Windows::AI::MachineLearning;
1011

@@ -35,7 +36,8 @@ void CommandLineArgs::PrintUsage()
3536
"will output all measurements"
3637
<< std::endl;
3738
std::cout << " -Iterations : # times perf measurements will be run/averaged. (maximum: 1024 times)" << std::endl;
38-
std::cout << " -Input <fully qualified path> : binds image or CSV to model" << std::endl;
39+
std::cout << " -Input <path to input file>: binds image or CSV to model" << std::endl;
40+
std::cout << " -InputImageFolder <path to directory of images> : specify folder of images to bind to model" << std::endl;
3941
std::cout << " -TopK <number> : print top <number> values in the result. Default to 1" << std::endl;
4042
std::cout << " -BaseOutputPath [<fully qualified path>] : base output directory path for results, default to cwd"
4143
<< std::endl;
@@ -139,7 +141,13 @@ CommandLineArgs::CommandLineArgs(const std::vector<std::wstring>& args)
139141
}
140142
else if ((_wcsicmp(args[i].c_str(), L"-Input") == 0))
141143
{
142-
m_inputData = args[++i];
144+
CheckNextArgument(args, i);
145+
m_inputData = FileHelper::GetAbsolutePath(args[++i]);
146+
}
147+
else if ((_wcsicmp(args[i].c_str(), L"-InputImageFolder") == 0))
148+
{
149+
CheckNextArgument(args, i);
150+
m_inputImageFolderPath = FileHelper::GetAbsolutePath(args[++i]);
143151
}
144152
else if ((_wcsicmp(args[i].c_str(), L"-PerfOutput") == 0))
145153
{
@@ -326,7 +334,7 @@ CommandLineArgs::CommandLineArgs(const std::vector<std::wstring>& args)
326334
if (m_inputData.find(L".png") != std::string::npos || m_inputData.find(L".jpg") != std::string::npos ||
327335
m_inputData.find(L".jpeg") != std::string::npos)
328336
{
329-
m_imagePath = m_inputData;
337+
m_imagePaths.push_back(m_inputData);
330338
}
331339
else if (m_inputData.find(L".csv") != std::string::npos)
332340
{
@@ -339,12 +347,31 @@ CommandLineArgs::CommandLineArgs(const std::vector<std::wstring>& args)
339347
throw hresult_invalid_argument(msg.c_str());
340348
}
341349
}
342-
350+
if (!m_inputImageFolderPath.empty())
351+
{
352+
PopulateInputImagePaths();
353+
}
343354
SetupOutputDirectories(sBaseOutputPath, sPerfOutputPath, sPerIterationDataPath);
344355

345356
CheckForInvalidArguments();
346357
}
347358

359+
void CommandLineArgs::PopulateInputImagePaths()
360+
{
361+
for (auto& it : std::filesystem::directory_iterator(m_inputImageFolderPath))
362+
{
363+
std::string path = it.path().string();
364+
if (it.path().string().find(".png") != std::string::npos ||
365+
it.path().string().find(".jpg") != std::string::npos ||
366+
it.path().string().find(".jpeg") != std::string::npos)
367+
{
368+
std::wstring fileName;
369+
fileName.assign(path.begin(), path.end());
370+
m_imagePaths.push_back(fileName);
371+
}
372+
}
373+
}
374+
348375
void CommandLineArgs::SetupOutputDirectories(const std::wstring& sBaseOutputPath,
349376
const std::wstring& sPerfOutputPath,
350377
const std::wstring& sPerIterationDataPath)
@@ -420,4 +447,8 @@ void CommandLineArgs::CheckForInvalidArguments()
420447
{
421448
throw hresult_invalid_argument(L"Cannot save tensor output if no input data is provided!");
422449
}
450+
if (m_imagePaths.size() > 1 && IsSaveTensor())
451+
{
452+
throw hresult_not_implemented(L"Saving tensor output for multiple images isn't implemented.");
453+
}
423454
}

Tools/WinMLRunner/src/CommandLineArgs.h

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ class CommandLineArgs
2424

2525
BitmapInterpolationMode AutoScaleInterpMode() const { return m_autoScaleInterpMode; }
2626

27-
const std::wstring& ImagePath() const { return m_imagePath; }
27+
const std::vector<std::wstring>& ImagePaths() const { return m_imagePaths; }
2828
const std::wstring& CsvPath() const { return m_csvData; }
2929
const std::wstring& OutputPath() const { return m_perfOutputPath; }
3030
const std::wstring& FolderPath() const { return m_modelFolderPath; }
@@ -38,7 +38,7 @@ class CommandLineArgs
3838
bool UseRGB() const
3939
{
4040
// If an image is specified without flags, we load it as a BGR image by default
41-
return m_useRGB || (!m_imagePath.empty() && !m_useBGR && !m_useTensor);
41+
return m_useRGB || (!m_imagePaths.empty() && !m_useBGR && !m_useTensor);
4242
}
4343

4444
bool UseTensor() const
@@ -70,10 +70,10 @@ class CommandLineArgs
7070
bool IsGarbageInput() const
7171
{
7272
// When there is no image or csv input provided, then garbage input binding is used.
73-
return m_imagePath.empty() && m_csvData.empty();
73+
return m_imagePaths.empty() && m_csvData.empty();
7474
}
75-
bool IsCSVInput() const { return m_imagePath.empty() && !m_csvData.empty(); }
76-
bool IsImageInput() const { return !m_imagePath.empty() && m_csvData.empty(); }
75+
bool IsCSVInput() const { return m_imagePaths.empty() && !m_csvData.empty(); }
76+
bool IsImageInput() const { return !m_imagePaths.empty() && m_csvData.empty(); }
7777

7878
uint32_t NumIterations() const { return m_numIterations; }
7979
uint32_t NumThreads() const { return m_numThreads; }
@@ -143,7 +143,8 @@ class CommandLineArgs
143143

144144
std::wstring m_modelFolderPath;
145145
std::wstring m_modelPath;
146-
std::wstring m_imagePath;
146+
std::vector<std::wstring> m_imagePaths;
147+
std::wstring m_inputImageFolderPath;
147148
std::wstring m_csvData;
148149
std::wstring m_inputData;
149150
#ifdef DXCORE_SUPPORTED_BUILD
@@ -161,4 +162,5 @@ class CommandLineArgs
161162
void CheckForInvalidArguments();
162163
void SetupOutputDirectories(const std::wstring& sBaseOutputPath, const std::wstring& sPerfOutputPath,
163164
const std::wstring& sPerIterationDataPath);
165+
void PopulateInputImagePaths();
164166
};

Tools/WinMLRunner/src/Filehelper.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,15 @@ namespace FileHelper
2222

2323
return val;
2424
}
25+
std::wstring GetAbsolutePath(std::wstring relativePath)
26+
{
27+
TCHAR** lppPart = { NULL };
28+
wchar_t absolutePath[MAX_PATH] = { 0 };
29+
errno_t err = GetFullPathName(relativePath.c_str(), MAX_PATH, absolutePath, lppPart);
30+
if (err == 0)
31+
{
32+
throw HRESULT_FROM_WIN32(GetLastError());
33+
}
34+
return absolutePath;
35+
}
2536
} // namespace FileHelper

Tools/WinMLRunner/src/Filehelper.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,5 @@
44
namespace FileHelper
55
{
66
std::wstring GetModulePath();
7+
std::wstring GetAbsolutePath(std::wstring relativePath);
78
}

Tools/WinMLRunner/src/OutputHelper.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -578,7 +578,7 @@ class OutputHelper
578578

579579
void SetCSVFileName(const std::wstring& fileName) { m_csvFileName = fileName; }
580580

581-
void WritePerIterationPerformance(const CommandLineArgs& args, std::wstring model)
581+
void WritePerIterationPerformance(const CommandLineArgs& args, const std::wstring model, const std::wstring imagePath)
582582
{
583583
if (m_csvFileNamePerIterationSummary.length() > 0)
584584
{
@@ -599,7 +599,7 @@ class OutputHelper
599599
std::string modelName = converter.to_bytes(model);
600600
std::string fileNameResultDevice = converter.to_bytes(m_fileNameResultDevice);
601601
std::string inputName = args.IsCSVInput() ? converter.to_bytes(args.CsvPath())
602-
: args.IsImageInput() ? converter.to_bytes(args.ImagePath()) : "";
602+
: args.IsImageInput() ? converter.to_bytes(imagePath) : "";
603603

604604
if (bNewFile)
605605
{

0 commit comments

Comments
 (0)