Ticket #1384 (closed enhancement: wontfix)

Opened 7 years ago

Last modified 6 years ago

Create ImageStatistics class

Reported by: aivar Owned by: aivar
Priority: minor Milestone: imagej2-b8-analysis
Component: ImgLib2 Version:
Severity: minor Keywords:
Cc: Blocked By:
Blocking: #794

Description (last modified by aivar) (diff)

Need to get statistics on an image with the minimal number of passes through the image.

For example it might include methods:

request certain things ahead of time
doMinMax();
doHistogram(int bins);

[or doHistogram(int bins, double min, double max);]

doMean();

do minimal number of passes through the image
process();

get accumulated results
double[] getMinMax();
long[] getHistogram();
double getMean;

Here if you don't specify a min/max for doHistogram the code has to take an initial pass to get min/max then another to build the histogram using that min/max.

Change History

comment:1 Changed 7 years ago by aivar

  • Blocking 1385 added

comment:2 Changed 7 years ago by aivar

  • Blocking 1385 removed

comment:3 Changed 7 years ago by aivar

  • Blocking 1386 added

comment:4 Changed 7 years ago by aivar

  • Component changed from other to imglib

comment:5 Changed 7 years ago by aivar

  • Description modified (diff)

comment:6 Changed 7 years ago by aivar

  • Blocking 1128 added; 1386 removed
  • Milestone changed from imagej-2.0.0-beta4 to imagej-2.0.0-beta5

comment:7 Changed 7 years ago by bdezonia

  • Blocking 794 added

comment:8 Changed 7 years ago by bdezonia

I think using the MeasurmentService all this can be accomplished. We should discuss actual examples so we can try.

(Later edit: I may have misspoke. The MeasurementService is pretty simple. Users need to provide aggregating classes to reuse computations.)

Last edited 7 years ago by bdezonia (previous) (diff)

comment:9 Changed 7 years ago by aivar

Here's some simplistic pseudo-code for the sort of approach I had in mind:

class MeanFunction extends AbstractBaseFunction {
  double sum;
  int count;
  double mean;

  @Override
  preprocess() { // called before any pixel processing
    sum = 0.0;
    count = 0;
  }

  @Override
  processPixel(T pixel) { // processes pixels one by one
    sum += T.getRealDouble();
    ++count;
  }

  @Override
  postprocess() { // called after all pixel processing
    double mean = sum / count;
  }

  getMean() {
    return mean;
  }
}

class AbstractBaseFunction implements Function {
  Function next;

  @Override
  chain(Function next) { // builds a chain of functions
    this.next = next;
  }

  @Override
  chainedPreprocess() { // preprocess the chain
     preprocess();
     if (null != next) {
       next.chainedPreprocess();
  }

  @Override
  chainedProcessPixel(T pixel) { // process pixel through the chain
    processPixel(pixel);
    if (null != next) {
      next.chainedProcessPixel(pixel);
    }
  }

  @Override
  chainedPostprocess() { // postprocess the chain
     postprocess();
     if (null != next) {
       next.postprocess();
     }
  }

  abstract preprocess();
  abstract postprocess();
  abstract processPixel();
}

  // usage
  MeanFunction meanFunction = new MeanFunction();
  MinMaxFunction minMaxFunction = new MinMaxFunction();
  meanFunction.chain(minMaxFunction);
  process(iterator, meanFunction);
  System.out.println("Mean is " + meanFunction.getMean());
  System.out.println("Min is " + minMaxFunction.getMin() + " max is " + minMaxFunction.getMax());

  . . .

  void process(PointSetIterator iterator, Function function) {
    function.chainedPreprocess();
    while (iterator.hasNext()) {
      point = iterator.next();
      function.chainedProcessPixel(point);
    }
    function.chainedPostprocess();
  }
Last edited 7 years ago by aivar (previous) (diff)

comment:10 Changed 7 years ago by aivar

This doesn't handle re-use of computations. Perhaps the chain could be a doubly-linked list, then in the preprocess( ) method you could look back up the chain for the Function you need results from (using a common helper method). Then in processPixel( ) you can call that Function's getters.

Last edited 7 years ago by aivar (previous) (diff)

comment:11 Changed 7 years ago by curtis

I think this chaining approach is unnecessarily complex. If you have a computation (we'll call it a "module") that could benefit from the outputs of another module, there are a couple of ways of dealing with that to manage efficiency:

1) Create two modules, one that takes e.g. a Dataset and a Histogram, and computes results based on those.

2) Create one module that takes a required Dataset and an optional Histogram, and uses the Histogram results if present (or if not, just computes what is needed on the fly from the Dataset itself).

comment:12 Changed 7 years ago by bdezonia

I've implemented some ideas in the measure-engine branch of Imlib2.

Related fiji email available at: ( https://groups.google.com/forum/?fromgroups=#!topic/fiji-devel/AnYq_caJA1M)

Note that after posting my email dscho chimed in that the KNIME developers have a good start on a measurement engine that they will release to Imglib.

comment:13 Changed 7 years ago by bdezonia

  • Blocking 1128 removed

(In #1128) I have been doing some work on this for our most recent beta on imglib's histogram-stuff branch. Soon I'll share with others and solicit feedback.

comment:14 Changed 6 years ago by bdezonia

  • Milestone changed from imagej2-b7-ndim-data to imagej2-b8-analysis

comment:15 Changed 6 years ago by aivar

  • Status changed from new to closed
  • Resolution set to wontfix

My original motivation for the 'minimal number of passes' was based on the case of a huge image that is possibly paged in/out. Other situations might require other optimizations.

Note: See TracTickets for help on using tickets.