Detecting almost surrounded background areas [closed] - matlab

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 5 years ago.
Improve this question
I'm doing image processing on tribological samples. I'm segmenting the wear track from the images, but have a recurring problem:
Setting the threshold too low leads to a complete failure. Setting it too high (as in the image) leads to multiple areas that are almost but not quite enveloped by the label. They mess up the distance transform I'm using to estimate track width, and should be detected and merged to the label.
I have used morphological operations to improve the quality of the label, but do not wish to make the structuring elements bigger due to some side effects in the rest of the image. The curvature of the label prevents me from using convex hulls. The bulk of the label prevents me from using the solidity of the label as an indicator. The unwanted interior objects are not enveloped completely by the label, so they can not be detected via the Euler characteristic etc.
Is there any good way to detect background objects that are 'almost completely' surrounded by foreground objects?

I used watershed to divide the background into separate regions and then bwboundaries to detect how much of a region's boundary is shared with the foreground object:
% generate example image
bw = im2double(rgb2gray(imread('example.jpg'))) == 1;
% dilate binary object to overlap watershed boundaries
bwDil = imdilate(bw,ones(5));
% get watershed labels
D = bwdist(bw);
D = -D;
D(bw) = -Inf;
L = watershed(D);
% get binary regions and remove fg object
R = (L > 0);
R(bw) = 0;
% get boundaries of all regions
BR = bwboundaries(R);
% set boundary ratio - if a regio's shares more boundary with fg object
% than this threshold it considered surrounded
boundaryRatio = zeros(numel(BR),1);
ratioThresh = 0.6;
mask = false(size(bw));
% go through region boundaries and add them to mask if above tresh
for ii = 1:numel(BR)
ind = sub2ind(size(bw),BR{ii}(:,1),BR{ii}(:,2));
boundaryRatio(ii) = nnz(bwDil(ind))/numel(ind);
if boundaryRatio(ii) > ratioThresh
mask(ind) = 1;
end
end
% fill mask
mask = imfill(mask,4,'holes');
% plot
subplot(121);
imshow(bw);
title('fg')
rgb = double(cat(3,mask,bw,bw));
subplot(122);
imshow(rgb);
title('fg with surrounded bg')

Related

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.

