Manually changing RGB to HSI matlab - matlab

I would like to preface this by saying, I know some functions, including RGB2HSI could do this for me, but I would like to do it manually for a deeper understanding.
So my goal here is to change my RGB image to HSI color scheme. The image is in .raw format, and i am using the following formulas on the binary code to try and convert it.
theta = arccos((.5*(R-G) + (R-B))/((R-G).^2 + (R-B).*(G-B)).^.5);
S = 1 - 3./(R + G + B)
I = 1/3 * (R + G + B)
if B <= G H = theta if B > G H = 360 - theta
So far I have tried two different things, that have resulted in two different errors. The first attempted was the following,
for iii = 1:196608
C(iii) = acosd((.5*(R-G) + (R-B))/((R-G).^2 + (R-B).*(G-B)).^.5);
S(iii) = 1 - 3./(R + G + B);
I(iii) = 1/3 * (R + G + B);
end
Now in attempting this I knew it was grossly inefficent, but I wanted to see if it was a viable option. It was not, and the computer ran out of memory and refused to even run it.
My second attempt was this
fid = fopen('color.raw');
R = fread(fid,512*384*3,'uint8', 2);
fseek(fid, 1, 'bof');
G = fread(fid, 512*384*3, 'uint8', 2);
fseek(fid, 2, 'bof');
B = fread(fid, 512*384*3, 'uint8', 2);
fclose(fid);
R = reshape(R, [512 384]);
G = reshape(G, [512 384]);
B = reshape(B, [512 384]);
C = acosd((.5*(R-G) + (R-B))/((R-G).^2 + (R-B).*(G-B)).^.5);
S = 1 - 3./(R + G + B);
I = 1/3 * (R + G + B);
if B <= G
H = B;
if B > G
H = 360 - B;
end
end
H = H/360;
figure(1);
imagesc(H * S * I)
There were several issues with this that I need help with. First of all, the matrix 'C' has different dimensions than S and I so multiplication is impossible, so my first question is, how would I call up each pixel so I could perform the operations on them individually to avoid this dilemma.
Secondly the if loops refused to work, if I put them after "imagesc" nothing would happen, and if i put them before "imagesc" then the computer would not recognize what variable H was. Where is the correct placement of the ends?

Normally, the matrix 'C' have same dimensions as S and I because:
C = acosd((.5*(R-G) + (R-B))/((R-G).^2 + (R-B).*(G-B)).^.5);
should be
C = acosd((.5*(R-G) + (R-B))./((R-G).^2 + (R-B).*(G-B)).^.5);
elementwise division in the middle was missing . Another point is:
if B <= G
H = B;
if B > G
H = 360 - B;
end
end
should be
H = zeros(size(B));
H(find(B <= G)) = B(find(B <= G));
H(find(B > G)) = 360 - B(find(B > G));

Related

Simplifying function by removing a loop

What would be the best way to simplify a function by getting rid of a loop?
function Q = gs(f, a, b)
X(4) = sqrt((3+2*sqrt(6/5))/7);
X(3) = sqrt((3-2*sqrt(6/5))/7);
X(2) = -sqrt((3-2*sqrt(6/5))/7);
X(1) = -sqrt((3+2*sqrt(6/5))/7);
W(4) = (18-sqrt(30))/36;
W(3) = (18+sqrt(30))/36;
W(2) = (18+sqrt(30))/36;
W(1) = (18-sqrt(30))/36;
Q = 0;
for i = 1:4
W(i) = (W(i)*(b-a))/2;
X(i) = ((b-a)*X(i)+(b+a))/2;
Q = Q + W(i) * f(X(i));
end
end
Is there any way to use any vector-like solution instead of a for loop?
sum is your best friend here. Also, declaring some constants and creating vectors is useful:
function Q = gs(f, a, b)
c = sqrt((3+2*sqrt(6/5))/7);
d = sqrt((3-2*sqrt(6/5))/7);
e = (18-sqrt(30))/36;
g = (18+sqrt(30))/36;
X = [-c -d d c];
W = [e g g e];
W = ((b - a) / 2) * W;
X = ((b - a)*X + (b + a)) / 2;
Q = sum(W .* f(X));
end
Note that MATLAB loves to handle element-wise operations, so the key is to replace the for loop at the end with scaling all of the elements in W and X with those scaling factors seen in your loop. In addition, using the element-wise multiplication (.*) is key. This of course assumes that f can handle things in an element-wise fashion. If it doesn't, then there's no way to avoid the for loop.
I would highly recommend you consult the MATLAB tutorial on element-wise operations before you venture onwards on your MATLAB journey: https://www.mathworks.com/help/matlab/matlab_prog/array-vs-matrix-operations.html

How can I get a proper double form for a symbolic matrix?

