MATLAB: Segment Image - matlab

New to MATLAB and image processing.I need to know how to segment an image into foreground and background, then generate a binary image as output.
I need this as an output:
I have already tried to accomplish this with online tutorials and this is what i managed to get:
Its a good start but not exactly what i need.
My Code:
I = imread('AssignmentInput.jpg');
figure;
imshow(I);
title('Step-1: Load input image');
img_filtered = I;
for c = 1 : 3
img_filtered(:, :, c) = medfilt2(I(:, :, c), [3, 3]);
end
figure;
imshow(img_filtered);
title('Step-3:Noise Removal');
H = fspecial('gaussian'); % Create the filter kernel.
img_filtered = imfilter(img_filtered,H); % Blur the image.
Mask = im2bw(img_filtered, 0.9); % Now we are generating the binary mask.
img_filtered([Mask, Mask, Mask]) = 0; % Now we have the image.
figure;
imshow(img_filtered);
title('Step-5:Segmented Image');

For a better noise removal process and cleaner separation between foreground and background, you can also add morphological operations like:
se = strel('square',2);
I = imclose(I,se);
You can try out different variations of 'strel' class also. The image below is after imclose operation with a square of 2 pixel

Related

How to smoothen the edges from an image obtained from imagesc function

I have an RGB image obtained from saving the imagesc function as shown below. how to refine/smoothen the edges present in the image.
It consists of sharper edges, where I need to smoothen them. Im not able to find a solution for performing this for an RGB image. Instead of the staircase effect seen in the image I'd like to even out the edges. Please help thanks in advance.
maybe imresize will help you:
% here im just generating an image similar to yours
A = zeros(20);
for ii = -2:2
A = A + (ii + 3)*diag(ones(20-abs(ii),1),ii);
end
A([1:5 16:20],:) = 0;A(:,[1:5 16:20]) = 0;
subplot(121);
imagesc(A);
title('original')
% resizing image with bi-linear interpolation
B = imresize(A,100,'bilinear');
subplot(122);
imagesc(B);
title('resized')
EDIT
here I do resize + filtering + rounding:
% generates image
A = zeros(20);
for ii = -2:2
A = A + (ii + 3)*diag(ones(20-abs(ii),1),ii);
end
A([1:5 16:20],:) = 0;A(:,[1:5 16:20]) = 0;
subplot(121);
imagesc(A);
title('original')
% resizing
B = imresize(A,20,'nearest');
% filtering & rounding
C = ceil(imgaussfilt(B,8));
subplot(122);
imagesc(C);
title('resized')
solution
use imfilter and fspecial to perform a convolution of you image with gaussian.
I = imread('im.png');
H = fspecial('gaussian',5,5);
I2 = imfilter(I,H);
change 'blurlevel' parameter (which determines the gaussian kernel size) to make the image smoother or sharper.
result
If you are just looking for straighter edges, like an elevation map you can try contourf.
cmap = colormap();
[col,row] = meshgrid(1:size(img,2), 1:size(img,1));
v = linspace(min(img(:)),max(img(:)),size(cmap,1));
contourf(col,row,img,v,'edgecolor','none');
axis('ij');
This produces the following result using a test function that I generated.

code to define mask in matlab

I am working on developing a CBIR system, where I have to segment my RGB image in the following way:
I am implementing the code in matlab, but I am unable to build the proper masks for it.
I used imellipse but that requires the image handle which is achieved using imshow, but I don't want to show my image.
My code is
img=imread('peppers.png');
h_im=imshow(img); %I want to get rid of imshow because I don't want to show the image
[height, width, planes]=size(img);
%(cX,cY) is image center
cX=width/2;
cY=(height)/2;
%Here I define my ROI which is an ellipse that stretches to 75 percent of
%height and width of the image
e=imellipse(gca,[(1/2-3/8)*width, (1/2-3/8)*height,(3/4)*width,(3/4)*height]);
mymask=createMask(e,h_im);
%extending mask to three channels
mymask=repmat(mymask,[1 1 3]);
ROI=img;
ROI(mymask==0)=0;
figure, imshow(ROI);
You can generate the ellipse mask yourself rather than using the imellipse command.
% Create a meshgrid the same size of the image in order to generate the mask
[x y] = meshgrid(1:size(img, 1), 1:size(img, 2));
% Create the eclipse mask using the general form of an eclipse
% This will be centered in the middle of the image
% and have a height and width of 75% of th eimage
A = (0.75/2)*size(img, 2);
B = (0.75/2)*size(img, 1);
mask = A^2*(x - floor(size(img, 1)/2)).^2 + B^2*(y - floor(size(img, 2)/2)).^2<=A^2*B^2;
% Apply the eclipse mask
masked_image = img.*repmat(mask, [1, 1, 3]);
A bit of a hack but you could create a 'hidden' figure. The only difference is that I added: figure('Visible', 'off') at the start of your code.
figure('Visible', 'off');
img=imread('peppers.png');
h_im = imshow(img); %I want to get rid of imshow because I don't want to show the image
[height, width, planes]=size(img);
%(cX,cY) is image center
cX=width/2;
cY=(height)/2;
%Here I define my ROI which is an ellipse that stretches to 75 percent of
%height and width of the image
e=imellipse(gca,[(1/2-3/8)*width, (1/2-3/8)*height,(3/4)*width,(3/4)*height]);
mymask=createMask(e,h_im);
%extending mask to three channels
mymask=repmat(mymask,[1 1 3]);
ROI=img;
ROI(mymask==0)=0;
figure, imshow(ROI);
I think this code is easy to understand and easily adjustable to address an arbitrary part of your image (It has the same output as your code) :
img = imread('peppers.png');
% define the size of your ellipse relatively to the image dimension
factor = 0.75;
hwidth = size(img, 2) / 2.0;
hheight = size(img, 1) / 2.0;
a = hwidth * factor;
b = hheight * factor;
[x, y] = meshgrid(1:hwidth, 1:hheight);
% simple ellipse equation gets us part three of your mask
bottom_right_mask = (((x.^2)/a^2+(y.^2)/b^2)<=1);
% flip to get the remaining ones
top_right_mask = flipud(bottom_right_mask);
bottom_left_mask = fliplr(bottom_right_mask);
top_left_mask = flipud(bottom_left_mask);
mask = [top_left_mask, top_right_mask; ...
bottom_left_mask, bottom_right_mask];
multichannel_mask = repmat(mask,[1 1 3]);
ROI = img;
ROI(multichannel_mask==0) = 0;
figure;
imshow(ROI);

