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

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));

Related

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

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

How to add element to array in MatLab?

I am trying to make a graph of the brightness of a pixel vs the distance from center of that pixel. To do so I used for loops to check each pixel for these values. But when adding them to my array I find that I can't. One of the issues is I have to define the array size first so no values get placed in the right spot. I believe everything else to be working except adding values to the arrays.
I've tried various methods of concatenation to add the values of each pixel to the array. I didn't have any more solutions to try.
folder3 = 'C:\Users\slenka\Desktop\Image_Analysis\Subtracted';
cd('C:\Users\slenka\Desktop\Image_Analysis\Subtracted');
subtractedFiles = [dir(fullfile(folder3,'*.TIF')); dir(fullfile(folder3,'*.PNG')); dir(fullfile(folder3,'*.BMP')); dir(fullfile(folder3,'*.jpg'))];
numberOfSubImages= length(subtractedFiles);
for b = 1 : numberOfSubImages
subFileName=fullfile(folder3, subtractedFiles(b).name);
chartImage=imread(subFileName);
[chartY, chartX, chartNumberOfColorChannels] = size(chartImage);
ccY= chartY/2;
ccX= chartX/2;
c=[ccX,ccY];
distanceArray=zeros(1,chartX);
intensityArray=zeros(1,chartY);
f=1;
g=1;
for y=1:chartY
for x=1:chartX
D = sqrt((y - c(1)) .^ 2 + (x - c(2)) .^ 2);
grayScale= impixel(chartImage, x, y);
distanceArray(f)=[D];
intensityArray(g)=[grayScale];
f=f+1;
g=g+1;
end
end
xAxis=distanceArray;
yAxis=intensityArray;
plot(xAxis,yAxis);
end
I'm expecting 2 arrays one full of the data values for the light intensity of each pixel in the image, and another for that pixels distance from the center of the image. I am wanting to plot these two arrays as the y and x axis respectively. At the moment the actual results is an entirely empty array full of zeros.

In Matlab how can I efficiently apply an operation with a 1D coefficient set to each colour of an image when stored in a 3D array using matrix math?

I have a large image in a 3D array with axes image[y,x,colour] I wish to apply the same operation using a 3 element array for the coefficients.
For example I am currently doing the following:
rgb(:,:,1) = (rgb(:,:,1) - kBlackOffset(1)) * gain(1);
rgb(:,:,2) = (rgb(:,:,2) - kBlackOffset(2)) * gain(2);
rgb(:,:,3) = (rgb(:,:,3) - kBlackOffset(3)) * gain(3);
It seems like I ought to be able to do this in a more efficient way in a single line.
Simply permute your offsets and gain to the 3rd dimensions.
Here is a quick and dirty example:
rgb = imread('peppers.png');
rgb = im2double(rgb);
gain = 1:3;
kBlackOffset = 1:3;
rgb = rgb - reshape(gain,[1 1 3]).*reshape(kBlackOffset,[1 1 3]);
If you're using an older version of MATLAB you may need to do this:
rgb = imread('peppers.png');
rgb = im2double(rgb);
gain = 1:3;
kBlackOffset = 1:3;
rgb = bsxfun(#minus,rgb,reshape(gain,[1 1 3]).*reshape(kBlackOffset,[1 1 3]));

Store arrays in index in a nested for loop in matlab

I have 50 images, stored as arrays in a 1x50 cell index called AllImages. Basically I want to make a new index with arrays that contain elements in the same position of the 50 arrays.
I want to see how each pixel in the same spot of the 50 images changes in the 50 images.
Theoretically, I would get an index of arrays with 50 elements each, because I want the first element of each of the 50 arrays in its own array, the second element of each of the 50 arrays in its own array, so on and so forth.
So far, here is my code:
for m = 1:5000 % number of pixels per image
for n = 1:50 % for the 50 images, all the same size
pixels(n) = allImages{n}(m)
end
allpixels{m} = pixels
end
I end up getting a 1x50 cell index for allpixels, even though I want 5000. I'm not sure what I did wrong.
Is there an easier way to do this or fix the code? Thanks so much!
are the images of the same size?
in that case first change them to a matrix using cell2mat
[i,j] = size(allImages{1})
n = numel(allImages)
allImages = cell2mat(allImages);
allImages = reshape(allImages,[i,j,n]);
because now you can just select your pixel. for example:
pixel = squeeze(allImages(1,1,:))
To get them all in a new cell you could permute and reshape your matrix
allImages = permute(allImages ,[3 1 2]);
allImages = reshape(allImages ,[n,i*j]);
pixels = mat2cell(allImages,n,ones([1,i*j]));
But for most mathematical operations it is easier to just keep them as one matrix.
As two rules of thumb in matlab you want to use matrices as much as possible, and avoid for-loops.

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;

Resources