How to use logical indexing instead of for loops for mapping interpolated image values? - matlab

My program transforms an original equirectangular image into an azimuthal orthographic projection.
This is done by locating every pixel on the projection image (destination) on the equirectangular image (input). Since the positions on the equirectangular image are often non inter numbers because of the projection (trigonomical and other functions) the color values of the projection images pixels have to be interpolated from the color values of the source image.
im_source is a 3 dimensional matrix, ordered by (row, column, color_channel), containing the value of the particular color channel
m_source_q is a 2 dimensional matrix, containing the row of the interpolation (query) grid, so for a 1024*512 original image and a grid precision of 0.1 pixels m_source_q looks like:
[1 1 1 1 .... 1
1.1 1.1 1.1 1.1 .... 1.1
1.2 1.2 1.2 1.2 ..... 1.2
...
512 512 512 512 .... 512]
n_source_q likewise contains the query column position
[1 1.1 1.2 1.3 .... 1024
1 1.1 1.2 1.3 .... 1024
...
1 1.1 1.2 1.3 .... 1024]
p_er is a 3-D matrix. it is a two-dimensional map that contains the fractioned coordinates of the interpolated original image for every pixel on the projection image. so for example p_er(502, 262, 1) == 259.9 (corresponding row in original image); p_er(592, 252, 2) == 513.2 (corresponding column in original image).
imq is a 3-D matrix that contains all interpolated color values for every color channel and for every grid position. imq is obtained by interpolation:
for idim=1:3
imq(:,:,idim) = interp2(n_source, m_source, imsource(:,:,idim), n_source_q, m_source_q, 'linear');
end;
note that idim is the index of the color channel (R: 1, G: 2, B: 3)
imp_double is a 3-D matrix that contains the (double type / real number) color values of all color channels for every projection image pixel. this
will just be converted to projection_image = uint8(imp_double).
The last step is mapping the projections pixels to the corresponding interpolated color values:
for idim=1:3
imq_idim = imq(:,:,idim);
for m=1:Hp % Hp .. height of projection image
for n=1:Wp % Wp .. width of projection image
if isRealPixel(m,n)
imp_double(m,n,idim)=imq_idim(m_source_q==per(m,n,1) & n_source_q==per(m,n,2));
end;
end;
end;
end;
This works, but it takes really too long, and I'm pretty sure that the two nested for loops are the cause of this.
My question: How can I avoid the for loops by logical indexing, or by some other method?
I tried
for idim=1:3
imp_double(:,:,idim) = imq_idim(m_source_q==per(:,:,1) & n_source_q==per(:,:,2));
end;
but the interpreter complains the sizes of the matrices don't match. Apparently I have to tell MATLAB over what index vector (is that how you call it?) the function should be applied and the two colons are not sufficient. Do I need the matrices m_source and n_source, which are just the real source images row, rsp. col positions, for that purpose?

