matlab - unfixed dimension matrix, set value to multiple fields of struct at once, avoid loop - matlab

I have 2 (not very small) 3-dimension structs with matrices as fields:
sz1 = 200;
sz2 = 9;
sz3 = [20, 40, 80, 160, 320, 640, 1280, 2560, 5120]
% actually the structs have 12 fields, each has size 200x9x5120
mat.p1(sz1, sz2, sz3(sz2)) = uint8(0);
mat.p2(sz1, sz2, sz3(sz2)) = uint8(0);
mat.p3(sz1, sz2, sz3(sz2)) = 0;
mat.p4(sz1, sz2, sz3(sz2)) = 0;
old_mat.p1(sz1, sz2, sz3(sz2)) = uint8(0);
old_mat.p2(sz1, sz2, sz3(sz2)) = uint8(0);
old_mat.p3(sz1, sz2, sz3(sz2)) = 0;
old_mat.p4(sz1, sz2, sz3(sz2)) = 0;
And i need to write a reset function, where i will re-assign the values to 3 of 4 (actually 10 of 12) fields in both matrices as follows:
for i = 1:sz1
for j = 1:sz2
for k = 1:sz3(j)
mat.p1(i,j,k) = uint8(255);
mat.p3(i,j,k) = -1;
mat.p4(i,j,k) = 0.01;
old_mat.p1(i,j,k) = uint8(255);
old_mat.p3(i,j,k) = -1;
old_mat.p4(i,j,k) = 0.01;
end
end
end
Note that actually what i need in the matrices is the same as the reset function, means i only need the 5120th index of the 3rd-dimension when 2nd-dimension is 9, if 2nd-dimension = 4, i only need up to 160 indices at 3rd-dimension etc.
The questions are:
Is there anyway to assign values to the fields at the same time (not 1 line 1 field) since i actually have to do with 10 fields?
Is there anyway to avoid the for-loops? I tried like this:
mat.p1(:) = uint8(255);
mat.p3(:) = -1;
mat.p4(:) = 0.01;
old_mat.p1(:) = uint8(255);
old_mat.p3(:) = -1;
old_mat.p4(:) = 0.01;
but here all the matrices are filled with max 3rd-dimension = 5120 so i expect someone can show me how to use the vectorizing function like arrayfun, bsxfun, cellfun etc., which can apply for just the "half-cubic" like the for-loops above.
UPDATE:
Thanks horchler for the video, it seems the problem with big size (in bytes) of the matrices is solved when i change the matrix of struct to a struct with matrices as fields. This also clears the timing problem even with nested for-loops. So i updated the questions and the input as well, please see above.

Yes, I think that using a structure of arrays will work better for you here. You should be able to allocate using zeros just as if mat.p1, mat.p2, etc. were regular arrays. I'd do something like this (note that you didn't indicate any values for mat.p2) using a single for loop:
sz1 = 200;
sz2 = 9;
sz3 = [20, 40, 80, 160, 320, 640, 1280, 2560, 5120];
% Be careful with this form of pre-allocation if your sz arrays change
% Clear your struct or make sure to use the code in a function
mat.p1(sz1,sz2,sz3(sz2)) = uint8(0);
mat.p3(sz1,sz2,sz3(sz2)) = 0;
mat.p4(sz1,sz2,sz3(sz2)) = 0;
for i = 1:sz2
mat.p1(:,i,1:sz3(i)) = uint8(255);
mat.p3(:,i,1:sz3(i)) = -1;
mat.p4(:,i,1:sz3(i)) = 0.01;
end
old_mat.p1 = mat.p1;
old_mat.p3 = mat.p3;
old_mat.p4 = mat.p4;
Alternatively, you could do something like this:
sz1 = 200;
sz2 = 9;
sz3 = [20, 40, 80, 160, 320, 640, 1280, 2560, 5120];
mat = struct('p1',zeros(sz1,sz2,sz3(sz2),'uint8'),...
'p3',zeros(sz1,sz2,sz3(sz2)),...
'p4',zeros(sz1,sz2,sz3(sz2)));
for i = 1:sz2
mat.p1(:,i,1:sz3(i)) = uint8(255);
mat.p3(:,i,1:sz3(i)) = -1;
mat.p4(:,i,1:sz3(i)) = 0.01;
end
old_mat = struct('p1',mat.p1,'p3',mat.p3,'p4',mat.p4);

Related

MATLAB: Finding minimal pixel values from a video

I'm trying to write some MATLAB code such that given a monochromatic video, It needs to produce a image such that each pixel of the image equals the minimal value that said pixel takes in the video. As an example the pixel (200,300) will equal the min value that pixel (200,300) through the course of the video. I have written some code to do this however it's terribly inefficient. Any comments to improve my code would be appriciated
hologramVideo = VideoReader('test.mp4')
mkdir('images')
frames = int16(hologramVideo.Duration * hologramVideo.FrameRate)
imageValues = cell(frames, 1);
ii = 1;
while hasFrame(hologramVideo)
imageValues{ii} = im2uint8(rgb2gray(readFrame(hologramVideo)));
ii = ii + 1;
end
newImage = zeros(512)
currentMin = 255
currentVal = 0
x = 1;
y = 1;
for x = 1:512
for y = 1:512
currentMin = 0;
for i = 1:frames
currentImg = imageValues(i,1,1);
currentVal = currentImg{1,1}(x,y)
if currentVal < currentMin;
currentMin = currentVal;
end
end
newImage(x,y) = currentMin;
end
end
I don't have a file to test, but the main bottleneck is in how you store the images. Rather than storing them in a cell array, you are better off storing them in a 3D-array:
imageValues = zeros([Nframes, 512, 512]);
ii=1;
while hasFrame(hologramVideo)
imageValues(ii,:,:) = im2uint8(rgb2gray(readFrame(hologramVideo)));
ii = ii + 1;
end
That would make the remainder of the code very easy and vectorized (i.e. fast):
newImage = squeeze(min(imageValues,[],1));

