Error of Averaging a Large Vector in MATLAB - matlab

How do you accurately take the average of large sets of integers in MATLAB?
I have two large vectors (2672x4008 in dimensions) I am dealing with, each the result of pixels in an image. Hence, the resulting vector is filled with values 0 to 256, all integers. My problem is that I want an accurate value of the average intensity of these grey-scale images. To do this, I used the line
meanvalue = mean(I(:))
This yielded a value of meanvalue = 155.9335 in the output line of MATLAB.
Next, I added 20 to each value of the vector, as below (this should raise the intensity of the overall image, if I am understanding correctly).
Ipt = I + 20;
I then took the mean value of this new vector, Ipt
meanvaluept = mean(Ipt(:))
and matlab spat out a value of meanvaluept = 175.8916. I'm no math wizard, but I know enough to know that 175.8916 - 20 ≠ 155.9335.
Any help would be appreciated, either mathematically (how to increase the precision of MATLAB), or procedurally (there is some built-in function of MATLAB which will find the intensity).

Since you are referring to "grey-scale images", and you have integers in the range 0-255 (the 256 you mention must be a typo), my guess is that your I is of type uint8.
In this case, MATLAB uses saturated addition, in which results larger than 255 are clamped to 255. The effect you describe is caused by this saturated addition.
Here is an example:
>> I = uint8(randi(255,1000,1000));
>> mean( I(:)+20 )
ans =
147.1954
>> mean(I(:)) + 20
ans =
148.0151
The solution is to convert to doubles first:
>> mean( double(I(:)) + 20 )
ans =
148.0151

Have you checked the image datatype?
It's true that if your the mean of image I is
meanvalue = mean(I(:)) = 155.9335
and you added 20 to each pixels
Ipt = I + 20
you supposed to have
meanept = mean(Ipt(:)) = meanvalue + 20 = 175.9335
But, don't forget that image's datatype is uint8, which limit the pixels value to 0-255. It means if you added 20 to a pixel and its value is greater than 255, its value will set to 255, and the same if you substract some value and it's lower than 0.
Maybe, some of your pixels restricted to 255 when normally you'll have more than 255.
For example:
I have vector X in double
X = [1 1 1; ...
1 1 1; ...
1 1 240];
The mean of X is
mean(X(:)) = 27.5556
since
( 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 240)/9 = 27.5556
If I added 20 to each pixels
X20 = X + 20
= [(1 + 20) (1 + 20) (1 + 20); ...
(1 + 20) (1 + 20) (1 + 20); ...
(1 + 20) (1 + 20) (240 + 20)];
= [21 21 21; ...
21 21 21; ...
21 21 255];
Notice that X20(3,3) is 255, not 260. It cause
meanX20 = mean(X20(:)) = 47
but if I change X's datatype to double
X_double = double(X)
and added 20 to each pixels
X20_double = X_double + 20
= [(1 + 20) (1 + 20) (1 + 20); ...
(1 + 20) (1 + 20) (1 + 20); ...
(1 + 20) (1 + 20) (240 + 20)];
= [21 21 21; ...
21 21 21; ...
21 21 260];
and the average of X20_double is
X20_double_mean = mean(X20_double(:)) = 47.5556
See the difference?
The double X20's mean is 47.5556 and the uint8 X20's mean is 47.
I hope this will help :)

There is a very important note in your question :
Suppose I = [2 3;4 9]
meanvalue = mean(I(:)) = 4.5
When you add 20 with I , You will have :
Ipt = I + 20;
Ipt = [22 23;24 29]
so you add 20 to all elements in I, therefore your mean will increase 20 value.

Related

Issue trying to sub sample an image

I am trying to resize a given image by sub-sampling it. I am using grayscae iamges.
The way I understand sub-sampling is basically this:
Let's say we have an 5x5 image and we want to resize it by a factor of 2, so the output image will be 3x3.
So if the 5x5 is: (1 2 3 4 5; 6 7 8 9 10; 11 12 13 14 15; 16 17 18 19 20; 21 22 23 24 25)
Then the 3x3 will be: (1 3 5; 11 13 15; 21 23 25)
So if the factor is 2, for the first row we take the first sample and then every second sample and so on. The same for columns.
The code that I wrote for this is:
i = 0;
j = 0;
for row=1:old_rows
if mod((row-1), a) == 0
i = i + 1;
end
for col=1:old_cols
if mod((row-1), a) == 0 && mod((col-1), a) == 0
j = j + 1;
sub_sampled_I(i, j) = I(row, col); % I is the input image
end
end
j = 0;
end
sub_sampled_I = im2uint8(sub_sampled_I);
end
The issue is that the final image has nothing to do with the original. It is just a white image with some black points here and there. What I understand wrong about sub-sampling?

How to get a vector from the elements along a fixed dimension in a 3D array?

