Matlab identify objects at boundary - matlab

I know basic commands in order to identify objects in a picture like:
level = graythresh(bw);
bw = im2bw(bw,level);
cc = bwconncomp(bw, 4);
cc.NumObjects;
graindata = regionprops(cc, 'basic');
perimeter = regionprops(cc, 'perimeter');
Those codes above is the code I am using.
In the picture attached, I can get the number to be 4. So the code identify that there is in total 4 objects.
However, this picture actually contains two objects. If we replicate this picture and move the replicate to the up, down, left and right, we can see that there is only two objects. But they are "separated" by the boundary.
It is not doable to change the way of making the image so the only way I can think of is to use some function or codes in matlab.
I will really appreciate it if someone can provide some matlab function to solve this problem.

All you need to do is loop over the border rows and columns and merge any regions that line up on opposite sides. The following code will produce an image with the regions labelled by number in the way you want.
cc=bwconncomp(bw);
[rows,cols] = size(reg);
% matrix of region labels
regions = uint8(zeros(rows,cols));
% label each pixel with an integer for its region number
for i = 1:length(cc.PixelIdxList)
region(cc.PixelIdxList{i}) = i;
end
% loop over rows, merge the regions if pixels line up
for i = 1:rows
left = region(i,1);
right = region(i,end);
if (left>0) && (right>0) && (left~=right)
region(region==right) = left;
end
end
% loop over columns, merge the regions if pixels line up
for j = 1:cols
top = region(1,j);
bottom = region(end,j);
if (top>0) && (bottom>0) && (top~=bottom)
region(region==bottom) = top;
end
end

Related

Why my segmentation using HSV space isn't giving me a good result?

I am attending a class on image processing with Matlab and I found a problem on the internet and I tried to solve it but it is hard, because I am new to this subject.
The problem has to be solved with Matlab and it is new for me as well.
Problem:
'Think of RGB and HSV spaces as 3-dimensional spaces. Define a
neighborhood distance (we can use ecludien distance) and set to 0 all
the pixels whose value in RGB or HSV space is far (in the sense of
this distance) from your reference value. See if you can easily
achieve the same type of segmentation in both cases. Try for example
to segment only the helmets of the warriors of the image
bilibinWar.jpg.'
I tried to code it but i don't know if the result is good.
%%
% 4) 8 - Segmentatiion dans l’’espace HSV et RVB..
clear all; close all;
ImagebilibinWar = imread("bilibinWar.jpg");
[lin, col, pla] = size(ImagebilibinWar);
figure(1);imshow(ImagebilibinWar);
Image_bilibinWar_HSV= rgb2hsv(ImagebilibinWar);
[lo,co] = ginput(1);
lo = round(lo);
co = round(co);
RIOH = Image_bilibinWar_HSV(lo,co,1);
%%
% Im going to use this first section so ican isolate the helmets inside a circle ARROUND THE HELMETS and get rid of REST background Circle_Image_bilibinWar_HSV = Image_bilibinWar_HSV;
for i= 1:lin
for j = 1:col
d(i,j) = ((i-lo)^2 + (j-co)^2)^0.5;
if d(i,j,:) > 90
Circle_Image_bilibinWar_HSV(i,j,:) = 0;
end
end
end
Circle_Image_bilibinWar_RGB = hsv2rgb(Circle_Image_bilibinWar_HSV);
figure(2);imshow(Circle_Image_bilibinWar_RGB); % i have isolated the circle
%% here im going to isolate the helmets inside the circle
figure(3); imshow(Circle_Image_bilibinWar_HSV); % as u can see here i will try to isolate the helmets from the rest
New_Circle_Image_bilibinWar_HSV = Circle_Image_bilibinWar_HSV % assinging a new image so as i can work on it seperatly;
valueH = New_Circle_Image_bilibinWar_HSV(lo,co,1);
for i=1:lin
for j=1:col
if (New_Circle_Image_bilibinWar_HSV(i,j,1) > valueH + 0.19)
New_Circle_Image_bilibinWar_HSV(i,j,:) = 0;
end
end
end
Circle_Image_bilibinWar_HSV = Circle_Image_bilibinWar_HSV - New_Circle_Image_bilibinWar_HSV;
NEW_Image_bilibinWar_RGB = hsv2rgb(Circle_Image_bilibinWar_HSV);
figure(4); subplot(1,2,1);imshow(ImagebilibinWar);
subplot(1,2,2);imshow(NEW_Image_bilibinWar_RGB);
So can some one help me with it? i don't know if its good and if not how can i make it good ?
I don't think there is anything wrong with your code. The answer is that segmenting using euclidean distance in colors simply does not work for RGB or HSV spaces. The entire purpose of the L*a*b color space was indeed this, creating a color space where similar colors would have the little euclidean distance.
Here a less cluttered version of it:
clear all; close all;
ImagebilibinWar = imread("https://i.stack.imgur.com/wnBDu.jpg");
[lin, col, pla] = size(ImagebilibinWar);
figure;imshow(ImagebilibinWar);
ImagebilibinWarHSV= rgb2hsv(ImagebilibinWar);
[lo,co] = ginput(1);
lo = round(lo);
co = round(co);
RIOH = ImagebilibinWarHSV(lo,co,1);
RIOS = ImagebilibinWarHSV(lo,co,2);
RIOV = ImagebilibinWarHSV(lo,co,3);
%reference value that i have to choose
for a=0:0.05:1
d = ((ImagebilibinWarHSV(:,:,1)-RIOH).^2 + (ImagebilibinWarHSV(:,:,2)-RIOS).^2+(ImagebilibinWarHSV(:,:,3)-RIOV).^2).^0.5;
ImagebilibinWarRGB = hsv2rgb(ImagebilibinWarHSV.*double(d<a));
figure;imshow(ImagebilibinWarRGB);
end

