RGB to YIQ conversion - matlab

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.

Related

How to crop matrix of any number of dimensions in Matlab?

Suppose I have 4D matrix:
>> A=1:(3*4*5*6);
>> A=reshape(A,3,4,5,6);
And now I want to cut given number of rows and columns (or any given chunks at known dimensions).
If I would know it's 4D I would write:
>> A1=A(1:2,1:3,:,:);
But how to write universally for any given number of dimensions?
The following gives something different:
>> A2=A(1:2,1:3,:);
And the following gives an error:
>> A2=A;
>> A2(3:3,4:4)=[];
It is possible to generate a code with general number of dimension of A using the second form of indexing you used and reshape function.
Here there is an example:
Asize = [3,4,2,6,4]; %Initialization of A, not seen by the rest of the code
A = rand(Asize);
%% This part of the code can operate for any matrix A
I = 1:2;
J = 3:4;
A1 = A(I,J,:);
NewSize = size(A);
NewSize(1) = length(I);
NewSize(2) = length(J);
A2 = reshape(A1,NewSize);
A2 will be your cropped matrix. It works for any Asize you choose.
I recommend the solution Luis Mendo suggested for the general case, but there is also a very simple solution when you know a upper limit for your dimensions. Let's assume you have at most 6 dimensions. Use 6 dimensional indexing for all matrices:
A1=A(1:2,1:3,:,:,:,:);
Matlab will implicit assume singleton dimensions for all remaining dimension, returning the intended result also for matrices with less dimensions.
It sounds like you just want to use ndims.
num_dimensions = ndims(A)
if (num_dimensions == 3)
A1 = A(1:2, 1:3, :);
elseif (num_dimensions == 4)
A1 = A(1:2, 1:3, :, :);
end
If the range of possible matrix dimensions is small this kind of if-else block keeps it simple. It seems like you want some way to create an indexing tuple (e.g. (1:2,:,:,:) ) on the fly, which I don't know if there is a way to do. You must match the correct number of dimensions with your indexing...if you index in fewer dimensions than the matrix has, matlab returns a value with the unindexed dimensions collapsed into a single array (similar to what you get with
A1 = A(:);

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.

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

How to Split Images into parts using MATLAB

