Use Freehand ROIs to Refine Segmentation Masks

This example shows how to refine a logical segmentation mask by converting it into a Freehand ROI object. In this method, you take advantage of the interactive reshaping capbility of the Freehand ROI object to make a better fit of the edge of the segmentation mask to the shape of the image region that you want to segment.

Introduction - Image Segmentation

Segmentation algorithms are used to segment interesting parts of an image. To illustrate, this example uses K-means clustering to segment bone and tissue in an MRI image.

im = dicomread('knee1.dcm');
segmentedLabels = imsegkmeans(im,3);
boneMask = segmentedLabels==2;
imshowpair(im, boneMask);

Post-Process the Segmentation Masks

Often, the results of automated segmentation algorithms need additional post-processing to clean up the masks. As a first step, select the two largest bones from the mask, the femur and the tibia.

boneMask = bwareafilt(boneMask, 2);
imshowpair(im, boneMask);

Convert Mask to Freehand ROI object

To refine the edges of the automatic k-means segmentation, convert the two masks into interactive freehand ROI objects. First, retrieve the locations of boundary pixels that delineate these two segmented regions. Note that these ROI objects are densely sampled--their Position property has the same resolution as the image pixels.

blocations = bwboundaries(boneMask,'noholes');
figure
imshow(im, []);
for ind = 1:numel(blocations)
    % Convert to x,y order.
    pos = blocations{ind};
    pos = fliplr(pos);
    % Create a freehand ROI.
    drawfreehand('Position', pos);
end

Edit the ROIs

The Freehand ROI object allows simple 'rubber-band' interactive edits. To edit the ROI, click and drag any of the waypoints along the ROI boundary. You can add additional waypoints anywhere on the boundary by double-clicking the ROI edge or by using the context menu accessible by right-clicking the edge.

Convert the Freehand ROIs Back to Masks

After editing the ROIs, convert these ROI objects back to binary masks using the ROI object's createMask method. Note the additional step required to include the boundary pixels in the final mask.

% Convert edited ROI back to masks.
hfhs = findobj(gca, 'Type', 'images.roi.Freehand');
editedMask = false(size(im));

for ind = 1:numel(hfhs)
    % Accumulate the mask from each ROI
    editedMask = editedMask | hfhs(ind).createMask();

    % Include the boundary of the ROI in the final mask.
    % Ref: https://blogs.mathworks.com/steve/2014/03/27/comparing-the-geometries-of-bwboundaries-and-poly2mask/
    % Here, we have a dense boundary, so we can take the slightly more
    % performant approach of just including the boundary pixels directly in
    % the mask.
    boundaryLocation = hfhs(ind).Position;
    bInds = sub2ind(size(im), boundaryLocation(:,2), boundaryLocation(:,1));
    editedMask(bInds) = true;
end

See Also

| | | | | |