How to crop face from landmarks in MATLAB? - matlab

I have the locations of outer landmark points, something like this:
And now I want to "close" this contour from the outer chin and eyebrows and mask everything out to zero, so I want to obtain something like this :
So I guess I have 2 things to do :
Calculate the "outer" polygon, and create a mask out of it
Determine the inner region, multiply with binary mask, and crop from the bounding box
I'm not sure how to do either of them. Are there easy, MATLAB-specific ways for this? Or an example?
Thanks for any help!
Edit
I found roipoly from this page, but it asks the user to give the polygon I guess. Can I compute it automatically?
Edit 2
Yes I can, simply with BW = roipoly(img, c,r); img2 = img.*uint8(BW);
However, the problem still remains which landmarks to use for the contours. I can assign them rule based, but what if I want to select the "outer" landmarks automatically?
So I guess now the question is transformed into:
Given N points on 2D plane, how do I select the subset (i.e. the outer surface) that creates a polygon that covers all of the others?

You can use boundary and poly2mask as follows. I manually marked several points as you did not provide your face coordinates.
clc; clear all;
img = imread('FAm1z.jpg');
figure; imshow(img);
[x,y] = ginput(50);
k = boundary(x,y,0);
hold on;
plot(x,y,'ro');
plot(x(k),y(k));
mask = poly2mask(x(k), y(k), size(img,1), size(img,2));
mask(:,:,2) = mask;
mask(:,:,3) = mask(:,:,1);
roi = img;
roi(mask == 0) = 0;
figure; imshow(roi);
Output images:

Related

How can I traverse through pixels?

Suppose, I have the following image in my hand.
I have marked some pixels of the image as follows,
Now, I have obtained the pixel mask,
How can I traverse through only those pixels that are in that mask?
Given a binary mask, mask, where you want to iterate over all the true pixels in mask, you have at least two options that are both better than the double for loop example.
1) Logical indexing.
I(mask) = 255;
2) Use find.
linearIdx = find(mask);
I(linearIdx) = 255;
The original question:
How can I save only those pixels which I am interested in?
...
Question: Now, in the Step#2, I want to save those pixels in a data-structure (or, whatever) d so that I can apply another function f2(I, d, p,q,r) which does something on that image on the basis of those pixels d.
Create a binary mask
Try using a logical mask of the image to keep track of the pixels of interest.
I'll make up a random image for example here:
randImg = rand(64,64,3);
imgMask = false(size(randImg(:,:,1)));
imgMask(:,[1:4:end]) = true; % take every four columns This would be your d.
% Show what we are talking about
maskImg = zeros(size(randImg));
imgMaskForRGB = repmat(imgMask,1,1,3);
maskImg(imgMaskForRGB) = randImg(imgMaskForRGB);
figure('name','Psychadelic');
subplot(2,1,1);
imagesc(randImg);
title('Random image');
subplot(2,1,2);
imagesc(maskImg);
title('Masked pixels of interest');
Here's what it looks like:
It will be up to you to determine how to store and use the image mask (d in your case) as I am not sure how your functions are written. Hopefully this example will give you an understanding of how it can be done though.
EDIT
You added a second question since I posted:
But, now the problem is, how am I going to traverse through those pixels in K?
Vectrorization
To set all pixels to white:
randImg(imgMaskForRGB) = 255;
In my example, I accessed all of the pixels of interest at the same time with my mask in a vectorized fashion.
I translated my 2D mask into a 3D mask, in order to grab the RGB values of each pixel. That was this code:
maskImg = zeros(size(randImg));
imgMaskForRGB = repmat(imgMask,1,1,3);
Then to access all of these pixels in the image of interest, I used this call:
randImg(imgMaskForRGB)
These are your pixels of interest. If you want to divide these values in 1/2 you could do something like this:
randImg(imgMaskForRGB) = randImg(imgMaskForRGB)/2;
Loops
If you really want to traverse, one pixel at a time, you can always use a double for loop:
for r=1:size(randImg,1)
for c=1:size(randImg,2)
if(imgMask(r,c)) % traverse all the pixels
curPixel = randImg(r,c,:); % grab the ones that are flagged
end
end
end
Okay. I have solved this using the answer of #informaton,
I = imread('gray_bear.png');
J = rgb2gray(imread('marked_bear.png'));
mask = I-J;
for r=1:size(I,1)
for c=1:size(I,2)
if(mask(r,c))
I(r,c) = 255;
end
end
end
imshow(I);

Find points within polygon with multiple self intersections with Matlab

