MATLAB estimateUncalibratedRectification non-deterministic behavior - matlab

I have written the function below to perform image rectification. I am using just the standard MATLAB library functions (estimateUncalibratedRectification and estimateFundamentalMatrix) and my own wrapper function to MATLAB's matchFeatures to perform stereo rectification. Yet, with the same inputs I get different results every time. I know this is related to the use of RANSAC to estimate the fundamental matrix. However, the rectification is terrible sometimes and passable others. For example, over 10 different runs of my function with identical input, two results were okay, while 8 gave me variations on this error:
Warning: An epipole may be located inside of an image. The epipoles
are located at [285.8503,76.1656] in I1 and [265.5734,130.3931] in I2,
but the specified imageSize was [320,568]. Severe distortion may
result if T1 or T2 are used to transform these images. See
isEpipoleInImage for more information.
> In coder.internal.warning (line 7)
In cvalgEstimateUncalibratedRectification (line 114)
In estimateUncalibratedRectification (line 107)
In pairwiseTransformation (line 48)
I believe that this means that the rectification was unable to project the epipole to infinity.
What's going on here? It is worth noting that I have 279 putative matches and 32 inlierMatches between my images.
My function:
function [t1, t2] = pairwiseTransformation(img1, img2, features1, features2)
% Identify putative matches
[matches1, matches2] = matchFeaturePoints(rgb2gray(img1), features1, ...
rgb2gray(img2), features2);
% Estimate the fundamental matrix so that matches2' * F * matches1 = 0
% F transforms matches1 to a line that runs through the corresponding
% point in matches1. Therefore, any rotation and translation derived from F
% (and E) will apply to camera 2's relative position, holding camera 1 fixed.
[F, inliers] = estimateFundamentalMatrix(matches1, matches2, 'Method', 'RANSAC', ...
'NumTrials', 2000, 'DistanceThreshold', 1e-4);
% Use the RANSAC inliers to determine the relative position of img2 compared to img1
inlierMatches1 = matches1(inliers, :);
inlierMatches2 = matches2(inliers, :);
[t1, t2] = estimateUncalibratedRectification(F, inlierMatches1, inlierMatches2, ...
size(img1));
r1 = imwarp(img1, projective2d(t1), 'OutputView', imref2d(size(img1)));
r2 = imwarp(img2, projective2d(t2), 'OutputView', imref2d(size(img1)));
figure;
subplot(2,2,1),imshow(img1)
subplot(2,2,2),imshow(img2)
subplot(2,2,3),imshow(r1)
subplot(2,2,4),imshow(r2)
end
Here's a decent rectification (top row is original images, bottom is rectified):
And here's a totally botched effort that gave the epipole warning:

32 inlier matches seems too few... How do your putative matches look?
One thing to try is to tweak the parameters of estimateFundamentalMatrix. I would use MSAC instead of RANSAC, and increase the DistanceThreshold to something like .1 or even 1. At the same time you may want to increase the Confidence parameter, maybe to 99.99. That would force RANSAC to go through more trials, and increase your chances of finding the right solution.
The other thing to try is to get more and better putative matches from matchFeatures. You should try tweaking the parameters of your feature detector function to get more features, and then tweak the parameters of matchFeatures to make sure that the matches are still good. You can also try different detectors and descriptors.

Related

On 1x2 convolution and combining gradients

So I'm trying to understand convolution and the process on making gradients and I wanted to just see the horizontal gradient of the 1x2 operator on an image named I1. When I tried to use this code I only get a black screen so I'm trying to figure out what went wrong here,sans using conv of course. (I'm also going to try out Sobel too, so I'd like some tips on how to get that going.)
I1 = uint8(round(sum(C1,3)/3));
figure,imshow(I1);
Kern =[-1,1];
Omega = zeros([size(I1,1) size(I1,2)]);
for i=1:ROWS
for j=1:COLS
Work = double(I1(i,j)).*Kern;
Omega(i,j) = sum(Work(:));
end
end
figure,imshow(uint8(Omega));
The problem is that you're only using 1 pixel from I1 to multiply by your kernel. Since you're using one value, the end effect is:
a.*[-1 1]
which gives you
[-a a]
When you sum this, obviously you get zero. To fix this, you'll need to use the same number of pixels from I1 as you have elements in your kernel (in this case, 2). This will also mean that you need to adjust your loop indices:
for i=1:ROWS-1 % avoid accessing outside image
for j=1:COLS-1
Work = double(I1(i,j:j+1)).*Kern; % j:j+1 gives us 2 pixels
Omega(i,j) = sum(Work(:));
end
end
You can also condense the two lines inside the loop into one:
Kern = [-1;1]; % make Kern a column vector
...
for i=1:ROWS-1
for j=1:COLS-1
Omega(i,j) = double(I1(i,j:j+1))*Kern; % vector multiplication, not elementwise
end
end
Another thing you might want to try is use imagesc(Omega) instead of imshow. imagesc will scale the values of the image so it's more visible.

