package cz.cuni.lf1.lge.ThunderSTORM.estimators;
import cz.cuni.lf1.lge.ThunderSTORM.estimators.PSF.Molecule;
import cz.cuni.lf1.lge.ThunderSTORM.estimators.PSF.PSFModel;
import cz.cuni.lf1.lge.ThunderSTORM.estimators.PSF.PSFModel.Params;
import cz.cuni.lf1.lge.ThunderSTORM.util.VectorMath;
import org.apache.commons.math3.optim.*;
import org.apache.commons.math3.optim.nonlinear.vector.ModelFunction;
import org.apache.commons.math3.optim.nonlinear.vector.ModelFunctionJacobian;
import org.apache.commons.math3.optim.nonlinear.vector.Target;
import org.apache.commons.math3.optim.nonlinear.vector.Weight;
import org.apache.commons.math3.optim.nonlinear.vector.jacobian.LevenbergMarquardtOptimizer;
import static cz.cuni.lf1.lge.ThunderSTORM.util.VectorMath.sub;
public class LSQFitter implements IOneLocationFitter, IOneLocationBiplaneFitter {
public final static int MAX_ITERATIONS = 1000;
public double[] fittedParameters;
public PSFModel psfModel;
public boolean useWeighting;
private int maxIter; // after `maxIter` iterations the algorithm converges
private int bkgStdColumn;
public LSQFitter(PSFModel psfModel, boolean useWeighting) {
this(psfModel, useWeighting, MAX_ITERATIONS + 1, -1 );
}
public LSQFitter(PSFModel psfModel, boolean useWeighting, int bkgStdIndex) {
this(psfModel, useWeighting, MAX_ITERATIONS + 1, Params.BACKGROUND );
}
public LSQFitter(PSFModel psfModel, boolean useWeighting, int maxIter, int bkgStdIndex) {// throws an exception after `MAX_ITERATIONS` iterations
this.psfModel = psfModel;
this.maxIter = maxIter;
this.useWeighting = useWeighting;
this.bkgStdColumn = bkgStdIndex;
this.fittedParameters = null;
}
@Override
public Molecule fit(SubImage img) {
return fit(new LsqMleSinglePlaneFunctions(psfModel, img));
}
@Override
public Molecule fit(SubImage plane1, SubImage plane2) {
return fit(new LsqMleBiplaneFunctions(psfModel, plane1, plane2));
}
protected Molecule fit(ILsqFunctions functions) {
// init
double[] weights = functions.calcWeights(useWeighting);
double[] observations = functions.getObservations();
// fit
LevenbergMarquardtOptimizer optimizer = new LevenbergMarquardtOptimizer(
new SimplePointChecker<PointVectorValuePair>(10e-10, 10e-10, maxIter));
PointVectorValuePair pv;
pv = optimizer.optimize(
MaxEval.unlimited(),
new MaxIter(MAX_ITERATIONS + 1),
new ModelFunction(functions.getValueFunction()),
new ModelFunctionJacobian(functions.getJacobianFunction()),
new Target(observations),
new InitialGuess(psfModel.transformParametersInverse(functions.getInitialParams())),
new Weight(weights));
// estimate background and return an instance of the `Molecule`
fittedParameters = pv.getPointRef();
if (bkgStdColumn >= 0) {
fittedParameters[bkgStdColumn] = VectorMath.stddev(sub(observations, functions.getValueFunction().value(fittedParameters)));
}
return psfModel.newInstanceFromParams(psfModel.transformParameters(fittedParameters), functions.getImageUnits(), true);
}
}