To detect irregular circles in MATLAB - matlab

I am new to MATLAB Image Processing. I am writing a code to detect some irregular circles, remove the remaining noise from the Image and find the center mean point of the irregular Black circles (ellipse). Here is the Image
This is the code I have written so far
m = imread('cbnimg.jpg');
imshow(m)
im = mean(m,3);
im = (im-min(im(:))) / (max(im(:))-min(im(:)));
figure;
imshow(im,[]);
impixelinfo
figure;
bin = im2bw(im);
imshow(bin);
figure;
bin = edge(bin);
SE = strel('disk',2);
cir =~imdilate(bin,SE);
imshow(cir);
Here is the result image of this code
[IMG]http://i61.tinypic.com/30n9egn.png[/IMG]
I want to detect only the black spots (Irregular Cicrcle) and remove the remaining noise from the picture as I want the Center Mean Point of these Black irregular Circles..
Can anyone suggest me some algorithms or techniques to get my center mean point?
Thank You

A very naïve approach: apply erosion twice and the dilation twice after a binarization:
m = imread('cbnimg.jpg');
imshow(m)
im = mean(m,3);
im = (im-min(im(:))) / (max(im(:))-min(im(:)));
bin = im2bw(im);
SE = strel('disk',10);
bin = ~imerode(~bin,SE);
bin = ~imerode(~bin,SE);
bin =~imdilate(~bin,SE);
bin =~imdilate(~bin,SE);
imshow(bin);
The shape of the circles is a bit changed, but the change in the center point should be really small. If you want something more ellaborated and robust, erode, label the elements in the image, divide them in two clusters depending on the mass (number of pixels) of each label (with k-means for instance) and then discard all the label correspoding to the cluster with lower masses.
However, for what you asked so far this should be enough.

Related

Template matching

I am trying to detect elevators on floor plans in MATLAB. The code I have now is not detecting elevators, it is instead just pointing at the edges of the image. I am expecting to detect all the elevators on a floor plan. Elevators are represented by a square or rectangle with an x inside, similar to the template image. I have attached the template, image and a result screenshot.
Template image:
Image:
Results:
Code:
template= rgb2gray(imread('ele7.png'));
image = rgb2gray(imread('floorplan.jpg'));
%imshowpair(image,template,'montage')
c = normxcorr2(template,image);% perform cross-correlation
figure, surf(c), shading flat
[ypeak, xpeak] = find(c==max(c(:)));%peak of correlation
%Compute translation from max location in correlation matrix, =padding
yoffSet = ypeak-size(template,1);
xoffSet = xpeak-size(template,2);
%Display matched area
figure
hAx = axes;
imshow(image,'Parent', hAx);
imrect(hAx, [xoffSet+1, yoffSet+1, size(template,2), size(template,1)]);
To check if everything runs smoothly, you should plot the correlation:
figure, surf(c)
As mention by #cris-luengo , it's easy to fail with the sizes of the image and so on. However, I've seen that you followed the tutorial on https://es.mathworks.com/help/images/ref/normxcorr2.html . Since both images, already black and white images (or 2-colour images), normxcorr2 works well with rgb images (with textures and objects, etc...). Thus, I think that is not a correct approach to use normxcorr2.
An approach I would consider is look for branches. Using Matlab's help and bwmorph:
BW = imread('circles.png');
imshow(BW);
BW1 = bwmorph(BW,'skel',Inf);
You first skeletonize the image, then you can use any of the functions that displayed on bwmorph's help (https://es.mathworks.com/help/images/ref/bwmorph.html). In this case, I'd search for branch points, i.e. crosslinks. It is as simple as:
BW2 = bwmorph(BW1,'branchpoints');
branchPointsPixels = find(BW2 == 1);
The indices of the branch points pixels, will be where it finds an X. However, it can be any rotated X (or +, ...). So you'll find more points that you desire, you would need to filter the points in order to get what you want.

remove background or mark filled areas from imfill

