http://imagej.273.s1.nabble.com/fitting-y-f-x-data-to-arbitrary-functions-in-ImageJ-tp5020273p5020319.html
setOffsetMultiplySlopeParams, etc.
where one would typically use that fit function...
> Greetings Michael,
>
> Thanks for the reply.
>
> I must have missed the NaN trick in the documentation.
>
> The fit function (UserFunction), is:
> public double userFunction(double[] p, double x) {
> return Math.abs( p[1] + p[2]*Math.exp(-x/p[0]) );
> }
>
> which was in the example plugin below my signature, which includes sample data
> and plot for the fit mentioned in 2 and can produce the exception if the
> variable 'ipv' is replaced with the null value.
>
> Note that without the call to setOffsetMultiplySlopeParams the fits are
> acceptable almost all of the time, i.e., only about 10-20 apparently valid
> datum fail with obnoxious results out of 40%*64x64 fits.
>
> My main interest in using the method setOffsetMultiplySlopeParams is
> efficiency. Right now on a fast computer it takes about 2-5 seconds to
> process a slice.
>
> Thanks,
>
> Fred
>
> On Fri, March 23, 2018 9:00 am, Michael Schmid wrote:
>> Hi Fred,
>>
>> concerning (1), restricting parameter ranges:
>> You can have a function that returns NaN if the parameter enters an
>> invalid range. Then the CurveFitter will avoid this range.
>> Convergence will be rather bad if the best fit lies very close to a 'NaN
>> boundary', however.
>>
>> > On fits to some data the parameters returned are obnoxiously bad.
>> What is the fit function? maybe I have some idea how to improve the
>> situation.
>>
>> Concering (2) setOffsetMultiplySlopeParams:
>> Parameter numbers that are not set should be -1. Note that 0 refers to
>> parameter 'a', 1 to 'b', etc. Since you can specify only two out of the
>> three arguments, at least one of (multiplyParam or slopeParam) them has
>> to be -1.
>> If you let me know the fit function, I can tell you the
>> setOffsetMultiplySlopeParams call should look like.
>>
>> Concerning (3), NullPointerException:
>> I'll have a look at it.
>> Anyhow, if you have problems with the fit not converging properly, I
>> would strongly suggest setting initial parameter that give at least the
>> order of magnitude of the initial parameters. Better, specify also the
>> initialParamVariations (e.g. for each parameter 1/10 of the range that
>> it may have).
>>
>>
>> Michael
>> ________________________________________________________________
>> On 22/03/2018 22:07, Fred Damen wrote:
>>> Greetings,
>>>
>>> First, thanks for expounding on CurveFitter.
>>>
>>> I have a few questions and a bug.
>>>
>>> a) Is there any way to keep the fitting within a given range for each
>>> parameter? On fits to some data the parameters returned are obnoxiously
>>> bad.
>>> The input data resembles data that fits reasonably, i.e., I do not suspect
>>> that the initial parameters are outside the local minimum. This is not
>>> using
>>> setOffsetMultiplySlopeParams.
>>>
>>> b) When using the method setOffsetMultiplySlopeParams and passing a value to
>>> doCustionFit's initialParamVariations parameter, set to what is documented
>>> as
>>> the default value that would be used if null is passed, the fitting is
>>> effectively a linear fit; see example below. A reasonable fit is had if the
>>> call to setOffsetMultiplySlopeParams is commented out. If this worked what
>>> kind of improvement would I expect to see in efficiency or goodnees-of-fit?
>>>
>>> b) When using the method setOffsetMultiplySlopeParams and passing a null
>>> value
>>> to doCustionFit's initialParamVariations parameter, a null pointer exception
>>> is thrown:
>>> ImageJ 1.50h; Java 1.8.0_91 [64-bit]; Linux 4.5.5-300.fc24.x86_64; 287MB of
>>> 1820MB (15%)
>>>
>>> java.lang.NullPointerException
>>> at
>>> ij.measure.CurveFitter.modifyInitialParamsAndVariations(CurveFitter.java:835)
>>> at ij.measure.CurveFitter.doFit(CurveFitter.java:178)
>>> at ij.measure.CurveFitter.doCustomFit(CurveFitter.java:283)
>>> at My_Plugin2.nonlinearFit(My_Plugin2.java:38)
>>> at My_Plugin2.run(My_Plugin2.java:14)
>>> at ij.plugin.PlugInExecuter.runCompiledPlugin(Compiler.java:318)
>>> at ij.plugin.PlugInExecuter.run(Compiler.java:307)
>>> at java.lang.Thread.run(Thread.java:745)
>>>
>>> Thanks in advance,
>>>
>>> Fred
>>>
>>> ----------------------------
>>> import ij.*;
>>> import ij.process.*;
>>> import ij.gui.*;
>>> import java.awt.*;
>>> import ij.plugin.*;
>>> import ij.plugin.frame.*;
>>> import ij.measure.*;
>>>
>>> public class My_Plugin2 implements PlugIn {
>>>
>>> public void run(String arg) {
>>> double[] x = {0.10,0.25,0.50,0.75,1.00,1.25,1.50,1.75,2.00,5.00};
>>> double[] y =
>>> {1123.175,838.206,469.320,453.003,725.135,1094.360,1450.741,1787.361,2128.119,4670.120};
>>> double[] p = nonlinearFit(x,y);
>>> Plot plot = new Plot("","x","y");
>>> plot.setLineWidth(2);
>>> plot.setColor(Color.black);
>>> plot.addPoints(x,y,PlotWindow.X);
>>> double[] y2 = new double[y.length];
>>> for(int i=0; i<y.length; i++)
>>> y2[i] = Math.abs( p[1] + p[2]*Math.exp(-x[i]/p[0]) );
>>> plot.setColor(Color.blue);
>>> plot.addPoints(x,y2,PlotWindow.LINE);
>>> plot.show();
>>>
>>> }
>>>
>>> private double[] nonlinearFit(double[] x, double[] y) {
>>>
>>> CurveFitter cf = new CurveFitter(x, y);
>>> double[] params = {1, 2*y[0], -(y[0]+y[y.length-1])};
>>> double[] ipv = new double[params.length];
>>> for(int i=0; i<ipv.length; i++)
>>> ipv[i] = Math.abs(params[i]*0.1);
>>>
>>> cf.setMaxIterations(200);
>>> cf.setOffsetMultiplySlopeParams(1, 2, -1);
>>> cf.doCustomFit(new UserFunction() {
>>> @Override
>>> public double userFunction(double[] p, double x) {
>>> return Math.abs( p[1] + p[2]*Math.exp(-x/p[0]) );
>>> }
>>> }, params.length, "", params, ipv, false);
>>> //IJ.log(cf.getResultString());
>>>
>>> return cf.getParams();
>>> }
>>> }
>>>
>>>
>>>
>>> On Tue, March 13, 2018 2:30 pm, Michael Schmid wrote:
>>>> Hi Kenneth,
>>>>
>>>> Concerning fitting an 8-parameter function:
>>>>
>>>> If the fit is not linear (as in the case of a difference of Gaussians),
>>>> having 8 fit parameters is a rather ambitious task, and there is a high
>>>> probability that the fit will run into a local minimum or some point
>>>> that looks like a local minimum to the fitting program.
>>>>
>>>> It would be best to reduce the number of parameters, e.g. using a fixed
>>>> ratio between the two sigma values in the Difference of Gaussians.
>>>>
>>>> You also need some reasonable guess for the initial values of the
>>>> parameters.
>>>>
>>>> For the ImageJ CurveFitter, if there are many parameters it is very
>>>> important to specify roughly how much the parameters can vary, these are
>>>> the 'initialParamVariations'
>>>> If cf is the CurveFitter, you will have
>>>> cf.doCustomFit(UserFunction userFunction, int numParams,
>>>> String formula, double[] initialParams,
>>>> double[] initialParamVariations, boolean showSettings
>>>>
>>>> For the initialParamVariations, use e.g. 1/10th of how much the
>>>> respective parameter might deviate from the initial guess (only the
>>>> order of magnitude is important).
>>>>
>>>> If you have many parameters and your function can be written as, e.g.
>>>> a + b*function(x; c,d,e...)
>>>> or
>>>> a + b*x + function(x; c,d,e)
>>>>
>>>> you should also specify these parameters via
>>>> cf.setOffsetMultiplySlopeParams(int offsetParam, int multiplyParam,
>>>> int slopeParam)
>>>> where 'offsetParam' is the number of the parameter that is only an
>>>> offset (in the examples above, 0 for 'a', 'multiplyParam' would be 1 for
>>>> 'b' in the first example above, or 'slopeParam' would be 1 for 'b' in
>>>> the second type of function above. You cannot have a 'multiplyParam' and
>>>> a 'slopeParam' at the same time, set the unused one to -1.
>>>>
>>>> Specifying an offsetParam and multiplyParam (or slopeParam) makes the
>>>> CurveFitter calculate these parameters via linear regression, so the
>>>> actual minimization does not include these parameters. In other words,
>>>> you get fewer parameters, which makes the fitting much more likely to
>>>> succeed.
>>>>
>>>> In my experience, if you end up with 3-4 parameters (not counting the
>>>> parameters eliminated by setOffsetMultiplySlopeParams), there is a good
>>>> chance that the fit will work very well, with 5-6 parameters it gets
>>>> difficult, and above 6 parameters the chance to get the correct result
>>>> is rather low.
>>>>
>>>> If you need to control the minimization process in detail, before
>>>> starting the fit, you can use
>>>> Minimizer minimizer = cf.getMinimizer()
>>>> to get access to the Minimizer that will be used and you can use the
>>>> Minimizer's methods to control its behavior (e.g. allow it to do more
>>>> steps than by default by minimizer.setMaxIterations, setting it to try
>>>> more restarts, use different error values for more/less accurate
>>>> convergence, etc.
>>>>
>>>> Best see the documentation in the source code, e.g.
>>>>
https://github.com/imagej/imagej1/blob/master/ij/measure/CurveFitter.java>>>>
https://github.com/imagej/imagej1/blob/master/ij/measure/Minimizer.java>>>>
>>>> -------------
>>>>
>>>> > Bonus question for Java Gurus:
>>>> > How to declare the user function as a variable, call it,
>>>> > and pass it to another function?
>>>>
>>>> public class MyFunction implements UserFunction {...
>>>> public double userFunction(double[] params, double x) {
>>>> return params[0]+params[1]*x;
>>>> }
>>>> }
>>>>
>>>> public class PassingClass { ...
>>>> UserFunction exampleFunction = new MyFunction(...);
>>>> otherClass.doFitting(xData, yData, exampleFunction)
>>>> }
>>>>
>>>> Public class OtherClass { ...
>>>> public void doFitting(double[] xData, double[] yData,
>>>> UserFunction userFunction) {
>>>> CurveFitter cf = new CurveFitter(xData, yData);
>>>> cf.doCustomFit(userFunction, /*numParams=*/2, null,
>>>> null, null, false);
>>>> }
>>>> }
>>>>
>>>> -------------
>>>>
>>>> Best,
>>>>
>>>> Michael
>>>> ________________________________________________________________
>>>>
>>>>
>>>> On 13/03/2018 18:56, Fred Damen wrote:
>>>>> Below is a routine to fit MRI Inversion Recover data for T1.
>>>>>
>>>>> Note:
>>>>> a) CurveFitter comes with ImageJ.
>>>>> b) Calls are made to UserFunction once for each x.
>>>>> c) If your initial guess is not close it does not seem to converge.
>>>>>
>>>>> Bonus question for Java Gurus:
>>>>> How to declare the user function as a variable, call it, and pass it to
>>>>> another function?
>>>>>
>>>>> Enjoy,
>>>>>
>>>>> Fred
>>>>>
>>>>> private double[] nonlinearFit(double[] x, double[] y) {
>>>>>
>>>>> CurveFitter cf = new CurveFitter(x, y);
>>>>> double[] params = {1, 2*y[0], -(y[0]+y[y.length-1])};
>>>>>
>>>>> cf.setMaxIterations(200);
>>>>> cf.doCustomFit(new UserFunction() {
>>>>> @Override
>>>>> public double userFunction(double[] p, double x) {
>>>>> return Math.abs( p[1] + p[2]*Math.exp(-x/p[0]) );
>>>>> }
>>>>> }, params.length, "", params, null, false);
>>>>> //IJ.log(cf.getResultString());
>>>>>
>>>>> return cf.getParams();
>>>>> }
>>>>>
>>>>>
>>>>> On Tue, March 13, 2018 11:06 am, Kenneth Sloan wrote:
>>>>>> I have some simple data: samples of y=f(x) at regularly spaced discrete
>>>>>> values
>>>>>> for x. The data
>>>>>> is born as a simple array of y values, but I can turn that into (for
>>>>>> example)
>>>>>> a polyline, if that
>>>>>> will help. I'm currently doing that to draw the data as an Overlay. The
>>>>>> size
>>>>>> of the y array is between 500 and 1000. (think 1 y value for every
>>>>>> integer
>>>>>> x-coordinate in an image - some y values may be recorded as "missing").
>>>>>>
>>>>>> Is there an ImageJ tool that will fit (more or less) arbitrary functions
>>>>>> to
>>>>>> this data? Approximately 8 parameters.
>>>>>>
>>>>>> The particular function I have in mind at the moment is a difference of
>>>>>> Gaussians. The Gaussians
>>>>>> most likely have the same location (in x) - but this is not guaranteed,
>>>>>> and
>>>>>> I'd prefer
>>>>>> to use this as a sanity check rather than impose it as a constraint.
>>>>>>
>>>>>> Note that the context is a Java plugin - not a macro.
>>>>>>
>>>>>> If not in ImageJ, perhaps someone could point me at a Java package that
>>>>>> will
>>>>>> do this. I am far
>>>>>> from an expert in curve fitting, so please be gentle.
>>>>>>
>>>>>> If not, I can do it in R - but I prefer to do it "on the fly" while
>>>>>> displaying
>>>>>> the image, and the Overlay.
>>>>>>
>>>>>> --
>>>>>> Kenneth Sloan
>>>>>>
[hidden email]
>>>>>> Vision is the art of seeing what is invisible to others.
>>>>>>
>>>>>> --
>>>>>> ImageJ mailing list:
http://imagej.nih.gov/ij/list.html>>>>>>
>>>>>
>>>>> --
>>>>> ImageJ mailing list:
http://imagej.nih.gov/ij/list.html>>>>>
>>>>
>>>> --
>>>> ImageJ mailing list:
http://imagej.nih.gov/ij/list.html>>>>
>>>
>>> --
>>> ImageJ mailing list:
http://imagej.nih.gov/ij/list.html>>>
>>
>> --
>> ImageJ mailing list:
http://imagej.nih.gov/ij/list.html>>
>
> --
> ImageJ mailing list:
http://imagej.nih.gov/ij/list.html>