I'm trying to make a kind of hsv histogram by converting the rgb values of a picture (80*120).
This is the code:
function Image_histogram = hsvHistogram(path, count_bins)
Image = double(imread(path));
Image_histogram = zeros(3 * count_bins);
[n m] = size(Image);
H_vect = zeros(n, m);
S_vect = zeros(n, m);
V_vect = zeros(n, m);
hue_vect = zeros(1, count_bins);
saturation_vect = zeros(1, count_bins);
value_vect = zeros(1, count_bins);
for line = 1 : n
for row = 1 : m
%[H_vect(line, row), S_vect(line, row), V_vect(line, row)] = rgb2hsv(Image(line, row, 1), Image(line, row, 2), Image(line, row, 3));
endfor
endfor
number = 100/count_bins;
for count = 0 : count_bins - 1
left = (number * count);
right = (number * count + number);
hue_vect(1, count + 1) = (sum(sum(H_vect(:,:) >= left & H_vect(:,:) < right)));
saturation_vect(1, count + 1) = (sum(sum(S_vect(:,:) >= left & S_vect(:,:) < right)));
value_vect(1, count + 1) = (sum(sum(V_vect(:,:) >= left & V_vect(:,:) < right)));
endfor
Image_histogram = horzcat(hue_vect, saturation_vect, value_vect);
endfunction
When i try to get the HSV matrix i always get the error : hsvHistogram: A(I,J,...): index to dimension 2 out of bounds; value 121 out of bound 120
rgb2hsv is a pixel by pixel converter. It converts R G B to H S V. It is not the built-in rgb2hsv function. The commented line seems to be the one with problems.
The problem is in the size() function.
If such image is RGB, the matrix Image will be a 3D matrix but in your size() function you just gather two outputs, which will lead to incorrect results.
You must gather all three outputs (for all three dimensions) and then eventually discard the third one (which we know it is 3). Try doing:
[n,m,~]=size(Image);
More into details, if your matrix has size n x m x q but you ask just for two outputs, like
[a,b]=size(Image);
you’ll have a=n and b=m*q.
These results are obviously incorrect because since q>1 then b>m where m is (again) the actual dimension size and you’ll experience the out-of-bounds error. In other words, the loop will run from 1 to b whereas the matrix has only dimension m (which is less than b).
As instead you must gather all three dimensions separately:
[a,b,c]=size(Image);
and (as above) eventually discard some unnecessary output arguments (thanks to the tilde ~ operator).
Related
I'm trying to create an adaptive elliptical structuring element for an image to dilate or erode it. I write this code but unfortunately all of the structuring elements are ones(2*M+1).
I = input('Enter the input image: ');
M = input('Enter the maximum allowed semi-major axes length: ');
% determining ellipse parameteres from eigen value decomposition of LST
row = size(I,1);
col = size(I,2);
SE = cell(row,col);
padI = padarray(I,[M M],'replicate','both');
padrow = size(padI,1);
padcol = size(padI,2);
for m = M+1:padrow-M
for n = M+1:padcol-M
a = (l2(m-M,n-M)+eps/l1(m-M,n-M)+l2(m-M,n-M)+2*eps)*M;
b = (l1(m-M,n-M)+eps/l1(m-M,n-M)+l2(m-M,n-M)+2*eps)*M;
if e1(m-M,n-M,1)==0
phi = pi/2;
else
phi = atan(e1(m-M,n-M,2)/e1(m-M,n-M,1));
end
% defining structuring element for each pixel of image
x0 = m;
y0 = n;
se = zeros(2*M+1);
row_se = 0;
for i = x0-M:x0+M
row_se = row_se+1;
col_se = 0;
for j = y0-M:y0+M
col_se = col_se+1;
x = j-y0;
y = x0-i;
if ((x*cos(phi)+y*sin(phi))^2)/a^2+((x*sin(phi)-y*cos(phi))^2)/b^2 <= 1
se(row_se,col_se) = 1;
end
end
end
SE{m-M,n-M} = se;
end
end
a, b and phi are semi-major and semi-minor axes length and phi is angle between a and x axis.
I used 2 MATLAB functions to compute the Local Structure Tensor of the image, and then its eigenvalues and eigenvectors for each pixel. These are the matrices l1, l2, e1 and e2.
This is the bit of your code I didn't understand:
a = (l2(m-M,n-M)+eps/l1(m-M,n-M)+l2(m-M,n-M)+2*eps)*M;
b = (l1(m-M,n-M)+eps/l1(m-M,n-M)+l2(m-M,n-M)+2*eps)*M;
I simplified the expression for b to (just removing the indexing):
b = (l1+eps/l1+l2+2*eps)*M;
For l1 and l2 in the normal range we get:
b =(approx)= (l1+0/l1+l2+2*0)*M = (l1+l2)*M;
Thus, b can easily be larger than M, which I don't think is your intention. The eps in this case also doesn't protect against division by zero, which is typically the purpose of adding eps: if l1 is zero, eps/l1 is Inf.
Looking at this expression, it seems to me that you intended this instead:
b = (l1+eps)/(l1+l2+2*eps)*M;
Here, you're adding eps to each of the eigenvalues, making them guaranteed non-zero (the structure tensor is symmetric, positive semi-definite). Then you're dividing l1 by the sum of eigenvalues, and multiplying by M, which leads to a value between 0 and M for each of the axes.
So, this seems to be a case of misplaced parenthesis.
Just for the record, this is what you need in your code:
a = (l2(m-M,n-M)+eps ) / ( l1(m-M,n-M)+l2(m-M,n-M)+2*eps)*M;
b = (l1(m-M,n-M)+eps ) / ( l1(m-M,n-M)+l2(m-M,n-M)+2*eps)*M;
^ ^
added parentheses
Note that you can simplify your code by defining, outside of the loops:
[se_x,se_y] = meshgrid(-M:M,-M:M);
The inner two loops, over i and j, to construct se can then be written simply as:
se = ((se_x.*cos(phi)+se_y.*sin(phi)).^2)./a.^2 + ...
((se_x.*sin(phi)-se_y.*cos(phi)).^2)./b.^2 <= 1;
(Note the .* and .^ operators, these do element-wise multiplication and power.)
A further slight improvement comes from realizing that phi is first computed from e1(m,n,1) and e1(m,n,2), and then used in calls to cos and sin. If we assume that the eigenvector is properly normalized, then
cos(phi) == e1(m,n,1)
sin(phi) == e1(m,n,2)
But you can always make sure they are normalized:
cos_phi = e1(m-M,n-M,1);
sin_phi = e1(m-M,n-M,2);
len = hypot(cos_phi,sin_phi);
cos_phi = cos_phi / len;
sin_phi = sin_phi / len;
se = ((se_x.*cos_phi+se_y.*sin_phi).^2)./a.^2 + ...
((se_x.*sin_phi-se_y.*cos_phi).^2)./b.^2 <= 1;
Considering trigonometric operations are fairly expensive, this should speed up your code a bit.
I want to verify the convolution theorem in matlab.
Firstly, I do a 2D discrete convolution of a 2D Gaussian with
an image graymap(x, y).
Secondly, I compute the Fourier Transform of
the same 2D Gaussian and of the original image. Then perform a scalar multiplication
of these two Fourier Transforms, followed by an inverse Fourier Transform of the result.
Finally, I will calculate the MSE between the two results. However, I found the err is 800+.
This is my code:
[row, col] = size(graymap);
[row_2, col_2] = size(z);
result = zeros(row, col);
for i = 1: col
for j = 1:row
accumulation_value = 0;
for k = -4:4
for h = -4:4
if ((i+k > 0 && i+k < col + 1) && (j+h > 0 && j+h < row + 1))
value_image = double(graymap(i+k, j+h));
else
value_image = 0;
end
accumulation_value = accumulation_value + value_image * double(z(5 + k, 5 + h));
weighted_sum = weighted_sum + z(5 + k, 5 + h);
end
end
result(i,j) = (accumulation_value);
end
result_blur_1 = uint8(255*mat2gray(result));
M = size(graymap,1);
N = size(graymap,2);
resIFFT = ifft2(fft2(double(graymap), M, N) .* fft2(double(z), M, N));
result_blur_2 = uint8(255*mat2gray(resIFFT));
err = immse(result_blur_1, result_blur_2);
z is the 9*9 gaussian kernel. I don't flip it because it is symmetric.
I think my implementation of convolution is correct because the result is same as conv2(graymap, z, 'same').
Therefore, I believe there are something wrong with the second part. In fact, I am confused on how padding works. May it is the cause of the big MSE.
There are indeed problems with your implementation of the second part. The most important rule to remember when implementing convolution via fft is that you are actually calculating a circular convolution, not a linear convolution. Fortunately, there is a condition under which the two become equivalent. This condition is that the two arrays should be zero-padded to have a size equal to the sum of the sizes of each minus 1 (in all dimensions). So if you are working with an image X of size MxN, and a mask Z of size PxQ, then you should pad the two arrays with zeros to so they have at least dimensions M+P-1xN+Q-1. Any additional zeros won't hurt, so it's convenient to match a 'fft-friendly' size if possible (using nextpow2 for example). You just have to take the first M+P-1xN+Q-1 values.
Now, that would work straight forward if you just wanted the full result of the convolution. But because you want the central part of the convolution (the option 'same'), you need to select the correct indexes. The first index will be ceil(([P Q] - 1)/2) + 1, and then you take as many consecutive indexes as the image size.
Here is an example putting all together:
M = randperm(1024,1);
N = randperm(1024,1);
X = rand(M,N);
P = randperm(64,1);
Q = randperm(64,1);
Z = rand(P,Q);
% 'standard' convolution with option 'same'
C1 = conv2(X,Z,'same');
R = 2^nextpow2(M+P-1);
S = 2^nextpow2(N+Q-1);
% convolution with fft. Notice the zero-padding to R,S
C2 = real(ifft2(fft2(X,R,S) .* fft2(Z,R,S)));
n = ceil(([P Q] - 1)/2);
ind{1} = n(1) + (1:M);
ind{2} = n(2) + (1:N);
C2 = C2(ind{:});
err = immse(C1,C2)
I get errors of the order of 1e-26
I have x and y data that has n number of points in each of the arrays.
I want to use polyfit on portions of the data.
I want to divide the data into a certain number of divisions(numDivisions).
My idea would be to do something along the lines of
n= size(x)%number of data points
numDivisions = 4;%number of times to divide the data
div = zeros(numDivisions,1)%number of points per division
p = zeros(numDivisions,4);% second number is degree of polynomial+1
S = zeros(numDivisions,1);
mu = zeros(numDivisions,1);
E = zeros(numDivisions,1);
for i = 1:numDivisions
div(i) = round(n(1,1)*i/numDivisions) %assign markers for divisions of points
end
for i = 1:size(div)
if i == 1
start = 1;
endpoint = div(i);
[p(i), S(i), mu(i)] = polyfit(x(start:endpoint), y(start:endpoint), 3);
else
[p(i), S(i), mu(i)] = polyfit(x(div(i-1):div(i)), y(div(i-1):div(i)), 3);
end
end
The goal would be to have an array of p values from the polyfits.
However, when I run it I get this error:
In an assignment A(I) = B, the number of elements in B
and I must be the same.
Error in (line 33)
[p(i), S(i), mu(i)] =
polyfit(x(start:endpoint),
y(start:endpoint), 3);
In Matlab, apparently, I get different values for width and height when I use size in these two ways:
% way 1
[height, width] = size(myLoadedImage);
% way 2
height = size(myLoadedImage, 1);
width = size(myLoadedImage, 2)
Why are these two ways different?
Read the complete help for the size function. Specifically it says
[d1,d2,d3,...,dn] = size(X), for n > 1, returns the sizes of the dimensions of
the array X in the variables d1,d2,d3,...,dn, provided the number of output
arguments n equals ndims(X).
If n does not equal ndims(X), the following exceptions hold:
n < ndims(X) di equals the size of the ith dimension of X for 0<i<n, but dn
equals the product of the sizes of the remaining dimensions of X,
that is, dimensions n through ndims(X).
As shown by your comment, your image is a 3-dimensional array. So as per the manual, if you are asking for only 2 out of the 3 sizes with [h,w] = size(...), the parameter w will contain the product of the 2nd and 3rd dimension. When doing h = size(..., 1) and w = size(..., 2), you get the exact values of the first and second dimensions.
Simulating your case:
>> im = randn(512, 143, 3);
>> h = size(im, 1)
h = 512
>> w = size(im, 2)
w = 143
>> [h, w] = size(im)
h = 512
w = 429
Note that in the last case w = 143 * 3 = 429.
In addition to #BasSwinckels explanation, you can write:
[h,w,~] = size(img);
This will work for any number of dimensions (grayscale image, RGB image, or higher dimension arrays).
I have a huge waveform matrix:
[w,fs] = wavread('file.wav');
length(w)
ans =
258048
I want to go through this matrix in segments (say 50) and get the maximum of these segments to compare it to another value. I tried this:
thold = max(w) * .04;
nwindows = 50;
left = 1;
right = length(w)/nwindows;
counter = 0;
for i = 1:nwindows
temp = w(left:right);
if (max(temp) > thold)
counter = counter + 1;
end
left = right;
right = right+right;
end
But MATLAB threw tons of warnings and gave me this error:
Index exceeds matrix dimensions.
Error in wlengthdur (line 17)
temp = w(left:right);
Am I close or way off course?
An alternative approach would be to use reshaped to arrange you vector in to a 2D matrix with number of row n and columns equal to ceil(length(w) / n) i.e. round up so that it is divisible as matlab matrices must be rectangular. This way you can find the max or whatever you need in one step without looping.
w = randn(47, 1);
%this needs to be a column vector, if yours isn't call w = w(:) to ensure that it is
n = 5;
%Pad w so that it's length is divisible by n
padded = [w; nan(n - mod(length(w), n), 1)];
segmented_w = reshape(padded, n, []);
max(segmented_w)