Skip to content

Commit 68f1ccb

Browse files
committed
Improved speed of ImagePyramidAccess and PatchGenerator with some optimizations and tile caching. Also moved runtime measurement stuff from ProcessObject to Object.
1 parent c4c37e8 commit 68f1ccb

File tree

14 files changed

+298
-149
lines changed

14 files changed

+298
-149
lines changed

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ project(FAST)
77

88
set(VERSION_MAJOR 4)
99
set(VERSION_MINOR 13)
10-
set(VERSION_PATCH 0)
10+
set(VERSION_PATCH 1)
1111
set(VERSION_SO 4) # SO version, should be incremented by 1 every time the existing API changes
1212
set(FAST_VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}")
1313

source/FAST/Algorithms/ImagePatch/PatchGenerator.cpp

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,19 @@ void PatchGenerator::generateStream() {
109109
const int patchesX = std::ceil((float) levelWidth / (float) (patchWidthWithoutOverlap*resampleFactor));
110110
const int patchesY = std::ceil((float) levelHeight / (float) (patchHeightWithoutOverlap*resampleFactor));
111111

112+
bool useTileCache = true;
113+
int tileCacheSize = -1;
114+
// No need to use tile cache if we are only going to get each tile exactly 1 time
115+
// This happens when the request patch size is the same as the tile size of the data
116+
if((int)round(m_width*resampleFactor) == m_inputImagePyramid->getLevelTileWidth(level) && (int)round(m_height*resampleFactor) == m_inputImagePyramid->getLevelTileHeight(level)) {
117+
useTileCache = false;
118+
} else {
119+
useTileCache = true;
120+
int rowsPerPatch = std::ceil((float)m_inputImagePyramid->getLevelTileHeight(level) / m_height*resampleFactor);
121+
tileCacheSize = patchesX*rowsPerPatch;
122+
reportInfo() << "PatchGenerator is using a tile cache with size limit of " << tileCacheSize << reportEnd();
123+
}
124+
auto access = m_inputImagePyramid->getAccess(ACCESS_READ, useTileCache, tileCacheSize);
112125
for(int patchY = 0; patchY < patchesY; ++patchY) {
113126
for(int patchX = 0; patchX < patchesX; ++patchX) {
114127
mRuntimeManager->startRegularTimer("create patch");
@@ -150,7 +163,6 @@ void PatchGenerator::generateStream() {
150163
}
151164
}
152165
reportInfo() << "Generating patch " << patchX << " " << patchY << reportEnd();
153-
auto access = m_inputImagePyramid->getAccess(ACCESS_READ);
154166
if(patchWidth < overlapInPixelsX*2 || patchHeight < overlapInPixelsY*2)
155167
continue;
156168
mRuntimeManager->startRegularTimer("getPatchAsImage");
@@ -160,6 +172,7 @@ void PatchGenerator::generateStream() {
160172
patchWidth + (patchOffsetX < 0 ? patchOffsetX : 0), // We have to reduce width and height if negative offset
161173
patchHeight + (patchOffsetY < 0 ? patchOffsetY : 0));
162174
mRuntimeManager->stopRegularTimer("getPatchAsImage");
175+
//access->getAllRuntimes()->printAll();
163176

164177
// If patch does not have correct size, pad it
165178
int paddingValue = m_paddingValue;

source/FAST/Data/Access/ImagePyramidAccess.cpp

Lines changed: 150 additions & 43 deletions
Large diffs are not rendered by default.

source/FAST/Data/Access/ImagePyramidAccess.hpp

Lines changed: 48 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,10 @@ class Image;
1414
class ImagePyramid;
1515
class NeuralNetwork;
1616

17-
1817
/**
1918
* @brief Image compression types for ImagePyramids
2019
*
20+
* @sa ImagePyramid
2121
* @ingroup wsi
2222
*/
2323
enum class ImageCompression {
@@ -31,41 +31,34 @@ enum class ImageCompression {
3131
DEFLATE, // Zlib lossless
3232
};
3333

34-
class FAST_EXPORT ImagePyramidPatch {
35-
public:
36-
std::unique_ptr<uchar[]> data;
37-
int width;
38-
int height;
39-
int offsetX;
40-
int offsetY;
41-
};
42-
34+
/**
35+
* @brief Object for metadata of an ImagePyramid level
36+
* @sa ImagePyramid
37+
* @ingroup wsi
38+
*/
4339
class FAST_EXPORT ImagePyramidLevel {
4440
public:
4541
int width;
4642
int height;
47-
int tileWidth = 256;
48-
int tileHeight = 256;
43+
int tileWidth = 1024;
44+
int tileHeight = 1024;
4945
int tilesX;
5046
int tilesY;
51-
bool memoryMapped;
52-
uint8_t* data;
5347
uint64_t offset = 0; // subifd offset used by OME-TIFF
54-
#ifdef WIN32
55-
void* fileHandle;
56-
#else
57-
int fileHandle;
58-
#endif
5948
};
6049

6150
/**
6251
* @brief CPU access to ImagePyramid
6352
* @ingroup access
6453
*/
65-
class FAST_EXPORT ImagePyramidAccess : Object {
54+
#ifdef SWIG
55+
class FAST_EXPORT ImagePyramidAccess {
56+
#else
57+
class FAST_EXPORT ImagePyramidAccess : public Object {
58+
#endif
6659
public:
6760
typedef std::unique_ptr<ImagePyramidAccess> pointer;
68-
ImagePyramidAccess(std::vector<ImagePyramidLevel> levels, openslide_t* fileHandle, TIFF* tiffHandle, std::shared_ptr<ImagePyramid> imagePyramid, bool writeAccess, std::unordered_set<std::string>& initializedPatchList, std::mutex& readMutex, ImageCompression compressionFormat);
61+
ImagePyramidAccess(std::vector<ImagePyramidLevel> levels, openslide_t* fileHandle, TIFF* tiffHandle, std::shared_ptr<ImagePyramid> imagePyramid, bool writeAccess, std::unordered_set<std::string>& initializedPatchList, std::mutex& readMutex, ImageCompression compressionFormat, bool useCache = false, int cacheLimit = -1);
6962
/**
7063
* @brief Write a patch to the pyramid
7164
* @param level
@@ -85,8 +78,32 @@ class FAST_EXPORT ImagePyramidAccess : Object {
8578
bool isPatchInitialized(int level, int x, int y);
8679
template <class T>
8780
std::unique_ptr<T[]> getPatchData(int level, int x, int y, int width, int height);
81+
/**
82+
* @brief Get a specific level in an ImagePyramid as an Image object.
83+
* If requesting a level with a width or height higher than 16384 pixels this will throw an exception.
84+
* @param level
85+
* @return
86+
*/
8887
std::shared_ptr<Image> getLevelAsImage(int level);
88+
/**
89+
* @brief Extract a patch from the image pyramid and return it as an Image
90+
* @param level Level to extract patch from
91+
* @param offsetX X offset
92+
* @param offsetY Y offset
93+
* @param width Width of patch
94+
* @param height Height of patch
95+
* @param convertToRGB convert to RGB when using OpenSlide, since it will return BGRA data
96+
* @return Patch as Image
97+
*/
8998
std::shared_ptr<Image> getPatchAsImage(int level, int offsetX, int offsetY, int width, int height, bool convertToRGB = true);
99+
/**
100+
* @brief Extract a tile from the Image Pyramid
101+
* @param level Level to extract tile from
102+
* @param patchIdX Tile X id
103+
* @param patchIdY Tile Y id
104+
* @param convertToRGB convert to RGB when using OpenSlide, since it will return BGRA data
105+
* @return Tile as Image
106+
*/
90107
std::shared_ptr<Image> getPatchAsImage(int level, int patchIdX, int patchIdY, bool convertToRGB = true);
91108
/**
92109
* @brief Get patch as Image at a specific magnification
@@ -95,12 +112,12 @@ class FAST_EXPORT ImagePyramidAccess : Object {
95112
* @param offsetY Physical offset y position of patch
96113
* @param width Width of patch in pixels
97114
* @param height Height of patch in pixels
98-
* @param convertToRGB Convert from BGR to RGB if needed
115+
* @param convertToRGB convert to RGB when using OpenSlide, since it will return BGRA data
99116
* @return patch as Image object
100117
*/
101118
std::shared_ptr<Image> getPatchAsImageForMagnification(float magnification, float offsetX, float offsetY, int width, int height, bool convertToRGB = true);
102119
void release();
103-
~ImagePyramidAccess();
120+
~ImagePyramidAccess() override;
104121
void setJPEGTables(uint32_t tableCount, void* tableData);
105122
private:
106123
std::unique_ptr<uchar[]> getPatchDataChar(int level, int x, int y, int width, int height);
@@ -123,6 +140,14 @@ class FAST_EXPORT ImagePyramidAccess : Object {
123140

124141
uint32_t m_JPEGTablesCount = 0;
125142
void* m_JPEGTablesData = nullptr;
143+
144+
// Optional tile cache
145+
void addTileToQueue(const std::string& id);
146+
std::unordered_map<std::string, std::shared_ptr<char[]>> m_tileCache; // Should ideally be a unique_ptr here, but MSVC can't handle that
147+
std::deque<std::string> m_tileCacheQueue;
148+
std::unordered_map<std::string, int> m_tileCacheCounter;
149+
uint64_t m_tileCacheSizeLimit;
150+
bool m_useTileCache;
126151
};
127152

128153
template <class T>

source/FAST/Data/ImagePyramid.cpp

Lines changed: 5 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -263,35 +263,20 @@ void ImagePyramid::free(ExecutionDevice::pointer device) {
263263
}
264264

265265
void ImagePyramid::freeAll() {
266+
m_levels.clear();
266267
if(m_fileHandle != nullptr) {
267-
m_levels.clear();
268268
openslide_close(m_fileHandle);
269269
} else if(m_tiffHandle != nullptr) {
270-
m_levels.clear();
271270
TIFFClose(m_tiffHandle);
272271
if(m_tempFile) {
273272
// If this is a temp file created by FAST. Delete it.
274273
std::remove(m_tiffPath.c_str());
275274
}
276-
} else {
277-
for(auto& item : m_levels) {
278-
if(item.memoryMapped) {
279-
#ifdef WIN32
280-
UnmapViewOfFile(item.data);
281-
CloseHandle(item.fileHandle);
282-
#else
283-
munmap(item.data, item.width*item.height*m_channels);
284-
close(item.fileHandle);
285-
#endif
286-
} else {
287-
delete[] item.data;
288-
}
289-
}
290-
m_levels.clear();
291275
}
292276

293277
m_initialized = false;
294278
m_fileHandle = nullptr;
279+
m_tiffHandle = nullptr;
295280
}
296281

297282
ImagePyramid::~ImagePyramid() {
@@ -301,7 +286,7 @@ int ImagePyramid::getNrOfChannels() const {
301286
return m_channels;
302287
}
303288

304-
ImagePyramidAccess::pointer ImagePyramid::getAccess(accessType type) {
289+
ImagePyramidAccess::pointer ImagePyramid::getAccess(accessType type, bool useTileCache, int tileCacheSize) {
305290
if(!m_initialized)
306291
throw Exception("ImagePyramid has not been initialized.");
307292

@@ -322,7 +307,7 @@ ImagePyramidAccess::pointer ImagePyramid::getAccess(accessType type) {
322307
std::unique_lock<std::mutex> lock(mDataIsBeingAccessedMutex);
323308
mDataIsBeingAccessed = true;
324309
}
325-
auto access = std::make_unique<ImagePyramidAccess>(m_levels, m_fileHandle, m_tiffHandle, std::static_pointer_cast<ImagePyramid>(mPtr.lock()), type == ACCESS_READ_WRITE, m_initializedPatchList, m_readMutex, m_compressionFormat);
310+
auto access = std::make_unique<ImagePyramidAccess>(m_levels, m_fileHandle, m_tiffHandle, std::static_pointer_cast<ImagePyramid>(mPtr.lock()), type == ACCESS_READ_WRITE, m_initializedPatchList, m_readMutex, m_compressionFormat, useTileCache, tileCacheSize);
326311
if(m_JPEGTablesCount > 0)
327312
access->setJPEGTables(m_JPEGTablesCount, m_JPEGTablesData);
328313
return access;
@@ -437,7 +422,7 @@ ImagePyramid::ImagePyramid(TIFF *fileHandle, std::vector<ImagePyramidLevel> leve
437422
int quality = -1;
438423
int res = TIFFGetField(m_tiffHandle, TIFFTAG_JPEGQUALITY, &quality);
439424
if(res != 1)
440-
throw Exception("Unable to get JPEG quality from TIFF");
425+
reportInfo() << "Unable to get JPEG quality from TIFF" << reportEnd();
441426
m_compressionQuality = quality;
442427
}
443428
{

source/FAST/Data/ImagePyramid.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ class FAST_EXPORT ImagePyramid : public SpatialDataObject {
8080
std::string getTIFFPath() const;
8181
void setSpacing(Vector3f spacing);
8282
Vector3f getSpacing() const;
83-
ImagePyramidAccess::pointer getAccess(accessType type);
83+
ImagePyramidAccess::pointer getAccess(accessType type, bool useTileCache = false, int tileCacheSize = -1);
8484
std::unordered_set<std::string> getDirtyPatches();
8585
bool isDirtyPatch(const std::string& tileID);
8686
bool isOMETIFF() const;

source/FAST/Object.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ Object::Object() {
9292
std::cout << "\033[0m" << std::flush; // Reset
9393
#endif
9494
});
95+
mRuntimeManager = RuntimeMeasurementsManager::New();
9596
}
9697

9798
Reporter& Object::reportError() {
@@ -273,4 +274,28 @@ std::unordered_map<std::string, std::shared_ptr<Attribute>> AttributeObject::get
273274
return mAttributes;
274275
}
275276

277+
void Object::enableRuntimeMeasurements() {
278+
mRuntimeManager->enable();
279+
}
280+
281+
void Object::disableRuntimeMeasurements() {
282+
mRuntimeManager->disable();
283+
}
284+
285+
RuntimeMeasurement::pointer Object::getRuntime() {
286+
return mRuntimeManager->getTiming("execute");
287+
}
288+
289+
RuntimeMeasurement::pointer Object::getRuntime(std::string name) {
290+
return mRuntimeManager->getTiming(name);
291+
}
292+
293+
RuntimeMeasurementsManager::pointer Object::getAllRuntimes() {
294+
return mRuntimeManager;
295+
}
296+
297+
RuntimeMeasurementsManager::pointer Object::getRuntimeManager() {
298+
return getAllRuntimes();
299+
}
300+
276301
} // end namespace fast

source/FAST/Object.hpp

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@
22

33
#define NOMINMAX // Removes windows min and max macros
44
#define _USE_MATH_DEFINES
5-
#include "FAST/Exception.hpp"
6-
#include "FAST/Reporter.hpp"
5+
#include <FAST/Exception.hpp>
6+
#include <FAST/Reporter.hpp>
77
#include "FASTVersion.hpp"
8-
#include "FAST/Attribute.hpp"
8+
#include <FAST/RuntimeMeasurement.hpp>
9+
#include <FAST/RuntimeMeasurementManager.hpp>
10+
#include <FAST/Attribute.hpp>
911
#include <unordered_map>
1012
#include <memory>
1113

@@ -299,12 +301,23 @@ class FAST_EXPORT Object {
299301
return "Object";
300302
}
301303
Reporter& getReporter();
304+
305+
// Runtime stuff
306+
RuntimeMeasurement::pointer getRuntime();
307+
RuntimeMeasurement::pointer getRuntime(std::string name);
308+
RuntimeMeasurementsManager::pointer getAllRuntimes();
309+
RuntimeMeasurementsManager::pointer getRuntimeManager();
310+
void enableRuntimeMeasurements();
311+
void disableRuntimeMeasurements();
312+
302313
protected:
303314
Reporter& reportError();
304315
Reporter& reportWarning();
305316
Reporter& reportInfo();
306317
ReporterEnd reportEnd() const;
307318
std::weak_ptr<Object> mPtr;
319+
320+
RuntimeMeasurementsManager::pointer mRuntimeManager;
308321
private:
309322
Reporter mReporter;
310323

source/FAST/ProcessObject.cpp

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ namespace fast {
1212

1313
ProcessObject::ProcessObject() : mIsModified(false) {
1414
mDevices[0] = DeviceManager::getInstance()->getDefaultDevice();
15-
mRuntimeManager = RuntimeMeasurementsManager::New();
1615
}
1716

1817
static bool isStreamer(ProcessObject* po) {
@@ -255,26 +254,6 @@ void ProcessObject::validateOutputPortExists(uint portID) {
255254
throw Exception(getNameOfClass() + " has no output port with ID " + std::to_string(portID));
256255
}
257256

258-
void ProcessObject::enableRuntimeMeasurements() {
259-
mRuntimeManager->enable();
260-
}
261-
262-
void ProcessObject::disableRuntimeMeasurements() {
263-
mRuntimeManager->disable();
264-
}
265-
266-
RuntimeMeasurement::pointer ProcessObject::getRuntime() {
267-
return mRuntimeManager->getTiming("execute");
268-
}
269-
270-
RuntimeMeasurement::pointer ProcessObject::getRuntime(std::string name) {
271-
return mRuntimeManager->getTiming(name);
272-
}
273-
274-
RuntimeMeasurementsManager::pointer ProcessObject::getAllRuntimes() {
275-
return mRuntimeManager;
276-
}
277-
278257
void ProcessObject::postExecute() {
279258
/*
280259
// TODO Release input data if they are marked as "release after execute"
@@ -437,10 +416,6 @@ int ProcessObject::getLastExecuteToken() const {
437416
return m_lastExecuteToken;
438417
}
439418

440-
RuntimeMeasurementsManager::pointer ProcessObject::getRuntimeManager() {
441-
return getAllRuntimes();
442-
}
443-
444419
void ProcessObject::setExecuteOnLastFrameOnly(bool executeOnLastFrameOnly) {
445420
m_executeOnLastFrameOnly = true;
446421
}

0 commit comments

Comments
 (0)