How to plot a vector in matlab with another vector as a parameter? - matlab

I am trying to optimize the speed of a function I am writing, and trying to use vectors as much as I can. I am new to Matlab and vectorization is sometimes understandable to me, but I would like some additional help. Here is my current code:
For note, the oracle() function represents a randomly shaped object, and if you input a 1x2 matrix, it will return whether or not the matrx (or in our case, x- y-coordinates) is inside the random object.
Code In Image
function area = MitchellLitvinov_areaCalc(n)
% create random coordinate vectors, with bounds from (3, 14)
x = rand(n, 1) * 11 + 3;
y = rand(n, 1) * 11 + 3;
% find every point that is inside of oracle
inOracle = oracle([x y]);
% calculate the proportion, and multiply total area by proportion to find area
% of oracle
numPointsInOracle = nnz(inOracle);
area = numPointsInOracle/n * (11*11);
% create variable to store number of points in the area, and create a
% matrix with size [numPoints, 2] to hold x and y values
oracleCoordinates = zeros(numPointsInOracle, 2);
% HERE IS WHERE I NEED ASSISTANCE!!!!!!!!!
% find the points that are in the oracle shape
index = 0; % start index at 0 % is the index of our oracleCoordinates matrix
for i = 1:n % have to go through every point again to get their index
% if point is inside oracle, increase index and record
% coordinates
if (inOracle(i) == 1) % see if point is in oracle
index = index + 1;
oracleCoordinates(index, 1) = x(i, 1);
oracleCoordinates(index, 2) = y(i, 1);
end
end
% plot all points inside the oracle
scatter(oracleCoordinates(:,1), oracleCoordinates(:,2))
title("Oracle Shape")
xlim([3, 14]);
ylim([3, 14]);
end
Yes, even with near maximum memory usage, the code will run fairly quickly. But I want it to be fully vectorized simply for speed reasons, and if I need to repurpose this code for imaging. Currently, to calculate the area I am using vectors, but to actually reproduce an image, I need to create a separate storage matrix, and manually use indexing/appending to then transfer over the points inside the oracle function. I was wondering if there were any direct "shortcuts" to make my plotting a bit faster.

You can use an array as the index to select certain items from another array. For example, using your variable names:
oracleCoordinates(:,1) = x(inOracle == 1);
oracleCoordinates(:,2) = y(inOracle == 1);
This should give the same result as the code in your question, without using a loop.

Related

How to efficiently create a non-linear mask using two boundary arrays

I can make a non-linear mask based on the two arrays containing lower and higher boundary of the mask. All values in between need to be set to 1. The way I do this now seems to take quite a lot of time and it is becoming a bottleneck. I was wondering if there is a way to do it more time efficient.
First, I was thinking to solve it using parfors to increase the speed. But since this is one of the inner loops in my code these seem highly inefficient since it's more feasible using parfor on the outer loop considering schedule overhead. So parallel techniques are not an option.
See here the creation of the mask:
mask = zeros(size(im));
n = length(bufLow);
for i=1:1:n
mask(bufLow(i):bufHigh(i),i) = 1;
end
im is an matrix of a certain size and bufLow and bufHigh are arrays in size equal to the horizontal size of im describing the higher and lower boundaries for each column of im. In between these values everything needs to be set to 1.
So the goal is to have something that reduces the execution time of this loop as much as possible. I was wondering if there is somebody with some the knowledge to enlight me.
Bests,
Matthijs
I admit, that your question allows for some interpretation and guesswork, but from the code you provided, I have an idea, what you want to achieve: For the i-th column in your mask you want to set all pixels between a start index (that would be bufLow(i)) and an end index (bufHigh(i)) to 1. Is that correct?
So, my idea to "vectorize" your loop would be to translate the "per column" subscript (or array) indices in your bufxxx to "image" linear indices and then find all linear indices between the start and end indices. The latter is a (common) problem, which has already several significant answers, like this one from Divakar.
I incorporated his answer in my solution. Please, see the following code:
dim = 25;
bufLow = int32(10 * rand(1, dim) + 1);
bufHigh = int32(10 * rand(1, dim) + 15);
% Reference implementation from question
mask = zeros(dim);
n = length(bufLow);
for i=1:1:n
mask(bufLow(i):bufHigh(i), i) = 1;
end
% Show mask
figure(1);
imshow(mask);
% Implementation using Divakar's approach
% Translate subscript indices to linear indices
bufLow = bufLow + (dim .* (0:dim-1));
bufHigh = bufHigh + (dim .* (0:dim-1));
% Divakar's approach for finding all indices between two boundaries
lens = bufHigh - bufLow + 1;
shift_idx = cumsum(lens(1:end-1)) + 1;
id_arr = ones(1, sum(lens));
id_arr([1 shift_idx]) = [bufLow(1) bufLow(2:end) - bufHigh(1:end-1)];
out = cumsum(id_arr);
% Generating mask
mask2 = zeros(dim);
mask2(out) = 1;
% Show mask
figure(2);
imshow(mask2);
The resulting masks are identical and look like this:
To have a look on the performance, I set up a separate timing script using both approaches on increasing dimension dim from 25 to 2500 in steps of 25. The result looks like this:
Hope that helps!