I'm doing some image segmentation in matlab of grayscale images taken from a drone using a thermo sensitive camera. The idea is that you should be able to put in a video whereafter it analyzes every frame and give a new video as output, now where each person is marked, clustered and a total count in the frame is given. So far what I am doing to remove the background is first imtophat and then some threshold on top of this I build some analysis to identify the people from e.g. fences, houses etc. However this threshold is way to static, so once there is a shift in outdoor temperature or the layer changes e.g. from grass to tarmac then I either get to many things in the picture or I remove some of the people. So what I am ultimately looking for is a way to get rid of the background. So what I have left is buildings, cars, people etc..
This is the ultimate goal and a solution to this would be highly appreciated.
What I tried to do was to first use the following code on the first picture (where pic1 is the original picture):
%Make it double
pic2 = double(pic1);
%Remove some noise
pic2 = wiener2(pic2);
%Make the pedestrians larger
pic2 = imdilate(pic2,strel('disk',5));
%In case of shadows take these to some minimum
pic3 = pic2.*(pic2>mean(mean(pic2))) + mean(mean(pic2))*(pic2<mean(mean(pic2)));
%Remove some of the background
pic4 = imtophat(pic3,strel('disk',10));
%Make the edges stand out.
hy = fspecial('sobel');
hx = hy';
Iy = imfilter(gaussian, hy, 'replicate');
Ix = imfilter(gaussian, hx, 'replicate');
gradmag = sqrt(Ix.^2 + Iy.^2);
%Threshold the edges
BW = gradmag>100;
%Close the circles
BW2 = imclose(BW1,strel('disk',5))
Now I have a binary image of the edges of the objects in the picture. And I want to fill out the pedestrians, such that I have an initial guess of where they are and how they look. So I apply imfill.
[BW3] = imfill(BW2);
Then what I want is the coordinates of all the pixels that matlab have turned white for me. How do I get that? I have tried with [BW3,locations] = infill(BW2), but this does not work (as I want it to.)
As testing you can use the attached picture. Also if you are trying the solve the ultimate problem at the top, then I have no problem of getting the house, the cars and the pedestrians out - the house and the cars I can perfectly fine sort out if they appear whole.
To get the pixel that imfill changes for you, compare the before and after image and use find to get the coordinates of the points whose values have been changed.
diffimg = (BW2 ~= Bw3);
[y, x] = find(diffimg);

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');

MATLAB Image Processing - Find Edge and Area of Image