How can I get a proper double form from this symbolic Matrix in MatLab? I've tried everything but I prefer not using feval or inline function as they're not recommended
This is the code to get the matrix
function T = Romberg (a, b, m, f)
T = zeros(m, m);
T = sym(T);
syms f(x) c h;
f(x) = f;
c = (subs(f,a)+subs(f,b)) / 2;
h = b - a;
T(1,1) = h * c;
som = 0 ;
n = 2;
for i = 2 : m
h = h / 2;
for r = 1 : n/2
som = som + subs(f,(a + 2*(r-1)*h));
T(i,1) = h * (c + som);
n = 2*n;
end
end
r = 1;
for j = 2 : m
r = 4*r;
for i = j : m
T(i,j) = (r * T(i, j-1) - T(i-1,j-1)/(r-1));
end
end
end
And with an input like this
Romberg(0, 1, 4, '2*x')
I get a symbolic matrix output with all the
3 * f(3)/2 + f(1)/2 + f(5)/2
I would like to have a double output.
Can you please help me?
Thank you very much in advance!

Not enough input arguments error in matlab

This is my matlab code , I got Not enough input argument error in line 2 and i don't know how to fix it. Anyhelp ? Thanks in advance .
function [] = Integr1( F,a,b )
i = ((b - a)/500);
x = a;k = 0; n = 0;
while x <= b
F1 = F(x);
x = x + i;
F2 = F(x);
m = ((F1+F2)*i)/2;
k = k +m;
end
k
x = a; e = 0; o = 0;
while x <= (b - 2*i)
x = x + i;
e = e + F(x);
x = x + i;
o = o + F(x);
end
n = (i/3)*(F(a) + F(b) + 2*o + 4*e)
This code performs integration by the trapezoidal rule. The last line of code gave it away. Please do not just push the Play button in your MATLAB editor. Don't even think about it, and ignore that it's there. Instead, go into your Command Prompt, and you need to define the inputs that go into this function. These inputs are:
F: A function you want to integrate:
a: The starting x point
b: The ending x point
BTW, your function will not do anything once you run it. You probably want to return the integral result, and so you need to modify the first line of your code to this:
function n = Integr1( F,a,b )
The last line of code assigns n to be the area under the curve, and that's what you want to return.
Now, let's define your parameters. A simple example for F is a linear function... something like:
F = #(x) 2*x + 3;
This defines a function y = 2*x + 3. Next define the starting and ending points:
a = 1; b = 4;
I made them 1 and 4 respectively. Now you can call the code:
out = Integr1(F, a, b);
out should contain the integral of y = 2*x + 3 from x = 1 to x = 4.

How to Vectorize Dependent For-Loops