Difference between fsurf and ezsurf - MATLAB bug report

I have a problem with fsurf command:
When I use
fsurf(#(x,y) ackleyfcn([x,y]),[-32 32 -32 32])
I got this warning:
Warning: Function behaves unexpectedly on array inputs. To improve performance, properly vectorize your function to return an output with the same size and shape as the input arguments.
And this picture (after a long time, about 20 sec):
fsurf plot
When I use
ezsurf(#(x,y) ackleyfcn([x,y]),[-32 32 -32 32])
I got no warning and the correct (and fast) picture:
ezsurf plot
Where:
function z = ackleyfcn(xx)
% Ackley's function
% Search domain: [-32,32]
% Global minimum: f(x) = 0 | x = (0,...,0)
d = size(xx, 2);
xx = max(-32,min(32,xx));
z = -20*exp(-0.2*sqrt(1/d*sum(xx.^2,2))) - exp(1/d*sum(cos(2*pi*xx),2)) + 20 + exp(1);
end
I think this Ackley's function is correctly vectorized. Am I right?
What is the error with fsurf and why the image generated by it is strange and takes longer to be generated?
Thanks in advance!
Ps.: I am using R2017b version.
From the documentation:
The function must accept two matrix input arguments and return a matrix output argument of the same size.
You assumed that the inputs are column vectors.
To fix this you could try
function z = ackleyfcn(x,y)
xx = [x(:),y(:)];
% ... your code here
z = reshape(z,size(x));
and
fsurf(#ackleyfcn,[-32 32 -32 32])
MathWorks' Support Response:
I have been able to reproduce the slow down that you were experiencing. The fsurf function tries to determine what density of points to use in order to give an accurate depiction of the function you pass it. Since the ackleyfcn has many small oscillations, fsurf decides to use a very dense mesh in order to display it. This feature is not available in ezsurf which is why the plots look so different.
The time fsurf takes is also much longer because if it displaying many more points. If you would like to use fsurf to produce the plot similar to ezplot, you can turn off the AdaptiveMeshDenstity feature by using the following line of code:
set(fsurf(#(x,y) ackleyfcn([x,y]),[-32 32 -32 32]),'AdaptiveMeshDensity',0,'MeshDensity',60)
Note that this will increase the speed of fsurf and will produce a plot similar to that of ezsurf. However, this new surface uses fewer points and so does not fully represent the ackleyfcn function.

comparing generated data to measured data

we have measured data that we managed to determine the distribution type that it follows (Gamma) and its parameters (A,B)
And we generated n samples (10000) from the same distribution with the same parameters and in the same range (between 18.5 and 59) using for loop
for i=1:1:10000
tot=makedist('Gamma','A',11.8919,'B',2.9927);
tot= truncate(tot,18.5,59);
W(i,:) =random(tot,1,1);
end
Then we tried to fit the generated data using:
h1=histfit(W);
After this we tried to plot the Gamma curve to compare the two curves on the same figure uing:
hold on
h2=histfit(W,[],'Gamma');
h2(1).Visible='off';
The problem s the two curves are shifted as in the following figure "Figure 1 is the generated data from the previous code and Figure 2 is without truncating the generated data"
enter image description here
Any one knows why??
Thanks in advance
By default histfit fits a normal probability density function (PDF) on the histogram. I'm not sure what you were actually trying to do, but what you did is:
% fit a normal PDF
h1=histfit(W); % this is equal to h1 = histfit(W,[],'normal');
% fit a gamma PDF
h2=histfit(W,[],'Gamma');
Obviously that will result in different fits because a normal PDF != a gamma PDF. The only thing you see is that for the gamma PDF fits the curve better because you sampled the data from that distribution.
If you want to check whether the data follows a certain distribution you can also use a KS-test. In your case
% check if the data follows the distribution speccified in tot
[h p] = kstest(W,'CDF',tot)
If the data follows a gamma dist. then h = 0 and p > 0.05, else h = 1 and p < 0.05.
Now some general comments on your code:
Please look up preallocation of memory, it will speed up loops greatly. E.g.
W = zeros(10000,1);
for i=1:1:10000
tot=makedist('Gamma','A',11.8919,'B',2.9927);
tot= truncate(tot,18.5,59);
W(i,:) =random(tot,1,1);
end
Also,
tot=makedist('Gamma','A',11.8919,'B',2.9927);
tot= truncate(tot,18.5,59);
is not depending in the loop index and can therefore be moved in front of the loop to speed things up further. It is also good practice to avoid using i as loop variable.
But you can actually skip the whole loop because random() allows to return multiple samples at once:
tot=makedist('Gamma','A',11.8919,'B',2.9927);
tot= truncate(tot,18.5,59);
W =random(tot,10000,1);

Matlab error message regarding use of polyfit function in script file

I am trying to run a script file which draws a line on a plotted graph. Sometimes the script file works but occasionally it does not work and the line drawn on the graph is not where I want it. When the file does not work I get the following error message:
Warning: Polynomial is not unique. Degree >= number of data points.
> In polyfit at 70
In FIXING_force_and_rate_of_rise at 48
Does anyone know what this error message means? What does it mean when a polynomial is not unique?
If it helps, when I click on the error message I get taken to this explanation:
Solve least squares problem.
[Q,R] = qr(V,0);
ws = warning('off','all');
p = R\(Q'*y);
% Same as p = V\y;
warning(ws);
if size(R,2) > size(R,1)
warning(message('MATLAB:polyfit:PolyNotUnique'))
elseif warnIfLargeConditionNumber(R)
if nargout > 2
warning(message('MATLAB:polyfit:RepeatedPoints'));
else
warning(message('MATLAB:polyfit:RepeatedPointsOrRescale'));
end
A section of the code I am trying to run is pasted below. Line 48 is where I think the error is: [SlpBL2]=polyfit(Time_scale(300:T2), Force_trace(300:T2),1);
Reject the 1st 300 data points
BL1=min(Force_trace(300:end));
col='rcgy'
figure; set(gcf, 'windowstyle', 'docked', 'color', 'w')
for j=1:100
cla
Force_trace=Force_trace-BL1;
Peak1=max(Force_trace(300:end-30));
Extrapolation_point=Peak1*0.10;
D=Force_trace<Extrapolation_point;
T2=find(D,1,'last');
[SlpBL2]=polyfit(Time_scale(300:T2), Force_trace(300:T2),1);
Slope=SlpBL2(1);
BL2=SlpBL2(2);
plot(Time_scale, Force_trace)
hold on
plot([0, Time_scale(T2)], [BL2, Time_scale(T2)*Slope+BL2],col(1))
disp(['round number ' int2str(j) ' baseline is ' num2str(BL2)])
if abs(BL2)<1e-10
break
end
BL1=BL2;
drawnow
end
Thanks in advance for your help!
Code discussion
Since you are fitting a one-degree polynomial to data, passing less than two data points is the only reason that error would be thrown.
This will happen if T2 is always 300 or empty (meaning that D is all false.
Explanation
In order to exactly fit a polynomial of degree n, n+1 data points are required to uniquely determine the n+1 coefficients of the polynomial.
For example, a cubic has the form:
.
A cubic equation requires four points of data to uniquely determine the four coefficients.
With less than n+1 data points, an exact polynomial can not be created since, assuming there is an underlying function from which the data was drawn, there are an infinite number of solutions.
However, a polynomial whose coefficients form the smallest/shortest solution can readily be calculated; this is the least-squares solution.
This brief overview (section 2) gives a succinct overview of this and overdetermined solutions, which is more common in my experience.
Consider this input:
>> x = linspace(0,1,50);
>> y = x.^3 + x.^2;
>> p3 = polyfit(x([1,25,50]),y([1,25,50]),3);
Warning: Polynomial is not unique; degree >= number of data points.
> In polyfit at 70
>> p4 = polyfit(x([1,10,25,50]),y([1,10,25,50]),3);
and the plot here:
The undetermined solution follows the true function closely but not exactly.

How can I use of norm(a,b) in matlab if a, b are double type?

I must to use angle = atan2(norm(cross(a,b)),dot(a,b)), for calculating the angle between two vectors a,b and these are double type and norm is undefined for this type. How do I resolve this problem? I need to calculate the angle between two vectors this way.
In your comments, you have shown us how you are actually writing out the angle calculation and it is not the same as how you have put it in your post.
atan2(norm(cross(I(i,j,:),I_avg)),dot(I(i,j,:),I_avg));
I is an image you are loading in. I'm assuming it's colour because of the way you are subsetting I. Because I is a 3D matrix, doing I(i,j,:) will give you a 1 x 1 x 3 vector when in fact this has to be a 1D vector. norm does not recognize this structure which is why you're getting this error. Therefore, you need to use squeeze to remove the singleton dimensions so that this will become a 3 x 1 vector, rather than a 1 x 1 x 3 vector. As such, you need to rewrite your code so that you're doing this instead. Bear in mind that in your comments, angle is always overwritten inside the for loop, so you probably want to save the results of each pixel. With this, you probably want to create a 2D array of angles that will store these results. In other words:
I=imread('thesis.jpg');
I = double(I);
angles = zeros(m,n);
I_avg = squeeze(I_avg); %// Just in case
for i=1:m
for j=1:n
pixels = squeeze(I(i,j,:)); %// Add this statement and squeeze
angles(i,j) = atan2(norm(pixels,I_avg)),dot(pixels,I_avg)); %// Change
end
end
Minor note
MATLAB has a built-in function called angle that determines the angle from the origin to a complex number in the complex plane. It is not recommended you call your variable angle as this will unintentionally shadow over the angle function, and any other code that you create from this point onwards may rely on that actual angle function, and you will get unintended results.
Another minor note
Using i and j as loop variables is not recommended. These letters are reserved for the complex number, and this can produce unintentional results. Take a look at this question and post by Shai here - Using i and j as variables in Matlab. As such, it is suggested you use other variable names instead.
As #rayryeng has successfully answered this question, I would like to turn my post into a more general one by sharing my experience in debugging in Matlab. I hope anyone who somehow managed to find this post get more or less thinking about the habits a good programmer should have.
The question goes like: "How would I do if I get errors?"
Here's an excellent article by Eric in which he lists the rule-of-thumbs when you encounter a bug and wish to get rid of it. It's originally been cited by Stackoverflow, and that's the reason I read it.
If you still get no clue / idea how you can play with your code, see how this person does:
Pin-point the buggy line
(The number should start with 0) Make sure before running a script, you clear out any previously stored variables, including the notorious i and j's (you should never see them in any workspace). If any one is needed for the buggy code to run, save('buggy.mat','importantvar') before clear and load('buggy.mat') after clear.
By doing so, you can isolate your buggy code from anything else, which could have bad influences. For example, in a previously called script, there is a line
double = [2,4,6]; % you should never name a variable `double`
and in the next script, you have
>> e = str2num('uint8(200)')
e =
200
>> double(e)
Index exceeds matrix dimensions.
>>
>> f = single(2.36)
f =
2.3600
>> double(f)
Subscript indices must either be real positive integers or
logicals.
>>
The reason is double is no longer an inbuild function, but a user-defined variable. Too bad to pick up a name carelessly!
....anyway, let's clear the workspace and get rid of double.
>> clear
Read the error message, thoroughly.
Now let's begin with OP's problem. The original code (trimmed) goes like this -
img = imread('peppers.png');
a = img(300,200,:);
b = img(200,300,:);
d = norm(cross(a,b));
.... hence the error
Undefined function 'norm' for input arguments of type 'uint8'.
Error in untitled (line 6)
d = norm(cross(a,b));
Most beginners are only interested in the first line of the error message, which by it alone usually doesn't provide any useful help, or only in the red color, which leads to the famous question "my code does not work!"
But think twice. You still have another 2 lines unread! Error in untitled (line 6) says I'm running a script named untitled and the (first) error lies in line 6, and the code in that line is d = norm(cross(a,b));.
Now, at least you know a little more about your code - "My code d = norm(cross(a,b)); doesn't work!"
Although most likely we may also vote this kind of question to get closed, it's still much much better than a simply "It does not work!".
Now we can pin-point the buggy line
try
% this line will raise an error
d = norm(cross(a,b));
catch err
disp(err.message)
end
Look into the functions
First, make sure the inner function cross works as expected -
>> cross(a,b)
ans(:,:,1) =
0
ans(:,:,2) =
255
ans(:,:,3) =
0
>>
Good. So now we can even narrow down the error to the outer norm.
One more thing to mention. You can always find Mathworks' documentation for any in-build function, by typing "matlab function", such as "matlab norm" in Google (or any other search engine) and clicking on the first result. If you prefer, you can also type in Matlab command window doc _function_ such as doc norm and read the doc in Matlab. It's of course a pleasure of us on Stackoverflow to give you the reference by doing the same thing, but it takes a longer time because a human is, in this aspect, always slower than a search engine.
The error reads Undefined function 'norm' for input arguments of type 'uint8'.. So the input for norm should not be uint8, unsigned 8-bit integer. But what should it be?
% why `norm` "does not work"?
% this line runs perfectly well
norm(cross([1,2,3], [4,5,6]))
% so what is working?
class([1,2,3]) % so `norm` works for `double`
One thing we can do now is convert a and b to double precision. Let's try it now.
% try fixing 'uint8' error
a2 = double(a);
b2 = double(b);
whos a b % now they are double, which `norm` should work for
try
% this line will raise an error
d = norm(cross(a2,b2));
catch err
disp(err.message)
end
Now the error becomes Input must be 2-D.. What's wrong with the input?
% what is "must be 2-D" error?
size(a2) % a2 is 3-D
disp(b2) % b2 is also 3-D
This gives output in command window
ans =
1 1 3
(:,:,1) =
255
(:,:,2) =
150
(:,:,3) =
0
In OP's problem, he/she is trying to calculate something about color difference (to the best of my knowledge) which involves the angle between two color vectors in RGB space. So the vectors are needed. With imread, each pixel of the image is stored as 3 elements in the matrix, first 2 dimension being its physical position, the 3 dimension being RGB channel components. Hence pixel(200,300) with color rgb[255,150,0] is stored by us in variable b wihch is a 3-D vector.
By understanding what we need and what Matlab can do, we can combine these two points into one. We need the norm of the cross product of a and b, while the useful information (the 3 component values) is stored in the 3rd dimension. Matlab can calculate the norm of the cross product of a vector with all its information in the 1st dimension. (Here, "dimension" refers to that of the Matlab variable; a vector with 3 elements in its 1st dimension is physically a 3-D vector).
After thinking twice, we are now able to debug our code - just put all 3 elements into the 1st dimension.
% so we want the 3 elements in the 3rd dimension become in the 1st dim
a3 = squeeze(a2);
b3 = reshape(b2,numel(b2),[]);
try
d = norm(cross(a3,b3));
catch err
disp(err.message)
end
d
Bonus: If by default Matlab treats a 3-D vector as a "1-D array", then most probably the cross function has not been working correctly. Let's make a check -
>> clear
>> a = [1,2,3]
a =
1 2 3
>> b=[4,5,6]
b =
4 5 6
>> cross(a,b)
ans =
-3 6 -3
>>
The result should be the same as the one we can get by calculating by hand.
Now if we put the components into the 3rd dimension of the variable -
>> clear
>> a(1,1,:)=[1,2,3]
a(:,:,1) =
1
a(:,:,2) =
2
a(:,:,3) =
3
>> b(1,1,:)=[4,5,6]
b(:,:,1) =
4
b(:,:,2) =
5
b(:,:,3) =
6
>> cross(a,b)
ans(:,:,1) =
-3
ans(:,:,2) =
6
ans(:,:,3) =
-3
>>
.... seems OK. cross also puts the result in the 3rd dimension. In fact, Mathworks' documentation says
If A and B are vectors, then they must have a length of 3.
If A and B are matrices or multidimensional arrays, then they must
have the same size. In this case, the cross function treats A and B as
collections of three-element vectors. The function calculates the
cross product of corresponding vectors along the first array dimension
whose size equals 3.
At last, one thing is always correct to anyone who wants to do something with programming - be cautious and prudent when writing your code.