Histogram of subblock matrix - matlab

Given some matrix, I want to divide it into blocks of size 2-by-2 and show a histogram for each of the blocks. The following is the code I wrote to solve the problem, but the sum of the histograms I'm generating is not the same as the histogram of the whole matrix. Actually the the sum of the blocks' histograms is double what I expected. What am I doing wrong?
im =[1 1 1 2 0 6 4 3; 1 1 0 4 2 9 1 2; 1 0 1 7 4 3 0 9; 2 3 4 7 8 1 1 4; 9 6 4 1 5 3 1 4; 1 3 5 7 9 0 2 5; 1 1 1 1 0 0 0 0; 1 1 2 2 3 3 4 4];
display(imhist(im));
[r c]=size(im);
bs = 2; % Block Size (8x8)
nob=[r c ]./ bs; % Total number of Blocks
% Dividing the image into 8x8 Blocks
kk=0;
for k=1:nob/2
for i=1:(r/bs)
for j=1:(c/bs)
Block(:,:,kk+j)=im((bs*(i-1)+1:bs*(i-1)+bs),(bs*(j-1)+1:bs*(j-1)+bs));
count(:,:,kk+j)=sum(sum(sum(hist(Block(:,:,kk+j)))));
p=sum(count(:,:,kk+j));
end
kk=kk+(r/bs);
end
end

The reason they aren't the same is because you use imhist for im and hist for the blocks. Hist separates data into 10 different bins based on your data range, imhist separates data based on the image type. Since your arrays are doubles, the imhist bins are from 0 to 1.0 Thats why your imhist has only values at 0, and 1. The hist produces bins based on your data range, so it will actually change slightly depending on what value you pass in. So you cant simply add bins together. Even though they are the same size vector 10x1 , the values in them can be very different. in one set bin(1) can be the range 1-5 but in another set of data bin(1) could be 1-500.
To fix all these issues I used imhist, and converted your data to uint8. At the very end I subtract the two histograms from one another and get zero, this shows that they are indeed the same
im =uint8([1 1 1 2 0 6 4 3 ;
1 1 0 4 2 9 1 2 ;
1 0 1 7 4 3 0 9 ;
2 3 4 7 8 1 1 4 ;
9 6 4 1 5 3 1 4 ;
1 3 5 7 9 0 2 5 ;
1 1 1 1 0 0 0 0 ;
1 1 2 2 3 3 4 4 ]);
orig_imhist = imhist(im);
%% next thing
[r c]=size(im);
bs=2; % Block Size (8x8)
nob=[r c ]./ bs; % Total number of Blocks
%creates arrays ahead of time
block = uint8(zeros(bs,bs,nob(1)*nob(2)));
%we use 256, because a uint8 has 256 values, or 256 'bins' for the
%histogram
block_imhist = zeros(256,nob(1)*nob(2));
sum_block_hist = zeros(256,1);
% Dividing the image into 2x2 Blocks
for i = 0:nob(1)-1
for j = 0:nob(2)-1
curr_block = i*nob(1)+(j+1);
%creates the 2x2 block
block(:,:,curr_block) = im(bs*i+1:bs*i+ bs,bs*j+1:bs*j+ bs);
%creates a histogram for the block
block_imhist(:,curr_block) = imhist(block(:,:,curr_block));
%adds the current histogram to the running sum
sum_block_hist = sum_block_hist + block_imhist(:,curr_block);
end
end
%shows that the two are the same
sum(sum(orig_imhist-sum_block_hist))
if my solution solves your problem please mark it as the answer

Related

Splitting Groups in MATLAB