As a preface: this is my first question - I've tried my best to make it as clear as possible, but I apologise if it doesn't meet the required standards.
As part of a summer project, I am taking time-lapse images of an internal melt figure growing inside a crystal of ice. For each of these images I would like to measure the perimeter of, and area enclosed by the figure formed. Linked below is an example of one of my images:
The method that I'm trying to use is the following:
Load image, crop, and convert to grayscale
Process to reduce noise
Find edge/perimeter
Attempt to join edges
Fill perimeter with white
Measure Area and Perimeter using regionprops
This is the code that I am using:
clear; close all;
% load image and convert to grayscale
tyrgb = imread('TyndallTest.jpg');
ty = rgb2gray(tyrgb);
figure; imshow(ty)
% apply a weiner filter to remove noise.
% N is a measure of the window size for detecting coherent features
N=20;
tywf = wiener2(ty,[N,N]);
tywf = tywf(N:end-N,N:end-N);
% rescale the image adaptively to enhance contrast without enhancing noise
tywfb = adapthisteq(tywf);
% apply a canny edge detection
tyedb = edge(tywfb,'canny');
%join edges
diskEnt1 = strel('disk',8); % radius of 4
tyjoin1 = imclose(tyedb,diskEnt1);
figure; imshow(tyjoin1)
It is at this stage that I am struggling. The edges do not quite join, no matter how much I play around with the morphological structuring element. Perhaps there is a better way to complete the edges? Linked is an example of the figure this code outputs:
The reason that I am trying to join the edges is so that I can fill the perimeter with white pixels and then use regionprops to output the area. I have tried using the imfill command, but cannot seem to fill the outline as there are a large number of dark regions to be filled within the perimeter.
Is there a better way to get the area of one of these melt figures that is more appropriate in this case?
As background research: I can make this method work for a simple image consisting of a black circle on a white background using the below code. However I don't know how edit it to handle more complex images with edges that are less well defined.
clear all
close all
clc
%% Read in RGB image from directory
RGB1 = imread('1.jpg') ;
%% Convert RPG image to grayscale image
I1 = rgb2gray(RGB1) ;
%% Transform Image
%CROP
IC1 = imcrop(I1,[74 43 278 285]);
%BINARY IMAGE
BW1 = im2bw(IC1); %Convert to binary image so the boundary can be traced
%FIND PERIMETER
BWP1 = bwperim(BW1);
%Traces perimeters of objects & colours them white (1).
%Sets all other pixels to black (0)
%Doing the same job as an edge detection algorithm?
%FILL PERIMETER WITH WHITE IN ORDER TO MEASURE AREA AND PERIMETER
BWF1 = imfill(BWP1); %This opens figure and allows you to select the areas to fill with white.
%MEASURE PERIMETER
D1 = regionprops(BWF1, 'area', 'perimeter');
%Returns an array containing the properties area and perimeter.
%D1(1) returns the perimeter of the box and an area value identical to that
%perimeter? The box must be bounded by a perimeter.
%D1(2) returns the perimeter and area of the section filled in BWF1
%% Display Area and Perimeter data
D1(2)
I think you might have room to improve the effect of edge detection in addition to the morphological transformations, for instance the following resulted in what appeared to me a relatively satisfactory perimeter.
tyedb = edge(tywfb,'sobel',0.012);
%join edges
diskEnt1 = strel('disk',7); % radius of 4
tyjoin1 = imclose(tyedb,diskEnt1);
In addition I used bwfill interactively to fill in most of the interior. It should be possible to fill the interior programatically but I did not pursue this.
% interactively fill internal regions
[ny nx] = size(tyjoin1);
figure; imshow(tyjoin1)
tyjoin2=tyjoin1;
titl = sprintf('click on a region to fill\nclick outside window to stop...')
while 1
pts=ginput(1)
tyjoin2 = bwfill(tyjoin2,pts(1,1),pts(1,2),8);
imshow(tyjoin2)
title(titl)
if (pts(1,1)<1 | pts(1,1)>nx | pts(1,2)<1 | pts(1,2)>ny), break, end
end
This was the result I obtained
The "fractal" properties of the perimeter may be of importance to you however. Perhaps you want to retain the folds in your shape.
You might want to consider Active Contours. This will give you a continous boundary of the object rather than patchy edges.
Below are links to
A book:
http://www.amazon.co.uk/Active-Contours-Application-Techniques-Statistics/dp/1447115570/ref=sr_1_fkmr2_1?ie=UTF8&qid=1377248739&sr=8-1-fkmr2&keywords=Active+shape+models+Andrew+Blake%2C+Michael+Isard
A demo:
http://users.ecs.soton.ac.uk/msn/book/new_demo/Snakes/
and some Matlab code on the File Exchange:
http://www.mathworks.co.uk/matlabcentral/fileexchange/28149-snake-active-contour
and a link to a description on how to implement it: http://www.cb.uu.se/~cris/blog/index.php/archives/217
Using the implementation on the File Exchange, you can get something like this:
%% Load the image
% You could use the segmented image obtained previously
% and then apply the snake on that (although I use the original image).
% This will probably make the snake work better and the edges
% in your image is not that well defined.
% Make sure the original and the segmented image
% have the same size. They don't at the moment
I = imread('33kew0g.jpg');
% Convert the image to double data type
I = im2double(I);
% Show the image and select some points with the mouse (at least 4)
% figure, imshow(I); [y,x] = getpts;
% I have pre-selected the coordinates already
x = [ 525.8445 473.3837 413.4284 318.9989 212.5783 140.6320 62.6902 32.7125 55.1957 98.6633 164.6141 217.0749 317.5000 428.4172 494.3680 527.3434 561.8177 545.3300];
y = [ 435.9251 510.8691 570.8244 561.8311 570.8244 554.3367 476.3949 390.9586 311.5179 190.1085 113.6655 91.1823 98.6767 106.1711 142.1443 218.5872 296.5291 375.9698];
% Make an array with the selected coordinates
P=[x(:) y(:)];
%% Start Snake Process
% You probably have to fiddle with the parameters
% a bit more that I have
Options=struct;
Options.Verbose=true;
Options.Iterations=1000;
Options.Delta = 0.02;
Options.Alpha = 0.5;
Options.Beta = 0.2;
figure(1);
[O,J]=Snake2D(I,P,Options);
If the end result is an area/diameter estimate, then why not try to find maximal and minimal shapes that fit in the outline and then use the shapes' area to estimate the total area. For instance, compute a minimal circle around the edge set then a maximal circle inside the edges. Then you could use these to estimate diameter and area of the actual shape.
The advantage is that your bounding shapes can be fit in a way that minimizes error (unbounded edges) while optimizing size either up or down for the inner and outer shape, respectively.

