Locations of pixels and setting them to `1` - matlab

In matlab, after meeting a specific criterion, I used to return back the pixel itself and store it in the vector pixels as follows:
pixels(index) = y(i,j);
Now, I would like to return the location of those pixels. Should I do the following?
pixels(index) = i,j;
EDIT
If I want to then set those indexes to the value 1, I do the following, right?
for i=1:m
for j=1:n
y(i,j)=1
end
end
Thanks.

It is extremely inefficient to do so in a nested loop in Matlab.
Using sub2ind can help you do so much faster:
y( sub2ind( size(y), i, j ) ) = 1;
EDIT - sub2ind
What sub2ind does?
Suppose you have a matrix M of size [4 6]:
M = [ 1 5 9 13 17 21
2 6 10 14 18 22
3 7 11 15 19 23
4 8 12 16 20 24 ];
You wish to access two elements: the one at the first row and second column, and another at the fourth row and the fifth column.
In that case you have the rows you wish to access r = [ 1 4 ] and the columns you wish to access c = [ 2 5 ]. However, if you try and access
>> M( r, c )
This is a 2x2 matrix
ans =
5 17
8 20
And not the two elements you were looking for (which are 5 and 20).
What sub2ind does is convert the row/column indices you have into linear indices
>> sub2ind( size(M), r, c )
ans =
5 20
which happens to be the linear indices of the requested entries.
You can think of linear indices as the single index required to access an element in a matrix in the case that the matrix was converted to a vector stacking its columns one after the other.
A few comments:
Matlab has a few ways of indexing matrices: by row / column indices (like i and j in your question). By linear indices (like index in your question). However, the more efficient way is to use logical indexing: that is, using a matrix of the same size as y with true for the entries you wish to set / get.
So, in your example, if you could get such a logical matrix instead of index or i and j it would have been better.
Matlab has many advantages over other programing languages. One of them is its ability to perform vector/matrix operations extremely efficient. Resorting to loops, or worse, nested loops, is something that should be avoided in Matlab.
It is not a good practice to use i and j as variables in Matlab.

If you want to find the occurrence of a value y(i,j) simply evaluate
idx = (pixels == y(i,j));
Depending on your variables you can then probably do
index(idx) = 1;

Related

1) Extract submatrices, 2) vectorize and then 3) put back

I simplify my problem, let says I have three matrices.
I want to extract the red-boxed sub-matrices. I define
S = [1 4;
2 5]
that are the linear indices of the above matrices. So, A(S), B(S) and C(S) can extract the entries of the three matrices.
I pack them into vector by V = [ A(S)(:); B(S)(:); C(S)(:) ]. Let says after some manipulations, I obtain a new vector
V_new = [12 9 8 12 21 8 7 5 3 12 11 10]'
Here comes to my problem:
E.g for matrix A, I want to obtain
2->12, 5->9, 4->8 and 6->12
which are the first four entries of my V_new.
Since I have around 200 matrices, I have no idea to swap along the 200 matrices and the updated vector, V_new at the same time. Is writing a for-loop best way to do this purpose?
Thanks in advance.
Assuming that your A, B and C matrices have the same dimensions, rather work with a 3D matrix.
e.g. assuming your example matrices
M = cat(3,A,B,C)
No to extract those 4 upper left elements:
M_subset = M(1:2,1:2,:)
And then to reshape them into the vector you had:
V = M_subset(:)
then manipulate it to get V_new and finally put it back in the original:
M(1:2,1:2,:) = reshape(V_new,2,2,[])

Matlab: Array of random integers with no direct repetition

