I have an NxM matrix in MATLAB that I would like to reorder in similar fashion to the way JPEG reorders its subblock pixels:
(image from Wikipedia)
I would like the algorithm to be generic such that I can pass in a 2D matrix with any dimensions. I am a C++ programmer by trade and am very tempted to write an old school loop to accomplish this, but I suspect there is a better way to do it in MATLAB.
I'd be rather want an algorithm that worked on an NxN matrix and go from there.
Example:
1 2 3
4 5 6 --> 1 2 4 7 5 3 6 8 9
7 8 9
Consider the code:
M = randi(100, [3 4]); %# input matrix
ind = reshape(1:numel(M), size(M)); %# indices of elements
ind = fliplr( spdiags( fliplr(ind) ) ); %# get the anti-diagonals
ind(:,1:2:end) = flipud( ind(:,1:2:end) ); %# reverse order of odd columns
ind(ind==0) = []; %# keep non-zero indices
M(ind) %# get elements in zigzag order
An example with a 4x4 matrix:
» M
M =
17 35 26 96
12 59 51 55
50 23 70 14
96 76 90 15
» M(ind)
ans =
17 35 12 50 59 26 96 51 23 96 76 70 55 14 90 15
and an example with a non-square matrix:
M =
69 9 16 100
75 23 83 8
46 92 54 45
ans =
69 9 75 46 23 16 100 83 92 54 8 45
This approach is pretty fast:
X = randn(500,2000); %// example input matrix
[r, c] = size(X);
M = bsxfun(#plus, (1:r).', 0:c-1);
M = M + bsxfun(#times, (1:r).'/(r+c), (-1).^M);
[~, ind] = sort(M(:));
y = X(ind).'; %'// output row vector
Benchmarking
The following code compares running time with that of Amro's excellent answer, using timeit. It tests different combinations of matrix size (number of entries) and matrix shape (number of rows to number of columns ratio).
%// Amro's approach
function y = zigzag_Amro(M)
ind = reshape(1:numel(M), size(M));
ind = fliplr( spdiags( fliplr(ind) ) );
ind(:,1:2:end) = flipud( ind(:,1:2:end) );
ind(ind==0) = [];
y = M(ind);
%// Luis' approach
function y = zigzag_Luis(X)
[r, c] = size(X);
M = bsxfun(#plus, (1:r).', 0:c-1);
M = M + bsxfun(#times, (1:r).'/(r+c), (-1).^M);
[~, ind] = sort(M(:));
y = X(ind).';
%// Benchmarking code:
S = [10 30 100 300 1000 3000]; %// reference to generate matrix size
f = [1 1]; %// number of cols is S*f(1); number of rows is S*f(2)
%// f = [0.5 2]; %// plotted with '--'
%// f = [2 0.5]; %// plotted with ':'
t_Amro = NaN(size(S));
t_Luis = NaN(size(S));
for n = 1:numel(S)
X = rand(f(1)*S(n), f(2)*S(n));
f_Amro = #() zigzag_Amro(X);
f_Luis = #() zigzag_Luis(X);
t_Amro(n) = timeit(f_Amro);
t_Luis(n) = timeit(f_Luis);
end
loglog(S.^2*prod(f), t_Amro, '.b-');
hold on
loglog(S.^2*prod(f), t_Luis, '.r-');
xlabel('number of matrix entries')
ylabel('time')
The figure below has been obtained with Matlab R2014b on Windows 7 64 bits. Results in R2010b are very similar. It is seen that the new approach reduces running time by a factor between 2.5 (for small matrices) and 1.4 (for large matrices). Results are seen to be almost insensitive to matrix shape, given a total number of entries.
Here's a non-loop solution zig_zag.m. It looks ugly but it works!:
function [M,index] = zig_zag(M)
[r,c] = size(M);
checker = rem(hankel(1:r,r-1+(1:c)),2);
[rEven,cEven] = find(checker);
[cOdd,rOdd] = find(~checker.'); %'#
rTotal = [rEven; rOdd];
cTotal = [cEven; cOdd];
[junk,sortIndex] = sort(rTotal+cTotal);
rSort = rTotal(sortIndex);
cSort = cTotal(sortIndex);
index = sub2ind([r c],rSort,cSort);
M = M(index);
end
And a test matrix:
>> M = [magic(4) zeros(4,1)];
M =
16 2 3 13 0
5 11 10 8 0
9 7 6 12 0
4 14 15 1 0
>> newM = zig_zag(M) %# Zig-zag sampled elements
newM =
16
2
5
9
11
3
13
10
7
4
14
6
8
0
0
12
15
1
0
0
Here's a way how to do this. Basically, your array is a hankel matrix plus vectors of 1:m, where m is the number of elements in each diagonal. Maybe someone else has a neat idea on how to create the diagonal arrays that have to be added to the flipped hankel array without a loop.
I think this should be generalizeable to a non-square array.
% for a 3x3 array
n=3;
numElementsPerDiagonal = [1:n,n-1:-1:1];
hadaRC = cumsum([0,numElementsPerDiagonal(1:end-1)]);
array2add = fliplr(hankel(hadaRC(1:n),hadaRC(end-n+1:n)));
% loop through the hankel array and add numbers counting either up or down
% if they are even or odd
for d = 1:(2*n-1)
if floor(d/2)==d/2
% even, count down
array2add = array2add + diag(1:numElementsPerDiagonal(d),d-n);
else
% odd, count up
array2add = array2add + diag(numElementsPerDiagonal(d):-1:1,d-n);
end
end
% now flip to get the result
indexMatrix = fliplr(array2add)
result =
1 2 6
3 5 7
4 8 9
Afterward, you just call reshape(image(indexMatrix),[],1) to get the vector of reordered elements.
EDIT
Ok, from your comment it looks like you need to use sort like Marc suggested.
indexMatrixT = indexMatrix'; % ' SO formatting
[dummy,sortedIdx] = sort(indexMatrixT(:));
sortedIdx =
1 2 4 7 5 3 6 8 9
Note that you'd need to transpose your input matrix first before you index, because Matlab counts first down, then right.
Assuming X to be the input 2D matrix and that is square or landscape-shaped, this seems to be pretty efficient -
[m,n] = size(X);
nlim = m*n;
n = n+mod(n-m,2);
mask = bsxfun(#le,[1:m]',[n:-1:1]);
start_vec = m:m-1:m*(m-1)+1;
a = bsxfun(#plus,start_vec',[0:n-1]*m);
offset_startcol = 2- mod(m+1,2);
[~,idx] = min(mask,[],1);
idx = idx - 1;
idx(idx==0) = m;
end_ind = a([0:n-1]*m + idx);
offsets = a(1,offset_startcol:2:end) + end_ind(offset_startcol:2:end);
a(:,offset_startcol:2:end) = bsxfun(#minus,offsets,a(:,offset_startcol:2:end));
out = a(mask);
out2 = m*n+1 - out(end:-1:1+m*(n-m+1));
result = X([out2 ; out(out<=nlim)]);
Quick runtime tests against Luis's approach -
Datasize: 500 x 2000
------------------------------------- With Proposed Approach
Elapsed time is 0.037145 seconds.
------------------------------------- With Luis Approach
Elapsed time is 0.045900 seconds.
Datasize: 5000 x 20000
------------------------------------- With Proposed Approach
Elapsed time is 3.947325 seconds.
------------------------------------- With Luis Approach
Elapsed time is 6.370463 seconds.
Let's assume for a moment that you have a 2-D matrix that's the same size as your image specifying the correct index. Call this array idx; then the matlab commands to reorder your image would be
[~,I] = sort (idx(:)); %sort the 1D indices of the image into ascending order according to idx
reorderedim = im(I);
I don't see an obvious solution to generate idx without using for loops or recursion, but I'll think some more.
Related
I want to repeat a row vector to create a matrix, in which every row is a slightly modified version of the original vector.
For example, if I have a vector v = [10 20 30 40 50], I want every row of my matrix to be that vector, but with a random number added to every element of the vector to add some fluctuations.
My matrix should look like this:
M = [10+a 20+a 30+a 40+a 50+a;
10+b 20+b 30+b 40+b 50+b;
... ]
Where a, b, ... are random numbers between 0 and 2, for an arbitrary number of matrix rows.
Any ideas?
In Matlab, you can add a column vector to a matrix. This will add the vector elements to each of the row values accordingly.
Example:
>> M = [1 2 3; 4 5 6; 7 8 9];
>> v = [1; 2; 3];
>> v + M
ans =
2 3 4
6 7 8
10 11 12
Note that in your case v is a row vector, so you should transpose it first (using v.').
As Sardar Usama and Wolfie note, this method of adding is only possible since MATLAB version R2016b, for earlier versions you will need to use bsxfun:
>> % instead of `v + M`
>> bsxfun(#plus, v, M)
ans =
2 4 6
5 7 9
8 10 12
If you have a MATLAB version earlier than 2016b (when implicit expansion was introduced, as demonstrated in Daan's answer) then you should use bsxfun.
v = [10 20 30 40 50]; % initial row vector
offsets = rand(3,1); % random values, add one per row (this should be a column vector)
output = bsxfun(#plus,offsets,v);
Result:
>> output =
10.643 20.643 30.643 40.643 50.643
10.704 20.704 30.704 40.704 50.704
10.393 20.393 30.393 40.393 50.393
This can be more easily understood with less random inputs!
v = [10 20 30 40 50];
offsets = [1; 2; 3];
output = bsxfun(#plus,offsets,v);
>> output =
11 21 31 41 51
12 22 32 42 52
13 23 33 43 53
Side note: to get an nx1 vector of random numbers between 0 and 2, use
offsets = rand(n,1)*2
I have a 4x5 matrix called A from which I want to select randomly 3 rows, then 4 random columns and then select those elements which coincide in those selected rows and columns so that I have 12 selected elements.Then I want to create a diagonal matrix called B which will have entries either 1 or 0 so that multiplication of that B matrix with reshaped A matrix (20x1) will give me those selected 12 elements of A.
How can I create that B matrix? Here is my code:
A=1:20;
A=reshape(A,4,5);
Mr=4;
Ma=3;
Na=4;
Nr=5;
M=Ma*Mr;
[S1,S2]=size(A);
N=S1*S2;
y2=zeros(size(A));
k1=randperm(S1);
k1=k1(1:Ma);
k2=randperm(S2);
k2=k2(1:Mr);
y2(k1,k2)=A(k1,k2);
It's a little hard to understand what you want and your code isn't much help but I think I've a solution for you.
I create a matrix (vector) of zeros of the same size as A and then use bsxfun to determine the indexes in this vector (which will be the diagonal of B) that should be 1.
>> A = reshape(1:20, 4, 5);
>> R = [1 2 3]; % Random rows
>> C = [2 3 4 5]; % Random columns
>> B = zeros(size(A));
>> B(bsxfun(#plus, C, size(A, 1)*(R-1).')) = 1;
>> B = diag(B(:));
>> V = B*A(:);
>> V = V(V ~= 0)
V =
2
3
4
5
6
7
8
9
10
11
12
13
Note: There is no need for B = diag(B(:)); we could have simply used element by element multiplication in Matlab.
>> V = B(:).*A(:);
>> V = V(V ~= 0)
Note: This may be overly complex or very poorly put together and there is probably a better way of doing it. It's my first real attempt at using bsxfun on my own.
Here is a hack but since you are creating y2 you might as well just use it instead of creating the useless B matrix. The bsxfun answer is much better.
A=1:20;
A=reshape(A,4,5);
Mr=4;
Ma=3;
Na=4;
Nr=5;
M=Ma*Mr;
[S1,S2]=size(A);
N=S1*S2;
y2=zeros(size(A));
k1=randperm(S1);
k1=k1(1:Ma);
k2=randperm(S2);
k2=k2(1:Mr);
y2(k1,k2)=A(k1,k2);
idx = reshape(y2 ~= 0, numel(y2), []);
B = diag(idx);
% "diagonal" matrix 12x20
B = B(all(B==0,2),:) = [];
output = B * A(:)
output =
1
3
4
9
11
12
13
15
16
17
19
20
y2 from example.
y2 =
1 0 9 13 17
0 0 0 0 0
3 0 11 15 19
4 0 12 16 20
I work with an image that I consider as a matrix.
I want to turn a 800 x 800 matrix (A) into a 400 x 400 matrix (B) where the mean of 4 cells of the A matrix = 1 cell of the B matrix (I know this not a right code line) :
B[1,1] =mean2(A[1,1 + 1,2 + 2,1 + 2,2])
and so on for the whole matrix ...
B [1,2]=mean2(A[1,3 + 1,4 + 2,3 + 2,4 ])
I thought to :
1) Reshape the A matrix into a 2 x 320 000 matrix so I get the four cells I need to average next to each other and it is easier to deal with the row number afterwards.
Im4bis=reshape(permute(reshape(Im4,size(Im4,2),2,[]),[2,3,1]),2,[]);
2) Create a cell-array with the 4 cells I need to average (subsetted) and calculate the mean of it. That's where it doesn't work
I{1,160000}=ones,
for k=drange(1:2:319999)
for n=1:160000
I{n}=mean2(Im4bis(1:2,k:k+1));
end
end
I created an empty matrix of 400 x 400 cells (actually a vector of 1 x 160000) and I wanted to fill it with the mean but I get a matrix of 1 x 319 999 cells with one cell out of 2 empty.
Looking for light
My Input Image:
Method 1
Using mat2cell and cellfun
AC = mat2cell(A, repmat(2,size(A,1)/2,1), repmat(2,size(A,2)/2,1));
out = cellfun(#(x) mean(x(:)), AC);
Method 2
using im2col
out = reshape(mean(im2col(A,[2 2],'distinct')),size(A)./2);
Method 3
Using simple for loop
out(size(A,1)/2,size(A,2)/2) = 0;
k = 1;
for i = 1:2:size(A,1)
l = 1;
for j = 1:2:size(A,2)
out(k,l) = mean(mean(A(i:i+1,j:j+1)));
l = l+1;
end
k = k+1;
end
Test on input image:
A = rgb2gray(imread('inputImage.png'));
%// Here, You could use any of the method from any answers
%// or you could use the best method from the bench-marking tests done by Divakar
out = reshape(mean(im2col(A,[2 2],'distinct')),size(A)./2);
imshow(uint8(out));
imwrite(uint8(out),'outputImage.bmp');
Output Image:
Final check by reading the already written image
B = imread('outputImage.bmp');
>> whos B
Name Size Bytes Class Attributes
B 400x400 160000 uint8
Let A denote your matrix and
m = 2; %// block size: rows
n = 2; %// block size: columns
Method 1
Use blockproc:
B = blockproc(A, [m n], #(x) mean(x.data(:)));
Example:
>> A = magic(6)
A =
35 1 6 26 19 24
3 32 7 21 23 25
31 9 2 22 27 20
8 28 33 17 10 15
30 5 34 12 14 16
4 36 29 13 18 11
>> B = blockproc(A, [m n], #(x) mean(x.data(:)))
B =
17.7500 15.0000 22.7500
19.0000 18.5000 18.0000
18.7500 22.0000 14.7500
Method 2
If you prefer the reshaping way (which is probably faster), use this great answer to organize the matrix into 2x2 blocks tiled along the third dimension, average along the first two dimensions, and reshape the result:
T = permute(reshape(permute(reshape(A, size(A, 1), n, []), [2 1 3]), n, m, []), [2 1 3]);
B = reshape(mean(mean(T,1),2), size(A,1)/m, size(A,2)/n);
Method 3
Apply a 2D convolution (conv2) and then downsample. The convolution computes more entries than are really necessary (hence the downsampling), but on the other hand it can be done separably, which helps speed things up:
B = conv2(ones(m,1)/m, ones(1,n)/n ,A,'same');
B = B(m-1:m:end ,n-1:n:end);
One approach based on this solution using reshape, sum & squeeze -
sublen = 2; %// subset length
part1 = reshape(sum(reshape(A,sublen,[])),size(A,1)/sublen,sublen,[]);
out = squeeze(sum(part1,2))/sublen^2;
Benchmarking
Set #1
Here are the runtime comparisons for the approaches listed so far for a input datasize of 800x 800 -
%// Input
A = rand(800,800);
%// Warm up tic/toc.
for k = 1:50000
tic(); elapsed = toc();
end
disp('----------------------- With RESHAPE + SUM + SQUEEZE')
tic
sublen = 2; %// subset length
part1 = reshape(sum(reshape(A,sublen,[])),size(A,1)/sublen,sublen,[]);
out = squeeze(sum(part1,2))/sublen^2;
toc, clear sublen part1 out
disp('----------------------- With BLOCKPROC')
tic
B = blockproc(A, [2 2], #(x) mean(x.data(:))); %// [m n]
toc, clear B
disp('----------------------- With PERMUTE + MEAN + RESHAPE')
tic
m = 2;n = 2;
T = permute(reshape(permute(reshape(A, size(A, 1), n, []),...
[2 1 3]), n, m, []), [2 1 3]);
B = reshape(mean(mean(T,1),2), size(A,1)/m, size(A,2)/m);
toc, clear B T m n
disp('----------------------- With CONVOLUTION')
tic
m = 2;n = 2;
B = conv2(ones(m,1)/m, ones(1,n)/n ,A,'same');
B = B(m-1:m:end ,n-1:n:end);
toc, clear m n B
disp('----------------------- With MAT2CELL')
tic
AC = mat2cell(A, repmat(2,size(A,1)/2,1), repmat(2,size(A,2)/2,1));
out = cellfun(#(x) mean(x(:)), AC);
toc
disp('----------------------- With IM2COL')
tic
out = reshape(mean(im2col(A,[2 2],'distinct')),size(A)./2);
toc
Runtime results -
----------------------- With RESHAPE + SUM + SQUEEZE
Elapsed time is 0.004702 seconds.
----------------------- With BLOCKPROC
Elapsed time is 6.039851 seconds.
----------------------- With PERMUTE + MEAN + RESHAPE
Elapsed time is 0.006015 seconds.
----------------------- With CONVOLUTION
Elapsed time is 0.002174 seconds.
----------------------- With MAT2CELL
Elapsed time is 2.362291 seconds.
----------------------- With IM2COL
Elapsed time is 0.239218 seconds.
To make the runtimes more fair, we can use a number of trials of 1000 on top of the fastest three approaches for the same input datasize of 800 x 800, giving us -
----------------------- With RESHAPE + SUM + SQUEEZE
Elapsed time is 1.264722 seconds.
----------------------- With PERMUTE + MEAN + RESHAPE
Elapsed time is 3.986038 seconds.
----------------------- With CONVOLUTION
Elapsed time is 1.992030 seconds.
Set #2
Here are the runtime comparisons for a larger input datasize of 10000x 10000 for the fastest three approaches -
----------------------- With RESHAPE + SUM + SQUEEZE
Elapsed time is 0.158483 seconds.
----------------------- With PERMUTE + MEAN + RESHAPE
Elapsed time is 0.589322 seconds.
----------------------- With CONVOLUTION
Elapsed time is 0.307836 seconds.
Given a matrix Z(i,j) such that it maps to two arrays X(i), and Y(j).
I am trying to find elements of Z (and hence the corresponding X and Y) within a certain range.
I am now doing the following using logical indexing. Given this example
X = 1:5;
Y = 1:5;
Z = [17 24 1 8 15
23 5 6 14 16
4 6 13 20 22
10 12 19 21 3
11 18 25 2 9]
Z((X>1 & X<4),Y==3)
This works fine, but now I wish to find the minimum of the returned values from this particular range,
Which I do with
min(Z((X>1 & X<4),Y==3))
But now how I get back the corresponding X and Y values of the minimum? Since my logical indexing returns an array, all methods I have tried so far returns the index of the min in the answer array, not the original Z matrix.
I can't use
[row col] = find(Z==min(Z((X>1 & X<4),Y==3)))
Because of the repeats. What are my alternatives?
To retrieve the original indices, you have to keep the memory of the indices of your two conditions on x and y (which I put in the arrays cX and cY) and then use the function ind2sub.
NB: your code is a little bit confusing since x stands for the lines
and y for the columns, but I have kept the same convention in my
answer.
In practice, this gives:
% --- Definition
X = 1:5;
Y = 1:5;
Z = [17 24 1 8 15
23 5 6 14 16
4 6 13 20 22
10 12 19 21 3
11 18 25 2 9];
% --- Get the values of interest
cX = find(X>1 & X<4);
cY = find(Y==3);
v = Z(cX,cY);
% --- Get position of the minimum in the initial array
[~, I] = min(v(:));
[Ix, Iy] = ind2sub([numel(cX) numel(cY)], I);
i = cX(Ix); % i = 2
j = cY(Iy); % j = 3
Best,
One approach -
%// Calculate all indices of the grid created with those two X-Y conditions
idx = bsxfun(#plus,(find(Y==3)-1)*size(Z,1),find((X>1 & X<4)).') %//'
%// Get the index corresponding to minimum from that grided Z
[~,min_idx] = min(Z(idx(:)))
%// Get corresponding X-Y indices by using indices calculated earlier
[indX,indY] = ind2sub([numel(X) numel(Y)],idx(min_idx))
I'm working on Matlab and was wondering how I add terms within a large matrix. Specifically, I have a 4914x4914 matrix and would like to create a 189x189 matrix, where each term is equal to the sum of the terms in each 26x26 subset.
To illustrate, say I had the magic 4x4 matrix as follows:
[16 2 3 13;
5 11 10 8;
9 7 6 12;
4 14 15 1]
and I wanted to create a 2x2 matrix equal to the sum of each 2x2 matrix within the original magic 4x4, i.e.:
[(16+2+5+11) (3+13+10+8);
(9+7+4+14) (6+12+15+1)]
Grateful for any advice!
Thanks
jake
Assuming A to be the input 4914x4914 matrix, this could be an efficient (in terms of runtime) approach -
sublen = 26; %// subset length
squeeze(sum(reshape(sum(reshape(A,sublen,[])),size(A,1)/sublen,sublen,[]),2))
For a generic block size, let's have a function -
function out = sum_blocks(A,block_nrows, block_ncols)
out = squeeze(sum(reshape(sum(reshape(A,block_nrows,[])),...
size(A,1)/block_nrows,block_ncols,[]),2));
return
Sample run -
>> A = randi(9,4,6);
>> A
A =
8 2 4 9 4 5
3 3 8 3 6 8
9 6 6 7 1 9
4 5 5 7 1 2
>> sum_blocks(A,2,3)
ans =
28 35
35 27
>> sum(sum(A(1:2,1:3)))
ans =
28
>> sum(sum(A(1:2,4:6)))
ans =
35
>> sum(sum(A(3:4,1:3)))
ans =
35
>> sum(sum(A(3:4,4:6)))
ans =
27
If you would like to avoid squeeze -
sum(permute(reshape(sum(reshape(A,sublen,[])),size(A,1)/sublen,sublen,[]),[1 3 2]),3)
Benchmarking
Hoping you would care about performance, here are the benchmark results for all the solutions posted here. The benchmarking code that I have used -
num_runs = 100; %// Number of iterations to run benchmarks
A = rand(4914);
for k = 1:50000
tic(); elapsed = toc(); %// Warm up tic/toc
end
disp('---------------------- With squeeze + reshape + sum')
tic
for iter = 1:num_runs
sublen = 26; %// subset length
out1 = squeeze(sum(reshape(sum(reshape(A,sublen,[])),...
size(A,1)/sublen,sublen,[]),2));
end
time1 = toc;
disp(['Avg. elapsed time = ' num2str(time1/num_runs) ' sec(s)']), clear out1 sublen
disp('---------------------- With kron + matrix multiplication')
tic
for iter = 1:num_runs
n = 189; k = 26;
B = kron(speye(k), ones(1,n));
result = B*A*B';
end
time2 = toc;
disp(['Avg. elapsed time = ' num2str(time2/num_runs) ' sec(s)']),clear result n k B
disp('---------------------- With accumarray')
tic
for iter = 1:num_runs
s = 26; n = size(A,1)/s;
subs = kron(reshape(1:(n^2), n, n),ones(s));
out2 = reshape(accumarray(subs(:), A(:)), n, n);
end
time2 = toc;
disp(['Avg. elapsed time = ' num2str(time2/num_runs) ' sec(s)']),clear s n subs out2
The benchmarks results I got on my system -
---------------------- With squeeze + reshape + sum
Avg. elapsed time = 0.050729 sec(s)
---------------------- With kron + matrix multiplication
Avg. elapsed time = 0.068293 sec(s)
---------------------- With accumarray
Avg. elapsed time = 0.64745 sec(s)
An alternative way is to reshape the whole matrix into a 4D matrix and sum the elements over first and third dimension:
result = squeeze(sum(sum(reshape(A,26,189,26,189),1),3));
If you don't have the image processing toolbox then you can do this using accumarray:
s = 26;
n = size(A,1)/s;
subs = kron(reshape(1:(n^2), n, n),ones(s));
reshape(accumarray(subs(:), A(:)), n, n)
this is reusable should you decide to aggregate some way other than a simple sum e.g. a median:
reshape(accumarray(subs(:), A(:), [], #median), n, n)
You can use matrix multiplication, of course:
n = 26;
k = 189;
B = kron(speye(k), ones(1,n));
result = B*A*B';