You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/ops/doc/CallingOps.md
+60-18Lines changed: 60 additions & 18 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,61 +1,96 @@
1
1
# Calling Ops with the `OpBuilder`
2
2
3
-
Ops are designed to be called from the Op matcher, using the `OpBuilder` syntax. OpBuilder chains follow the [builder pattern](https://refactoring.guru/design-patterns/builder), allowing users to create complex Op calls by "chaining" or appending consecutive, simpler method calls.
3
+
Use of the Ops framework centers on a process of matching Op requests to algorithm implementations based on the parameters provided. The easiest way to make these queries is to use the `OpBuilder` syntax, which follows the [builder pattern](https://refactoring.guru/design-patterns/builder)to assemble the required components of an Op matching request from a particular `OpEnvironment`.
4
4
5
-
On this page, we will be constructing an `OpBuilder` call on an `OpEnvironment ops` to execute a Gaussian Blur on an input image `inImage`, with our output buffer `outImage`.
5
+
In this page, we start after having [identified a Gaussian Blur Op](SearchingForOps) that we would like to use. We assume we already have created an `OpEnvironment` named `ops`, as well as the input image to blur, and a pre-allocated output image for the result—`inImage` and `outImage`, respectively.
6
+
7
+
**Note:** we are incrementally constructing one line of code in this example. Running an intermediate step simply returns an appropriate builder that knows what has been set so far, and which step is next. If you're following along in an IDE or script editor, the code you actually *run* would be the last step, once our builder call is fully constructed.
6
8
7
9
## Specifying the name with `.op()`
8
10
9
-
From the `OpEnvironment`, an `OpBuilder`call is initialized with the method `OpEnvironment.op(String)`, which is used to describe the name of the Op that the `OpBuilder` must return:
11
+
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:
10
12
11
13
```groovy
12
14
ops.op("filter.gauss")
13
15
```
14
16
15
17
## Passing the inputs with `.input()`
16
18
17
-
With the name defined in the `OpBuilder`call, we can then chain the inputs with the `input()` method.
19
+
With the name established in the `OpBuilder`chain, we can then specify our input(s) with the `.input()` method.
18
20
19
-
For our gaussian blur, we will pass as inputs our input image `inImage`, and a `double` as our sigma parameter:
21
+
For this Gaussian blur, we have two inputs: `inImage` is the image we want to blur, and a `double` value as our sigma parameter:
20
22
21
23
```groovy
22
24
ops.op("filter.gauss").input(inImage, 2.0)
23
25
```
24
26
25
27
## Passing an output buffer with `.output()`
26
28
27
-
Now that the inputs are specified, we can chain the output buffer using the `output()` method.
29
+
After specifying inputs, we provide a preallocated output container using the `.output()` method.
28
30
29
-
For our gaussian blur, we will pass as the output buffer our output image `outImage`:
31
+
For our Gaussian blur, we will pass our output image `outImage` as a receptacle for the result:
In the call to `compute()`, the `OpEnvironment` will use the components of the `OpBuilder` syntax to:
44
-
* Match an Op based on the name provided, as well as the types of the provided input and output `Object`s
45
-
* Execute the Op on the provided input and output `Object`s.
45
+
In the call to `compute()`, the `OpEnvironment` will use all of the parameters provided to:
46
+
* Match an Op based on the name provided, as well as the types of the provided input and output objects
47
+
* Execute the Op on the provided input and output objects.
48
+
49
+
After this step, `outImage` will contain the results of the Gaussian blur on `inImage`.
50
+
51
+
## Variations on use
46
52
47
-
##Additions: Repeating execution
53
+
### Using a *function* or *inplace* Op
48
54
49
-
When an Op should be executed many times on different inputs, the `OpBuilder` syntax can be modified to return the *Op* instead. Instead of calling the `.compute()` function at the end of our `OpBuilder` call, we can instead call the `.computer()` method to get back the matched Op:
55
+
Calling our Gaussian blur as a *computer* above is great when we have pre-allocated output, but for other scenarios we can request Ops as *functions* or *inplaces*.
56
+
57
+
*Functions* are used when we want to *create* the final output, indicated by ending the builder with `.apply()`:
58
+
59
+
```groovy
60
+
var outImage = ops.op("filter.gauss").input(inImage, 2.0).apply()
61
+
```
62
+
63
+
*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:
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()`:
A key takeaway from this section is that how you **request** the Op does not necessarily need to match how the Op is **implemented**. *Functions* and *computers* should be largely interchangeable, thanks to the Ops engine's adaptation subsystem. For the 1.0.0 release we do not have the necessary adapters to go between *inplaces* and the other paradigms, but it is on our [development roadmap](https://github.com/scijava/scijava/issues/47)!
78
+
79
+
### Repeating execution
80
+
81
+
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):
50
82
51
83
```groovy
52
84
var gaussOp = ops.op("filter.gauss").input(inImage, 2.0).output(outImage).computer()
53
-
gaussOp.compute(inImage, 2.0, outImage)
85
+
gaussOp.compute(inImage, 2.0, outImage1)
86
+
gaussOp.compute(inImage, 5.0, outImage2)
54
87
```
55
88
89
+
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.
90
+
56
91
*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.
57
92
58
-
##Additions: Matching with classes
93
+
###Matching with classes
59
94
60
95
In addition to the `.input()` and `.output()` builder steps, there are parallel `.inType()` and `.outType()`
61
96
methods. These accept either a `Class` or a `Nil` - the latter allowing retention of generic types.
@@ -64,8 +99,15 @@ methods. These accept either a `Class` or a `Nil` - the latter allowing retentio
64
99
var computer = ops.op("filter.gauss").inType(ImgPlus.class, Double.class).outType(ImgPlus.class).computer()
65
100
```
66
101
67
-
When using the `*Type` methods of the builder, the terminal steps will only allow *creation* of the Op, not
68
-
direct execution, since the parameters have not been concretely specified yet.
102
+
In this case, we *must* use the `computer()` terminal method of the builder: we
103
+
can only *create* the Op, not directly execute it, since the parameters have
104
+
not been concretely specified yet. This is very sensible when we want to re-use a computer many times.
105
+
106
+
We can also use the `.outType()` methods to add type safety to our `Function` calls:
@@ -89,7 +131,7 @@ Generally speaking, op requests are **cached**, meaning repeated OpBuilder calls
89
131
90
132
### Solution 2: Avoid using wildcards
91
133
92
-
If you *know* that your `Img` will always contain unsigned byte values, for example, define your variable as an `Img<UnsignedByteType>` rather than using `Img<?>`.
134
+
If you know that your `Img` will always contain unsigned byte values, for example, define your variable as an `Img<UnsignedByteType>` rather than using `Img<?>`.
Using SciJava Ops within scripts unlocks the most powerful aspects of Ops. The following page will explain how you can write a script in Fiji's Script Editor that utilizes Ops for image processing.
3
+
Scripts provide a simple, familiar interface for accessing SciJava Ops and allows combination with additional resources for image processing and beyond. The following page will explain how you can write a script in Fiji's [Script Editor](https://imagej.net/scripting/) that utilizes Ops for image processing.
4
4
5
5
## Obtaining an OpEnvironment
6
6
7
-
To run Ops, scripts require an `OpEnvironment`. The easiest way to obtain an `OpEnvironment`with all available Ops is to declare an `OpEnvironment`as a script parameter:
7
+
To run Ops we always start with an `OpEnvironment`. Within Fiji, the easiest way to obtain an `OpEnvironment` is to declare it as a [script parameter](https://imagej.net/scripting/parameters):
8
8
9
9
```text
10
10
#@ OpEnvironment ops
11
11
```
12
12
13
-
## Obtaining inputs
13
+
## Setting inputs and outputs
14
14
15
-
Scripts using SciJava Ops obtain inputs like any other SciJava script, and the lines below will provide us with an `Img` input parameter and an `Img` output parameter.
15
+
A good starting point is to declare script parameters that match your desired Op's parameters. When performing image processing, we are operating on an existing image; we also want to create an output image in the script so that our result will be shown. The following lines use SciJava script parameters to obtain the active image as an input along with a user-defined sigma, while establishing our output.
16
16
17
17
```text
18
18
#@ Img imgInput
19
+
#@ Double sigma
19
20
#@output Img out
20
21
```
21
22
22
-
For more information on SciJava scripting parameters, please visit [this page](https://imagej.net/scripting/parameters).
23
-
24
23
## Calling Ops
25
24
26
-
The OpBuilder syntax should be used to retrieve and execute Ops from the `OpEnvironment`. The following line executes a Gaussian Blur on an input image using a `filter.gauss` Op:
25
+
The [OpBuilder syntax](CallingOps) should be used to retrieve and execute Ops from the `OpEnvironment`. The following line executes a Gaussian blur on an input image using a `filter.gauss` Op:
27
26
```text
28
-
out = ops.op("filter.gauss").input(imgInput, new Double(3.0)).apply()
27
+
out = ops.op("filter.gauss").input(imgInput, sigma).apply()
29
28
```
30
29
31
30
## Putting it all together
32
31
33
-
The below script can be pasted into the Script Editor. **Ensure that the Script Editor is configured to run a Groovy script**.
32
+
The below script can be pasted into the Script Editor. **Ensure that the Script Editor is configured to run a Groovy script** (*Language → Groovy* in the Script Editor menu).
34
33
35
34
```text
36
35
#@ OpEnvironment ops
37
36
#@ Img imgInput
37
+
#@ Double sigma
38
38
#@output Img out
39
39
40
-
// Call some Ops!
41
-
out = ops.op("filter.gauss").input(imgInput, new Double(3.0)).apply()
40
+
// Call our Op!
41
+
out = ops.op("filter.gauss").input(imgInput, sigma).apply()
42
42
```
43
43
44
-
Scripting in Fiji is a convenient gateway to accessing SciJava Ops. To see more, check out some examples, such as [image deconvolution](examples/deconvolution.rst) or [FLIM analysis](examples/flim_analysis.rst)!
44
+
## Add your Op to the menu
45
+
46
+
If you want to reuse an Op outside of the script editor: good news! All SciJava scripts are runnable as ImageJ commands, and can be [installed](https://imagej.net/plugins/index#installing-plugins-manually) into your Fiji installation. For example, suppose we create new nested folders in our `Fiji.app/scripts` directory: first a `Plugins` folder (if it does not already exist), and then inside of that, a new `Ops` folder. If we then save our script there as `Filter_Gauss.groovy` (or similar—just don't forget the `_`!) then after re-starting Fiji we can run our Op from the *Plugins → Ops* menu, which matches the folder structure we created. The command will also be accessible from the [search bar](https://imagej.net/learn/#the-search-bar).
47
+
48
+
## Next steps
49
+
50
+
Check out the How-To Guides for important information like how to [explore the available Ops](SearchingForOps).
51
+
52
+
Check out some examples such as [image deconvolution](examples/deconvolution.rst) or [FLIM analysis](examples/flim_analysis.rst) to see more complete cases of Ops being used in the Script Editor!
Copy file name to clipboardExpand all lines: docs/ops/doc/SearchingForOps.md
+39-25Lines changed: 39 additions & 25 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,23 +1,37 @@
1
1
# Searching for Ops in the Environment
2
2
3
-
As the `OpEnvironment` is fully extensible, different `OpEnvironment`s might contain different Ops, and it is important to be able to query an `OpEnvironment` about the available Ops.
3
+
The first step when working with Opsis always to obtain an `OpEnvironment`: your gateway to all Ops functionality.
4
4
5
-
The `OpEnvironment.help()` API allows you to query an `OpEnvironment` about the types of Ops it contains, as we show in the following sections. **Note that the example printouts from the help API may not reflect the Ops available in *your* Op environment**.
5
+
If you're working in a [Fiji script](ScriptingInFiji) then this is done with a script parameter:
6
6
7
-
## Searching for operations
7
+
```
8
+
#@ OpEnvironment ops
9
+
```
8
10
9
-
The no-argument method `OpEnvironment.help()` is designed to give you a broad overview over the *categories* of Ops available within the `OpEnvironment`:
11
+
Otherwise we can import and build one ourselves:
10
12
11
-
```groovy
13
+
```
12
14
import org.scijava.ops.api.OpEnvironment
13
15
ops = OpEnvironment.build()
16
+
```
17
+
18
+
Typically we would only want to do this once per application, to avoid diverging environments and reincurring the performance cost of the build. All code examples in this section will assume we have created an `OpEnvironment` named `ops`.
19
+
20
+
As the `OpEnvironment` is fully extensible, different `OpEnvironment`s might contain different Ops, so it is important to be able to query an `OpEnvironment` about its available Ops. We also need to be able to get information about the usage of these Ops, to know what parameters may be required.
14
21
22
+
The `OpEnvironment.help()` API is your window into the `OpEnvironment`. In the following sections we cover the different types of information that can be obtained. **Note that the exact printouts from the help API may be different from the Ops available in *your* environment**.
23
+
24
+
## Listing Namespaces
25
+
26
+
The no-argument method `ops.help()` is designed to give you a broad overview over the *categories* (namespaces) of Ops available within the `OpEnvironment`:
27
+
28
+
```python
15
29
print(ops.help())
16
30
```
17
31
18
-
This gives the following printout:
32
+
Might print output such as:
19
33
20
-
```
34
+
```text
21
35
Namespaces:
22
36
> coloc
23
37
> convert
@@ -45,19 +59,19 @@ Namespaces:
45
59
> types
46
60
```
47
61
48
-
## Interrogating a Namespace
62
+
These namespace categories can then be interrogated further to explore the particular Ops in each.
63
+
64
+
## Querying a Namespace
49
65
50
66
You can choose one of the above namespaces, and `ops.help()` will give you information about the algorithms contained within:
51
-
```groovy
52
-
import org.scijava.ops.api.OpEnvironment
53
-
ops = OpEnvironment.build()
54
67
68
+
```python
55
69
print(ops.help("filter"))
56
70
```
57
71
58
-
This gives the following printout:
72
+
Prints the current list of `filter` ops in the `OpEnvironment`:
59
73
60
-
```
74
+
```text
61
75
Names:
62
76
> filter.dog
63
77
> filter.addNoise
@@ -96,35 +110,33 @@ Names:
96
110
> filter.variance
97
111
```
98
112
99
-
## Signatures for Op Names
100
-
101
-
Finally, you can use `OpEnvironment.help()` on any Op name to see the list of signatures:
113
+
## Querying Op Signatures
102
114
103
-
```groovy
104
-
import org.scijava.ops.api.OpEnvironment
105
-
ops = OpEnvironment.build()
115
+
Finally, you can use `ops.help()` on any Op name to see the list of signatures:
Note that these descriptions are simple, and you can obtain more verbose descriptions by instead using the method `OpEnvironment.helpVerbose()`:
129
+
## In-depth Op Information
119
130
120
-
```groovy
121
-
import org.scijava.ops.api.OpEnvironment
122
-
ops = OpEnvironment.build()
131
+
The basic descriptions from `ops.help()` are intentionally simplified to avoid providing overwhelming amounts of information. However, you can obtain more complete descriptions, including documentation (if available), from `ops.helpVerbose()`:
123
132
133
+
```
124
134
print(ops.helpVerbose("filter.gauss"))
125
135
```
126
136
127
-
```
137
+
Gives us actual typing and usage notes for the parameters:
0 commit comments