Segmenting a grayscale image

I am having trouble achieving the correct segmentation of a grayscale image:
The ground truth, i.e. what I would like the segmentation to look like, is this:
I am most interested in the three components within the circle. Thus, as you can see, I would like to segment the top image into three components: two semi-circles, and a rectangle between them.
I have tried various combinations of dilation, erosion, and reconstruction, as well as various clustering algorithms, including k-means, isodata, and mixture of gaussians--all with varying degrees of success.
Any suggestions would be appreciated.
Edit: here is the best result I've been able to obtain. This was obtained using an active contour to segment the circular ROI, and then applying isodata clustering:
There are two problems with this:
The white halo around the bottom-right cluster, belonging to the top-left cluster
The gray halo around both the top-right and bottom-left cluster, belonging to the center cluster.
Here's a starter...
use circular Hough transform to find the circular part. For that I initially threshold the image locally.
im=rgb2gray(imread('Ly7C8.png'));
imbw = thresholdLocally(im,[2 2]); % thresold localy with a 2x2 window
% preparing to find the circle
props = regionprops(imbw,'Area','PixelIdxList','MajorAxisLength','MinorAxisLength');
[~,indexOfMax] = max([props.Area]);
approximateRadius = props(indexOfMax).MajorAxisLength/2;
radius=round(approximateRadius);%-1:approximateRadius+1);
%find the circle using Hough trans.
h = circle_hough(edge(imbw), radius,'same');
[~,maxIndex] = max(h(:));
[i,j,k] = ind2sub(size(h), maxIndex);
center.x = j; center.y = i;
figure;imagesc(im);imellipse(gca,[center.x-radius center.y-radius 2*radius 2*radius]);
title('Finding the circle using Hough Trans.');
select only what's inside the circle:
[y,x] = meshgrid(1:size(im,2),1:size(im,1));
z = (x-j).^2+(y-i).^2;
f = (z<=radius^2);
im=im.*uint8(f);
EDIT:
look for a place to start threshold the image to segment it by looking at the histogram, finding it's first local maxima, and iterating from there until 2 separate segments are found, using bwlabel:
p=hist(im(im>0),1:255);
p=smooth(p,5);
[pks,locs] = findpeaks(p);
bw=bwlabel(im>locs(1));
i=0;
while numel(unique(bw))<3
bw=bwlabel(im>locs(1)+i);
i=i+1;
end
imagesc(bw);
The middle part can now be obtained by taking out the two labeled parts from the circle, and what is left will be the middle part (+some of the halo)
bw2=(bw<1.*f);
but after some median filtering we get something more reasonble
bw2= medfilt2(medfilt2(bw2));
and together we get:
imagesc(bw+3*bw2);
The last part is a real "quick and dirty", I'm sure that with the tools you already used you'll get better results...
One can also obtain an approximate result using the watershed transformation. This is the watershed on the inverted image -> watershed(255-I) Here is an example result:
Another Simple method is to perform a morphological closing on the original image with a disc structuring element (one can perform multiscale closing for granulometries) and then obtain the full circle. After this extracting the circle is and components withing is easier.
se = strel('disk',3);
Iclo = imclose(I, se);% This closes open circular cells.
Ithresh = Iclo>170;% one can locate this threshold automatically by histogram modes (if you know apriori your cell structure.)
Icircle = bwareaopen(Ithresh, 50); %to remove small noise components in the bg
Ithresh2 = I>185; % This again needs a simple histogram.