How to replace a block matrix with a new one? - matlab

I have an image size 128*128, I divided into 32*32 non-overlapping blocks, the problem is that I want to change the values of a specific or choosing block (the first or the 3rd.. for example) and getting a block with new values then replace it in my image. Do you have any idea how to get an image with ONE block modified ( NOT all of them)?
Thank you
This is an example with a small matrix
%*********************
A=magic(6) ; %%%%% matrix size 6*6
B=Block(A,2,2) ; % divide the matrix into 2*2 non-overlapping blocks
subblock=B{3} ; % Choose the 3rd block
new_block= 2*subblock; % change the block values by multipliying by 2
This is what I get
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
I extract the 3rd block
sub_block=19 24
23 25
Now I multiplied by 2 :
new_block= 38 48
46 50
This is my Block function:
function A=Block(IM,p,q)
p=3;
q=3;
[m,n] = size(IM);
IJ = zeros(p,q);
z = 1;
for m1 = 1:m/p
for n1 = 1:n/q
if m1*p <= m;
if n1*q <= n;
for i = (m1-1)*p+1:m1*p
for j = (n1-1)*q+1:n1*q
IJ(i-(m1-1)*p,j-(n1-1)*q) = IM(i,j);
if (i-(m1-1)*p)==p&&(j-(n1-1)*q)==q;
OUT = IJ;
A{1,z} = OUT;
z = z+1;
end
end
end
end
end
end
end
I want to replace these values in matrix A , but depending on block number. How can I do it?

Just type in the rows and cols you want to access, for your example
A(1:2,5:6)=A(1:2,5:6)*2
more generic, where n is the nth column block and m is the mth row block and c is the block width and r is the block height (in your example, n = 3, m = 1, r=c=2)
A(((m-1)*r+1):(m*r), ((n-1)*c+1):(n*c)) = any block of size(r,c)

I don't know about your Block function, you don't actually need to convert to a cell matrix but if you do want to then I would do this:
A = magic(6);
[m,n] = size(A);
r=2; %// Desired number rows of blocks, m must be a multiple of r
c=2; %// Desired number cols of blocks, n must be a multiple of c
%// Create blocks (but as a 2D grid rather than a list)
B = mat2cell(A,r*ones(m/r,1), c*ones(n/c,1))
%// Manipulate a block
B(1,3) = {2*B{1,3}};
%// Convert back to a matrix
cell2mat(B)
I think that RobertStettler's answer is the better way to go though, if this is all you are trying to do

Related

How do I save the values from every iteration of a for-loop?

I have a for-loop, but every iteration overwrites the variable and I have only the final data left.
How can I save the other values from every iteration of the for-loop?
Here is the code I tried:
p = [1:1:20]';
for x = 0.1:0.1:1
q = x.*p
end
Here is the result I got:
q =
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
You can make q a two-dimensional matrix or a cell.
Two-dimensional matrix:
q=zeros(numel(p),10); %better to pre-allocate if you know the dimensions beforehand.
count=0;
for x=.1:.1:1
count=count+1;
q(:,count)=x.*p;
end
Cell:
q=cell(10); %better to pre-allocate if you know the dimensions beforehand.
count=0;
for x=.1:.1:1
count=count+1;
q{count}=x.*p;
end
Here is an alternative solution use bsxfun() It multiplies each x index with p' in just one line
p = [1:1:20]';
x = 0.1:0.1:1;
q = bsxfun(#times,x,p)

How can I select random value that previously selected value are excluded from the selection

I want to select a random value but previously selected value are excluded from the selection. How can make like this??
Create a list of random numbers within the range, and pick one at a time
minimum=1;
maximum=16;
randomNumbers=randperm(maximum-minimum+1)+minimum-1
and sample output is randomNumbers=[7 13 2 15 12 4 16 11 9 5 3 1 14 6 8 10]. You can display each number sequentially using, for example,
for k=1:maximum+1-minimum
randomNumbers(k)
end
This approach is generalized one.. A could have any values, some numbers repeating or non repeating.. also any number of(of course upto numel(A)) could be generated.
Code:
A = randi(50,3,3); %// replace this with your own matrix
idx = 1:numel(A); %// generating linear indices
noOfRandNos = 5; %// How many such numbers do you want?
randNos(noOfRandNos,1) = 0; %// pre Allocation
%// This loop is run as many times as the number of such numbers you require.
%// Maximum possible runs will be equal to the numel(A).
for ii = 1:noOfRandNos
randidx = randi(numel(idx)); %// generating a rand Indx within the current size of idx
randNos(ii) = A(idx(randidx)); %// Getting corresponding number in-turn from the indx
idx(randidx) = []; %// removing the particular indx so that it is not repeated
end
Sample Run:
>> A
A =
12 28 25
23 27 32
49 12 34
>> randNos
randNos =
28
49
34
12
32
To pick 5 numbers from the set 1:16 without repetition: use randsample:
result = randsample(1:16, 5);

Finding index of element matching condition of matrix - Matlab

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))

MATLAB: Use a single row vector to specify which row element of a multiple row matrix to select

Given an mxn matrix e.g.
M= [ 10 20 30 40; 11 12 13 14; 19 18 17 16];
and a 1xn 'selector'
S = [1 2 3 1];
where all elements of S are in the range 1..m, I want the output vector O with size 1xn s.t. O[1, i] = M[ S[i], i]. In this example
O = [10 12 17 40];
Clearly I can do this using a loop. Is there a way to vectorize it which is more cost effective than a loop assuming that m and n are in the hundreds ?
You are looking for sub2ind. So the desired output could be achieved with -
O = M(sub2ind(size(M),S,1:numel(S)))
Or for performance, you can use a raw version of sub2ind -
O = M([0:numel(S)-1]*size(M,1) + S)

Matrix "Zigzag" Reordering

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.