detecting finger valleys with Matlab [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 6 years ago.
Improve this question
I have a binary image of the hand like that:
I have to write a Matlab function that detects the valley between two fingers.
The parameters are the binary image and the coordinates of the two finger's tips.
I'm new in image processing and I don't know how to start.
I suggest to isolate the black area between the two input points, and then find the highest point in this connected component.
You can try the following approach (you may need to tweak some of the parameters, but it should be a good start).
I = rgb2gray(imread('<your path>'));
%input parameters - points which represents two finger tips.
x1 = 408; y1 = 441;
x2 = 454; y2 = 373;
%binarize image
I = im2bw(I);
%noise reduction - close holes
I2 = imclose(I,strel('disk',10));
%draw a line between p1 and p2
ind = drawline([y1 x1],[y2 x2],size(I));
lineMat = zeros(size(I));
lineMat(ind) = 1;
%adds the line to the image
I2 = I2 | lineMat;
%finds a point in the middle of the line
[lineY, lineX] = ind2sub(size(I),ind);
midX = lineX(ceil(length(ind)/2));
midY = lineY(ceil(length(ind)/2));
%finds a point which resides in the connected component which is between
%the line and the two finger.
xSeed = midX;
ySeed = midY -5;
%perform imfill operation, starting from (xSeed,ySeed),
%in order to find the conected component in which the point (xSeed,ySeed)
%resides.
diffMat = imfill(I2,[ySeed xSeed])~=I2;
%finding the highest point in this connected component
[Y, X] = ind2sub(size(diffMat),find(diffMat));
minInd = find(Y==min(Y),1,'first');
yValley = Y(minInd);
xValley = X(minInd);
%presents result
imshow(I);hold on;
plot(x1,y1,'r.','MarkerSize',20);
plot(x2,y2,'r.','MarkerSize',20);
plot(xValley,yValley,'b.','MarkerSize',20);
*draw line function is taken from drawline webpage.
Final result (input points in red, output point in blue).
It's just the algorithm, but all these function certainly exist in MatLab:
Compute the convex hull
Compute the difference: Convex Hull minus the original shape. Then you have all the valleys you are looking for, plus some small patterns.
Connected components labeling.
Delete the small components. Then you have all the valleys between the fingers.
Then you can select the one you want using the fingertip coordinates.

How to get pixel location of object in image [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 7 years ago.
Improve this question
Suppose I have a binary image shown in below. I want to get the location(pixel value in X,Y coordinates) of all rectangular shapes and also circular shapes. How to detect how many rectangular and circular shapes are present there. I want to get the solution in Matlab. The rectangle and circle may be in different size. The small circles are noise here. Thanks in advance.
You need to:
find connected components (bwconncomp)
find some statistic for each connected component (regionprops)
discard connected components that are too small, defining a threshold on the area
find if the connected component is a rectangle or a circle / ellipse. You can use the circularity, defined as 4*pi*Area / (Perimeter^2). Perfect circles will have value 1, squares 0.785, and so on. So you can define a threshold on circularity to determine if a shape is a circle/ellipse or not.
Here the result, with smaller blobs removed, and circles/ellipses and rectangles in different colors:
Code:
% Read image
img = imread('path_to_image');
% Convert to grayscale
img = rgb2gray(img);
% Remove JPEG artifacts
bw = img > 20;
% Prepare output image
output = zeros(size(bw));
% Compute connected components, and their statistics
cc = bwconncomp(bw);
props = regionprops(cc, 'Area', 'Perimeter');
% Will contain x,y coordinates for rectanles and circles
coord_rect = {};
coord_circ = {};
% Define thresholds on size and circularity
th_size = 200;
th_circ = 0.8;
% For each connected component
for i = 1 : cc.NumObjects
% Is big enough?
if(props(i).Area < th_size)
continue;
end
% Rectangle or Circle?
circularity = 4 * pi * props(i).Area / (props(i).Perimeter ^ 2);
[y,x] = ind2sub(cc.ImageSize, cc.PixelIdxList{i});
if(circularity > th_circ)
% Circle
output(cc.PixelIdxList{i}) = 1;
coord_circ{end+1} = [x, y];
else
% Rectangle
output(cc.PixelIdxList{i}) = 2;
coord_rect{end+1} = [x, y];
end
end
% Show results
imshow(bw);
figure();
imagesc(output);

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

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.

Count black pixels within a circle [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 7 years ago.
Improve this question
I have one vector of radii and second vector of hundreds of [X,Y] coordinates. For each possible coordinate-radius pair I have count all black pixels within a circle (whose center is placed in the coordinate) on an input binary image.
What is the fastest way to do it? My only idea is to iterate through every pixel of an image, check the circle equation and then the pixel color but it doesn't seem much optimized for several hundred such operations.
Matlab is great for working with images thanks to the matrix syntax. It does also work with indices so most time you can avoid "iterating through pixels" (although sometimes you'll still have to).
Instead of checking all the pixels within each circle, and having to detect how many pixels were counted twice, another approach is to create a mask, the same size of you image. Blank this mask for each of your circles (so overlapping pixels are only 'blanked' once), then apply the mask on your original picture and count the remaining illuminated pixels.
For an example, I have to take some sample data, the image:
load trees
BW = im2bw(X,map,0.4);
imshow(BW)
And 20 random point/circle coordinates (you can change the number of points and the min/max radii easily):
%// still building sample data
s = size(BW) ;
npt = 20 ; Rmin=5 ; Rmax=20 ; %// problem constants
x = randi([1 s(2)] ,npt,1); %// random X coordinates
y = randi([1 s(1)] ,npt,1); %// Y
r = randi([Rmin Rmax],npt,1); %// radius size between 5 to 20 pixels.
Then we build your custom mask :
%// create empty mask with enough overlap for the circles on the border of the image
mask = false( s+2*Rmax ) ;
%// prepare grid for a sub-mask of pixels, as wide as the maximum circle
xmask = -Rmax:Rmax ;
[xg,yg] = ndgrid(xmask,xmask) ;
rg = sqrt( (xg.^2+yg.^2) ) ; %// radius of each pixel in the subgrid
for ip=1:npt
mrow = xmask+Rmax+y(ip) ; %// calc coordinates of subgrid on original mask
mcol = xmask+Rmax+x(ip) ; %// calc coordinates of subgrid on original mask
cmask = rg <= r(ip) ; %// calculate submask for this radius
mask(mrow,mcol) = mask(mrow,mcol) | cmask ; %// apply the sub-mask at the x,y coordinates
end
%// crop back the mask to image original size (=remove border overlap)
mask = mask(Rmax+1:end-Rmax,Rmax+1:end-Rmax) ;
imshow(mask)
Then you just apply the mask and count :
%% // Now apply on original image
BWm = ~BW & mask ; %// note the ~ (not) operator because you want the "black" pixels
nb_black_pixels = sum(sum(BWm)) ;
imshow(BWm)
nb_black_pixels =
5283
Here is one implementation:
Advantages:
No loops, meshgrid/ndgrid. Instead used faster bsxfun and pdist2
Dots are counted only once, even when the circles overlap.
Variable radius used (radius of all circle is not same)
Code:
%// creating a binary image with little black dots
A = randi(600,256);
imbw = A ~= 1;
%// Your binary image with black dots
imshow(imbw);
%// getting the index of black dots
[dotY, dotX] = find(~imbw);
nCoords = 10; %// required number of circles
%// generating its random coordinates as it is unknown here
Coords = randi(size(A,1),nCoords,2);
%// calculating the distance from each coordinate with every black dots
out = pdist2(Coords,[dotX, dotY]).'; %//'
%// Getting only the black dots within the radius
%// using 'any' avoids calculating same dot twice
radius = randi([10,25],1,size(Coords,1));
pixelMask = any(bsxfun(#lt, out, radius),2);
nPixels = sum(pixelMask);
%// visualizing the results by plotting
hold on
scatter(dotX(pixelMask),dotY(pixelMask));
viscircles([Coords(:,1),Coords(:,2)],radius.'); %//'
hold off
Output:
>> nPixels
nPixels =
19