Finding notes in an image - matlab

I have an image like this. And I want to find the location of notes. What is the best way to find them ? (They are not circle and they are so small so circlefinder can't find them !)
Image of Notes !

Here is a bit of code to get you going...that's not perfect but it was a lof of fun haha.
What I did is to erode the image with a disk structuring element until what was left in the image was shapes the looked the most like circles. Then I eroded again but this time with a line structuring element oriented at an angle close to that of the notes; I figured it's about 15 degrees.
After that, call regionprops to get the centroids, and then plot them.
Code:
clear
clc
BW = im2bw(imread('Notes.png'));
BW = imclearborder(BW);
%//Erode the image with a disk structuring element to obtain circleish
%// shapes.
se = strel('disk',2);
erodedBW = imerode(BW,se);
Here erodedBW looks like this:
%// Erode again with a line oriented at 15 degrees (to ~ match orientation of major axis of notes...very approximate haha)
se2 = strel('line',5,15);
erodedBW2 = imerode(erodedBW,se2);
erodedBW2 looks like this:
Then find centroids and plot them
S = regionprops(erodedBW2,'Centroid');
figure;
imshow(BW)
hold on
for k = 1:numel(S)
scatter(S(k).Centroid(:,1), S(k).Centroid(:,2),60,'filled')
end
Output:
Empty notes are not detected but that's manageable using other morphological operations I guess.
Hope that helps!

Related

Image Segmentation Matlab

I have this BW image:
And using the function RegionProps, it shows that some objetcs are connected:
So I used morphological operations like imerode to separte the objects to get their centroids:
Now I have all the centroids of each object separated, but to that I lost a lot of information when eroding the region, like you can see in picture 3 in comparison with picture 1.
So I was thinking if is there anyway to "dilate" the picture 3 till get closer to picture 1 but without connecting again the objects.
You might want to take a look at bwmorph(). With the 'thicken', inf name-value pair it will thicken the labels until they would overlap. It's a neat tool for segmentation. We can use it to create segmentation borders for the original image.
bw is the original image.
labels is the image of the eroded labels.
lines = bwmorph(labels, 'thicken', inf);
segmented_bw = bw & lines
You could also skip a few phases and achieve similiar results with a marker based watershed. Or even better, as the morphological seesaw has destroyed some information as seen on the poorly segmented cluster on the lower right.
You can assign each white pixel in the mask to the closest centroid and work with the resulting label map:
[y x]= find(bw); % get coordinates of mask pixels
D = pdist2([x(:), y(:)], [cx(:), cy(:)]); % assuming cx, cy are centers' coordinates
[~, lb] = min(D, [], 2); % find index of closest center
lb_map = 0*bw;
lb_map(bw) = lb; % should give you the map.
See pdist2 for more information.

Generating a bounding box around a fragmented silhouette in matlab

Hello I am working with matlab. I am trying to generate a bounding box around a silhouette. The problem here is that the silhouette is fragmented
as shown here
The code i tried is
BW=bwconncomp(image);
STATS = regionprops(BW, 'FilledArea','BoundingBox');
which gives me a bounding box around a part of the silhouette. I cannot use dilate which is the preferred morphological operation in this case as it connects the silhouette with neighboring fragments.
Thanks in advance for the help.
Here is something to get you going with the image you posted. I used a line structuring element with an angle to dilate the image and amplify the signal from the small white chunks at the left of the silhouette. Then using regionprops its easier to identify objects individually and select the object with the largest area (i.e. the silhouette), calculated with the property FilledArea, and report back the bounding box on the original image. It might not be perfect but it's a start and it seems to give a pretty decent result.
Here is the code:
clear
clc
close all
BW = im2bw(imread('Silhouette.png'));
BW = imclearborder(BW);
%// Dilate with a line structuring element oriented at about 60 degrees to
%// amplify the elements at an angle that you don't want.
se = strel('line',5,60);
dilateddBW = imdilate(BW,se);
figure;
imshow(dilateddBW)
The dilated image looks like this:
Calling regionprops and displaying the output:
%// Get the region properties and select that with the largest area.
S = regionprops(dilateddBW,'BoundingBox','FilledArea','PixelIdxList');
boundingboxes = cat(1, S.BoundingBox);
FilledAreas = cat(1,S.FilledArea);
[~,MaxAreaIndex] = max(FilledAreas);
%// Get linear indices of the corresponding silhouette to display along
%// with its bounding box.
MaxIndices = S(MaxAreaIndex).PixelIdxList;
%// Create empty image to put the silhouette + box
NewIm = false(size(dilateddBW));
NewIm(MaxIndices) = 1;
figure;
imshow(BW)
rectangle('Position',boundingboxes(MaxAreaIndex,:),'EdgeColor','r')
Output:
Hope that helps somehow!
Since you have an array of vectors containing indice of pixels(bwconncomp() returns a struct which have a member named PixelIdxList), you can create a rectangle by finding pixels with min x, min y, max x, max y.
Here is a good example: 2D Minimal Bounding Box

How to select the largest contour in MATLAB

In my progress work, I have to detect a parasite. I have found the parasite using HSV and later made it into a grey image. Now I have done edge detection too. I need some code which tells MATLAB to find the largest contour (parasite) and make the rest of the area as black pixels.
You can select the "largest" contour by filling in the holes that each contour surrounds, figure out which shape gives you the largest area, then use the locations of the largest area and copy that over to a final image. As what Benoit_11 suggested, use regionprops - specifically the Area and PixelList flags. Something like this:
im = imclearborder(im2bw(imread('http://i.stack.imgur.com/a5Yi7.jpg')));
im_fill = imfill(im, 'holes');
s = regionprops(im_fill, 'Area', 'PixelList');
[~,ind] = max([s.Area]);
pix = sub2ind(size(im), s(ind).PixelList(:,2), s(ind).PixelList(:,1));
out = zeros(size(im));
out(pix) = im(pix);
imshow(out);
The first line of code reads in your image from StackOverflow directly. The image is also a RGB image for some reason, and so I convert this into binary through im2bw. There is also a white border that surrounds the image. You most likely had this image open in a figure and saved the image from the figure. I got rid of this by using imclearborder to remove the white border.
Next, we need to fill in the areas that the contour surround, so use imfill with the holes flag. Next, use regionprops to analyze the different filled objects in the image - specifically the Area and which pixels belong to each object in the filled image. Once we obtain these attributes, find the filled contour that gives you the biggest area, then access the correct regionprops element, extract out the pixel locations that belong to the object, then use these and copy over the pixels to an output image and display the results.
We get:
Alternatively, you can use the Perimeter flag (as what Benoit_11) suggested, and simply find the maximum perimeter which will correspond to the largest contour. This should still give you what you want. As such, simply replace the Area flag with Perimeter in the third and fourth lines of code and you should still get the same results.
Since my answer was pretty much all written out I'll give it to you anyway, but the idea is similar to #rayryeng's answer.
Basically I use the Perimeter and PixelIdxList flags during the call to regionprops and therefore get the linear indices of the pixels forming the largest contour, once the image border has been removed using imclearborder.
Here is the code:
clc
clear
BW = imclearborder(im2bw(imread('http://i.stack.imgur.com/a5Yi7.jpg')));
S= regionprops(BW, 'Perimeter','PixelIdxList');
[~,idx] = max([S.Perimeter]);
Indices = S(idx).PixelIdxList;
NewIm = false(size(BW));
NewIm(Indices) = 1;
imshow(NewIm)
And the output:
As you see there are many ways to achieve the same result haha.
This could be one approach -
%// Read in image as binary
im = im2bw(imread('http://i.stack.imgur.com/a5Yi7.jpg'));
im = im(40:320,90:375); %// clear out the whitish border you have
figure, imshow(im), title('Original image')
%// Fill all contours to get us filled blobs and then select the biggest one
outer_blob = imfill(im,'holes');
figure, imshow(outer_blob), title('Filled Blobs')
%// Select the biggest blob that will correspond to the biggest contour
outer_blob = biggest_blob(outer_blob);
%// Get the biggest contour from the biggest filled blob
out = outer_blob & im;
figure, imshow(out), title('Final output: Biggest Contour')
The function biggest_blob that is based on bsxfun is an alternative to what other answers posted here perform with regionprops. From my experience, I have found out this bsxfun based technique to be faster than regionprops. Here are few benchmarks comparing these two techniques for runtime performances on one of my previous answers.
Associated function -
function out = biggest_blob(BW)
%// Find and labels blobs in the binary image BW
[L, num] = bwlabel(BW, 8);
%// Count of pixels in each blob, basically should give area of each blob
counts = sum(bsxfun(#eq,L(:),1:num));
%// Get the label(ind) cooresponding to blob with the maximum area
%// which would be the biggest blob
[~,ind] = max(counts);
%// Get only the logical mask of the biggest blob by comparing all labels
%// to the label(ind) of the biggest blob
out = (L==ind);
return;
Debug images -

Calculate the average of part of the image

How can i calculate the average of a certain area in an image using mat-lab?
For example, if i have an intensity image with an area that is more alight and i want to know what is the average of the intensity there- how do i calculate it?
I think i can find the coordinates of the alight area by using the 'impixelinfo' command.
If there is another more efficient way to find the coordinates i will also be glad to know.
After i know the coordinates how do i calculate the average of part of the image?
You could use one of the imroi type functions in Matlab such as imfreehand
I = imread('cameraman.tif');
h = imshow(I);
e = imfreehand;
% now select area on image - do not close image
% this makes a mask from the area you just drew
BW = createMask(e);
% this takes the mean of pixel values in that area
I_mean = mean(I(BW));
Alternatively, look into using regionprops, especially if there's likely to be more than one of these features in the image. Here, I'm finding points in the image above some threshold intensity and then using imdilate to pick out a small area around each of those points (presuming the points above the threshold are well separated, which may not be the case - if they are too close then imdilate will merge them into one area).
se = strel('disk',5);
BW = imdilate(I>thresh,se);
s = regionprops(BW, I, 'MeanIntensity');

Cropping Circular region of interest around a point in MATLAB

I have an image and I want to crop the circular region of interest around a point. I did following in MATLAB:
vessel=imread('vessel.jpg');
imshow( vessel)
t = 0:pi/20:2*pi;
xc=230; % point around which I want to extract/crop image
yc=79;
r=20; %Radium of circular region of interest
xcc = r*cos(t)+xc;
ycc = r*sin(t)+yc;
roimaskcc = poly2mask(double(xcc),double(ycc), size(vessel,1),size(vessel,2));
pr_gccc = find(roimaskcc);
roimean_cc= mean(vessel(pr_gccc));
figure, imshow(roimaskcc)
roimaskcc is correct but when I do the following it gives in nX1 matrix but not the region of interest under mask:
vessel_undermask=vessel(roimaskcc==1);
Can anybody pls. help to extract the circular region of interest around point of interest (xc, yc).
Thanks
What you're doing is extract everything from vessel that's outside the mask. Since there's no such thing as circular matrices, Matlab's solution is to output a vector of all values inside the mask.
While that is technically correct, that vector can be pretty hard to work with. An alternative solution is to leave the data matrix square, and set everything outside the mask to NaN:
% make a copy
vessel_undermask = vessel;
% NaN everything outside the mask (in R, G and B layers)
vessel_undermask(repmat(~roimaskcc,[1,1,3])) = NaN;
imshow(vessel_undermask)
This should give you a matrix that's somewhat simpler to work with.
Note that
vessel_undermask(~isnan(vessel_undermask)) == vessel(roimaskcc)
Yes I got it. I did something like that:
vesseltry=vessel;
vesseltry(~roimaskcc)=0;
vesseltry is now my new image with circular region of interest...