For my experiment I have 20 categories which contain 9 pictures each. I want to show these pictures in a pseudo-random sequence where the only constraint to randomness is that one image may not be followed directly by one of the same category.
So I need something similar to
r = randi([1 20],1,180);
just with an added constraint of two numbers not directly following each other. E.g.
14 8 15 15 7 16 6 4 1 8 is not legitimate, whereas
14 8 15 7 15 16 6 4 1 8 would be.
An alternative way I was thinking of was naming the categories A,B,C,...T, have them repeat 9 times and then shuffle the bunch. But there you run into the same problem I think?
I am an absolute Matlab beginner, so any guidance will be welcome.
The following uses modulo operations to make sure each value is different from the previous one:
m = 20; %// number of categories
n = 180; %// desired number of samples
x = [randi(m)-1 randi(m-1, [1 n-1])];
x = mod(cumsum(x), m) + 1;
How the code works
In the third line, the first entry of x is a random value between 0 and m-1. Each subsequent entry represents the change that, modulo m, will give the next value (this is done in the fourth line).
The key is to choose that change between 1 and m-1 (not between 0 and m-1), to assure consecutive values will be different. In other words, given a value, there are m-1 (not m) choices for the next value.
After the modulo operation, 1 is added to to transform the range of resulting values from 0,...,m-1 to 1,...,m.
Test
Take all (n-1) pairs of consecutive entries in the generated x vector and count occurrences of all (m^2) possible combinations of values:
count = accumarray([x(1:end-1); x(2:end)].', 1, [m m]);
imagesc(count)
axis square
colorbar
The following image has been obtained for m=20; n=1e6;. It is seen that all combinations are (more or less) equally likely, except for pairs with repeated values, which never occur.
You could look for the repetitions in an iterative manner and put new set of integers from the same group [1 20] only into those places where repetitions have occurred. We continue to do so until there are no repetitions left -
interval = [1 20]; %// interval from where the random integers are to be chosen
r = randi(interval,1,180); %// create the first batch of numbers
idx = diff(r)==0; %// logical array, where 1s denote repetitions for first batch
while nnz(idx)~=0
idx = diff(r)==0; %// logical array, where 1s denote repetitions for
%// subsequent batches
rN = randi(interval,1,nnz(idx)); %// new set of random integers to be placed
%// at the positions where repetitions have occured
r(find(idx)+1) = rN; %// place ramdom integers at their respective positions
end

Create vector from elements other than diagonal ones

I would like to create a column vector from the elements of a matrix A of size (3,3) that are not on the diagonal. Thus, I would have 6 elements in that output vector. How can I do this?
Use eye and logical negation, although this is no better than Divakar's original answer, and possibly significantly slower for very large matrices.
>> A = magic(4)
A =
16 2 3 13
5 11 10 8
9 7 6 12
4 14 15 1
>> A(~eye(size(A)))
ans =
5
9
4
2
7
14
3
10
15
13
8
12
Use this to get such a column vector, assuming A is the input matrix -
column_vector = A(eye(size(A))==0)
If you don't care about the order of the elements in the output, you can also use a combination of setdiff and diag -
column_vector = setdiff(A,diag(A))
You can also use linear indexing to access the diagonal elements and null them. This will automatically reshape itself to a single vector:
A(1:size(A,1)+1:end) = [];
Bear in mind that this will mutate the original matrix A. If you don't want this to happen, make a copy of your matrix then perform the above operation on that copy. In other words:
Acopy = A;
Acopy(1:size(A,1)+1:end) = [];
Acopy will contain the final result. You need to create a vector starting from 1 and going to the end in increments of the rows of the matrix A added with 1 due to the fact that linear indices are column-major, so the linear indices used to access a matrix progress down each row first for a particular column. size(A,1) will allow us to offset by each column and we add 1 each time to ensure we get the diagonal coefficient for each column in the matrix.
Assuming that the matrix is square,
v = A(mod(0:numel(A)-1, size(A,1)+1) > 0).';

Why does a column vector return a row vector when used to index matrices?

Imagine the following matrix is defined in MATLAB:
>> matrix=10:18
matrix =
10 11 12 13 14 15 16 17 18
Now I want to use another matrix to index the first one
>> index=[1,2;3,4]
index =
1 2
3 4
>> matrix(index)
ans =
10 11
12 13
So far so good, the size of the answer matches that of the matrix 'index'. If I use a row vector as the indexing matrix, the output is a row vector too. But the problem appears when I use a column vector as the indexing matrix:
>> index=(1:3)'
index =
1
2
3
>> matrix(index)
ans =
10 11 12
As you can see, here the size of the answer does not agree with the size of the matrix 'index'. This inconsistency in the sizes of the indexing matrix and the ans matrix prevents me from writing a piece of code accepting an indexing matrix of an arbitrary size.
I wonder if someone else has come across this problem before and has found some sort of solution to it; in other words, how can I force MATLAB to give me an ans matrix of the same size as the arbitrarily sized indexing matrix?
Cheers
Solution
#Dev-iL has nicely explained here why this is the way Matlab behaves, and #Dan has presented a general solution here. However, there was a simpler ad-hoc workaround for my code which I've explained here.
The reason comes from the function subsref.m, which is called when using parentheses:
%SUBSREF Subscripted reference.
% A(I) is an array formed from the elements of A specified by the
% subscript vector I. The resulting array is the same size as I except
% for the special case where A and I are both vectors. In this case,
% A(I) has the same number of elements as I but has the orientation of A.
As you can see, in the special case of vectors, the shape of the result will be the same as that of matrix in your example.
As for a workaround, IMHO Dan has the cleanest solution.
Preallocate the output matrix with the size of index matrix.
Then assign corresponding indices the corresponding values
out = zeros(size(index));
out(index) = matrix(index);
Example
index = (1:3)'
index =
1
2
3
>> out = zeros(size(index))
out =
0
0
0
>> in = magic(3)
in =
8 1 6
3 5 7
4 9 2
>> out(index) = in(index)
out =
8
3
4
>>
Dev-iL has explained why it is like that. Here is a potential workaround:
reshape(matrix(index),size(index))

Find given row in a matrix

I have an m by n matrix in MATLAB, say M. I have an n-element row vector, i.e. a one by n column matrix, say X.
I know X is a row somewhere in M. How can I find the index in M?
EDIT:
gnovice's suggestion is even simpler than mine:
[~,indx]=ismember(X,M,'rows')
indx =
3
FIRST SOLUTION:
You can easily do it using find and ismember. Here's an example:
M=magic(4); %#your matrix
M =
16 2 3 13
5 11 10 8
9 7 6 12
4 14 15 1
X=[9 7 6 12]; %#your row vector
find(ismember(M,X),1)
ans =
3
Before I learned of ismember, I used to do:
index = find(all(bsxfun(#eq, M, X), 2));
But using ismember(X, M, 'rows') is definitely preferable.
Another solution that returns a row index for each occurrence of X is
find(sum(abs(M-ones(rows(M),1)*X),2)==0)
Also, this solution can be easily adapted to find rows that are within threshold of X as follows (if numerical noise is an issue)
tolerance = 1e-16; %setting the desired tolerance
find(sum(abs(M-ones(rows(M),1)*X),2)<tolerance)
This is a non-loop version. It is only suitable, if M (your matrix) is not very large, ie. n and m are small. X is your row:
function ind = findRow(M,X)
tmp = M - repmat(X,size(M,1),1);
ind = find(tmp,1);
end
If M is too large, it might be faster, to iterate the rows of M and compare every row with your vector.
#Edit: renamed variables to match the names used in the question.