Return indices of matrix A that match an item in matrix B
out = ismember(A,B);
out in this case will have the shape of A, and contains only items that are in both A and B.
Partial Solution
This assumes imq_idim is the same size as m_source. If that's not the case and per(:,:,1) is the same size as img_idim then flip the arguments.
This also assumes that what you are trying to do is find any m_source values that are in per(:,:,1). If you are not trying to do this, then you also need to look at your looped solution, because that is what is currently happening there
for idim=1:3
imp_double(:,:,idim) = imq_idim(ismember(m_source_q,per(:,:,1)) & ....
ismember(n_source_q,per(:,:,2));
end;
Dimension Mismatch
The above code is correct on the right-side but won't pass to the left-side correctly in imp_double(:,:,idim) = ... %
Left-side
imp_double(:,:,idim) is asking for a matrix that is the same size as the first two dimensions of imp_double.
Right-side
imq_idim(ismember(m_source_q,per(:,:,1)) & ... ismember(n_source_q,per(:,:,2)); returns a one-dimensional, variable sized vector (depends on how many matches we get).
Solution
a) Make the right-side a matrix imp_double(:,:,idim) but with correct values changed
% imp_double MUST BE PREALLOCATED
temp = imp_double(:,:,idim); % temp is a 2D matrix
temp(ismember(m_source_q,per(:,:,1)) & ....
ismember(n_source_q,per(:,:,2))) = ...
imq_idim(ismember(m_source_q,per(:,:,1)) & ....
ismember(n_source_q,per(:,:,2)));
imp_double(:,:,idim) == temp;
b) Specify the indices to be changed in imp_double
This would be the easy solution with something like the below. The problem is, you can't index a 3D matrix with a 2D logical followed by a 3rd index. Any solution here will be redundant with a) but slightly more complicated
% Doesn't work because can't index imp_double(mat,ind)
imp_double(ismember(m_source_q,per(:,:,1)) & ....
ismember(n_source_q,per(:,:,2)),...
idim)) = ...
imp_double(:,:,idim) = imq_idim(ismember(m_source_q,per(:,:,1)) & ....
ismember(n_source_q,per(:,:,2));

Related

Finding Location of matrices within a structure in matlab

I am importing an RGB image U of the stars and doing the following:
im=rgb2gray(U);
img=(im>200);
BW=im2bw(img,0);
L=bwlabeln(BW,18);
b=regionprops(L,'PixelList');
The goal of this program is to find the largest and most prominent stars in this picture of hundreds of stars. b is a 2566x1 struct array that contains all the points with a value greater than 200. If a certain connected region within the image contains multiple values over 200, b will store a coordinate matrix of these points. Otherwise, it will only store a single coordinate pair.
I need a way to find all the rows within b that contain matrices? If possible, a way to find all the rows within b that contain matrices that contain 30 or more points?
You can use the arrayfun function to apply a function to each element in an array. Note that this is just a shorter way of writing a loop.
In this case you'd need to apply the function size(b(i).PixelList, 1) > 30 to each element i of the struct array b:
m = arrayfun(#(x)size(x.PixelList, 1) > 1, b)
This is identical to:
m = false(size(b));
for i=1:numel(b)
m(i) = size(b(i).PixelList, 1) > 30;
end
The matrix m is a logical array, you can use it to index as b(m). You can also get indices using find(m).
If you also include 'Area' in the properties calculated by regionprops, you'll already have the number of pixels in each component:
b=regionprops(L,'PixelList','Area');
idx = [b.Area] >= 30;

How can I easily create a dynamic 2D matrix of vectors in Matlab

Given my code:
G=zeros(height,width); %zeros or whatever
for y = 1 : height
for x = 1 : width
magnitude = sqrt(Gx(y,x)^2 + Gy(y,x)^2);
gradient_direction = atan(Gy(y,x)/Gx(y,x));
G(y,x) = [magnitude gradient_direction];
end
end
I keep getting this (if I don't use zeros()):
Subscripted assignment dimension mismatch.
or this:
Assignment has more non-singleton rhs dimensions than non-singleton
subscripts
While #atru answer works, I would want to suggest a vectorize way that faster and neater, if that will help. The operations here can easily converted to vectorized operations:
G=cat(3,hypot(Gy,Gx),atan(Gy./Gx));
By using G(y,x,:) = [magnitude, gradient_direction]; you are attempting to assign two values to a spot reserved for a single value with indices (y,x). One way to fix this is to use a 3 dimensional array G instead,
G=zeros(height,width,2);
for y = 1 : height
for x = 1 : width
magnitude = sqrt(Gx(y,x)^2 + Gy(y,x)^2);
gradient_direction = atan(Gy(y,x)/Gx(y,x));
G(y,x,:) = [magnitude, gradient_direction];
end
end
Now at each point G(y,x) you can store both of the values and access them as for instance G(1,2,1) for magnitude at (1,2) position and G(1,2,2) for gradient_direction. This assumes Gx and Gy are both arrays with size height x width.
Important thing to note is that slices of G along the third dimension will also be 3D arrays, i.e. mag_dir = G(3,2,:) will have a size [1 1 2] and not [1 2]. This may cause errors in some applications, examples include trying to concatenate mag_dir with another vector (that does not have the extra dimension) and linear algebra operations.
To resolve this, use reshape to explicitly change the dimensions to to target ones. For the vector here it would be reshape(mag_dir, 1, 2). Same holds for 2D slices like more_md = G(1,:,:) - this will need for instance more_md = reshape(more_md,2,5).

Converting subsripts and indices

I'm having a hard time figuring out transformation between spaces using ind2sub and sub2ind. Could someone help? The problem is as follows:
I have a mask Y (or region-of-interest) in which voxel values are either 1 or zero: Y=72x72x33 double. I then find all the voxels with value of 1 (there are 15 of them) then use ind2sub to get the x y z coordinates for these voxels:
indx = find(Y>0);
[x,y,z] = ind2sub(size(Y),indx);
XYZ = [x y z]';
Since there are 15 voxels with the value of 1, I end up with XYZ=3x15 double, containing coordinates of these 15 voxels, something like this:
25 26 24 25 26 ...26
28 28 29 29 29 ...30
8 8 8 8 8 ...9
Based on some arbitrary criteria, I remove 6 voxels so XYZ become 3x9 double. Let's call this new_XYZ. Now I would like to transform this new_XYZ back into a mask (let's call it new_Y). I tried this:
new_Y=sub2ind(size(Y),new_XYZ);
Here, I probably did something wrong with the sub2ind since new_Y didn't give me what I expected. The dimensions are also not 72x72x33. The old mask is a sphere so I expect the new mask to be close to that. Instead, I get a straight line. Can someone help me with the transformation?
Thanks.
A.
There are a couple of things wrong in your approach. To detail I'll use a slightly smaller sample data set, but the explanation can scale up to any size.
Y = randi(10,5,5,3)-5 ;
This create a 5x5x3 containing random integers numbers from -5 to 5 (so a good chance to have about half of them positive (>0).
Before I go further, you can get a direct mask of your condition in the same shape of your matrix:
mask_positive_Y = Y>0 ;
>> whos mask_positive_Y
Name Size Bytes Class Attributes
mask_positive_Y 5x5x3 75 logical
This gives you an array of logical (boolean), the same size of your matrix, containing 0 (false) everywhere and 1 (true) where your condition is validated (Y>0).
If you can work directly with this mask, you do not need to use ind2sub and sub2ind back and forth. For example, doing Y(mask_positive_Y) = NaN; would replace all the values indicated by the mask with NaN.
Since you want to modify the mask (remove some points), you may still need to get their indices, in which case you can simply call:
indx = find( mask_positive_Y ) ; %// will give the same result as: indx = find(Y>0);
Now let's assume you got your indices the way you specified, removed the 6 points and got your new_XYZ matrice. The way to rebuild the mask is as follow.
ind2sub gave you 3 vectors as output, and right enough, sub2ind will expect 3 vectors as input (not a 3 column matrix as you did). So you will have to decompose your new_XYZ into 3 vectors before you send it to sub2ind.
new_indx = sub2ind(size(Y), new_XYZ(:,1) , new_XYZ(:,2) , new_XYZ(:,3) );
But don't forget that you did transpose your result matrix when you did XYZ = [x y z]';, so make sure your new_XYZ is also transposed back with new_XYZ = new_XYZ.' ; before you decompose it in the code line above (or simply send the lines of new_XYZ instead of the columns as showed).
By the way, the proper transpose shorthand notation is .' (and not simply ' which is the Complex conjugate transpose).
Now this new_indx is only a new vector of linear indices, homogeneous to the indx you had earlier. You could already use this to assign values under the mask, but if you want a new mask the same shape than your matrix Y, you have to go a bit further:
new_Ymask = false( size(Y) ) ; %// build an empty mask (false everywhere)
new_Ymask(new_indx) = true ; %// assign true to the masked values
This will be the same size as your initial matrix Y, but also the same size as the first boolean mask I showed you mask_positive_Y.

RGB to YIQ conversion

I wrote code for rgb to yiq conversion.I get results but i don't know if this is correct.
%extract the red green blue elements
ImageGridRed = double(ImageRGB(:,:,1))';
ImageGridGreen = double(ImageRGB(:,:,2))';
ImageGridBlue = double(ImageRGB(:,:,3))';
%make the 300x300 matrices into 1x90000 matrices
flag = 1;
for i =1:1:300
for j = 1:1:300
imageGR(flag) = ImageGridRed(j,i);
imageGG(flag) = ImageGridGreen(j,i);
imageGB(flag) = ImageGridBlue(j,i);
flag = flag+1;
end
end
%put the 3 matrices into 1 matrix 90000x3
for j=1:1:300*300
colorRGB(j,1) = imageGR(j);
colorRGB(j,2) = imageGG(j);
colorRGB(j,3) = imageGB(j);
end
YIQ = rgb2ntsc([colorRGB(:,1) colorRGB(:,2) colorRGB(:,3)]);
I wrote this because the rgb2ntsc function needs mx3 matrix for input.I use the number 300 beacuse the picture is 300x300 pixels.I am going to seperate the picture in blocks in my project so dont give attention to the 300 number because i am going to change that, i put it just as an example.
thank you.
What you're doing is completely unnecessary. If you consult the documentation on rgb2ntsc, it also accepts a RGB image. Therefore, when you put in a RGB image, the output will be a 3 channel image, where the first channel is the luminance, or Y component and the second and third channels are the hue and saturation information (I and Q respectively). You don't need to decompose the image into a M x 3 matrix.
Therefore, simply do:
YIQ = rgb2ntsc(ImageRGB);
Make sure that ImageRGB is a RGB image where the first channel is red, second is green and third is blue.
Edit
With your comments, you want to take all of the pixels and place it into a M x 3 matrix where M is the total number of pixels. You would use this as input into rgb2ntsc. The function accepts a M x 3 matrix of RGB values where each row is a RGB tuple. The output in this case will be another M x 3 matrix where each row is its YIQ counterpart. Your code does do what you want it to do, but I would recommend that you do away with the for loops and replace it with:
colorRGB = reshape(permute(ImageRGB, [3 1 2]), 3, []).';,
After, do YIQ = rgb2ntsc(colorRGB);. colorRGB will already be a M x 3 matrix, so that column indexing you're doing is superfluous.
With the above using reshape and permute, it's very unnecessary to use the loops. In fact, I would argue that the for loop code is slower. Stick with the above code to get this done fast. Once you have your matrix in this fashion, then I suppose the code is doing what you want it to do.... however, I would personally just do a conversion on the image itself, then split it up into blocks or whatever you want to do after the fact.

Translating a matlab command to c#

Im new to matlab and i having trouble understanding this line of code
A((i-1)*nneg+1:i*nneg,:) =
ones(nneg,1)*temp(i,2:n+1)+
temp(npos+1:npos+nneg,2:n+1);
does this mean that ->
each element in A where x is being varied between :
(i-1)*nneg+1 and the upper bound i*nneg and for all y, will have assigned 1* .....
an element from temp or all elements in the range of the y (temp(i,2:n+1))?
and by the same reasoning one of the range of temp(npos+1:npos+nneg,2:n+1) or all added up?
The command updates some horizontal sub-matrix of A
A(a:b, :) = some range of rows, and ALL columns = some horizontal sub-matrix of A
A(:, c:d) = some range of columns, and ALL rows = = some vertical sub-matrix of A
UPDATE:
Without seeing more of your code, i cant be sure but the syntax suggests that temp(npos+1:npos+nneg,2:n+1) is a matrix, and ones(nneg,1)*temp(i,2:n+1) is offcourse also a matrix of the same size which contains only 1's.
(i-1)*nneg+1 and i*nneg will both be integers, where (i-1)*nneg+1 <= i*nneg. these two integers define a sub-matrix of A which will have their values updated.
ones(nneg,1) creates a vertical array of ones [1,1,1,1...] with length nneg. this is then multiplies with a horizontal array temp(i,2:n+1) which creates a matrix X. X is added to another matrix temp(npos+1:npos+nneg,2:n+1), and the sub-matrix of A (explained above) is updated with this result.