Say I have the following matrix:
A(:,1) = [-5 -5 5 5 -5 -5 -5 -5 5 5 5 5]';
A(:,2) = [8 7 3 6 5 9 8 7 9 4 3 2 ]';
I'd like to split it into 4 groups based on the signs in the first column (i.e. everything prior to a sign flip is one group):
1) -5 -5
2) 5 5
3) -5 -5 -5 -5
4) 5 5 5 5
and the corresponding grouping in the second column would then be:
1) 8 7
2) 3 6
3) 5 9 8 7
4) 9 4 3 2
My intuition is to use
diff(A(:,1)) ~= 0
as the first step, but I am unsure how to continue from there. Any help would be appreciated, thanks!
You can use accumarray to create this cell array for you. We first need to assign a unique value to each "group" of consecutive numbers which share a sign. We can then use accumarray to place all elements in a given group into an element of a cell array.
A = cat(1, [-5 -5 5 5 -5 -5 -5 -5 5 5 5 5], [8 7 3 6 5 9 8 7 9 4 3 2 ]).';
% Compute the sign of each element: -1 for negative, 1 for positive
% Repeat first element for diff
S = sign(A([1 1:end],1));
% -1 -1 -1 1 1 -1 -1 -1 -1 1 1 1 1
% Compute element-by-element differences
D = diff(S);
% 0 0 2 0 -2 0 0 0 2 0 0 0
% Convert to a logical matrix which will make any non-zero 1 and any zero stays 0
L = logical(D);
% 0 0 1 0 1 0 0 0 1 0 0 0
% Take the cumulative sum (and add 1) to give each group of elements a unique number
subs = cumsum(L) + 1;
% 1 1 2 2 3 3 3 3 4 4 4 4
% Use this as the first input to accumarray and perform a given action on all elements in
% A(:,2) which share these values. Our action will be to convert to a cell array
result = accumarray(subs, A(:,2), [], #(x){x});
% result{1} =
% 8 7
%
% result{2} =
% 3 6
%
% result{3} =
% 5 9 8 7
%
% result{4} =
% 9 4 3 2
If we really want to reduce it so a single line we could do this.
accumarray(1 + cumsum(logical(diff(sign(A([1 1:end],1))))), A(:,2), [], #(x){x})
Use:
groupsSizes= diff([0;find(conv(A(:,1),[1,-1],'same')~=0)]);
firstGroup = mat2cell(A(:,1),groupsSizes,1);
secondGroup = mat2cell(A(:,2),groupsSizes,1);
Here's another way:
result = mat2cell(A(:), diff([0; find([diff(sign(A(:))); true])]));
This uses mat2cell to split A into pieces and put each into a cell. The length of the pieces is computed using sign to detect the sign and then diff and find to obtain the run lengths.

Identify adjacent superpixels iteratively

Let A be:
1 1 1 1 1 1
1 2 2 3 3 3
4 4 2 2 3 4
4 4 4 4 4 4
4 4 5 5 6 6
5 5 5 5 5 6
I need to identify a particular superpixel's adjacent pixels,
e.g.
The 1st adjacency of 2 is 1, 3, 4
The 2nd adjacency of 2 is 5, 6
The 3rd adjacency of 2 is ...
What is the FASTEST way to do it?
Assume you have a function adj(value), that has the code from your previous question.
sidenote: you probably would like that adj() function not to return the value of the pixel you are analyzing. you can make that easily.
you could do:
img=[your stuff];
imgaux=img;
ii=1;
val=2; %whatever value you want
while numel(unique(imgaux))>1 % Stop if the whole image is a single superpixel
adjacent{ii}=adj(val);
% expand the superpixel to the ii order of adjacency
for jj=1:size(adjacent{ii},1)
imgaux(imgaux==adjacent{ii}(jj))==val;
end
ii=ii+1;
end
Now size(adjacent,2) will be the amount of adjacency levels for that superpixel.
I am guessing this code is optimizable, I welcome any try for it!
Following Dan's suggestion on the comments, here is a possible implementation:
% Parameters
pixVal = 2;
adj = {};
prevMask = A == pixVal;
for ii = 1:length(A)
currMask = imdilate(prevMask, ones(2 * ii + 1));
adj{ii} = setdiff(unique(A(currMask & ~prevMask))', [adj{:}]);
if isempty(adj{ii})
break
end
prevMask = currMask;
end
Where pixVal is the pixel you want to look at.
Result:
>> adj{:}
ans =
1 3 4
ans =
5 6
ans =
Empty matrix: 1-by-0
Here's another approach reusing the code from your previous question:
%// create adjacency matrix
%// Includes code from #LuisMendo's answer
% // Data:
A = [ 1 1 1 1 1 1
1 2 2 3 3 3
4 4 2 2 3 4
4 4 4 4 4 4
4 4 5 5 6 6
5 5 5 5 5 6 ];
adj = [0 1 0; 1 0 1; 0 1 0]; %// define adjacency. [1 1 1;1 0 1;1 1 1] to include diagonals
nodes=unique(A);
J=zeros(numel(nodes));
for value=nodes.'
mask = conv2(double(A==value), adj, 'same')>0; %// from Luis' code
result = unique(A(mask)); %// from Luis' code
J(value,result)=1;
J(value,value)=0;
end
J is now the adjacency matrix for your matrix A and this becomes a graph problem. From here you would use the appropriate algorithm to find the shortest path. Path length of 1 is your "1st adjacency", path length of 2 is "2nd adjacency" and so on.
Dijkstra to find shortest path from a single node
Floyd-Warshall to find shortest paths from all the nodes
Breadth-first search for a single node, plus you can generate a handy tree
Update
I decided to play around with a custom Breadth-First Traversal to use in this case, and it's a good thing I did. It exposed some glaring errors in my pseudocode, which has been corrected above with working Matlab code.
Using your sample data, the code above generates the following adjacency matrix:
J =
0 1 1 1 0 0
1 0 1 1 0 0
1 1 0 1 0 0
1 1 1 0 1 1
0 0 0 1 0 1
0 0 0 1 1 0
We can then perform a depth-first traversal of the graph, putting each level of the breadth-first tree in a row of a cell array so that D{1} lists the nodes that have a distance of 1, D{2} has a distance of 2, etc.
function D = BFD(A, s)
%// BFD - Breadth-First Depth
%// Find the depth of all nodes connected to node s
%// in graph A (represented by an adjacency matrix)
A=logical(A); %// all distances are 1
r=A(s,:); %// newly visited nodes at the current depth
v=r; %// previously visited nodes
v(s)=1; %// we've visited the start node
D={}; %// returned Depth list
while any(r)
D(end+1,:)=find(r);
r=any(A(r,:))&~v;
v=r|v;
end
end
For a start node of 2, the output is:
>> D=BFD(J,2)
D =
{
[1,1] =
1 3 4
[2,1] =
5 6
}

Dividing a vector to form different matrices

I have a two long vector. Vector one contains values of 0,1,2,3,4's, 0 represent no action, 1 represent action 1 and 2 represent the second action and so on. Each action is 720 sample point which means that you could find 720 consecutive twos then 720 consecutive 4s for example. Vector two contains raw data corresponding to each action. I need to create a matrix for each action ( 1, 2, 3 and 4) which contains the corresponding data of the second vector. For example matrix 1 should has all the data (vector 2 data) which occurred at the same indices of action 1. Any Help??
Example on small amount of data:
Vector 1: 0 0 1 1 1 0 0 2 2 2 0 0 1 1 1 0 0 2 2 2
Vector 2: 6 7 5 6 4 6 5 9 8 7 9 7 0 5 6 4 1 5 8 0
Result:
Matrix 1:
5 6 4
0 5 6
Matrix 2:
9 8 7
5 8 0
Here is one approach. I used a cell array to store the output matrices, hard-coding names for such variables isn't a good plan.
V1=[0 0 1 1 1 0 0 2 2 2 0 0 1 1 1 0 0 2 2 2]
V2=[6 7 5 6 4 6 5 9 8 7 9 7 0 5 6 4 1 5 8 0]
%// Find length of sequences of 1's/2's
len=find(diff(V1(find(diff(V1)~=0,1)+1:end))~=0,1)
I=unique(V1(V1>0)); %// This just finds how many matrices to make, 1 and 2 in this case
C=bsxfun(#eq,V1,I.'); %// The i-th row of C contains 1's where there are i's in V1
%// Now pick out the elements of V2 based on C, and store them in cell arrays
Matrix=arrayfun(#(m) reshape(V2(C(m,:)),len,[]).',I,'uni',0);
%// Note, the reshape converts from a vector to a matrix
%// Display results
Matrix{1}
Matrix{2}
Since, there is a regular pattern in the lengths of groups within Vector 1, that could be exploited to vectorize many things while proposing a solution. Here's one such implementation -
%// Form new vectors out of input vectors for non-zero elements in vec1
vec1n = vec1(vec1~=0)
vec2n = vec2(vec1~=0)
%// Find positions of group shifts and length of groups
df1 = diff(vec1n)~=0
grp_change = [true df1]
grplen = find(df1,1)
%// Reshape vec2n, so that we end up with N x grplen sized array
vec2nr = reshape(vec2n,grplen,[]).' %//'
%// ID/tag each group change based on their unique vector 2 values
[R,C] = sort(vec1n(grp_change))
%// Re-arrange rows of reshaped vector2, s.t. same ID rows are grouped succesively
vec2nrs = vec2nr(C,:)
%// Find extents of each group & use those extents to have final cell array output
grp_extent = diff(find([1 diff(R) 1]))
out = mat2cell(vec2nrs,grp_extent,grplen)
Sample run for the given inputs -
>> vec1
vec1 =
0 0 1 1 1 0 0 2 2 2 ...
0 0 1 1 1 0 0 2 2 2
>> vec2
vec2 =
6 7 5 6 4 6 5 9 8 7 ...
9 7 0 5 6 4 1 5 8 0
>> celldisp(out)
out{1} =
5 6 4
0 5 6
out{2} =
9 8 7
5 8 0
Here is another solution:
v1 = [0 0 1 1 1 0 0 2 2 2 0 0 1 1 1 0 0 2 2 2];
v2 = [6 7 5 6 4 6 5 9 8 7 9 7 0 5 6 4 1 5 8 0];
m1 = reshape(v2(v1 == 1), 3, [])'
m2 = reshape(v2(v1 == 2), 3, [])'
EDIT: David's solution is more flexible and probably more efficient.

Greatest values in a matrix, row by row - matlab

I have an m-by-n matrix. For each row, I want to find the position of the k greatest values, and set the others to 0.
Example, for k=2
I WANT
[1 2 3 5 [0 0 3 5
4 5 9 3 0 5 9 0
2 6 7 1] 0 6 7 0 ]
You can achieve it easily using the second output of sort:
data = [ 1 2 3 5
4 5 9 3
2 6 7 1 ];
k = 2;
[M N] = size(data);
[~, ind] = sort(data,2);
data(repmat((1:M).',1,N-k) + (ind(:,1:N-k)-1)*M) = 0;
In the example, this gives
>> data
data =
0 0 3 5
0 5 9 0
0 6 7 0
You can use prctile command to find the threshold per-line.
prctile returns percentiles of the values in the rows of data and thus can be easily tweaked to return the threshold value above which the k-th largest elements at each row exist:
T = prctile( data, 100*(1 - k/size(data,2)), 2 ); % find the threshold
out = bsxfun(#gt, data, T) .* data; % set lower than T to zero
For the data matrix posted in the question we get
>> out
out =
0 0 3 5
0 5 9 0
0 6 7 0

Find Value at a given Orientation in Matrix

In Matlab I've matrix where, in a previous stage of my code, an specific element was chosen. From this point of the matrix I would like to find a maximum, not just the maximum value between all its surounding neighbours for a given radius, but the maximum value at a given angle of orientation. Let me explain this with an example:
This is matrix A:
A =
0 1 1 1 0 0 9 1 0
0 2 2 4 3 2 8 1 0
0 2 2 3 3 2 2 1 0
0 1 1 3 2 2 2 1 0
0 8 2 3 3 2 7 2 1
0 1 1 2 3 2 3 2 1
The element chosen in the first stage is the 4 in A(2,4), and the next element should be the maximum value with, for example, a 315 degrees angle of orientation, that is the 7 in A(5,7).
What I've done is, depending on the angle, subdivide matrix A in different quadrants and make a new matrix (an A's submatrix) with only the values of that quadrant.
So, for this example, the submatrix will be A's 4th quadrant:
q_A =
4 3 2 8 1 0
3 3 2 2 1 0
3 2 2 2 1 0
3 3 2 7 2 1
2 3 2 3 2 1
And now, here is my question, how can I extract the 7?
The only thing I've been able to do (and it works) is to find all the values over a threshold value and then calculate how those points are orientated. Then, saving all the values that have a similar orientation to the given one (315 degrees in this example) and finally finding the maximum among them. It works but I guess there could be a much faster and "cleaner" solution.
This is my theory, but I don't have the image processing toolbox to test it. Maybe someone who does can comment?
%make (r,c) the center by padding with zeros
if r > size(A,1)/2
At = padarray(A, [size(A,1) - r], 0, 'pre');
else
At = padarray(A, [r-1], 0 'post');
if c > size(A,2)/2
At = padarray(At, [size(A,2) - c], 0, 'pre');
else
At = padarray(At, [c-1], 0 'post');
%rotate by your angle (maybe this should be -angle or else 360-angle or 2*pi-angle, I'm not sure
Ar = imrotate(At,angle, 'nearest', 'loose'); %though I think nearest and loose are defaults
%find the max
max(Ar(size(Ar,1)/2, size(Ar,2)/2:end); %Obviously you must adjust this to handle the case of odd dimension sizes.
Also depending on your array requirements, padding with -inf might be better than 0
The following is a relatively inexpensive solution to the problem, although I found wrapping my head around the matrix coordinate system a real pain, and there is probably room to tidy it up somewhat. It simply traces all matrix entries along a line around the starting point at the supplied angle (all coordinates and angles are of course based on matrix index units):
A = [ 0 1 1 1 0 0 9 1 0
0 2 2 4 3 2 8 1 0
0 2 2 3 3 2 2 1 0
0 1 1 3 2 2 2 1 0
0 8 2 3 3 2 7 2 1
0 1 1 2 3 2 3 2 1 ];
alph = 315;
r = 2;
c = 4;
% generate a line through point (r,c) with angle alph
[nr nc] = size(A);
x = [1:0.1:nc]; % overkill
m = tan(alph);
b = r-m*c;
y = m*x + b;
crd = unique(round([y(:) x(:)]),'rows');
iok = find((crd(:,1)>0) & (crd(:,1)<=nr) & (crd(:,2)>0) & (crd(:,2)<=nc));
crd = crd(iok,:);
indx=sub2ind([nr,nc],crd(:,1),crd(:,2));
% find max and position of max
[val iv]=max(A(indx)); % <-- val is the value of the max
crd(iv,:) % <-- matrix coordinates (row, column) of max value
Result:
val =
7
iv =
8
ans =
5 7