How to Deal with Edge Cases: For Loops and Modulo

I'm trying to apply bare-bones image processing to images like this: My for-loop does exactly what I want it to: it allows me to find the pixels of highest intensity, and also remember the coordinates of that pixel. However, the code breaks whenever it encounters a multiple of rows – which in this case is equal to 18.
For example, the length of this image (rows * columns of image) is 414. So there are 414/18 = 23 cases where the program fails (i.e., the number of columns).
Perhaps there is a better way to accomplish my goal, but this is the only way I could think of sorting an image by pixel intensity while also knowing the coordinates of each pixel. Happy to take suggestions of alternative code, but it'd be great if someone had an idea of how to handle the cases where mod(x,18) = 0 (i.e., when the index of the vector is divisible by the total # of rows).
image = imread('test.tif'); % feed program an image
image_vector = image(:); % vectorize image
[sortMax,sortIndex] = sort(image_vector, 'descend'); % sort vector so
%that highest intensity pixels are at top
max_sort = [];
[rows,cols] = size(image);
for i=1:length(image_vector)
x = mod(sortIndex(i,1),rows); % retrieve original coordinates
% of pixels from matrix "image"
y = floor(sortIndex(i,1)/rows) +1;
if image(x,y) > 0.5 * max % filter out background noise
max_sort(i,:) = [x,y];
else
continue
end
end
You know that MATLAB indexing starts at 1, because you do +1 when you compute y. But you forgot to subtract 1 from the index first. Here is the correct computation:
index = sortIndex(i,1) - 1;
x = mod(index,rows) + 1;
y = floor(index/rows) + 1;
This computation is performed by the function ind2sub, which I recommend you use.
Edit: Actually, ind2sub does the equivalent of:
x = rem(sortIndex(i,1) - 1, rows) + 1;
y = (sortIndex(i,1) - x) / rows + 1;
(you can see this by typing edit ind2sub. rem and mod are the same for positive inputs, so x is computed identically. But for computing y they avoid the floor, I guess it is slightly more efficient.
Note also that
image(x,y)
is the same as
image(sortIndex(i,1))
That is, you can use the linear index directly to index into the two-dimensional array.

Retaining color when subsampling a triangulated surface: get indices from reducepatch?

I have a very densely tessellated surface which looks like this:
This surface is too densely tessellated for me, so I subsample it to get a coarser surface. To do this, I used Matlab's reducepatch function. This works pretty well:
Unfortunately, the coloring is based on a variable called sulcal_depth, which is defined for every vertex of my tessellated surface. So I need to retain sulcal depth information only from the vertices which remain after subsampling. Essentially, I need reducepatch to give me not just the subsampled version of the surface, but also the indices of vertex points that it retained. If I know the preserved indices, I can just index my sulcal_depth variable to get the new depth map.
Currently, I'm doing this as follows (this is also how I colored the subsampled version above):
function indices = compute_reduced_indices(before, after)
%% Function to compute the indices of vertices preserved during an operation of
% reducepatch. This allows you to use reducepatch to subsample a surface and
% re-compute an original signal on the vertices for the new subsampled mesh
indices = zeros(length(after), 1);
for i = 1:length(after)
dotprods = (before * after(i, :)') ./ sqrt(sum(before.^2, 2));
[~, indices(i)] = max(dotprods);
end
But as you might imagine, this is pretty slow, because of the for loop over vertices. I don't have enough memory to vectorize the loop and compute the full dot product matrix in one go.
Is there a smart way to get reducepatch to give me indices, or an alternative approach (with or without reducepatch) that's faster?
If reducepath only delete some vertex but doesn't change the coordinate of the preserved points you can use the function ismember:
%Load the "flow" matlab's dataset.
[x,y,z,v] = flow(100);
%Patch the isosurface
p = patch(isosurface(x,y,z,v,-3));
%Reducepatch
rp = reducepatch(p,0.15);
%Create an index of the preserved vertex.
[ind,loc] = ismember(p.Vertices,rp.vertices,'rows');
%Checksum
sum(find(ind) == sort(indices)) == length(indices) %should be = 1
%if you want to preserve the index order:
locb = loc(ind);
subind = find(ind);
[~,revsor] = sort(locb);
ind = subind(revsor);
BENCHMARKING
[x,y,z,v] = flow(100);
p = patch(isosurface(x,y,z,v,-3));
rp = reducepatch(p,0.15);
tic
ind = ismember(p.Vertices,rp.vertices,'rows');
toc
before = p.Vertices;
after = rp.vertices;
tic
indices = zeros(length(after), 1);
for i = 1:length(after)
dotprods = (before * after(i, :)') ./ sqrt(sum(before.^2, 2));
[~, indices(i)] = max(dotprods);
end
toc
RESULT
Elapsed time is 0.196078 seconds. %ismember solution
Elapsed time is 11.280293 seconds. %dotproduct solution

Plot a cell into a time-changing curve

I have got a cell, which is like this : Data={[2,3],[5,6],[1,4],[6,7]...}
The number in every square brackets represent x and y of a point respectively. There will be a new coordinate into the cell in every loop of my algorithm.
I want to plot these points into a time-changing curve, which will tell me the trajectory of the point.
As a beginner of MATLAB, I have no idea of this stage. Thanks for your help.
Here is some sample code to get you started. It uses some basic Matlab functionalities that you will hopefully find useful as you continue using it. I added come data points to you cell array for illustrative purposes.
The syntax to access elements into the cell array might seem weird but is important. Look here for details about cell array indexing.
In order to give nice colors to the points, I generated an array based on the jet colormap built-in in Matlab. Basically issuing the command
Colors = jet(N)
create a N x 3 matrix in which every row is a 3-element color ranging from blue to red. That way you can see which points were detected before other (i.e. blue before red). Of course you can change that to anything you want (look here if you're interested).
So here is the code. If something is unclear please ask for clarifications.
clear
clc
%// Get data
Data = {[2,3],[5,6],[1,4],[6,7],[8,1],[5,2],[7,7]};
%// Set up a matrix to color the points. Here I used a jet colormap
%// available from MATLAB but that could be anything.
Colors = jet(numel(Data));
figure;
%// Use "hold all" to prevent the content of the figure to be overwritten
%// at every iterations.
hold all
for k = 1:numel(Data)
%// Note the syntax used to access the content of the cell array.
scatter(Data{k}(1),Data{k}(2),60,Colors(k,:),'filled');
%// Trace a line to link consecutive points
if k > 1
line([Data{k-1}(1) Data{k}(1)],[Data{k-1}(2) Data{k}(2)],'LineStyle','--','Color','k');
end
end
%// Set up axis limits
axis([0 10 0 11])
%// Add labels to axis and add a title.
xlabel('X coordinates','FontSize',16)
ylabel('Y coordinates','FontSize',16)
title('This is a very boring title','FontSize',18)
Which outputs the following:
This would be easier to achieve if all of your data was stored in a n by 2 (or 2 by n) matrix. In this case, each row would be a new entry. For example:
Data=[2,3;
5,6;
1,4;
6,7];
plot(Data(:, 1), Data(:, 2))
Would plot your points. Fortunately, Matlab is able to handle matrices which grow on every iteration, though it is not recommended.
If you really wanted to work with cells, there are a couple of ways you could do it. Firstly, you could assign the elements to a matrix and repeat the above method:
NumPoints = numel(Data);
DataMat = zeros(NumPoints, 2);
for I = 1:NumPoints % Data is a cell here
DataMat(I, :) = cell2mat(Data(I));
end
You could alternatively plot the elements straight from the cell, though this would limit your plot options.
NumPoints = numel(Data);
hold on
for I = 1:NumPoints
point = cell2mat(Data(I));
plot(point(1), point(2))
end
hold off
With regards to your time changing curve, if you find that Matlab starts to slow down after it stores lots of points, it is possible to limit your viewing window in time with clever indexing. For example:
index = 1;
SamplingRate = 10; % How many times per second are we taking a sample (Hertz)?
WindowTime = 10; % How far into the past do we want to store points (seconds)?
NumPoints = SamplingRate * WindowTime
Data = zeros(NumPoints, 2);
while running
% Your code goes here
Data(index, :) = NewData;
index = index + 1;
index = mod(index-1, NumPoints)+1;
plot(Data(:, 1), Data(:, 2))
drawnow
end
Will store your data in a Matrix of fixed size, meaning Matlab won't slow down.

optimizing manually-coded k-means in MATLAB?

So I'm writing a k-means script in MATLAB, since the native function doesn't seem to be very efficient, and it seems to be fully operational. It appears to work on the small training set that I'm using (which is a 150x2 matrix fed via text file). However, the runtime is taking exponentially longer for my target data set, which is a 3924x19 matrix.
I'm not the greatest at vectorization, so any suggestions would be greatly appreciated. Here's my k-means script so far (I know I'm going to have to adjust my convergence condition, since it's looking for an exact match, and I'll probably need more iterations for a dataset this large, but I want it to be able to finish in a reasonable time first, before I crank that number up):
clear all;
%take input file (manually specified by user
disp('Please type input filename (in working directory): ')
target_file = input('filename: ', 's');
%parse and load into matrix
data = load(target_file);
%prompt name of output file for later) UNCOMMENT BELOW TWO LINES LATER
% disp('Please type output filename (to be saved in working directory): ')
% output_name = input('filename:', 's')
%prompt number of clusters
disp('Please type desired number of clusters: ')
c = input ('number of clusters: ');
%specify type of kmeans algorithm ('regular' for regular, 'fuzzy' for fuzzy)
%UNCOMMENT BELOW TWO LINES LATER
% disp('Please specify type (regular or fuzzy):')
% runtype = input('type: ', 's')
%initialize cluster centroid locations within bounds given by data set
%initialize rangemax and rangemin row vectors
%with length same as number of dimensions
rangemax = zeros(1,size(data,2));
rangemin = zeros(1,size(data,2));
%map max and min values for bounds
for dim = 1:size(data,2)
rangemax(dim) = max(data(:,dim));
rangemin(dim) = min(data(:,dim));
end
% rangemax
% rangemin
%randomly initialize mu_k (center) locations in (k x n) matrix where k is
%cluster number and n is number of dimensions/coordinates
mu_k = zeros(c,size(data,2));
for k = 1:size(data,2)
mu_k(k,:) = rangemin + (rangemax - rangemin).*rand(1,1);
end
mu_k
%iterate k-means
%initialize holding variable for distance comparison
comparisonmatrix = [];
%initialize assignment vector
assignment = zeros(size(data,1),1);
%initialize distance holding vector
dist = zeros(1,size(data,2));
%specify convergence threshold
%threshold = 0.001;
for iteration = 1:25
%save current assignment values to check convergence condition
hold_assignment = assignment;
for point = 1:size(data,1)
%calculate distances from point to centers
for k = 1:c
%holding variables
comparisonmatrix = [data(point,:);mu_k(k,:)];
dist(k) = pdist(comparisonmatrix);
end
%record location of mininum distance (location value will be between 1
%and k)
[minval, location] = min(dist);
%assign cluster number (analogous to location value)
assignment(point) = location;
end
%check convergence criteria
if isequal(assignment,hold_assignment)
break
end
%revise mu_k locations
%count number of each label
assignment_count = zeros(1,c);
for i = 1:size(data,1)
assignment_count(assignment(i)) = assignment_count(assignment(i)) + 1;
end
%compute centroids
point_total = zeros(size(mu_k));
for row = 1:size(data,1)
point_total(assignment(row),:) = point_total(assignment(row)) + data(row,:);
end
%move mu_k values to centroids
for center = 1:c
mu_k(center,:) = point_total(center,:)/assignment_count(center);
end
end
There are a lot of loops in there, so I feel that there's a lot of optimization to be made. However, I think I've just been staring at this code for far too long, so some fresh eyes could help. Please let me know if I need to clarify anything in the code block.
When the above code block is executed (in context) on the large dataset, it takes 3732.152 seconds, according to MATLAB's profiler, to make the full 25 iterations (I'm assuming it hasn't "converged" according to my criteria yet) for 150 clusters, but about 130 of them return NaNs (130 rows in mu_k).
Profiling will help, but the place to rework your code is to avoid the loop over the number of data points (for point = 1:size(data,1)). Vectorize that.
In your for iteration loop here is a quick partial example,
[nPoints,nDims] = size(data);
% Calculate all high-dimensional distances at once
kdiffs = bsxfun(#minus,data,permute(mu_k,[3 2 1])); % NxDx1 - 1xDxK => NxDxK
distances = sum(kdiffs.^2,2); % no need to do sqrt
distances = squeeze(distances); % Nx1xK => NxK
% Find closest cluster center for each point
[~,ik] = min(distances,[],2); % Nx1
% Calculate the new cluster centers (mean the data)
mu_k_new = zeros(c,nDims);
for i=1:c,
indk = ik==i;
clustersizes(i) = nnz(indk);
mu_k_new(i,:) = mean(data(indk,:))';
end
This isn't the only (or the best) way to do it, but it should be a decent example.
Some other comments:
Instead of using input, make this script into a function to efficiently handle input arguments.
If you want an easy way to specify a file, see uigetfile.
With many MATLAB functions, such as max, min, sum, mean, etc., you can specify a dimension over which the function should operate. This way you an run it on a matrix and compute values for multiple conditions/dimensions at the same time.
Once you get decent performance, consider iterating longer, specifically until the centers no longer change or the number of samples that change clusters becomes small.
The cluster with the smallest distance for each point, ik, will be the same with squared Euclidean distance.