Indexing a set of indices to a matrix - matlab

I retrieved all non-white pixel from an image :
[ii, jj] = find(BlackOnWhite < 255)
Then I tried to index those pixel coordinates to a matrix :
image(ii, jj) = 0
But zeros do not appear in the expected places. How can I put zeros only in the places specified by pairs from ii and jj (i.e. [ii(1), jj(1)], [ii(2), jj(2)] etc.)?

A simple way to do that is to use linear indexing. This means using a single index that traverses all entries in the matrix (down, then across). In your case:
Use find with one ouput. This gives the linear indices of the desired pixels.
Use that to index into the matrix.
So:
ind = find(BlackOnWhite < 255);
image(ind) = 0;
You can even remove find and use logical indexing. This means that the result of the logical comparison is directly used as an index:
ind = BlackOnWhite < 255;
image(ind) = 0;
The problem with the code shown in your question is that ii and jj are being used as "subscript indices". This selects all pairs formed by any value from ii and any value from jj, which is not what you want.
If you have subscripts ii and jj like in your question and you need to only select corresponding values from each subscript (instead of all pairs), you can use sub2ind to convert to a linear index:
[ii, jj] = find(BlackOnWhite < 255);
image(sub2ind(size(image), ii, jj)) = 0;

It doesn't work because MATLAB treats the subscripts as a grid, which means roughly "set all intersection of any of ii and any of jj to zero" and not "set the locations specified by these separate pairs of coordinates to zero".
In some cases (but not this one) you might need to convert a set of subscripts to indices, in which case I suggest familiarizing yourself with sub2ind.
As mentioned in the other answer(s), the best thing to do in your case is simply:
image(BlackOnWhite < 255) = 0;

Related

Generating all ordered samples with replacement

I would like to generate an array which contains all ordered samples of length k taken from a set of n elements {a_1,...,a_n}, that is all the k-tuples (x_1,...,x_k) where each x_j can be any of the a_i (repetition of elements is allowed), and whose total number is n^k.
Is there a built-in function in Matlab to obtain it?
I have tried to write a code that iteratively uses the datasample function, but I couldn't get what desired so far.
An alternative way to get all the tuples is based on k-base integer representation.
If you take the k-base representation of all integers from 0 to n^k - 1, it gives you all possible set of k indexes, knowing that these indexes start at 0.
Now, implementing this idea is quite straightforward. You can use dec2base if k is lower than 10:
X = A(dec2base(0:(n^k-1), k)-'0'+1));
For k between 10 and 36, you can still use dec2base but you must take care of letters as there is a gap in ordinal codes between '9' and 'A':
X = A(dec2base(0:(n^k-1), k)-'0'+1));
X(X>=17) = X(X>=17)-7;
Above 36, you must use a custom made code for retrieving the representation of the integer, like this one. But IMO you may not need this as 2^36 is quite huge.
What you are looking for is ndgrid: it generates the grid elements in any dimension.
In the case k is fixed at the moment of coding, get all indexes of all elements a this way:
[X_1, ..., X_k] = ndgrid(1:n);
Then build the matrix X from vector A:
X = [A(X_1(:)), ..., A(X_k(:))];
If k is a parameter, my advice would be to look at the code of ndgrid and adapt it in a new function so that the output is a matrix of values instead of storing them in varargout.
What about this solution, I don't know if it's as fast as yours, but do you think is correct?
function Y = ordsampwithrep(X,K)
%ordsampwithrep Ordered samples with replacement
% Generates an array Y containing in its rows all ordered samples with
% replacement of length K with elements of vector X
X = X(:);
nX = length(X);
Y = zeros(nX^K,K);
Y(1,:) = datasample(X,K)';
k = 2;
while k < nX^K +1
temprow = datasample(X,K)';
%checknew = find (temprow == Y(1:k-1,:));
if not(ismember(temprow,Y(1:k-1,:),'rows'))
Y(k,:) = temprow;
k = k+1;
end
end
end

Converting matlab sparse matrix to single precision

I want to convert a sparse matrix in matlab to single precision, however it appears that matlab doesn't have single sparse implemented.
Instead of that, I am just planning on checking it the values are outside of the single precision range and rounding them off to the highest and lowest values of the single precision range.
I'd like to do something like this:
for i = 1:rows
for j = 1:cols
if (abs(A(i,j) < 2^-126))
A(i,j) == 0;
end
end
end
However, ths is extremely slow. Is there another command I can use that will work on sparse matrix class type in MATLAB? I notice most commands don't work for the sparse data type.
EDIT 1:
I also tried the following, but as you can see I run out of memory (the matrix is sparse and is 200K x 200K with ~3 million nonzeros):
A(A < 2^-126) = 0
Error using <
Out of memory. Type HELP MEMORY for your options.
EDIT 2:
Current solution I developed based on input from #rahnema1:
% Convert entries that aren't in single precision
idx = find(A); % find locations of nonzeros
idx2 = find(abs(A(idx)) < double(realmin('single')));
A(idx(idx2)) = sign(A(idx(idx2)))*double(realmin('single'));
idx3 = find(abs(Problem.A(idx)) > double(realmax('single')));
A(idx(idx3)) = sign(A(idx(idx3)))*double(realmax('single'));
You can find indices of non zero elements and use that to change the matrix;
idx = find(A);
Anz = A(idx);
idx = idx(Anz < 2^-126);
A(idx) = 0;
Or more compact:
idx = find(A);
A(idx(A(idx) < 2^-126)) = 0;
However if you want to convert from double to single you can use single function:
idx = find(A);
A(idx) = double(single(full(A(idx))));
or
A(find(A)) = double(single(nonzeros(A)));
To convert Inf to realmax you can write:
A(find(A)) = double(max(-realmax('single'),min(realmax('single'),single(nonzeros(A)))));
If you only want to convert Inf to realmax you can do:
Anz = nonzeros(A);
AInf = isinf(A);
Anz(AInf) = double(realmax('single')) * sign(Anz(AInf));
A(find(A)) = Anz;
As one still reaches this thread when interested in how to convert sparse to single: an easy answer is to use full(). However, this can be problematic if you rely on the memory savings that sparse gives you, as it basically converts to double first.
sparseMatrix = sparse(ones(2, 'double'));
single(full(sparseMatrix)) % works
% ans =
% 2×2 single matrix
% 1 1
% 1 1
single(sparseMatrix) % doesn't work
% Error using single
% Attempt to convert to unimplemented sparse type

