Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 78 additions & 0 deletions docs/ops/doc/WritingYourOwnOpPackage.md
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,84 @@ class DoubleSizeOp implements Function<double[], Double> {

```

### Defining Op Progress
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Op progress mechanism here described seems to exist primarily for SciJava Ops via Fiji. Can you add a bit about scijava-progress's relationship with Fiji?


The `scijava-progress` module provides a mechanism for long-running tasks to describe their progress through textual or graphical means. For example, `scijava-progress` enables [Fiji](https://fiji.sc/) users to observe the progress of every Op invoked within the application, as shown below.

<center>
<figure>
<img src="https://media.scijava.org/scijava-ops/1.0.0/scijava_progress_example.png" alt="scijava-progress updates within Fiji" style="width:50%;"/>
<figcaption><em>scijava-progress provides updates from all Op executions within Fiji's `Tasks` pane. </em></figcaption>
</figure>
</center>

While all Ops emit "binary" progress (denoting each Op's beginning and end), your Op can provide richer updates by adding the `scijava-progress` module, providing user value for long-running Ops. To add progress to your Op, you must add the following steps to your Op:

* Before any significant computation, add the line `Progress.defineTotal(long elements)` where `elements` is the number of "discrete packets" of computation.
* At convenient spots within your Op, call `Progress.update()` to denote that one packet of computation has finished.
* **Alternatively**, it may be more convenient or performant to call `Progress.update(long numElements)` to denote `numElements` packets have completed at once.

```java
import java.util.function.Function;
import org.scijava.progress.Progress;

/**
* A simple summer
*
* @implNote op names="stats.sum"
*/
class DoubleSumOp implements Function<double[], Double> {
public Double apply(final double[] inArray) {
// define total progress size
Progress.defineTotal(inArray.length);
double sum = 0;
for (double v : inArray) {
sum += v;
// increment progress
Progress.update();
}
return i;
}
}
```

If your want to include the progress of Op dependencies within your Op's total progress, you can make the following changes.
* For each Op dependency that you want to track, pass the Hint `"progress.TRACK"` within the `@OpDependency` annotation. Note that it is **not** necessary for each Op to explicitly define its progress, but if it does so your Op will provide richer progress updates!
* Replace `Progress.defineTotal(long elements)` with `Progress.defineTotal(long elements, long subTasks)`, where `subTasks` is the **total** number of times you will invoke Op dependencies annotated with `"progress.TRACK"`.


```java
import java.util.function.Function;
import org.scijava.progress.Progress;
import org.scijava.ops.spi.OpDependency;

/**
* A simple mean calculator
*
* @implNote op names="stats.mean"
*/
class DoubleMeanOp implements Function<double[], Double> {

// This Op will contribute to progress
@OpDependency(name="stats.sum", hints={"progress.TRACK"})
public Function<double[], Double> sumOp;

// This Op will also contribute to progress
@OpDependency(name="stats.size", hints={"progress.TRACK"})
public Function<double[], Double> sizeOp;

public Double apply(final double[] inArray) {
// There's no significant work here, but we do have 2 subtasks.
Progress.defineTotal(0, 2);
final Double sum = sumOp.apply(inArray);
final Double size = sizeOp.apply(inArray);
return sum / size;
}
}
```

Comment on lines +353 to +354
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be nice to have a little screen shot of the progress/task manager in Fiji running decon/saca.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you discuss what, if any, performance hit a developer might expect with Progress? Maybe a little section on ideal use versus what a bad use of Progress looks like. I don't think we need to go crazy, but just a flavor.

For best results, ensure your Op records Progress updates at a reasonable frequency. If too frequent, progress updates can detract from algorithm performance, and if too infrequent, they will be of little help to the user!

### Element-wise Ops

Simple pixel-wise operations like addition, inversion, and more can be written on a single pixel (i.e. `RealType`) - therefore, SciJava Ops Image takes care to automagically adapt pixel-wise Ops across a wide variety of image types. If you would like to write a pixel-wise Op, we recommend the following structure.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,13 @@
*/
public interface OpEnvironment extends Prioritized<OpEnvironment> {

/**
* Generates an <b>empty</b> {@link OpEnvironment}, which can be populated
* with the Ops of the caller's choice.
*
* @return an empty {@link OpEnvironment}
* @see #build() for an {@link OpEnvironment} that is fully populated
*/
static OpEnvironment buildEmpty() {
Optional<OpEnvironment> opsOptional = Discoverer //
.using(ServiceLoader::load) //
Expand All @@ -72,22 +79,31 @@ static OpEnvironment buildEmpty() {
);
}

/**
* Generates an {@link OpEnvironment} with all available Ops.
*
* @return an {@link OpEnvironment} with all available Ops.
* @see #buildEmpty() for an {@link OpEnvironment} that is empty
*/
static OpEnvironment build() {
OpEnvironment ops = buildEmpty();
ops.discoverEverything();
return ops;
}

/**
* Obtains all Ops in the {@link OpEnvironment}.
* Obtains all Ops in the {@link OpEnvironment}, sorted by priority.
*
* @return a {@link SortedSet} containing all Ops contained in the
* {@link OpEnvironment}.
*/
SortedSet<OpInfo> infos();
default SortedSet<OpInfo> infos() {
return infos(null, getDefaultHints());
}

/**
* Obtains all Ops in the {@link OpEnvironment} that are named {@code name}.
* Obtains all Ops in the {@link OpEnvironment} that are named {@code name},
* sorted by priority.
*
* @param name the {@link String} of all Ops to be returned.
* @return a {@link SortedSet} containing all Ops in the {@link OpEnvironment}
Expand All @@ -96,7 +112,8 @@ static OpEnvironment build() {
SortedSet<OpInfo> infos(String name);

/**
* Obtains all Ops in the {@link OpEnvironment} that match {@code hints}
* Obtains all Ops in the {@link OpEnvironment} that match {@code hints},
* sorted by priority
*
* @param hints the {@link Hints} used to filter available Ops.
* @return a {@link SortedSet} containing all Ops in the {@link OpEnvironment}
Expand All @@ -106,7 +123,7 @@ static OpEnvironment build() {

/**
* Obtains all Ops in the {@link OpEnvironment} that are named {@code name}
* and match {@code hints}
* and match {@code hints}, sorted by priority
*
* @param name the {@link String} of all Ops to be returned.
* @param hints the {@link Hints} used to filter available Ops.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
import org.scijava.ops.spi.OpCollection;
import org.scijava.ops.spi.OpDependency;
import org.scijava.priority.Priority;
import org.scijava.progress.Progress;
import org.scijava.struct.FunctionalMethodType;
import org.scijava.struct.ItemIO;
import org.scijava.types.*;
Expand Down Expand Up @@ -167,13 +168,16 @@ public SortedSet<OpInfo> infos(String name, Hints hints) {

@Override
public void discoverUsing(Discoverer... arr) {
Progress.register(this, "OpEnvironment: Discovering Ops");
Progress.defineTotal(arr.length);
for (Discoverer d : arr) {
discoverers.add(d);

d.discover(OpInfo.class).forEach(this::registerInfosFrom);
d.discover(Op.class).forEach(this::registerInfosFrom);
d.discover(OpCollection.class).forEach(this::registerInfosFrom);
Progress.update();
}
Progress.complete();
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -251,19 +251,19 @@ public static ColorTable8 spci() {
* and returns {@code r}, {@code g}, and {@code b} in the set [0, 255].
* </p>
* <p>
* Conversion formula adapted from Wikipedia's <a
* href="https://en.wikipedia.org/wiki/HSL_and_HSV">HSL and HSV article</a> and
* Michael Jackson's <a href="http://bit.ly/9L2qln">blog post on additive
* Conversion formula adapted from Wikipedia's
* <a href="https://en.wikipedia.org/wiki/HSL_and_HSV">HSL and HSV article</a>
* and Michael Jackson's <a href="http://bit.ly/9L2qln">blog post on additive
* color model conversion algorithms</a>.
* </p>
*
*
* @param h The hue
* @param s The saturation
* @param v The value
* @return ColorRGB The RGB representation
*/
private static void hsvToRgb(final double h, final double s,
final double v, final int[] rgb)
private static void hsvToRgb(final double h, final double s, final double v,
final int[] rgb)
{
double r01 = 0, g01 = 0, b01 = 0;

Expand Down
2 changes: 1 addition & 1 deletion scijava-ops-image/src/main/java/module-info.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@
requires org.scijava.concurrent;
requires org.scijava.function;
requires org.scijava.meta;
requires org.scijava.ops.api;
requires org.scijava.progress;
requires org.scijava.ops.api;
requires org.scijava.ops.spi;
requires org.scijava.priority;
requires org.scijava.types;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ public static <I extends RealType<I>> void execute(

LoopBuilder.setImages(oldsqrtN).multiThreaded().forEachPixel(t -> t
.setOne());
Progress.setStageMax(TU);
Progress.defineTotal(TU);
for (int s = 0; s < TU; s++) {
intSize = (int) Math.floor(size);
singleiteration(image1, image2, thres1, thres2, stop, oldtau, oldsqrtN,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,6 @@ public void compute(final RandomAccessibleInterval<I> image1,
}

// set seed, compute thresholds and create empty result if necessary
Progress.defineTotalProgress(1);
if (seed == null) seed = 0xdeadbeefL;
if (thres1 == null) thres1 = otsuOp.apply(histOp.apply(Views.iterable(
image1)));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,8 @@ public static <T> void copyRAI( //
final RandomAccessibleInterval<T> copy //
) {
ensureEqualDimensions(input, copy);
LoopBuilder.setImages(input, copy).forEachPixel(copier::compute);
LoopBuilder.setImages(input, copy).multiThreaded().forEachPixel(
copier::compute);
}

/**
Expand Down
Loading