Skip to content

Commit 472ba4a

Browse files
committed
Convert double[][] -> Img & other fun stuff
1 parent 52b6201 commit 472ba4a

5 files changed

Lines changed: 196 additions & 27 deletions

File tree

docs/ops/doc/Concepts.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
This page is designed to help *Ops users* understand the SciJava Ops concepts powering the framework.
44

55
## Ops
6-
An **algorithm** is a mathematical routine that transforms, interrogate, or refines input values into output values. Algorithms are used throughout scientific computing, and the fundamental [purpose](Purpose) of SciJava Ops is to facilitate their application.
6+
An **algorithm** is a mathematical routine that transforms, interrogate, or refines input values into output values. Algorithms are used throughout scientific computing, and the fundamental [purpose](Purpose.rst) of SciJava Ops is to facilitate their application.
77

88
We do that by providing a framework for consistently defining and invoking algorithm implementations as **Ops**. Ops have:
99
* a **name**, establishing its purpose. An Op's name describes *what* the Op does, but not *how* the Op does it.
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
========================================
2+
Parameter Conversion for Developers
3+
========================================
4+
5+
In this example, we show how Ops developers can implement parameter conversion to enable seamless interoperability between Ops utilizing different data structures.between
6+
7+
Basics
8+
======
9+
10+
A key :ref:`value <driving-values>` of SciJava Ops is flexibility, and flexibility is (in part) achieved through **parameter conversion**. At its core, parameter conversion allows *translation* of data stored in one data structure (e.g. an ImgLib2 ``RandomAccessibleInterval``) into a different data structure (e.g. an OpenCV ``Mat``) **on the fly**. This allows SciJava Ops to execute Ops backed by OpenCV code **on ImgLib2 data structures**.
11+
12+
.. figure:: https://media.scijava.org/scijava-ops/1.0.1/parameter-conversion-opencv.svg
13+
14+
At matching time, parameter conversion is invoked when an Op matches a user request in name and in Op type, but differing in individual parameter types. In these situations, it looks for ``engine.convert`` Ops that could potentially convert the user's provided inputs into the required Op inputs, and the same, in the other direction, for the output.
15+
16+
A toy example
17+
=============
18+
19+
Suppose we have an Op that inherently operates on ``RandomAccessibleInterval<DoubleType>``\ s:
20+
21+
.. code-block:: java
22+
23+
/**
24+
* Conolves an image with a kernel, returning the output in a new object
25+
*
26+
* @param input the input data
27+
* @param kernel the kernel
28+
* @return the convolution of {@code input} and {@code kernel}
29+
* @implNote op names="filter.convolve"
30+
*/
31+
public static RandomAccessibleInterval<DoubleType> convolveNaive(
32+
final RandomAccessibleInterval<DoubleType> input,
33+
final RandomAccessibleInterval<DoubleType> kernel
34+
) {
35+
// convolve convolve convolve //
36+
}
37+
38+
This Op might work well, however if users have a small kernel that is *only* used for this Op, they may find it frustrating to represent that data as a ``RandomAccessibleInterval``. Fortunately, SciJava Ops allows us to write ``engine.convert`` Ops, so users can pass their data in a data structure more convenient for them.
39+
40+
41+
.. code-block:: java
42+
43+
Img<DoubleType> in = ...
44+
// 3x3 averaging kernel
45+
double m = 1/9;
46+
double[] data = new double[] { //
47+
m, m, m, //
48+
m, m, m, //
49+
m, m, m //
50+
};
51+
// Not ideal
52+
Img<DoubleType> kernel = ArrayImgs.doubles(data, new long[] {3, 3});
53+
54+
var result = ops.op("filter.convolve").input(in, kernel).apply();
55+
56+
In this case, users might find it nicer to specify their kernel as a ``double[][]``, which is much easier for users to construct
57+
58+
.. code-block:: java
59+
60+
Img<DoubleType> in = ...
61+
// 3x3 averaging kernel
62+
double m = 1/9;
63+
double[] kernel = new double[][] { //
64+
new double[] { m, m, m}, //
65+
new double[] { m, m, m}, //
66+
new double[] { m, m, m} //
67+
}
68+
69+
// Ideal case - no need to wrap to Img
70+
var result = ops.op("filter.convolve").input(in, kernel).apply();
71+
72+
The only step for us as the developer is to tell SciJava Ops that it can convert ``double[][]``\ s to ``RandomAccessibleInterval<DoubleType>``\ s, which we do with ``engine.convert`` Ops.
73+
74+
A simple ``engine.convert`` Op
75+
==============================
76+
77+
All ``engine.convert`` Ops are simple ``Function``\ s, that take as input the user argument to the Op, and return a *translation* of that data into the type expected by Ops. In our case, we want to convert *from* the user's ``double[][]`` into a ``RandomAccessibleInterval<DoubleType>``:
78+
79+
.. code-block:: java
80+
81+
/**
82+
* @param input the input data
83+
* @return an output image whose values are equivalent to {@code input}s
84+
* values but whose element types are {@link BitType}s.
85+
* @implNote op names='engine.convert', type=Function
86+
*/
87+
public static RandomAccessibleInterval<DoubleType> arrayToDoubles(final double[][] input)
88+
{
89+
// Creates an empty image of doubles
90+
var img = ArrayImgs.doubles(input.length, input[0].length);
91+
var ra = img.randomAccess();
92+
// Deep copies the double[][] into the RAI
93+
for(int i = 0; i < input.length; i++) {
94+
for(int j = 0; j < input[0].length; j++) {
95+
ra.setPositionAndGet(i, j).set(input[i][j]);
96+
}
97+
}
98+
return img;
99+
}
100+
101+
This Op, discovered through the SciJava Ops Indexer, is **all** that is needed to make the execution pattern we want functional.
102+
103+
104+
Adding efficiency
105+
=================
106+
107+
While the above ``engine.convert`` Op is *functional*, it may not be *fast* as the data size increases. This is due to the **copy** inherent in its execution, as the ``ArrayImg`` contains new data structures.
108+
109+
In such cases, devising methods to *wrap* the user arguments, instead of *copying* it, will maximize performance and wow your users. In our case, we can refine our ``engine.convert`` Op to wrap user data, using the ``DoubleAccess`` interface of ImgLib2:
110+
111+
.. code-block:: java
112+
113+
/**
114+
* @param input the input data
115+
* @return an output image whose values are equivalent to {@code input}s
116+
* values but whose element types are {@link BitType}s.
117+
* @implNote op names='engine.convert', type=Function
118+
*/
119+
public static RandomAccessibleInterval<DoubleType> arrayToDoubles(final double[][] input)
120+
{
121+
// Wrap 2D array into DoubleAccess usable by ArrayImg
122+
var access = new DoubleAccess() {
123+
124+
private final int rowSize = input[0].length;
125+
126+
@Override
127+
public double getValue(int index) {
128+
var row = index / rowSize;
129+
var col = index % rowSize;
130+
return input[row][col];
131+
}
132+
133+
@Override
134+
public void setValue(int index, double value) {
135+
var row = index / rowSize;
136+
var col = index % rowSize;
137+
input[row][col] = value;
138+
}
139+
};
140+
return ArrayImgs.doubles(access, input.length, input[0].length);
141+
}
Lines changed: 51 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,68 @@
1-
# Purpose
1+
=======
2+
Purpose
3+
=======
24

