relabeling pixels based on distance between object's centerline and boundary - matlab

I've a binary image containing an object as illustrated in the figure below. The centerline of the object is depicted in red. For each pixel belonging to the object, I would like to relabel it with a color. For instance, pixels whose orthogonal distance to the centerline are half of the distance to the object boundary from the centerline, should be labeled blue, otherwise green. An illustration is given below. Any ideas?
Also, how could I fit a 1D gaussian centered in the object centerline and orthogonal to it?
The image in full resolution can be found under: http://imgur.com/AUK9Hs9

Here is what comes to mind (providing you have the Image Processing Toolbox):
Create two binary images, one BWin with 1 (true) pixels at the location of your red line, and one BWout that is the opposite of your white region (1 outisde the region and 0 (false) inside).
Like this:
BWin:
BWout:
Then apply the euclidean transform to both using bwdist:
Din = bwdist(BWin);
Dout = bwdist(BWout);
You now have two images with pixel intensities that represent the euclidean distance to the closest non-0 pixel.
Now subtract both, the values of the difference will be positive on one side of the equidistance and negative on the other side:
blueMask=Din-Dout>0;
greenMask=~BWout & blueMask;
You can then populate the RGB layer using the masks:
Result=zeros(size(II));
Result(:,:,1)=BWin;
Result(:,:,2)=greenMask;
Result(:,:,3)=~blueMask & ~BWin;
imshow(Result);

Related

How to create a mask to segment a color image by activecontour?

I want to use the function activecontour in matlab to segment a color image, but I don't know how to create the mask.
The documentation says:
For color and multi-channel images, mask must be a 2-D logical array where the first two dimensions match the first two dimensions of the image A.
But I don't understand what has to be done. Any suggestions?
Let's consider that the size of your image is NxM pixels, N is the number of rows, M the number of columns.
If it is a color image, each pixel is probably composed of 3 values, one for intensity of red (R), one intensity of blue (B) and one for intensity of green (G). These are called the color channels. So the real shape of the matrix representing your image is NxMx3.
What the documentation says is that the mask should be 2-D, and the dimensions should match the first two dimension of your image. It means the mask should have the same number of rows and cols than your image, but each pixel of the mask is not composed of 3 values anymore. It is composed of 1 value (a logical value : 0 or 1).
So what you need to do is to give the function a matrix NxM with only 0 and 1 as possible values. The doc says that the mask is the :
Initial contour at which the evolution of the segmentation begins, specified as a binary image the same size as A.
So the mask needs to represent an initial guess of the contour. If you already know that what you want to see is in the upper left corner of the image, you can set the initial contour as a square located in the upper left corner for example.
Now to represent the contour by a matrix of logicals, you simply set all the elements of the matrix to 0 and just the elements representing the contour to 1 I guess.
Lets me know if there is something you don't understand, I'd be glad to answer you.

Measure circularity or ellipticality

I have some binary images that want to classify them base on shape of them in MATLAB. If they have circular or elliptical shape they belong to class one,if they have elliptical shape with dent in their boundary they belong to class two. I dont know how can I use this feature. Can any body help me with this?
You can use the eccentricity property in regionprops. From MATLAB documentation of eccentricity:
The eccentricity is the ratio of the distance between the foci of the ellipse and its major axis length. The value is between 0 and 1. (0 and 1 are degenerate cases. An ellipse whose eccentricity is 0 is actually a circle, while an ellipse whose eccentricity is 1 is a line segment.)
So as the value of eccentricity increases , the ellipse starts becoming flatter. Hence, at its maximum value = 1, it is a line segment.
To check if there is a dent in the ellipse, you can use check for convexity. Whenever there is a dent in an ellipse, it will be non-convex. In other words, if you try to fit a convex polygon, it won't be able to approximate the shape well enough. You can use convexArea property to check the same. From MATLAB documentation of convexArea:
Returns a p-by-2 matrix that specifies the smallest convex polygon that can contain the region. Each row of the matrix contains the x- and y-coordinates of one vertex of the polygon. Only supported for 2-D label matrices.
So you use bwlabel to create a 2-D label matrix from your binary image and then check the difference between the area of your binary image and the area of the fitted convex polygon. Measuring area could be as simple as counting pixels. You already know that the number pixels of your fitted convex polygon = p. Just take the absolute difference between p and the number of pixels in your original binary image. You should be able to easily set a threshold to classify into one of the two classes.
I think you can write the code for this. Hope this helps.

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.

find the number of neighbour pixels

I create an image, which has random groups of random pixels:
img=ones(100,100)
numRandom = 505;
linearIndices = ceil(numel(img) * rand(1, numRandom));
img(linearIndices) = 0;
imshow(img)`
Then I turn this image into binary and find the area of each group of pixels with:
regionprops(L, 'Area');
I also need the perimeter of each group. Unfortunately, regionprops doesn't give me correct results (for example, if there is one pixel the function returns 0 instead of 4), so I think that it is better to find number of neighbour pixels of each group (so that for the case of only one pixel the answer will be 4). If the group is on the border of the image it should also be taken into consideration.
Can anybody give me a tip about how to do it?
Perimeter and regionprops is not what you need then, or find all these single pixels using regionprops(L, 'Area')==1 and set their perimeter to 4....
From Matlab documentation:
Perimeter — is the distance around the boundary of the region. regionprops computes the perimeter by calculating the distance between each adjoining pair of pixels around the border of the region. If the image contains discontiguous regions, regionprops returns unexpected results. The following figure shows the pixels included in the perimeter calculation for this object.
From this image you can see that the edge pixels are counted only once, not twice.

Efficient segment boundary marking after segmentation of an image

One can mark the boundary of a binary image by bwboundaries function of MATLAB.
What should be done for obtaining boundaries of all segments as a binary image?
I have segmented an image and want to know if there is a way to mark boundaries between each neighbouring segment without applying morphological operations on each segment.
I have added images to illustrate what i want to do. Actually i want to obtain a binary image that keeps pink boundary marker pixels between all segments. Thus, I can overlay them with original image by the help of imoverlay function of Steve Eddins.
Random colored labeling of segmentation result:
Roughly-marked pink boundaries between segments:
You can find the region boundaries using a range filter, which finds the intensity range within each pixel's neighborhood. This takes advantage of the fact that the label matrix only has non-zero range at the region boundaries.
im = imread('http://i.stack.imgur.com/qPiA3.png');
boundaries = rangefilt(im,ones(3)) > 0;
imoverlay(label2rgb(im),boundaries,[0 0 0]);
These edges are also two pixels wide. Actually, I think the edges have to be two pixels wide; otherwise the regions will "lose" pixels to the border non-uniformly.
Since erosion and dilation work on non-binary images as well, you can write
img = imread('http://i.stack.imgur.com/qPiA3.png');
ei = imerode(img,ones(3));
di = imdilate(img,ones(3));
boundaries = ei~=img | di~=img;
This results in a bw image that has a boundary at the edge of each colored region (thus, the boundary line will be two pixels wide).
Note that this will not return an ordered list of pixels as bwboundaries, but rather a logical mask like bwperim, which is what imoverlay needs as input.
As a round-about way, I thought of making use of edge function of MATLAB.
First, I need to apply something like a label2gray operation. labels is the segmentation output (first image provided in the question) in the code below.
grayLabels = mat2gray(255* double(labels) ./ double(max(labels(:)))); %label2gray
bw_boundaries = edge(grayLabels,0.001);