I'm working on a function that takes a 1xn vector x as input and returns a nxn matrix L.
I'd like to speed things up by vectorizing the loops, but there's a catch that puzzles me: loop index b depends on loop index a. Any help would be appreciated.
x = x(:);
n = length(x);
L = zeros(n, n);
for a = 1 : n,
for b = 1 : a-1,
c = b+1 : a-1;
if all(x(c)' < x(b) + (x(a) - x(b)) * ((b - c)/(b-a))),
L(a,b) = 1;
end
end
end
From a quick test, it looks like you are doing something with the lower triangle only. You might be able to vectorize using ugly tricks like ind2sub and arrayfun similar to this
tril_lin_idx = find(tril(ones(n), -1));
[A, B] = ind2sub([n,n], tril_lin_idx);
C = arrayfun(#(a,b) b+1 : a-1, A, B, 'uniformoutput', false); %cell array
f = #(a,b,c) all(x(c{:})' < x(b) + (x(a) - x(b)) * ((b - c{:})/(b-a)));
L = zeros(n, n);
L(tril_lin_idx) = arrayfun(f, A, B, C);
I cannot test it, since I do not have x and I don't know the expected result. I normally like vectorized solutions, but this is maybe pushing it a bit too much :). I would stick to your explicit for-loop, which might be much clearer and which Matlab's JIT should be able to speed up easily. You could replace the if with L(a,b) = all(...).
Edit1
Updated version, to prevent wasting ~ n^3 space on C:
tril_lin_idx = find(tril(ones(n), -1));
[A, B] = ind2sub([n,n], tril_lin_idx);
c = #(a,b) b+1 : a-1;
f = #(a,b) all(x(c(a, b))' < x(b) + (x(a) - x(b)) * ((b - c(a, b))/(b-a)));
L = zeros(n, n);
L(tril_lin_idx) = arrayfun(f, A, B);
Edit2
Slight variant, which does not use ind2sub and which should be more easy to modify in case b would depend in a more complex way on a. I inlined c for speed, it seems that especially calling the function handles is expensive.
[A,B] = ndgrid(1:n);
v = B<A; % which elements to evaluate
f = #(a,b) all(x(b+1:a-1)' < x(b) + (x(a) - x(b)) * ((b - (b+1:a-1))/(b-a)));
L = false(n);
L(v) = arrayfun(f, A(v), B(v));
If I understand your problem correctly, L(a, b) == 1 if for any c with a < c < b, (c, x(c)) is “below” the line connecting (a, x(a)) and (b, x(b)), right?
It is not a vectorization, but I found the other approach. Rather than comparing all c with a < c < b for each new b, I saved the maximum slope from a to c in (a, b), and used it for (a, b + 1). (I tried with only one direction, but I think that using both directions is also possible.)
x = x(:);
n = length(x);
L = zeros(n);
for a = 1:(n - 1)
L(a, a + 1) = 1;
maxSlope = x(a + 1) - x(a);
for b = (a + 2):n
currSlope = (x(b) - x(a)) / (b - a);
if currSlope > maxSlope
maxSlope = currSlope;
L(a, b) = 1;
end
end
end
I don't know your data, but with some random data, the result is the same with original code (with transpose).
An esoteric answer: You could do the calculations for every a,b,c from 1:n, exclude the don't cares, and then do the all along the c dimension.
[a, b, c] = ndgrid(1:n, 1:n, 1:n);
La = x(c)' < x(b) + (x(a) - x(b)) .* ((b - c)./(b-a));
La(b >= a | c <= b | c >= a) = true;
L = all(La, 3);
Though the jit would probably do just fine with the for loops since they do very little.
Edit: still uses all of the memory, but with less maths
[A, B, C] = ndgrid(1:n, 1:n, 1:n);
valid = B < A & C > B & C < A;
a = A(valid); b = B(valid); c = C(valid);
La = true(size(A));
La(valid) = x(c)' < x(b) + (x(a) - x(b)) .* ((b - c)./(b-a));
L = all(La, 3);
Edit2: alternate last line to add the clause that c of no elements is true
L = all(La,3) | ~any(valid,3);

Matlab: Fsolve giving incorrect roots

I'm trying to solve this system:
x = a + e(c - e*x/((x^2+y^2)^(3/2)))
y = b + c(d - e*y/((x^2+y^2)^(3/2)))
I'm using fsolve, but not matter what I put in as the starting points for the iteration, I get the answer that the starting points are the roots of the equation.
close all, clear all, clc
a = 1;
b = 2;
c = 3;
d = 4;
e = 5;
fsolve(#u1FsolveFUNC, [1,2])
Function:
function outvar = u1FsolveFUNC(invar)
global a b c d e
outvar = [...
-invar(1) + a + e*(c - e*(invar(1) / ((invar(1)^2 + invar(2)^2)^(3/2)))) ;
-invar(2) + b + e*(d - e*(invar(2) / ((invar(1)^2 + invar(2)^2)^(3/2))))]
end
I could try with [1,2] as invariables, and it will say that that is a root to the equation, alltough the correct answer for [1,2] is [12.76,15.52]
Ideas?
If you write your script like this
a = 1;
b = 2;
c = 3;
d = 4;
e = 5;
f = #(XY) [...
-XY(1) + a + e*(c - e*(XY(1) ./ (XY(1).^2 + XY(2).^2).^(3/2)))
-XY(2) + b + e*(d - e*(XY(2) ./ (XY(1).^2 + XY(2).^2).^(3/2)))];
fsolve(f, [1,2])
it is a lot clearer and cleaner. Moreover, it works :)
It works because you haven't declared a,b,c,d and e to be global before you assigned values to them. You then try to import them in your function, but at that time, they are still not defined as being global, so MATLAB thinks you just initialized a bunch of globals, setting their initial values to empty ([]).
And the solution to an empty equation is the initial value (I immediately admit, this is a bit counter-intuitive).
So this equation involves some inverse-square law...Gravity? Electrodynamics?
Note that, depending on the values of a-e, there may be multiple solutions; see this figure:
Solutions are those points [X,Y] where Z is simultaneously zero for both equations. for the values you give, there is a point like that at
[X,Y] = [15.958213798693690 13.978039302961506]
but also at
[X,Y] = [0.553696225634946 0.789264790080377]
(there's possibly even more...)
When you are using global command you have to use the command with all the variables in each function (and main workspace).
eg.
Main script
global a b c d e % Note
a = 1; b = 2; c = 3; d = 4; e = 5;
fsolve(#u1FsolveFUNC,[1,2])
Function
function outvar = u1FsolveFUNC(invar)
global a b c d e % Note
outvar = [-invar(1) + a + e*(c - e*(invar(1) / ((invar(1)^2 + invar(2)^2)^(3/2)))) ; -invar(2) + b + e*(d - e*(invar(2) / ((invar(1)^2 + invar(2)^2)^(3/2))))]