How can I separate same colored connected object from an image? - matlab

I have an image shown below:
I am applying some sort of threshold like in the code. I could separate the blue objects like below:
However, now I have a problem separating these blue objects. I applied watershed (I don't know whether I made it right or wrong) but it didn't work out, so I need help to separate these connected objects.
The code I tried to use is shown below:
RGB=imread('testImage.jpg');
RGB = im2double(RGB);
cform = makecform('srgb2lab', 'AdaptedWhitePoint', whitepoint('D65'));
I = applycform(RGB,cform);
channel1Min = 12.099;
channel1Max = 36.044;
channel2Min = -9.048;
channel2Max = 48.547;
channel3Min = -53.996;
channel3Max = 15.471;
BW = (I(:,:,1) >= channel1Min ) & (I(:,:,1) <= channel1Max) & ...
(I(:,:,2) >= channel2Min ) & (I(:,:,2) <= channel2Max) & ...
(I(:,:,3) >= channel3Min ) & (I(:,:,3) <= channel3Max);
maskedRGBImage = RGB;
maskedRGBImage(repmat(~BW,[1 1 3])) = 0;
figure
imshow(maskedRGBImage)

In general, this type of segmentation is a serious research problem. In your case, you could do pretty well using a combination of morphology operations. These see widespread use in microscopy image processing.
First, clean up BW a bit by removing small blobs and filling holes,
BWopen = imopen(BW, strel('disk', 6));
BWclose = imclose(BWopen, strel('disk', 6));
(you may want to tune the structuring elements a bit, "6" is just a radius that seemed to work on your test image.)
Then you can use aggressive erosion to generate some seeds
seeds = imerode(BWclose, strel('disk', 35));
which you can use for watershed, or just assign each point in BW to its closest seed
labels = bwlabel(seeds);
[D, i] = bwdist(seeds);
closestLabels = labels(i);
originalLabels = BWopen .* closestLabels;
imshow(originalLabels, []);

I would try the following steps:
Convert the image to gray and then to a binary mask.
Apply morphological opening (imopen) to clean small noisy objects.
Apply Connected Component Analysis (CCA) using bwlabel. Each connected component contains at least 1 object.
These blue objects really look like stretched/distorted circles, so I would try Hough transform to detect cicles inside each labeled component. There is a built-in function (imfindcircles) or code available online (Hough transform for circles), depending on your Matlab version and available toolboxes.
Then, you need to take some decisions regarding the number of objects, N, inside each component (N>=1). I don't know in advance what the best criteria should be, but you could also apply these simple rules:
[i] An object needs to be of a minimum size.
[ii] Overlaping circles correspond to the same object (or not, depending on the overlap amount).
The circle centroids can then serve as seeds to complete the final object segmentation. Of course, if there is only one circle in each component, you just keep it directly as an object.
I didn't check all steps for validity in Matlab, but I quickly checked 1, 2, and 4 and they seemed to be quite promising. I show the result of circle detection for the most difficult component, in the center of the image:
The code I used to create this image is:
close all;clear all;clc;
addpath 'circle_hough'; % add path to code of [Hough transform for circles] link above
im = imread('im.jpg');
img = rgb2gray(im);
mask = img>30; mask = 255*mask; % create a binary mask
figure;imshow(mask)
% filter the image so that only the central part of 6 blue objects remains (for demo purposes only)
o = zeros(size(mask)); o(170:370, 220:320) = 1;
mask = mask.*o;
figure;imshow(mask);
se = strel('disk',3);
mask = imopen(mask,se); % apply morphological opening
figure;imshow(mask);
% check for circles using Hough transform (see also circle_houghdemo.m in [Hough transform for circles] link above)
radii = 15:5:40; % allowed circle radii
h = circle_hough(mask, radii, 'same', 'normalise');
% choose the 10 biggest circles
peaks = circle_houghpeaks(h, radii, 'nhoodxy', 15, 'nhoodr', 21, 'npeaks', 10);
% show result
figure;imshow(im);
for peak = peaks
[x, y] = circlepoints(peak(3));
hold on;plot(x+peak(1), y+peak(2), 'g-');
end

Some spontaneous thoughts. I assume the ultimate goal is to count the blue corpuscles. If so, I would search for a way to determine the total area of those objects and the average area of a corpuscle. As a first step convert to binary (black and White):
level = graythresh(RGB);
BW = im2bw(RGB,level);
Since morphological operations (open/close) will not preserve the areas, I would work with bwlabel to find the connected components. Looking at the picture I see 12 isolated corpuscles, two connected groups, some truncated corpuscles at the edge and some small noisy fragments. So first remove small objects and those touching edges. Then determine total area (bwarea) and the median size of objects as a proxy for the average area of one corpuscle.

Related

how to segment the colony When I can't use bw image

I tried to use image processing toolbox to count my colony. I used imfindcircles to find the colony and count. But I got some problems:
(1)Due to that my colony could be white and black, I tried to find the colony by using Bright or Dark in ObjectPolarity, and use a if loop to select which one I finally choose. But the first step of my if loop doesn't really work.
(2) For using imfindcircles to find the circle, I found it works for the colony in white, while the method is a disaster for the black colony. I'm kind of desperate now because I can't find other ways to segment the colony.
So finally i need to: label each colony in the plate, count the colony number, calculate each colony size, extract the mean gray value for each colony (the colour).
Thank you very much!!!
So here is my code:
im = imread(test.jpeg)
imshow(im)
% to find the colony
[centersBright, radiiBright] = imfindcircles(im,[30 60],'ObjectPolarity','bright','Sensitivity',0.925,'Method','twostage','EdgeThreshold',0.1)
[centersDark, radiiDark] = imfindcircles(im,[30 60],'ObjectPolarity','dark','Sensitivity',0.925,'Method','twostage','EdgeThreshold',0.15)
% to select which one is the correct count. if one of the length(centres) value is lower than 10, I consider it as an error. But if both length(centres) is low than 10, I consider it as a treatment effect and accept the value.
if length(centersDark)<10<length(centersBright)
centers=centersBright
radii=radiiBright
elseif length(centersBright)<10<length(centersDark)
centers=centersDark
radii=radiiDark
else
centers=[centersBright,centersDark]
radii=[radiiBright,radiiDark]
end
% view and label the colony
h = viscircles(centers,radii)
for k = 1:length(radii)
string = sprintf('%d',k)
text(centers(k,1),centers(k,2),string,'color','y','HorizontalAlignment', 'center','VerticalAlignment', 'middle')
area(k)=pi*radii(k)^2
end
Suggested Solution
While it's hard to distinguish between the colonies and their surrounding in the black and white version of your input, it is not hard to do so in the hue space by using thresholding.
The reason is that the colonies have a unique hue which is different from their background.
This will be more noticable after converting to HSV space:
Therefore, I suggest the folowing solution:
convert input image to HSV space
use thresholding on the hue component.
extract connected components
Use the connected components which are large enough for the mask
perform imclose opertation for cleaning the artifacts
Code
%reads the image
I = imread('colony2.png');
%convert to hsv
hsvIm = rgb2hsv(I);
%thresholding on the hue space
bwIm = hsvIm(:,:,1) < 0.15;
%find connected components
CC = bwconncomp(bwIm);
%choose only cc with sufficient amount of pixels
numPixels = cellfun(#numel,CC.PixelIdxList);
relevantCC = CC.PixelIdxList(numPixels > 10);
%generate a binary mask with these conencted componants
colonyMask = false(size(bwIm));
for ii=1:length(relevantCC)
colonyMask(relevantCC{ii}) = true;
end
%perform morpholopical operations for cleaning
colonyMask = imclose(colonyMask,strel('disk',1));
%display result
h = imshow(I); % Save the handle; we'll need it later
set(h, 'AlphaData', ~colonyMask);
Results
Selection of threshold
The threshold was chosen by choosing the first pick in the histogram of the hue component
histogram(hsvIm(:,:,1))

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

Matlab : Remove skin part on segmentation

How to remove skin parts on segmentation ?
First, I made this first picture smaller, since the picture is somehow intimidating, I'll give the image at the end section.
I'm using RGB & ycbcr segmentation, but it seems the segmentation didn't work well.
clear all;
close all;
clc;
img=imread('acne.jpg');
%ycbcr segmentation
img_ycbcr=img; %image from the previous segmentation
ycbcr=rgb2ycbcr(img_ycbcr);
cb=ycbcr(:,:,2);
cr=ycbcr(:,:,3);
%Detect Skin
%[r,c,v] = find(cb>=77 & cb<=127 & cr>=133 & cr<=173);
[r c v] = find(cb<=77 | cb >=127 | cr<=133 | cr>=173);
numid = size(r,1);
%Mark Skin Pixels
for i=1:numid
img_ycbcr(r(i),c(i),:) = 0;
% bin(r(i),c(i)) = 1;
end
figure
title('ycbcr segmentation');
imshow(img_ycbcr);
%==============================================================
%rgb segmentation
img_rgb=img_ycbcr;
r=img_rgb(:,:,1);
g=img_rgb(:,:,2);
b=img_rgb(:,:,3);
[row col v]= find(b>0.79*g-67 & b<0.78*g+42 & b>0.836*g-14 & b<0.836*g+44 ); %non skin pixels
numid=size(row,1);
for i=1:numid
img_rgb(row(i),col(i),:)=0;
end
figure
imshow(img_rgb);
Here my sample :
I agree with Adriaan. Don't do it with just colour, use additional information such as the shape and the edges.
The last two colorplanes seem to have the most contrast, so let's use one of them:
Nipple = imread('N8y6Q.jpg')
Nipple = imadjust(Nipple(:,:,2));
imshow(Nipple)
[centers, radii] = imfindcircles(Nipple, [30,60]);
hold on
imshow(Nipple);
viscircles(centers, radii);
The circular Hough transform is a robust way to find circular objects if you know the approximate radius range and are satisfied with the approx. location and size of the object.
If not you can try other classical methods, e.g. (Canny) edge detection, using the Hough center point as a marker -> region growing, fitting a snake etc. etc.

How can i remove overlaping circles after Hough Transform segmentation

I'm working in image segmentation, testing a lot of different segmentation algorithms, in order to do a comparitive study. At the moment i'm using Hough transform to find circles in the image. The images that i'm using have plenty objects, so when Í count the objects the result is hudge. I think the problem, is the overlaping circle. Do you know how can i maybe remove the overlaping circles to have a result more close to reality?
The code that i'm using is:
clear all, clc;
% Image Reading
I=imread('0001_c3.png');
figure(1), imshow(I);set(1,'Name','Original')
image used
% Gaussian Filter
W = fspecial('gaussian',[10,10]);
J = imfilter(I,W);
figure(2);imshow(J);set(2,'Name','Filtrada média');
X = rgb2gray(J);
figure(3);imshow(X);set(3,'Name','Grey');
% Finding Circular objects -- Houng Transform
[centers, radii, metric] = imfindcircles(X,[10 20], 'Sensitivity',0.92,'Edge',0.03); % [parasites][5 30]
centersStrong = centers(1:60,:); % number of objects
radiiStrong = radii(1:60);
metricStrong = metric(1:60);
viscircles(centersStrong, radiiStrong,'EdgeColor','r');
length(centers)% result=404!
You could simply loop over the circles and check if others are "close" to them. If so, you ignore them.
idx_mask = ones(size(radii));
min_dist = 1; % relative value. Tweak this if slight overlap is OK.
for i = 2:length(radii)
cur_cent = centers(i, :);
for j = 1:i-1
other_cent = centers(j,:);
x_dist = other_cent(1) - cur_cent(1);
y_dist = other_cent(2) - cur_cent(2);
if sqrt(x_dist^2+y_dist^2) < min_dist*(radii(i) + radii(j)) && idx_mask(j) == 1
idx_mask(i) = 0;
break
end
end
end
%%
idx_mask = logical(idx_mask);
centers_use = centers(idx_mask, :);
radii_use = radii(idx_mask, :);
metric_use = metric(idx_mask, :);
viscircles(centers_use, radii_use,'EdgeColor','b');
The picture shows all circles in red, and the filtered circles in blue.
The if clause checks two things:
- Are the centers of the circles closer than the sum of their radii?
- Is the other circle still on the list of considered circles?
If the answer to both questions is yes, then ignore the "current circle".
The way the loop is set up, it will keep circles that are higher up (have a lower row index). As is, the circles are already ordered by descending metric. In other words, as is this code will keep circles with a higher metric.
The code could optimized so that the loops run faster, but I don't think you'll have millions of circles in a single picture. I tried writing it in a way that it's easier to read for humans.

Matlab: Matrix manipulation: Change values of pixels around center pixel in binary matrix

I have another problem today:
I have a binary matrix t, in which 1 represents a river channel, 0 represents flood plane and surrounding mountains:
t = Alog>10;
figure
imshow(t)
axis xy
For further calculations, I would like to expand the area of the riverchannel a few pixels in each direction. Generally speaking, I want to have a wider channel displayed in the image, to include a larger region in a later hydraulic model.
Here is my attempt, which does work in certain regions, but in areas where the river runs diagonal to the x-y axis, it does not widen the channel. There seems to be a flow in approaching this, which I cannot quite grasp.
[q,o] = find(t == 1);
qq = zeros(length(q),11);
oo = zeros(length(o),11);
% add +-5 pixel to result
for z=1:length(q)
qq(z,:) = q(z)-5:1:q(z)+5;
oo(z,:) = o(z)-5:1:o(z)+5;
end
% create column vectors
qq = qq(:);
oo = oo(:);
cords = [oo qq]; % [x y]
% remove duplicates
cords = unique(cords,'rows');
% get limits of image
[limy limx] = size(t);
% restrict to x-limits
cords = cords(cords(:,1)>=1,:);
cords = cords(cords(:,1)<=limx,:);
% restrict to y-limits
cords = cords(cords(:,2)>=1,:);
cords = cords(cords(:,2)<=limy,:);
% test image
l = zeros(size(img));
l(sub2ind(size(l), cords(:,2)',cords(:,1)')) = 1;
figure
imshow(l)
axis xy
This is the image I get:
It does widen the channel in some areas, but generally there seems to be a flaw with my approach. When I use the same approach on a diagonal line of pixels, it will not widen the line at all, because it will just create more pairs of [1 1; 2 2; 3 3; etc].
Is there a better approach to this or even something from the realm of image processing?
A blur filter with a set diameter should be working somewhat similar, but I could not find anything helpful...
PS: I wasn't allowed to add the images, although I already have 10 rep, so here are the direct links:
http://imageshack.us/a/img14/3122/channelthin.jpg
http://imageshack.us/a/img819/1787/channelthick.jpg
If you have the image processing toolbox, you should use the imdilate function. This performs the morphological dilation operation. Try the following code:
SE = strel('square',3);
channelThick = imdilate(channelThin,SE);
where SE is a 3x3 square structuring element used to dilate the image stored in channelThin. This will expand the regions in channelThin by one pixel in every direction. To expand more, use a larger structuring element, or multiple iterations.
You may apply morphological operations from image processing. Morphological dilation can be used in your example.
From the image processing toolbox, you can use bwmorth command BW2 = bwmorph(BW,'dilate') or imdilate command IM2 = imdilate(IM,SE).
Where IM is your image and SE is the structuring element. You can set SE = ones(3); to dilate the binary image by "one pixel" - but it can be changed depending on your application. Or you can dilate the image several times with the same structuring element if needed.