I have a series of images (upwards of 100) from a Reconyx motion triggered camera, observing a compost pile, with images of animals present in the scene. They are not in constant seqeunce (i.e not like you would expect if you took a video fram by frame). What would be the best method to obtain the contours or the edges of the animal(s) present in the images while removing the background(noting that all of the images more or less share the same background). Attached are some images with just the background, and some with animals present in the foreground.
MATLAB: Foreground Object Detection from Reconyx Camera Trap Images
image processingImage Processing Toolboximage segmentation
Related Solutions
Try this:
clc; % Clear the command window.
close all; % Close all figures (except those of imtool.)
clearvars;workspace; % Make sure the workspace panel is showing.
format long g;format compact;fontSize = 16;%=======================================================================================
% Have user browse for a file, from a specified "starting folder."
% For convenience in browsing, set a starting folder from which to browse.
% startingFolder = pwd; % or 'C:\wherever';
% if ~exist(startingFolder, 'dir')
% % If that folder doesn't exist, just start in the current folder.
% startingFolder = pwd;
% end
% % Get the name of the file that the user wants to use.
% defaultFileName = fullfile(startingFolder, 'n*.*');
% [baseFileName, folder] = uigetfile(defaultFileName, 'Select a file');
% if baseFileName == 0
% % User clicked the Cancel button.
% return;
% endfolder = pwd;baseFileName = 'IMG_Segment_Failed.JPG';% baseFileName = 'IMG_Segment_Success.JPEG';
fullFileName = fullfile(folder, baseFileName);rgbImage = imread(fullFileName);% Get the dimensions of the image.
[rows1, columns1, numberOfColorChannels1] = size(rgbImage)% Display the original image.
subplot(2, 2, 1);imshow(rgbImage, []);axis('on', 'image');caption = sprintf('Original Color Image\n"%s"', baseFileName);title(caption, 'FontSize', fontSize, 'Interpreter', 'None');drawnow;hp = impixelinfo(); % Set up status line to see values when you mouse over the image.
% Set up figure properties:
% Enlarge figure to full screen.
set(gcf, 'Units', 'Normalized', 'OuterPosition', [0 0.05 1 0.95]);% Get rid of tool bar and pulldown menus that are along top of figure.
% set(gcf, 'Toolbar', 'none', 'Menu', 'none');
% Give a name to the title bar.
set(gcf, 'Name', 'Demo by Image Analyst', 'NumberTitle', 'Off')[mask, maskedRGBImage] = createMask(rgbImage);% Display the image.
subplot(2, 2, 2);imshow(mask, []);axis('on', 'image');title('Initial Mask Image', 'FontSize', fontSize, 'Interpreter', 'None');drawnow;hp = impixelinfo(); % Set up status line to see values when you mouse over the image.% Do area filtering.
mask = bwareafilt(mask, [30000, 60000]);% Display the image.subplot(2, 2, 3);imshow(mask, []);axis('on', 'image');title('Mask Image after area filtering', 'FontSize', fontSize, 'Interpreter', 'None');drawnow;hp = impixelinfo(); % Set up status line to see values when you mouse over the image.% Measure things in the mask
labeledImage = bwlabel(mask);props = regionprops(labeledImage, 'Area', 'Perimeter');allAreas = [props.Area];% sortedAreas = sort(allAreas)
allPerims = [props.Perimeter];circularities = allPerims .^ 2 ./ (4 * pi * allAreas)subplot(2, 2, 4);% histogram(circularities);
% grid on;
% Extract the roundest blob - the one with circularity closest to 1
[~, index] = min(abs(circularities - 1))mask = ismember(labeledImage, index);imshow(mask, []);axis('on', 'image');title('Mask Image after circularity filtering', 'FontSize', fontSize, 'Interpreter', 'None');drawnow;hp = impixelinfo(); % Set up status line to see values when you mouse over the image.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 19-Dec-2019
%------------------------------------------------------
% Convert RGB image to chosen color space
I = rgb2hsv(RGB);% Define thresholds for channel 1 based on histogram settings
channel1Min = 0.254;channel1Max = 0.991;% Define thresholds for channel 2 based on histogram settings
channel2Min = 0.000;channel2Max = 1.000;% Define thresholds for channel 3 based on histogram settings
channel3Min = 0.000;channel3Max = 0.523;% 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
It might need some tweaking to get it to work with all images.
Or you could try deep learning. I tried imfindcircles() and was not very successful with that function.
Threshold it and call imclose(). Untested code:
mask = grayImage > someValue;se = strel('disk', 5, 0);mask = imclose(mask, se);mask = bwareaopen(mask, 4) % Get rid of blobs smaller than 4 pixels.
props = regionprops(mask, 'Centroid');xy = vertcat(props.Centroid)
Best Answer