Segmenting a grayscale image - matlab

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.

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.

Autonomous seam detection in Images on matlab

I'm trying to detect seams in welding images for an autonomous welding process.
I want to find pixel positions of the detected line (the red line in the desired image) in the original image.
I used the following code and finally removed noise from the image to reach the result below.
clc,clear,clf;
im = imread('https://i.stack.imgur.com/UJcKA.png');
imshow(im);title('Original image'); pause(0.5);
sim = edge(im, 'sobel');
imshow(sim);title('after Sobel'); pause(0.5);
mask = im > 5;
se = strel('square', 5);
mask_s = imerode(mask, se);
mask(mask_s) = false;
mask = imdilate(mask, se);
sim(mask) = false;
imshow(sim);title('after mask');pause(0.5);
sim= medfilt2(sim);
imshow(sim);title('after noise removal')
Unfortunately there is nothing remaining in the image to find the seam perfectly.
Any help would be appreciated.
Download Original image.
You need to make your filter more robust to noise. This can be done by giving it a larger support:
filter = [ones(2,9);zeros(1,9);-ones(2,9)];
msk = imerode(im > 0, ones(11)); % only object pixels, discarding BG
fim =imfilter(im,filter);
robust = bwmorph((fim>0.75).*msk,'skel',inf); % get only strong pixels
The robust mask looks like:
As you can see, the seam line is well detected, we just need to pick it as the largest connected component:
st = regionprops(bwlabel(robust,8), 'Area', 'PixelList');
[ma mxi] = max([st.Area]); % select the region with the largest area
Now we can fit a polygon (2nd degree) to the seem:
pp=polyfit(st(mxi).PixelList(:,1), st(mxi).PixelList(:,2), 2);
And here it is over the image:
imshow(im, 'border','tight');hold on;
xx=1:size(im,2);plot(xx,polyval(pp,xx)+2,'r');
Note the +2 Y offset due to filter width.
PS,
You might find this thread relevant.
Shai gives a great answer, but I wanted to add a bit more context about why your noise filtering doesn't work.
Why median filtering doesn't work
Wikipedia suggests that median filtering removes noise while preserving edges, which is why you might have chosen to use it. However, in your case it will almost certainly not work, here's why:
Median filtering slides a window across the image. In each area, it replaces the central pixel with the median value from the surrounding window. medfilt2 uses a 3x3 window by default. Let's look at a 3x3 block near your line,
A 3x3 block around [212 157] looks like this
[0 0 0
1 1 1
0 0 0]
The median value is 0! So even though we're in the middle of a line segment, the pixel will be filtered out.
The alternative to median filtering
Shai's method for removing noise instead finds the largest connected group of pixels and ignores smaller groups of pixels. If you also wanted to remove these small groups from your image, Matlab provides a filter bwareaopen which removes small objects from binary images.
For example, if you replace your line
sim= medfilt2(sim);
with
sim= bwareaopen(sim, 4);
The result is much better
Alternative edge detectors
One last note, Shai uses a horizontal gradient filter to find horizontal edges in your image. It works great because your edge is horizontal. If you edge will not always be horizontal, you might want to use another edge detection method. In your original code, you use Sobel, but Matlab provides many options, all of which perform better if you tune their thresholds. As an example, in the following image, I've highlighted the pixels selected by your code (with bwareaopen modification) using four different edge detectors.

Matlab: separate connected components

