Clear Filters
Clear Filters

Measuring different parts of a region within a binary-masked image

30 views (last 30 days)
Hello,
I am attempting to quantify the dimensions of a region within timelapse frames (here, measuring an area of sand in water) for several thousand images. I want to measure the relative width of the sand area of each image, including local maxima and minima as well as the sediment-water boundary length (see magenta bars/line in 'SurfaceTestRGBandBinaryMaskMeas.jpg').
I am using the Matlab Image Processing Toolbox and, using the Image Batch Processor, I have been able to use the Image Segmenter and imcrop functions to isolate the sand and water from frame artifacts (see cyan box in 'SurfaceTestRGBandBinaryMaskMeas.jpg') and convert the RGB image into a binary mask (see attached code), but after that, I'm at a loss for how to get the measurements I indicated above without measuring each frame by hand.
Additionally, due to lighting issues for some of the frames, the binary masking sometimes does not separate dark areas of sand from the water, which sometimes results in being unable to measure part of the sand boundary (see binary image in attached jpg).
Any help is much appreciated!

Accepted Answer

Taylor
Taylor on 2 Jul 2024 at 19:37
I used the Color Thresholder app to create a function that transforms the cropped image from the RGB to Lab colorspace and thesholds the second channel at "0" (max value doesn't really matter). Then I used the Image Region Analyzer app to create a function that fills holes in the BW image/logical mask and filters out smaller areas (<1000). Then I sum each row and report the maximum and minimum values of each sum and where they occur. Hope this helps!
img = imread("SurfaceTestRGB.jpg");
imshow(img)
imgCrop = imcrop(img, [364 67 2008 1675]);
imshow(imgCrop)
[bwCrop, imgCropMasked] = createMask(imgCrop);
imshow(imgCropMasked)
[bwCropFilt, properties] = filterRegions(bwCrop);
imshow(bwCropFilt)
rowSums = sum(bwCropFilt, 2);
[maxRowWidth, maxRowWidthIdx] = max(rowSums);
[minRowWidth, minRowWidthIdx] = min(rowSums);
disp("The maximum width is " + maxRowWidth + " and occurs at row " + maxRowWidthIdx)
The maximum width is 968 and occurs at row 625
disp("The minimum width is " + minRowWidth + " and occurs at row " + minRowWidthIdx)
The minimum width is 712 and occurs at row 6
areaSand = nnz(bwCropFilt) / numel(bwCropFilt);
disp("The area of sand in the image is " + areaSand)
The area of sand in the image is 0.44632
function [BW,maskedRGBImage] = createMask(RGB)
%createMask Threshold RGB image using auto-generated code from colorThresholder app.
% [BW,MASKEDRGBIMAGE] = createMask(RGB) thresholds image RGB using
% auto-generated code from the colorThresholder app. The colorspace and
% range for each channel of the colorspace were set within the app. The
% segmentation mask is returned in BW, and a composite of the mask and
% original RGB images is returned in maskedRGBImage.
% Auto-generated by colorThresholder app on 02-Jul-2024
%------------------------------------------------------
% Convert RGB image to chosen color space
I = rgb2lab(RGB);
% Define thresholds for channel 1 based on histogram settings
channel1Min = 2.494;
channel1Max = 99.201;
% Define thresholds for channel 2 based on histogram settings
channel2Min = 0;
channel2Max = 30.353;
% Define thresholds for channel 3 based on histogram settings
channel3Min = -4.991;
channel3Max = 59.831;
% Create mask based on chosen histogram thresholds
sliderBW = (I(:,:,1) >= channel1Min ) & (I(:,:,1) <= channel1Max) & ...
(I(:,:,2) >= channel2Min ) & (I(:,:,2) <= channel2Max) & ...
(I(:,:,3) >= channel3Min ) & (I(:,:,3) <= channel3Max);
BW = sliderBW;
% Initialize output masked image based on input image.
maskedRGBImage = RGB;
% Set background pixels where BW is false to zero.
maskedRGBImage(repmat(~BW,[1 1 3])) = 0;
end
function [BW_out,properties] = filterRegions(BW_in)
%filterRegions Filter BW image using auto-generated code from imageRegionAnalyzer app.
% Auto-generated by imageRegionAnalyzer app on 02-Jul-2024
%---------------------------------------------------------
BW_out = BW_in;
% Fill holes in regions.
BW_out = imfill(BW_out, 'holes');
% Filter image based on image properties.
BW_out = bwpropfilt(BW_out,'Area',[1000 + eps(1000), Inf]);
% Get properties.
properties = regionprops(BW_out, {'Area', 'Circularity', 'Eccentricity', 'EquivDiameter', 'EulerNumber', 'MajorAxisLength', 'MinorAxisLength', 'Orientation', 'Perimeter'});
end
  1 Comment
William
William on 3 Jul 2024 at 21:05
That's exaclty what I was looking for, thank you so much! I'll aslo add that for some of my frames, the sand color changed due to lighting changes, and I found that using Graph Cut in the Image Segmenter and marking foreground and background worked pretty well for images with varying lighting/sand color. Thanks again!

Sign in to comment.

More Answers (0)

Categories

Find more on Image Processing Toolbox in Help Center and File Exchange

Products


Release

R2023b

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!