Skip to content
Merged
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
40 changes: 22 additions & 18 deletions docs/ops/doc/CallingOps.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ In this page, we start after having [identified a Gaussian Blur Op](SearchingFor

From the `OpEnvironment`, an `OpBuilder` chain is initialized with the `op(String)` method, which describes the name of the Op that we ultimately want to call:

```groovy
```java
ops.op("filter.gauss")
```

Expand All @@ -20,7 +20,7 @@ With the name established in the `OpBuilder` chain, we can then specify our inpu

For this Gaussian blur, we have two inputs: `inImage` is the image we want to blur, and a `double` value as our sigma parameter:

```groovy
```java
ops.op("filter.gauss").input(inImage, 2.0)
```

Expand All @@ -30,15 +30,15 @@ After specifying inputs, we provide a preallocated output container using the `.

For our Gaussian blur, we will pass our output image `outImage` as a receptacle for the result:

```groovy
```java
ops.op("filter.gauss").input(inImage, 2.0).output(outImage)
```

## Computing with `.compute()`

With all of our desired Op's inputs and output now specified, we can run it with the `.compute()` method.

```groovy
```java
ops.op("filter.gauss").input(inImage, 2.0).output(outImage).compute()
```

Expand All @@ -56,21 +56,21 @@ Calling our Gaussian blur as a *computer* above is great when we have pre-alloca

*Functions* are used when we want to *create* the final output, indicated by ending the builder with `.apply()`:

```groovy
```java
var outImage = ops.op("filter.gauss").input(inImage, 2.0).apply()
```

*Inplaces* are used when we want to destructively modify one of the existing inputs (which is explicitly forbidden by *computers*; a *computer* Op's output should be a different object from all of its inputs). We indicate this by the `mutate#()` method, where the `#` corresponds to the *parameter index* that will be modified:
*Inplaces* are used when we want to destructively modify one of the existing inputs (which is explicitly forbidden by *computers*; a *computer* Op's output should be a different object from all of its inputs). We indicate this by the `mutate#()` method, where the `#` corresponds to the *parameter index* (starting from 1 for the first parameter) that will be modified:

```
# Modify the first input in-place
```java
// Modify the first input in-place
ops.op("filter.gauss").input(inImage, 2.0).mutate1()
```

Note that although the final method call changes for each mode of operation, *this is based on the path taken through the `OpBuilder` chain*. For example, we cannot call the `compute()` method if we haven't provided an `.output()`:

```
# Does not compute
```java
// Does not compute
ops.op("filter.gauss").input(inImage, 2.0).compute()
```

Expand All @@ -80,22 +80,26 @@ A key takeaway from this section is that how you **request** the Op does not nec

When you want to call an Op many times on different inputs, the `OpBuilder` can be used to return the *Op* itself, instead of performing the computation. Instead of calling the `.compute()` function at the end of our `OpBuilder` chain, we can use the `.computer()` method (or `.inplace()` or `.function()`, as appropriate) to get back the matched Op, which can then be reused via its `.compute()` method (or `.apply()` or `.mutate#()`, respectively):

```groovy
var gaussOp = ops.op("filter.gauss").input(inImage, 2.0).output(outImage).computer()
gaussOp.compute(inImage, 2.0, outImage1)
gaussOp.compute(inImage, 5.0, outImage2)
```java
var gaussOp = ops.op("filter.gauss").input(inImage, 2.0).output(outImage).computer();
gaussOp.compute(inImage, 2.0, outImage1);
gaussOp.compute(inImage, 5.0, outImage2);
```

While we do pass concrete inputs and outputs in this example, they are essentially just being used to reason about the desired *types* - which we'll cover in the next section.

*Note that the default `OpEnvironment` implementations cache Op requests* - this means that repeated `OpBuilder` requests targeting the same action will be faster than the original matching call.

### Matching with classes
### Matching with types/classes

In addition to the `.input()` and `.output()` builder steps, there are parallel `.inType()` and `.outType()`
methods. These accept either a `Class` or a `Nil` - the latter allowing retention of generic types.
These methods makes it possible to search for an Op without actually having an instance of the objects
you (eventually) plan to operate upon. For example, you might want to look up the `filter.gauss` Op that
would be used to smooth an `ImgPlus` by a particular `double` value, without actually having an `ImgPlus`
image available to pass to the builder request:

```groovy
```java
var computer = ops.op("filter.gauss").inType(ImgPlus.class, Double.class).outType(ImgPlus.class).computer()
```

Expand All @@ -106,7 +110,7 @@ not been concretely specified yet. This is very sensible when we want to re-use
We can also use the `.outType()` methods to add type safety to our `Function` calls:

```java
Img outImage = ops.op("filter.gauss").input(inImage, 2.0).outType(Img.class).apply();
Img outImage = ops.op("filter.gauss").input(inImage, 2.0).outType(Img.class).apply()
```

## Common Pitfalls: Wildcards
Expand All @@ -124,7 +128,7 @@ gaussOp.compute(inImage, 2.0, outImage);
If you don't need to save the Op to a variable, *just [call it directly](#computing-with-compute)*:

```java
ops.op("filter.gauss").input(inImage, 2.0).output(outImage).compute();
ops.op("filter.gauss").input(inImage, 2.0).output(outImage).compute()
```

Generally speaking, op requests are **cached**, meaning repeated OpBuilder calls that directly execute Ops will **not** significantly decrease performance.
Expand Down