Converting subsripts and indices - matlab

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.

Related

optimizing a matlab code involving for loop

I have a Matlab code that goes like this
s=[0.5 0.6 0.7];
u=[0.3618 0.9826 0.7237 0.0346 0.5525 0.0692 0.8949 0.1384
0.3418 0.9226 0.7213 0.0346 0.7525 0.0692 0.8949 0.1384
0.3318 0.9326 0.7237 0.0336 0.5575 0.0792 0.8949 0.1385]
A= u(1:2:7); % Here u is a 1-D vector and hence A
B=u(2:2:8); % Here u is a 1-D vector and hence B
C=mod(s(1)-(A+B),1);
I want to implement this code for the other two values of s also using next 8 values of u i.e now my code becomes
A=u(9:2:15);
B=u(10:2:16);
C=mod(s(2)-(A+B),1);
Similarly for last value of s. But each time i need the next 8 values of u. How do i code this so that it takes less time.
So you start with a 24 element array in u that you wish to perform this operation on in a vectorized fashion. I assume you have many more elements but that they all fit in memory. The way to do this is to reshape u to where you want the elements to be. You can do this via:
u1 = reshape(u1,[2,4,3]);
From there you also need to modify s to match it
s1 = permute(s,[1 3 2]);
Finally, you can calculate your C matrix in vectorized form
C1 = mod(s1-sum(u1),1);
For this problem, this gives a 1x4x3 matrix where the 3rd dimension represents each set of 8. From there you can then extract the problem set you want
C = squeeze(C1(1,:,1));

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

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.

Split matrix into non-overlapping submatrices

I have an image of any size that I need to split into non-overlapping regions of 15x15 pixels. Previously I looked this up and used the following code:
newIm = rand(size(im2, 1),size(im2, 2));
subIm = mat2cell(newIm, 15*ones(size(newIm,1)/15,1), 15*ones(size(newIm,2)/15,1));
My problem is that I may not always be able to split the matrix into nice 15x15 regions. How can I fix this? Also, can you explain what exactly happens? I'd like to understand Matlab better for future use!
If you use the above code with a size not perfectly divisible by 15 (say 160), you will get the following error in MATLAB:
Input arguments, D1 through D2, must sum to each dimension of the input matrix size, [160 160].'
So you have to make your second and third input arguments of mat2cell sum to 160. Then you are done.
Code taken from here
blockSize=[15 15];
wholeBlockRows = floor(size(newIm,1)/ blockSize(1));
blockVectorR = [blockSize(1) * ones(1, wholeBlockRows), rem(size(newIm,1), blockSize(1))];
wholeBlockCols = floor(size(newIm,2)/ blockSize(2));
blockVectorC = [blockSize(2) * ones(1, wholeBlockCols), rem(size(newIm,2), blockSize(2))];
% sum of blockVectorR and blockVectorC will be equal to size(newIm,1) and
% size(newIm,2) respectively.
ca = mat2cell(newIm, blockVectorR, blockVectorC, size(newIm,3));
In your output cell array, you will see sub-images in the last row and column where either rows or columns (or both) are equal to: rem(size(newIm,1), blockSize(1)) or (and) rem(size(newIm,2), blockSize(2))

Count pixels with certain indices that have a certain value in Matlab

I have two vectors xx and yy holding the x and y indices of certain pixels respectively in matrix A . What I want to do is to check the values of the pixels with those indices and count how many of those pixels have the value 0. For example, if xx=[1 2 3] and y=[2 5 8], I want to check how many of these pixels(x,y) (1,2), (2,5), (3,8) have the value 0. I can do this with for loops but I think it can be done easier in Matlab, so if anyone could please advise.
The following should work:
sum(A(sub2ind(size(A),xx,yy)) == 0)
First, you convert the row and column indices into single indices into the matrix A. Then, you check where A is zero for these indices (which will result in ones). Then you simply sum up the ones.
A dirtier way than sub2ind is
sum( A( [1 size(A,1)]*( [ yy; xx ] - 1 ) + 1 ) == 0 )
You can check here and see that the dirty method is ~x4 times faster than sub2ind. So, if you are in need for speed, use the dirty method ;)