I was working on my image processing problem with detecting coins.
I have some images like this one here:
and wanted to separate the falsely connected coins.
We already tried the watershed method as stated on the MATLAB-Homepage:
the-watershed-transform-strategies-for-image-segmentation.html
especially since the first example is exactly our problem.
But instead we get a somehow very messed up separation as you can see here:
We already extracted the area of the coin using the regionprops Extrema parameter and casting the watershed only on the needed area.
I'd appreciate any help with the problem or even another method of getting it separated.
If you have the Image Processing Toolbox, I can also suggest the Circular Hough Transform through imfindcircles. However, this requires at least version R2012a, so if you don't have it, this won't work.
For the sake of completeness, I'll assume you have it. This is a good method if you want to leave the image untouched. If you don't know what the Hough Transform is, it is a method for finding straight lines in an image. The circular Hough Transform is a special case that aims to find circles in the image.
The added advantage of the circular Hough Transform is that it is able to detect partial circles in an image. This means that those regions in your image that are connected, we can detect them as separate circles. How you'd call imfindcircles is in the following fashion:
[centers,radii] = imfindcircles(A, radiusRange);
A would be your binary image of objects, and radiusRange is a two-element array that specifies the minimum and maximum radii of the circles you want to detect in your image. The outputs are:
centers: A N x 2 array that tells you the (x,y) co-ordinates of each centre of a circle that is detected in the image - x being the column and y being the row.
radii: For each corresponding centre detected, this also gives the radius of each circle detected. This is a N x 1 array.
There are additional parameters to imfindcircles that you may find useful, such as the Sensitivity. A higher sensitivity means that it is able to detect circular shapes that are more non-uniform, such as what you are showing in your image. They aren't perfect circles, but they are round shapes. The default sensitivity is 0.85. I set it to 0.9 to get good results. Also, playing around with your image, I found that the radii ranged from 50 pixels to 150 pixels. Therefore, I did this:
im = im2bw(imread('http://dennlinger.bplaced.net/t06-4.jpg'));
[centers,radii] = imfindcircles(im, [50 150], 'Sensitivity', 0.9);
The first line of code reads in your image directly from StackOverflow. I also convert this to logical or true black and white as the image you uploaded is of type uint8. This image is stored in im. Next, we call imfindcircles in the method that we described.
Now, if we want to visualize the detected circles, simply use imshow to show your image, then use the viscircles to draw the circles in the image.
imshow(im);
viscircles(centers, radii, 'DrawBackgroundCircle', false);
viscircles by default draws the circles with a white background over the contour. I want to disable this because your image has white circles and I don't want to show false contouring. This is what I get with the above code:
Therefore, what you can take away from this is the centers and radii variables. centers will give you the centre of each detected circle while radii will tell you what the radii is for each circle.
Now, if you want to simulate what regionprops is doing, we can iterate through all of the detected circles and physically draw them onto a 2D map where each circle would be labeled by an ID number. As such, we can do something like this:
[X,Y] = meshgrid(1:size(im,2), 1:size(im,1));
IDs = zeros(size(im));
for idx = 1 : numel(radii)
r = radii(idx);
cen = centers(idx,:);
loc = (X - cen(1)).^2 + (Y - cen(2)).^2 <= r^2;
IDs(loc) = idx;
end
We first define a rectangular grid of points using meshgrid and initialize an IDs array of all zeroes that is the same size as the image. Next, for each pair of radii and centres for each circle, we define a circle that is centered at this point that extends out for the given radius. We then use these as locations into the IDs array and set it to a unique ID for that particular circle. The result of IDs will be that which resembles the output of bwlabel. As such, if you want to extract the locations of where the idx circle is, you would do:
cir = IDs == idx;
For demonstration purposes, this is what the IDs array looks like once we scale the IDs such that it fits within a [0-255] range for visibility:
imshow(IDs, []);
Therefore, each shaded circle of a different shade of gray denotes a unique circle that was detected with imfindcircles.
However, the shades of gray are probably a bit ambiguous for certain coins as this blends into the background. Another way that we could visualize this is to apply a different colour map to the IDs array. We can try using the cool colour map, with the total number of colours to be the number of unique circles + 1 for the background. Therefore, we can do something like this:
cmap = cool(numel(radii) + 1);
RGB = ind2rgb(IDs, cmap);
imshow(RGB);
The above code will create a colour map such that each circle gets mapped to a unique colour in the cool colour map. The next line applies a mapping where each ID gets associated with a colour with ind2rgb and we finally show the image.
This is what we get:
Edit: the following solution is more adequate to scenarios where one does not require fitting the exact circumferences, although simple heuristics could be used to approximate the radii of the coins in the original image based on the centers found in the eroded one.
Assuming you have access to the Image Processing toolbox, try imerode on your original black and white image. It will apply an erosion morphological operator to your image. In fact, the Matlab webpage with the documentation of that function has an example strikingly similar to your problem/image and they use a disk structure.
Run the following code (based on the example linked above) assuming the image you submitted is called ima.jpg and is local to the code:
ima=imread('ima.jpg');
se = strel('disk',50);
eroded = imerode(ima,se);
imshow(eroded)
and you will see the image that follows as output. After you do this, you can use bwlabel to label the connected components and compute whatever properties you may want, for example, count the number of coins or detect their centers.

