Skip to content

Commit 6b1e6ed

Browse files
committed
Fixed merge
2 parents c8e8c81 + cc3ceb5 commit 6b1e6ed

File tree

5 files changed

+225
-165
lines changed

5 files changed

+225
-165
lines changed

source/FAST/Visualization/ImagePyramidRenderer/ImagePyramidRenderer.cpp

Lines changed: 105 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -76,15 +76,94 @@ void ImagePyramidRenderer::loadAttributes() {
7676
setSharpening(getBooleanAttribute("sharpening"));
7777
}
7878

79-
void
80-
ImagePyramidRenderer::draw(Matrix4f perspectiveMatrix, Matrix4f viewingMatrix, float zNear, float zFar, bool mode2D,
81-
int viewWidth,
82-
int viewHeight) {
79+
int ImagePyramidRenderer::loadTileTexture(std::string tileID) {
80+
// Check if tile has been processed before
81+
if(mTexturesToRender.count(tileID) > 0)
82+
return 0;
83+
84+
//std::cout << "Loading tile " << tileID << " queue size: " << m_tileQueue.size() << std::endl;
85+
86+
// Create texture
87+
auto parts = split(tileID, "_");
88+
if(parts.size() != 3)
89+
throw Exception("incorrect tile format");
90+
91+
int level = std::stoi(parts[0]);
92+
int tile_x = std::stoi(parts[1]);
93+
int tile_y = std::stoi(parts[2]);
94+
//std::cout << "Creating texture for tile " << tile_x << " " << tile_y << " at level " << level << std::endl;
95+
Image::pointer tile;
96+
{
97+
auto access = m_input->getAccess(ACCESS_READ);
98+
try {
99+
tile = access->getPatchAsImage(level, tile_x, tile_y, false);
100+
} catch(Exception &e) {
101+
//reportWarning() << "Error occured while trying to open patch " << tile_x << " " << tile_y << reportEnd();
102+
// Tile was missing, just skip it..
103+
std::lock_guard<std::mutex> lock(m_tileQueueMutex);
104+
mTexturesToRender[tileID] = 0;
105+
return 0;
106+
}
107+
if(m_postProcessingSharpening) {
108+
m_sharpening->setInputData(tile);
109+
tile = m_sharpening->updateAndGetOutputData<Image>();
110+
}
111+
}
112+
auto tileAccess = tile->getImageAccess(ACCESS_READ);
113+
// Copy data from CPU to GL texture
114+
GLuint textureID;
115+
glGenTextures(1, &textureID);
116+
glBindTexture(GL_TEXTURE_2D, textureID);
117+
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
118+
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
119+
120+
// TODO Why is this needed:
121+
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
122+
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
123+
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
124+
125+
if(tile->getWidth()*tile->getNrOfChannels() % 4 != 0) {
126+
// By default OpenGL assumes that the start of each row of an image is aligned 4 bytes.
127+
// Thus we have to change this here
128+
glPixelStorei(GL_UNPACK_ALIGNMENT, 1); // Fix alignment issues
129+
}
130+
131+
// WSI data from openslide is stored as ARGB, need to handle this here: BGRA and reverse
132+
if(m_input->isBGRA()) {
133+
glTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGBA, tile->getWidth(), tile->getHeight(), 0, GL_BGRA,
134+
GL_UNSIGNED_BYTE,
135+
tileAccess->get());
136+
} else {
137+
if(tile->getNrOfChannels() == 3) {
138+
glTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGB, tile->getWidth(), tile->getHeight(), 0, GL_RGB,
139+
GL_UNSIGNED_BYTE,
140+
tileAccess->get());
141+
} else if(tile->getNrOfChannels() == 4) {
142+
glTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGBA, tile->getWidth(), tile->getHeight(), 0, GL_RGBA,
143+
GL_UNSIGNED_BYTE,
144+
tileAccess->get());
145+
}
146+
}
147+
GLint compressedImageSize = 0;
148+
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &compressedImageSize);
149+
glBindTexture(GL_TEXTURE_2D, 0);
150+
glFinish(); // Make sure texture is done before adding it
151+
152+
{
153+
std::lock_guard<std::mutex> lock(m_tileQueueMutex);
154+
mTexturesToRender[tileID] = textureID;
155+
}
156+
return compressedImageSize;
157+
}
158+
159+
void ImagePyramidRenderer::draw(Matrix4f perspectiveMatrix, Matrix4f viewingMatrix, float zNear, float zFar, bool mode2D,
160+
int viewWidth,
161+
int viewHeight) {
83162
auto dataToRender = getDataToRender();
84163
if(dataToRender.empty())
85164
return;
86165

87-
if(!m_bufferThread) {
166+
if(!m_bufferThread && m_view != nullptr) {
88167
// Create thread to load patches
89168
// Create a GL context for the thread which is sharing with the context of the view
90169
auto context = new QGLContext(View::getGLFormat(), m_view);
@@ -139,83 +218,7 @@ ImagePyramidRenderer::draw(Matrix4f perspectiveMatrix, Matrix4f viewingMatrix, f
139218
m_tileQueue.pop_back();
140219
}
141220

142-
// Check if tile has been processed before
143-
if(mTexturesToRender.count(tileID) > 0)
144-
continue;
145-
146-
//std::cout << "Loading tile " << tileID << " queue size: " << m_tileQueue.size() << std::endl;
147-
148-
// Create texture
149-
auto parts = split(tileID, "_");
150-
if(parts.size() != 3)
151-
throw Exception("incorrect tile format");
152-
153-
int level = std::stoi(parts[0]);
154-
int tile_x = std::stoi(parts[1]);
155-
int tile_y = std::stoi(parts[2]);
156-
//std::cout << "Creating texture for tile " << tile_x << " " << tile_y << " at level " << level << std::endl;
157-
Image::pointer tile;
158-
{
159-
auto access = m_input->getAccess(ACCESS_READ);
160-
try {
161-
tile = access->getPatchAsImage(level, tile_x, tile_y, false);
162-
} catch(Exception &e) {
163-
//reportWarning() << "Error occured while trying to open patch " << tile_x << " " << tile_y << reportEnd();
164-
// Tile was missing, just skip it..
165-
std::lock_guard<std::mutex> lock(m_tileQueueMutex);
166-
mTexturesToRender[tileID] = 0;
167-
continue;
168-
}
169-
if(m_postProcessingSharpening) {
170-
m_sharpening->setInputData(tile);
171-
tile = m_sharpening->updateAndGetOutputData<Image>();
172-
}
173-
}
174-
auto tileAccess = tile->getImageAccess(ACCESS_READ);
175-
// Copy data from CPU to GL texture
176-
GLuint textureID;
177-
glGenTextures(1, &textureID);
178-
glBindTexture(GL_TEXTURE_2D, textureID);
179-
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
180-
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
181-
182-
// TODO Why is this needed:
183-
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
184-
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
185-
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
186-
187-
if(tile->getWidth()*tile->getNrOfChannels() % 4 != 0) {
188-
// By default OpenGL assumes that the start of each row of an image is aligned 4 bytes.
189-
// Thus we have to change this here
190-
glPixelStorei(GL_UNPACK_ALIGNMENT, 1); // Fix alignment issues
191-
}
192-
193-
// WSI data from openslide is stored as ARGB, need to handle this here: BGRA and reverse
194-
if(m_input->isBGRA()) {
195-
glTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGBA, tile->getWidth(), tile->getHeight(), 0, GL_BGRA,
196-
GL_UNSIGNED_BYTE,
197-
tileAccess->get());
198-
} else {
199-
if(tile->getNrOfChannels() == 3) {
200-
glTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGB, tile->getWidth(), tile->getHeight(), 0, GL_RGB,
201-
GL_UNSIGNED_BYTE,
202-
tileAccess->get());
203-
} else if(tile->getNrOfChannels() == 4) {
204-
glTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGBA, tile->getWidth(), tile->getHeight(), 0, GL_RGBA,
205-
GL_UNSIGNED_BYTE,
206-
tileAccess->get());
207-
}
208-
}
209-
GLint compressedImageSize = 0;
210-
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &compressedImageSize);
211-
glBindTexture(GL_TEXTURE_2D, 0);
212-
glFinish(); // Make sure texture is done before adding it
213-
214-
{
215-
std::lock_guard<std::mutex> lock(m_tileQueueMutex);
216-
mTexturesToRender[tileID] = textureID;
217-
}
218-
memoryUsage += compressedImageSize;
221+
memoryUsage += loadTileTexture(tileID);
219222
//std::cout << "Texture cache in ImagePyramidRenderer using " << (float)memoryUsage / (1024 * 1024) << " MB" << std::endl;
220223
}
221224
});
@@ -257,7 +260,7 @@ ImagePyramidRenderer::draw(Matrix4f perspectiveMatrix, Matrix4f viewingMatrix, f
257260
float percentageShownX = (float)width / fullWidth;
258261
float percentageShownY = (float)height / fullHeight;
259262
// With current level, do we have have enough pixels to fill the view?
260-
if(percentageShownX * levelWidth > m_view->width() && percentageShownY * levelHeight > m_view->height()) {
263+
if(percentageShownX * levelWidth > viewWidth && percentageShownY * levelHeight > viewHeight) {
261264
// If yes, stop here
262265
levelToUse = level;
263266
break;
@@ -349,26 +352,30 @@ ImagePyramidRenderer::draw(Matrix4f perspectiveMatrix, Matrix4f viewingMatrix, f
349352
))
350353
continue;
351354

352-
353-
// Is patch in cache?
354-
bool textureReady = false;
355355
uint textureID;
356-
{
357-
std::lock_guard<std::mutex> lock(m_tileQueueMutex);
358-
textureReady = mTexturesToRender.count(tileString) > 0;
359-
}
360-
if(!textureReady) {
361-
// Add to queue if not in cache
356+
if(m_view != nullptr) {
357+
// Is patch in cache?
358+
bool textureReady = false;
362359
{
363360
std::lock_guard<std::mutex> lock(m_tileQueueMutex);
364-
// Remove any duplicates first
365-
m_tileQueue.remove(tileString); // O(n) time complexity..
366-
m_tileQueue.push_back(tileString);
367-
//std::cout << "Added tile " << tileString << " to queue" << std::endl;
361+
textureReady = mTexturesToRender.count(tileString) > 0;
362+
}
363+
if(!textureReady) {
364+
// Add to queue if not in cache
365+
{
366+
std::lock_guard<std::mutex> lock(m_tileQueueMutex);
367+
// Remove any duplicates first
368+
m_tileQueue.remove(tileString); // O(n) time complexity..
369+
m_tileQueue.push_back(tileString);
370+
//std::cout << "Added tile " << tileString << " to queue" << std::endl;
371+
}
372+
m_queueEmptyCondition.notify_one();
373+
continue;
374+
} else {
375+
textureID = mTexturesToRender[tileString];
368376
}
369-
m_queueEmptyCondition.notify_one();
370-
continue;
371377
} else {
378+
int bytes = loadTileTexture(tileString);
372379
textureID = mTexturesToRender[tileString];
373380
}
374381

source/FAST/Visualization/ImagePyramidRenderer/ImagePyramidRenderer.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ class FAST_EXPORT ImagePyramidRenderer : public Renderer {
5252
std::shared_ptr<ImagePyramid> m_input;
5353

5454
void drawTextures(Matrix4f &perspectiveMatrix, Matrix4f &viewingMatrix, bool mode2D);
55+
int loadTileTexture(std::string tileID);
5556
};
5657

5758
}

source/FAST/Visualization/RenderToImage/Tests.cpp

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,12 @@
1010
#include <FAST/Visualization/VolumeRenderer/MaximumIntensityProjection.hpp>
1111
#include <FAST/Visualization/SliceRenderer/SliceRenderer.hpp>
1212
#include <FAST/Visualization/VolumeRenderer/ThresholdVolumeRenderer.hpp>
13+
#include <FAST/Visualization/ImagePyramidRenderer/ImagePyramidRenderer.hpp>
14+
#include <FAST/Importers/WholeSlideImageImporter.hpp>
15+
#include <FAST/Algorithms/ImagePatch/PatchGenerator.hpp>
16+
#include <FAST/Algorithms/TissueSegmentation/TissueSegmentation.hpp>
17+
#include <FAST/Algorithms/ImagePatch/PatchStitcher.hpp>
18+
#include <FAST/Algorithms/RunUntilFinished/RunUntilFinished.hpp>
1319

1420
using namespace fast;
1521

@@ -83,3 +89,40 @@ TEST_CASE("RenderToImage 3D geom", "[fast][RenderToImage]") {
8389
auto toImage = RenderToImage::create(Color::White(), 1024 )->connect(renderer2);
8490
ImageExporter::create("test_render_to_image_3d_geom.png")->connect(toImage)->run();
8591
}
92+
TEST_CASE("RenderToImage image pyramid", "[fast][RenderToImage]") {
93+
auto importer = WholeSlideImageImporter::create(Config::getTestDataPath() + "/WSI/CMU-1.svs");
94+
95+
auto renderer = ImagePyramidRenderer::create()->connect(importer);
96+
97+
auto toImage = RenderToImage::create()->connect(renderer);
98+
auto image = toImage->runAndGetOutputData<Image>();
99+
100+
//auto renderer2 = ImageRenderer::create()->connect(image);
101+
//SimpleWindow2D::create()->connect(renderer2)->run();
102+
103+
ImageExporter::create("test_render_to_image_image_pyramid.png")->connect(image)->run();
104+
}
105+
106+
TEST_CASE("RenderToImage image pyramid + segmentation", "[fast][RenderToImage]") {
107+
auto importer = WholeSlideImageImporter::create(Config::getTestDataPath() + "/WSI/CMU-1.svs");
108+
109+
auto generator = PatchGenerator::create(512, 512, 1, 1)->connect(importer);
110+
111+
auto segmentation = TissueSegmentation::create()->connect(generator);
112+
113+
auto stitcher = PatchStitcher::create()->connect(segmentation);
114+
115+
auto finish = RunUntilFinished::create()->connect(stitcher);
116+
117+
auto renderer = ImagePyramidRenderer::create()->connect(importer);
118+
119+
auto renderer2 = SegmentationRenderer::create()->connect(finish);
120+
121+
auto toImage = RenderToImage::create()->connect({renderer, renderer2});
122+
auto image = toImage->runAndGetOutputData<Image>();
123+
124+
//auto renderer3 = ImageRenderer::create()->connect(image);
125+
//SimpleWindow2D::create()->connect(renderer3)->run();
126+
127+
ImageExporter::create("test_render_to_image_image_pyramid_and_segmentation.png")->connect(image)->run();
128+
}

0 commit comments

Comments
 (0)