Indicies of zero ranges in a zero-one matrix - arrays

I am using Matlab for one of my projects. I am actually stuck at a point since some time now. Tried searching on google, but, not much success.
I have an array of 0s and 1s. Something like:
A = [0,0,0,1,1,1,1,1,0,0,1,1,1,1,1,1,0,0,0,0,0,1,1,1,0,0,0,0];
I want to extract an array of indicies: [x_1, x_2, x_3, x_4, x_5, ..]
Such that x_1 is the index of start of first range of zeros. x_2 is the index of end of first range of zeros.
x_3 is the index of start of second range of zeros. x_4 is the index of end of second range of zeros.
For the above example:
x_1 = 1, x_2 = 3
x_3 = 9, x_4 = 10
and so on.
Of course, I can do it by writing a simple loop. I am wondering if there is a more elegant (vectorized) way to solve this problem. I was thinking about something like prefix some, but, no luck as of now.
Thanks,
Anil.

The diff function is great for this sort of stuff and pretty quick.
temp = diff(A);
Starts = find([A(1) == 0, temp==-1]);
Ends = find([temp == 1,A(end)==0])
Edit: Fixed the error in the Ends calculation caught by gnovice.

Zeros not preceded by other zeros: A==0 & [true A(1:(end-1))~=0]
Zeros not followed by other zeros: A==0 & [A(2:end)~=0 true]
Use each of these plus find to get starts and ends of runs of zeros. Then, if you really want them in a single vector as you described, interleave them.

If you want to get your results in a single vector like you described above (i.e. x = [x_1 x_2 x_3 x_4 x_5 ...]), then you can perform a second-order difference using the function DIFF and find the points greater than 0:
x = find(diff([1 A 1],2) > 0);
EDIT:
The above will work for the case when there are at least 2 zeroes in every string of zeroes. If you will have single zeroes appearing in A, the above can be modified to handle them like so:
diffA = diff([1 A 1],2);
[~,x] = find([diffA > 0; diffA == 2]);
In this case, a single zero value will create repeated indices in x (i.e. if A starts with a single zero, then x(1) and x(2) will both be 1).

Related

How to optimize conditional statement in for loop over image?

I'm wondering if there's an indexable way of doing the following code on Octave, as it's iterative and thus really slow compared to working with indexation.
for i = [1:size(A, 1)]
for j = [1:size(A, 2)]
if (max(A(i, j, :)) == 0)
A(i, j, :) = B(i, j, :);
endif
endfor
endfor
A and B are two RGB images that overlaps and I want A(i,j) to have B(i,j) value if A(i,j) is 0 on all of the three channels. It is very slow in this form but I'm not experimented enough with this language to vectorize it.
Your code can be vectorized as follows:
I = max(A,[],3) == 0;
I = repmat(I,1,1,3);
A(I) = B(I);
The first line is a direct copy of your max conditional statement within the loop, but vectorized across all of A. This returns a 2D array, which we cannot directly use to index into the 3D arrays A and B, so we apply repmat to replicate it along the 3rd dimension (the 3 here is the number of repetitions, we're assuming A and B are RGB images with 3 elements along the 3rd dimension). Finally, an indexed assignment copies the relevant values over from B to A.
To generalize this to any array size, replace the "3" in the repmat statement with size(A,3).
Not adding much here, but perhaps this will give you a better understanding so worth adding another solution.
% example data
A = randi( 255, [2,4,3] ); A(2,2,:) = [0,0,0];
B = randi( 255, [2,4,3] );
% Logical array with size [Dim1, Dim2], such that Dim3 is 'squashed' into a
% single logical value at each position, indicating whether the third dimension
% at that position does 'not' have 'any' true (i.e. nonzero) values.
I = ~any(A, 3);
% Use this to index A and B for assignment.
A([I,I,I]) = B([I,I,I])
This approach may be more efficient than the repmat one, which is a slightly more expensive operation, but may be slightly less obvious to understand why it works. But. Understanding how this works teaches you something about matlab/octave, so it's a nice learning point.
Matlab and Octave store arrays in column major order (as opposed to, say, Python). This is also the reason that doing A(:) will return A as a vector, constructed in a column-by-column basis. It is also the reason that you can index a 3-dimensional array using a single index (called a "linear index"), which will correspond to the element you reach when you count that number of elements going down columns.
When performing logical indexing, matlab/octave effectively takes a logical vector, matches each linear index of that vector to the equivalent linear index of A and decides whether to return it or not, based on whether the boolean value of the logical index at that linear index is true or false. If you provide a logical array I that is of a smaller size than A, the indexing will simply stop at the last linear index of I. Specifically, note that the shape of I is irrelevant, since it will be interpreted in a linear indexing manner anyway.
In other words, logical indexing with I is the same as logical indexing with I(:), and logical indexing with [I,I,I] is the same as logical indexing with [ I(:); I(:); I(:) ].
And if I is of size A(:,:,1) then [I,I,I] is of size A(:,:,:), such that in a linear indexing sense it can be used as a valid logical index matching each linear index of I to the equivalent linear index of A.
The max() function can take a single matrix and return the maximum value along a dimension
There's also the all() function that tells you if all values along a dimension are nonzero, and the any() function that tells you if any of the values along a dimension are nonzero
A = reshape(1:75, 5, 5, 3)
A(2, 3, :) = 0;
B = ones(size(A)) * 1000
use_pixel_from_A = any(A, 3)
use_pixel_from_B = ~use_pixel_from_A
Now for each element of the 3rd axis, you know which pixels to take from A and which to take from B. Since our use_pixel... matrices contain 0 and 1, we can element-wise multiply them to A and B to filter out elements of A and B as required.
C = zeros(size(A));
for kk = 1:size(A, 3)
C(:, :, kk) = A(:, :, kk) .* use_pixel_from_A + B(:, :, kk) .* use_pixel_from_B
end

Find nearest smaller value in Matlab

I have two vectors of different size. Just as an example:
Triggs = [38.1680, 38.1720, 38.1760, 38.1800, 38.1840, 38.1880, 38.1920, 38.1960, 38.2000, 38.2040, 38.2080, 38.2120, 38.2160, 38.2200, 38.2240, 38.2280, 38.2320, 38.2360, 38.2400, 38.2440, 38.2480, 38.2520, 38.2560, 38.2600, 38.2640, 38.2680]
Peaks = [27.7920, 28.4600, 29.1360, 29.8280, 30.5200, 31.2000, 31.8920, 32.5640, 33.2600, 33.9480, 34.6520, 35.3680, 36.0840, 36.7680, 37.5000, 38.2440, 38.9920, 39.7120, 40.4160, 41.1480, 41.8840, 42.5960, 43.3040, 44.0240, 44.7160, 45.3840, 46.1240, 46.8720, 47.6240, 48.3720, 49.1040, 49.8080, 50.5200, 51.2600]
For each element in Triggs I need to find the nearest smaller element in Peaks.
That is, if Triggs(1) == 38.1680, I need to find the column number equal to Peaks(15) (the 15th element of Peaks).
Just to be 100% clear, the closest element of course could be the next one, that is 38.2440. That would not be ok for me. I will always need the one to the left of the array.
So far I have this:
for i = 1:length(triggersStartTime)
[~,valuePosition] = (min(abs(Peaks-Triggs(i))))
end
However, this could give me the incorrect value, that is, one bigger than Triggs(i), right?
As a solution I was thinking I could do this:
for i = 1:length(Triggs)
[~,valuePosition] = (min(abs(Peaks-Triggs(i))))
if Peaks(valuePosition) >= Triggs(i)
valuePosition = valuePosition-1
end
end
Is there a better way of doing this?
This can be done in a vectorized way as follows (note that the intermediate matrix d can be large). If there is no number satisfying the condition the output is set to NaN.
d = Triggs(:).'-Peaks(:); % matrix of pair-wise differences. Uses implicit expansion
d(d<=0) = NaN; % set negative differences to NaN, so they will be disregarded
[val, result] = min(d, [], 1); % for each column, get minimum value and its row index
result(isnan(val)) = NaN; % if minimum was NaN the index is not valid
If it is assured that there will always be a number satisfying the condition, the last line and the variable val can be removed:
d = Triggs(:).'-Peaks(:); % matrix of pair-wise differences. Uses implicit expansion
d(d<=0) = NaN; % set negative differences to NaN, so they will be disregarded
[~, result] = min(d, [], 1); % for each column, get row index of minimum value
I think this should help you:
temp=sort(abs(Peaks-Triggs));
lowest=find(abs(Peaks-Triggs)==temp(1))

Having trouble randomly generating numbers in multidimensional arrays

I'm trying to generate coordinates in a mulidimensional array.
the range for each digit in the coords is -1 to 1. <=> seems like the way to go comparing two random numbers. I'm having trouble because randomizing it takes forever, coords duplicate and sometimes don't fill all the way through. I've tried uniq! which only causes the initialization to run forever while it tries to come up with the different iterations.
the coords look something like this. (-1, 0, 1, 0, 0)
5 digits give position. I could write them out but I'd like to generate the coords each time the program is initiated. The coords would then be assigned to a hash tied to a key. 1 - 242.
I could really use some advice.
edited to add code. It does start to iterate but it doesn't fill out properly. Short of just writing out an array with all possible combos and randomizing before merging it with the key. I can't figure out how.
room_range = (1..241)
room_num = [*room_range]
p room_num
$rand_loc_cords = []
def Randy(x)
srand(x)
y = (rand(100) + 1) * 1500
z = (rand(200) + 1) * 1000
return z <=> y
end
def rand_loc
until $rand_loc_cords.length == 243 do
x = Time.new.to_i
$rand_loc_cords.push([Randy(x), Randy(x), Randy(x), Randy(x), Randy(x)])
$rand_loc_cords.uniq!
p $rand_loc_cords
end
#p $rand_loc_cords
end
rand_loc
You are trying to get all possible permutations of -1, 0 and 1 with a length of 5 by sheer luck, which can take forever. There are 243 of them (3**5) indeed:
coords = [-1,0,1].repeated_permutation(5).to_a
Shuffle the array if the order should be randomized.

Comparing two arrays of pixel values, and store any matches

I want to compare the pixel values of two images, which I have stored in arrays.
Suppose the arrays are A and B. I want to compare the elements one by one, and if A[l] == B[k], then I want to store the match as a key value-pair in a third array, C, like so: C[l] = k.
Since the arrays are naturally quite large, the solution needs to finish within a reasonable amount of time (minutes) on a Core 2 Duo system.
This seems to work in under a second for 1024*720 matrices:
A = randi(255,737280,1);
B = randi(255,737280,1);
C = zeros(size(A));
[b_vals, b_inds] = unique(B,'first');
for l = 1:numel(b_vals)
C(A == b_vals(l)) = b_inds(l);
end
First we find the unique values of B and the indices of the first occurrences of these values.
[b_vals, b_inds] = unique(B,'first');
We know that there can be no more than 256 unique values in a uint8 array, so we've reduced our loop from 1024*720 iterations to just 256 iterations.
We also know that for each occurrence of a particular value, say 209, in A, those locations in C will all have the same value: the location of the first occurrence of 209 in B, so we can set all of them at once. First we get locations of all of the occurrences of b_vals(l) in A:
A == b_vals(l)
then use that mask as a logical index into C.
C(A == b_vals(l))
All of these values will be equal to the corresponding index in B:
C(A == b_vals(l)) = b_inds(l);
Here is the updated code to consider all of the indices of a value in B (or at least as many as are necessary). If there are more occurrences of a value in A than in B, the indices wrap.
A = randi(255,737280,1);
B = randi(255,737280,1);
C = zeros(size(A));
b_vals = unique(B);
for l = 1:numel(b_vals)
b_inds = find(B==b_vals(l)); %// find the indices of each unique value in B
a_inds = find(A==b_vals(l)); %// find the indices of each unique value in A
%// in case the length of a_inds is greater than the length of b_inds
%// duplicate b_inds until it is larger (or equal)
b_inds = repmat(b_inds,[ceil(numel(a_inds)/numel(b_inds)),1]);
%// truncate b_inds to be the same length as a_inds (if necessary) and
%// put b_inds into the proper places in C
C(a_inds) = b_inds(1:numel(a_inds));
end
I haven't fully tested this code, but from my small samples it seems to work properly and on the full-size case, it only takes about twice as long as the previous code, or less than 2 seconds on my machine.
So, if I understand your question correctly, you want for each value of l=1:length(A) the (first) index k into B so that A(l) == B(k). Then:
C = arrayfun(#(val) find(B==val, 1, 'first'), A)
could give you your solution, as long as you're sure that every element will have a match. The above solution would fail otherwise, complaning that the function returned a non-scalar (because find would return [] if no match is found). You have two options:
Using a cell array to store the result instead of a numeric array. You would need to call arrayfun with 'UniformOutput', false at the end. Then, the values of A without matches in B would be those for which isempty(C{i}) is true.
Providing a default value for an index into A with no matches in B (e.g. 0 or NaN). I'm not sure about this one, but I think that you would need to add 'ErrorHandler', #(~,~) NaN to the arrayfun call. The error handler is a function that gets called when the function passed to arrayfun fails, and may either rethrow the error or compute a substitute value. Thus the #(~,~) NaN. I am not sure that it would work, however, since in this case the error is in arrayfun and not in the passed function, but you can try it.
If you have the images in arrays A & B
idx = A == B;
C = zeros(size(A));
C(idx) = A(idx);

vectorize a filter to a subsequence of an array in Matlab

I have a vector,"a", and a filter,"b".Both of those vectors contain only 0 or 1.
I would like to transform "a" such that any sequence of 1 only starts when b is at 1.
I have illustrated this using a loop but, as my vectors are huge, it is extremely inefficient.
The result I would like is stored in "r".
a=[0;0;1;1;1;1;1;1;0;0;1;1;0;0;1;1;1;1;1];
b=[0;0;0;0;1;0;1;0;0;1;0;1;0;1;1;0;0;0;0];
r=[0;0;0;0;1;1;1;1;0;0;0;1;0;0;1;1;1;1;1];
for i=2:length(a)
if a(i)==1 &&a(i-1)==0 && b(i)==0
a(i)=a(i-1);
end
end
assert(sum(a==r)==length(a))
Here's a two-liner:
r = a;
r([false; diff(a)>0 & b(2:end)==0]) = 0;
Please note that you need to adapt the code for row vectors (this works for column vectors).

Resources