Related
I have an assignment to fill the difference between two overlapping circles with a 4-way recursive Flood_Fill algorithm in MATLAB. I pick the starting point with a mouse. I created a function for it.
function [] = Flood_Fill(M,x,y,new_color,old_color,border_color)
if(x < 0 || y < 0 || x >= 60 || y >= 60 || M(x,y) ~= old_color) % M is a 60x60 array
return
else
M(x,y) = new_color;
pause(0.16);
image(M');
Flood_Fill(M,x+1,y,new_color,old_color,border_color);
Flood_Fill(M,x-1,y,new_color,old_color,border_color);
Flood_Fill(M,x,y+1,new_color,old_color,border_color);
Flood_Fill(M,x,y-1,new_color,old_color,border_color);
end
disp("looped");
end
I call this function in my program and it does work, partially, it starts to fill the circle. But when it detects a corner it doesn't fill it, leaving a non-coloured pixel. It also "forgets" that it ever drew over pixels when it "returns". For example, here it goes into an infinite loop because it draws a line, meets 3 adjacent already drawn pixels, returns, and does it again (the empty space on the bottom right of the brightly coloured patch):
Flood fill algorithm entered a loop
As far as I can tell my algorithm seems ok, but I do not know why it forgets that it has set M(x,y) = new_color. I also do not see how the program will ever complete even if it has filled the circle, once it "returns" it forgets the drawn pixels so it's bound to get stuck. I am really at a loss, I'm not really familiar with MATLAB.
An 8-way algorithm works better, but it means I have to draw the border of the circles to be at least 2 pixels, so that I don't overlap it. I tried to increase my search region, for example, looping x+n (where n = 1,2,3...) in all the recursive calls, obviously that gave off an error...
I tried to add some "edge cases" where it would jump a row or fill a pixel next to the border, but as I stated it forgets that ever happened. It also looked horrible code-wise.
In all the material for flood fill on the internet, it just seems to fill the space. I'm stuck with trying to do it with a recursive algorithm. Could MATLAB be the issue?
Everything else works fine, it's just the flood fill that is not working correctly.
Additionally: To better explain. I tried debugging it, it seems that when the return condition is met, it forgets the last drawn pixel and puts it in the old_color. It does this even with adding memory to the function (x_prev,y_prev arguments) and setting it to draw the M(x_prev,y_prev) = new_color. It acts as if it is searching for a path between two points in a maze, when it goes in a wrong direction it goes back and starts in another direction
function M = Zadacha_4_Fill_Recursion(M,x,y,new_color,old_color,border_color,x_prev,y_prev)
if(x < 0 || y < 0 || x >= 60 || y >= 60 || M(x,y) ~= old_color )
fprintf("return cond: x = %d, y = %d\n", x,y);
if(M(x,y) ~= border_color)
fprintf("erase_not: x = %d, y = %d | x_prev = %d, y_prev = %d\n", x,y,x_prev,y_prev);
M(x_prev,y_prev) = new_color;
x_prev = x;
y_prev = y;
end
pause(1.2);
image(M');
return
else
M(x,y) = new_color;
x_prev = x;
y_prev = y;
pause(0.3);
image(M');
fprintf("normal: x = %d, y = %d | x_prev = %d, y_prev = %d\n", x,y,x_prev,y_prev);
Zadacha_4_Fill_Recursion(M,x+1,y,new_color,old_color,border_color,x_prev,y_prev);
Zadacha_4_Fill_Recursion(M,x-1,y,new_color,old_color,border_color,x_prev,y_prev);
Zadacha_4_Fill_Recursion(M,x,y+1,new_color,old_color,border_color,x_prev,y_prev);
Zadacha_4_Fill_Recursion(M,x,y-1,new_color,old_color,border_color,x_prev,y_prev);
end
%disp("looped");
end ```
In MATLAB you pass arguments by value, when you change M in your function, you change a copy of it. You need to return the modified M for your code to work:
function M = Flood_Fill(M,x,y,new_color,old_color,border_color)
% ^^^
if(x < 0 || y < 0 || x >= 60 || y >= 60 || M(x,y) ~= old_color) % M is a 60x60 array
return
else
M(x,y) = new_color;
%pause(0.16);
%image(M');
M = Flood_Fill(M,x+1,y,new_color,old_color,border_color);
M = Flood_Fill(M,x-1,y,new_color,old_color,border_color);
M = Flood_Fill(M,x,y+1,new_color,old_color,border_color);
M = Flood_Fill(M,x,y-1,new_color,old_color,border_color);
% ^^^
end
%disp("looped");
end
Demo data
Data set can be found here :
Dataset
Each point (x;y) indicated the value (y) of the xth sample measured
I am trying to bound a data region which has the most data points just like in the figure , by using 2 line y=a and y=b
How can i approach this solution
Let's say your minimum and maximum for y values are respectively 960 and 972:
y(y < 960) = 960;
y(y > 972) = 972;
Alternatively, you can remove those outliers instead of bounding them:
y_idx = find((y < 960) | (y > 972));
x(y_idx) = [];
y(y_idx) = [];
I implemented the following user defined function in MATLAB:
function Q = Calc_Q(Head, freq)
b6 = [3.7572E-07 -1.5707E-05 6.0490E-03 5.0018E-02 2.1180E-01];
b5 = [-9.0927E-06 8.9033E-04 -3.2415E-02 5.4525E-01 -8.1649E+00] / 10e2;
b4 = [7.5172E-06 -5.6565E-04 1.0024E-02 3.5888E-01 3.8894E-02] / 10e5;
b3 = [-4.8767E-06 4.8787E-04 -1.3311E-02 -1.2189E-01 -5.3522E+00] / 10e8;
b2 = [5.9227E-06 -8.1716E-04 3.5392E-02 -4.5413E-01 1.9547E+00] / 10e11;
b1 = [-2.0004E-06 2.9027E-04 -1.3754E-02 2.3490E-01 -1.2363E+00] / 10e14;
a = [polyval(b1,abs(freq)), polyval(b2, abs(freq)), polyval(b3, abs(freq)), polyval(b4, abs(freq)), polyval(b5, abs(freq)), polyval(b6, abs(freq)) - Head];
Q_roots = roots(a);
%Delete roots with imaginary part
i = 1;
while i <= length(Q_roots)
if(imag(Q_roots(i)) ~= 0)
Q_roots(i) = [];
i = i - 1;
end
i = i + 1;
end
%Delete roots with real part greater then 3100
i = 1;
while i <= length(Q_roots)
if(Q_roots(i) >= 3100 || Q_roots(i) < 0)
Q_roots(i) = [];
i = i - 1;
end
i = i +1;
end
if freq < 0
Q = real(Q_roots(1)) * -1;
else
Q = real(Q_roots(1));
end
end
When I Call this function in Matlab it works fine. However if I use this exact code as a MATLAB function in simulink it stop's working. (actually it works, but the ouput is always zero.)
I do have a suspicion of what the problem might be. When running the script in de-bugging mode, I cannot view a result for Q_roots (It just doesn't display anything).
Q_roots = roots(a);
Any ideas ?
The problem is most likely due to your logic that eliminates any roots that do not have exactly zero in the imaginary part. This is a mathematical way of thinking that does not really work well numerically, at least not in general. All the roots are probably being found in both cases (there is no limitation that implies otherwise), but in Simulink and in code generation the problem is treated as a complex one, and some roots might be coming back with tiny imaginary parts. Instead of deleting roots if their imaginary parts are not exactly zero, eliminate the roots with imaginary parts that are numerically insignificant, either very small relative to the real part or very small altogether. Something like
tol = 10*eps(class(Q_roots));
keepers = abs(imag(Q_roots)) < tol*max(abs(real(Q_roots)),1) & ...
real(Q_roots) >= 0 & real(Q_roots) <= 3100;
Q_roots = Q_roots(keepers);
would take care of all the deletions in one fell swoop. I used 10*eps as a tolerance here.
But if you only need the first qualifying root, then you could just do this:
Q = nan('like',a);
tol = 10*eps(class(a));
for k = 1:numel(Q_roots)
r = real(Q_roots(k));
if abs(imag(Q_roots(k))) < tol*max(abs(r),1) && r >= 0 && r <= 3100;
Q = r;
break
end
end
if freq < 0
Q = -Q;
end
Oke I found the problem.
From a different forum:
Hi Cosmin,
I took a look at the implementation of roots for the Embedded MATLAB
Function block (\toolbox\eml\lib\matlab\polyfun\roots.m).
It's stated there:
% Limitations: % Output is always variable size. % Output is
always complex. % Roots may not be in the same order as MATLAB. %
Roots of poorly conditioned polynomials may not match MATLAB. The last
sentence is what makes you the headache (and yes, your polynomial is
badly conditioned!). If you take a look at the plot you will see, that
the curve hardly touches the x-axis.
I have a suggestion though: the value -z/b is a (very) good
approximation of the root you are looking for ...?
Titus
http://www.mathworks.com/matlabcentral/answers/25624-roots-in-simulink
Apparently the root function in simulink does not always found all the roots of a given polynominal.
This is unfortunate and not easily solvable. I did however found a solution.
For all the different polynomials I have to solve, I know the interval of the root I am interested in ( [-3000, 3000]).
I just basically takes steps of 50 from -3000 to 3000, until the function drops below 0. I then know the approximate solution of the root. I use this approximation as seed for the Newton-Raphson method.
Straight implementation of the Newton raphson method with a given seed for all the polynomials I have to solve did not work because sometimes it iterated to a different root (one which i was not interested in.)
Here's the code:
function Q = Calc_Q(Head, freq)
b6 = [3.7572E-07 -1.5707E-05 6.0490E-03 5.0018E-02 2.1180E-01];
b5 = [-9.0927E-06 8.9033E-04 -3.2415E-02 5.4525E-01 -8.1649E+00] / 10e2;
b4 = [7.5172E-06 -5.6565E-04 1.0024E-02 3.5888E-01 3.8894E-02] / 10e5;
b3 = [-4.8767E-06 4.8787E-04 -1.3311E-02 -1.2189E-01 -5.3522E+00] / 10e8;
b2 = [5.9227E-06 -8.1716E-04 3.5392E-02 -4.5413E-01 1.9547E+00] / 10e11;
b1 = [-2.0004E-06 2.9027E-04 -1.3754E-02 2.3490E-01 -1.2363E+00] / 10e14;
%coeff for the polynominal
a = [polyval(b1,abs(freq)), polyval(b2, abs(freq)), polyval(b3, abs(freq)), polyval(b4, abs(freq)), polyval(b5, abs(freq)), polyval(b6, abs(freq)) - Head];
%coeff for the derrivative of polynominal
da = [5*a(1) 4*a(2) 3*a(3) 2*a(4) a(5)];
Q = -3000;
%Search for point where function goes below 0
while (polyval(a, Q) > 0)
Q = Q + 25;
end
error_max = 0.01
iter_counter = 1;
while abs(polyval(a,Q)) >= error_max && iter_counter <= 1000
Q = Q - polyval(a, Q)/polyval(da, Q);
iter_counter = iter_counter + 1;
error = abs(polyval(a,Q));
end
if(freq < 0)
Q = Q * - 1;
end
end
I need to filter an image using a bank of filters in Matlab. My first attempt was to use a simple for loop to repeatedly call the "imfilter" function for each filter in the bank.
I will need to repeat this process many times for my application, so I need to this step to be as efficient as possible. Therefore, I was wondering if there was any way this operation could be vectorized to speed up the process. In an effort to simplify things, all of my filter kernels are the same size (9x9).
As an example of what I am going for, my filters are set up in a 9x9x32 element block, which needs to be applied to my image. I thought about replicating the image into a block (e.g. 100x100x32), but I'm not sure if there's a way to apply an operation like convolution without resorting to loops. Does anyone have suggestions for a good way of tackling this problem?
Other than pre allocating the space, there is not a faster way to arrive at an exact solution. If approximations are ok, then you might be able to decompose the 32 filters into a set of linear combinations of a smaller number of filters, say eight. See for instance Steerable filters.
http://people.csail.mit.edu/billf/papers/steerpaper91FreemanAdelson.pdf
edit: here is a tool to help apply filters to images.
function FiltIm = ApplyFilterBank(im,filters)
%#function FiltIm = ApplyFilterBank(im,filters)
%#
%#assume im is a single layer image, and filters is a cell array
nFilt = length(filters);
maxsz = 0;
for i = 1:nFilt
maxsz = max(maxsz,max(size(filters{i})));
end
FiltIm = zeros(size(im,1), size(im,2), nFilt);
im = padimage(im,maxsz,'symmetric');
for i = 1:nFilt
FiltIm(:,:,i) = unpadimage(imfilter(im,filters{i}),maxsz);
end
function o = padimage(i,amnt,method)
%#function o = padimage(i,amnt,method)
%#
%#padarray which operates on only the first 2 dimensions of a 3 dimensional
%#image. (of arbitrary number of layers);
%#
%#String values for METHOD
%# 'circular' Pads with circular repetion of elements.
%# 'replicate' Repeats border elements of A.
%# 'symmetric' Pads array with mirror reflections of itself.
%#
%#if(amnt) is length 1, then pad all sides same amount
%#
%#if(amnt) is length 2, then pad y direction amnt(1), and x direction amnt(2)
%#
%#if(amnt) is length 4, then pad sides unequally with order LTRB, left top right bottom
if(nargin < 3)
method = 'replicate';
end
if(length(amnt) == 1)
o = zeros(size(i,1) + 2 * amnt, size(i,2) + 2* amnt, size(i,3));
for n = 1:size(i,3)
o(:,:,n) = padarray(i(:,:,n),[amnt,amnt],method,'both');
end
end
if(length(amnt) == 2)
o = zeros(size(i,1) + 2 * amnt(1), size(i,2) + 2* amnt(2), size(i,3));
for n = 1:size(i,3)
o(:,:,n) = padarray(i(:,:,n),amnt,method,'both');
end
end
if(length(amnt) == 4)
o = zeros(size(i,1) + amnt(2) + amnt(4), size(i,2) + amnt(1) + amnt(3), size(i,3));
for n = 1:size(i,3)
o(:,:,n) = padarray(padarray(i(:,:,n),[amnt(2), amnt(1)],method,'pre'),[amnt(4), amnt(3)],method,'post');
end
end
function o = unpadimage(i,amnt)
%#un does padimage
%#if length(amnt == 1), unpad equal on each side
%#if length(amnt == 2), first amnt is left right, second up down
%#if length(amnt == 4), then [left top right bottom];
switch(length(amnt))
case 1
sx = size(i,2) - 2 * amnt;
sy = size(i,1) - 2 * amnt;
l = amnt + 1;
r = size(i,2) - amnt;
t = amnt + 1;
b = size(i,1) - amnt;
case 2
sx = size(i,2) - 2 * amnt(1);
sy = size(i,1) - 2 * amnt(2);
l = amnt(1) + 1;
r = size(i,2) - amnt(1);
t = amnt(2) + 1;
b = size(i,1) - amnt(2);
case 4
sx = size(i,2) - (amnt(1) + amnt(3));
sy = size(i,1) - (amnt(2) + amnt(4));
l = amnt(1) + 1;
r = size(i,2) - amnt(3);
t = amnt(2) + 1;
b = size(i,1) - amnt(4);
otherwise
error('illegal unpad amount\n');
end
if(any([sx,sy] < 1))
fprintf('unpadimage newsize < 0, returning []\n');
o = [];
return;
end
o = zeros(sy, sx, size(i,3));
for n = 1:size(i,3)
o(:,:,n) = i(t:b,l:r,n);
end
New Answer: Use colfilt() or block filtering style. Matlab can transform your image into large matrix where each distinct 9x9 pixel area is a single column (81 elements). Make it using im2col() method. If your image is N by M the result matrix would be 81 X (N-8)*(M-8).
Then you can concatenate all your filters to single matrix (each filter is a row) and multiply those huge matrices. This will give you the result of all filters. Now you have to reconstruct back 32 result images from the result matrix. use col2im() method.
For more information type 'doc colfilt'
This method works almost as fast as mex file and doesnt require any 'for' loop
Old answer:
Do you want to get different 32 results or single result for combination of filters?
If it is a sungle result than there is an easy way.
If you use linear filters (like convolutions) then apply filters one on another. Finally apply the resulting filter on the image. Thus image will be convolved only once.
If you filters are symmetric (x and y direction) then instead of applying the 9x9 filter apply 9x1 on y direction and 1x9 on x direction. Works a bit faster.
Finally, you can try using Mex file
I'm taking images using a tunneling microscope. However, the scope is drifting between successive images. I'm trying to use MatLab to calculate the offset between images. The code below calculates in seconds for small images (e.g. 64x64 pixels), but takes >2 hrs to handle the 512x512 pixel images I'm dealing with. Do you have any suggestions for speeding up this code? Or do you know of better ways to track images in MatLab? Thanks for your help!
%Test templates
template = .5*ones(32);
template(25:32,:) = 0;
template(:,25:64) = 0;
data_A = template;
close all
imshow(data_A);
template(9:32,41:64) = .5;
template(:,1:24) = 0;
data_B = template;
figure, imshow(data_B);
tic
[m n] = size(data_B);
z = [];
% Loop over all possible displacements
for x = -n:n
for y = -m:m
paddata_B = data_B;
ax = abs(x);
zerocols = zeros(m,ax);
if x > 0
paddata_B(:,1:ax) = [];
paddata_B = [paddata_B zerocols];
else
paddata_B(:,(n-ax+1):n) = [];
paddata_B = [zerocols paddata_B];
end
ay = abs(y);
zerorows = zeros(ay,n);
if y < 0
paddata_B(1:ay,:) = [];
paddata_B = vertcat(paddata_B, zerorows);
else
paddata_B((m-ay+1):m,:) = [];
paddata_B = vertcat(zerorows, paddata_B);
end
% Full matrix sum after array multiplication
C = paddata_B.*data_A;
matsum = sum(sum(C));
% Populate array of matrix sums for each displacement
z(x+n+1, y+m+1) = matsum;
end
end
toc
% Plot matrix sums
figure, surf(z), shading flat
% Find maximum value of z matrix
[max_z, imax] = max(abs(z(:)));
[xpeak, ypeak] = ind2sub(size(z),imax(1))
% Calculate displacement in pixels
corr_offset = [(xpeak-n-1) (ypeak-m-1)];
xoffset = corr_offset(1)
yoffset = corr_offset(2)
What you're calculating is known as the cross-correlation of the two images. You can calculate the cross-correlation of all offsets at once using Discrete Fourier Transforms (DFT or FFT). So try something like
z = ifft2( fft2(dataA) .* fft2(dataB).' );
If you pad with zeros in the Fourier domain, you can even use this sort of math to get offsets in fractions of a pixel, and apply offsets of fractions of a pixel to an image.
A typical approach to this kind of problem is to use the fact that it works quickly for small images to your advantage. When you have large images, decimate them to make small images. Register the small images quickly and use the computed offset as your initial value for the next iteration. In the next iteration, you don't decimate the images as much, but you're starting with a good initial estimate of the offset so you can constrain your search for solutions to a small neighborhood near your initial estimate.
Although not written with tunneling microscopes in mind, a review paper that may be of some assistance is: "Mutual Information-Based Registration of Medical Images: A Survey" by Pluim, Maintz, and Viergever published in IEEE Transactions on Medical Imaging, Vol. 22, No. 8, p. 986.
below link will help you find transformation between 2 images and correct/recover the distorted (in your case, image with offset)
http://in.mathworks.com/help/vision/ref/estimategeometrictransform.html
index_pairs = matchFeatures(featuresOriginal,featuresDistorted, 'unique', true);
matchedPtsOriginal = validPtsOriginal(index_pairs(:,1));
matchedPtsDistorted = validPtsDistorted(index_pairs(:,2));
[tform,inlierPtsDistorted,inlierPtsOriginal] = estimateGeometricTransform(matchedPtsDistorted,matchedPtsOriginal,'similarity');
figure; showMatchedFeatures(original,distorted,inlierPtsOriginal,inlierPtsDistorted);
The inlierPtsDistored, inlierPtsOriginal have attributes called locations.
These are nothing but matching locations of one image on another. I think from that point it is very easy to calculate offset.
The function below was my attempt to compute the cross-correlation of the two images manually. Something's not quite right though. Will look at it again this weekend if I have time. You can call the function with something like:
>> oldImage = rand(64);
>> newImage = circshift(oldImage, floor(64/2)*[1 1]);
>> offset = detectOffset(oldImage, newImage, 10)
offset =
32 -1
function offset = detectOffset(oldImage, newImage, margin)
if size(oldImage) ~= size(newImage)
offset = [];
error('Test images must be the same size.');
end
[imageHeight, imageWidth] = size(oldImage);
corr = zeros(2 * imageHeight - 1, 2 * imageWidth - 1);
for yIndex = [1:2*imageHeight-1; ...
imageHeight:-1:1 ones(1, imageHeight-1); ...
imageHeight*ones(1, imageHeight) imageHeight-1:-1:1];
oldImage = circshift(oldImage, [1 0]);
for xIndex = [1:2*imageWidth-1; ...
imageWidth:-1:1 ones(1, imageWidth-1); ...
imageWidth*ones(1, imageWidth) imageWidth-1:-1:1];
oldImage = circshift(oldImage, [0 1]);
numPoint = abs(yIndex(3) - yIndex(2) + 1) * abs(xIndex(3) - xIndex(2) + 1);
corr(yIndex(1),xIndex(1)) = sum(sum(oldImage(yIndex(2):yIndex(3),xIndex(2):xIndex(3)) .* newImage(yIndex(2):yIndex(3),xIndex(2):xIndex(3)))) * imageHeight * imageWidth / numPoint;
end
end
[value, yOffset] = max(corr(margin+1:end-margin,margin+1:end-margin));
[dummy, xOffset] = max(value);
offset = [yOffset(xOffset)+margin-imageHeight xOffset+margin-imageWidth];