Which filter to apply in the image to remove all the objects other than vehicles

I used otsu thresh holding technique to separate the background from vehicles. Now I want to calculate the number of vehicles and when I apply my algorithm it calculates the other objects along with vehicles so I want to apply any filter that would remove other objects from the image. The image and code for otsu separation and counting of objects are shown below. Help me out in this.
Vehicles separated from road:
My code:
i=imread('14.png');
figure,imshow(i);
t2=graythresh(i)
it=im2bw(i,t2)
figure,imshow(it);
c=imfill(it,'holes');
figure,imshow(c);
se=strel('disk',20);
iopenned=imopen(c,se);
figure,imshow(iopenned);
[labeled,numObjects]=bwlabel(iopenned,4);
The code seems to be fine, I only changed disk size to 8, and then you have to ignore small objects, say, smaller than 1000 pixels.
Try this:
% set car size threshold
minCarSize = 1000;
% read image from stackoverflow
img = imread('https://i.stack.imgur.com/0JVK9.jpg');
it = img(31:425,83:1035,1) > 200;
% process the image with disk size 8
c = imfill(it,'holes');
se = strel('disk',8);
iopenned = imopen(c,se);
[labeled,numObjects]=bwlabel(iopenned,4);
% check object sizes
objSize = nan(numObjects,1);
loc = nan(numObjects,2);
for iObj = 1:numObjects
objSize(iObj,1) = sum(sum(labeled == iObj));
[loc(iObj,1),loc(iObj,2)] = find(labeled == iObj,1); % these are x-y to display car numbers
end
% select big shapes
car = find(objSize > minCarSize);
% create image with car count
carNum = insertText(uint8(iopenned*255),fliplr(loc(car,:)),regexp(num2str(1:length(car)),' *','split'));
figure
imshow(carNum);
title('iopenned + car count')
axis off

Grouping of points using Triangulation

