Matlab: iterate through image blocks - matlab

I would like to divide an image into 8 by 6 blocks and then from each block would like to get the average of red, green and blue values then store the average values from each block into an array. Say that if I have image divided into 4 blocks the result array would be:
A = [average_red, average_green, average_blue,average_red, ...
average_green, average_blue,average_red, average_green, ...
average_blue,average_red, average_green, average_blue,...
average_red, average_green, average_blue,]
The loop I have created looks very complicated, takes a long time to run and I'm not even sure if it's working properly or not as I have no clue how to check. Is there any simpler way to implement this.
Here is the loop:
[rows, columns, ~] = size(img);
[rows, columns, ~] = size(img);
rBlock = 6;
cBlock = 8;
NumberOfBlocks = rBlock * cBlock;
bRow = ceil(rows/rBlock);
bCol = ceil(columns/cBlock);
row = bRow;
col = bCol;
r = zeros(row*col,1);
g = zeros(row*col,1);
b = zeros(row*col,1);
n = 1;
cl = 1;
rw = 1;
for x = 1:NumberOfBlocks
for i = cl : col
for j = rw : row
% some code
end
end
%some code
if i == columns && j ~= rows
cl = 1;
rw = j - (bRow -1);
col = (col - col) + bCol;
row = row + bRaw;
elseif a == columns && c == rows
display('done');
else
cl = i + 1;
rw = j - (bRow -1);
col = col + col;
row = row + row;
end
end