"Cut out" an image based on edge detection

This is my original image:
I've asked the user to crop it, converted it to grayscale and ran this line of code on it:
edgeImg = edge(grayImg,'canny',0.23);
This is the result:
I want to "cut out" everything the middle circle and the outside edge, essentially. I'm having a really hard time figuring out how to do this and honestly I'm at a loss.
I considered trying to fill in the area that I want to keep in the binary image, and then I could use it as a stamp, but I couldn't come up with a way that didn't fill in the middle circle as well.
Any ideas?
Thanks.
EDIT: This white area is what I want to keep:
I would suggest not to do edge detection in the first place, you are loosing valuable information related to color. You may try some clustering algorithm, like K-Means (including source code) or whatever else.
After clustering is done, you may keep pixels that are related to cluster(s) with the object. Desirable cluster(s) may be selected based on the object position in the image (including cropping of the image) and its color.
Code example of K-Means clustering for 2 clusters is below:
he = imread('D:\1.jpg');
imshow(he);
cform = makecform('srgb2lab');
lab_he = applycform(he,cform);
ab = double(lab_he(:,:,2:3));
nrows = size(ab,1);
ncols = size(ab,2);
ab = reshape(ab,nrows*ncols,2);
%One cluster for your object and one for background
nColors = 2;
[cluster_idx, cluster_center] = kmeans(ab,nColors,'distance','sqEuclidean', ...
'Replicates',2);
pixel_labels = reshape(cluster_idx,nrows,ncols);
segmented_images = cell(1,3);
rgb_label = repmat(pixel_labels,[1 1 3]);
for k = 1:nColors
color = he;
color(rgb_label ~= k) = 0;
segmented_images{k} = color;
end
%Show both clusters: object and non-object
imshow(segmented_images{1});
figure;
imshow(segmented_images{2});
The resulting segmentation is quite good:
Instead of using K-Means, you can simply use the color thresholder since you have so much color information. Then you can call the mask function automatically generated called createMask and further post process your image there. The code is below. The best part of this method is that createMask is reusable for any image, not just your own!
% Read Image
I = imread('r8ATB.jpg');
figure; imshow( I );
% Crop Image
C = I(75:490,40:460,:);
figure; imshow( C );
% Plot Noisy Mask
[BW,MK] = createMask( C );
figure; imshow( BW );
figure; imshow( BW );
% Fix Holes
imopen( ... );
This is the original image.
Cropped Image
Start threshold window.
Threshold Parameters
Created mask
Final Image
The createMask.m function that is automatically generated using my parameter is as follows.
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
% minimum/maximum values for each channel of the colorspace were set in the
% App and result in a binary mask BW and a composite image maskedRGBImage,
% which shows the original RGB image values under the mask BW.
% Auto-generated by colorThresholder app on 23-Apr-2015
%------------------------------------------------------
% Convert RGB image to chosen color space
I = rgb2hsv(RGB);
% Define thresholds for channel 1 based on histogram settings
channel1Min = 0.983;
channel1Max = 0.167;
% Define thresholds for channel 2 based on histogram settings
channel2Min = 0.205;
channel2Max = 1.000;
% Define thresholds for channel 3 based on histogram settings
channel3Min = 0.341;
channel3Max = 1.000;
% Create mask based on chosen histogram thresholds
BW = ( (I(:,:,1) >= channel1Min) | (I(:,:,1) <= channel1Max) ) & ...
(I(:,:,2) >= channel2Min ) & (I(:,:,2) <= channel2Max) & ...
(I(:,:,3) >= channel3Min ) & (I(:,:,3) <= channel3Max);
% Invert mask
BW = ~BW;
% 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;
You can then proceed to use imopen and imclose to clean up your mask. Then apply it to the image. My method requires tuning to get it perfect as per any method, but it will give you consistent results.
To obtain the complement of your image, all you need to do is invert the mask and apply it.

