How can I turn Structure to n-dimensional matrix - matlab

I have 2 matrices.
First one is the names.
Names={'a','b','c'};
Second one is Numbers.
a=[1 3]; b=[4]; c=[2 4 5];
Then i have the structure names which is combination of names and numbers, and they are equal some random matrices with equal rows and columns.
For this case i have 6 combination (2*1*3) and it looks like =
a1.b4.c2=[7 8 9; 10 11 14];
a1.b4.c4=[2 4 5; 3 4 7];
a1.b4.c5=[3 2 11; 4 7 8];
a3.b4.c2=[1 1 1; 3 5 12];
a3.b4.c4=[2 7 9 ; 10 11 12];
a3.b4.c5=[4 2 7 ; 5 6 8];
I want to return this into n-dimensional matrix. In this case it is 5-dimensional which has to look like this;
(:,:,1,4,2)=[7 8 9; 10 11 14]; %%%% for a=1 b=4 c=2
(:,:,1,4,4)=[2 4 5; 3 4 7]; %%%% for a=1 b=4 c=4
(:,:,1,4,5)=[3 2 11; 4 7 8]; %%%% for a=1 b=4 c=5
(:,:,3,4,2)=[1 1 1; 3 5 12]; %%%% for a=3 b=4 c=2
(:,:,3,4,4)=[2 7 9 ; 10 11 12]; %%%% for a=3 b=4 c=4
(:,:,3,4,5)=[4 2 7 ; 5 6 8]; %%%% for a=3 b=4 c=5
I want to write a generalized code that helps me do this job for different numbers of names and numbers yet I couldnt do it. Hope you can help me! Thanks.

