Efficient Implementation of `im2col` and `col2im` - matlab

MATLAB's im2col and col2im are very important function for vectorization in MATLAB when dealing with images.
Yet they require MATLAB's Image Processing Toolbox.
My question is, is there an efficient (Vectorzied) way to implement the using MATLAB's functions (With no toolbox)?
I need both the sliding and distinct mode.
I don't need any padding.
Thank You.

I can only hope that Mathworks guys don't sue you or me or Stackoverflow for that matter, trying to create vectorized implementations of their IP toolbox functions, as they have put price on that toolbox. But in any case, forgetting those issues, here are the implementations.
Replacement for im2col with 'sliding' option
I wasn't able to vectorize this until I sat down to write solution to another problem on Stackoverflow. So, I would strongly encourage to look into it too.
function out = im2col_sliding(A,blocksize)
nrows = blocksize(1);
ncols = blocksize(2);
%// Get sizes for later usages
[m,n] = size(A);
%// Start indices for each block
start_ind = reshape(bsxfun(#plus,[1:m-nrows+1]',[0:n-ncols]*m),[],1); %//'
%// Row indices
lin_row = permute(bsxfun(#plus,start_ind,[0:nrows-1])',[1 3 2]); %//'
%// Get linear indices based on row and col indices and get desired output
out = A(reshape(bsxfun(#plus,lin_row,[0:ncols-1]*m),nrows*ncols,[]));
return;
Replacement for im2col with 'distinct' option
function out = im2col_distinct(A,blocksize)
nrows = blocksize(1);
ncols = blocksize(2);
nele = nrows*ncols;
row_ext = mod(size(A,1),nrows);
col_ext = mod(size(A,2),ncols);
padrowlen = (row_ext~=0)*(nrows - row_ext);
padcollen = (col_ext~=0)*(ncols - col_ext);
A1 = zeros(size(A,1)+padrowlen,size(A,2)+padcollen);
A1(1:size(A,1),1:size(A,2)) = A;
t1 = reshape(A1,nrows,size(A1,1)/nrows,[]);
t2 = reshape(permute(t1,[1 3 2]),size(t1,1)*size(t1,3),[]);
t3 = permute(reshape(t2,nele,size(t2,1)/nele,[]),[1 3 2]);
out = reshape(t3,nele,[]);
return;
Some quick tests show that both these implementations particularly sliding one for small to decent sized input data and distinct for all datasizes perform much better than the in-built MATLAB function implementations in terms of runtime performance.
How to use
With in-built MATLAB function -
B = im2col(A,[nrows ncols],'sliding')
With our custom function -
B = im2col_sliding(A,[nrows ncols])
%// ------------------------------------
With in-built MATLAB function -
B = im2col(A,[nrows ncols],'distinct')
With our custom function -
B = im2col_distinct(A,[nrows ncols])

You can cheat while looking to GNU Octave image package. There are im2col and col2im as script language implemented:
im2col
col2im
As far as I see, it differs most in different comment style (# instead of %) and different string style (" instead of '). If you change this and remove the assert test on the bottom, it might be runnable already. If not, go through it with the debugger.
Furthermore, be aware of the License (GPLv3). It's free, but your changes have to be free too!

Related

Vectorizing the solution of a linear equation system in MATLAB

Summary: This question deals with the improvement of an algorithm for the computation of linear regression.
I have a 3D (dlMAT) array representing monochrome photographs of the same scene taken at different exposure times (the vector IT) . Mathematically, every vector along the 3rd dimension of dlMAT represents a separate linear regression problem that needs to be solved. The equation whose coefficients need to be estimated is of the form:
DL = R*IT^P, where DL and IT are obtained experimentally and R and P must be estimated.
The above equation can be transformed into a simple linear model after applying a logarithm:
log(DL) = log(R) + P*log(IT) => y = a + b*x
Presented below is the most "naive" way to solve this system of equations, which essentially involves iterating over all "3rd dimension vectors" and fitting a polynomial of order 1 to (IT,DL(ind1,ind2,:):
%// Define some nominal values:
R = 0.3;
IT = 600:600:3000;
P = 0.97;
%// Impose some believable spatial variations:
pMAT = 0.01*randn(3)+P;
rMAT = 0.1*randn(3)+R;
%// Generate "fake" observation data:
dlMAT = bsxfun(#times,rMAT,bsxfun(#power,permute(IT,[3,1,2]),pMAT));
%// Regression:
sol = cell(size(rMAT)); %// preallocation
for ind1 = 1:size(dlMAT,1)
for ind2 = 1:size(dlMAT,2)
sol{ind1,ind2} = polyfit(log(IT(:)),log(squeeze(dlMAT(ind1,ind2,:))),1);
end
end
fittedP = cellfun(#(x)x(1),sol); %// Estimate of pMAT
fittedR = cellfun(#(x)exp(x(2)),sol); %// Estimate of rMAT
The above approach seems like a good candidate for vectorization, since it does not utilize MATLAB's main strength that is MATrix operations. For this reason, it does not scale very well and takes much longer to execute than I think it should.
There exist alternative ways to perform this computation based on matrix division, as demonstrated here and here, which involve something like this:
sol = [ones(size(x)),log(x)]\log(y);
That is, appending a vector of 1s to the observations, followed by mldivide to solve the equation system.
The main challenge I'm facing is how to adapt my data to the algorithm (or vice versa).
Question #1: How can the matrix-division-based solution be extended to solve the problem presented above (and potentially replace the loops I am using)?
Question #2 (bonus): What is the principle behind this matrix-division-based solution?
The secret ingredient behind the solution that includes matrix division is the Vandermonde matrix. The question discusses a linear problem (linear regression), and those can always be formulated as a matrix problem, which \ (mldivide) can solve in a mean-square error senseā€”. Such an algorithm, solving a similar problem, is demonstrated and explained in this answer.
Below is benchmarking code that compares the original solution with two alternatives suggested in chat1, 2 :
function regressionBenchmark(numEl)
clc
if nargin<1, numEl=10; end
%// Define some nominal values:
R = 5;
IT = 600:600:3000;
P = 0.97;
%// Impose some believable spatial variations:
pMAT = 0.01*randn(numEl)+P;
rMAT = 0.1*randn(numEl)+R;
%// Generate "fake" measurement data using the relation "DL = R*IT.^P"
dlMAT = bsxfun(#times,rMAT,bsxfun(#power,permute(IT,[3,1,2]),pMAT));
%% // Method1: loops + polyval
disp('-------------------------------Method 1: loops + polyval')
tic; [fR,fP] = method1(IT,dlMAT); toc;
fprintf(1,'Regression performance:\nR: %d\nP: %d\n',norm(fR-rMAT,1),norm(fP-pMAT,1));
%% // Method2: loops + Vandermonde
disp('-------------------------------Method 2: loops + Vandermonde')
tic; [fR,fP] = method2(IT,dlMAT); toc;
fprintf(1,'Regression performance:\nR: %d\nP: %d\n',norm(fR-rMAT,1),norm(fP-pMAT,1));
%% // Method3: vectorized Vandermonde
disp('-------------------------------Method 3: vectorized Vandermonde')
tic; [fR,fP] = method3(IT,dlMAT); toc;
fprintf(1,'Regression performance:\nR: %d\nP: %d\n',norm(fR-rMAT,1),norm(fP-pMAT,1));
function [fittedR,fittedP] = method1(IT,dlMAT)
sol = cell(size(dlMAT,1),size(dlMAT,2));
for ind1 = 1:size(dlMAT,1)
for ind2 = 1:size(dlMAT,2)
sol{ind1,ind2} = polyfit(log(IT(:)),log(squeeze(dlMAT(ind1,ind2,:))),1);
end
end
fittedR = cellfun(#(x)exp(x(2)),sol);
fittedP = cellfun(#(x)x(1),sol);
function [fittedR,fittedP] = method2(IT,dlMAT)
sol = cell(size(dlMAT,1),size(dlMAT,2));
for ind1 = 1:size(dlMAT,1)
for ind2 = 1:size(dlMAT,2)
sol{ind1,ind2} = flipud([ones(numel(IT),1) log(IT(:))]\log(squeeze(dlMAT(ind1,ind2,:)))).'; %'
end
end
fittedR = cellfun(#(x)exp(x(2)),sol);
fittedP = cellfun(#(x)x(1),sol);
function [fittedR,fittedP] = method3(IT,dlMAT)
N = 1; %// Degree of polynomial
VM = bsxfun(#power, log(IT(:)), 0:N); %// Vandermonde matrix
result = fliplr((VM\log(reshape(dlMAT,[],size(dlMAT,3)).')).');
%// Compressed version:
%// result = fliplr(([ones(numel(IT),1) log(IT(:))]\log(reshape(dlMAT,[],size(dlMAT,3)).')).');
fittedR = exp(real(reshape(result(:,2),size(dlMAT,1),size(dlMAT,2))));
fittedP = real(reshape(result(:,1),size(dlMAT,1),size(dlMAT,2)));
The reason why method 2 can be vectorized into method 3 is essentially that matrix multiplication can be separated by the columns of the second matrix. If A*B produces matrix X, then by definition A*B(:,n) gives X(:,n) for any n. Moving A to the right-hand side with mldivide, this means that the divisions A\X(:,n) can be done in one go for all n with A\X. The same holds for an overdetermined system (linear regression problem), in which there is no exact solution in general, and mldivide finds the matrix that minimizes the mean-square error. In this case too, the operations A\X(:,n) (method 2) can be done in one go for all n with A\X (method 3).
The implications of improving the algorithm when increasing the size of dlMAT can be seen below:
For the case of 500*500 (or 2.5E5) elements, the speedup from Method 1 to Method 3 is about x3500!
It is also interesting to observe the output of profile (here, for the case of 500*500):
Method 1
Method 2
Method 3
From the above it is seen that rearranging the elements via squeeze and flipud takes up about half (!) of the runtime of Method 2. It is also seen that some time is lost on the conversion of the solution from cells to matrices.
Since the 3rd solution avoids all of these pitfalls, as well as the loops altogether (which mostly means re-evaluation of the script on every iteration) - it unsurprisingly results in a considerable speedup.
Notes:
There was very little difference between the "compressed" and the "explicit" versions of Method 3 in favor of the "explicit" version. For this reason it was not included in the comparison.
A solution was attempted where the inputs to Method 3 were gpuArray-ed. This did not provide improved performance (and even somewhat degradaed them), possibly due to wrong implementation, or the overhead associated with copying matrices back and forth between RAM and VRAM.

Vectorize Evaluations of Meshgrid Points in Matlab

I need the "for" loop in the following representative section of code to run as efficiently as possible. The mean function in the code is acting as a representative placeholder for my own function.
x = linspace(-1,1,15);
y = linspace(2,4,15);
[xgrid, ygrid] = meshgrid(x,y);
mc = rand(100000,1);
z=zeros(size(xgrid));
for i=1:length(xgrid)
for j=1:length(ygrid)
z(i,j) = mean(xgrid(i,j) + ygrid(i,j) + xgrid(i,j)*ygrid(i,j)*mc);
end
end
I have vectorized the code and improved its speed by about 2.5 times by building a matrix in which mc is replicated for each grid point. My implementation results in a very large matrix (3 x 22500000) filled with repeated data. I've mitigated the memory penalty of this approach by converting the matrix to single precision, but it seems like there should be a more efficient way to do what I want that avoids replicating so much data.
You could use bsxfun with few reshapes -
A = bsxfun(#times,y,x.'); %//'
B = bsxfun(#plus,y,x.'); %//'
C = mean(bsxfun(#plus,bsxfun(#times,mc,reshape(A,1,[])) , reshape(B,1,[])),1);
z_out = reshape(C,numel(x),[]).';

matlab inline function with argument conditions

folks,
I am wondering if it is possible to write the following function of r as an inline function in matlab. I tried to include the condition as a separate factor such as *(r>a) and I got NaN due to the division of 1/r^3 when r is 0.
I fould a simple way out. It's basically what Shai and Jigg suggested, i.e. using an extra multiplicative factor of (r>a).
To get rid of NaN, we just need to add eps to the denominator of 1/r3, i.e.
1/(r+eps)^3 *(r>a)
First, you haven't stated what should actually happen if r = 0. Mathematically the term gets infinity. I assumed you rather want to set it to zero. And what should happen for r = a? Just another ill-defined case, are you sure your formula is correct?
If you have the Statistics Toolbox you can use nansum. If not, I'd say there is no way around to write your own function similar to nansum, which can't be done inline.
r = -5:1:5;
a = 1;
R = 42; %// rest of your function
%// not working, or removing of nan afterwards required
X = #( r ) (r>=a).*(a./r).^3*R;
%// inline solution with statistics toolbox
Y = #( r ) arrayfun(#(x) nansum( (x>=a)*(a/x)^3*R ), r);
output = [X(r)' Y(r)']
nansum is not vectorized, if you still want to use it for vectors wrap it into arrayfun.
The code of nansum does exactly what was suggested in the comments (output(isnan(output))=0), I'm probably not allowed to copy&paste it here. It filters out all NaN and then sums the input. Use open nansum to have insight.
As pointed out by Jigg, similar functions like nanmean would do the trick as well.
You can try
chi = 1; %// arbitrary value
a = 1; %// arbitrary value
theta = pi/3; %// arbitrary value
nu = #( r ) (r>a).*( (chi/3).*((a.^3)./(r.^3)).*(3*cos(theta).^2 -1);

Tabulated values's management in MATLAB

I have to build a function from tabulated values (two columns) which are written in a text file. The process to make it is the following:
Use the command importdata to read the data file
Xp = importdata('Xp.dat','\t',1);
Store each column in a variable
x = Xp(1:18304,1);
y = Xp(1:18304,2);
Make a curve fitting with both variables
ft = fittype('linearinterp');
datos.f_Xp = fit(x,y,ft);
However, when I am profiling the code I have found out that my bottleneck are the built-in functions fittype.fittype, fittype.evaluate, cfit.feval, ppval and cfit.subsref
which are related to the curve fitting. So I ask myself how I should manage the tabulated values for improving my code.
you're trying to fit 18304 data points to a curve. Also, you're using linearinterp... which means a routine is being run in a piecewise fashion. if you want to make the code faster use less datapoints.
Or perhaps try:
ft = fittype('poly1');
Not sure is it will be the answer you need as I don't have access to the data
May be "Eval" function could work in your case,
some simple example :
A = '1+4'; eval(A)
ans =
5
P = 'pwd'; eval(P)
ans =
/home/myname
and a bit more advanced!
for n = 1:12
eval(['M',int2str(n),' = magic(n)'])
end
Also, it has a sister name "feval"
guess, what does it do !
[V,D] = feval('eig',A)
[V,D] = eig(A)
and here
function plotf(fun,x)
y = feval(fun,x);
plot(x,y)
You are right ! all are equivalent,
check out here and find more relevant function

block processing with multiple input matrices

I'm working in matlab processing images for steganography. In my work so far I have been working with block processing command blockproc to break the image up into blocks to work on it. I'm now looking to start working with two image, the secret and the cover, but i can't find anyway to use blockproc with two input matrices instead of one.
Would anyone knowof a way to do this?
blockproc allows you to iterate over a single image only, but doesn't stop you from operating on whatever data you would like. The signature of the user function takes as input a "block struct", which contains not only the data field (which is used in all the blockproc examples) but also several other fields, one of which is "location". You can use this to determine "where you are" in your input image and to determine what other data you need to operate on that block.
for example, here's how you could do element-wise multiplication on 2 same-size images. This is a pretty clunky example but just here to demonstrate how this could look:
im1 = rand(100);
im2 = rand(100);
fun = #(bs) bs.data .* ...
im2(bs.location(1):bs.location(1)+9,bs.location(2):bs.location(2)+9);
im3 = blockproc(im1,[10 10],fun);
im4 = im1 .* im2;
isequal(im3,im4)
Using the "location" field of the block struct you can figure out the appropriate parts of a 2nd, 3rd, 4th, etc. data set you need for that particular block.
hope this helps!
-brendan
I was struggling with the same thing recently and solved it by combining both my input matrices into a single 3D matrix as follows. The commented out lines were my original code, prior to introducing block processing to it. The other problem I had was using variables other than the image matrix in the function: I had to do that part of the calculation first. If someone can simplify it please let me know!
%%LAB1 - L*a*b nearest neighbour classification
%distance_FG = ((A-FG_A).^2 + (B-FG_B).^2).^0.5;
%distance_BG = ((A-BG_A).^2 + (B-BG_B).^2).^0.5;
distAB = #(bs) ((bs.data(:,:,1)).^2 + (bs.data(:,:,2)).^2).^0.5;
AB = A - FG_A; AB(:,:,2) = B - FG_B;
distance_FG = blockproc(AB, [1000, 1000], distAB);
clear AB
AB = A - BG_A; AB(:,:,2) = B - BG_B;
distance_BG = blockproc(AB, [1000, 1000], distAB);
clear AB
I assume the solution to your problem lies in creating a new matrix that contains both input matrices.
e.g. A(:,:,1) = I1; A(:,:,2) = I2;
Now you can use blockproc on A.