I got a satellite image of size [17935 10968] pixels, I want to cut image equally and process my required algorithm on individual parts (eg: I need to cut above pixel range into 4 equal parts).
How can I split image without loosing intermediate pixels? My requirement is like (1 to 5600 and 5601 to the end pixel).
And anybody got any idea how to split images that are this big in MATLAB?
Method 1
If you have the Image Processing Toolbox, this is the preferred and most efficient method. It utilizes the extremely useful blockproc function which is designed exactly for processing large image in blocks. For instance, it takes care of padding when your image does not divide equally into same size blocks and concatenates results from the processing of blocks into one result matrix.
Best you take a look at the official documentation, but here's how it would look like in your case:
vSize = [17935 10968];
imBig = rand([vSize 3]);
nParts = [2 2]; %means divide into 4 parts, 2 horizontal, 2 vertical
blockproc(imBig, ceil(vSize ./ nParts), #yourAlgorithm);
function res = yourAlgorithm(blockStruct)
%do your processing of the block here and
%optionally return a result in 'res'
%for example, just return the RGB vector of the first pixel
res = blockStruct.data(1,1,:);
end
Method 2
If you don't have the Image Processing Toolbox you can use the mat2cell function instead. Fisrt you figure out the required block sizes and then you get a cell array containing the different blocks. For such large images though, speed and memory may become an issue. The code is borrowed from this Matlab Central answer.
vSize = [17935 10968];
imBig = rand([vSize 3]);
nParts = [2 2]; %means divide into 4 parts, 2 horizontal, 2 vertical
%figure out the size of "regular" block and the last block
vRegBlockSize = ceil(vSize ./ nParts);
vLastBlockSize = vSize - vRegBlockSize .* (nParts - 1);
%put the sizes into a vector
vSplitR = [vRegBlockSize(1)*ones(1,nParts(1)-1), vLastBlockSize(1)];
vSplitC = [vRegBlockSize(2)*ones(1,nParts(2)-1), vLastBlockSize(2)];
%split the image
C = mat2cell(imBig, vSplitR, vSplitC, 3);
%access RGB pixel (x,y) in top left {1,1} block
p = C{1,1}(x, y, :);
upperLeft = theImage(1:5600, 1:5600, :);
upperRight = theImage(1:5600, 5601:end, :);
lowerLeft = theImage(5601:end, 1:5600, :);
lowerLeft = theImage(5601:end, 1:5601:end, :);
you can use reshape to make 4 matrices from the image:
A=reshape(Img, 17935 , 10968/4,[]);
then process A(:,:,1) , etc...
Use the following code to divide the image into 4 different images:
A=reshape(Img, 17935 , 10968/4, 3, []);
then A(:,:,:,1) is the first image.
Suppose A is your 17935x10968x3 matrix, I think you can do:
B = reshape(A, 17935, 10968 / 4, 4, 3);
In this way the last dimension still represents RGB. Only difference is that it becomes 4-D array.

Histogram Equalization method without use of histeq

I am new to Matlab and am trying to implement code to perform the same function as histeq without actual use of the function. In my code the image colour I get changes drastically when it should not change that much. The average intensity in the image (ranging between 0 and 255) is 105.3196. The image is of an open source pollen particle.
Any help would be much appreciated. The sooner the better! Please could any help be simplified as my Matlab understanding is limited. Thanks.
clc;
clear all;
close all;
pollenJpg = imread ('pollen.jpg', 'jpg');
greyscalePollen = rgb2gray (pollenJpg);
histEqPollen = histeq(greyscalePollen);
averagePollen = mean2 (greyscalePollen)
sizeGreyScalePollen = size(greyscalePollen);
rowsGreyScalePollen = sizeGreyScalePollen(1,1);
columnsGreyScalePollen = sizeGreyScalePollen(1,2);
for i = (1:rowsGreyScalePollen)
for j = (1:columnsGreyScalePollen)
if (greyscalePollen(i,j) > averagePollen)
greyscalePollen(i,j) = greyscalePollen(i,j) + (0.1 * averagePollen);
if (greyscalePollen(i,j) > 255)
greyscalePollen(i,j) = 255;
end
elseif (greyscalePollen(i,j) < averagePollen)
greyscalePollen(i,j) = greyscalePollen(i,j) - (0.1 * averagePollen);
if (greyscalePollen(i,j) > 0)
greyscalePollen(i,j) = 0;
end
end
end
end
figure;
imshow (pollenJpg);
title ('Original Image');
figure;
imshow (greyscalePollen);
title ('Attempted Histogram Equalization of Image');
figure;
imshow (histEqPollen);
title ('True Histogram Equalization of Image');
To implement the equalisation algorithm described on the Wikipedia page, follow these these steps:
Decide on a binSize to group greyscale values. (This is a tweakable, the larger the bin, the less accurate the result from the ideal case, but I think it can cause problems if chosen too small on real images).
Then, calculate the probability of a pixel being a shade of grey:
pixelCount = imageWidth * imageHeight
histogram = all zero
for each pixel in image at coordinates i, j
histogram[floor(pixel / 255 / 10) + 1] += 1 / pixelCount // 1-based arrays, not 0-based
// Note a technicality here: you may need to
// write special code to handle pixels of 255,
// because they will fall in their own bin. Or instead use rounding with an offset.
The histogram in this calculation is scaled (divided by the pixel count) so that the values make sense as probabilities. You can of course factor the division out of the for loop.
Now you need to calculate the accumulative sum of this:
histogramSum = all zero // length of histogramSum must be one bigger than histogram
for i == 1 .. length(histogram)
histogramSum[i + 1] = histogramSum[i] + histogram[i]
Now you have to invert this function and this is the tricky part. The best is to not calculate an explicit inverse, but calculate it on the spot, and apply it on the image. The basic idea is to search for the pixel value in the histogramSum (find the closest index below), and then do a linear interpolation between the index and the next index.
foreach pixel in image at coordinates i, j
hIndex = findIndex(pixel, histogramSum) // You have to write findIndex, it should be simple
equilisationFactor = (pixel - histogramSum[hIndex])/(histogramSum[hIndex + 1] - histogramSum[hIndex]) * binSize
// This above is the linear interpolation step.
// Notice the technicality that you need to handle:
// histogramSum[hIndex + 1] may be out of bounds
equalisedImage[i, j] = pixel * equilisationFactor
Edit: without drilling into the maths, I can't be 100% sure, but I think that division by 0 errors are possible. These can occur if one bin is empty, so consecutive sums are equal. So you need special code to handle this case too. The best you can do is take the value for the factor as halfway between hIndex, hIndex + n, where n is the highest value for which histogramSum[hIndex + n] == histogramSum[hIndex].
And that should be it, once you have dealt with all the technicalities.
The above algorithm is slow (especially in the findIndex step). You may be able to optimize this with a special lookup datastructure. But only do that when it's working, and only if necessary.
One more thing about your Matlab code: the rows and columns are inverted. Because of the symmetry in the algorithm, the result is the same, but it can cause puzzling bugs in other algorithms, and be very confusing if you examine pixel values during debugging. In the pseudocode above I used them the same as you, though.
Relatively few (5) lines of code can do this. I used a low contrast file called 'pollen.jpg' that I found at http://commons.wikimedia.org/wiki/File%3ALepismium_lorentzianum_pollen.jpg
I read it in using your code, run all the above, then do the following:
% find out the index of pixels sorted by intensity:
[gv gi] = sort(greyscalePollen(:));
% create a table of "approximately equal" intensity values:
N = numel(gv);
newVals = repmat(0:255, [ceil(N/256) 1]);
% perform lookup:
% the pixels in sorted order need new values from "equal bins" table:
newImg = zeros(size(greyscalePollen));
newImg(gi) = newVals(1:N);
% if the size of the image doesn't divide into 256, the last bin will have
% slightly fewer pixels in it than the others
When I run this algorithm, and then create a composite of the four images (original, your attempt, my attempt, and histeq), you get the following:
I think it's convincing. The images are not exactly identical - I believe that is because the matlab histeq routine ignores all pixels with value 0. Since it is fully vectorized it is also pretty fast (although not nearly as fast as histeq by about a factor 15 on my image.
EDIT: a bit of explanation might be in order. The repmat command I use to create the newVals matrix creates a matrix that looks like this:
0 1 2 3 4 ... 255
0 1 2 3 4 ... 255
0 1 2 3 4 ... 255
...
0 1 2 3 4 ... 255
Since matlab stores matrices in "first index first" order, if you read this matrix with a single index (as I do in the line newVals(1:N)), you access first all the zeros, then all the ones, etc:
0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 ...
So - when I know the indices of the pixels in the order of their intensity (as returned by the second argument of the sort command, which I called gi), then I can easily assign the value 0 to the first N/256 pixels, the value 1 to the next N/256 etc, with the command I used:
newImg(gi) = newVals(1:N);
I hope this makes the code a little easier to understand.