/**
   * TODO
   *
   * @param cumulativeMinCutoff
   * @param cumulativeMaxCutoff
   * @param state
   * @param setupAssignments
   */
  public static void initBrightness(
      final double cumulativeMinCutoff,
      final double cumulativeMaxCutoff,
      final ViewerState state,
      final SetupAssignments setupAssignments) {
    final Source<?> source = state.getSources().get(state.getCurrentSource()).getSpimSource();
    final int timepoint = state.getCurrentTimepoint();
    if (!source.isPresent(timepoint)) return;
    if (!UnsignedShortType.class.isInstance(source.getType())) return;
    @SuppressWarnings("unchecked")
    final RandomAccessibleInterval<UnsignedShortType> img =
        (RandomAccessibleInterval<UnsignedShortType>)
            source.getSource(timepoint, source.getNumMipmapLevels() - 1);
    final long z = (img.min(2) + img.max(2) + 1) / 2;

    final int numBins = 6535;
    final Histogram1d<UnsignedShortType> histogram =
        new Histogram1d<UnsignedShortType>(
            Views.iterable(Views.hyperSlice(img, 2, z)),
            new Real1dBinMapper<UnsignedShortType>(0, 65535, numBins, false));
    final DiscreteFrequencyDistribution dfd = histogram.dfd();
    final long[] bin = new long[] {0};
    double cumulative = 0;
    int i = 0;
    for (; i < numBins && cumulative < cumulativeMinCutoff; ++i) {
      bin[0] = i;
      cumulative += dfd.relativeFrequency(bin);
    }
    final int min = i * 65535 / numBins;
    for (; i < numBins && cumulative < cumulativeMaxCutoff; ++i) {
      bin[0] = i;
      cumulative += dfd.relativeFrequency(bin);
    }
    final int max = i * 65535 / numBins;
    final MinMaxGroup minmax = setupAssignments.getMinMaxGroups().get(0);
    minmax.getMinBoundedValue().setCurrentValue(min);
    minmax.getMaxBoundedValue().setCurrentValue(max);
  }
  @Override
  public long computeBin(final Histogram1d<T> hist) {
    long[] histogram = hist.toLongArray();
    // Otsu's threshold algorithm
    // C++ code by Jordan Bevik <*****@*****.**>
    // ported to ImageJ plugin by G.Landini
    int k, kStar; // k = the current threshold; kStar = optimal threshold
    int L = histogram.length; // The total intensity of the image
    long N1, N; // N1 = # points with intensity <=k; N = total number of
    // points
    long Sk; // The total intensity for all histogram points <=k
    long S;
    double BCV, BCVmax; // The current Between Class Variance and maximum
    // BCV
    double num, denom; // temporary bookkeeping

    // Initialize values:
    S = 0;
    N = 0;
    for (k = 0; k < L; k++) {
      S += k * histogram[k]; // Total histogram intensity
      N += histogram[k]; // Total number of data points
    }

    Sk = 0;
    N1 = histogram[0]; // The entry for zero intensity
    BCV = 0;
    BCVmax = 0;
    kStar = 0;

    // Look at each possible threshold value,
    // calculate the between-class variance, and decide if it's a max
    for (k = 1; k < L - 1; k++) { // No need to check endpoints k = 0 or k =
      // L-1
      Sk += k * histogram[k];
      N1 += histogram[k];

      // The float casting here is to avoid compiler warning about loss of
      // precision and
      // will prevent overflow in the case of large saturated images
      denom = (double) (N1) * (N - N1); // Maximum value of denom is
      // (N^2)/4 =
      // approx. 3E10

      if (denom != 0) {
        // Float here is to avoid loss of precision when dividing
        num = ((double) N1 / N) * S - Sk; // Maximum value of num =
        // 255*N =
        // approx 8E7
        BCV = (num * num) / denom;
      } else BCV = 0;

      if (BCV >= BCVmax) { // Assign the best threshold found so far
        BCVmax = BCV;
        kStar = k;
      }
    }
    // kStar += 1; // Use QTI convention that intensity -> 1 if intensity >=
    // k
    // (the algorithm was developed for I-> 1 if I <= k.)
    return kStar;
  }