How to fit rotated hyperbola accurately? - curve-fitting

My experimental data points looks like pieces of hyperbola. Below I provide a code (Matlab), which generates "dummy" data, which is very similar to original one:
function [x_out,y_out,alpha1,alpha2,ecK,offsetX,offsetY,branchDirection] = dummyGenerator(mu_alpha)
alpha_range=0.1;
numberPoint2Return=100; % number of points to return
ecK=10.^((rand(1)-0.5)*2*2); % eccentricity-related parameter
% slope of the first asimptote (radians)
alpha1 = ((rand(1)-0.5)*alpha_range+mu_alpha);
% slope of the first asimptote (radians)
alpha2 = -((rand(1)-0.5)*alpha_range+mu_alpha);
beta = pi-abs(alpha1-alpha2); % angle between asimptotes (radians)
branchDirection = datasample([0,1],1); % where branch directed
% up: branchDirection==0;
% down: branchDirection==1;
% generate branch
x = logspace(-3,2,numberPoint2Return*100)'; %over sampling
y = (tan(pi/2-beta)*x+ecK./x);
% rotate branch using branchDirection
theta = -(pi/2-alpha1)-pi*branchDirection;
% get rotation matrix
rotM = [ cos(theta), -sin(theta);
sin(theta), cos(theta) ];
% get rotated coordinates
XY1=[x,y]*rotM;
x1=XY1(:,1); y1=XY1(:,2);
% remove possible Inf
x1(~isfinite(y1))=[];
y1(~isfinite(y1))=[];
% add noise
y1=((rand(numel(y1),1)-0.5)+y1);
% downsampling
%x_out=linspace(min(x1),max(x1),numberPoint2Return)';
x_out=linspace(-10,10,numberPoint2Return)';
y_out=interp1(x1,y1,x_out,'nearest');
% randomize offset
offsetX=(rand(1)-0.5)*50;
offsetY=(rand(1)-0.5)*50;
x_out=x_out+offsetX;
y_out=y_out+offsetY;
end
Typical results are presented on figure:
The data has following important property: slopes of both asymptotes comes from the same distribution (just different signs), so for my fitting I have rather goo estimation for mu_alpha.
Now starts the problematic part. I try to fit these data points. The main idea of my approach is to find a rotation to obtain y=k*x+b/x shape and then just fit it.
I use the following code:
function Rsquare = fitFunction(x,y,alpha1,alpha2,ecK,offsetX,offsetY)
R=[];
for branchDirection=[0 1]
% translate back
xt=x-offsetX;
yt=y-offsetY;
% rotate back
theta = (pi/2-alpha1)+pi*branchDirection;
rotM = [ cos(theta), -sin(theta);
sin(theta), cos(theta) ];
XY1=[xt,yt]*rotM;
x1=XY1(:,1); y1=XY1(:,2);
% get fitted values
beta = pi-abs(alpha1-alpha2);
%xf = logspace(-3,2,10^3)';
y1=y1(x1>0);
x1=x1(x1>0);
%x1=x1-min(x1);
xf=sort(x1);
yf=(tan(pi/2-beta)*xf+ecK./xf);
R(end+1)=sum((xf-x1).^2+(yf-y1).^2);
end
Rsquare=min(R);
end
Unfortunately this code works not good, very often I have bad results, even when I use known(from simulation) initial parameters.
Could You help me to find a good solution for such fitting problem?
UPDATE:
I find a solution (see Answer), but
I still have a small problem - my estimation of aparameter is bad, sometimes I did no have good fits because of this reason.
Could You suggest some ideas how to estimate a from experimental point?

I found the main problem (it was my brain as usually)! I did not know about general equation of hyperbola. So equation for my hyperbolas are:
((x-x0)/a).^2-((y-y0)/b).^2=-1
So ,we may not take care about sign, then I may use the following code:
mu_alpha=pi/6;
[x,y,alpha1,alpha2,ecK,offsetX,offsetY,branchDirection] = dummyGenerator(mu_alpha);
% hyperb=#(alpha,a,x0,y0) tan(alpha)*a*sqrt(((x-x0)/a).^2+1)+y0;
hyperb=#(x,P) tan(P(1))*P(2)*sqrt(((x-P(3))./P(2)).^2+1)+P(4);
cost =#(P) fitFunction(x,y,P);
x0=mean(x);
y0=mean(y);
a=(max(x)-min(x))./20;
P0=[mu_alpha,a,x0,y0];
[P,fval] = fminsearch(cost,P0);
hold all
plot(x,y,'-o')
plot(x,hyperb(x,P))
function Rsquare = fitFunction(x,y,P)
%x=sort(x);
yf=tan(P(1))*P(2)*sqrt(((x-P(3))./P(2)).^2+1)+P(4);
Rsquare=sum((yf-y).^2);
end
P.S. LaTex tags did not work for me

