This example shows how to process a big image efficiently by using a mask to isolate ROIs.
Some sources of large images have meaningful data in only a small portion of the image. You can improve the total processing time by limiting processing to the regions of interest (ROI) containing meaningful data. Use a mask to define ROIs. A mask is a logical image in which true
pixels represent the ROI.
In the bigimage
workflow, the mask represents the same spatial region as the image data but it does not need not have the size as the image. To further improve the efficiency of the workflow, create a mask from a coarse image, especially one that fits in memory. Then, use the coarse mask to process the finer images.
Create a bigimage
using a modified version of image "tumor_091.tif" from the CAMELYON16 data set. The original image is a training image of a lymph node containing tumor tissue. The original image has eight resolution levels, and the finest level has resolution 53760-by-61440. The modified image has only three coarse resolution levels. The spatial referencing of the modified image has been adjusted to enforce a consistent aspect ratio and to register features at each level.
bim = bigimage('tumor_091R.tif');
Display the big image by using the bigimageshow
function.
bigimageshow(bim);
Determine the image size at the coarsest level.
coarseLevel = bim.CoarsestResolutionLevel; coarseLevelSize = bim.LevelSizes(coarseLevel,:)
coarseLevelSize = 1×2
625 670
Get the image at the coarsest resolution level.
imLowRes = bim.getFullLevel(bim.CoarsestResolutionLevel);
You can generate a mask from the coarse level using the Image Segmenter app. The app expects a grayscale input image, so get the lightness channel from the coarse image.
imLowResL = rgb2lightness(imLowRes);
To run Image Segmenter, enter this command in the command window:
imageSegmenter(imLowResL)
You can export the mask, BW
, or code that reproduces the processing pipleline used to create a mask. This section of the example uses code that the app exports. Run this code to create a mask from the coarse input image.
%---------------------------------------------------- % Normalize input data to range in [0,1]. Xmin = min(imLowResL(:)); Xmax = max(imLowResL(:)); if isequal(Xmax,Xmin) imLowResL = 0*imLowResL; else imLowResL = (imLowResL - Xmin) ./ (Xmax - Xmin); end % Threshold image - global threshold BW = imbinarize(imLowResL); % Invert mask BW = imcomplement(BW); % Open mask with square width = 3; se = strel('square', width); BW = imopen(BW, se); %---------------------------------------------------- imshow(BW)
Create a bigimage
from the mask with the same spatial referencing as the input mask.
bmask = bigimage(BW, ... 'SpatialReferencing',bim.SpatialReferencing(coarseLevel));
Display the mask with a green background.
h = bigimageshow(bim); h.Parent.Color = 'g'; h.Parent.Alphamap = [1 .5]; h.AlphaData = bmask; h.AlphaDataMapping = 'direct';
The apply
function processes big images one block at a time. You can use the 'InclusionThreshold'
property with the mask to specify which blocks the apply
function uses. The inclusion threshold specifies the percentage of mask pixels that must be true
for apply
to process the block.
Highlight the blocks that apply
will process using the default inclusion threshold, 0.5
. Only center blocks, highlighted in green, will be processed.
h = bigimageshow(bim);
showmask(h,bmask,1);
title('Mask with Default Inclusion Threshold')
To process more blocks of the image, decrease the inclusion threshold.
showmask(h,bmask,1,'InclusionThreshold',0.4); title('InclusionThreshold == 0.4')
In the extreme case, process all blocks that have at least a single true
pixel in the mask. To specify this option, set the 'InclusionThreshold'
property to 0
. Not all blocks of the image are included.
showmask(h,bmask,1,'InclusionThreshold',0); title('InclusionThreshold == 0')
Using the mask with any value of 'InclusionThreshold'
will decrease the total execution time because apply
will process fewer blocks than the full image. The benefit of using a mask becomes more significant as the image increases in resolution and as the processing pipelines increases in complexity.
Measure the execution time of filtering the full image.
tic bout = apply(bim,1, ... @(block)imnlmfilt(block,'DegreeOfSmoothing',15)); tFullProcessing = toc;
Measure the execution time of filtering only blocks with the ROI.
tic boutMasked = apply(bim,1, ... @(block)imnlmfilt(block,'DegreeOfSmoothing',15), ... 'Mask',bmask,'InclusionThreshold',0); tMaskedProcessing = toc; bigimageshow(boutMasked) defaultBlockSize = bim.BlockSize(1,:); title(['Processed Image Using Mask with Default BlockSize == [' ... num2str(defaultBlockSize) ']']);
Compare the execution time of processing the full image compared to processing only blocks in the ROI.
disp(['Speedup using mask: ' ... num2str(tFullProcessing/tMaskedProcessing) 'x']);
Speedup using mask: 1.6817x
You can decrease the block size to gain a tighter wrap around the ROI. For some block sizes, this will reduce the execution time because apply
will process fewer pixels outside the ROI. However, if the block size is too small, then performance will decrease because the overhead of processing a larger number of block will offset the reduction in the number of pixels processed.
Highlight the blocks that apply
will process using a smaller block size. To specify the block size, set the 'BlockSize'
property.
blockSize = [512 512]; h = bigimageshow(bim); showmask(h,bmask,1,'BlockSize',blockSize,'InclusionThreshold',0); title(['BlockSize == [' num2str(blockSize) '], InclusionThreshold == 0'])
Measure the execution time of filtering all blocks within the ROI with a decreased block size.
tic boutMasked = apply(bim,1, ... @(block)imnlmfilt(block,'DegreeOfSmoothing',15), ... 'Mask',bmask,'BlockSize',blockSize,'InclusionThreshold',0); tSmallerBlockProcessing = toc; bigimageshow(boutMasked); title(['Processed Image Using Mask with BlockSize == [' ... num2str(blockSize) ']']);
Compare the execution time of processing the entire ROI with smaller blocks compared to larger blocks.
disp(['Additional speedup using mask with decreased block size: ' ... num2str(tMaskedProcessing/tSmallerBlockProcessing) 'x']);
Additional speedup using mask with decreased block size: 1.3684x