Separate two overlapping circles in an image using MATLAB

How do I separate the two connected circles in the image below, using MATLAB? I have tried using imerode, but this does not give good results. Eroding does not work, because in order to erode enough to separate the circles, the lines disappear or become mangled. In other starting pictures, a circle and a line overlap, so isolating the overlapping objects won't work either.
The image shows objects identified by bwboundaries, each object painted a different color. As you can see, the two light blue circles are joined, and I want to disjoin them, producing two separate circles. Thanks
I would recommend you use the Circular Hough Transform through imfindcircles. However, you need version 8 of the Image Processing Toolbox, which was available from version R2012a and onwards. If you don't have this, then unfortunately this won't work :(... but let's go with the assumption that you do have it. However, if you are using something older than R2012a, Dev-iL in his/her comment above linked to some code on MATLAB's File Exchange on an implementation of this, most likely created before the Circular Hough Transform was available: http://www.mathworks.com/matlabcentral/fileexchange/9168-detect-circles-with-various-radii-in-grayscale-image-via-hough-transform/
This is a special case of the Hough Transform where you are trying to find circles in your image rather than lines. The beauty with this is that you are able to find circles even when the circle is partially completed or overlapping.
I'm going to take the image that you provided above and do some post-processing on it. I'm going to convert the image to binary, and remove the border, which is white and contains the title. I'm also going to fill in any holes that result so that all of the objects are filled in with solid white. There is also some residual quantization noise after I do this step, so I'm going to a small opening with a 3 x 3 square element. After, I'm going to close the shapes with a 3 x 3 square element, as I see that there are noticeable gaps in the shapes. Therefore:
Therefore, directly reading in your image from where you've posted it:
im = imread('http://s29.postimg.org/spkab8oef/image.jpg'); %// Read in the image
im_gray = im2double(rgb2gray(im)); %// Convert to grayscale, then [0,1]
out = imclearborder(im_gray > 0.6); %// Threshold using 0.6, then clear the border
out = imfill(out, 'holes'); %// Fill in the holes
out = imopen(out, strel('square', 3));
out = imclose(out, strel('square', 3));
This is the image I get:
Now, apply the Circular Hough Transform. The general syntax for this is:
[centres, radii, metric] = imfindcircles(img, [start_radius, end_radius]);
img would be the binary image that contains your shapes, start_radius and end_radius would be the smallest and largest radius of the circles you want to find. The Circular Hough Transform is performed such that it will find any circles that are within this range (in pixels). The outputs are:
centres: Which returns the (x,y) positions of the centres of each circle detected
radii: The radius of each circle
metric: A measure of purity of the circle. Higher values mean that the shape is more probable to be a circle and vice-versa.
I searched for circles having a radius between 30 and 60 pixels. Therefore:
[centres, radii, metric] = imfindcircles(out, [30, 60]);
We can then demonstrate the detected circles, as well as the radii by a combination of plot and viscircles. Therefore:
imshow(out);
hold on;
plot(centres(:,1), centres(:,2), 'r*'); %// Plot centres
viscircles(centres, radii, 'EdgeColor', 'b'); %// Plot circles - Make edge blue
Here's the result:
As you can see, even with the overlapping circles towards the top, the Circular Hough Transform was able to detect two distinct circles in that shape.
Edit - November 16th, 2014
You wish to ensure that the objects are separated before you do bwboundaries. This is a bit tricky to do. The only way I can see you do this is if you don't even use bwboundaries at all and do this yourself. I'm assuming you'll want to analyze each shape's properties by themselves after all of this, so what I suggest you do is iterate through every circle you have, then place each circle on a new blank image, do a regionprops call on that shape, then append it to a separate array. You can also keep track of all of the circles by having a separate array that adds the circles one at a time to this array.
Once you've finished with all of the circles, you'll have a structure array that contains all of the measured properties for all of the measured circles you have found. You would use the array that contains only the circles from above, then use these and remove them from the original image so you get just the lines. You'd then call one more regionprops on this image to get the information for the lines and append this to your final structure array.
Here's the first part of the procedure I outlined above:
num_circles = numel(radii); %// Get number of circles
struct_reg = []; %// Save the shape analysis per circle / line here
%// For creating our circle in the temporary image
[X,Y] = meshgrid(1:size(out,2), 1:size(out,1));
%// Storing all of our circles in this image
circles_img = false(size(out));
for idx = 1 : num_circles %// For each circle we have...
%// Place our circle inside a temporary image
r = radii(idx);
cx = centres(idx,1); cy = centres(idx,2);
tmp = (X - cx).^2 + (Y - cy).^2 <= r^2;
% // Save in master circle image
circles_img(tmp) = true;
%// Do regionprops on this image and save
struct_reg = [struct_reg; regionprops(tmp)];
end
The above code may be a bit hard to swallow, but let's go through it slowly. I first figure out how many circles we have, which is simply looking at how many radii we have detected. I keep a separate array called struct_reg that will append a regionprops struct for each circle and line we have in our image. I use meshgrid to determine the (X,Y) co-ordinates with respect to the image containing our shapes so that I can draw one circle onto a blank image at each iteration. To do this, you simply need to find the Euclidean distance with respect to the centre of each circle, and set the pixels to true only if that location has its distance less than r. After doing this operation, you will have created only one circle and filtered all of them out. You would then use regionprops on this circle, add it to our circles_img array, which will only contain the circles, then continue with the rest of the circles.
At this point, we will have saved all of our circles. This is what circles_img looks like so far:
You'll notice that the circles drawn are clean, but the actual circles in the original image are a bit jagged. If we tried to remove the circles with this clean image, you will get some residual pixels along the border and you won't completely remove the circles themselves. To illustrate what I mean, this is what your image looks like if I tried to remove the circles with circles_img by itself:
... not good, right?
If you want to completely remove the circles, then do a morphological reconstruction through imreconstruct where you can use this image as the seed image, and specify the original image to be what we're working on. The job of morphological reconstruction is essentially a flood fill. You specify seed pixels, and an image you want to work on, and the job of imreconstruct is from these seeds, flood fill with white until we reach the boundaries of the objects that the seed pixels resided in. Therefore:
out_circles = imreconstruct(circles_img, out);
Therefore, we get this for our final reconstructed circles image:
Great! Now, use this and remove the circles from the original image. Once you do this, run regionprops again on this final image and append to your struct_reg variable. Obviously, save a copy of the original image before doing this:
out_copy = out;
out_copy(out_circles) = false;
struct_reg = [struct_reg; regionprops(out_copy)];
Just for sake of argument, this is what the image looks like with the circles removed:
Now, we have analyzed all of our shapes. Bear in mind I did the full regionprops call because I don't know exactly what you want in your analysis... so I just decided to give you everything.
Hope this helps!
erosion is the way to go. You should probably use a larger structuring element.
How about
1 erode
2 detect your objects
3 dilate each object for itself using the same structuring element

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.