Program: 149538
Project: 149538
Buggy source:
/** 
 * Fit the single spot location as two spots (a doublet). <p> Perform quadrant analysis as per rapidSTORM. If the residuals of the the fit are skewed around the single fit result then an attempt is made to fit two spots (a doublet)
 * @param fitResult the fit result
 * @param region the region
 * @param regionBounds the region bounds
 * @param residualsThreshold the residuals threshold
 * @param neighbours the neighbours
 * @param peakNeighbours the peak neighbours
 * @param qa the qa object that performed quadrant analysis
 * @return the multi path fit result. fit result
 */
private MultiPathFitResult.FitResult fitAsDoublet(FitResult fitResult,double[] region,Rectangle regionBounds,double residualsThreshold,CandidateList neighbours,PeakResult[] peakNeighbours,QuadrantAnalysis qa,double singleSumOfSquares,double singelValue){
  final int width=regionBounds.width;
  final int height=regionBounds.height;
  final double[] params=fitResult.getParameters();
  final int cx=(int)Math.round(params[Gaussian2DFunction.X_POSITION]);
  final int cy=(int)Math.round(params[Gaussian2DFunction.Y_POSITION]);
  if (logger != null)   logger.info("String_Node_Str");
  if (!qa.computeDoubletCentres(width,height,cx,cy,params[Gaussian2DFunction.X_SD],params[Gaussian2DFunction.Y_SD]))   return null;
  final double[] doubletParams=new double[1 + 2 * 6];
  doubletParams[Gaussian2DFunction.BACKGROUND]=params[Gaussian2DFunction.BACKGROUND];
  doubletParams[Gaussian2DFunction.SIGNAL]=params[Gaussian2DFunction.SIGNAL] * 0.5;
  doubletParams[Gaussian2DFunction.X_POSITION]=(float)(qa.x1 - 0.5);
  doubletParams[Gaussian2DFunction.Y_POSITION]=(float)(qa.y1 - 0.5);
  doubletParams[6 + Gaussian2DFunction.SIGNAL]=params[Gaussian2DFunction.SIGNAL] * 0.5;
  doubletParams[6 + Gaussian2DFunction.X_POSITION]=(float)(qa.x2 - 0.5);
  doubletParams[6 + Gaussian2DFunction.Y_POSITION]=(float)(qa.y2 - 0.5);
  final double shift=fitConfig.getCoordinateShift();
  final int maxIterations=fitConfig.getMaxIterations();
  final int maxEvaluations=fitConfig.getMaxFunctionEvaluations();
  fitConfig.setCoordinateShift(FastMath.min(width,height));
  fitConfig.setFitRegion(0,0,0);
  fitConfig.setMaxIterations(maxIterations * ITERATION_INCREASE_FOR_DOUBLETS);
  fitConfig.setMaxFunctionEvaluations(maxEvaluations * FitWorker.EVALUATION_INCREASE_FOR_DOUBLETS);
  final boolean isComputeResiduals=gf.isComputeResiduals();
  gf.setComputeResiduals(false);
  final boolean[] amplitudeEstimate=new boolean[2];
  final FitResult newFitResult=gf.fit(region,width,height,2,doubletParams,amplitudeEstimate,doubletParams[Gaussian2DFunction.BACKGROUND] == 0);
  gf.setComputeResiduals(isComputeResiduals);
  fitConfig.setCoordinateShift(shift);
  fitConfig.setFitRegion(width,height,0.5);
  fitConfig.setMaxIterations(maxIterations);
  fitConfig.setMaxFunctionEvaluations(maxEvaluations);
  updateError(newFitResult);
  if (newFitResult.getStatus() == FitStatus.OK) {
    final double doubleSumOfSquares=gf.getFinalResidualSumOfSquares();
    final int length=width * height;
    final double ic1, ic2;
    if (fitConfig.getFitSolver() == FitSolver.MLE && fitConfig.isModelCamera()) {
      final double doubleValue=gf.getValue();
      ic1=Maths.getBayesianInformationCriterion(singleValue,length,fitResult.getNumberOfFittedParameters());
      ic2=Maths.getBayesianInformationCriterion(doubleValue,length,newFitResult.getNumberOfFittedParameters());
      if (logger != null)       logger.info("String_Node_Str",singleSumOfSquares,singleValue,ic1,doubleSumOfSquares,doubleValue,ic2,ic1 - ic2);
    }
 else {
      ic1=Maths.getBayesianInformationCriterionFromResiduals(singleSumOfSquares,length,fitResult.getNumberOfFittedParameters());
      ic2=Maths.getBayesianInformationCriterionFromResiduals(doubleSumOfSquares,length,newFitResult.getNumberOfFittedParameters());
      if (logger != null)       logger.info("String_Node_Str",singleSumOfSquares,ic1,doubleSumOfSquares,ic2,ic1 - ic2);
    }
    if (logger2 != null) {
      double[] peakParams=newFitResult.getParameters();
      if (peakParams != null) {
        peakParams=Arrays.copyOf(peakParams,peakParams.length);
        int npeaks=peakParams.length / 6;
        for (int i=0; i < npeaks; i++) {
          peakParams[i * 6 + Gaussian2DFunction.X_POSITION]+=0.5 + regionBounds.x;
          peakParams[i * 6 + Gaussian2DFunction.Y_POSITION]+=0.5 + regionBounds.y;
        }
      }
      String msg=String.format("String_Node_Str",slice,cc.fromRegionToGlobalX(cx),cc.fromRegionToGlobalY(cy),newFitResult.getStatus(),newFitResult.getStatusData(),singleValue,gf.getValue(),singleSumOfSquares,doubleSumOfSquares,ic1,ic2,Arrays.toString(peakParams));
      logger2.debug(msg);
    }
    if (ic2 > ic1) {
      return createResult(newFitResult,null,FitStatus.NO_MODEL_IMPROVEMENT);
    }
    final double[] fitParams=newFitResult.getParameters();
    final double[] initialParams=newFitResult.getInitialParameters();
    final double halfWindow=0.5 * FastMath.min(regionBounds.width,regionBounds.height);
    final int[] position=new int[2];
    final int[] candidateIndex=new int[2];
    int nPeaks=0;
    NEXT_PEAK:     for (int n=0; n < 2; n++) {
      final int offset=n * 6;
      initialParams[Gaussian2DFunction.X_POSITION + offset]=candidates.get(candidateId).x - regionBounds.x;
      initialParams[Gaussian2DFunction.Y_POSITION + offset]=candidates.get(candidateId).y - regionBounds.y;
      final double xpos=fitParams[Gaussian2DFunction.X_POSITION + offset] + 0.5;
      final double ypos=fitParams[Gaussian2DFunction.Y_POSITION + offset] + 0.5;
      if (xpos < 0 || xpos > width || ypos < 0 || ypos > height) {
        if (logger != null)         logger.info("String_Node_Str",xpos,ypos,width,height);
        continue;
      }
      double xShift=fitParams[Gaussian2DFunction.X_POSITION + offset] - params[Gaussian2DFunction.X_POSITION];
      double yShift=fitParams[Gaussian2DFunction.Y_POSITION + offset] - params[Gaussian2DFunction.Y_POSITION];
      if (Math.abs(xShift) > halfWindow || Math.abs(yShift) > halfWindow) {
        final double a=QuadrantAnalysis.getAngle(qa.vector,new double[]{xShift,yShift});
        if (a > 0.785398 && a < 2.356194) {
          if (logger != null) {
            logger.info("String_Node_Str",n,xShift,yShift,a * 57.29578);
          }
          continue;
        }
      }
      xShift=fitParams[Gaussian2DFunction.X_POSITION + offset] - initialParams[Gaussian2DFunction.X_POSITION];
      yShift=fitParams[Gaussian2DFunction.Y_POSITION + offset] - initialParams[Gaussian2DFunction.Y_POSITION];
      final double distanceToSingleFit2=xShift * xShift + yShift * yShift;
      if (peakNeighbours.length != 0) {
        final float fcx2=(float)(regionBounds.x + fitParams[Gaussian2DFunction.X_POSITION + offset] + 0.5);
        final float fcy2=(float)(regionBounds.y + fitParams[Gaussian2DFunction.Y_POSITION + offset] + 0.5);
        final float d2=(float)distanceToSingleFit2;
        for (int i=0; i < peakNeighbours.length; i++) {
          if (d2 > distance2(fcx2,fcy2,peakNeighbours[i].params)) {
            if (logger != null) {
              logger.info("String_Node_Str",n,candidates.get(candidateId).x,candidates.get(candidateId).y,fcx2,fcy2,peakNeighbours[i].getXPosition(),peakNeighbours[i].getYPosition());
            }
            continue NEXT_PEAK;
          }
        }
      }
      int otherId=candidateId;
      if (neighbours.size != 0) {
        float fcx2=(float)(regionBounds.x + fitParams[Gaussian2DFunction.X_POSITION + offset]);
        float fcy2=(float)(regionBounds.y + fitParams[Gaussian2DFunction.Y_POSITION + offset]);
        double mind2=distanceToSingleFit2;
        for (int j=0; j < neighbours.size; j++) {
          final int id=neighbours.list[j].index;
          if (isFit(id))           continue;
          final double d2=distance2(fcx2,fcy2,candidates.get(id));
          if (mind2 > d2) {
            mind2=d2;
            otherId=id;
          }
        }
        if (otherId != candidateId) {
          if (logger != null) {
            logger.info("String_Node_Str",n,candidates.get(candidateId).x,candidates.get(candidateId).y,fcx2 + 0.5f,fcy2 + 0.5f,candidates.get(otherId).x,candidates.get(otherId).y);
          }
          initialParams[Gaussian2DFunction.X_POSITION + offset]=candidates.get(otherId).x - regionBounds.x;
          initialParams[Gaussian2DFunction.Y_POSITION + offset]=candidates.get(otherId).y - regionBounds.y;
        }
      }
      candidateIndex[nPeaks]=otherId;
      position[nPeaks++]=n;
    }
    if (nPeaks == 0) {
      return createResult(newFitResult,null,FitStatus.FAILED_VALIDATION);
    }
    convertParameters(fitParams);
    final PreprocessedPeakResult[] results=new PreprocessedPeakResult[nPeaks];
    for (int i=0; i < nPeaks; i++) {
      final ResultType resultType=(candidateIndex[i] <= candidateId) ? ResultType.NEW : ResultType.CANDIDATE;
      results[i]=resultFactory.createPreprocessedPeakResult(candidateIndex[i],position[i],initialParams,fitParams,0,resultType);
    }
    return createResult(newFitResult,results);
  }
 else {
    if (logger != null)     logger.info("String_Node_Str",newFitResult.getStatus());
    if (logger2 != null) {
      double[] peakParams=newFitResult.getParameters();
      if (peakParams != null) {
        peakParams=Arrays.copyOf(peakParams,peakParams.length);
        int npeaks=peakParams.length / 6;
        for (int i=0; i < npeaks; i++) {
          peakParams[i * 6 + Gaussian2DFunction.X_POSITION]+=0.5 + regionBounds.x;
          peakParams[i * 6 + Gaussian2DFunction.Y_POSITION]+=0.5 + regionBounds.y;
        }
      }
      String msg=String.format("String_Node_Str",slice,cc.fromRegionToGlobalX(cx),cc.fromRegionToGlobalY(cy),newFitResult.getStatus(),newFitResult.getStatusData(),Arrays.toString(peakParams));
      logger2.debug(msg);
    }
    return createResult(newFitResult,null);
  }
}

