@Override
  protected OperationData process(IDataset input, IMonitor monitor) {

    RectangularROI box = model.getBox();

    Dataset in1 =
        BoxSlicerRodScanUtils.rOIBox(input, monitor, box.getIntLengths(), box.getIntPoint());

    if (g2 == null) g2 = new Polynomial2D(model.getFitPower());
    if ((int) Math.pow(model.getFitPower() + 1, 2) != g2.getNoOfParameters())
      g2 = new Polynomial2D(model.getFitPower());

    Dataset[] fittingBackground =
        BoxSlicerRodScanUtils.LeftRightTopBottomBoxes(
            input, monitor, box.getIntLengths(), box.getIntPoint(), model.getBoundaryBox());

    Dataset offset = DatasetFactory.ones(fittingBackground[2].getShape(), Dataset.FLOAT64);

    Dataset intermediateFitTest = Maths.add(offset, fittingBackground[2]);
    Dataset matrix =
        LinearLeastSquaresServicesForSXRD.polynomial2DLinearLeastSquaresMatrixGenerator(
            model.getFitPower(), fittingBackground[0], fittingBackground[1]);

    DoubleDataset test = (DoubleDataset) LinearAlgebra.solveSVD(matrix, intermediateFitTest);
    double[] params = test.getData();

    DoubleDataset in1Background =
        g2.getOutputValues0(
            params, box.getIntLengths(), model.getBoundaryBox(), model.getFitPower());

    IndexIterator it = in1Background.getIterator();

    while (it.hasNext()) {
      double v = in1Background.getElementDoubleAbs(it.index);
      if (v < 0) in1Background.setObjectAbs(it.index, 0);
    }

    Dataset pBackgroundSubtracted = Maths.subtract(in1, in1Background, null);

    pBackgroundSubtracted.setName("pBackgroundSubtracted");

    IndexIterator it1 = pBackgroundSubtracted.getIterator();

    while (it1.hasNext()) {
      double q = pBackgroundSubtracted.getElementDoubleAbs(it1.index);
      if (q < 0) pBackgroundSubtracted.setObjectAbs(it1.index, 0);
    }

    Dataset output = DatasetUtils.cast(pBackgroundSubtracted, Dataset.FLOAT64);

    output.setName("Region of Interest, polynomial background removed");

    return new OperationData(output);
  }
  /**
   * - * @param axes specify real and imaginary coordinates as two 1d datasets
   *
   * @return a list containing a dataset of recursion limits
   */
  @Override
  public List<Dataset> value(IDataset... axes) {
    Dataset xaxis, yaxis;

    if (axes.length < 2) {
      throw new IllegalArgumentException("Need two axes");
    }
    xaxis = DatasetUtils.convertToDataset(axes[0]);
    yaxis = DatasetUtils.convertToDataset(axes[1]);
    if (xaxis.getRank() != 1 || yaxis.getRank() != 1) {
      throw new IllegalArgumentException("Need both axes to be 1d datasets");
    }

    IntegerDataset count =
        DatasetFactory.zeros(IntegerDataset.class, yaxis.getShapeRef()[0], xaxis.getShapeRef()[0]);

    List<Dataset> result = new ArrayList<Dataset>();

    final IndexIterator iy = yaxis.getIterator();
    int n = 0;
    while (iy.hasNext()) {
      final double iv = yaxis.getElementDoubleAbs(iy.index);
      final IndexIterator ix = xaxis.getIterator();
      while (ix.hasNext()) {
        final double rv = xaxis.getElementDoubleAbs(ix.index);

        double x = 0, y = 0;
        int c = -1;
        do {
          double t = x * x - y * y + rv;
          y = 2. * x * y + iv;
          x = t;
        } while (++c < maxRecursion && x * x + y * y <= 4.);
        count.setAbs(n++, c);
      }
    }
    result.add(count);
    return result;
  }
  /**
   * Fast statistics as a rough guide - this is faster than Dataset.getMin() and getMax() which may
   * cache but slows the opening of images too much. The return array[2] was added in "Updated for
   * Diffraction Tool." commit, but no trace of such usage. However it should not be removed,
   * because it is useful as return array[3].
   *
   * @param bean
   * @return [0] = min [1] = max(=mean*constant) [2] = mean [3] max
   */
  public double[] getFastStatistics(ImageServiceBean bean) {

    Dataset image = getImageLoggedData(bean);

    if (bean.getHistogramType() == HistoType.OUTLIER_VALUES && !bean.isLogColorScale()) {

      double[] ret = null;
      try {
        double[] stats = Stats.outlierValues(image, bean.getLo(), bean.getHi(), -1);
        ret = new double[] {stats[0], stats[1], -1};
      } catch (IllegalArgumentException iae) {
        bean.setLo(10);
        bean.setHi(90);
        double[] stats = Stats.outlierValues(image, bean.getLo(), bean.getHi(), -1);
        ret = new double[] {stats[0], stats[1], -1};
      }

      if (bean.isLogColorScale() && ret != null) {
        ret = new double[] {Math.pow(10, ret[0]), Math.pow(10, ret[1]), -1};
      }

      return ret;
    }

    double min = Double.MAX_VALUE;
    double max = -Double.MAX_VALUE;
    double sum = 0.0;
    int size = 0;

    BooleanDataset mask =
        bean.getMask() != null
            ? (BooleanDataset) DatasetUtils.cast(bean.getMask(), Dataset.BOOL)
            : null;

    // Big loop warning:
    final IndexIterator it = image.getIterator();
    final IndexIterator mit = mask == null ? null : mask.getIterator();
    while (it.hasNext()) {

      final double val = image.getElementDoubleAbs(it.index);
      if (mit != null && mit.hasNext()) {
        if (!mask.getElementBooleanAbs(mit.index)) {
          continue; // Masked!
        }
      }

      if (Double.isNaN(val)) continue;
      if (!bean.isInBounds(val)) continue;

      sum += val;
      if (val < min) min = val;
      if (val > max) max = val;
      size++;
    }

    double retMax = Double.NaN;
    double retExtra = Double.NaN;

    if (bean.getHistogramType() == HistoType.MEDIAN) {

      double median = Double.NaN;
      try {
        median = ((Number) Stats.median(image)).doubleValue(); // SLOW
      } catch (Exception ne) {
        median = ((Number) Stats.median(image.cast(Dataset.INT16))).doubleValue(); // SLOWER
      }
      retMax = 2 * median;
      retExtra = median;

    } else { // Use mean based histo
      double mean = sum / size;
      retMax = (Math.E) * mean; // Not statistical, E seems to be better than 3...
      retExtra = mean;
    }

    if (retMax > max) retMax = max;

    if (bean.isLogColorScale()) {
      return new double[] {Math.pow(10, min), Math.pow(10, retMax), Math.pow(10, retExtra)};
    }

    return new double[] {min, retMax, retExtra, max};
  }