How to separate circles and lines in a binary image? - matlab

I got a problem and I'm supposed to separate circles and lines in the following image:
the thing is that I've just made some codes and it's working in a way that only separates them as different objects and the final separated objects are not the same as input ones! I post this question as it might be helpful for someone and if there's anybody that can make a better code to separate objects more clearly and maybe as accurate as the input ones.
I appreciate any comments in advance ...
The code is as follows:
clear; close all; clc; warning off
im = imread('input\circle_and_lines.jpg');
level = graythresh(im);
imBin = im2bw(im,level);
figure; imshow(imBin);
%% Separating circles
se1 = strel('disk',6);
imCircles = imopen(imBin,se1);
figure; imshow(imCircles);
[~,numCC] = bwlabel(imCircles,8);
imwrite(imCircles,'output\ex04_circle.png');
%% Separating lines
linDeg0 = imopen(imBin,strel('line',60,0)); figure; imshow(linDeg0);
linDeg10 = imopen(imBin,strel('line',60,10)); figure; imshow(linDeg10);
linDeg20 = imopen(imBin,strel('line',60,20)); figure; imshow(linDeg20);
linDeg75 = imopen(imBin,strel('line',60,75)); linDeg75 = imclose(linDeg75,strel('line',60,75)); figure; imshow(linDeg75);
linDeg95 = imopen(imBin,strel('line',40,95)); linDeg95 = imdilate(linDeg95,strel('disk',1)); figure; imshow(linDeg95);
linDeg135 = imopen(imBin,strel('line',60,135)); linDeg135 = imdilate(linDeg135,strel('disk',1)); figure; imshow(linDeg135);
linDeg170 = imopen(imBin,strel('line',60,170)); figure; imshow(linDeg170);
imLines = linDeg0 | linDeg10 | linDeg20 | linDeg75 | linDeg95 | linDeg135 | linDeg170;
figure; imshow(imLines);
imwrite(imLines,'output\ex04_line.png');
The final results are as follows:

% load image
I = imread('circles_and_lines.jpg');
% Separate the Circles
S1 = strel('disk',6,0);
circles = imopen(I, S1);
figure, imshow(circles);
title('Circles only');
lines = imopen(I,strel('line',60,1));
% Separate the Lines
for c = 1:1:200
lines = lines + imopen(I,strel('line',60,c));
end
figure, imshow(lines);
title('Lines only');
basically a slightly better version of your solution since the for loop scans and separates with more detail the lines

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

How to add standrad deviation and moving average

What I want to is:
I got folder with 32 txt files and 1 excle file, each file contain some data in two columns: time, level.
I already managed to pull the data from the folder and open each file in Matlab and get the data from it. What I need to do is create plot for each data file.
each of the 32 plots should have:
Change in average over time
Standard deviation
With both of this things I am straggling can't make it work.
also I need to make another plot this time the plot should have the average over each minute from all the 32 files.
here is my code until now:
clc,clear;
myDir = 'my path';
dirInfo = dir([myDir,'*.txt']);
filenames = {dirInfo.name};
N = numel(filenames);
data=cell(N,1);
for i=1:N
fid = fopen([myDir,filenames{i}] );
data{i} = textscan(fid,'%f %f','headerlines',2);
fclose(fid);
temp1=data{i,1};
time=temp1{1};
level=temp1{2};
Average(i)=mean(level(1:find(time>60)));
AverageVec=ones(length(time),1).*Average(i);
Standard=std(level);
figure(i);
plot(time,level);
xlim([0 60]);
hold on
plot(time, AverageVec);
hold on
plot(time, Standard);
legend('Level','Average','Standard Deviation')
end
the main problam with this code is that i get only average over all the 60 sec not moving average, and the standard deviation returns nothing.
few things you need to know:
*temp1 is 1x2 cell
*time and level are 22973x1 double.
Apperently you need an alternative to movmean and movstd since they where introduced in 2016a. I combined the suggestion from #bla with two loops that correct for the edge effects.
function [movmean,movstd] = moving_ms(vec,k)
if mod(k,2)==0,k=k+1;end
L = length(vec);
movmean=conv(vec,ones(k,1)./k,'same');
% correct edges
n=(k-1)/2;
movmean(1) = mean(vec(1:n+1));
N=n;
for ct = 2:n
movmean(ct) = movmean(ct-1) + (vec(ct+n) - movmean(ct-1))/N;
N=N+1;
end
movmean(L) = mean(vec((L-n):L));
N=n;
for ct = (L-1):-1:(L-n)
movmean(ct) = movmean(ct+1) + (vec(ct-n) - movmean(ct+1))/N;
N=N+1;
end
%mov variance
movstd = nan(size(vec));
for ct = 1:n
movstd(ct) = sum((vec(1:n+ct)-movmean(ct)).^2);
movstd(ct) = movstd(ct)/(n+ct-1);
end
for ct = n+1:(L-n)
movstd(ct) = sum((vec((ct-n):(ct+n))-movmean(ct)).^2);
movstd(ct) = movstd(ct)/(k-1);
end
for ct = (L-n):L
movstd(ct) = sum((vec((ct-n):L)-movmean(ct)).^2);
movstd(ct) = movstd(ct)/(L-ct+n);
end
movstd=sqrt(movstd);
Someone with matlab >=2016a can compare them using:
v=rand(1,1E3);m1 = movmean(v,101);s1=movstd(v,101);
[m2,s2] = moving_ms(v,101);
x=1:1E3;figure(1);clf;
subplot(1,2,1);plot(x,m1,x,m2);
subplot(1,2,2);plot(x,s1,x,s2);
It should show a single red line since the blue line is overlapped.