To get every combination of the possible numbers for each fields, use ndgrid.
[Numbers{1:3}] = ndgrid(a,b,c);
Having the numbered structures sitting in the workspace as you describe makes it messy to access them programmatically and you should avoid it if possible; however they can still be accessed using eval.
evalPattern = strjoin(strcat(Names,'%d'), '.'); % 'a%d.b%d.c%d'
firstNumbers = cellfun(#(n) n(1), Numbers); % [1 4 2]
firstElement = eval(sprintf(evalPattern,firstNumbers)); % returns the value of a1.b4.c2
result = nan([size(firstElement) size(Numbers{1}]);
for ii = 1:numel(Numbers{1})
iiNumbers = cellfun(#(n) n(ii), Numbers);
result(:,:,ii) = eval(sprintf(evalPattern,iiNumbers));
end

Ok, it took longer than I expected, but the following code should work for arbitrary number of names and numbers, given the following requirements:
At the moment, names are considered to be single characters - that could be tweaked by regexp or something like this.
All your ax.bx.cx.... can be stored in some superordinated structure by your application (beforehand).
Your struct always follows the presented order of ax.bx.cx..., and the matrix dimensions are equal.
So, the script is quite long and - I'm afraid - needs some explanation. Please just ask. The basic idea is to loop through the struct(s) as long as the particular "children" are still structs. That assures arbitrary "depth" of structs, i.e. number of names and numbers.
I expanded your data, so you see, that it also works for (a) additional names, (b) additional numbers, and (c) different matrix sizes. Of course, it also works on your original data.
Also, one doesn't need Names or Numbers in the beginning as these information are automatically extracted from the (has to be there superordinated) structure.
(Attention: Written in Octave. I tried to verify, that all functionality is available in Matlab, too. Please report any issues, if that's not the case. I will then refactor the code.)
% Structs given.
a1.b4.c2.d3 = ones(4, 4);
a1.b4.c4.d3 = ones(4, 4) * 2;
a1.b4.c5.d3 = ones(4, 4) * 3;
a1.b6.c2.d3 = ones(4, 4) * 4;
a1.b6.c4.d3 = ones(4, 4) * 5;
a1.b6.c5.d3 = ones(4, 4) * 6;
a2.b4.c2.d3 = ones(4, 4) * 7;
a2.b4.c4.d3 = ones(4, 4) * 8;
a2.b4.c5.d3 = ones(4, 4) * 9;
a2.b6.c2.d3 = ones(4, 4) * 10;
a2.b6.c4.d3 = ones(4, 4) * 11;
a2.b6.c5.d3 = ones(4, 4) * 12;
% REQUIREMENT: Store your structs in some superordinated struct.
super.a1 = a1;
super.a2 = a2;
% Initialize combined struct for names and numbers.
NamesNumbers = struct();
% Initialize Names cell array.
Names = {};
% Extract names and numbers from superordinated struct.
totalNames = 0;
totalNumbers = 1;
current = super;
while (isstruct(current))
fields = fieldnames(current);
totalNames = totalNames + 1;
totalNumbers = totalNumbers * numel(fields);
for iField = 1:numel(fields)
field = fields{iField};
name = field(1);
Names{totalNames} = name;
number = field(2:end);
if (isfield(NamesNumbers, name) == false)
NamesNumbers.(name) = str2num(number);
else
NamesNumbers.(name) = [NamesNumbers.(name) str2num(number)];
end
end
current = current.(fields{1});
if (isstruct(current) == false)
[nRows, nCols] = size(current);
end
end
% Extract all values from superordinated struct.
level = struct2cell(super);
while (isstruct([level{:}]))
level = struct2cell([level{:}]);
end
values = vertcat(level{:});
% Determine indices.
maxIdx = cellfun(#(x) max(x), struct2cell(NamesNumbers));
idx = zeros([totalNumbers, totalNames]);
factorProd = 1;
for iName = 1:totalNames
numbers = NamesNumbers.(Names{iName});
n = numel(numbers);
factorProd = factorProd * n;
inner = totalNumbers / factorProd;
resh = totalNumbers * n / factorProd;
outer = factorProd / n;
column = repmat(reshape(repmat(numbers, inner, 1), resh, 1), outer, 1);
START = (iName - 1) * totalNumbers + 1;
STOP = iName * totalNumbers;
idx(START:STOP) = column;
end
% Initialize output.
output = zeros([nRows nCols maxIdx']);
% Fill output with values.
for iIdx = 1:size(idx, 1)
temp = num2cell(idx(iIdx, :));
START = (iIdx - 1) * nRows + 1;
STOP = iIdx * nRows;
output(:, :, temp{:}) = values(START:STOP, :);
end

Related

Extract column data of table in a struct array

Summary / TLDR
How do I extract all rows of one table-column if the table is in a struct array and I want to combine alle struct-array-elements to one big matrix?
First Approch
I have a table (Values) with multiple columns and rows stored in a struct. Multiple of these structs are stored in an array (Data):
Data(1).Values = table([1; 2; 3; 4; 8], 'VariableNames', {'Rk'});
Data(2).Values = table([3; 6; 10; 8], 'VariableNames', {'Rk'});
Data(3).Values = table([2; 10; 11; 7], 'VariableNames', {'Rk'});
There are many more variables in the struct and also in the table, so it is just a simplified example. As you can see the height of the table can vary. I want to plot the columns Rk in a boxplot. So i need to create a matrix like this:
matrix = [1 1; 2 1; 3 1; 4 1; 8 1; 3 2; 6 2; 10 2; 8 2; 2 3; 10 3; 11 3; 7 3];
I use the following code to create the matrix:
matrix = zeros(0, 2);
for i=1:length(Data)
l = height(Data(1, i).Values(:, 'Rk'));
e = length(matrix) + 1;
% Reshape the data into an array
matrix((end+1):(end+l), 1) = table2array(Data(1, i).Values(:, 'Rk'));
% Creating the index of each data-row
matrix(e:end, 2) = i;
end
% Plot the boxplot
boxplot(matrix(:, 1), matrix(:, 2))
I really don't like this for-loop-version, especially because it becomes slow for big Data-Arrays and also because I don't know the size of matrix, so I can't reserve the space. Theoretically I could run through the whole data-array, counting the elements, initalizing the matrix-variable and then fill it.
Is there a more elegant version without a for-loop?
Second approach
I already tried another solution by changing the structure of the struct. Semantically, this really makes no sense, but this way I found a more elegant solution, creating the matrix-Variable without the problems of the first solution:
% Creating Data
Data(1).Values.Rk = [1; 2; 3; 4; 8];
Data(2).Values.Rk = [3; 6; 10; 8];
Data(3).Values.Rk = [2; 10; 11; 7];
% Reshape the data into an array
a = {cell2mat({Data.Values}).Rk};
b = vertcat(a{:});
% Creating the index of each data (b)-row
c = cumsum(cellfun('length', a(1, :)));
d = meshgrid(1:c(end), 1:length(c));
e = d>c';
f = sum(e);
% Plot the boxplot
boxplot(b, f);
Questions
I would apreciate a solution combining both approaches (having a table, no for-loop, no need of matrix-size-calculation) but:
I don't know how to extract the data of the table in a struct in an array.
I am asking myself if there is a more elegant solution creating the boxplot-indexes.
Whole code
%% Boxplot of Table
clear
% Creating Data
Data(1).Values = table([1; 2; 3; 4; 8], 'VariableNames', {'Rk'});
Data(2).Values = table([3; 6; 10; 8], 'VariableNames', {'Rk'});
Data(3).Values = table([2; 10; 11; 7], 'VariableNames', {'Rk'});
matrix = zeros(0, 2);
for i=1:length(Data)
l = height(Data(1, i).Values(:, 'Rk'));
e = length(matrix) + 1;
% Reshape the data into an array
matrix((end+1):(end+l), 1) = table2array(Data(1, i).Values(:, 'Rk'));
% Creating the index of each data
matrix(e:end, 2) = i;
end
boxplot(matrix(:, 1), matrix(:, 2));
%% Boxplot of Arrays
clear
% Creating Data
Data(1).Values.Rk = [1; 2; 3; 4; 8];
Data(2).Values.Rk = [3; 6; 10; 8];
Data(3).Values.Rk = [2; 10; 11; 7];
% Reshape the data into an array
a = {cell2mat({Data.Values}).Rk};
b = vertcat(a{:});
% Creating the index of each data (b)-row
c = cumsum(cellfun('length', a(1, :)));
d = meshgrid(1:c(end), 1:length(c));
e = d>c';
f = sum(e);
% Plot the boxplot
boxplot(b, f);
I have a partial answer if you are allowed to change the dimensions on your input table. If you transpose your input values, such that you have a row vector
Data(1).Values.Rk = [1; 2; 3; 4; 8]';
...
You can use the following commands to concatenate all the elements:
tmp = [Data.Values];
allvals = [tmp.Rk]'; %output is a column vector of your aggregated values
If you eliminate the Rk field (if it is not informative), you don't need the first step and can do the operation in one step.
If you aggregate the values into a column vector in this manner you now know the dimensions of the second column and can initialize that column before executing a for loop to populate the second column with a monotonically increasing index for each element of data.
I cannot think of a way to get the total number of elements and corresponding value for your box plot in the second column (without a for loop) unless you have the flexibility to add another field to your data structure (e.g. Data(1).nVals = 5, etc.), in which case you can get the total number of elements via sum([Data.nVals])

copy move forgery detection stuck with algorithm

I am trying to implement the paper detection of copy move forgery using histogram of oriented gradients.
The algorithm is:
Divide the image into overlapping blocks.
Calculate feature vectors for each block and store them in a matrix.
Sorting the matrix lexicographically
Using block matching to identify forged regions.
https://www.researchgate.net/publication/276518650_Detection_of_copy-move_image_forgery_using_histogram_of_orientated_gradients
I am stuck with the 3rd step and can't proceed.
The code I have implemented is:
clc;
clear all;
close all;
%read image
img = imread('006_F.png');
img=rgb2gray(img);
img=imresize(img, 1/4);
figure(1);
imshow(img);
b=16; %block size
nrc=5; %no. of rows to check
td=416; %threshold
[r, c]=size(img);%Rows and columns;
column=(r-b+1)*(c-b+1);
M= zeros(column,4);
Mi = zeros(1,2);
i=1;
disp('starting extraction of features');
for r1 = 1:r-b+1
for c1 = 1:c-b+1
% Extract each block
B = img(r1:r1+b-1,c1:c1+b-1);
features = extractHOGFeatures(B);%extracting features
M(i, :) = features;
Mi(i,:) = [r1 c1];
i=i+1;
end
end
[S, index] = sortrows(M , [ 1 2 3 4]);
P= zeros(1,6);
b2=r-b+1;
disp('Finding Duplicates');
for i = 1:column
iv = index(i);
xi=mod(iv,b2) + 1;
yi=ceil(iv/b2);
j = i+1;
while j < column && abs(i - j) < 5
jv=index(j);
xj=mod(jv,b2) + 1;
yj=ceil(jv/b2);
z=sqrt(power(xi-xj,2) + power(yi-yj,2));
% only process those whose size is above Nd
if z > 16
offset = [xi-xj yi-yj];
P = [P;[xi yi xj yj xi-xj yi-yj]];
end
j = j + 1;
end
end
rows = size(P,1);
P(:,6) = P(:,6) - min(P(:,6));
P(:,5) = P(:,5) - min(P(:,5));
maxValP = max(P(:,6)) + 1;
P(:,5) = maxValP .* P(:,5) + P(:,6);
mostfrequentval = mode(P(:,5));
disp('Creating Image');
idx = 2;
% Create a copy of the image and mask it
RI = img;
while idx < rows
x1 = P(idx,1);
y1 = P(idx,2);
x2 = P(idx,3);
y2 = P(idx,4);
if (P(idx,5) == mostfrequentval)
RI(y1:y1,x1:x1) = 0;
RI(y2:y2,x2:x2) = 0;
end
idx = idx + 1;
end;
After going through some references indicated in the paper you are working on (ref. [8] and [20]):
The lexicographic sorting is the equivalent of the alphabetical one, for numbers i.e., [1 1 1 1] < [1 1 2 1] < [2 3 4 5] < [2 4 4 5]
So, in your case, you case use the function sortrows() in the following way:
A = [1 1 1 1;1 1 1 2;1 1 1 4;1 2 2 2; 1 2 2 1; 1 4 6 3; 2 3 4 5; 2 3 6 6]; % sample matrix
[B,idx] = sortrows(A,[1 2 3 4]); % Explicit notation but it is the Matlab default setting so equivalent to sortrows(A)
It means: Sort the rows of A by first looking at the first column and, in case of equality, looking at the second one, and so on.
If your are looking for a reverse order, you specify '-' before the number of the column.
So in the end, your code is good and if the results are not as expected it has to come from another step of the implementation...
Edit: the parameter idx records the original index of the sorted rows.

How to oppositely order two vectors in Matlab?

I have the code below for oppositely ordering two vectors. It works, but I want to specify the line
B_diff(i) = B(i) - B(i+1);
to hold true not just for only
B_diff(i) = B(i) - B(i+1); but for
B_diff(i) = B(i) - B(i+k); where k can be any integer less than or equal to n. The same applies to "A". Any clues as to how I can achieve this in the program?
For example, I want to rearrange the first column of the matrix
A =
1 4
6 9
3 8
4 2
such that, the condition should hold true not only for
(a11-a12)(a21-a22)<=0;
but also for all
(a11-a13)(a21-a23)<=0;
(a11-a14)(a21-a24)<=0;
(a12-a13)(a22-a23)<=0;
(a12-a14)(a22-a24)<=0; and
(a13-a14)(a23-a24)<=0;
## MATLAB CODE ##
A = xlsread('column 1');
B = xlsread('column 2');
n = numel(A);
B_diff = zeros(n-1,1); %Vector to contain the differences between the elements of B
count_pos = 0; %To count the number of positive entries in B_diff
for i = 1:n-1
B_diff(i) = B(i) - B(i+1);
if B_diff(i) > 0
count_pos = count_pos + 1;
end
end
A_desc = sort(A,'descend'); %Sort the vector A in descending order
if count_pos > 0 %If B_diff contains positive entries, divide A_desc into two vectors
A_less = A_desc(count_pos+1:n);
A_great = sort(A_desc(1:count_pos),'ascend');
A_new = zeros(n,1); %To contain the sorted elements of A
else
A_new = A_desc; %This is then the sorted elements of A
end
if count_pos > 0
A_new(1) = A_less(1);
j = 2; %To keep track of the index for A_less
k = 1; %To keep track of the index for A_great
for i = 1:n-1
if B_diff(i) <= 0
A_new(i+1) = A_less(j);
j = j + 1;
else
A_new(i+1) = A_great(k);
k = k + 1;
end
end
end
A_diff = zeros(n-1,1);
for i = 1:n-1
A_diff(i) = A_new(i) - A_new(i+1);
end
diff = [A_diff B_diff]
prod = A_diff.*B_diff
The following code orders the first column of A opposite to the order of the second column.
A= [1 4; 6 9; 3 8; 4 2]; % sample matrix
[~,ix]=sort(A(:,2)); % ix is the sorting permutation of A(:,2)
inverse=zeros(size(ix));
inverse(ix) = numel(ix):-1:1; % the un-sorting permutation, reversed
B = sort(A(:,1)); % sort the first column
A(:,1)=B(inverse); % permute the first column according to inverse
Result:
A =
4 4
1 9
3 8
6 2

Using accumarray and #min to extract min from groups but also output corresponding values from another variable/column

I have 3 columns of data:
time = [1;2;3;4;5;6;7;8;9;10;11;12;13;14;15;16];
category = [1;1;1;1;2;2;2;2;3; 3; 3; 3; 4; 4; 4; 4];
data = [1;1;0;1;2;2;1;2;3; 3; 2; 3; 4; 4; 4; 3];
I am using the following to extract the minimum data values for each category:
groupmin = accumarray(category,data,[],#min)
Which outputs:
groupmin = [0;1;2;3]
However, I would really like to have an output that also tells me which time point the minimums are from, e.g.
timeofgroupmin = [3;7;11;16]
groupmin = [0;1; 2; 3]
Alternatively, I would like to have the minimums output in a vector of their own, with NaNs for any row which was not the minimum of its group, e.g.
groupminallrows = [NaN;NaN;0;NaN;NaN;NaN;1;NaN;NaN;NaN;2;NaN;NaN;NaN;NaN;3];
Either approach would solve my problem. As a Matlab novice I'm struggling to know which terms to search for.
This works if all data of the same category are in a single run and the categories are sorted, as in your example. Several minimizers are allowed within each category.
r = accumarray(category,data,[],#(v) {(min(v)==v)});
r = vertcat(r{:});
groupminallrows = NaN(size(data));
groupminallrows(r) = data(r);
Try this solution:
% first we group the data into cell according to the group they belong to
grouped = accumarray(category, data, [], #(x){x});
% find the minimum and corresponding index of each group
[mn,idx] = cellfun(#min, grouped);
% fix index by offsetting the position to point the whole data vector
offset = cumsum([0;cellfun(#numel, grouped)]);
idx = idx + offset(1:end-1);
% result
[mn(:) idx(:)]
assert(isequal(mn, data(idx)))
% build the vector with NaNs
mnAll = nan(size(data));
mnAll(idx) = mn;
The resulting vectors:
>> mn'
ans =
0 1 2 3
>> idx'
ans =
3 7 11 16
>> mnAll'
ans =
NaN NaN 0 NaN NaN NaN 1 NaN NaN NaN 2 NaN NaN NaN NaN 3
EDIT:
Here is an alternate solution:
% find the position of min value in each category
idx = accumarray(category, data, [], #minarg);
% fix position in terms of the whole vector
offset = cumsum([0;accumarray(category,1)]);
idx = idx + offset(1:end-1);
% corresponding min values
mn = data(idx);
I'm using the following custom function to extract the second output argument from min:
minarg.m
function idx = minarg(X)
[~,idx] = min(X);
end
The results are the same as above.
Use accumarray with a custom function:
time = [1;2;3;4;5;6;7;8;9;10;11;12;13;14;15;16];
category = [1;1;1;1;2;2;2;2;3; 3; 3; 3; 4; 4; 4; 4];
data = [1;1;0;1;2;2;1;2;3; 3; 2; 3; 4; 4; 4; 3];
groupmin = accumarray( A(:,1), A(:,2), [], #min)
Is what you have, but to get the indices of the minima and their time you'd need the second output of the min function, which I don't know if it is possible to get when used with accumarray. But there is the following workaround:
groupidx = accumarray( category, data, [], #(x) find(x == min(x) )).'
occ = cumsum(hist(category,unique(category)))
idx = -occ(1)+occ+groupidx;
timeofgroupmin = time(idx).'
groupmin = data(idx).'
groupmin =
0 1 2 3
timeofgroupmin =
3 7 11 16
The desired NaN-vector you could get like:
groupminallrows = NaN(1,numel(data));
groupminallrows(idx) = data(idx)
Regarding your comment:
I assume the reason for that, is that you have multiple minima in each group, then find returns an array. To resolve that you can substitute find(x == min(x)) with find(x == min(x),1). But then you would just get the first occurance of every minimum in each group.
If that is not desired I'd say accumarray is generally the wrong way to go.

Copy vector to vector in Matlab

I have a simple question about how can I copy a vector into another. I have a vector with a length of 66x1 and then, another with a length of 2151x1. I want to copy the values from the first one in a exactly position on the other. I've tried that but it doesn't work.
inter= 66x1
out= 2151x1
for i=1:numel(inter)
out(101:167)= inter(i)
end
Also I've tried this:
for inter=(1:66);
out(101:167)=inter;
end
And this:
for k= (101:167)
out(k)=inter(1:66);
end
Am I doing wrong? Thanks in advance,
Let's say your vectors are
a = [1; 2; 3];
b = [4; 5; 6; 7; 8; 9];
for simplicity.
There is no need to use loops. You can just go ahead and do it like this:
startIdx = 2; %101 in your case
finalIdx = startIdx + size(a,1) - 1; % 166 in your case
b(startIdx:finalIdx) = a;
Then b would be:
b =
4
1
2
3
8
9
A very important point here is the -1 in finalIdx. You need to substract 1 from the final index.