35
The fundamental goal of SciJava Ops is to fit the "best" algorithm possible to each task. Historically, identifying and applying the "best" algorithm has been difficult for a variety of reasons:
6+
47
* **Technology iterates quickly:** the "best" algorithm five years ago may be replaced by "better" algorithms within new libraries, in different programming languages, incompatible with established workflows.
58
* **Hardware dependence limits reuse:** as analysis increasingly migrates to GPU-based calculations, operating environments proliferate and algorithms become less portable.
6-
* **Algorithm libraries are fragmented:** actually *finding* the "best" algorithm is no simple task, as diverse implementations may exist across any number of programming languages and documentation styles.
9+
* **Algorithm libraries are fragmented:** actually *finding* the "best" algorithm is no simple task, as diverse implementations may exist across any number of programming languages and documentation styles.
710

811
SciJava Ops takes strides to ease these burdens by separating the *what* ("I want to perform a gaussian blur on this image with this sigma value) from the *how* (using scikit-image on zarr arrays). By creating these abstractions, we move towards a single unified, standardized mechanism for applying algorithms. In such an environment, portable workflows can be created quickly and new technologies can be integrated seamlessly.
912

10-
## Driving Values
13+
.. _driving-values:
14+
15+
Driving Values
16+
==============
17+
18+
#. **Consistency**: All Ops are called in the same way, regardless of the mechanisms used by the underlying framework. This means that you don't have to learn Python to call Ops written in Python, but it also means that you could pass the output from an Op written in Python to an Op written in Java, all with the same syntax!
19+
#. **Reusability**: Ops extends Java's mantra of "write once, run anywhere" to image processing algorithms. Algorithms written for the SciJava Ops framework are usable as-is from any SciJava-compatible software project including Fiji, or from Python using PyImageJ.
20+
#. **Flexibility**: Through adaptation and conversion pathways, Ops can be applied to all kinds of inputs, relaxing considerations for data structures. For example, binary numerical Ops are automatically looped and parallelized to operate on images. New data types extending core interfaces can be supported immediately, without rewriting existing algorithms.
21+
#. **Safety**: An op may consist of any number of strongly typed inputs, and calls to access those ops can be as specific as desired. This allows analyst users to use ops without regard for data structure, while developers can rely on the type safety guarantees needed for optimization.
22+
#. **Extensibility**: Ops provides a mechanism for incorporating existing algorithm implementations into the framework code-free. Existing ops can always be extended in new directions or specialized for particular inputs.
23+
#. **Performance**: The Ops framework provides a means to override any general-but-slow op with a faster-but-more-specific alternative and the execution framework adds minimal overhead.
24+
1125

12-
1. **Consistency**: All Ops are called in the same way, regardless of the mechanisms used by the underlying framework. This means that you don't have to learn Python to call Ops written in Python, but it also means that you could pass the output from an Op written in Python to an Op written in Java, all with the same syntax!
13-
2. **Reusability**: Ops extends Java's mantra of "write once, run anywhere" to image processing algorithms. Algorithms written for the SciJava Ops framework are usable as-is from any SciJava-compatible software project including Fiji, or from Python using PyImageJ.
14-
4. **Flexibility**: Through adaptation and conversion pathways, Ops can be applied to all kinds of inputs, relaxing considerations for data structures. For example, binary numerical Ops are automatically looped and parallelized to operate on images. New data types extending core interfaces can be supported immediately, without rewriting existing algorithms.
15-
5. **Safety**: An op may consist of any number of strongly typed inputs, and calls to access those ops can be as specific as desired. This allows analyst users to use ops without regard for data structure, while developers can rely on the type safety guarantees needed for optimization.
16-
6. **Extensibility**: Ops provides a mechanism for incorporating existing algorithm implementations into the framework code-free. Existing ops can always be extended in new directions or specialized for particular inputs.
17-
7. **Performance**: The Ops framework provides a means to override any general-but-slow op with a faster-but-more-specific alternative and the execution framework adds minimal overhead.
26+
How does SciJava Ops compare with ImageJ Ops?
27+
=============================================
1828

29+
As an evolution of the `ImageJ Ops`_ project, SciJava Ops adds many new features and makes many improvements. We first highlight how users can benefit by adopting SciJava Ops; we then provide benefits for developers who choose to write Ops to be exposed within the framework.
1930

20-
## How does SciJava Ops compare with ImageJ Ops?
31+
User Features
32+
-------------
2133

22-
As an evolution of the [ImageJ Ops](https://imagej.net/libs/imagej-ops/) project, SciJava Ops adds many new features and makes many improvements. We first highlight how users can benefit by adopting SciJava Ops; we then provide benefits for developers who choose to write Ops to be exposed within the framework.
34+
#. |Faster Matching|_: SciJava Ops is able to match Ops to user requests dramatically faster than ImageJ Ops, and caches matching requests to provide virtually no overhead on re-requests.
35+
#. **Precise Matching**: SciJava Ops uses the :doc:`Op builder pattern <CallingOps>` to obtain precise knowledge about the user's desired Op inputs and outputs, leading to precise Op matches.
2336

24-
### User Features
37+
* *ImageJ Ops's* ``OpService.run`` *mechanism is vulnerable to uncertain return values and frequent result casting*.
38+
#. **Simple API**: In SciJava Ops, each Op is **either** a ``Function``, a ``Computer``, or an ``Inplace``, containing a **single method** that executes all functionality.
2539

26-
1. [**Faster Matching**](Benchmarks.rst): SciJava Ops is able to match Ops to user requests dramatically faster than ImageJ Ops, and caches matching requests to provide virtually no overhead on re-requests.
27-
2. **Precise Matching**: SciJava Ops uses the [Op builder pattern](CallingOps.md) to obtain precise knowledge about the user's desired Op inputs and outputs, leading to precise Op matches.
28-
* *ImageJ Ops' `OpService.run` mechanism is vulnerable to uncertain return values and frequent result casting*.
29-
3. **Simple API**: In SciJava Ops, each Op is **either** a `Function`, a `Computer`, or an `Inplace`, containing a **single method** that executes all functionality.
3040
* *In ImageJ Ops, an Op might implement many different interfaces and expose redundant API*.
31-
4. **Automatic Progress Updates**: SciJava Ops automatically records Op executions within the SciJava Progress framework, providing a simple mechanism for monitoring script execution.
41+
#. **Automatic Progress Updates**: SciJava Ops automatically records Op executions within the SciJava Progress framework, providing a simple mechanism for monitoring script execution.
42+
3243
* *In ImageJ Ops, Op execution is silent unless explicitly defined, and very few Ops do so.*
3344

34-
### Developer Features
35-
1. **Zero-code Op Declaration**: Executable code can be registered as an Op purely through YAML specifications, allowing the induction of entire external libraries without upstream edits.
45+
Developer Features
46+
------------------
47+
#. **Zero-code Op Declaration**: Executable code can be registered as an Op purely through YAML specifications, allowing the induction of entire external libraries without upstream edits.
48+
3649
* *In ImageJ Ops, a separate class is required to wrap each algorithm from an external source*.
37-
2. **Minimal Op Boilerplate**: Developers can choose to write Ops as Java classes, methods, or fields, depending on Op requirements.
50+
#. **Minimal Op Boilerplate**: Developers can choose to write Ops as Java classes, methods, or fields, depending on Op requirements.
51+
3852
* *In ImageJ Ops, a distinct class, including annotations and interface inheritance, is necessary for each Op*.
39-
3. **Op Adaptation**: SciJava Ops can automatically iterate pixelwise algorithms across entire images, and can automatically create output buffers if the user does not provide one, **without any additional code**.
53+
#. **Op Adaptation**: SciJava Ops can automatically iterate pixelwise algorithms across entire images, and can automatically create output buffers if the user does not provide one, **without any additional code**.
54+
4055
* *In ImageJ Ops, developers were required to program each additional way to call an Op to provide the same flexibility*.
41-
4. **Full support for Java Generics**: Generic typing information baked into Java code enables SciJava Ops to consider each parameter's **generic** type in determining a match. This functionality allows developers to write separate Ops to specialize in increasingly narrow parameter types.
56+
#. **Full support for Java Generics**: Generic typing information baked into Java code enables SciJava Ops to consider each parameter's **generic** type in determining a match. This functionality allows developers to write separate Ops to specialize in increasingly narrow parameter types.
57+
4258
* *In ImageJ Ops, developers were required to encapsulate functionality within delegation Ops*.
4359

4460

45-
## How do we integrate all of these different libraries and data structures?
61+
How do we integrate all of these different libraries and data structures?
62+
=========================================================================
4663

4764
Core to SciJava Ops is the observation that algorithmic data structures generally fall into a one of a set of "forms", including:
65+
4866
* scalars
4967
* sets
5068
* tensors
@@ -56,16 +74,25 @@ To enable the goals described above, SciJava Ops provides an interface for seaml
5674
To enable a uniform interface for algorithm execution, we extend the notion of "form"s to algorithms as well, as human intuition is good at determining whether an algorithm is a "gaussian blur", a "convolution", a "segmentation", and so on.
5775

5876
Within each algorithm "form", there are several different sub-algorithms - for example, for a gaussian blur, you might see algorithms requiring
77+
5978
* an image and a single sigma
6079
* an image and a sigma per dimension
6180
* an image and a shape over which to compute the gaussian blur
6281
* ...
6382

6483
It is SciJava Ops' goal to collect all algorithms into such descriptions, and to provide a uniform execution pathway for each - hiding the complexities of each individual algorithm library.
6584

66-
## Isn't SciJava Ops just another algorithm library?
85+
Isn't SciJava Ops just another algorithm library?
86+
-------------------------------------------------
6787

6888
While it may currently seem that way, SciJava Ops is designed to *wrap* existing algorithms, not to create new ones (although SciJava Ops makes that easy too!)
6989

7090
To facilitate the inclusion of existing algorithms, SciJava Ops exposes a rich API to integrate Ops from arbitrary sources, spanning libraries and programming languages.
7191

92+
93+
94+
.. _`ImageJ Ops`: https://imagej.net/libs/imagej-ops/
95+
96+
.. HACK: bold text link - see https://stackoverflow.com/a/63394243
97+
.. _Faster Matching: Benchmarks.html
98+
.. |Faster Matching| replace:: **Faster Matching**

docs/ops/doc/examples/opencv_denoise.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
Fast Non-Local Means Denoise with OpenCV
33
========================================
44

5-
In this example we will use a denoise algorithm from the external `OpenCV libray`_.
5+
In this example we will use a denoise algorithm from the external `OpenCV library`_.
66

77
This method progressively scans through an image's pixels, comparing a patch centered around
88
a pixel of interest (*e.g.* a 5x5 patch) with patches from other pixels from the image. These
@@ -120,5 +120,5 @@ SciJava Ops via Fiji's scripting engine with `script parameters`_:
120120
result = output
121121

122122
.. _`script parameters`: https://imagej.net/scripting/parameters
123-
.. _`OpenCV libray`: https://docs.opencv.org/4.x/d5/d69/tutorial_py_non_local_means.html
123+
.. _`OpenCV library`: https://docs.opencv.org/4.x/d5/d69/tutorial_py_non_local_means.html
124124
.. _`here`: https://media.scijava.org/scijava-ops/1.0.0/opencv_denoise_16bit.tif

docs/ops/doc/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ The combination of these libraries allows declarative image analysis workflows,
4242
:caption: 🛠️ Development
4343

4444
WritingYourOwnOpPackage
45+
ParameterConversion
4546
Migrating Ops Written for ImageJ Ops<MigratingFromImageJOps>
4647
Benchmarks
4748

0 commit comments

Comments
 (0)