Skip to content

Commit 3b3f3b2

Browse files
authored
Merge pull request #249 from scijava/quick-start
Update RTD to support new users
2 parents 018d03e + f2f193c commit 3b3f3b2

File tree

4 files changed

+122
-58
lines changed

4 files changed

+122
-58
lines changed

docs/ops/doc/CallingOps.md

Lines changed: 60 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,61 +1,96 @@
11
# Calling Ops with the `OpBuilder`
22

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`.
44

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.
68

79
## Specifying the name with `.op()`
810

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:
1012

1113
```groovy
1214
ops.op("filter.gauss")
1315
```
1416

1517
## Passing the inputs with `.input()`
1618

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.
1820

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:
2022

2123
```groovy
2224
ops.op("filter.gauss").input(inImage, 2.0)
2325
```
2426

2527
## Passing an output buffer with `.output()`
2628

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.
2830

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:
3032

3133
```groovy
3234
ops.op("filter.gauss").input(inImage, 2.0).output(outImage)
3335
```
3436

3537
## Computing with `.compute()`
3638

37-
With all of the components of the needed Op specified, we can begin computation with the `.compute()` method.
39+
With all of our desired Op's inputs and output now specified, we can run it with the `.compute()` method.
3840

3941
```groovy
4042
ops.op("filter.gauss").input(inImage, 2.0).output(outImage).compute()
4143
```
4244

43-
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
4652

47-
## Additions: Repeating execution
53+
### Using a *function* or *inplace* Op
4854

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:
64+
65+
```
66+
# Modify the first input in-place
67+
ops.op("filter.gauss").input(inImage, 2.0).mutate1()
68+
```
69+
70+
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()`:
71+
72+
```
73+
# Does not compute
74+
ops.op("filter.gauss").input(inImage, 2.0).compute()
75+
```
76+
77+
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):
5082

5183
```groovy
5284
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)
5487
```
5588

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+
5691
*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.
5792

58-
## Additions: Matching with classes
93+
### Matching with classes
5994

6095
In addition to the `.input()` and `.output()` builder steps, there are parallel `.inType()` and `.outType()`
6196
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
6499
var computer = ops.op("filter.gauss").inType(ImgPlus.class, Double.class).outType(ImgPlus.class).computer()
65100
```
66101

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:
107+
108+
```java
109+
Img outImage = ops.op("filter.gauss").input(inImage, 2.0).outType(Img.class).apply();
110+
```
69111

70112
## Common Pitfalls: Wildcards
71113

@@ -89,7 +131,7 @@ Generally speaking, op requests are **cached**, meaning repeated OpBuilder calls
89131

90132
### Solution 2: Avoid using wildcards
91133

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<?>`.
93135

94136
### Solution 3: Use raw casts (not type-safe!)
95137

docs/ops/doc/ScriptingInFiji.md

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,52 @@
11
# Scripting in Fiji
22

3-
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.
44

55
## Obtaining an OpEnvironment
66

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):
88

99
```text
1010
#@ OpEnvironment ops
1111
```
1212

13-
## Obtaining inputs
13+
## Setting inputs and outputs
1414

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.
1616

1717
```text
1818
#@ Img imgInput
19+
#@ Double sigma
1920
#@output Img out
2021
```
2122

22-
For more information on SciJava scripting parameters, please visit [this page](https://imagej.net/scripting/parameters).
23-
2423
## Calling Ops
2524

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:
2726
```text
28-
out = ops.op("filter.gauss").input(imgInput, new Double(3.0)).apply()
27+
out = ops.op("filter.gauss").input(imgInput, sigma).apply()
2928
```
3029

3130
## Putting it all together
3231

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 &rarr; Groovy* in the Script Editor menu).
3433

3534
```text
3635
#@ OpEnvironment ops
3736
#@ Img imgInput
37+
#@ Double sigma
3838
#@output Img out
3939
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()
4242
```
4343

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&mdash;just don't forget the `_`!) then after re-starting Fiji we can run our Op from the *Plugins &rarr; 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!

docs/ops/doc/SearchingForOps.md

Lines changed: 39 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,37 @@
11
# Searching for Ops in the Environment
22

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 Ops is always to obtain an `OpEnvironment`: your gateway to all Ops functionality.
44

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:
66

7-
## Searching for operations
7+
```
8+
#@ OpEnvironment ops
9+
```
810

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:
1012

11-
```groovy
13+
```
1214
import org.scijava.ops.api.OpEnvironment
1315
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.
1421

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
1529
print(ops.help())
1630
```
1731

18-
This gives the following printout:
32+
Might print output such as:
1933

20-
```
34+
```text
2135
Namespaces:
2236
> coloc
2337
> convert
@@ -45,19 +59,19 @@ Namespaces:
4559
> types
4660
```
4761

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
4965

5066
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()
5467

68+
```python
5569
print(ops.help("filter"))
5670
```
5771

58-
This gives the following printout:
72+
Prints the current list of `filter` ops in the `OpEnvironment`:
5973

60-
```
74+
```text
6175
Names:
6276
> filter.dog
6377
> filter.addNoise
@@ -96,35 +110,33 @@ Names:
96110
> filter.variance
97111
```
98112

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
102114

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:
106116

117+
```python
107118
print(ops.help("filter.gauss"))
108119
```
109120

110-
```
121+
```text
111122
filter.gauss:
112123
- (input, sigmas, @CONTAINER container1) -> None
113124
- (input, sigmas, outOfBounds = null, @CONTAINER container1) -> None
114125
- (input, sigma, @CONTAINER container1) -> None
115126
- (input, sigma, outOfBounds = null, @CONTAINER container1) -> None
116127
```
117128

118-
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
119130

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()`:
123132

133+
```
124134
print(ops.helpVerbose("filter.gauss"))
125135
```
126136

127-
```
137+
Gives us actual typing and usage notes for the parameters:
138+
139+
```text
128140
filter.gauss:
129141
- org.scijava.ops.image.filter.gauss.Gaussians.defaultGaussRAI(net.imglib2.RandomAccessibleInterval<I>,double[],net.imglib2.outofbounds.OutOfBoundsFactory<I, net.imglib2.RandomAccessibleInterval<I>>,net.imglib2.RandomAccessibleInterval<O>)
130142
> input : net.imglib2.RandomAccessibleInterval<I>
@@ -147,3 +159,5 @@ filter.gauss:
147159
> container1 : @CONTAINER net.imglib2.RandomAccessibleInterval<O>
148160
the preallocated output image
149161
```
162+
163+
The `ops.helpVerbose()` method can be used interchangeably whenever you would use `ops.help()`, as needed.

docs/ops/doc/index.rst

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,15 @@ The combination of these libraries allows declarative image analysis workflows,
1515

1616
WhyOps
1717
Installation
18+
ScriptingInFiji
1819

1920
.. toctree::
2021
:maxdepth: 2
2122
:caption: 🪄 How-to guides
2223

23-
CallingOps
2424
SearchingForOps
25+
CallingOps
2526
IntrospectingOps
26-
Benchmarks
27-
ScriptingInFiji
2827

2928
.. toctree::
3029
:maxdepth: 2
@@ -43,6 +42,7 @@ The combination of these libraries allows declarative image analysis workflows,
4342

4443
WritingYourOwnOpPackage
4544
Migrating Ops Written for ImageJ Ops<MigratingFromImageJOps>
45+
Benchmarks
4646

4747
.. toctree::
4848
:maxdepth: 2

0 commit comments

Comments
 (0)