Skip to content

Commit f7f02c3

Browse files
committed
Added option to provide exact level sizes to ImagePyramid.
1 parent ff78f20 commit f7f02c3

File tree

3 files changed

+64
-26
lines changed

3 files changed

+64
-26
lines changed

source/FAST/Data/ImagePyramid.cpp

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ namespace fast {
2929

3030
int ImagePyramid::m_counter = 0;
3131

32-
ImagePyramid::ImagePyramid(int width, int height, int channels, int patchWidth, int patchHeight, ImageCompression compression, int compressionQuality, DataType dataType, std::vector<float> levelDownsamples) {
32+
ImagePyramid::ImagePyramid(int width, int height, int channels, int patchWidth, int patchHeight, ImageCompression compression, int compressionQuality, DataType dataType, std::vector<float> levelDownsamples, std::vector<Vector2i> levelSizes) {
3333
if(channels <= 0 || channels > 4)
3434
throw Exception("Nr of channels must be between 1 and 4");
3535

@@ -96,21 +96,28 @@ ImagePyramid::ImagePyramid(int width, int height, int channels, int patchWidth,
9696
float currentDownsample = 1.0f;
9797
while(true) {
9898
if(currentLevel > 0) {
99-
if(levelDownsamples.empty()) {
100-
currentWidth = std::ceil((float)width / std::pow(2, currentLevel));
101-
currentHeight = std::ceil((float)height / std::pow(2, currentLevel));
102-
if(currentLevel > 0 && (currentWidth < 1024 || currentHeight < 1024))
99+
if(!levelSizes.empty()) {
100+
if(currentLevel-1 == levelSizes.size())
103101
break;
104-
} else {
102+
if(levelSizes[currentLevel-1].x() <= 2 && levelSizes[currentLevel-1].y() <= 2)
103+
throw Exception("Invalid level size smaller than 2");
104+
currentWidth = levelSizes[currentLevel-1].x();
105+
currentHeight = levelSizes[currentLevel-1].y();
106+
} else if(!levelDownsamples.empty()) {
105107
if(currentLevel-1 == levelDownsamples.size())
106108
break;
107109
if(levelDownsamples[currentLevel-1] <= 1.0f)
108110
throw Exception("Invalid level downsample factor smaller than 1.0");
109111
currentDownsample *= levelDownsamples[currentLevel-1];
110-
currentWidth = std::ceil((float)width / currentDownsample);
111-
currentHeight = std::ceil((float)height / currentDownsample);
112+
currentWidth = round((float)width / currentDownsample);
113+
currentHeight = round((float)height / currentDownsample);
112114
if(currentWidth < 1 || currentHeight < 1)
113115
throw Exception("Invalid level downsamples resulting in width and height smaller than 1");
116+
} else {
117+
currentWidth = round((float)width / std::pow(2, currentLevel));
118+
currentHeight = round((float)height / std::pow(2, currentLevel));
119+
if(currentLevel > 0 && (currentWidth < 1024 || currentHeight < 1024))
120+
break;
114121
}
115122
}
116123

source/FAST/Data/ImagePyramid.hpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ class FAST_EXPORT ImagePyramid : public SpatialDataObject {
4141
* An alternative image pyramid structure can be achieved by specifying the downsampling factor for each level
4242
* in this list. E.g. [4, 4] will create an image pyramid with 3 levels, level 0 with the original width and height.
4343
* Level 1 with the size width/4, height/4, and level 2 with the size width/(4*4), height/(4*4).
44+
* @param levelSizes If not specified an image pyramid with the default structure is used where each level is
45+
* downsampled with a factor of 2 for each level until a level with smaller width or height than 1024 is reached.
46+
* An alternative image pyramid structure can be achieved by specifying the exact sizes for each level
47+
* in this list. If both levelDownsamples and levelSizes are provided, levelSizes is used.
4448
* @return instance
4549
*/
4650
FAST_CONSTRUCTOR(ImagePyramid,
@@ -52,7 +56,8 @@ class FAST_EXPORT ImagePyramid : public SpatialDataObject {
5256
ImageCompression, compression, = ImageCompression::UNSPECIFIED,
5357
int, compressionQuality, = 90,
5458
DataType, dataType, = TYPE_UINT8,
55-
std::vector<float>, levelDownsamples, = std::vector<float>()
59+
std::vector<float>, levelDownsamples, = std::vector<float>(),
60+
std::vector<Vector2i>, levelSizes, = std::vector<Vector2i>()
5661
);
5762
#ifndef SWIG
5863
FAST_CONSTRUCTOR(ImagePyramid, openslide_t*, fileHandle,, std::vector<ImagePyramidLevel>, levels,);

source/FAST/Data/Tests/ImagePyramidTests.cpp

Lines changed: 43 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -115,19 +115,19 @@ TEST_CASE("Create image pyramid with default levels", "[fast][ImagePyramid]") {
115115
CHECK(pyramid->getNrOfLevels() == 3);
116116
CHECK(pyramid->getWidth() == 12000);
117117
CHECK(pyramid->getHeight() == 8000);
118-
CHECK(pyramid->getLevelWidth(1) == 12000/2);
119-
CHECK(pyramid->getLevelHeight(1) == 8000/2);
120-
CHECK(pyramid->getLevelWidth(2) == 12000/4);
121-
CHECK(pyramid->getLevelHeight(2) == 8000/4);
118+
CHECK(pyramid->getLevelWidth(1) == fast::round(12000.0f/2.0f));
119+
CHECK(pyramid->getLevelHeight(1) == fast::round(8000.0f/2.0f));
120+
CHECK(pyramid->getLevelWidth(2) == fast::round(12000.0f/4.0f));
121+
CHECK(pyramid->getLevelHeight(2) == fast::round(8000.0f/4.0f));
122122
CHECK_THROWS(pyramid->getLevelWidth(3));
123123
CHECK(pyramid->getDataType() == TYPE_UINT8);
124124
CHECK(pyramid->getLevelTileWidth(0) == 512);
125125
CHECK(pyramid->getLevelTileHeight(0) == 512);
126126
CHECK(pyramid->getCompression() == ImageCompression::JPEG);
127127
CHECK(pyramid->getNrOfChannels() == 3);
128-
CHECK(pyramid->getLevelScale(0) == 1);
129-
CHECK(pyramid->getLevelScale(1) == 2);
130-
CHECK(pyramid->getLevelScale(2) == 4);
128+
CHECK(pyramid->getLevelScale(0) == Approx(1));
129+
CHECK(pyramid->getLevelScale(1) == Approx(2));
130+
CHECK(pyramid->getLevelScale(2) == Approx(4));
131131
}
132132

133133
TEST_CASE("Create image pyramid with custom levels", "[fast][ImagePyramid]") {
@@ -137,20 +137,46 @@ TEST_CASE("Create image pyramid with custom levels", "[fast][ImagePyramid]") {
137137
CHECK(pyramid->getNrOfLevels() == 4);
138138
CHECK(pyramid->getWidth() == 22000);
139139
CHECK(pyramid->getHeight() == 13000);
140-
CHECK(pyramid->getLevelWidth(1) == 22000/4);
141-
CHECK(pyramid->getLevelHeight(1) == 13000/4);
142-
CHECK(pyramid->getLevelWidth(2) == 22000/(4*2));
143-
CHECK(pyramid->getLevelHeight(2) == 13000/(4*2));
144-
CHECK(pyramid->getLevelWidth(3) == 22000/(4*2*2));
145-
CHECK(pyramid->getLevelHeight(3) == 13000/(4*2*2));
140+
CHECK(pyramid->getLevelWidth(1) == fast::round(22000.0f/4.0f));
141+
CHECK(pyramid->getLevelHeight(1) == fast::round(13000.0f/4.0f));
142+
CHECK(pyramid->getLevelWidth(2) == fast::round(22000.0f/(4.0f*2.0f)));
143+
CHECK(pyramid->getLevelHeight(2) == fast::round(13000.0f/(4.0f*2.0f)));
144+
CHECK(pyramid->getLevelWidth(3) == fast::round(22000.0f/(4.0f*2.0f*2.0f)));
145+
CHECK(pyramid->getLevelHeight(3) == fast::round(13000.0f/(4.0f*2.0f*2.0f)));
146146
CHECK_THROWS(pyramid->getLevelWidth(4));
147147
CHECK(pyramid->getDataType() == TYPE_UINT8);
148148
CHECK(pyramid->getLevelTileWidth(0) == 512);
149149
CHECK(pyramid->getLevelTileHeight(0) == 256);
150150
CHECK(pyramid->getCompression() == ImageCompression::JPEG);
151151
CHECK(pyramid->getNrOfChannels() == 1);
152-
CHECK(pyramid->getLevelScale(0) == 1);
153-
CHECK(pyramid->getLevelScale(1) == 4);
154-
CHECK(pyramid->getLevelScale(2) == 8);
155-
CHECK(pyramid->getLevelScale(3) == 16);
152+
CHECK(pyramid->getLevelScale(0) == Approx(1));
153+
CHECK(pyramid->getLevelScale(1) == Approx(4));
154+
CHECK(pyramid->getLevelScale(2) == Approx(8));
155+
CHECK(pyramid->getLevelScale(3) == Approx(16));
156+
}
157+
158+
TEST_CASE("Create image pyramid with custom level sizes", "[fast][ImagePyramid]") {
159+
std::vector<Vector2i> sizes = {
160+
{22000/1.5f, 13000/1.5f},
161+
{22000/4.5f, 13000/4.5f}
162+
};
163+
auto pyramid = ImagePyramid::create(22000, 13000, 1, 512, 512, ImageCompression::JPEG, 90,
164+
fast::TYPE_UINT8, {}, sizes);
165+
166+
CHECK(pyramid->getNrOfLevels() == 3);
167+
CHECK(pyramid->getWidth() == 22000);
168+
CHECK(pyramid->getHeight() == 13000);
169+
CHECK(pyramid->getLevelWidth(1) == sizes[0].x());
170+
CHECK(pyramid->getLevelHeight(1) == sizes[0].y());
171+
CHECK(pyramid->getLevelWidth(2) == sizes[1].x());
172+
CHECK(pyramid->getLevelHeight(2) == sizes[1].y());
173+
CHECK_THROWS(pyramid->getLevelWidth(3));
174+
CHECK(pyramid->getDataType() == TYPE_UINT8);
175+
CHECK(pyramid->getLevelTileWidth(0) == 512);
176+
CHECK(pyramid->getLevelTileHeight(0) == 512);
177+
CHECK(pyramid->getCompression() == ImageCompression::JPEG);
178+
CHECK(pyramid->getNrOfChannels() == 1);
179+
CHECK(pyramid->getLevelScale(0) == Approx(1.0f).margin(0.01));
180+
CHECK(pyramid->getLevelScale(1) == Approx(1.5f).margin(0.01));
181+
CHECK(pyramid->getLevelScale(2) == Approx(4.5f).margin(0.01));
156182
}

0 commit comments

Comments
 (0)