How can I speed up looping through pixels in an image? - arrays

I am working with images as numpy arrays. I have a mask and I am editing the picture based on the mask (also an array). Presently, I am looping through each pixel and changing a the corresponding pixel in a white image the the values of the original image (in the case called cropped array). It seems like there should be a way to say something like white1=cropped array where mask==True. I thought that may speed this process up since I am working with so many images. Is this possible? I will try to make a simple example.
mask=[[True,True,False,False],[True,True,True,True]]
white1=[[[255,255,255],[255,255,255],[255,255,255],[255,255,255]],[[255,255,255],[255,255,255],[255,255,255],[255,255,255]]]
cropped_array=[[110,200,17],[110,200,17],[110,200,17],[110,200,17]],[[110,200,17],[110,200,17],[110,200,17],[110,200,17]]]
h , w = mask.shape
for i in range(h):
for j in range(w):
if mask[i][j]==True:
white1[i][j] = cropped_array[i][j]

You can selectively choose elements by simply multiplying with the mask.
import numpy as np
mask=[[True,True,False,False],[True,True,True,True]]
white1=[[[255,255,255],[255,255,255],[255,255,255],[255,255,255]],[[255,255,255],[255,255,255],[255,255,255],[255,255,255]]]
cropped_array=[[[110,200,17],[110,200,17],[110,200,17],[110,200,17]],[[110,200,17],[110,200,17],[110,200,17],[110,200,17]]]
mask = np.array(mask)
white1 = np.array(white1)
cropped_array = np.array(cropped_array)
h , w = mask.shape
for i in range(h):
for j in range(w):
if mask[i][j]==True:
white1[i][j] = cropped_array[i][j]
white = (cropped_array * mask[..., None]) + (white1 * ~mask[..., None])
print(np.allclose(white, white1))
Gives True

Related

How does a one liner in matlab cell array works?

SUBSAMPLE = 2; % Subsample your image by this much
for person_num = 1:40
for sample_num = 1:10
filename = ['orl_faces/s' num2str(person_num) '/' num2str(sample_num) '.pgm'];
if sample_num < 8
img = imread(filename);
[m,n] = size(img);
imagetrain{person_num,sample_num} = img(1:SUBSAMPLE:m,1:SUBSAMPLE:n)(:);
else
img = imread(filename);
imagetest{person_num,sample_num} = img(1:SUBSAMPLE:m,1:SUBSAMPLE:n)(:);
end
end
end
Above is code fragment from a matlab code for facial recognition. I am new to matlab therefore I am trying to understand statements that are new to me. My question is, imagetrain and imagetest is being assigned something from img(). Can anyone explain to me what img() is being assigned to the matrices? It looks like something of oneliner to me.
img is the image loaded using imread.
The code img(1:SUBSAMPLE:m,1:SUBSAMPLE:n) subsamples the image and reduces its size. The result is a new image (i.e. a new matrix) with fewer number of rows and fewer number of columns.
The leading (:) converts (reshapes) the matrix into a column vector.
So, for example if the original image is 256*256, the subsampled image will be 128*128, and after (:) it will become a column vector with 128*128=16384 elements.

How do i store sequence of 2D matrices into a 3D array in Matlab?

I have these series of 2D CT images and i have been able to read them into Matlab using "imread". The issue however is that i need the image read-in as a single 3D matrix rather than stack of several 2D matrices. I have been made aware that it is possible to store the number of 2D layers as the 3rd dimension, but i have no idea how to do this as i am still a learner.
The code i have for reading in the 2D stack are as follows:
a = dir('*.tif');
for i = 1: numel(a)
b = imread(a(i).name); %read in the image
b_threshold = graythresh(b); %apply threshold
b_binary = im2bw(b, b_threshold); %binarize image
[m, n] = size(b); %compute the size of the matrix
phi(i) = ((m*n) - sum((b_binary(:))))/(m*n); %compute the fraction of pore pixels in the image
phi(:,i) = phi(i); %store each of the above result
end
I have added just a single image although several of these are needed. Nevertheless, one can easily duplicate the image to create a stack of 2D images. For the code to work, it is however important to rename them in a numerical order.pore_image
Any help/suggestions/ideas is welcomed. Thanks!
You can simply assign along the third dimension using i as your index
stack_of_images(:,:,i) = b_binary
Well, the first advice is try to don't use the variable i and j in matlab because they are reserved (have a look here and here).
After it depends on along which dimension you want to store the 2D images:
if you want to store the images along the first dimension just use this code:
a = dir('*.tif');
for ii = 1: numel(a)
b = imread(a(ii).name); %read in the image
b_threshold = graythresh(b); %apply threshold
b_binary = im2bw(b, b_threshold); %binarize image
[m, n] = size(b); %compute the size of the matrix
phi(ii) = ((m*n) - sum((b_binary(:))))/(m*n); %compute the fraction of pore pixels in the image
phi(:,ii) = phi(ii); %store each of the above result
matrix_3D_images(ii,:,:)=b_binary; %adding a new layer
end
If you want to store the images along other dimensions it is easy to do: just change the posizion of the "pointer" ii:
matrix_3D_images(:,ii,:)=b_binary; or
matrix_3D_images(:,:,ii)=b_binary;

