I'm implementing some sort of flood fill algorithm in Matlab that, given a starting pixel in a binary image, will output a binary image containing only the pixels that can be directly connected to it.
Basically, let
foo =
1 1 1
1 0 0
1 0 1
calling flood_fill(foo,1,1) would yield
1 1 1
1 0 0
1 0 0
Now, I'm pretty new to Matlab. I initially implemented flood_fill in a recursive style, but the pass-by-value behaviour of Matlab made working with large images very inefficient. To fix this, I reimplemented flood_fill like this
function [outImage] = flood_fill(inImage,start_x,start_y)
width = size(inImage,2);
height = size(inImage,1);
outImage = zeros(height,width);
points = [];
points = [points; [start_x start_y]];
while size(points,1)>0
point = points(1,:);
points = points(2:end,:);
y=point(2);
x=point(1);
outImage(y,x)=1;
if (y>1 && (outImage(y-1,x)==0) && (inImage(y-1,x)==1))
points = [points; [x y-1]];
end
if (y<height && (outImage(y+1,x)==0) && (inImage(y+1,x)==1))
points = [points; [x y+1]];
end
if (x>1 && (outImage(y,x-1)==0) && (inImage(y,x-1)==1))
points = [points; [x-1 y]];
end
if (x<width && (outImage(y,x+1)==0) && (inImage(y,x+1)==1))
points = [points; [x+1 y]];
end
end
end
Now, this works on small matrices/images but takes forever on large images as well. I suspect the reason why is the large amount of array resizes going on. Normally (in C++ for example), I'd use an unordered linked list and use it as a stack (remove from and insert at the head) to avoid costly array resizes.
Is there an equivalent to such a data structure in Matlab? If not, what's a Matlab idiom I could use? Perhaps a built-in function?
The function that you search for called bwselect:
foo=[1 1 1; 1 0 0; 1 0 1]
b=bwselect(foo,1, 1)
Note that you can define also fourth input n (like that: bwselect(foo,1,1,n)), that can have a value of 4 to specify 4-connected region, or 8 to specify 8-connected region.
Adiel answered your second question "Perhaps a built-in function?". As for the first part:
I'm not familiar with linked lists in MATLAB. However, you can speed up your function significantly by initializing the size of the points-matrix and don't change the size after that. Pre-initialization should always be done in MATLAB. If the function won't work with matrices of fixed size, I would always recommend you to try to rewrite the function.
For your specific case:
function [outImage] = flood_fill(inImage,start_x,start_y)
width = size(inImage,2);
height = size(inImage,1);
outImage = zeros(height,width);
points = zeros(nnz(inImage),2); % I take it this is the maximum size
points(1,:) = [start_x start_y];
k = 1; % Increment row number in points
while size(points,1)>0
k = k + 1;
y=points(k, 2);
x=points(k, 1);
I understand it you have programming skills in general, so I believe you should be able to adapt the remaining code to the new format. (I don't have time to go through it and rewrite it). I'm quite sure it will run much faster!
Related
I am trying to use the Metropolis Hastings algorithm with a random walk sampler to simulate samples from a function $$ in matlab, but something is wrong with my code. The proposal density is the uniform PDF on the ellipse 2s^2 + 3t^2 ≤ 1/4. Can I use the acceptance rejection method to sample from the proposal density?
N=5000;
alpha = #(x1,x2,y1,y2) (min(1,f(y1,y2)/f(x1,x2)));
X = zeros(2,N);
accept = false;
n = 0;
while n < 5000
accept = false;
while ~accept
s = 1-rand*(2);
t = 1-rand*(2);
val = 2*s^2 + 3*t^2;
% check acceptance
accept = val <= 1/4;
end
% and then draw uniformly distributed points checking that u< alpha?
u = rand();
c = u < alpha(X(1,i-1),X(2,i-1),X(1,i-1)+s,X(2,i-1)+t);
X(1,i) = c*s + X(1,i-1);
X(2,i) = c*t + X(2,i-1);
n = n+1;
end
figure;
plot(X(1,:), X(2,:), 'r+');
You may just want to use the native implementation of matlab mhsample.
Regarding your code, there are a few things missing:
- function alpha,
- loop variable i (it might be just n but it is not suited for indexing since it starts at zero).
And you should always allocate memory in matlab if you want to fill it dynamically, i.e. X in your case.
To expand on the suggestions by #max, the code appears to work if you change the i indices to n and replace
n = 0;
with
n = 2;
X(:,1) = [.1,.1];
It would probably be better to assign X(:,1) to random values within your accept region (using the same code you use later), and/or include a burn-in period.
Depending upon what you are going to do with this, it may also make things cleaner to evaluate the argument to sin in the f function to keep it within 0 to 2 pi (likely by shifting the value by 2 pi if it exceeds those bounds)
In the code below, I want to ensure that there is an even chance of theImageRand being equal to theImage or theImage2, but I realized that between 1 and 100 more numbers are equal to 2 mod 1 than 2 mod 0. So theImage is being chosen a disproportionate amount of the time.
This was the simplest idea that came to me, but maybe there is a function that can do this easier? I was also thinking that I could find a number that fits what I'm looking for and put that into randi(n).
xRand = randi(100);
if mod(xRand,2) == 1
theImageRand = theImage;
elseif mod(xRand,2) == 0
theImageRand = theImage2;
end
Please let me know if I can explain more clearly. Thanks in advance.
tl;dr
Your code does exactly what you want, but it can be simplified by using randi(2) and removing the calculation of mod. However, it is worth to address some more points...
mod(xRand,2) to reduce 1-100 to 0/1
For xRand between 1 and 100, the result of mod(xRand,2) will be distributed equally on 0 and 1 as you can see by executing the following code:
xRand = 1:100;
xMod = mod(xRand,2);
cnt1 = sum(xMod == 1) % results in 50
cnt0 = sum(xMod == 0) % results in 50 as well
Basically, your code works as expected because randi chooses uniformly distributed numbers from 1 to 100. Subsequently, mod reduces them into a binary representation which is still uniformly distributed since the mapping is done for equal bins.
Simplification using randi(2)
The whole process of generating these uniformly distributed binary numbers can be simplified by just generating a binary set from the beginning. To achieve this, you can use randi(2) which directly gives you 1 or 2 as rayryeng pointed out in his comment to the question.
That would give you the following code:
xRand = randi(2);
if xRand == 1
theImageRand = theImage;
elseif xRand == 2
theImageRand = theImage2;
end
Is it correct?
Now we have a look at the interesting part of this question: Is the result really uniformly distributed? To check that, we can run the code N times and then analyze how many times each image has been chosen. Therefore, we assign 1 to the first image and 2 to the second image and store the result in res. After the for-loop, we take the sum of the elements where they are 1 or 2.
N = 1000000;
theImage = 1;
theImage2 = 2;
res = zeros(N,1);
for n = 1:N
xRand = randi(2);
if xRand == 1
theImageRand = theImage;
elseif xRand == 2
theImageRand = theImage2;
end
% xRand = randi(100);
% if mod(xRand,2) == 1
% theImageRand = theImage;
% elseif mod(xRand,2) == 0
% theImageRand = theImage2;
% end
res(n) = theImageRand;
end
cnt1 = sum(res==1);
cnt2 = sum(res==2);
percentage1 = cnt1/N*100 % approximately 50
percentage2 = cnt2/N*100 % approximately 50 as well
As we can see, percentage1 and percentage2 are approximately 50 which means the two images get both chosen around 50% of the time. It can be misleading to count the difference between cnt1 and cnt2 because this number can be high if N is large. However, if we observe this difference for many realization, the overall mean will be approximately zero. Furthermore, we can observe that your code using mod(randi(100),2) gives a distribution of 50% as well. It is just not as efficient and straight-forward as the solution with randi(2), which performs approximately 15% faster on my machine using R2016a.
Bottomline: I would recommend to use randi(2) as proposed above since it is more intuitive and more efficient as well. The observed difference is attributed to the random process and equalizes itself with more realizations. It is important to consider the percentage and not the absolute difference of the two images.
You can generate a number between zero and two and then check if xRand equals zero or one, which has an even chance of happening; however larger numbers make the chance of getting one over the other vary more.
(I've said two because when declaring random values, always have one extra.)
I have different images and I would like to crop them and keep only what is different between both. Here is the code I have so far.
video = VideoReader('frames.avi', 'Tag', 'my reader object');
frameFirst = read(video,1);
frameSecond = read(video, video.NumberOfFrames-1 );
imshowpair (frameSecond,frameFirst);
pause();
Well, it's tough to give you a good answer without much more detail. I think I understand what you're trying to do, and this might get you moving in the right direction. This code iterates through each pixel of the image (each pixel contains a 1x3 vector of RGB data ranging from 0 to 1), by row and column. If the difference in any of the elements of the 1x3 RGB vector exceeds some threshold (in this case, set to 0.1), we make that whole pixel black (set it to [0 0 0]). Else, we lust make it whatever the last frame was. To filter out all but those pixels that are identical, set the thresh value to 0. It goes like this:
thresh = 0.1
for ii = 1:size(frameFirst, 1)
for jj = 1:size(frameFirst, 2)
pixDiff = frameFirst{ii, jj} - frameSecond{ii, jj}
if (pixDiff(1) > thresh || pixDiff(2) > thresh || pixDiff(3) > thresh)
outputFrame = frameSecond{ii, jj};
else
outputFrame = [0 0 0];
end
end
end
I hope this does what you're looking for. Good luck!
Edit 1: Ok, I understand what you are looking for now. You need to have the indices of the bottom-right and top-left. If you already have those, just do this: frameOut = frameIn(xStart:xStop, yStart, yStop. If you need to find those points, that's harder. Let me know and I'll help you work it out.
I have written the following piece of code:
M = [3 0 0; 0 2 0; 0 0 0.5] % mass matrix
i_vals = 1:1000:60e06; % values of k_12 from 1 to 600 million in steps of 1000
modes = zeros(3, length(i_vals));
for n=1:length(i_vals)
i = i_vals(n) % i is the value of k_12
K = [i+8e06 -i -2e06; -i i+2e06 -1e06; -2e06 -1e06 5e06]; % stiffness matrix
[V,L]=eig(K,M);
V(:,I)=V;
A = V(:, [1])
transpose(A)
modes(:, n) = A
end
loglog(i_vals, modes')
But the loop seems to go forever and I do now know what is wrong with it. The idea was to get the first column from matrix V, and see what happens to the 3 elements in this column when value of k_12 is changed.
I don't know how you make this run forever. To me it looks as if it won't run at all. This won't answer your question, but will hopefully help you on the way =)
What do you want to do with this line? V(:,I)=V; What is I? Was it supposed to be i? Btw, using i and j as variables in MATLAB is not recommended (however, if you don't use complex numbers in your field, you shouldn't care too much).
You have a loop that goes 60,000 times, with calculations of eigenvalues etc. That is bound to take time (although not forever, as you state it does). You should get the answer eventually (if only the rest of the code worked). The resolution of your plot would be more than accurate enough with 10,000 or even 100,000 steps at a time.
This part:
A = V(:, [1])
transpose(A)
modes(:, n) = A
could simply be written as:
modes(:,n) = V(:,1)';
assuming you want the transposed of A. transpose(A) does nothing in this context actually. You would have to do A = transpose(A) (or rather A = A') for it to work.
There are all kinds of problems with your code - some of which may contribute to your issue.
You are computing values of i on a linear scale, but ultimately will be plotting on a log scale. You are doing a huge amount of work towards the end, when there is nothing visible in the graph for your effort. Much better to use a log scale for i_vals:
i_vals = logspace(0, 7.778, 200); % to get 200 log spaced values from
% 1 to approx 60E6`
You are using a variable I that has not been defined (in the code snippet you provide). Depending on its size, you may find that V is growing...
You are using a variable name i - while that is legal, it overwrites a built in (sqrt(-1)) which I personally find troublesome.
Your transpose(A); line doesn't do anything (You would have to do A = transpose(A);).
You don't have ; after several lines - this is going to make Matlab want to print to the console. This will take a huge amount of resource. Suppress the output with ; after every statement.
EDIT the following program runs quickly:
M = [3 0 0.0;
0 2 0.0;
0 0 0.5]; % mass matrix
i_vals = logspace(0, 7.78, 200); % values of k_12 from 1 to 600 million in steps of 1000
modes = zeros(3, length(i_vals));
for n=1:length(i_vals)
i = i_vals(n); % i is the value of k_12
K = [i+8e06 -i -2e06; -i i+2e06 -1e06; -2e06 -1e06 5e06]; % stiffness matrix
[V,L]=eig(K,M);
modes(:, n) = V(:,1);
end
loglog(i_vals, modes')
Resulting graph:
If I didn't break anything (hard to know what you were doing with I), maybe this can be helpful.
I have a big square matrix of structs which represents creatures. Each creature can move left,up,right or down. I'm checking the neighbouring matrix positions for empty cells and do the following calculation for the new coordinates:
function res = Move2(worldDimension,row,col,left,up,right,down)
%In the following matrices, good moves are > 0.
%Directions pattern:
patternMatrix = [0 2 0;
1 5 3;
0 4 0];
%Possible border moves:
borderMatrix = [0 (row>1) 0;
(col>1) 1 (col<worldDimension);
0 (row<worldDimension) 0;];
%Possible neighbor moves:
neighborsMatrix = [0 (up==0) 0 ;
(left==0) 1 (right==0);
0 (down==0) 0;];
%Matrix of possible directions including neighbors and borders
possibleMovesMatrix = ((borderMatrix).*(neighborsMatrix)).*(patternMatrix);
%Vector of possible directions:
possibleMovesVector = sort(possibleMovesMatrix(possibleMovesMatrix(:) > 0));
%Random direction:
randomDirection = possibleMovesVector(randi(length(possibleMovesVector)));
directionCoordsVector = [[row (col-1)];[(row-1) col];[row (col+1)];[(row+1) col];[row col]];
res = [directionCoordsVector(randomDirection,1) directionCoordsVector(randomDirection,2)];
end
This function is kind of slow, when I run the profiler it tells me that:
borderMatrix = [0 (row>1) 0;
(col>1) 1 (col<worldDimension);
0 (row<worldDimension) 0;];
takes 36% of time and that:
randomDirection = possibleMove...
takes 15% of time. Is there a way I can accelerate the process?
Maybe I can take a different approach by taking from the main game board the free spots arround the coordinate of the creature immediately? If so, how do I take a submatrix if a creature is near the border of the board with out having to deal with off-boundary indexes?
Thanks,
Guy.
So you have an array of structs, and move around the structs inside the array? That seems extremely inefficient to me.
Also, the reason the borderMatrix-line takes so long is because you are constructing a possibly large array.
Here's a suggestion for handling moving creatures:
Store your creatures as a nCreatures-by-mProperties numeric array. It's much easier to apply functions on a column of an array that to crawl individual fields. For example creatures = [x,y,prop1,prop2];
Move your creatures one-by-one:
for iCreature = 1:nCreatures
currentPos = creatures(iCreature,1:2);
%# initialize array of allowed moves with border conditions
goodMoves = [currentPos(1) > 1, currentPos(2) > 1, currentPos(1) < maxX, currentPos(2) < maxY, true];
%# check for neighbors
if any(creatures(:,1) == currentPos(1) - 1 & creatures(:,2) == currentPos(2))
goodMoves(1) = false;
end
%# etc
%# identify remaining good moves
if any(goodMoves(1:4))
goodMoveIdx = find(goodMoves);
move = goodMoveIdx(randi(length(goodMoveIdx)));
else
move = 5; %# remain stationary
end
end
It is not really clear whether there are multiple highly dependant creatures, but otherwise this would be an efficient workflow:
Generate 1 random number per creature
Determine per creature how many possible moves it has
Use the corresponding random number to make a choice
If they are dependant, but not too much you can do this several times untill you find a feasible value or include the dependance in step 2.