I have a 3D array in MATLAB like this:
val(:,:,1) =
1.1461 2.3993
2.3993 15.4036
val(:,:,2) =
1.0041 0.8106
0.8106 10.6503
val(:,:,3) =
1.0001 0.9895
0.9895 3.0384
val(:,:,4) =
1.0024 0.9936
0.9936 2.0169
It's a 2 x 2 x 600 array. I want the second element of each of the 600 "matrices". Is there a simple way to extract these in MATLAB?
The output I would desire is like this:
output = [ 2.3993; 0.8106; 0.9895; 0.9936 ];
My attempt so far has been the following:
val(1, 2, :)
But, this returns:
ans(:,:,1) =
2.3993
ans(:,:,2) =
0.8106
ans(:,:,3) =
0.9895
ans(:,:,4) =
0.9936
I need these values as a vector.
Your approach accessing val(1, 2, :) is correct. Nevertheless, the output produced has a size of 1 x 1 x 4. What you want to do is to remove the (unnecessary) dimensions of length 1. That is what the squeeze function is for.
Having a 3-dimensional array val like yours and fixed indices x, y for the first and second dimensions, we just surround your command with squeeze:
% 3-dimensional array
val = reshape(1:36, 3, 3, 4)
% Squeezed output for fixed x, y in dimensions 1 and 2
x = 1;
y = 2;
out3d = squeeze(val(x, y, :))
The output is the following:
val =
ans(:,:,1) =
1 4 7
2 5 8
3 6 9
ans(:,:,2) =
10 13 16
11 14 17
12 15 18
ans(:,:,3) =
19 22 25
20 23 26
21 24 27
ans(:,:,4) =
28 31 34
29 32 35
30 33 36
out3d =
4
13
22
31
This works for arbitrary dimensions and number of indices.
A 4-dimensional array with two fixed indices will produce a 2-dimensional output:
% 4-dimensional array
val = reshape(1:108, 3, 3, 4, 3)
% Squeezed output for fixed x, y in dimensions 1, 2
x = 1;
y = 2;
out4d = squeeze(val(x, y, :, :))
Output:
val = (omitted here)
out4d =
4 40 76
13 49 85
22 58 94
31 67 103
A 4-dimensional array with three fixed indices will again produce a 1-dimensional output:
% 4-dimensional array
val = reshape(1:108, 3, 3, 4, 3)
% Squeezed output for fixed x, y, z in dimensions 1, 2, 3
x = 1;
y = 2;
z = 1;
out4d = squeeze(val(x, y, z, :))
Output:
val = (omitted here)
out4d =
4
40
76
Hope that helps!

How to create a vector in Matlab with two different step sizes?