Because there are only 48 block, you may use simple for loop iterating blocks. (I think it's going to be fast enough).
Here is my code:
%Build test image
img = double(imresize(imread('peppers.png'), [200, 300]));
[rows, columns, ~] = size(img);
rBlock = 6;
cBlock = 8;
NumberOfBlocks = rBlock * cBlock;
bRow = ceil(rows/rBlock);
bCol = ceil(columns/cBlock);
idx = 1;
A = zeros(1, rBlock*cBlock*3);
for y = 0:rBlock-1
for x = 0:cBlock-1
%Block (y,x) boundaries: (x0,y0) to (x1,y1)
x0 = x*bCol+1;
y0 = y*bRow+1;
x1 = min(x0+bCol-1, columns); %Limit x1 to columns
y1 = min(y0+bRow-1, rows); %Limit y1 to rows
redMean = mean2(img(y0:y1, x0:x1, 1)); %Mean of red pixel in block (y,x)
greenMean = mean2(img(y0:y1, x0:x1, 2)); %Mean of green pixel in block (y,x)
blueMean = mean2(img(y0:y1, x0:x1, 3)); %Mean of blue pixel in block (y,x)
%Fill 3 elements of array A.
A(idx) = redMean;
A(idx+1) = greenMean;
A(idx+2) = blueMean;
%Advance index by 3.
idx = idx + 3;
end
end

Related

How to divide a 2D shape in patches of approx equal size in matlab?

I have a 2D matrix of zeros and ones, where the ones indicate a convex figure
I now want to divide this figure (that is the elements of value 1) in nonoverlapping patches of equally the same size, as in this figure
Do you have any suggestion? I could go for mat2cell and have just rectangles, and keep the rectangles with at least one value 1 in them, but I would prefer a more equal division.
For similar problems, I often use a method called 'orthogonal recursive bisection'.
An example of what it does with your circle is in the picture.
As the name suggests, the method divides subdomains into two smaller subdomains,
until the total number of subdomains is the desired value.
My implementation for your case is
function array = ORB(array,nparts)
%
% array = ORB(array,nparts)
%
% Divide the nonzeros of array into nparts equally large,
% approximately square parts.
%
% convert true/false array into 0/1:
ar = array; array = zeros(size(ar)); array(ar) = 1;
% initialize subdivision-admin
istart = 1; iend = nparts; values = 1;
last_value = max(values);
% Divide up the parts that need dividing up
while length(values) < nparts
new_istart = []; new_iend = []; new_values = [];
for i = 1:length(values)
if iend(i) > istart(i)
disp(sprintf('Current values %d should eventually be split into domains %d-%d',values(i),istart(i),iend(i)))
last_value = last_value + 1;
new_istart = [new_istart, istart(i), istart(i) + floor((iend(i)-istart(i)+1)/2)];
new_iend = [new_iend, istart(i) + floor((iend(i)-istart(i)+1)/2)-1, iend(i)];
new_values = [new_values, values(i), last_value];
n = length(new_values);
disp(sprintf('Current values %d should now be split into domains %d and %d, in ratio %d:%d\n', ...
values(i), new_values(n-1:n),new_iend(n-1:n)-new_istart(n-1:n)+1));
array = Split(array,new_values(n-1:n),new_iend(n-1:n)-new_istart(n-1:n)+1);
else
disp(sprintf('Domain %d is done\n',values(i)))
new_istart = [new_istart, istart(i)];
new_iend = [new_iend, iend(i)];
new_values = [new_values, values(i)];
end
end
iend = new_iend; istart = new_istart; values = new_values;
end
for i = 1:nparts
disp(sprintf('Part %d has %d points',i,length(find(array==i))))
end
close all
pcolor(array)
which needs the function Split:
function array = Split(array,parts,sizes)
%
% array = Split(array,parts,sizes)
%
% Change some of the values of array which are now equal to parts(1) into the value parts(2).
% At the end, the ratio
% length(find(array==parts(1))) : length(find(array==parts(2)))
% should be
% sizes(1) : sizes(2)
%
% Calculate sizes of each patch
[i,j] = find(array==parts(1));
npoints = size(i,1); sizes = npoints * sizes/(sizes(1)+sizes(2));
imin = min(i); imax = max(i); jmin = min(j); jmax = max(j);
nmin = 0; nmax = npoints;
if jmax-jmin>imax-imin
% divide domain in (j < jmid) and (jmid <= j)
while jmax > jmin + 1
jmid = (jmax + jmin)/2; n_this = size(find(j<jmid));
if n_this < sizes(1)
jmin = jmid; nmin = n_this;
else
jmax = jmid; nmax = n_this;
end
end
i = i(j>=jmid); j = j(j>=jmid);
else
% divide domain in (i < imid) and (imid <= i)
while imax > imin + 1
imid = (imax + imin)/2; n_this = size(find(i<imid));
if n_this < sizes(1)
imin = imid; nmin = n_this;
else
imax = imid; nmax = n_this;
end
end
j = j(i>=imid); i = i(i>=imid);
end
% Change the values in array
array(sub2ind(size(array),i,j)) = parts(2);

Vectorization in Matlab to speed up expensive loop

How can I speed up the following MATLAB code, using vectorization? Right now the single line in the loop is taking hours to run for the case upper = 1e7.
Here is the commented code with sample output:
p = 8;
lower = 1;
upper = 1e1;
n = setdiff(lower:upper,primes(upper)); % contains composite numbers between lower + upper
x = ones(length(n),p); % Preallocated 2-D array of ones
% This loop stores the unique prime factors of each composite
% number from 1 to n, in each row of x. Since the rows will have
% varying lengths, the rows are padded with ones at the end.
for i = 1:length(n)
x(i,:) = [unique(factor(n(i))) ones(1,p-length(unique(factor(n(i)))))];
end
output:
x =
1 1 1 1 1 1 1 1
2 1 1 1 1 1 1 1
2 3 1 1 1 1 1 1
2 1 1 1 1 1 1 1
3 1 1 1 1 1 1 1
2 5 1 1 1 1 1 1
For example, the last row contains the prime factors of 10, if we ignore the ones. I have made the matrix 8 columns wide to account for the many prime factors of numbers up to 10 million.
Thanks for any help!
This is not vectorization, but this version of the loop will save about half of the time:
for k = 1:numel(n)
tmp = unique(factor(n(k)));
x(k,1:numel(tmp)) = tmp;
end
Here is a quick benchmark for this:
function t = getPrimeTime
lower = 1;
upper = 2.^(1:8);
t = zeros(numel(upper),2);
for k = 1:numel(upper)
n = setdiff(lower:upper(k),primes(upper(k))); % contains composite numbers between lower to upper
t(k,1) = timeit(#() getPrime1(n));
t(k,2) = timeit(#() getPrime2(n));
disp(k)
end
p = plot(log2(upper),log10(t));
p(1).Marker = 'o';
p(2).Marker = '*';
xlabel('log_2(range of numbers)')
ylabel('log(time (sec))')
legend({'getPrime1','getPrime2'})
end
function x = getPrime1(n) % the originel function
p = 8;
x = ones(length(n),p); % Preallocated 2-D array of ones
for k = 1:length(n)
x(k,:) = [unique(factor(n(k))) ones(1,p-length(unique(factor(n(k)))))];
end
end
function x = getPrime2(n)
p = 8;
x = ones(numel(n),p); % Preallocated 2-D array of ones
for k = 1:numel(n)
tmp = unique(factor(n(k)));
x(k,1:numel(tmp)) = tmp;
end
end
Here's another approach:
p = 8;
lower = 1;
upper = 1e1;
p = 8;
q = primes(upper);
n = setdiff(lower:upper, q);
x = bsxfun(#times, q, ~bsxfun(#mod, n(:), q));
x(~x) = inf;
x = sort(x,2);
x(isinf(x)) = 1;
x = [x ones(size(x,1), p-size(x,2))];
This seems to be faster than the other two options (but is uses more memory). Borrowing EBH's benchmarking code:
function t = getPrimeTime
lower = 1;
upper = 2.^(1:12);
t = zeros(numel(upper),3);
for k = 1:numel(upper)
n = setdiff(lower:upper(k),primes(upper(k)));
t(k,1) = timeit(#() getPrime1(n));
t(k,2) = timeit(#() getPrime2(n));
t(k,3) = timeit(#() getPrime3(n));
disp(k)
end
p = plot(log2(upper),log10(t));
p(1).Marker = 'o';
p(2).Marker = '*';
p(3).Marker = '^';
xlabel('log_2(range of numbers)')
ylabel('log(time (sec))')
legend({'getPrime1','getPrime2','getPrime3'})
grid on
end
function x = getPrime1(n) % the originel function
p = 8;
x = ones(length(n),p); % Preallocated 2-D array of ones
for k = 1:length(n)
x(k,:) = [unique(factor(n(k))) ones(1,p-length(unique(factor(n(k)))))];
end
end
function x = getPrime2(n)
p = 8;
x = ones(numel(n),p); % Preallocated 2-D array of ones
for k = 1:numel(n)
tmp = unique(factor(n(k)));
x(k,1:numel(tmp)) = tmp;
end
end
function x = getPrime3(n) % Approach in this answer
p = 8;
q = primes(max(n));
x = bsxfun(#times, q, ~bsxfun(#mod, n(:), q));
x(~x) = inf;
x = sort(x,2);
x(isinf(x)) = 1;
x = [x ones(size(x,1), p-size(x,2))];
end

Finding correct index value for matrix in Matlab using meshgrid

I'm trying to build make a code where an equation is not calculated for some certain values. I have a meshgrid with several values for x and y and I want to include a for loop that will calculate some values for most of the points in the meshgrid but I'm trying to include in that loop a condition that if the points have a specified index, the value will not be calculated. In my second group of for/if loops, I want to say that for all values of i and k (row and column), the value for z and phi are calculated with the exception of the specified i and k values (in the if loop). What I'm doing at the moment is not working...
The error I'm getting is:
The expression to the left of the equals sign is not a valid target for an assignment.
Here is my code at the moment. I'd really appreciate any advice on this! Thanks in advance
U_i = 20;
a = 4;
c = -a*5;
b = a*10;
d = -20;
e = 20;
n = a*10;
[x,y] = meshgrid([c:(b-c)/n:b],[d:(e-d)/n:e]');
for i = 1:length(x)
for k = 1:length(x)
% Zeroing values where cylinder is
if sqrt(x(i,k).^2 + y(i,k).^2) < a
x(i,k) = 0;
y(i,k) = 0;
end
end
end
r = sqrt(x.^2 + y.^2);
theta = atan2(y,x);
z = zeros(length(x));
phi = zeros(length(x));
for i = 1:length(x)
for k = 1:length(x)
if (i > 16 && i < 24 && k > 16 && k <= length(x))
z = 0;
phi = 0;
else
z = U_i.*r.*(1-a^2./r.^2).*sin(theta); % Stream function
phi = U_i*r.*(1+a^2./r.^2).*cos(theta); % Velocity potential
end
end
end
The original code in the question can be rewritten as seen below. Pay attention in the line with ind(17:24,:) since your edit now excludes 24 and you original question included 24.
U_i = 20;
a = 4;
c = -a*5;
b = a*10;
d = -20;
e = 20;
n = a*10;
[x,y] = meshgrid([c:(b-c)/n:b],[d:(e-d)/n:e]');
ind = find(sqrt(x.^2 + y.^2) < a);
x(ind) = 0;
y(ind) = 0;
r = sqrt(x.^2 + y.^2);
theta = atan2(y,x);
ind = true(size(x));
ind(17:24,17:length(x)) = false;
z = zeros(size(x));
phi = zeros(size(x));
z(ind) = U_i.*r(ind).*(1-a^2./r(ind).^2).*sin(theta(ind)); % Stream function
phi(ind) = U_i.*r(ind).*(1+a^2./r(ind).^2).*cos(theta(ind)); % Velocity potential

Filter points using hist in matlab

I have a vector. I want to remove outliers. I got bin and no of values in that bin. I want to remove all points based on the number of elements in each bin.
Data:
d1 =[
360.471912914169
505.084636471948
514.39429429184
505.285068055647
536.321181755858
503.025854206322
534.304229816684
393.387035881967
396.497969729985
520.592172434431
421.284713703215
420.401106087984
537.05330275495
396.715779872694
514.39429429184
404.442344469518
476.846474245118
599.020867750031
429.163139144079
514.941744277933
445.426761656729
531.013596812737
374.977332648255
364.660115724218
538.306752697753
519.042387479096
1412.54699036882
405.571202133485
516.606049132218
2289.49623498271
378.228766753667
504.730621222846
358.715764917016
462.339366699398
512.429858614816
394.778786157514
366
498.760463549388
366.552861126468
355.37022947906
358.308526273099
376.745272034036
366.934599077274
536.0901883079
483.01740134285
508.975480745389
365.629593988233
536.368800360349
557.024236456548
366.776498701866
501.007025898839
330.686029339009
508.395475983019
429.563732174866
2224.68806802212
534.655786464525
518.711297351426
534.304229816684
514.941744277933
420.32368479542
367.129404978681
525.626188464768
388.329756778952
1251.30895065927
525.626188464768
412.313764019587
513.697381733643
506.675438520558
1517.71183364959
550.276294237722
543.359917550053
500.639590923451
395.129864728041];
Histogram computation:
[nelements,centers] = hist(d1);
nelements=55 13 0 0 1 1 1 0 0 2
I want to remove all points apearing less than 5 (in nelements). It means only first 2 elements in nelements( 55, 13 ) remains.
Is there any function in matlab.
You can do it along these lines:
threshold = 5;
bin_halfwidth = (centers(2)-centers(1))/2;
keep = ~any(abs(bsxfun(#minus, d1, centers(nelements<threshold))) < bin_halfwidth , 2);
d1_keep = d1(keep);
Does this do what you want?
binwidth = centers(2)-centers(1);
centersOfRemainingBins = centers(nelements>5);
remainingvals = false(length(d1),1);
for ii = 1:length(centersOfRemainingBins )
remainingvals = remainingvals | (d1>centersOfRemainingBins (ii)-binwidth/2 & d1<centersOfRemainingBins (ii)+binwidth/2);
end
d_out = d1(remainingvals);
I don't know Matlab function for this problem, but I think, that function with follow code is what are you looking for:
sizeData = size(data);
function filter_hist = filter_hist(data, binCountRemove)
if or(max(sizeData) == 0, binCountRemove < 1)
disp('Error input!');
filter_hist = [];
return;
end
[n, c] = hist(data);
sizeN = size(n);
intervalSize = c(2) - c(1);
if sizeData(1) > sizeData(2)
temp = transpose(data);
else
temp = data;
end
for i = 1:1:max(sizeN)
if n(i) < binCountRemove
a = c(i) - intervalSize / 2;
b = c(i) + intervalSize / 2;
sizeTemp = size(temp);
removeInds = [];
k = 0;
for j = 1:1:max(sizeTemp)
if and(temp(j) > a, less_equal(temp(j), b) == 1)
k = k + 1;
removeInds(k) = j;
end
end
temp(removeInds) = [];
end
end
filter_hist = transpose(temp);
%Determines when 'a' less or equal to 'b' by accuracy
function less_equal = less_equal(a, b)
delta = 10^-6; %Accuracy
if a < b
less_equal = 1;
return;
end
if abs(b - a) < delta
less_equal = 1;
return;
end
less_equal = 0;
You can do something like this
nelements=nelements((nelements >5))

For loop output as an array

I wrote a code in Matlab which I predefine the variable "a" and then set up a for loop of 5 iterations where the variable "a" goes through some basic operations. However, the for loop output only saves the fifth iteration of "a." How do I save all 5 iterations in a 1x5 array?
The code is as follows:
a = 10;
k = 0.5;
n = 2;
for m = 1:5
a = a + (a*k) + n;
end
Edit:
I just found it that I have to create a new variable.
a = 10;
k = 0.5;
n = 2;
a_n = zeros(1,5);
for m = 1:5
a = a + (a*k) + n;
a_n(m) = a;
end
You may need to store value of a after each iteration into an another variable x
a = 10;
k = 0.5;
n = 2;
for m = 1:5
a = a + (a*k) + n;
x(m) = a;
end
x
Output:
x =
17.000 27.500 43.250 66.875 102.312
You would need to use a different variable to store the 5 iterations as an array.
Code would look something like this:
a = 10;
k = 0.5;
n = 2;
b = [];
for m = 1:5
a = (a + (a*k) + n)
b = [b a];
end
You can now print b for all 5 iteration values.
Here is an alternate way to update values into the 1-D matrix.