Create a multiple horizontal line plot, plotting numerous variable for multiple years

I have 9 years of data, each has the same variables saved e.g. summer, warmest months, length of sunlit months etc. I want to plot each year's variables as a set of horizontal lines, grouped together, with different line properties. So years will be on the y axis and months on the x axis.
Sorry this is a little vague, I'm not sure how else to describe it.
The following code might get you close to, if not the exact thing, what you are looking for -
%% PLOT YEARLY DATA ON TOP OF EACH OTHER
%% NOTE: Tinker with the text and plot properties to put Y labels and the custom-made legend at custom positions on the plot
%% Data - Insert your data in this way
years = 2001:2009;
months_string = {'Jan'; 'Feb'; 'Mar';'Apr'; 'May'; 'Jun';'Jul'; 'Aug'; 'Sep';'Oct'; 'Nov'; 'Dec'};
num_years = numel(years);
num_months = numel(months_string);
for count = 1:num_years
data.year(count).summer = 12.*rand(num_months,1);
data.year(count).warmest_months = 48*rand(num_months,1);
data.year(count).len_sunlit = 23*rand(num_months,1);
end
%% Params
offset_factor = 0.5;
x_legend_offset_factor = 0.75;
extension_top_legend = 0.2;
ylabel_pos = -1.0;
%% Add some useful info the struct, to be used later on
for count = 1:num_years
data.year(count).minval = min([ min(data.year(count).summer) min(data.year(count).warmest_months) min(data.year(count).len_sunlit)]);
data.year(count).maxval = max([ max(data.year(count).summer) max(data.year(count).warmest_months) max(data.year(count).len_sunlit)]);
data.year(count).range1 = data.year(count).maxval - data.year(count).minval;
end
%% Global Offset
max_range = max(extractfield(data.year,'range1'));
global_offset = offset_factor*max_range;
off1 = zeros(num_years,1);
for count = 2:num_years
off1(count) = data.year(count-1).maxval + global_offset;
end
off1 = cumsum(off1);
%% Plot
figure,hold on,grid on,set(gca, 'YTick', []);
xlabel('Months');
for count = 1:num_years
plot(1:num_months,off1(count)+data.year(count).summer,'b')
plot(1:num_months,off1(count)+data.year(count).warmest_months,'r')
plot(1:num_months,off1(count)+data.year(count).len_sunlit,'g')
text(ylabel_pos,(data.year(count).minval+data.year(count).maxval)/2+off1(count),['Year - ',num2str(years(count))])
end
% Find Y Limits and extending the plot at the top to accomodate the custom legend
ylimit = off1(num_years) + data.year(num_years).maxval;
ylim_1 = data.year(1).minval;
ylim_2 = ylimit+(ylimit - data.year(1).minval)*extension_top_legend;
ylim([ylim_1 ylim_2])
xlimits = xlim;
x_legend_offset = (xlimits(2) - xlimits(1))*x_legend_offset_factor;
% Adding text to resemble legends
txstr(1) = {'\color{blue} Summer'};
txstr(2) = {'\color{red} Warmest Months'};
txstr(3) = {'\color{green} Length of sunlit months'};
text(x_legend_offset,ylim_2,txstr,'HorizontalAlignment','center','EdgeColor','red','LineWidth',3)
set(gca, 'XTickLabel',months_string, 'XTick',1:numel(months_string))
For a random data, the plot might look like -
Let us know if the above code works for you!