Related

How to create a mask or detect image section based on the intensity value?

I have a matrix named figmat from which I obtain the following pcolor plot (Matlab-Version R 2016b).
Basically I only want to extract the bottom red high intensity line from this plot.
I thought of doing it in some way of extracting the maximum values from the matrix and creating some sort of mask on the main matrix. But I'm not understanding a possible way to achieve this. Can it be accomplished with the help of any edge/image detection algorithms?
I was trying something like this with the following code to create a mask
A=max(figmat);
figmat(figmat~=A)=0;
imagesc(figmat);
But this gives only the boundary of maximum values. I also need the entire red color band.
Okay, I assume that the red line is linear and its values can uniquely be separated from the rest of the picture. Let's generate some test data...
[x,y] = meshgrid(-5:.2:5, -5:.2:5);
n = size(x,1)*size(x,2);
z = -0.2*(y-(0.2*x+1)).^2 + 5 + randn(size(x))*0.1;
figure
surf(x,y,z);
This script generates a surface function. Its set of maximum values (x,y) can be described by a linear function y = 0.2*x+1. I added a bit of noise to it to make it a bit more realistic.
We now select all points where z is smaller than, let's say, 95 % of the maximum value. Therefore find can be used. Later, we want to use one-dimensional data, so we reshape everything.
thresh = min(min(z)) + (max(max(z))-min(min(z)))*0.95;
mask = reshape(z > thresh,1,n);
idx = find(mask>0);
xvec = reshape(x,1,n);
yvec = reshape(y,1,n);
xvec and yvec now contain the coordinates of all values > thresh.
The last step is to do some linear polynomial over all points.
pp = polyfit(xvec(idx),yvec(idx),1)
pp =
0.1946 1.0134
Obviously these are roughly the coefficients of y = 0.2*x+1 as it should be.
I do not know, if this also works with your data, since I made some assumptions. The threshold level must be chosen carefully. Maybe some preprocessing must be done to dynamically detect this level if you really want to process your images automatically. There might also be a simpler way to do it... but for me this one was straight forward without the need of any toolboxes.
By assuming:
There is only one band to extract.
It always has the maximum values.
It is linear.
I can adopt my previous answer to this case as well, with few minor changes:
First, we get the distribution of the values in the matrix and look for a population in the top values, that can be distinguished from the smaller values. This is done by finding the maximum value x(i) on the histogram that:
Is a local maximum (its bin is higher than that of x(i+1) and x(i-1))
Has more values above it than within it (the sum of the height of bins x(i+1) to x(end) < the height of bin x):
This is how it is done:
[h,x] = histcounts(figmat); % get the distribution of intesities
d = diff(fliplr(h)); % The diffrence in bin height from large x to small x
band_min_ind = find(cumsum(d)>size(figmat,2) & d<0, 1); % 1st bin that fit the conditions
flp_val = fliplr(x); % the value of x from large to small
band_min = flp_val(band_min_ind); % the value of x that fit the conditions
Now we continue as before. Mask all the unwanted values, interpolate the linear line:
mA = figmat>band_min; % mask all values below the top value mode
[y1,x1] = find(mA,1); % find the first nonzero row
[y2,x2] = find(mA,1,'last'); % find the last nonzero row
m = (y1-y2)/(x1-x2); % the line slope
n = y1-m*x1; % the intercept
f_line = #(x) m.*x+n; % the line function
And if we plot it we can see the red line where the band for detection was:
Next, we can make this line thicker for a better representation of this line:
thick = max(sum(mA)); % mode thickness of the line
tmp = (1:thick)-ceil(thick/2); % helper vector for expanding
rows = bsxfun(#plus,tmp.',floor(f_line(1:size(A,2)))); % all the rows for each column
rows(rows<1) = 1; % make sure to not get out of range
rows(rows>size(A,1)) = size(A,1); % make sure to not get out of range
inds = sub2ind(size(A),rows,repmat(1:size(A,2),thick,1)); % convert to linear indecies
mA(inds) = true; % add the interpolation to the mask
result = figmat.*mA; % apply the mask on figmat
Finally, we can plot that result after masking, excluding the unwanted areas:
imagesc(result(any(result,2),:))

Why surface fitting is not working in logarithmic domain?

Let's have a corrupted Image C, Bias profile, B and True Image A. So if we can define a model,
C = A * B;
We can get the original Image back as,
A = C / B;
in the log domain,
log A = log C - log B.
Now let's say, I have true image, A and I am introducing the bias B and I am getting the corrupted image C. Now I can correct this biased Image, C using the polynomial regression. I will fit the surface once I convert the corrupted image C in the log domain, and I can subtract the bias profile from it as shown above. After the subtraction, I don't need to apply exp(log C - log B) as obvious. Onlu normalization is needed to get [0 255] range.
Algorithm:
Original Image without any bias field is introduced with the polynomial profile, which results in an image having non-uniform illumination.
Biased image is converted in log domain and surface is approximated using polynomial fit
approximated surface is subtracted from the Biased image which results in original image back with no bias fields.(Ideally).
measure RMSE between approximated surface and introduced polynomial field in step 1. Measure RMSE between the Biased Image and the image we get back at the end after subtraction.
Code:
clear;clc;close all;
%read the image, on which profile is to be generated
I = ones(300);
I = padarray(I,[20,20],'both','symmetric'); % padding
%%
%creating a bias profile using polynomial modeling
[x,y] = meshgrid(1:size(I,1),1:size(I,2));
profile = -2.5.*x.^3 - 2.5.* y.^3 + 0.25 .*(x.* y.^2) - 0.3*( x.^2 .* y ) - 0.5.* x .* y - x + y - 2.5*( x.^2) - y.^2 + 0.5 .* x .*y + 1;
% come to scale [0 1]
profile = profile - min(profile(:));
profile = profile / max(profile(:));
figure,imshow(profile,[]); %introduced bias profile
%% corrupt the image
biasedImage = (I .* profile);
figure,imshow(biasedImage,[]); %biased Image
cImage = log(biasedImage+1);% conversion to log domain/ +1 is needed to avoid infinite values in case of 0 intensty values in corrupted image.
%% forming the input for prediction of surface
colorChannel = cImage;
rows = size(colorChannel, 1);
columns = size(colorChannel, 2);
[X,Y] = meshgrid(1:columns, 1:rows);
z = colorChannel;
x1d = reshape(X, numel(X), 1);
y1d = reshape(Y, numel(Y), 1);
z1d = double(reshape(z, numel(z), 1)); %dependent variables
x = [x1d y1d]; % two regressors
%% surface fitting
m = 3; %define the order of polynomial
p = polyfitn(x,z1d,m); %prediction step
zg = polyvaln(p, x);
modeledColorChannel = reshape(zg, [rows columns]); % predicted surface
%modeledColorChannel = exp(modeledColorChannel)-1; if we use this step then the step below will be division instead of subtraction
%f = biasedImage./ modeledColorChannel; Same as the step below but as we are using exponential, it will be devision.
%% correction
f = cImage- modeledColorChannel; %getting the original image back.
%grayImage = f(21:end-20,21:end-20);
%modeledColorChannel = modeledColorChannel(21:end-20,21:end-20); %to remove the padding
figure,imshow(f,[]);
figure,imshow(modeledColorChannel,[]);
%% measure the RMSE for image
y = (I - f);
RMSE = sqrt(mean(y(:).^2));
disp(RMSE);
% RMSE for profile
z = (modeledColorChannel - profile);
RMSE = sqrt(mean(z(:).^2));
disp(RMSE);
Results:
In case of: f = cImage- modeledColorChannel
1.0000
0.2127
Corrected Image: enter image description here
In case of division: f = cImage ./ modeledColorChannel (although it is not correct as per theory.)
0.0190
0.2127
Corrected Image:enter image description here
Now, the question is: I am getting lower RMSE value at the end if I do division in the log domain instead of subtraction as I am doing here(See %% correction section). How does it possible to have higher RMSE for subtraction where it is theoriticaly correct? As per my understanding if I keep all of my calculation in log domain image division will become image subtraction.It is obvious if you run the code and see the image f at the end of the correction for division and subtraction in log domain.
Note: In both the cases, RMSE between the introduced and perceived profile is same as I am doing my estimation in log domain in both the cases.Either image division or in image subtraction.
See this for polyfitn tool box.
www.mathworks.com/matlabcentral/fileexchange/34765-polyfitn
Let me add to the answer to my question as I found my mistake, in case anybody faces same issue in future.
Mistake 1: After the subtraction, I don't need to apply exp(log C - log B) as obvious. Only normalisation is needed to get [0 255] range.
My intuition was, I don't need to apply exp() to get the original values back. But in fact I have to apply exp(). Log-log on LHS and RHS never cancels each other.
if log(A) = log(B), to get the values of A back I need A = exp(log(B)).
Mistake 2: In log domain, I subtract two images so I do not have to face infinity problem in the log domain, we usually face in case of division.
So simply while converting image in log domain, I could just do,
cImage = log(biasedImage);
instead of,
cImage = log(biasedImage+1);
Here, adding +1 is creating the unwanted blur in the images because in estimation while predicting surface it will push the surface to high values in the dark areas.

How to find the order of discrete point-set efficiently?

I have a series of discrete point on a plane, However, their order is scattered. Here is an instance:
To connect them with a smooth curve, I wrote a findSmoothBoundary() to achieve the smooth boundary.
Code
function findSmoothBoundary(boundaryPointSet)
%initialize the current point
currentP = boundaryPointSet(1,:);
%Create a space smoothPointsSet to store the point
smoothPointsSet = NaN*ones(length(boundaryPointSet),2);
%delete the current point from the boundaryPointSet
boundaryPointSet(1,:) = [];
ptsNum = 1; %record the number of smoothPointsSet
smoothPointsSet(ptsNum,:) = currentP;
while ~isempty(boundaryPointSet)
%ultilize the built-in knnsearch() to
%achieve the nearest point of current point
nearestPidx = knnsearch(boundaryPointSet,currentP);
currentP = boundaryPointSet(nearestPidx,:);
ptsNum = ptsNum + 1;
smoothPointsSet(ptsNum,:) = currentP;
%delete the nearest point from boundaryPointSet
boundaryPointSet(nearestPidx,:) = [];
end
%visualize the smooth boundary
plot(smoothPointsSet(:,1),smoothPointsSet(:,2))
axis equal
end
Although findSmoothBoundary() can find the smooth boundary rightly, but its efficiency is much lower ( About the data, please see here)
So I would like to know:
How to find the discrete point order effieciently?
Data
theta = linspace(0,2*pi,1000)';
boundaryPointSet= [2*sin(theta),cos(theta)];
tic;
findSmoothBoundary(boundaryPointSet)
toc;
%Elapsed time is 4.570719 seconds.
This answer is not perfect because I'll have to make a few hypothesis in order for it to work. However, for a vast majority of cases, it should works as intended. Moreover, from the link you gave in the comments, I think these hypothesis are at least weak, if not verified by definition :
1. The point form a single connected region
2. The center of mass of your points lies in the convex hull of those points
If these hypothesis are respected, you can do the following (Full code available at the end):
Step 1 : Calculate the center of mass of your points
Means=mean(boundaryPointSet);
Step 2 : Change variables to set the origin to the center of mass
boundaryPointSet(:,1)=boundaryPointSet(:,1)-Means(1);
boundaryPointSet(:,2)=boundaryPointSet(:,2)-Means(2);
Step3 : Convert coordinates to polar
[Angles,Radius]=cart2pol(boundaryPointSet(:,1),boundaryPointSet(:,2));
Step4 : Sort the Angle and use this sorting to sort the Radius
[newAngles,ids]=sort(Angles);
newRadius=Radius(ids);
Step5 : Go back to cartesian coordinates and re-add the coordinates of the center of mass:
[X,Y]=pol2cart(newAngles,newRadius);
X=X+Means(1);
Y=Y+means(2);
Full Code
%%% Find smooth boundary
fid=fopen('SmoothBoundary.txt');
A=textscan(fid,'%f %f','delimiter',',');
boundaryPointSet=cell2mat(A);
boundaryPointSet(any(isnan(boundaryPointSet),2),:)=[];
idx=randperm(size(boundaryPointSet,1));
boundaryPointSet=boundaryPointSet(idx,:);
tic
plot(boundaryPointSet(:,1),boundaryPointSet(:,2))
%% Find mean value of all parameters
Means=mean(boundaryPointSet);
%% Center values around Mean point
boundaryPointSet(:,1)=boundaryPointSet(:,1)-Means(1);
boundaryPointSet(:,2)=boundaryPointSet(:,2)-Means(2);
%% Get polar coordinates of your points
[Angles,Radius]=cart2pol(boundaryPointSet(:,1),boundaryPointSet(:,2));
[newAngles,ids]=sort(Angles);
newRadius=Radius(ids);
[X,Y]=pol2cart(newAngles,newRadius);
X=X+Means(1);
Y=Y+means(2);
toc
figure
plot(X,Y);
Note : As your values are already sorted in your input file, I had to mess it up a bit by permutating them
Outputs :
Boundary
Elapsed time is 0.131808 seconds.
Messed Input :
Output :

calculating Euclidean distance between two image in matlab

I want to calculate the Euclidean distance between two images in Matlab. I find some examples and I've try them but they are not correct.
The result of this Euclidean distance should be between 0 and 1 but with two different ways I reached to different solutions.
The first algorithm gives me a 4 digit number such as 2000 and other digits like this and by the other way I reached numbers such as 0.007
What is wrong with it?
This is one of those algorithms I mentioned:
Im1 = imread('1.jpeg');
Im2 = imread('2.jpeg');
Im1 = rgb2gray(Im1);
Im2 = rgb2gray(Im2);
hn1 = imhist(Im1)./numel(Im1);
hn2 = imhist(Im2)./numel(Im2);
% Calculate the Euclidean distance
f = sum((hn1 - hn2).^2)
the final line of code needs a sqrt command:
f = sum(sqrt(hn1-hn2).^2);
check this link
You can also use the norm command
f = norm(hn1-hn2);
These post1 and post2 can be useful.
Oh, I'm not sure where to begin but here are some things that you should think about:
1: You're normalising your histograms incorrectly. You want them to have unit L1-norm:
hn1 = imhist(Im1);
hn2 = imhist(Im2);
hn1 = hn1/numel(hn1);
hn2 = hn2/numel(hn2);
2: Taking L2-distance between histograms doesn't really make sense (what is an euclidian distance between two distributions really?). You should rather take a look at something like a L1 or Chi-2 distance, or use an intersection kernel. L1 would be
f=norm(hn1-hn2,1);
3: If you really do want it to be L2 euclidian distance, the last line should be
f=norm(hn1-hn2);
but then you should rather L2-normalize the histogram:
hn1 = imhist(Im1);
hn2 = imhist(Im2);
hn1 = hn1/norm(hn1);
hn2 = hn2/norm(hn2);
4: Please try to be clearer in the formulation of your questions - it was a bit hard to decode :). If your would have mentioned the application - I could have given some additional pointers. :)

Image manipulation code in MATLAB (corner detection)

I'd like to know exactly how this line of code works
corners = (m==n)&(n>threshold);
It's in a piece of code I'm using and I want to understand it. Basically, m and n are both equal-sized images, and "threshold" is a decimal value.
To understand the context, a segment of the code is below.
% compute the m cornerness measure
m = (ix2s.*iy2s - ixys.^2) - 0.04*(ix2s+iy2s).^2;
% perform non-maximal suppression using ordfilt2
n = ordfilt2(m, radius^2, ones([radius radius]));
% display corner spots
corners = (m==n)&(n>threshold);
% superimpose corners
Q = corners+im;
Q(Q>1) = 1;
C = repmat(im,[1 1 3]);
C(:,:,1) = Q;
If I understand correctly, n is the max of m ("cornerness measure") for the vicinity, so the line means - "if m is the local maximum and large enough(larger than threshold), then this is probably a corner", it could have arguably been more readable as:
corners = (m==n)&(m>threshold);
You should read more about Harris corner detector. Taken from Wikipedia:
This line is implementation of the function mentioned above. It is used to detect corners.

Resources