I want to create a vector in Matlab with two step sizes that will alternate.
vector =
0 50 51 101 102 152 etc.
So the step size is 50 and 1 which will alternate. How to write a script that will create this vector?
Code
N = 6; %// Number of elements needed in the final output
sz1 = 50; %// Stepsize - 1
sz2 = 4; %// Stepsize - 2
startval = 8; %// First element of the output
vector = reshape(bsxfun(#plus,[startval startval+sz1]',[0:N/2-1]*(sz1+sz2)),1,[])
Output
vector =
8 58 62 112 116 166
Note: For your problem, you need to use sz2 = 1 and startval = 0 instead.
Explanation
Internally it creates two matrices which when "flattened-out" to form vectors would resemble the two vectors that you have pointed out in the comments. You can get those two matrices with the following two sets of conditions.
Set #1: If you keep N = 6, sz1 = 50, sz2 = 0 and startval = 0 -
bsxfun(#plus,[startval startval+sz1]',[0:N/2-1]*(sz1+sz2))
gives us -
0 50 100
50 100 150
Set #2: If you keep N = 6, sz1 = 0, sz2 = 1 and startval = 0 -
bsxfun(#plus,[startval startval+sz1]',[0:N/2-1]*(sz1+sz2))
gives us -
0 1 2
0 1 2
Good thing about bsxfun is that these two matrices can be summed internally to give the final output -
0 51 102
50 101 152
Since, you needed the output as a vector, we need to flatten it out using reshape -
reshape(...,1,[])
giving us -
0 50 51 101 102 152
Thus, we have the final code that was listed earlier.
Here are some ideas I've been playing around with:
Initialization (comparable to Divakar's answer):
N = 6; %// Number of pairs in the final output
firstStep = 50;
secndStep = 1;
startVal = 8; %// First element of the output
And then:
%// Idea 1:
V1 = [startVal cumsum(repmat([firstStep,secndStep],[1,N])) + startVal];
%// Idea 2:
trimVec = #(vec)vec(1:end-1);
V2 = trimVec(circshift(kron((startVal:firstStep:startVal + ...
N*firstStep),[1,1]),[0,-1]) + kron((0:N),[1,1]));
Note that both of these vectors result in length = 2*N + 1.
The thing I'd like to point out is that if you create your vector of differences (e.g. [1 50 1 50 ...]), cumsum() really does the trick from there (you can also have more than 2 step sizes if you choose).
Let the input data be
step1 = 50;
step2 = 1;
start = 0;
number = 9; %// should be an odd number
Then:
n = (number-1)/2;
vector = cumsum([start reshape(([repmat(step1,1,n); repmat(step2,1,n)]),1,[])]);
The result in this example is
vector =
0 50 51 101 102 152 153 203 204

Matrix "Zigzag" Reordering

I have an NxM matrix in MATLAB that I would like to reorder in similar fashion to the way JPEG reorders its subblock pixels:
(image from Wikipedia)
I would like the algorithm to be generic such that I can pass in a 2D matrix with any dimensions. I am a C++ programmer by trade and am very tempted to write an old school loop to accomplish this, but I suspect there is a better way to do it in MATLAB.
I'd be rather want an algorithm that worked on an NxN matrix and go from there.
Example:
1 2 3
4 5 6 --> 1 2 4 7 5 3 6 8 9
7 8 9
Consider the code:
M = randi(100, [3 4]); %# input matrix
ind = reshape(1:numel(M), size(M)); %# indices of elements
ind = fliplr( spdiags( fliplr(ind) ) ); %# get the anti-diagonals
ind(:,1:2:end) = flipud( ind(:,1:2:end) ); %# reverse order of odd columns
ind(ind==0) = []; %# keep non-zero indices
M(ind) %# get elements in zigzag order
An example with a 4x4 matrix:
» M
M =
17 35 26 96
12 59 51 55
50 23 70 14
96 76 90 15
» M(ind)
ans =
17 35 12 50 59 26 96 51 23 96 76 70 55 14 90 15
and an example with a non-square matrix:
M =
69 9 16 100
75 23 83 8
46 92 54 45
ans =
69 9 75 46 23 16 100 83 92 54 8 45
This approach is pretty fast:
X = randn(500,2000); %// example input matrix
[r, c] = size(X);
M = bsxfun(#plus, (1:r).', 0:c-1);
M = M + bsxfun(#times, (1:r).'/(r+c), (-1).^M);
[~, ind] = sort(M(:));
y = X(ind).'; %'// output row vector
Benchmarking
The following code compares running time with that of Amro's excellent answer, using timeit. It tests different combinations of matrix size (number of entries) and matrix shape (number of rows to number of columns ratio).
%// Amro's approach
function y = zigzag_Amro(M)
ind = reshape(1:numel(M), size(M));
ind = fliplr( spdiags( fliplr(ind) ) );
ind(:,1:2:end) = flipud( ind(:,1:2:end) );
ind(ind==0) = [];
y = M(ind);
%// Luis' approach
function y = zigzag_Luis(X)
[r, c] = size(X);
M = bsxfun(#plus, (1:r).', 0:c-1);
M = M + bsxfun(#times, (1:r).'/(r+c), (-1).^M);
[~, ind] = sort(M(:));
y = X(ind).';
%// Benchmarking code:
S = [10 30 100 300 1000 3000]; %// reference to generate matrix size
f = [1 1]; %// number of cols is S*f(1); number of rows is S*f(2)
%// f = [0.5 2]; %// plotted with '--'
%// f = [2 0.5]; %// plotted with ':'
t_Amro = NaN(size(S));
t_Luis = NaN(size(S));
for n = 1:numel(S)
X = rand(f(1)*S(n), f(2)*S(n));
f_Amro = #() zigzag_Amro(X);
f_Luis = #() zigzag_Luis(X);
t_Amro(n) = timeit(f_Amro);
t_Luis(n) = timeit(f_Luis);
end
loglog(S.^2*prod(f), t_Amro, '.b-');
hold on
loglog(S.^2*prod(f), t_Luis, '.r-');
xlabel('number of matrix entries')
ylabel('time')
The figure below has been obtained with Matlab R2014b on Windows 7 64 bits. Results in R2010b are very similar. It is seen that the new approach reduces running time by a factor between 2.5 (for small matrices) and 1.4 (for large matrices). Results are seen to be almost insensitive to matrix shape, given a total number of entries.
Here's a non-loop solution zig_zag.m. It looks ugly but it works!:
function [M,index] = zig_zag(M)
[r,c] = size(M);
checker = rem(hankel(1:r,r-1+(1:c)),2);
[rEven,cEven] = find(checker);
[cOdd,rOdd] = find(~checker.'); %'#
rTotal = [rEven; rOdd];
cTotal = [cEven; cOdd];
[junk,sortIndex] = sort(rTotal+cTotal);
rSort = rTotal(sortIndex);
cSort = cTotal(sortIndex);
index = sub2ind([r c],rSort,cSort);
M = M(index);
end
And a test matrix:
>> M = [magic(4) zeros(4,1)];
M =
16 2 3 13 0
5 11 10 8 0
9 7 6 12 0
4 14 15 1 0
>> newM = zig_zag(M) %# Zig-zag sampled elements
newM =
16
2
5
9
11
3
13
10
7
4
14
6
8
0
0
12
15
1
0
0
Here's a way how to do this. Basically, your array is a hankel matrix plus vectors of 1:m, where m is the number of elements in each diagonal. Maybe someone else has a neat idea on how to create the diagonal arrays that have to be added to the flipped hankel array without a loop.
I think this should be generalizeable to a non-square array.
% for a 3x3 array
n=3;
numElementsPerDiagonal = [1:n,n-1:-1:1];
hadaRC = cumsum([0,numElementsPerDiagonal(1:end-1)]);
array2add = fliplr(hankel(hadaRC(1:n),hadaRC(end-n+1:n)));
% loop through the hankel array and add numbers counting either up or down
% if they are even or odd
for d = 1:(2*n-1)
if floor(d/2)==d/2
% even, count down
array2add = array2add + diag(1:numElementsPerDiagonal(d),d-n);
else
% odd, count up
array2add = array2add + diag(numElementsPerDiagonal(d):-1:1,d-n);
end
end
% now flip to get the result
indexMatrix = fliplr(array2add)
result =
1 2 6
3 5 7
4 8 9
Afterward, you just call reshape(image(indexMatrix),[],1) to get the vector of reordered elements.
EDIT
Ok, from your comment it looks like you need to use sort like Marc suggested.
indexMatrixT = indexMatrix'; % ' SO formatting
[dummy,sortedIdx] = sort(indexMatrixT(:));
sortedIdx =
1 2 4 7 5 3 6 8 9
Note that you'd need to transpose your input matrix first before you index, because Matlab counts first down, then right.
Assuming X to be the input 2D matrix and that is square or landscape-shaped, this seems to be pretty efficient -
[m,n] = size(X);
nlim = m*n;
n = n+mod(n-m,2);
mask = bsxfun(#le,[1:m]',[n:-1:1]);
start_vec = m:m-1:m*(m-1)+1;
a = bsxfun(#plus,start_vec',[0:n-1]*m);
offset_startcol = 2- mod(m+1,2);
[~,idx] = min(mask,[],1);
idx = idx - 1;
idx(idx==0) = m;
end_ind = a([0:n-1]*m + idx);
offsets = a(1,offset_startcol:2:end) + end_ind(offset_startcol:2:end);
a(:,offset_startcol:2:end) = bsxfun(#minus,offsets,a(:,offset_startcol:2:end));
out = a(mask);
out2 = m*n+1 - out(end:-1:1+m*(n-m+1));
result = X([out2 ; out(out<=nlim)]);
Quick runtime tests against Luis's approach -
Datasize: 500 x 2000
------------------------------------- With Proposed Approach
Elapsed time is 0.037145 seconds.
------------------------------------- With Luis Approach
Elapsed time is 0.045900 seconds.
Datasize: 5000 x 20000
------------------------------------- With Proposed Approach
Elapsed time is 3.947325 seconds.
------------------------------------- With Luis Approach
Elapsed time is 6.370463 seconds.
Let's assume for a moment that you have a 2-D matrix that's the same size as your image specifying the correct index. Call this array idx; then the matlab commands to reorder your image would be
[~,I] = sort (idx(:)); %sort the 1D indices of the image into ascending order according to idx
reorderedim = im(I);
I don't see an obvious solution to generate idx without using for loops or recursion, but I'll think some more.

Problem with numbers

I am given a number N, and i must add some numbers from the array V so that they wil be equal. V is consisting of numbers that are all powers of 3:
N = 17
S = 0
V = 1 3 9 27 81 ..
I should add numbers from V to N and S in order to make them equal. The solution to the example above is :
17 + 1 + 9 = 27, 27, 1 and 9 are taken from V, a number from V can be taken only once, and when taken it's removed from V.
I tried sorting V and then adding the biggest numbers from V to S until S has reached N, but it fails on some tests when it's like:
N = 7
S = 0
V = 1 3 9 27
So the solution will be:
7 + 3 = 9 + 1
In examples like this i need to add numbers both to N and S, and also select them so they become equal.
Any idea of solving this ? Thanks.
Write N in base 3: 17 = 2*1 + 2*3 + 1*9
Find the first power of 3 with coefficient 2, in this case 1.
Add this power of 3: 17 + 1
Repeat until all coefficients are 0 or 1.
17 = 2*1 + 2*3 + 1*9
17 + 1 = 2*9
17 + 1 + 9 = 27
7 = 1*1 + 2*3
7 + 3 = 1*1 + 1*9