Hand segmentation using Depth thresholding

I want to segment hand from a depth image using depth thresholding. I used this kinect and leap dataset from this link-
http://lttm.dei.unipd.it/downloads/gesture/
I tried these 2 codes, but the output I got is total black image in both the cases. The original .png image is
I selected depth value from 1_depth.bin file in the dataset.
Code 1
I = fopen('D:\dsktop\kinect_leap_dataset\acquisitions\P1\G1\1_depth.bin', 'r');
A = fread(I, 480*640, 'uint8=>uint8');
A = reshape(A, 480, 640);
min_row = min(A);
min_col = min(min_row);
for i = 1:480
for j = 1:640
if ((A(i,j) > (min_col + 10)) || (A(i,j) == (min_col + 10)))
A(i,j) = 1;
else
A(i,j) = 0;
end
end
end
imshow(A)
Code 2
image = imread('D:\dsktop\kinect_leap_dataset\acquisitions\P1\G1\1_depth.png');
I = fopen('D:\dsktop\kinect_leap_dataset\acquisitions\P1\G1\1_depth.bin', 'r');
A = fread(I, 480*640, 'uint8=>uint8');
A = reshape(A, 480, 640);
min_row = min(A);
min_col = min(min_row);
for i = 1:480
for j = 1:640
if ((A(i,j) > (min_col + 10)) || (A(i,j) == (min_col + 10)))
image(i,j) = 1;
else
image(i,j) = 0;
end
end
end
imshow(image)
The output I am getting is
Kindly tell what is wrong in this code and why I am not getting any out?
Your code is extremely not vectorize. Here's how to re-write your code in a more vectorize fashion. This is both more efficient and more "readable":
I = fopen('D:\dsktop\kinect_leap_dataset\acquisitions\P1\G1\1_depth.bin', 'r');
A = fread(I, 480*640, 'uint8=>uint8');
A = reshape(A, 480, 640);
min_ = min(A(:)); % minimal value across rows and columns
mask = A>=(min_+10); % no need for loop, vectorize code.
imshow(mask, []);

Matlab: iterate through image blocks

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

Occasionally, figure size is not set properly in Matlab

I tried to set same figure size for several images using for loop in matlab and save in png
But some (usually one) of them has different size.
In below code, I tried to save image in (48,64).
Why some figure sizes are not set properly as I commanded?
nMarker = 5;
mark = ['o', 's', 'd', '^', 'p'];
nSize = 3;
mSize = [9, 18, 27];
nRow = 48;
nCol = 64;
BG = zeros(nRow, nCol);
idxStage = 2;
numAction = 1;
numPositionX = 4;
numPositionY = 4;
xtrain = [1,2,3,4];
ytrain = [1,2,3,4];
xpos = [20, 30, 40, 50];
ypos = [8, 18, 28, 38];
nStepS = 10;
nStepB = 10;
nStep = nStepS + nStepB;
for a = 1
for x = 1:numPositionX
for y = 1:numPositionY
for obj = 1:nMarker
for s = 1:nSize
obj_command = x*1000 + y*100 + obj*10 + s;
fig1 = figure(1);
imagesc(BG)
hold on
scatter(xpos(x), ypos(y), mSize(s), mark(obj), 'k', 'filled')
axis off
set(fig1, 'Position', [500, 500, 64, 48]);
set(gca,'position',[0 0 1 1],'units','normalized')
F = getframe(gcf);
pause(0.05)
[X, Map] = frame2im(F);%
tmp_frame = rgb2gray(X);
tmp_im_fn = sprintf('tmp/image_seq%04d.png',obj_command);
imwrite(tmp_frame, tmp_im_fn)
clf
end
end
end
end
end
I found some trick to solve the problem for now.
I put,
fig1 = figure(1);
drawnow
in front of the for loop and it seems all sizes are equal now.
But still waiting for better solution...

Vectorize code that operates on 3-D matrices

I want to organize the data returned by sph2cart as a matrix with vector elements, and operate on each element in this matrix (vector-vector or vector-scalar calculation). Here is an example wher I achieve this:
lightV = zeros(1, 1, 3);
lightV(1,1,1) = 0.5;
lightV(1,1,2) = 0.4;
lightV(1,1,3) = 0.7;
[Az El] = meshgrid(0:60:360, 0:15:90);
[x y z] = sph2cart(Az*pi/180, El*pi/180, 1);
refV = zeros(size(Az,1), size(Az,2), 3);
radius = zeros(size(Az,1), size(Az,2));
for i = 1:size(Az,1)
for j = 1:size(Az,2)
refV(i,j,1) = -x(i,j);
refV(i,j,2) = -y(i,j);
refV(i,j,3) = z(i,j);
radius(i,j) = dot(refV(i,j,:), lightV(1,1,:));
end
end
However this looks somewhat redundant, how could I make it more terse?
Well, you can vectorize your code like so:
S.lightV = [0.5, 0.4, 0.7];
[Az, El] = meshgrid(0:60:360, 0:15:90);
[S.x, S.y, S.z] = sph2cart(Az * pi/180, El * pi/180, 1);
S.refV = cat(3, -x, -y, z);
S.radius = sum(bsxfun(#times, S.refV, reshape(S.lightV, 1, 1, [])), 3);
Note the usage of cat to concatenate along the third dimension, and the combination of bsxfun and sum to replace the dot product inside the nested for loop. I've also bound everything together in one struct S.