-
Notifications
You must be signed in to change notification settings - Fork 0
Update RTD to support new users #249
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
f33fa5d
bab193d
a36b6c1
02d05a9
a747966
97f7de5
f2f193c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,61 +1,96 @@ | ||
| # Calling Ops with the `OpBuilder` | ||
|
|
||
| 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. | ||
| 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`. | ||
|
|
||
| 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`. | ||
| 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. | ||
|
|
||
| **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. | ||
|
|
||
| ## Specifying the name with `.op()` | ||
|
|
||
| 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: | ||
| 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 | ||
| ops.op("filter.gauss") | ||
| ``` | ||
|
|
||
| ## Passing the inputs with `.input()` | ||
|
|
||
| With the name defined in the `OpBuilder` call, we can then chain the inputs with the `input()` method. | ||
| With the name established in the `OpBuilder` chain, we can then specify our input(s) with the `.input()` method. | ||
|
|
||
| For our gaussian blur, we will pass as inputs our input image `inImage`, and a `double` as our sigma parameter: | ||
| 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 | ||
| ops.op("filter.gauss").input(inImage, 2.0) | ||
| ``` | ||
|
|
||
| ## Passing an output buffer with `.output()` | ||
|
|
||
| Now that the inputs are specified, we can chain the output buffer using the `output()` method. | ||
| After specifying inputs, we provide a preallocated output container using the `.output()` method. | ||
|
|
||
| For our gaussian blur, we will pass as the output buffer our output image `outImage`: | ||
| For our Gaussian blur, we will pass our output image `outImage` as a receptacle for the result: | ||
|
|
||
| ```groovy | ||
| ops.op("filter.gauss").input(inImage, 2.0).output(outImage) | ||
| ``` | ||
|
|
||
| ## Computing with `.compute()` | ||
|
|
||
| With all of the components of the needed Op specified, we can begin computation with the `.compute()` method. | ||
| With all of our desired Op's inputs and output now specified, we can run it with the `.compute()` method. | ||
|
|
||
| ```groovy | ||
| ops.op("filter.gauss").input(inImage, 2.0).output(outImage).compute() | ||
| ``` | ||
|
|
||
| In the call to `compute()`, the `OpEnvironment` will use the components of the `OpBuilder` syntax to: | ||
| * Match an Op based on the name provided, as well as the types of the provided input and output `Object`s | ||
| * Execute the Op on the provided input and output `Object`s. | ||
| In the call to `compute()`, the `OpEnvironment` will use all of the parameters provided to: | ||
| * Match an Op based on the name provided, as well as the types of the provided input and output objects | ||
| * Execute the Op on the provided input and output objects. | ||
|
|
||
| After this step, `outImage` will contain the results of the Gaussian blur on `inImage`. | ||
|
|
||
| ## Variations on use | ||
|
|
||
| ## Additions: Repeating execution | ||
| ### Using a *function* or *inplace* Op | ||
|
|
||
| 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: | ||
| 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*. | ||
|
|
||
| *Functions* are used when we want to *create* the final output, indicated by ending the builder with `.apply()`: | ||
|
|
||
| ```groovy | ||
| 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: | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would it be confusing, or clarifying, to note this is 1-indexed?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
|
||
| ``` | ||
| # 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 | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 😂 |
||
| ops.op("filter.gauss").input(inImage, 2.0).compute() | ||
| ``` | ||
|
|
||
| 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)! | ||
|
|
||
| ### Repeating execution | ||
|
|
||
| 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, outImage) | ||
| 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. | ||
|
|
||
| ## Additions: Matching with classes | ||
| ### Matching with classes | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
|
||
| 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. | ||
|
|
@@ -64,8 +99,15 @@ methods. These accept either a `Class` or a `Nil` - the latter allowing retentio | |
| var computer = ops.op("filter.gauss").inType(ImgPlus.class, Double.class).outType(ImgPlus.class).computer() | ||
| ``` | ||
|
|
||
| When using the `*Type` methods of the builder, the terminal steps will only allow *creation* of the Op, not | ||
| direct execution, since the parameters have not been concretely specified yet. | ||
| In this case, we *must* use the `computer()` terminal method of the builder: we | ||
| can only *create* the Op, not directly execute it, since the parameters have | ||
| not been concretely specified yet. This is very sensible when we want to re-use a computer many times. | ||
|
|
||
| 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(); | ||
| ``` | ||
|
|
||
| ## Common Pitfalls: Wildcards | ||
|
|
||
|
|
@@ -89,7 +131,7 @@ Generally speaking, op requests are **cached**, meaning repeated OpBuilder calls | |
|
|
||
| ### Solution 2: Avoid using wildcards | ||
|
|
||
| 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<?>`. | ||
| 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<?>`. | ||
|
|
||
| ### Solution 3: Use raw casts (not type-safe!) | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,44 +1,52 @@ | ||
| # Scripting in Fiji | ||
|
|
||
| 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. | ||
| 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. | ||
|
|
||
| ## Obtaining an OpEnvironment | ||
|
|
||
| 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: | ||
| 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): | ||
|
|
||
| ```text | ||
| #@ OpEnvironment ops | ||
| ``` | ||
|
|
||
| ## Obtaining inputs | ||
| ## Setting inputs and outputs | ||
|
|
||
| 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. | ||
| 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. | ||
|
|
||
| ```text | ||
| #@ Img imgInput | ||
| #@ Double sigma | ||
| #@output Img out | ||
| ``` | ||
|
|
||
| For more information on SciJava scripting parameters, please visit [this page](https://imagej.net/scripting/parameters). | ||
|
|
||
| ## Calling Ops | ||
|
|
||
| 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: | ||
| 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: | ||
| ```text | ||
| out = ops.op("filter.gauss").input(imgInput, new Double(3.0)).apply() | ||
| out = ops.op("filter.gauss").input(imgInput, sigma).apply() | ||
| ``` | ||
|
|
||
| ## Putting it all together | ||
|
|
||
| The below script can be pasted into the Script Editor. **Ensure that the Script Editor is configured to run a Groovy script**. | ||
| 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). | ||
|
|
||
| ```text | ||
| #@ OpEnvironment ops | ||
| #@ Img imgInput | ||
| #@ Double sigma | ||
| #@output Img out | ||
|
|
||
| // Call some Ops! | ||
| out = ops.op("filter.gauss").input(imgInput, new Double(3.0)).apply() | ||
| // Call our Op! | ||
| out = ops.op("filter.gauss").input(imgInput, sigma).apply() | ||
| ``` | ||
|
|
||
| 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)! | ||
| ## Add your Op to the menu | ||
|
|
||
| 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). | ||
|
|
||
| ## Next steps | ||
|
|
||
| Check out the How-To Guides for important information like how to [explore the available Ops](SearchingForOps). | ||
|
|
||
| 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! |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,23 +1,37 @@ | ||
| # Searching for Ops in the Environment | ||
|
|
||
| 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. | ||
| The first step when working with Ops is always to obtain an `OpEnvironment`: your gateway to all Ops functionality. | ||
|
|
||
| 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**. | ||
| If you're working in a [Fiji script](ScriptingInFiji) then this is done with a script parameter: | ||
|
|
||
| ## Searching for operations | ||
| ``` | ||
| #@ OpEnvironment ops | ||
| ``` | ||
|
|
||
| The no-argument method `OpEnvironment.help()` is designed to give you a broad overview over the *categories* of Ops available within the `OpEnvironment`: | ||
| Otherwise we can import and build one ourselves: | ||
|
|
||
| ```groovy | ||
| ``` | ||
| import org.scijava.ops.api.OpEnvironment | ||
| ops = OpEnvironment.build() | ||
| ``` | ||
|
|
||
| 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`. | ||
|
|
||
| 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. | ||
|
|
||
| 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**. | ||
|
|
||
| ## Listing Namespaces | ||
|
|
||
| The no-argument method `ops.help()` is designed to give you a broad overview over the *categories* (namespaces) of Ops available within the `OpEnvironment`: | ||
|
|
||
| ```python | ||
| print(ops.help()) | ||
| ``` | ||
|
|
||
| This gives the following printout: | ||
| Might print output such as: | ||
|
|
||
| ``` | ||
| ```text | ||
| Namespaces: | ||
| > coloc | ||
| > convert | ||
|
|
@@ -45,19 +59,19 @@ Namespaces: | |
| > types | ||
| ``` | ||
|
|
||
| ## Interrogating a Namespace | ||
| These namespace categories can then be interrogated further to explore the particular Ops in each. | ||
|
|
||
| ## Querying a Namespace | ||
|
|
||
| You can choose one of the above namespaces, and `ops.help()` will give you information about the algorithms contained within: | ||
| ```groovy | ||
| import org.scijava.ops.api.OpEnvironment | ||
| ops = OpEnvironment.build() | ||
|
|
||
| ```python | ||
| print(ops.help("filter")) | ||
| ``` | ||
|
|
||
| This gives the following printout: | ||
| Prints the current list of `filter` ops in the `OpEnvironment`: | ||
|
|
||
| ``` | ||
| ```text | ||
| Names: | ||
| > filter.dog | ||
| > filter.addNoise | ||
|
|
@@ -96,35 +110,33 @@ Names: | |
| > filter.variance | ||
| ``` | ||
|
|
||
| ## Signatures for Op Names | ||
|
|
||
| Finally, you can use `OpEnvironment.help()` on any Op name to see the list of signatures: | ||
| ## Querying Op Signatures | ||
|
|
||
| ```groovy | ||
| import org.scijava.ops.api.OpEnvironment | ||
| ops = OpEnvironment.build() | ||
| Finally, you can use `ops.help()` on any Op name to see the list of signatures: | ||
|
|
||
| ```python | ||
| print(ops.help("filter.gauss")) | ||
| ``` | ||
|
|
||
| ``` | ||
| ```text | ||
| filter.gauss: | ||
| - (input, sigmas, @CONTAINER container1) -> None | ||
| - (input, sigmas, outOfBounds = null, @CONTAINER container1) -> None | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not part of this PR I know, but this output mixes |
||
| - (input, sigma, @CONTAINER container1) -> None | ||
| - (input, sigma, outOfBounds = null, @CONTAINER container1) -> None | ||
| ``` | ||
|
|
||
| Note that these descriptions are simple, and you can obtain more verbose descriptions by instead using the method `OpEnvironment.helpVerbose()`: | ||
| ## In-depth Op Information | ||
|
|
||
| ```groovy | ||
| import org.scijava.ops.api.OpEnvironment | ||
| ops = OpEnvironment.build() | ||
| 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()`: | ||
|
|
||
| ``` | ||
| print(ops.helpVerbose("filter.gauss")) | ||
| ``` | ||
|
|
||
| ``` | ||
| Gives us actual typing and usage notes for the parameters: | ||
|
|
||
| ```text | ||
| filter.gauss: | ||
| - 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>) | ||
| > input : net.imglib2.RandomAccessibleInterval<I> | ||
|
|
@@ -147,3 +159,5 @@ filter.gauss: | |
| > container1 : @CONTAINER net.imglib2.RandomAccessibleInterval<O> | ||
| the preallocated output image | ||
| ``` | ||
|
|
||
| The `ops.helpVerbose()` method can be used interchangeably whenever you would use `ops.help()`, as needed. | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you think about using the technical term?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the simpler
our sigma parameter😉