How to vectorize this code in MATLAB?
If possible, I wish matrix B to be a sparse matrix.
%% Y is a matrix l*n
%% X is a matrix k*n
B = [];
for i=1:l
for j=1:n
temp1 = zeros(1,n*l);
temp1((i-1)*n+j) = -1;
temp2 = zeros(1,l*k);
temp2((i-1)*k+1:i*k) = (-Y(i,j)).*(X(:,j)');
B = [B;[temp1,temp2]];
end
end
I don't know how to vectorize this code, please help! Thanks!
Making use of bsxfun for masking and vectorized calculations of the elementwise multiplications, here's a vectorized approach -
%// Create left part of the output that is basically an identity matrix
parte1 = -eye(n*l);
%// Setup right part of output
parte2 = zeros(n*l,l*k);
%// Mask to set elements from the calculations of (-Y(i,j)).*(X(:,j)')
M = bsxfun(#eq,reshape(repmat(1:l,n,1),[],1),reshape(repmat(1:l,k,1),1,[]));
%// OR concisely : M = kron(eye(l),ones(n,k))==1
%// Perform vectorized calculations of (-Y(i,j)).*(X(:,j)') and set those
%// into second part at masked places
parte2(M) = -bsxfun(#times,permute(X,[2,1,3]),permute(Y,[2,3,1]));
%// Finally concatenate those parts for final output
out = [parte1,parte2];
Related
I have an 3d array U, and a 2d matrix A. I want to do the multiplication like following way. How can I vectorize my code? The loop is too slow, of course.
for j=1:N
for k=1:N
UU(:,j,k)=A*U(:,j,k);
end
end
Reshape U to 2D and perform the matrix-multiplication, thus reducing the first axis/dimension of U with A's last axis to give us a 2D array. Finally, reshape back to 3D for the final result, like so -
[m1,n1] = size(A);
[~,m2,n2] = size(U);
out = reshape(A*reshape(U,[n1,m2*n2]),[m1,m2,n2])
Depending on the size of your matrices, you might find that eliminating both loops chews up a lot of memory, and that removing just the loop over the columns is sufficient,
for k = 1:N
UU(:,:,k) = A*U(:,:,k);
end
I am implementing the partial derivative equations from the Horn & Schunck paper on optical flow. However, even for relative small images (320x568), it takes a frustratingly long time (~30-40 seconds) to complete. I assume this is due to the 320 x 568 = 181760 loop iterations, but I can't figure out a more efficient way to do this (short of a MEX file).
Is there some way to turn this into a more efficient MATLAB operation (a convolution perhaps)? I can figure out how to do this as a convolution for It but not Ix and Iy. I've also considered matrix shifting, but that only works for It as well, as far as I can figure out.
Has anyone else run into this issue and found a solution?
My code is below:
function [Ix, Iy, It] = getFlowParams(img1, img2)
% Make sure image dimensions match up
assert(size(img1, 1) == size(img2, 1) && size(img1, 2) == size(img2, 2), ...
'Images must be the same size');
assert(size(img1, 3) == 1, 'Images must be grayscale');
% Dimensions of original image
[rows, cols] = size(img1);
Ix = zeros(numel(img1), 1);
Iy = zeros(numel(img1), 1);
It = zeros(numel(img1), 1);
% Pad images to handle edge cases
img1 = padarray(img1, [1,1], 'post');
img2 = padarray(img2, [1,1], 'post');
% Concatenate i-th image with i-th + 1 image
imgs = cat(3, img1, img2);
% Calculate energy for each pixel
for i = 1 : rows
for j = 1 : cols
cube = imgs(i:i+1, j:j+1, :);
Ix(sub2ind([rows, cols], i, j)) = mean(mean(cube(:, 2, :) - cube(:, 1, :)));
Iy(sub2ind([rows, cols], i, j)) = mean(mean(cube(2, :, :) - cube(1, :, :)));
It(sub2ind([rows, cols], i, j)) = mean(mean(cube(:, :, 2) - cube(:, :, 1)));
end
end
2D convolution is the way to go here as also predicted in the question to replace those heavy mean/average calculations. Also, those iterative differentiations could be replaced by MATLAB's diff. Thus, incorporating all that, a vectorized implementation would be -
%// Pad images to handle edge cases
img1 = padarray(img1, [1,1], 'post');
img2 = padarray(img2, [1,1], 'post');
%// Store size parameters for later usage
[m,n] = size(img1);
%// Differentiation along dim-2 on input imgs for Ix calculations
df1 = diff(img1,[],2)
df2 = diff(img2,[],2)
%// 2D Convolution to simulate average calculations & reshape to col vector
Ixvals = (conv2(df1,ones(2,1),'same') + conv2(df2,ones(2,1),'same'))./4;
Ixout = reshape(Ixvals(1:m-1,:),[],1);
%// Differentiation along dim-1 on input imgs for Iy calculations
df1 = diff(img1,[],1)
df2 = diff(img2,[],1)
%// 2D Convolution to simulate average calculations & reshape to col vector
Iyvals = (conv2(df1,ones(1,2),'same') + conv2(df2,ones(1,2),'same'))./4
Iyout = reshape(Iyvals(:,1:n-1),[],1);
%// It just needs elementwise diffentiation between input imgs.
%// 2D convolution to simulate mean calculations & reshape to col vector
Itvals = conv2(img2-img1,ones(2,2),'same')./4
Itout = reshape(Itvals(1:m-1,1:n-1),[],1)
Benefits with such a vectorized implementation would be :
Memory efficiency : No more concatenation along the third dimension that would incur memory overhead. Again, performance wise it would be a benefit as we won't need to index into such heavy arrays.
The iterative differentiations inside the loopy codes are replaced by differentiation with diff, so this should be another improvement.
Those expensive average calculations are replaced by very fast convolution calculations and this should be the major improvement section.
A faster method, with improved results (in the cases I have observed) is the following:
function [Ix, Iy, It] = getFlowParams(imNew,imPrev)
gg = [0.2163, 0.5674, 0.2163];
f = imNew + imPrev;
Ix = f(:,[2:end end]) - f(:,[1 1:(end-1)]);
Ix = conv2(Ix,gg','same');
Iy = f([2:end end],:) - f([1 1:(end-1)],:);
Iy = conv2(Iy,gg ,'same');
It = 2*conv2(gg,gg,imNew - imPrev,'same');
This handles the boundary cases elegantly.
I made this as part of my optical flow toolbox, where you can easily view H&S, Lucas Kanade and more in realtime. In the toolbox, the function is called grad3D.m. You may also want to check out grad3Drec.m, in the same toolbox, which adds simple temporal blurring.
I have the following setup
matrix2D_1 = zeros(40,191);
matrix2D_2 = zeros(40,191);
matrix3D_1 = zeros(40,191,191);
for j = 1:40
for jw = 1:191
matrix2D_1(j,jw) = sum(squeeze(matrix3D_1(j,jw,:))'*matrix2D_2' );
end
end
so I want the sum of all products of the 3rd dimension of the 3D matrix with the elements of the of the first 2D matrix which is the matrix product in
squeeze(matrix3D_1(j,jw,:))'*matrix2D_2'
The sum of these results shall then be stored in the first 2D matrix.
As I have to run this in a large loop this takes the most time in my code. I cant get my head around it how to vectorize it in a more elegant way. Any faster solution would be higly appreciated....
Yup! Use matrix-multiplication and reshape magic -
M = size(matrix2D_1,2);
matrix2D_1 = reshape(sum(reshape(matrix3D_1,[],M)*matrix2D_2.',2),[],M)
Or sum and then do matrix-multiplication -
matrix2D_1 = reshape(reshape(matrix3D_1,[],M)*sum(matrix2D_2,1).',[],M)
I calculated the distance between an image A1 and different images like image1,image2,image3 and image4 based on its hierarchicalCentroid. dist_1_1{ii} contains 4 values. I want to find the minimum value present in the dist_1_1{ii}.But I shows the value 1 and also i want to show the image which gives minimum value. Please help me. Thanks in advance
%% demo
clc,clear all,close all
plotFlag = 1;
depth = 6;
alef1 = im2bw(imread('C1.bmp')); %% Binary image
vec1 = hierarchicalCentroid(alef1,depth,plotFlag);
% subplot(1,3,1);
A=[];
vec2=[];
dist_1_1=[];
for ii=1:4
A{ii} = imread(['image' num2str(ii) '.bmp']);
% subplot(1,3,2);
vec2{ii} = hierarchicalCentroid(A{ii},depth,plotFlag);
%subplot(1,3,3);
%vec3 = hierarchicalCentroid(tav,depth,plotFlag);
% vec4=hierarchicalCentroid(A,depth,plotFlag);
% vec5=hierarchicalCentroid(A,depth,plotFlag);
dist_1_1{ii} = sum((vec1 - vec2{ii}) .^ 2);
[~,I] = min(dist_1_1{ii});
figure;
subplot(1,2,1);imshow(alef1);
subplot(1,2,2);imshow(A{I});
end
Considering that your images are named such as image1.png, image2.png,...
first, read and store the images in a cell
for ii=1:n
A{ii} = imread(['image' num2str(ii) '.png']);
end
Then compute the similarity between the image A1 and other images:
ind = computeSimilarity(A1,A); % here you compute the similarity and
(of course you would need a for-loop.)
After you have stored the values in the ind vector:
ind = [0.76,1.96,2.96];
Then find the index of a minimum value and choose the image accordingly
[~,I] = min(ind);
figure;
subplot(1,2,1);imshow(A1);
subplot(1,2,2);imshow(A{I});
What should be corrected in your code:
First of all, avoid using cell when it is not necessary and define it correctly when you using it. You cannot define a cell array like A=[]. You should do it like this: A=cell(2,3). For instance, for storing the vector of the descriptors you do not need a cell, just store them as a matrix, as I did.
Second of all when posting your code here, remove the unnecessary parts such as commented plots and commands.
And then, try to modify your code as follows, I might made some mistake about the dimensions, but you can get the main idea.
and also remember that you do not need to check each distance inside the loop. Calculate the vectors first and then find the distances in one step, as I did.
depth = 6;
alef1 = im2bw(imread('C1.bmp'));
vec1 = hierarchicalCentroid(alef1,depth,0);
A=cell(1,4);
vMatrix=zeros(4,length(vec1));
for ii=1:4
A{1,ii} = imread(['image' num2str(ii) '.bmp']);
vecMatrix(ii,:) = hierarchicalCentroid(A{1,ii},depth,0);
end
dist = sum((repmat(vec1,4,1) - vMatrix) .^ 2,2);
[~,I] = min(dist);
I have an image of size as RGB uint8(576,720,3) where I want to classify each pixel to a set of colors. I have transformed using rgb2lab from RGB to LAB space, and then removed the L layer so it is now a double(576,720,2) consisting of AB.
Now, I want to classify this to some colors that I have trained on another image, and calculated their respective AB-representations as:
Cluster 1: -17.7903 -13.1170
Cluster 2: -30.1957 40.3520
Cluster 3: -4.4608 47.2543
Cluster 4: 46.3738 36.5225
Cluster 5: 43.3134 -17.6443
Cluster 6: -0.9003 1.4042
Cluster 7: 7.3884 11.5584
Now, in order to classify/label each pixel to a cluster 1-7, I currently do the following (pseudo-code):
clusters;
for each x
for each y
ab = im(x,y,2:3);
dist = norm(ab - clusters); // norm of dist between ab and each cluster
[~, idx] = min(dist);
end
end
However, this is terribly slow (52 seconds) because of the image resolution and that I manually loop through each x and y.
Are there some built-in functions I can use that performs the same job? There must be.
To summarize: I need a classification method that classifies pixel images to an already defined set of clusters.
Approach #1
For a N x 2 sized points/pixels array, you can avoid permute as suggested in the other solution by Luis, which could slow down things a bit, to have a kind of "permute-unrolled" version of it and also let's bsxfun work towards a 2D array instead of a 3D array, which must be better with performance.
Thus, assuming clusters to be ordered as a N x 2 sized array, you may try this other bsxfun based approach -
%// Get a's and b's
im_a = im(:,:,2);
im_b = im(:,:,3);
%// Get the minimum indices that correspond to the cluster IDs
[~,idx] = min(bsxfun(#minus,im_a(:),clusters(:,1).').^2 + ...
bsxfun(#minus,im_b(:),clusters(:,2).').^2,[],2);
idx = reshape(idx,size(im,1),[]);
Approach #2
You can try out another approach that leverages fast matrix multiplication in MATLAB and is based on this smart solution -
d = 2; %// dimension of the problem size
im23 = reshape(im(:,:,2:3),[],2);
numA = size(im23,1);
numB = size(clusters,1);
A_ext = zeros(numA,3*d);
B_ext = zeros(numB,3*d);
for id = 1:d
A_ext(:,3*id-2:3*id) = [ones(numA,1), -2*im23(:,id), im23(:,id).^2 ];
B_ext(:,3*id-2:3*id) = [clusters(:,id).^2 , clusters(:,id), ones(numB,1)];
end
[~, idx] = min(A_ext * B_ext',[],2); %//'
idx = reshape(idx, size(im,1),[]); %// Desired IDs
What’s going on with the matrix multiplication based distance matrix calculation?
Let us consider two matrices A and B between whom we want to calculate the distance matrix. For the sake of an easier explanation that follows next, let us consider A as 3 x 2 and B as 4 x 2 sized arrays, thus indicating that we are working with X-Y points. If we had A as N x 3 and B as M x 3 sized arrays, then those would be X-Y-Z points.
Now, if we have to manually calculate the first element of the square of distance matrix, it would look like this –
first_element = ( A(1,1) – B(1,1) )^2 + ( A(1,2) – B(1,2) )^2
which would be –
first_element = A(1,1)^2 + B(1,1)^2 -2*A(1,1)* B(1,1) + ...
A(1,2)^2 + B(1,2)^2 -2*A(1,2)* B(1,2) … Equation (1)
Now, according to our proposed matrix multiplication, if you check the output of A_ext and B_ext after the loop in the earlier code ends, they would look like the following –
So, if you perform matrix multiplication between A_ext and transpose of B_ext, the first element of the product would be the sum of elementwise multiplication between the first rows of A_ext and B_ext, i.e. sum of these –
The result would be identical to the result obtained from Equation (1) earlier. This would continue for all the elements of A against all the elements of B that are in the same column as in A. Thus, we would end up with the complete squared distance matrix. That’s all there is!!
Vectorized Variations
Vectorized variations of the matrix multiplication based distance matrix calculations are possible, though there weren't any big performance improvements seen with them. Two such variations are listed next.
Variation #1
[nA,dim] = size(A);
nB = size(B,1);
A_ext = ones(nA,dim*3);
A_ext(:,2:3:end) = -2*A;
A_ext(:,3:3:end) = A.^2;
B_ext = ones(nB,dim*3);
B_ext(:,1:3:end) = B.^2;
B_ext(:,2:3:end) = B;
distmat = A_ext * B_ext.';
Variation #2
[nA,dim] = size(A);
nB = size(B,1);
A_ext = [ones(nA*dim,1) -2*A(:) A(:).^2];
B_ext = [B(:).^2 B(:) ones(nB*dim,1)];
A_ext = reshape(permute(reshape(A_ext,nA,dim,[]),[1 3 2]),nA,[]);
B_ext = reshape(permute(reshape(B_ext,nB,dim,[]),[1 3 2]),nB,[]);
distmat = A_ext * B_ext.';
So, these could be considered as experimental versions too.
Use pdist2 (Statistics Toolbox) to compute the distances in a vectorized manner:
ab = im(:,:,2:3); % // get A, B components
ab = reshape(ab, [size(im,1)*size(im,2) 2]); % // reshape into 2-column
dist = pdist2(clusters, ab); % // compute distances
[~, idx] = min(dist); % // find minimizer for each pixel
idx = reshape(idx, size(im,1), size(im,2)); % // reshape result
If you don't have the Statistics Toolbox, you can replace the third line by
dist = squeeze(sum(bsxfun(#minus, clusters, permute(ab, [3 2 1])).^2, 2));
This gives squared distance instead of distance, but for the purposes of minimizing it doesn't matter.