MATLAB webcam background replacement

how to change real time webcam background like this vid :
https://www.youtube.com/watch?v=YhJPbeI3vVU
can someone explain what to do and where to start , do i need chromakey,opencv or something like that ? it doesnt need to be video background, image will do.
im using matlab and already done the GUI
%% object initialitation
caminf = imaqhwinfo;
mycam = char(caminf.InstalledAdaptors(end));
mycaminfo = imaqhwinfo(mycam);
resolution = char(mycaminfo.DeviceInfo.SupportedFormats(end));
vd = videoinput(mycam, 1, resolution);
%% Previewing video
vidRes = get(vd, 'VideoResolution');
nBands = get(vd, 'NumberOfBands');
hImage = image(zeros(vidRes(2), vidRes(1), nBands));
preview(vd, hImage);
thx,
regards.
Here's a bit of code that should hopefully get you in the right direction. Basically you take a background image and a foreground image and where the two differ by more than a tolerance threshold value, those pixels are considered 'foreground'. Replace these same pixels in the new background with those captured on the webcam to get your background replacement image. Do that with images taken from a video to get a dynamic background.
% User Input:
threshVal = 0; % Tolerance for difference between 'webcam' background and foreground images
%%%%%%%%%%%%
% End User input
% Read in some images
% img1 is the webcam background image
img1 = imread('http://i.imgur.com/toD56.jpg');
% bkgdImg is the image to replace the background of img1 with
bkgdImg = imread('http://i.imgur.com/dIMct6u.jpg');
% Resize bkgdImg so it is the same size as img1. With two images from the same webcam this will not be necessary.
bkgdImg = imresize(bkgdImg, [size(img1, 1), size(img1, 2)]);
% Draw on 'webcam' image to make it different
% In actual algorithm, img2 would be a second image captured with the
% webcam with the same background but different foregrounds, such as a
% person now standing in front of camera.
img2 = img1;
% Draw Ncircles number of random-colored circles. Details here are not
% important except that it adds a unique foreground to the second 'webcam'
% image
Ncircles = 10;
X = bsxfun(#plus,(1:size(img1, 1))',zeros(1,size(img1, 2)));
Y = bsxfun(#plus,(1:size(img1, 2)),zeros(size(img1, 1),1));
for k = 1:Ncircles
circleCenter = rand(1,2).*[size(img1, 1), size(img1, 2)];
circleRadii = 50*rand(1);
B = sqrt(sum(bsxfun(#minus,cat(3,X,Y),reshape(circleCenter,1,1,[])).^2,3))<=circleRadii;
Npix = sum(B(:));
img2(repmat(B, 1, 1, 3) == 1) = reshape(255*ones(Npix, 1)*rand(1,3), [], 1);
end
% Take difference of background and foreground images
% If difference is greater than threshVal, then add those pixels to the
% mask.
% The operation has to be done both ways because images are unsigned
% integers and negative values are ignored.
diffMask = ((img1 - img2) > threshVal) | ((img2 - img1) > threshVal);
% Reduce mask by taking any value along 3rd dimension as logical true
diffMask = any(diffMask, 3);
% Replicate replacement background
bkgdImgReplace = bkgdImg;
% In regions where mask is true (ie where background ~= foreground from
% webcam) replace those pixels in the replacement background image with the
% foreground pixels
bkgdImgReplace(repmat(diffMask, 1, 1, 3)) = img2(repmat(diffMask, 1, 1, 3));
% Display
figure()
subplot(2, 2, 1)
imshow(img1)
title('Original Background')
subplot(2,2,2)
imshow(img2)
title('Modified Background w/ New foreground')
subplot(2,2,3)
imshow(bkgdImg)
title('Replacement Background')
subplot(2,2,4)
imshow(bkgdImgReplace);
title('Replacement Background w/ New foreground')

Image Morphing in Matlab

I want to use mathematical morphology function in MATLAB to find the boundary of can.png image. The input image is:
I want to get a boundary such as :
I tried to use different combination and parameter using strel, imerode, imdilate , But the result is not good enough (far from the expectation)
One of my trial code is:
a = imread ('can.png');
b = im2bw(a);
SE = strel('rectangle', [10 50 ]) ;
i2 = imdilate(b,SE);
figure(1); imshow(i2);
p = ones(4);
c = b - imerode(b,p);
figure(2); imshow(c);
The output is:
Can any body help me, how to create the expected image (black background with thin boundary for the can, please? Thank you very much.
Binarize on its morphological gradient, then do a dilation with an elementary SE, fill holes and finally obtain its border (trivial given the current image). This doesn't require any magical arbitrary threshold.
im = imread('can.png');
% turn image to BW
imb = (im < 220);
% fill the holes in the image
imf = imfill(imb, 'holes');
% find the edge
ed = edge(imf);
Resulting image: