1
votes

I have a matrix A with size 10x100 as shown below. What I want to do is:

  1. I'll work row by row in which for each row I'll check the data of each coloumn in this row
  2. Let's say I'm now in the first col cell in the first row. I'll check if the value is zero I'll move to the next col, and so on till I found a col having a non-zero value and save its col number e.g. col 3 "this means that col 1&2 were zeros"
  3. Now I'm in the first non zero col in row1, I'll move to the next col till I find a col with zero value. I'll fetch the col just before this zero one which must be a non-zero one and save it. e.g col 7 "this means that col4&5&6 are non-zeros and col8 is zero"
  4. Now I want to save the median middle col between this two columns e.g col3 and col7 then the middle col is col5 so I'll save the index row1_col5. if there are two middle values then any of them is fine.
  5. I'll then move to the next col till I find a non-zero col "do the same steps from 2-->5" till the first row is finished.
  6. Move to the next row and start over again from step 2-->5.
  7. There are two rules: -The first one is that I'll get the middle index of non-zero consecutive values only if there is a minimum of 3 non-zero consecutive values, if there are two non-zero consecutive value then the middle will not be calculated -The second one is that if the number of zero consecutive values are less than 3 then they will be ignored and will be considered as non-zero values. e.g in the below example the first row middle values are col5 and col11. In row2 col5 is counted, while no cols in row3 satisfy this conditions , and in row4 col6 or col7 will be counted.
  8. After finishing all the rows want to have a vector or array holding the positions of all the middle indexes e.g row1_col5 row1_col17 row2_col_10 and so on.

example:

A = [ 0 0 0 2 4 1 0 0 0 1 3 2;
      0 0 0 5 1 1 1 1 0 0 0 1;
      0 3 4 1 0 3 1 2 0 0 1 3;
      0 0 0 0 1 3 4 5 0 0 0 0];

for the first row the middle value will be 5 and 11 and so on So if anyone could please advise how can I do this with least processing as this can be done using loops but if there is more efficient way of doing it? Please let me know if any clarification is needed.

3
Do you also want to count cases where there's a non-zero value with a zero on either side?nkjt
@nkjt If you mean by this that the first or last col are non-zero, then yesTak

3 Answers

3
votes

Now you have clarified your question (again...) here is a solution (still using a for loop...). It includes "rule 7" - excluding runs of fewer than three elements; it also includes the second part of that rule - runs of fewer than three zeros don't count as zero. The new code looks like this:

A = [ 0 0 0 2 4 1 0 0 0 1 3 2;
      0 0 0 5 1 1 1 1 0 0 0 1;
      0 3 4 1 0 3 1 2 0 0 1 3;
      0 0 0 0 1 3 4 5 0 0 0 0];

 retVal = cell(1, size(A, 1));
 for ri = 1:size(A,1)
     temp = [1 0 0 0 A(ri,:) 0 0 0 1]; % pad ends with 3 zeros + 1
                                       % so that is always a "good run"
     isz = (temp == 0); % find zeros - pad "short runs of 0" with ones
     diffIsZ = diff(isz);
     f = find(diffIsZ == 1);     
     l = find(diffIsZ == -1);
     shortRun = find((l-f)<3); % these are the zeros that need eliminating

     for ii = 1:numel(shortRun)
         temp(f(shortRun(ii))+1:l(shortRun(ii))) = 1;
     end

     % now take the modified row:
     nz = (temp(4:end-3)~=0);
     dnz = diff(nz); % find first and last nonzero elements
     f = find(dnz==1); 
     l = find(dnz==-1);
     middleValue = floor((f + l)/2);
     rule7 = find((l - f) > 2);
     retVal{ri} = middleValue(rule7);
 end

You have to use a cell array for the return value since you don't know how many elements will be returned per row (per your updated requirement).

The code above returns the following cell array:

{[5 11], [6], [7], [7]}

I appear still not to understand your "rule 7", because you say that "no columns in row 3 satisfy this condition". But it seems to me that once we eliminate the short runs of zeros, it does. Unless I misinterpret how you want to treat a run of non-zero numbers that goes right to the edge (I assume that's OK - which is why you return 11 as a valid column in row 1; so why wouldn't you return 7 for row 3??)

1
votes

Try this:

sizeA = size(A);
N = sizeA(1);
D = diff([zeros(1, N); (A.' ~= 0); zeros(1,N)]) ~= 0;
[a b] = find(D ~= 0);

c = reshape(a, 2, []);
midRow = floor(sum(c)/2);
midCol = b(1:2:length(b))

After this, midRow and midCol contain the indices of your centroids (e.g. midRow(1) = 1, midCol(1) = 4 for the example matrix you gave above.

0
votes

If you don't mind using a for loop:

A = [ 0 0 1 1 1 0 1;
      0 0 0 0 0 0 0;
      0 1 1 1 1 0 0;
      0 1 1 1 0 1 1;
      0 0 0 0 1 0 0]; % data

sol = repmat(NaN,size(A,1),1);
for row = 1:size(A,1)
  [aux_row aux_col aux_val] = find(A(row,:));
  if ~isempty(aux_col)
    sol(row) = aux_col(1) + floor((find(diff([aux_col 0])~=1,1)-1)/2);
    % the final 0 is necessary in case the row of A ends with ones
    % you can use either "floor" or "ceil"
  end
end

disp(sol)

Try it and see if it does what you want. I hope the code is clear; if not, tell me