imagej / imagej-ops

ImageJ Ops: "Write once, run anywhere" image processing
https://imagej.net/libs/imagej-ops
BSD 2-Clause "Simplified" License
88 stars 42 forks source link

Order of inputs and outputs in ComputerOps and HybridCFs #606

Closed frauzufall closed 5 years ago

frauzufall commented 5 years ago

I have an issue with the order of input and output parameters of ComputerOps and HybridCFs.

The table in the JavaDoc of SpecialOps says it's void compute(O, I) for a UnaryComputerOp. The source code of UnaryComputerOp says compute(I input, O output), same for the order of the generics in the class definition in AbstractUnaryOp (implements UnaryComputerOp<I, O>).

I tested it and it seems to execute them as output first, input second, see this example:

import net.imagej.ImageJ;
import net.imagej.ops.Op;
import net.imagej.ops.special.computer.AbstractUnaryComputerOp;
import net.imglib2.img.Img;
import org.scijava.plugin.Plugin;

import java.util.Arrays;

@Plugin(type = Op.class)
public class TestComputerOp extends
        AbstractUnaryComputerOp<Img, Img>
        implements Op {

    @Override
    public void compute(Img input, Img output) {
        System.out.println("op input: " + dimensions(input));
        System.out.println("op output: " + dimensions(output));
    }

    private static String dimensions(Img input) {
        long[] dims = new long[input.numDimensions()];
        input.dimensions(dims);
        return Arrays.toString(dims);
    }

    public static void main(String... args) {
        ImageJ ij = new ImageJ();
        Img input = ij.op().create().img(new long[]{1,2});
        Img output = ij.op().create().img(new long[]{10, 20});
        System.out.println("input: " + dimensions(input));
        System.out.println("output: " + dimensions(output));
        ij.op().run(TestComputerOp.class, input, output);
    }
}

This is the output:

input: [1, 2]
output: [10, 20]
op input: [10, 20]
op output: [1, 2]

As far as I can guess without digging more into it the order is determined by the order of parameters in AbstractUnaryComputerOp and AbstractUnaryHybridC (same for the binary ops).

ctrueden commented 5 years ago

Yeah, it's super confusing. ☹️

Firstly, the SpecialOp documentation was wrong. I fixed it with e201033d1e83488a98a0a052af674f00e0c9ac88.

Secondly: the order of parameters to the compute method is separate from the order of op parameters in general. For example, a unary computer op that extends AbstractUnaryComputerOp will end up with parameters declared in the order (out, in, a, b, c, d, ...), where a, b, etc., are the additional parameters defined in the op itself, and out and in are the base parameters defined in AbstractUnaryComputerOp.

The takeaway is: when calling a computer via the run method, you need to write run(out, in, a, b, c, d, ...). Whereas when calling it via the type-safe compute method, you need to write compute(in, out).

The new version of SciJava Ops, due later this year, fixes this confusion, mapping unary functions and computers to run(in, out), binary to run(in1, in2, out), and so forth.