Language : MATLAB
Problem Defenition:
I have a set of 2D points in space. I would like to group the points based on their euclidean distance. My data has a property that two groups are always separated by at least R units. Hence for a given point, all points that are closer than 50 units can be considered to be its neighbors. Combining points having common neighbors would result in the groups (that is the idea at least).
Proposed Method:
Use delaunay triangulation in matlab and get list of edges of the resulting triangles. Remove all edges that are greater than R units. Each group of points left are the groups I am looking for. Remaining unconnected points can be ignored.
Attempt:
I tried to implement the above in MATLAB, but I am making a mistake in grouping the left over points. I am attaching my code.
DT = delaunayTriangulation(double(frame(:,1:2)));
edgeList = edges(DT);
edgeVertex1 = frame(edgeList(:,1),:);
edgeVertex2 = frame(edgeList(:,2),:);
dVec = edgeVertex1 - edgeVertex2;
edgeLengths = sqrt(sum(abs(dVec).^2,2));
requiredEdges = edgeLengths < NEIGH_RADIUS;
edgeLengthsFiltered = edgeLengths(requiredEdges);
edgeListFiltered = edgeList(requiredEdges,:);
% Clustering
edgeOrigins = edgeListFiltered(:,1);
edgeEndings = edgeListFiltered(:,2);
nodeList = unique(edgeOrigins);
if isempty(nodeList)
Result = struct([]);
super_struct(i).result = Result;
else
groups = cell(10,1);
groups{1} = nodeList(1);
groupLength = 2;
flag = 0;
% grouping
for j = 1:1:length(nodeList);
neighbourList = [nodeList(j); edgeEndings(edgeOrigins==nodeList(j))];
% add current node as part of neighbourList
for k = 1:1:groupLength-1
te = ismembc(groups{k}, neighbourList);
if sum(te) ~=0
temp = sort([groups{k}; neighbourList]);
groups{k} = temp([true;diff(temp(:))>0]);
flag = 1;
break;
end
end
if ~flag
groups{groupLength} = neighbourList;
groupLength = groupLength + 1;
end
flag = 0;
end
largeGroups = cell(1,1);
largeGroups_c = 1;
for j = 1:1:groupLength -1;
if ~ isempty(groups{j})
for k = j+1:1:groupLength - 1
te = ismembc(groups{j}, groups{k});
if sum(te) ~= 0
temp = sort([groups{j}; groups{k}]);
groups{j} = temp([true;diff(temp(:))>0]);
groups{k} =[];
end
end
% ignore small groups
if length(groups{j}) > MIN_PTS_IN_GROUP
largeGroups{largeGroups_c} = groups{j};
largeGroups_c = largeGroups_c+1;
end
end
end
in the above code, frame is the variable that has the list of points. The constants NEIGH_RADIUS represents R from the question. The other constant MIN_PTS_IN_GROUP is user defined to select the minimum no of points needed to consider it a cluster of interest.
When I run the above code, there are still instances where a single group of points are still represented as multiple groups.
The red lines border a single group as identified by the code above. Clearly there are intersecting groups which is wrong.
Question 1
Can someone suggest a better (and correct) way of grouping?
Question 2
Any other alternate methods of obtaining the groups faster than Triangulation would also be great!
Thank you in advance
If you know the number of group you search for, you can use kmeans function in matlab statistic toolbox or you can find other implentation on matlab exchange (kmeans clustering)

Matlab get vector of specific pixels

I am pretty new to Matlab and encountered a problem when working with images.
I want to get a pixel that is in a specific colour (blue) in the following image:
image
My current code looks something like this:
function p = mark(image)
%// display image I in figure
imshow(image);
%// first detect all blue values higher 60
high_blue = find(image(:,:,3)>60);
%cross elements is needed as an array later on, have to initialize it with 0
cross_elements = 0;
%// in this iteration the marked values are reduced to the ones
%where the statement R+G < B+70 applies
for i = 1:length(high_blue)
%// my image has the size 1024*768, so to access the red/green/blue values
%// i have to call the i-th, i+1024*768-th or i+1024*768*2-th position of the "array"
if ((image(high_blue(i))+image(high_blue(i)+768*1024))<...
image(high_blue(i)+2*768*1024)+70)
%add it to the array
cross_elements(end+1) = high_blue(i);
end
end
%// delete the zero element, it was only needed as a filler
cross_elements = cross_elements(cross_elements~=0);
high_vector = zeros(length(cross_elements),2);
for i = 1:length(cross_elements)
high_vector(i,1) = ceil(cross_elements(i)/768);
high_vector(i,2) = mod(cross_elements(i), 768);
end
black = zeros(768 ,1024);
for i = 1:length(high_vector)
black(high_vector(i,2), high_vector(i,1)) = 1;
end
cc = bwconncomp(black);
a = regionprops(cc, 'Centroid');
p = cat(1, a.Centroid);
%// considering the detection of the crosses:
%// RGB with B>100, R+G < 100 for B<150
%// consider detection in HSV?
%// close the figure
%// find(I(:,:,3)>150)
close;
end
but it is not optimized for Matlab, obviously.
So i was wondering if there was a way to search for pixels with specific values,
where the blue value is larger than 60 (not hard with the find command,
but at the same time the values in the red and green area not too high.
Is there a command I am missing?
Since English isn't my native language, it might even help if you gave me some suitable keywords for googling ;)
Thanks in advance
Based on your question at the end of the code, you could get what you want in a single line:
NewImage = OldImage(:,:,1) < SomeValue & OldImage(:,:,2) < SomeValue & OldImage(:,:,3) > 60;
imshow(NewImage);
for example, where as you see you provide a restriction for each channel using logical operators, that you can customize of course (eg. using | as logical OR). Is this what you are looking for? According to your code you seem to be looking for specific regions in the image like crosses or coins is that the case? Please provide more details if the code I gave you is completely off the track :)
Simple example:
A = imread('peppers.png');
B = A(:,:,3)>60 & A(:,:,2)<150 & A(:,:,1) < 100;
figure;
subplot(1,2,1);
imshow(A);
subplot(1,2,2)
imshow(B);
Giving this:

Accessing rows at a fixed interval

I'm looking for a way to update certain elements in a vector [nx113] for every full rotation of my system.
%% # Iterate through timesteps
for tt = 1:nTimeSteps
% # Initialise ink on transfer roller
rollers(2).ink = [zeros(1,98),ones(1,5),zeros(1,113)];
% # Rotate all rollers
for ii = 1:N
rollers(ii).ink(:) = ...
circshift(rollers(ii).ink(:),rollers(ii).rotDirection);
end
% # Update all roller-connections
for ii = 1:N
for jj = 1:nBins(ii)
if(rollers(ii).connections(jj) ~= 0)
index1 = rollers(ii).connections(jj);
index2 = find(ii == rollers(index1).connections);
ink1 = rollers(ii).ink(jj);
ink2 = rollers(index1).ink(index2);
rollers(ii).ink(jj) = (ink1+ink2)/2;
rollers(index1).ink(index2) = (ink1+ink2)/2;
end
end
end
% # Calculate average amount of ink on each roller
for ii = 1:N
averageAmountOfInk(tt,ii) = mean(rollers(ii).ink);
end
rollers(18).TakeOff = averageAmountOfInk*0.6;
end
the vector rollers(2).ink is the vector i'd like to update. currently the vector is populated only once so i have ones from row 98:103. I would like this range of elements to be populated for each 'rotation' of my system not just the first time.
The reason - I'm trying to show ink being added intermittently from only a small section of the roller surface, hence the need for only five cells to be populated.
i thought that if i iterated from 1 to the number of timesteps, in steps size nBins-Max in the loop:
for tt = 1:nBins_max:nTimeSteps
this doesn't seem to be what i'm after.
I'm also hoping to remove ink from the system at the end. for every revolution i would like to be able to remove a percentage of ink on each rotation so it does not stay in the system (as if it was being printed onto a sheet and taken away).
Hopefully someone can understand this and perhaps offer some advice on how to proceed on either or both of my issues.
Your explanation doesn't quite match your code (or vice-versa if you prefer) so I'm not entirely sure what you want to do, but the following may help you towards a solution or towards expressing your problem more clearly.
The vector rollers(2).ink has 1 row and 216 columns, so an operation such as rollers(2).ink(98:103) = something is not updating rows 98 through to 103. Note also that element 98 of that vector is initialised to 0, it's not included in the elements which are initialised to 1.
You write that you want to update a range of the elements in that vector, then write a loop statement for tt = 1:nBins_max:nTimeSteps which strides over a vector of time steps. Surely you want to write something like rollers(2).ink(99:103) = new_values.
As for removing ink from the rollers at every rotation, you could just execute a line such as rollers(2).ink = rollers(2).ink * 0.975 every rotation; obviously you'll want to replace the removal rate of 2.5% every rotation that I have chosen with whatever is right for your simulation.