MATLAB - copying specific values from one 3d array to another

I need to update image 1 with rgb values from image 2 for specific coordinates.
I have two 2d matrices (im1Cart_toupdate 2x114056 and im2EstCart_tocopyfrom also 2x114056). These contain the ordered x-y pairs for which I want to copy rgb values from image 2 to image 1.
i.e. there are 114,056 pixels where I want to copy colours across.
im1 (440x1370x3) and im2 (240x320x3) are the image arrays. Note im2 is going to be stretched, so some pixels from im2 will appear more than once in im2EstCart_tocopyfrom.
I need an efficient way of doing this, as even with the above image sizes my current implementation is very slow. I had thought that there may be some approach using sub2ind - but am not sure how to do this with 3d arrays.
Here's my current code. It's the for loop that's killing me!
%Create a matrix of all pixel coordinates in im1 (homogenised form)
[im1gridx im1gridy]=meshgrid(1:im1width,1:im1height);
im1Cart = [im1gridx(:) im1gridy(:)]';
im1Hom = [im1Cart; ones(1,numel(im1gridy))];
%transform pixel positions with homography (HEst is a matrix built
%elsewhere) to find where they are in the coordinates of image 2
im2EstHom = HEst*im1Hom;
im2EstCart = im2EstHom(1:2,:)./repmat(im2EstHom(3,:),2,1);
im2EstCart = round(im2EstCart);
%check if the the transformed position is within the boundary of image 2
validCoords = im2EstCart(1,:)>0 & im2EstCart(2,:)>0 & im2EstCart(1,:)<=im2width & im2EstCart(2,:)<=im2height;
im1Cart_toupdate=im1Cart(:,validCoords);
im2EstCart_tocopyfrom=im2EstCart(:,validCoords);
%copy colour from image 2 to image 1 - currently pixel by pixel
%but CAN THIS BE VECTORISED?
for i=1:size(im1Cart_toupdate,2)
im1y=im1Cart_toupdate(1,i);
im1x=im1Cart_toupdate(2,i);
im2y=im2EstCart_tocopyfrom(1,i);
im2x=im2EstCart_tocopyfrom(2,i);
im1(im1y,im1x,:) = im2(im2y,im2x,:);
drawnow
end
Many thanks for any advice!
Approach #1
This would be one vectorized approach using linear indexing with bsxfun -
[m2,n2,r2] = size(im2);
RHS_idx1 = (im2EstCart_tocopyfrom(2,:)-1)*m2 + im2EstCart_tocopyfrom(1,:)
RHS_allidx = bsxfun(#plus,RHS_idx1(:),(0:r2-1)*m2*n2)
[m1,n1,r1] = size(im1);
LHS_idx1 = (im1Cart_toupdate(2,:)-1)*m1 + im1Cart_toupdate(1,:)
LHS_allidx = bsxfun(#plus,LHS_idx1(:),(0:r1-1)*m1*n1)
im1(LHS_allidx) = im2(RHS_allidx)
Approach #2
Here's another approach that reshapes the input 3D array to a 2D array after merging the first two dimensions, then using linear indexing for extracting and setting values and finally reshaping back to its original 3D size, like so -
[m2,n2,r2] = size(im2)
RHS_idx1 = (im2EstCart_tocopyfrom(2,:)-1)*m2 + im2EstCart_tocopyfrom(1,:)
im2r = reshape(im2,[],r2)
[m1,n1,r1] = size(im1)
LHS_idx1 = (im1Cart_toupdate(2,:)-1)*m1 + im1Cart_toupdate(1,:)
im1r = reshape(im1,[],r1)
im1r(LHS_idx1,:) = im2r(RHS_idx1,:)
im1 = reshape(im1r,size(im1));

Matlab - Cropping 2d image maps in a loop and storing in a single variable

I have a code to crop connected components of input image, input, by finding the boundary conditions from a binary image's labelled map, labelledmap ([labelledmap, labelcount] = bwlabel(hvedged, 8);)
I'm new to matlab so this might sound stupid..
The problem is, I am unable to store different cropped images in the same variable, Because matlab seems to merge the ends of the already existing image and the new cropped image, i.e, it is storing the complete map between the two cropped images, the way i see it :/
This is the output Using different variables for storing cropped image (the kind of output i want)
Output Using different variables for storing cropped image
This is the output i'm getting by storing the cropped image in the same variable(not helpful)
Output when storing cropped image in the same varible
I tried using an array of size equal to total number of labels produced but it's giving the same result.. also i tried clearvars for clearing the output token image, ltoken, after every iteration of the loop but it's not helping
So, is there any possible way to display individual cropped images.. also the number of cropped images might be in thousands so i want to use a loop to code their cropping mechanism
here is a part of the code attached.. thanks in advance ;)
for h=1:labelcount
for i=1:r
for j=1:c
if labelledmap(i,j)==h
if i<ltop
ltop=i;
end
if i>lbottom
lbottom=i;
end
if j<lleft
lleft=j;
end
if j>lright
lright=j;
end
end
end
end
if ltop>5
ltop=ltop-5;
end
if lbottom<r-5
lbottom=lbottom+5;
end
if lleft>5
lleft=lleft-5;
end
if lright<c-5
lright=lright+5;
end
lwidth=lright-lleft;
lheight=lbottom-ltop;
ltoken=imcrop(input,[lleft ltop lwidth lheight]);
figure('Name', 'Cropped Token'), imshow(ltoken);
clearvars ltoken;
end
you need to initialize ltop lbottom lleft and lright for each iteration of label h. I think this is the reason why you get the cropped images "glued" together.
It is EXTREMELY inefficient to go through all the pixels for each and every one of your labels. Especially when you are expected to have many labels.
Use regionprops to get the 'BoundingBox' property for each label.
Here's an example
st = regionprops( labelledmap, 'BoundingBox' );
imlist = cell( 1, numel(st) ); % pre-allocate
for ii=1:numel(st)
r = st(ii).BoundingBox;
% I understand you want to increase the BB by 5 pixels at each side:
r(1:2) = r(1:2) - 5; % start point moves -5
r(3:4) = r(3:4) + 10; % width and height increases by 10
imlist{ii} = imcrop( input, r );
end
I'm still a bit in shock by your code that explicitly loops through all pixels just for finding the bouding box. This is NOT the matlab way of doing things.
If you insist on NOT using regionprops here's a more Matlab-ish way of finding the ii-th bounding box:
imsk = (labeledmap == ii); % create a binary map with True for ii-th region
xFlat = any(imsk,1); % "flattening" imsk on the x-axis
lleft = find( xFlat, 1, 'first' );
lright = find( xFlat, 1, 'last' );
yFlat = any(imsk, 2);
ltop = find( yFlat, 1, 'first' );
lbottom = find( yFlat, 1, 'last' );
No loops at all over image coordinates.

MATLAB: vectorize filling of 3D-array

I would like to safe a certain amount of grayscale-images (->2D-arrays) as layers in a 3D-array.
Because it should be very fast for a realtime-application I would like to vectorize the following code, where m is the number of shifts:
for i=1:m
array(:,:,i)=imabsdiff(circshift(img1,[0 i-1]), img2);
end
nispio showed me a very advanced version, which you can see here:
I = speye(size(img1,2)); E = -1*I;
ii = toeplitz(1:m,[1,size(img1,2):-1:2]);
D = vertcat(repmat(I,1,m),E(:,ii));
data_c = shape(abs([double(img1),double(img2)]*D),size(data_r,1),size(data_r,2),m);
At the moment the results of both operations are not the same, maybe it shifts the image into the wrong direction. My knowledge is very limited, so I dont understand the code completely.
You could do this:
M = 16; N = 20; img1 = randi(255,M,N); % Create a random M x N image
ii = toeplitz(1:N,circshift(fliplr(1:N)',1)); % Create an indexing variable
% Create layers that are shifted copies of the image
array = reshape(img1(:,ii),M,N,N);
As long as your image dimensions don't change, you only ever need to create the ii variable once. After that, you can call the last line each time your image changes. I don't know for sure that this will give you a speed advantage over a for loop, but it is vectorized like you requested. :)
UPDATE
In light of the new information shared about the problem, this solution should give you an order of magnitudes increase in speed:
clear all;
% Set image sizes
M = 360; N = 500;
% Number of column shifts to test
ncols = 200;
% Create comparison matrix (see NOTE)
I = speye(N); E = -1*I;
ii = toeplitz([1:N],[1,N:-1:(N-ncols+2)]);
D = vertcat(repmat(I,1,ncols),E(:,ii));
% Generate some test images
img1 = randi(255,M,N);
img2 = randi(255,M,N);
% Compare images (vectorized)
data_c = reshape(abs([img2,img1]*D),M,N,ncols);
% Compare images (for loop)
array = zeros(M,N,ncols); % <-- Pre-allocate this array!
for i=1:ncols
array(:,:,i)=imabsdiff(circshift(img1,[0 i-1]),img2);
end
This uses matrix multiplication to do the comparisons instead of generating a whole bunch of shifted copies of the image.
NOTE: The matrix D should only be generated one time if your image size is not changing. Notice that the D matrix is completely independent of the images, so it would be wasteful to regenerate it every time. However, if the image size does change, you will need to update D.
Edit: I have updated the code to more closely match what you seem to be looking for. Then I throw the "original" for-loop implementation in to show that they give the same result. One thing worth noting about the vectorized version is that it has the potential to be very memory instensive. If ncols = N then the D matrix has N^3 elements. Even though D is sparse, things fall apart fast when you multiply D by the non-sparse images.
Also, notice that I pre-allocate array before the for loop. This is always good practice in Matlab, where practical, and it will almost invariably give you a large performance boost over the dynamic sizing.
If question is understood correctly, I think you need for loop
for v=1:1:20
array(:,:,v)=circshift(image,[0 v]);
end

Resources