I have a polygon which intersects itself multiple times. I try to create a mask from this polygon, i.e., to find all points/pixels location within the polygon. I use the Matlab function poly2mask for this. However, due to the multiple self-intersections this is the results I obtain:
Resulting mask from poly2mask for multi-self-intersecting polygon
So, some areas remain unmasked, because of the intersections. I think Matlab sees this as some sort of inclusions. The Matlab help for poly2mask doesn't mention anything about this. Does anyone have an idea how to also include these regions in the mask?
I obtain good results combining a small erosion/dilation step and imfill as follows:
data = load('polygon_edge.mat');
x = data.polygon_edge(:, 1);
y = data.polygon_edge(:, 2);
bw1 = poly2mask(x,y,ceil(max(y)),ceil(max(x)));
se = strel('sphere',1);
bw2 = imerode(imdilate(bw1,se), se);
bw3 = imfill(bw2, 'holes');
figure
imshow(bw3)
hold on
plot(x(:, 1),y(:, 1),'g','LineWidth',2)
The small erosion and dilation step is needed to be sure that all the regions are connected even at places where the polygon is only connected through a single point, otherwise imfill may see some non-existing holes.
you can use inpolygon:
bw1 = poly2mask(x,y,1000,1000);
subplot(131)
imshow(bw1)
hold on
plot(x([1:end 1]),y([1:end 1]),'g','LineWidth',2)
title('using poly2mask')
[xq,yq] = meshgrid(1:1000);
[IN,ON] = inpolygon(xq,yq,x,y);
bw2 = IN | ON;
subplot(132)
imshow(bw2)
hold on
plot(x([1:end 1]),y([1:end 1]),'g','LineWidth',2)
title('using inpolygon')
% boundary - seggested by another answer
k = boundary(x, y, 1); % 1 == tightest single-region boundary
bw3 = poly2mask(x(k), y(k), 1000, 1000);
subplot(133)
imshow(bw3)
hold on
plot(x([1:end 1]),y([1:end 1]),'g','LineWidth',2)
title('using boundary')
Update - I updated my answer to include boundary - it not seems to work well in my case.
You should first calculate the boundary of your polygon and use this to create your mask.
k = boundary(x, y, 0.99); % 1 == tightest single-region boundary
BW = poly2mask(x(k), y(k), m, n)
Using a shrink factor of 0.99 instead of 1 avoids undercutting, but sharp non-convex corners are still not fitted correctly.

Matlab: Crop objects from binary image

I'm new to matlab, I have a image and i want to crop all the three circles and store them. My code works for single circle in an image. But fails to work when i have more circles in image.
My code:
im=imread('D:\capture.png');
im_gray = rgb2gray(im);
BW = im2bw(im_gray, graythresh(im));
se = strel('disk',3);
bw2=imopen(BW,se);
bw2=~bw2;
s = regionprops(bw2, 'BoundingBox');
rectangle('Position', s.BoundingBox);
imCrop = imcrop(bw2, s.BoundingBox);
figure, imshow(imCrop);
Any ideas about it?
You almost have it working. Bear in mind that when you do s.BoundingBox by itself, you are only extracting the first circle. As such, I would recommend you make a cell array that stores the individual circles for each bounding box, then run a for loop through all of your bounding boxes. As such, each element in your cell array would be a cropped circle. As such, try doing this:
%// Your code
im=imread('D:\capture.png');
im_gray = rgb2gray(im);
BW = im2bw(im_gray, graythresh(im));
se = strel('disk',3);
bw2=imopen(BW,se);
bw2=~bw2;
s = regionprops(bw2, 'BoundingBox');
%// New code here
circles = cell(1,numel(s));
for idx = 1 : numel(s)
rect = s(idx).BoundingBox;
circles{idx} = imcrop(bw2, rect);
end
circles will now be a cell array of cropped circles. To access the ith circle, simply do:
imCrop = circles{i};
Edit
From your comments, you want to detect the largest and smallest circles. This can easily be done by checking the Area attribute from regionprops. You would find the bounding box that generates the minimum and maximum areas. You would need to modify your regionprops call to include the Area flag. As such:
s = regionprops(bw2, 'BoundingBox', 'Area');
[~,indMin] = min([s.Area]);
[~,indMax] = max([s.Area]);
circleSmall = circles{indMin};
circleLarge = circles{indMax};
The above code will find the circles with the minimum and maximum area, then extract those corresponding circles, assuming you've run the code to extract all of those circles in that for loop I wrote earlier. Bear in mind that I had to enclose s.Area in square braces. The reason why is because when you do this, you'll be able to extract all of the areas as a single array instead of a matrix with singleton dimensions, and min/max can't work on something like that.

Matlab Shape Labelling

I have a set of shapes in an image I would like to label according to their area, I have used bwboundaries to find them, and regionprops to determine their area. I would like to label them such that they are labelled different based on whether their area is above or below the threshold i have determined.
I've thought about using inserObjectAnnotation, but I'm not sure how to add on a condition based on their area into the function?
Assuming TH to be the threshold area and BW to be the binary image and if you are okay with labeling them as o's and x's with matlab figure text at their centers (centroids to be exact), based on the thresholding, see if this satisfies your needs -
stats = regionprops(BW,'Area')
stats2 = regionprops(BW,'Centroid')
figure,imshow(BW)
for k = 1:numel(stats)
xy = stats2(k).Centroid
if (stats(k).Area>TH)
text(xy(1),xy(2),'L') %// Large Shape
else
text(xy(1),xy(2),'S') %// Small Shape
end
end
Sample output -
You could use CC = bwconncomp(BW,conn).
To get the number of pixels of every connected compontent you can use:
numPixels = cellfun(#numel,CC.PixelIdxList);
In CC.PixelIdxList you have a list of all found objects and the indices of the pixels belonging to the components. I guess to label your areas you could do something like:
for ind = 1:size(CC.PixelIdxList,2)
Image(CC.PixelIdxList{ind}) = ind;
end

MATLAB Boundingbox around object detected with Eigen Value Algorithm

I have an image of an object that I want to crop using the Eigen Value Algorithm, everything is fine until I want to draw a Bounding Box around the detected features to use as the area of significance.
original = imread('1.jpg');
img = rgb2gray(original);
corners = detectMinEigenFeatures(img);
figure;
imshow(original); hold on;
plot(corners.selectStrongest(4000));
%st = regionprops( corners.selectStrongest(4000), 'BoundingBox' );
%rect = st.BoundingBox;
crop = imcrop(original,rect);
figure
imshow(crop);
My problem is that the variable corners is (n x 1) and I don't know how that relates to coordinates in my original image.
your output corner is an object for storing corner points, use corner.Location to get an M-by-2 array of [x y] point coordinate.