Access a list of entries in MATLAB

I have a huge matrix MxN matrix, say, A=rand([M,N]); and an index vector with N integer values between 1 and M, say, RandomIndex = randi(M,[1,N]);.
Now I would like to generate a row vector with entries
result = [A(RandomIndex(1),1), A(RandomIndex(2),2), ..., A(RandomIndex(N),N)]
What would be an efficient way to do this? It should be a very cheap operation but all my implementations are slow. I don't think there is a notation in Matlab to do this directly, is there?
The fastest option so far is
indexFunction = #(r,c) A(r,c);
result = cell2mat(arrayfun(indexFunction,RandomIndex,1:N,'UniformOutput',false));
Is there a more efficient way?
Use sub2ind
A(sub2ind(size(A), RandomIndex, 1:N))
sub2ind will convert the row and column indices given by RandomIndex and 1:N to linear indices based on size(A) which you can then use to index A directly.
Another way to do this is to use RandomIndex and 1:N to return an NxN matrix and then take the diagonal of this with diag
diag(A(RandomIndex, 1:N)).'
Note: .' is used to convert the row vector returned by diag to a column vector.
M=10;N=50;
A=rand([M,N]);
RandomIndex = randi(M,[1,N]);
out = zeros(1,numel(RandomIndex));
for ii = 1:numel(RandomIndex)
out(ii)=A(RandomIndex(ii),ii);
end
Another approach would be to use sparse and logical indexing:
M = sparse(RandomIndex, 1:N, 1) == 1;
out = A(M);
The first line of code generates a logical matrix where there is only 1 true value set in each column. This is defined by each value of RandomIndex. We convert this to a logical matrix, then index into your matrix to obtain the final random vector.
Use your index directly.
M = 100;N=100;
A = rand(M,N);
% get a random index that can be as large as your matrix
% 10 rows by 1 column
idx = randi(numel(A), 10,1);
t = A(idx);

Matlab Matrix one-to-one correspondence index

I have a small code below for one-to-one correspondence index of the img matrix
for k = 1:length(I)
img(I(k),J(k)) = 0;
end
Now, I hope to get rid of for loop, but I cannot find proper matlab syntax to realize it.
img(I(1:length(I)), J(1:length(I)),1:3) = 0;
is not one-to-one correspondence index. Any help to realize the same function is appreciated.
Indexing in a linear fashion along multiple dimensions can be done using the sub2ind function:
img(sub2ind(size(img), I, J(1:length(I))) = 0;
You can also use sparse to get this done:
ind = sparse(I, J, 1, size(img,1), size(img,2)) == 1;
img(ind) = 0;
The first line of code generates a sparse matrix where the row values stored in I and the column values stored in J set the matrix values to 1, and we ensure that this is the same size as your image. We also convert to a logical array by equating the statement with 1. When you're done, simply use the result to index into your actual array and set the values to 0.
If you have a multi-channel matrix, you can do this temporally by making a call to repmat:
img(repmat(ind, [1 1 size(img,3)])) = 0;

Accessing Ranges of Data in Vectorized Way MATLAB

I have a column vector of data in variable vdata and a list of indeces idx. I want to access vdata at the indeces x before and x after each index in idx. One way I would do it in a for loop is:
x = 10;
accessed_data = [];
for (ii = 1:length(idx))
accessed_data = vdata(idx-x:idx+x);
end
Is there a way to do this in a vectorized function? I found a solution to a very similar question here: Addressing multiple ranges via indices in a vector but I don't understand the code :(.
Assuming min(idx)-x>0 and max(idx)+x<=numel(vdata) then you can simply do
iidx = bsxfun(#plus, idx(:), -x:x); % create all indices
accessed_data = vdata( iidx );
One scheme that uses direct indexing instead of a for loop:
xx = (-x:x).'; % Range of indices
idxx = bsxfun(#plus,xx(:,ones(1,numel(idx))),idx(:).'); % Build array
idxx = idxx(:); % Columnize to interleave columns
idxx = idxx(idxx>=1&idxx<=length(vdata)); % Make sure the idx+/-x is valid index
accessed_data = vdata(idxx); % Indices of data
The second line can be replaced with a form of the first line from #Shai's answer. This scheme checks that all of the resultant indices are valid. Because some might have to be removed, you could end up with a ragged array. One way to solve this is to use cell arrays, but here I just make idxx a vector, and thus accessed_data is as well.
This gives the solution in a matrix, with one row for each value in idx. It assumes that all values in idx are greater than or equal to x, and less than or equal to length(vdata)-x.
% Data
x = 10;
idx = [12 20 15];
vdata = 1:100;
ind = repmat(-x:x,length(idx),1) + repmat(idx(:),1,2*x+1);
vdata(ind)