I'm writing a script to process .png images similar to how 3D printing slicers work.
I need to fill the inside of the objects 'shell' but not gaps in the shell itself.
First I tried using MATLAB imfill function which gets me 90% of the way but runs into trouble if there's a internal wall or gap, in that case it fills the shell and the gap as well. (image 2).
I then tried a set of if statements that fill based on encountering walls moving along the image, filling after every other wall encountered. This works except for when it encounters a wall that doesn't have a void after it like a long side wall, (image 3).
My desired result is in image 4. That was done with a manual imfill but the image sets I want to process are in the thousands so an automated method is nearly essential.
%method 1
solid = imfill(im);
%method 2
% im aware this code is bad and full of redundant checks and conditions
% it was written as a fast check to see if what i believed the failure would
% be was correct which it sadly was. I'm not as interested in the content
% (I can fix that myself) as I am the general outcome.
slick = imread('image.png');
solid_im = zeros(size(slick));
for kx=1:x
last=0;
is_filling = 0;
for ky =1:y
if slick(kx,ky) == 1 && last == 0 && is_filling == 0
solid_im(kx,ky) = 1;
last = 1;
is_filling = 1;
elseif slick(kx,ky) == 1 && last == 1 && is_filling == 0
solid_im(kx,ky) = 1;
elseif slick(kx,ky) == 0 && last == 1 && is_filling == 0
solid_im(kx,ky) = 0;
last = 0;
elseif slick(kx,ky) == 0 && last == 0 && is_filling ==0
solid_im(kx,ky) = 0;
elseif slick(kx,ky) == 1 && last == 0 && is_filling == 1
solid_im(kx,ky)= 1;
last = 1;
is_filling = 0;
elseif slick(kx,ky) == 1 && last == 1 && is_filling == 1
solid_im(kx,ky) = 1;
elseif slick(kx,ky) == 0 && last == 1 && is_filling == 1
solid_im(kx,ky) = 1;
last = 0;
elseif slick(kx,ky) == 0 && last == 0 && is_filling == 1
solid_im(kx,ky) = 1;
end
end
end
Image with only shell walls:
Completely filled with imfill:
Image filled by algorithm with bad lines:
Correctly filled image:
Looks like a an old answer of mine solves the problem:
Refer to: How can I fill between edges of these characters in matlab?
Here is the code:
I = imread('https://i.stack.imgur.com/6Rdl1.png');
I = imbinarize(rgb2gray(I));
J1 = imfill(I, 'holes');
J2 = imfill(I, [1, 1]);
J3 = imfill(~J2, [1, 1]);
J4 = imfill(~J3, 'holes');
J5 = J1 & (~J4);
figure;imshow(J5)
Result:
I can't guarantee the solution is going to work for thousands of images.
Related
So I'm working on a program. I need to iterate over a vector similar to this sample = [0; 0; 0; 1; 1; 1 ; 0]. I was thinking about using loops with accumulators to build a new 2d array where column 1 is how many 0s or 1s appear and column 2 is for which token it is. But I'm new to the syntax of matlab and checking the docs i mostly see slicing. Any ideas about building the new matrix are welcome.
sample vector and output below
arr = [0; 0; 0; 1; 1; 1; 0];
tokenizeSignal(arr)
ans =
3 0
3 1
1 0
Proposed strategy (array contains only 1 and 0) :
Initialize 2 counters
count_0 = 0;
count_1 = 0;
Iterate over the whole array arr
arr = [0; 0; 0; 1; 1; 1; 0];
[n,m] = size(arr); %m is espected equal to 1
y = arr[1,1]; %first element of the array, we need a reference
Start the loop and read the current element
for i=1:n
x = arr[i,1];
start to count how many zeros or how many ones from the last group if the last element was zero or one
if (x == 0 && y == 0)
count_0 = count_0 + 1;
count_1 = 0;
else if (x == 1 && y == 1)
count_1 = count_1 + 1;
count_0 = 0;
end
print every time the value will change (last term of a "sequence")
if (x != arr[1,i+1] && count_1 > 0 && i<n)
print(count_1, '1');
else if (x != arr[1,i+1] && count_0 > 0 && i<n)
print(count_0, '0');
end
update the values and close the FOR loop
y = x;
end
out of the cycle, print the last time
if (count_1 > 0)
print(count_1, '1');
else if (count_0 >)
print(count_0, '0');
end
Of course you can change the print with store the values in a proper array.
I am trying to create a simple perceptron training function in MATLAB. I want to return the weights when no errors are found.
Here is the data I want to classify.
d = rand(10,2);
figure
labels = ones(10,1);
diff = d(:,1) + d(:,2) - 1;
labels( diff + 1 >= 1) = 2;
pathWidth = 0.05;
labels( abs(diff) < pathWidth) = [];
d(abs(diff) < pathWidth,:) = [];
plot(d(labels == 1,1),d(labels == 1,2),'k.','MarkerSize',10)
plot(d(labels == 2,1),d(labels == 2,2),'r.','MarkerSize',10)
It produces a labelled data set, where the division between the two classes (red, black) are more visible if you increase the number of points for d.
For my perceptron function I pass the data (d) and labels. I have 3 inputs, the x values, y values and bias which is one. Each input has a random weight between 0 and 1 assigned. Note that the dataset d I have named Z in the perceptron function.
I did use a sigmoid activation function but that would run once through the while loop and always return true after that, the sigmoid function also gave me values of either inf or 1. Below I am using just a threshold activation but it seems to continually loop and not returning my weights. I think the problem may lie in the if-statement below
if(v >= 0 && labels(i) == 1 || v < 0 && labels(i) == 2)
Perceptron function:
function perceptron(data,labels)
sizea = numel(data(:,1));
weights = rand(sizea,3);
Z = data(:,:)
eta = 0.5;
errors = 1;
count = 0;
while errors > 0
errors = 0;
v = sum((1*weights(1,1)) + (Z(:,1)*weights(1,2)) + (Z(:,2)*weights(1,3)));
if v >= 1
v = 1;
else
v = 0;
end
count = count + 1
for i = 1:sizea % for each object in dataset
if(v == 1 && labels(i) == 1 || v == 0 && labels(i) == 2)
errors = 1;
weights(1,1) = weights(1,1) - (2*v-1) * eta * 1;
weights(1,2) = weights(1,2) - (2*v-1) * eta * Z(i,1);
weights(1,3) = weights(1,3) - (2*v-1) * eta * Z(i,2);
v = sum((1*weights(1,1)) + (Z(:,1)*weights(1,2)) + (Z(:,2)*weights(1,3)));
if v >= 1
v = 1;
else
v = 0;
end
end
end
end
There are two major problems in your code:
You need to update v in your loop every time your weights vectors are updated.
It seems like you have 10 training set. So you have to update v sequentially in the loop instead of simultaneously. Keep iterating for every training set, update weights, then use the new weights to calculate the v for the next training set, so on and so forth, until there is no error (errors = 0 in your case).
Minor issue:
if(v >= 0 && labels(i) == 1 || v < 0 && labels(i) == 2)
should be
if(v == 1 && labels(i) == 1 || v == 0 && labels(i) == 2)
You may refer to this example to get more details of the algorithm.
Having issues with my move checker currently, it seems that the failsafe I put in my code to make it not look outside the bounds of the matrix isn't working, any suggestions?
There is also the issue that it doesn't seem to be working (i.e. I am still able to place pieces wherever I want!). The code I use earlier up is listed below as well
code:
function legal = legalMove()
d_l = [0, -1];
d_r = [0, 1];
d_u = [-1, 0];
d_d = [1, 0];
d_ul = [-1, -1];
d_ur = [-1, 1];
d_dl = [1, -1];
d_dr = [1, 1];
directions = {'d_l' 'd_ul' 'd_u' 'd_ur' 'd_r' 'd_dr' 'd_d' 'd_dl'};
valid_moves = zeros(8,8);
for ci = 1:8
for cj = 1:8
if game_state(ci,cj) == 0 %check element = 0
for count = 1:8
d = eval( directions{count} );
ti = ci+d(1);
tj = cj+d(2);
% Check if out of the board
if (ti > 8 || ti < 1) || (tj > 8 || tj < 1)
break
else
% Number of enemy pieces you went over
cnt = 0;
selected = game_state(ti, tj);
% Move while going over enemy pieces
while selected == player_number * -1
ti = ti + d(1);
tj = tj + d(2);
selected = game_state(ti, tj);
% Check if out of the board
if (ti > 8 || ti < 1) || (tj > 8 || tj < 1)
break
else
end
% Count pieces you went over
cnt = cnt + 1;
end
end
% Check if you moved over enemy pieces & whether you landed on your piece
if selected == player_number
valid_moves(ti,tj) = 1;
else
end
end
else
end
end
end
if ~isempty(valid_moves)
legal = 1;
else
legal = 0;
end
end
Error returned when done # boundries:
Attempted to access game_state(0,7); index must be a positive integer or
logical.
Error in umpire/legalMove (line 217)
selected = game_state(ti, tj);
Error in umpire/buttonPress (line 85)
legal = legalMove();
other piece:
function buttonPress(hObject, eventdata)
ended = game_is_over();
if ended == 1;
setAllInactive();
winner = calc_winner();
if winner == -1;
set(stat_text,'string','Winner is White! Restart?')
elseif winner == 1;
set(stat_text,'string','Winner is Black! Restart?')
else
set(stat_text,'string','Game is a tie! Restart?')
end
else
end
legal = legalMove();
if legal ~= 1;
set(stat_text,'Illegal move! Try again')
return
else
end
game_state(get(hObject,'userdata')) = player_number;
drawScreen();
player_number = player_number * -1;
end
In the second place where you assign variable selected (near the end of the function legalMove), you have out-of-board check in the wrong place.
Here's a fixed version
% ...
% Number of enemy pieces you went over
cnt = 0;
selected = game_state(ti, tj);
% Move while going over enemy pieces
while selected == player_number * -1
ti = ti + d(1);
tj = tj + d(2);
% Check if out of the board
if (ti > 8 || ti < 1) || (tj > 8 || tj < 1)
break
else
end
selected = game_state(ti, tj);
% Count pieces you went over
cnt = cnt + 1;
end
end
% Check if you moved over enemy pieces & whether you landed on your piece
if selected == player_number
valid_moves(ti,tj) = 1;
else
end
end
I am creating random starting and ending points. I want to plot those who cross/intersect with a rectangular which is placed at the origin. I found out that my code misses some lines, as shown in the figure. After that, I want to count were the tracks hit the rectangle. For expample the track came from top side and exited from right side etc.
My code is
function [hot, cold, h] = MuonTracks(tracks)%#eml
% NOTE: no variables larger than 1x1 are initialized
width = 1e-4;
height = 2e-4;
% constant used for Laplacian noise distribution
bL = 15 / sqrt(2);
% Loop through all tracks
X = [];
bhit= [];
hot = 0;
ii = 0;
TopBottom= 0;
LeftRight= 0;
TopRight= 0;
TopLeft= 0;
LeftBottom= 0;
RightBottom= 0;
ihit= 0;
while ii <= tracks
ii = ii + 1;
% Note that I've inlined (== copy-pasted) the original laprnd()
% function call. This was necessary to work around limitations
% in loops in Matlab, and prevent the nececessity of those HUGE
% variables.
%
% Of course, you can still easily generalize all of this:
% the new data
u = rand-0.5;
Ystart = 15;
Xstart = 80*rand-40;
Xend = Xstart - bL*sign(u)*log(1-2*abs(u));
%Xend=laprnd(tracks,1,Xstart,15);
b = (Ystart*Xend)/(Xend-Xstart);
% the test
if ((b < height && b > 0)) ||...
(Xend < width/2 && Xend > -width/2)
hot = hot+1;
% growing an array is perfectly fine when the chances of it
% happening are so slim
X = [X [Xstart; Xend]]; %#ok
bhit=b;
end
end
% This is trivial to do here, and prevents an 'else'
cold = tracks - hot;
% Now plot the chosen ones
h = figure;
hold all
%Y = bsxfun(#times, 15, ones(size(X)));
if (size(X)==0)
%Disp('No hits were found');
msgbox('No tracks were found','Result','warn');
elseif (size(X)>1)
Y = bsxfun(#times, [15; 0], ones(size(X)));
plot(X, Y, 'r');
msgbox( [num2str(hot) ' tracks were found'],'Result','help',num2str(hot));
else
Y = bsxfun(#times, [15; 0], ones(size(X)));
plot(X, Y, 'r');
msgbox( [num2str(hot) ' track was found'],'Result','help',num2str(hot));
end
%X;
%Y;
%size(X,2)
while ihit<size(X,2)
ihit=ihit+1
%X(2,ihit)
if ((X(2,ihit)==-width && (bhit<=0 && bhit<=height))&&(bhit==0 && (X(2,ihit)>=-width && X(2,ihit)>=width)))
LeftBottom=LeftBottom+1;
elseif ((bhit==height && (X(2,ihit)>=-width && X(2,ihit)>=width)) && (X(2,ihit)==width && (bhit<=0 && bhit<=height)))
TopRight=TopRight+1;
elseif ((bhit==height && (X(2,ihit)>=-width && X(2,ihit)>=width)) && (bhit==0 && (X(2,ihit)>=-width && X(2,ihit)>=width)))
TopBottom=TopBottom+1;
elseif ((X(2,ihit)==-width && (bhit<=0 && bhit<=height)) && (X(2,ihit)==width && (bhit<=0 && bhit<=height)))
LeftRight=LeftRight+1;
elseif ((X(2,ihit)==-width && (bhit<=0 && bhit<=height)) && (bhit==height && (X(2,ihit)>=-width && X(2,ihit)>=width)))
TopLeft=TopLeft+1;
elseif ((X(2,ihit)==width && (bhit<=0 && bhit<=height)) && (bhit==0 && (X(2,ihit)>=-width && X(2,ihit)>=width)))
RightBottom=RightBottom+1;
else
display('sth is wrong');
end
X(2,ihit)
end
%X(1,:);
%Y(1,:);
LeftBottom
TopRight
TopBottom
LeftRight
TopLeft
RightBottom
%display('sdfghjk');
end
Any ideas would be more than welcome!
Here you have a function that is capable of intersecting whole groups of segments and returning the intersection points. I hope it helps.
This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
How to concatenate a number to a variable name in MATLAB?
It must be easy but I just cannot find it in help!
I am operating with a vector x for 10 loops (for example) and at the end I want to concatenate all the results in a matrix 10by10. In order to do that I have to name them x1,x2,x3 etc. how can I do this?
Edit: A portion of my code thus far (copied from comments):
x = [0,0,0,1,0,0,1,0];
for k = 1:50
if x(1,8) ==1 && x(1,1)==1 && x(1,2)==1
x(1,1)=0;
elseif x(1,8) ==1 && x(1,1)==1 && x(1,2)==0
x(1,1)=0;
elseif x(1,8) ==1 && x(1,1)==0 && x(1,2)==1
x(1,1)=0;
elseif x(1,8) ==1 && x(1,1)==0 && x(1,2)==0
x(1,1)=1;
elseif x(1,8) ==0 && x(1,1)==1 && x(1,2)==1
x(1,1)=1;
elseif x(1,8) ==0 && x(1,1)==1 && x(1,2)==0
x(1,1)=1;
elseif x(1,8) ==0 && x(1,1)==0 && x(1,2)==1
x(1,1)=1;
end
...etc...
disp(x)
You should preallocate a matrix before your loop, and in the loop you just insert the vectors directly in the columns (or rows). Like:
A= zeros(10, 10);
for k in 1: 10
A(:, k)= %# result of your processing
end