Matlab GUI for array of spots

I need to create a GUI in Matlab. It requires me to identify the spots for two images, and calculate the distance between them.
I have obtained the code for finding and encircling a single spot. It is as follows:
function [meanx,meany] = centroid(pic)
[x,y,z] = size(pic);
if(z==1)
;
else
pic = rgb2gray(pic);
end
% N=2;
% image = interp2(double(pic),N,'spline');
image = sort(sort(pic,1),2);
image =reshape(image,1,numel(image));
i=0;
while(i<3)
if(image(end)==image(end-1))
image(end)=[];
else
image(end)=[];
i=i+1;
end
end
threshold = image(end);
pic2 =(pic>=threshold);
pic=(pic-threshold).*uint8(pic2);
% % image=(pic-threshold+1).*uint8(image); %minus threshold
[rows,cols] = size(pic);
x = ones(rows,1)*[1:cols];
y = [1:rows]'*ones(1,cols);
area = sum(sum(pic));
if area ~= 0
meanx = sum(sum(double(pic).*x))/area;
meany = sum(sum(double(pic).*y))/area;
else
meanx = cols/2;
meany = rows/2;
end
However, I need it to work for multiple spots as shown below :
http://imgur.com/oEe0mRV,UAnbH5y#0
http://imgur.com/oEe0mRV,UAnbH5y#1
So far, I have come up with this, but it only circles separate spots and not all together.
PLEASE HELP - I need to encircle at least 10X10 spots and store their values, and do this for two images as shown above, and find the distance between them!
img1 = imread('r0.bmp');
centroidmat=zeros(10,10,2);
for numx=1:2
for numy=1:2
single_spot=img1((numx*220+780):((numx+1)*220+780),(numy*220+1272):((numy+1)*220+1272),:);
figure
imshow(single_spot);
figure
[cx,cy] = centroid(single_spot);
centroidmat(numx,numy,1)=cx;
centroidmat(numx,numy,2)=cy;
imshow(single_spot);
hold on;
plot(cx,cy,'og')
end
end
Please HELP GUYS! Any help is appreciated!
Would this work ? -
centroidmat=zeros(10,10,2);
for numx=1:2
for numy=1:2
single_spot=img1((numx*220+780):((numx+1)*220+780),(numy*220+1272):((numy+1)*220+1272),:);
[cx,cy] = centroid(single_spot);
centroidmat(numx,numy,1)=cx;
centroidmat(numx,numy,2)=cy;
figure,
imshow(single_spot);
hold on;
plot(cx,cy,'og')
end
end
I have only removed the redundant figure and imshow(single_spot); at the start of the loop, as they appear again later on inside the same loop.

Matlab Legend after FOR loop

I am creating a file to read in a certain number of .wav files, each of these corresponding to a musical note. I am performing an FFT on each of them and plotting them all on the same figure. However I am having a problem with getting the legend to print correctly, it is separating the names I want to use into individual letters instead of using them as a string. My code is as follows:
clear all
mydir = 'Note Values/';
wavFiles = dir([mydir '*.wav']);
length(wavFiles)
legendText = [];
figure(1);
hold on;
for i = 1:length(wavFiles)
wavFiles(i).name
[y, fs] = wavread([mydir wavFiles(i).name]);
sound(y, fs)
currentSample = y(round(length(y)/2)-2048:round(length(y)/2)+2047);
FFT = abs(fft(currentSample));
Power = FFT.*conj(FFT)/length(FFT);
if (mod(i, 2) == 1)
h = plot(Power, 'Color', 'red');
else
h = plot(Power, 'Color', 'blue');
end
sri = wavFiles(i).name;
sri
legendText = [legendText, sri];
end
length(legendText)
legendText(1)
legend(legendText(:));
hold off;
The sri variable is always a full string, but legendText(1) only prints out A instead of A3.wav. I know it's probably something really obvious but I just can't find it. Thanks
The output on my graph appears as this:
You should use
legendText{i} = sri
to fill the cell with strings and
legend(legendText{:});
at end.
I don't have MATLAB by me, so I'm not able to test it, but as I recall, you should use { instead of [ :
legendText = {legendText, sri};