Fixed version (ground truth):
/** 
 * Fit the single spot location as two spots (a doublet). <p> Perform quadrant analysis as per rapidSTORM. If the residuals of the the fit are skewed around the single fit result then an attempt is made to fit two spots (a doublet)
 * @param fitResult the fit result
 * @param region the region
 * @param regionBounds the region bounds
 * @param residualsThreshold the residuals threshold
 * @param neighbours the neighbours
 * @param peakNeighbours the peak neighbours
 * @param qa the qa object that performed quadrant analysis
 * @return the multi path fit result. fit result
 */
private MultiPathFitResult.FitResult fitAsDoublet(FitResult fitResult,double[] region,Rectangle regionBounds,double residualsThreshold,CandidateList neighbours,PeakResult[] peakNeighbours,QuadrantAnalysis qa,double singleSumOfSquares,double singleValue){
  final int width=regionBounds.width;
  final int height=regionBounds.height;
  final double[] params=fitResult.getParameters();
  final int cx=(int)Math.round(params[Gaussian2DFunction.X_POSITION]);
  final int cy=(int)Math.round(params[Gaussian2DFunction.Y_POSITION]);
  if (logger != null)   logger.info("String_Node_Str");
  if (!qa.computeDoubletCentres(width,height,cx,cy,params[Gaussian2DFunction.X_SD],params[Gaussian2DFunction.Y_SD]))   return null;
  final double[] doubletParams=new double[1 + 2 * 6];
  doubletParams[Gaussian2DFunction.BACKGROUND]=params[Gaussian2DFunction.BACKGROUND];
  doubletParams[Gaussian2DFunction.SIGNAL]=params[Gaussian2DFunction.SIGNAL] * 0.5;
  doubletParams[Gaussian2DFunction.X_POSITION]=(float)(qa.x1 - 0.5);
  doubletParams[Gaussian2DFunction.Y_POSITION]=(float)(qa.y1 - 0.5);
  doubletParams[6 + Gaussian2DFunction.SIGNAL]=params[Gaussian2DFunction.SIGNAL] * 0.5;
  doubletParams[6 + Gaussian2DFunction.X_POSITION]=(float)(qa.x2 - 0.5);
  doubletParams[6 + Gaussian2DFunction.Y_POSITION]=(float)(qa.y2 - 0.5);
  final double shift=fitConfig.getCoordinateShift();
  final int maxIterations=fitConfig.getMaxIterations();
  final int maxEvaluations=fitConfig.getMaxFunctionEvaluations();
  fitConfig.setCoordinateShift(FastMath.min(width,height));
  fitConfig.setFitRegion(0,0,0);
  fitConfig.setMaxIterations(maxIterations * ITERATION_INCREASE_FOR_DOUBLETS);
  fitConfig.setMaxFunctionEvaluations(maxEvaluations * FitWorker.EVALUATION_INCREASE_FOR_DOUBLETS);
  final boolean isComputeResiduals=gf.isComputeResiduals();
  gf.setComputeResiduals(false);
  final boolean[] amplitudeEstimate=new boolean[2];
  final FitResult newFitResult=gf.fit(region,width,height,2,doubletParams,amplitudeEstimate,doubletParams[Gaussian2DFunction.BACKGROUND] == 0);
  gf.setComputeResiduals(isComputeResiduals);
  fitConfig.setCoordinateShift(shift);
  fitConfig.setFitRegion(width,height,0.5);
  fitConfig.setMaxIterations(maxIterations);
  fitConfig.setMaxFunctionEvaluations(maxEvaluations);
  updateError(newFitResult);
  if (newFitResult.getStatus() == FitStatus.OK) {
    final double doubleSumOfSquares=gf.getFinalResidualSumOfSquares();
    final int length=width * height;
    final double ic1, ic2;
    if (fitConfig.getFitSolver() == FitSolver.MLE && fitConfig.isModelCamera()) {
      final double doubleValue=gf.getValue();
      ic1=Maths.getBayesianInformationCriterion(singleValue,length,fitResult.getNumberOfFittedParameters());
      ic2=Maths.getBayesianInformationCriterion(doubleValue,length,newFitResult.getNumberOfFittedParameters());
      if (logger != null)       logger.info("String_Node_Str",singleSumOfSquares,singleValue,ic1,doubleSumOfSquares,doubleValue,ic2,ic1 - ic2);
    }
 else {
      ic1=Maths.getBayesianInformationCriterionFromResiduals(singleSumOfSquares,length,fitResult.getNumberOfFittedParameters());
      ic2=Maths.getBayesianInformationCriterionFromResiduals(doubleSumOfSquares,length,newFitResult.getNumberOfFittedParameters());
      if (logger != null)       logger.info("String_Node_Str",singleSumOfSquares,ic1,doubleSumOfSquares,ic2,ic1 - ic2);
    }
    if (logger2 != null) {
      double[] peakParams=newFitResult.getParameters();
      if (peakParams != null) {
        peakParams=Arrays.copyOf(peakParams,peakParams.length);
        int npeaks=peakParams.length / 6;
        for (int i=0; i < npeaks; i++) {
          peakParams[i * 6 + Gaussian2DFunction.X_POSITION]+=0.5 + regionBounds.x;
          peakParams[i * 6 + Gaussian2DFunction.Y_POSITION]+=0.5 + regionBounds.y;
        }
      }
      String msg=String.format("String_Node_Str",slice,cc.fromRegionToGlobalX(cx),cc.fromRegionToGlobalY(cy),newFitResult.getStatus(),newFitResult.getStatusData(),singleValue,gf.getValue(),singleSumOfSquares,doubleSumOfSquares,ic1,ic2,Arrays.toString(peakParams));
      logger2.debug(msg);
    }
    if (ic2 > ic1) {
      return createResult(newFitResult,null,FitStatus.NO_MODEL_IMPROVEMENT);
    }
    final double[] fitParams=newFitResult.getParameters();
    final double[] initialParams=newFitResult.getInitialParameters();
    final double halfWindow=0.5 * FastMath.min(regionBounds.width,regionBounds.height);
    final int[] position=new int[2];
    final int[] candidateIndex=new int[2];
    int nPeaks=0;
    NEXT_PEAK:     for (int n=0; n < 2; n++) {
      final int offset=n * 6;
      initialParams[Gaussian2DFunction.X_POSITION + offset]=candidates.get(candidateId).x - regionBounds.x;
      initialParams[Gaussian2DFunction.Y_POSITION + offset]=candidates.get(candidateId).y - regionBounds.y;
      final double xpos=fitParams[Gaussian2DFunction.X_POSITION + offset] + 0.5;
      final double ypos=fitParams[Gaussian2DFunction.Y_POSITION + offset] + 0.5;
      if (xpos < 0 || xpos > width || ypos < 0 || ypos > height) {
        if (logger != null)         logger.info("String_Node_Str",xpos,ypos,width,height);
        continue;
      }
      double xShift=fitParams[Gaussian2DFunction.X_POSITION + offset] - params[Gaussian2DFunction.X_POSITION];
      double yShift=fitParams[Gaussian2DFunction.Y_POSITION + offset] - params[Gaussian2DFunction.Y_POSITION];
      if (Math.abs(xShift) > halfWindow || Math.abs(yShift) > halfWindow) {
        final double a=QuadrantAnalysis.getAngle(qa.vector,new double[]{xShift,yShift});
        if (a > 0.785398 && a < 2.356194) {
          if (logger != null) {
            logger.info("String_Node_Str",n,xShift,yShift,a * 57.29578);
          }
          continue;
        }
      }
      xShift=fitParams[Gaussian2DFunction.X_POSITION + offset] - initialParams[Gaussian2DFunction.X_POSITION];
      yShift=fitParams[Gaussian2DFunction.Y_POSITION + offset] - initialParams[Gaussian2DFunction.Y_POSITION];
      final double distanceToSingleFit2=xShift * xShift + yShift * yShift;
      if (peakNeighbours.length != 0) {
        final float fcx2=(float)(regionBounds.x + fitParams[Gaussian2DFunction.X_POSITION + offset] + 0.5);
        final float fcy2=(float)(regionBounds.y + fitParams[Gaussian2DFunction.Y_POSITION + offset] + 0.5);
        final float d2=(float)distanceToSingleFit2;
        for (int i=0; i < peakNeighbours.length; i++) {
          if (d2 > distance2(fcx2,fcy2,peakNeighbours[i].params)) {
            if (logger != null) {
              logger.info("String_Node_Str",n,candidates.get(candidateId).x,candidates.get(candidateId).y,fcx2,fcy2,peakNeighbours[i].getXPosition(),peakNeighbours[i].getYPosition());
            }
            continue NEXT_PEAK;
          }
        }
      }
      int otherId=candidateId;
      if (neighbours.size != 0) {
        float fcx2=(float)(regionBounds.x + fitParams[Gaussian2DFunction.X_POSITION + offset]);
        float fcy2=(float)(regionBounds.y + fitParams[Gaussian2DFunction.Y_POSITION + offset]);
        double mind2=distanceToSingleFit2;
        for (int j=0; j < neighbours.size; j++) {
          final int id=neighbours.list[j].index;
          if (isFit(id))           continue;
          final double d2=distance2(fcx2,fcy2,candidates.get(id));
          if (mind2 > d2) {
            mind2=d2;
            otherId=id;
          }
        }
        if (otherId != candidateId) {
          if (logger != null) {
            logger.info("String_Node_Str",n,candidates.get(candidateId).x,candidates.get(candidateId).y,fcx2 + 0.5f,fcy2 + 0.5f,candidates.get(otherId).x,candidates.get(otherId).y);
          }
          initialParams[Gaussian2DFunction.X_POSITION + offset]=candidates.get(otherId).x - regionBounds.x;
          initialParams[Gaussian2DFunction.Y_POSITION + offset]=candidates.get(otherId).y - regionBounds.y;
        }
      }
      candidateIndex[nPeaks]=otherId;
      position[nPeaks++]=n;
    }
    if (nPeaks == 0) {
      return createResult(newFitResult,null,FitStatus.FAILED_VALIDATION);
    }
    convertParameters(fitParams);
    final PreprocessedPeakResult[] results=new PreprocessedPeakResult[nPeaks];
    for (int i=0; i < nPeaks; i++) {
      final ResultType resultType=(candidateIndex[i] <= candidateId) ? ResultType.NEW : ResultType.CANDIDATE;
      results[i]=resultFactory.createPreprocessedPeakResult(candidateIndex[i],position[i],initialParams,fitParams,0,resultType);
    }
    return createResult(newFitResult,results);
  }
 else {
    if (logger != null)     logger.info("String_Node_Str",newFitResult.getStatus());
    if (logger2 != null) {
      double[] peakParams=newFitResult.getParameters();
      if (peakParams != null) {
        peakParams=Arrays.copyOf(peakParams,peakParams.length);
        int npeaks=peakParams.length / 6;
        for (int i=0; i < npeaks; i++) {
          peakParams[i * 6 + Gaussian2DFunction.X_POSITION]+=0.5 + regionBounds.x;
          peakParams[i * 6 + Gaussian2DFunction.Y_POSITION]+=0.5 + regionBounds.y;
        }
      }
      String msg=String.format("String_Node_Str",slice,cc.fromRegionToGlobalX(cx),cc.fromRegionToGlobalY(cy),newFitResult.getStatus(),newFitResult.getStatusData(),Arrays.toString(peakParams));
      logger2.debug(msg);
    }
    return createResult(newFitResult,null);
  }
}