Skip to content
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

napari-imagej can't determine type hint for ArrayImg #56

Closed
hinerm opened this issue May 27, 2022 · 14 comments
Closed

napari-imagej can't determine type hint for ArrayImg #56

hinerm opened this issue May 27, 2022 · 14 comments
Assignees
Milestone

Comments

@hinerm
Copy link
Member

hinerm commented May 27, 2022

Running the math.add(arg, value) op results in this exception when generating the widget panel:
image

See #48 (comment)

@hinerm hinerm added this to the 0.2.0 milestone May 27, 2022
@gselzer
Copy link
Collaborator

gselzer commented Jun 2, 2022

Man, that is just a really picky op 😕

It's an Inplace with a mutable ArrayImg. PyImageJ couldn't even handle that with a python (e.g. numpy) input image!

Right now, PyImageJ will convert the python input to an ImgView, which is not an ArrayImg.

I'd think we'd be able to convert a numpy ndarray into an ArrayImg of some sort, but that is no trivial matter. And it would have nothing to do with napari-imagej.

I think we instead would have to "hide" these Ops, but that is also no easy task.

I'm not sure what the best path forward is. What do you think, @hinerm?

@hinerm
Copy link
Member Author

hinerm commented Jun 3, 2022

I'm not sure what the best path forward is

I agree that this isn't a problem for napari-imagej. I believe Fiji has the same problem when trying to run ops that require an input ArrayImg. So maybe this issue could be moved to ops.

I don't think you need to explicitly hide these Ops in napari-imagej since the problem is consistent across environments.

@gselzer
Copy link
Collaborator

gselzer commented Jun 3, 2022

I agree that this isn't a problem for napari-imagej. I believe Fiji has the same problem when trying to run ops that require an input ArrayImg. So maybe this issue could be moved to ops.

I agree that an issue should be filed in Ops-land, but what issue? There isn't really anything wrong with this Op; it is typed on ArrayImg because it makes use of ArrayImg functionality. The whole point of Ops was that you could write algorithms like this that would only be matched if your inputs were specific enough.

One change we could make would be to write more Ops with the same signature but a broader input object, at a lower priority. This would allow you to get the functionality across the board. But it would not fix the problem we are describing here, which is that we expect to run this Op but cannot because its input type is too specific. Making this change would not avoid the issue, it would just introduce more Ops and probably more confusion. (As an aside, it sucks to keep filing issues and adding ops in imagej-ops when imagej-ops2 is so close...)

The more impactful change might be what is described in imagej/imagej-ops#643, because adding that typing would give more clarity and could tell us what can be run.

Here's an idea: what if we wrote a Searcher implementation that could wrap another Searcher with additional predicates? This could allow us to only return SearchResults that e.g. belong to a set of types we know how to resolve.

Here's a draft:

public class SearcherWithPredicates implements Searcher {
    public SearcherWithPredicates(Searcher s, List<? extends Predicate<SearchResult>> predicates) {
        this.s = s;
        this.uberPredicate = predicates.get(0);
        for( i = 1; i < predicates.size(); i++)
            this.uberPredicate = this.uberPredicate.and(predicates.get(i));
    }

    @Override
    public List<SearchResult> search(final String text, final boolean fuzzy) {
        return s.search(text).stream()
          .filter(uberPredicate::test) // Only allow results that satisfy ALL predicates
          .collect(Collections.toList());
    }
}

@ctrueden
Copy link
Member

ctrueden commented Jun 3, 2022

The core issue here is that ops are not meant to be run directly like this, but rather matched against compatible inputs.

I do not think we should be exposing individual op signatures. Rather, when you execute a particular op, inputs should be fed to the Ops matcher as per the design, so the right one can be selected by the framework.

Of course, for this to work, we do need to scan all the ops, and aggregate them into supported more general signatures of some kind. Otherwise, there is no way to know what (what types, how many) the user wants to feed to the matcher. It's a sort of chicken-and-egg problem.

The simplest way I can think of to aggregate the ops is to collapse ops with same "simple" signatures into one entry. E.g. two theoretical ops math.add(Img, RealType) and math.add(RandomAccessibleInterval, double) are both math.add(image, number) and so just showing that one entry in the list, and harvesting such values, would let the matcher do its job most of the time. We would unfortunately need to handle the case where the user ends up giving inputs that cannot in fact be matched against any particular existing op, though. There is also a discussion needed about what exactly these prototypical "simple" op types should be. I believe @gselzer and I already solved this with SciJava Ops with the simplification layer, but we don't have that for Ops v1.

@ctrueden
Copy link
Member

ctrueden commented Jun 3, 2022

A more complex way to collapse the ops would be:

  • Make a list of supported "native" types provided by a particular execution context. In this case, this "execution context" is "napari" so the list of supported "native" types is "distinct Python types compatible with magicgui".
  • For each op, loop its inputs, and ask the Python-side conversion framework: can a napari-native type map to this particular op input? If so, which napari-native type?
  • Same for output types, but in the other direction.
  • For ops where the answer is "no" for any of its parameter types, exclude it from the napari-imagej UI.
  • For ops where the answer is yes for all parameter types, produce a "napari-native" type signature for that op, adding it to a set of such signatures to collapse down duplicate ones, since sometimes two or more ops will end up with the same napari-native signature.
  • When searching for ops, search the set of napari-native signatures, and display those to the user.

@gselzer
Copy link
Collaborator

gselzer commented Jun 3, 2022

A more complex way to collapse the ops would be:

  • Make a list of supported "native" types provided by a particular execution context. In this case, this "execution context" is "napari" so the list of supported "native" types is "distinct Python types compatible with magicgui".
  • For each op, loop its inputs, and ask the Python-side conversion framework: can a napari-native type map to this particular op input? If so, which napari-native type?
  • Same for output types, but in the other direction.
  • For ops where the answer is "no" for any of its parameter types, exclude it from the napari-imagej UI.

Yup, this is exactly what I was getting at, and would be easily doable with that SearcherWithPredicates class I described above.

  • For ops where the answer is yes for all parameter types, produce a "napari-native" type signature for that op, adding it to a set of such signatures to collapse down duplicate ones, since sometimes two or more ops will end up with the same napari-native signature.
  • When searching for ops, search the set of napari-native signatures, and display those to the user.

I think this is easier said than done. We will have to figure out how to do that reduction. Which op is the one that gets chosen? You can't use priority.

@ctrueden
Copy link
Member

ctrueden commented Jun 3, 2022

Which op is the one that gets chosen? You can't use priority.

Yeah, my point is: you aren't choosing specific op implementations. When you present a "napari-native" op signature to the user, they then populate a corresponding magicgui widget, which is then fed to the ops matcher, not to any specific implementation. So issues of priority don't matter anymore (from the napari-imagej perspective).

@gselzer
Copy link
Collaborator

gselzer commented Jun 3, 2022

Yeah, that might work out. Will be a bit of work, though.

Let's implement this on the Java side, though, in an extensible way? We also would want this behavior in Fiji...

@gselzer gselzer self-assigned this Jun 3, 2022
@ctrueden
Copy link
Member

ctrueden commented Jun 3, 2022

Let's implement this on the Java side, though, in an extensible way? We also would want this behavior in Fiji...

I agree that we want this behavior in Fiji, too! However, it's a bit tricky to generalize the "context-native" idea, especially across the Java/Python border. I guess on the Java side, we'd have all the napari-based Python types becoming some canonical Java type via to_java, right? So then, we can limit our code to Java only by building the "napari-native" types from those Java-ified types. Probably even fine to hardcode them on the napari side.

So then we'd have an mechanism in Java that accepts a map from Java type to simple names, something like:

napari_native_types = {
    'java.lang.Integer': 'int',
    'java.lang.Double': 'float',
    'java.lang.String', 'str',
    'java.util.List': 'list', # what about generics...?
    'net.imglib2.python.ReferenceGuardingRandomAccessibleInterval`: 'image',
    'net.imagej.Dataset': 'image',
    ...
}

And then for each op signature, for each input and output type, checks if that type instanceof each key of the map, and if so, simplifies the type name accordingly for the simplified signature. These simplified signatures get thrown into a set to squash duplicates.

There would be no need to remember, for each simplified signature, which op(s) generated it, although if we want to be clever we could keep a list of op implementations for which it applies, to display to the user in the search results for things like looking up source code. But this is extra credit.

The simplified signatures need to be sufficiently well-typed to generate the magicgui widget. Then, once the user has populated the widget, you can call ops.run with those inputs.

@hinerm
Copy link
Member Author

hinerm commented Jun 3, 2022

FWIW I did work isolating ops that operate on images and consolidating parameters in the op finder.

@gselzer
Copy link
Collaborator

gselzer commented Jun 3, 2022

FWIW I did work isolating ops that operate on images and consolidating parameters in the op finder.

Thanks @hinerm, I'll probably try to use as much of that as I can. It would be cool if we could migrate some of that to imagej-ops.

@gselzer
Copy link
Collaborator

gselzer commented Sep 29, 2022

Can we close this issue now, with the OpListing work, @hinerm?

EDIT: Nope, we can't 😦

The op signature still shows up post-OpListings:

image

You get a clearer exception, which I pasted below. But I still dislike exposing this issue to users.

I feel like I've mentioned this before, but I'm suprised that OpCandidate 28 doesn't match... maybe PyImageJ 2.0.0 would solve this @ctrueden?

Exception: Caught Java Exception

 java.lang.IllegalArgumentException: No matching 'math.add' op

Request:
-	math.add(
		DefaultDataset,
		Integer)

Candidates:
1. 	(ArrayImg arg) =
	net.imagej.ops.math.ConstantToArrayImageP$AddByte(
==>		ArrayImg arg,
		byte value)
	Inconvertible type: net.imagej.DefaultDataset => net.imglib2.img.array.ArrayImg<net.imglib2.type.numeric.integer.ByteType, net.imglib2.img.basictypeaccess.array.ByteArray>
2. 	(ArrayImg arg) =
	net.imagej.ops.math.ConstantToArrayImageP$AddDouble(
==>		ArrayImg arg,
		double value)
	Inconvertible type: net.imagej.DefaultDataset => net.imglib2.img.array.ArrayImg<net.imglib2.type.numeric.real.DoubleType, net.imglib2.img.basictypeaccess.array.DoubleArray>
3. 	(ArrayImg arg) =
	net.imagej.ops.math.ConstantToArrayImageP$AddFloat(
==>		ArrayImg arg,
		float value)
	Inconvertible type: net.imagej.DefaultDataset => net.imglib2.img.array.ArrayImg<net.imglib2.type.numeric.real.FloatType, net.imglib2.img.basictypeaccess.array.FloatArray>
4. 	(ArrayImg arg) =
	net.imagej.ops.math.ConstantToArrayImageP$AddInt(
==>		ArrayImg arg,
		int value)
	Inconvertible type: net.imagej.DefaultDataset => net.imglib2.img.array.ArrayImg<net.imglib2.type.numeric.integer.IntType, net.imglib2.img.basictypeaccess.array.IntArray>
5. 	(ArrayImg arg) =
	net.imagej.ops.math.ConstantToArrayImageP$AddLong(
==>		ArrayImg arg,
		long value)
	Inconvertible type: net.imagej.DefaultDataset => net.imglib2.img.array.ArrayImg<net.imglib2.type.numeric.integer.LongType, net.imglib2.img.basictypeaccess.array.LongArray>
6. 	(ArrayImg arg) =
	net.imagej.ops.math.ConstantToArrayImageP$AddShort(
==>		ArrayImg arg,
		short value)
	Inconvertible type: net.imagej.DefaultDataset => net.imglib2.img.array.ArrayImg<net.imglib2.type.numeric.integer.ShortType, net.imglib2.img.basictypeaccess.array.ShortArray>
7. 	(ArrayImg arg) =
	net.imagej.ops.math.ConstantToArrayImageP$AddUnsignedByte(
==>		ArrayImg arg,
		byte value)
	Inconvertible type: net.imagej.DefaultDataset => net.imglib2.img.array.ArrayImg<net.imglib2.type.numeric.integer.UnsignedByteType, net.imglib2.img.basictypeaccess.array.ByteArray>
8. 	(ArrayImg arg) =
	net.imagej.ops.math.ConstantToArrayImageP$AddUnsignedInt(
==>		ArrayImg arg,
		int value)
	Inconvertible type: net.imagej.DefaultDataset => net.imglib2.img.array.ArrayImg<net.imglib2.type.numeric.integer.UnsignedIntType, net.imglib2.img.basictypeaccess.array.IntArray>
9. 	(ArrayImg arg) =
	net.imagej.ops.math.ConstantToArrayImageP$AddUnsignedLong(
==>		ArrayImg arg,
		long value)
	Inconvertible type: net.imagej.DefaultDataset => net.imglib2.img.array.ArrayImg<net.imglib2.type.numeric.integer.UnsignedLongType, net.imglib2.img.basictypeaccess.array.LongArray>
10. 	(ArrayImg arg) =
	net.imagej.ops.math.ConstantToArrayImageP$AddUnsignedShort(
==>		ArrayImg arg,
		short value)
	Inconvertible type: net.imagej.DefaultDataset => net.imglib2.img.array.ArrayImg<net.imglib2.type.numeric.integer.UnsignedShortType, net.imglib2.img.basictypeaccess.array.ShortArray>
11. 	(ArrayImg arg) =
	net.imagej.ops.math.ConstantToArrayImage$AddByte(
==>		ArrayImg arg,
		byte value)
	Inconvertible type: net.imagej.DefaultDataset => net.imglib2.img.array.ArrayImg<net.imglib2.type.numeric.integer.ByteType, net.imglib2.img.basictypeaccess.array.ByteArray>
12. 	(ArrayImg arg) =
	net.imagej.ops.math.ConstantToArrayImage$AddDouble(
==>		ArrayImg arg,
		double value)
	Inconvertible type: net.imagej.DefaultDataset => net.imglib2.img.array.ArrayImg<net.imglib2.type.numeric.real.DoubleType, net.imglib2.img.basictypeaccess.array.DoubleArray>
13. 	(ArrayImg arg) =
	net.imagej.ops.math.ConstantToArrayImage$AddFloat(
==>		ArrayImg arg,
		float value)
	Inconvertible type: net.imagej.DefaultDataset => net.imglib2.img.array.ArrayImg<net.imglib2.type.numeric.real.FloatType, net.imglib2.img.basictypeaccess.array.FloatArray>
14. 	(ArrayImg arg) =
	net.imagej.ops.math.ConstantToArrayImage$AddInt(
==>		ArrayImg arg,
		int value)
	Inconvertible type: net.imagej.DefaultDataset => net.imglib2.img.array.ArrayImg<net.imglib2.type.numeric.integer.IntType, net.imglib2.img.basictypeaccess.array.IntArray>
15. 	(ArrayImg arg) =
	net.imagej.ops.math.ConstantToArrayImage$AddLong(
==>		ArrayImg arg,
		long value)
	Inconvertible type: net.imagej.DefaultDataset => net.imglib2.img.array.ArrayImg<net.imglib2.type.numeric.integer.LongType, net.imglib2.img.basictypeaccess.array.LongArray>
16. 	(ArrayImg arg) =
	net.imagej.ops.math.ConstantToArrayImage$AddShort(
==>		ArrayImg arg,
		short value)
	Inconvertible type: net.imagej.DefaultDataset => net.imglib2.img.array.ArrayImg<net.imglib2.type.numeric.integer.ShortType, net.imglib2.img.basictypeaccess.array.ShortArray>
17. 	(ArrayImg arg) =
	net.imagej.ops.math.ConstantToArrayImage$AddUnsignedByte(
==>		ArrayImg arg,
		byte value)
	Inconvertible type: net.imagej.DefaultDataset => net.imglib2.img.array.ArrayImg<net.imglib2.type.numeric.integer.UnsignedByteType, net.imglib2.img.basictypeaccess.array.ByteArray>
18. 	(ArrayImg arg) =
	net.imagej.ops.math.ConstantToArrayImage$AddUnsignedInt(
==>		ArrayImg arg,
		int value)
	Inconvertible type: net.imagej.DefaultDataset => net.imglib2.img.array.ArrayImg<net.imglib2.type.numeric.integer.UnsignedIntType, net.imglib2.img.basictypeaccess.array.IntArray>
19. 	(ArrayImg arg) =
	net.imagej.ops.math.ConstantToArrayImage$AddUnsignedLong(
==>		ArrayImg arg,
		long value)
	Inconvertible type: net.imagej.DefaultDataset => net.imglib2.img.array.ArrayImg<net.imglib2.type.numeric.integer.UnsignedLongType, net.imglib2.img.basictypeaccess.array.LongArray>
20. 	(ArrayImg arg) =
	net.imagej.ops.math.ConstantToArrayImage$AddUnsignedShort(
==>		ArrayImg arg,
		short value)
	Inconvertible type: net.imagej.DefaultDataset => net.imglib2.img.array.ArrayImg<net.imglib2.type.numeric.integer.UnsignedShortType, net.imglib2.img.basictypeaccess.array.ShortArray>
21. 	(IterableInterval out?) =
	net.imagej.ops.math.IIToIIOutputII$Add(
		IterableInterval out?,
		IterableInterval in1,
==>		IterableInterval in2)
	Inconvertible type: java.lang.Integer => net.imglib2.IterableInterval<capture of ? extends net.imglib2.type.numeric.NumericType<capture of ?>>
22. 	(NumericType out?) =
	net.imagej.ops.math.NumericTypeBinaryMath$Add(
		NumericType out?,
==>		NumericType in1,
		NumericType in2)
	Inconvertible type: net.imagej.DefaultDataset => capture of ?
23. 	(int result) =
	net.imagej.ops.math.PrimitiveMath$IntegerAdd(
==>		int a,
		int b)
	Inconvertible type: net.imagej.DefaultDataset => int
24. 	(long result) =
	net.imagej.ops.math.PrimitiveMath$LongAdd(
==>		long a,
		long b)
	Inconvertible type: net.imagej.DefaultDataset => long
25. 	(float result) =
	net.imagej.ops.math.PrimitiveMath$FloatAdd(
==>		float a,
		float b)
	Inconvertible type: net.imagej.DefaultDataset => float
26. 	(double result) =
	net.imagej.ops.math.PrimitiveMath$DoubleAdd(
==>		double a,
		double b)
	Inconvertible type: net.imagej.DefaultDataset => double
27. 	(RealType out) =
	net.imagej.ops.math.BinaryRealTypeMath$Add(
		RealType out,
		RealType in1,
		RealType in2)
	Not enough arguments: 2 < 3
28. 	(IterableInterval out?) =
	net.imagej.ops.math.ConstantToIIOutputII$Add(
		IterableInterval out?,
		IterableInterval in,
==>		NumericType value)
	Inconvertible type: java.lang.Integer => capture of ?
29. 	(PlanarImg arg) =
	net.imagej.ops.math.ConstantToPlanarImage$AddByte(
==>		PlanarImg arg,
		byte value)
	Inconvertible type: net.imagej.DefaultDataset => net.imglib2.img.planar.PlanarImg<net.imglib2.type.numeric.integer.ByteType, net.imglib2.img.basictypeaccess.array.ByteArray>
30. 	(PlanarImg arg) =
	net.imagej.ops.math.ConstantToPlanarImage$AddDouble(
==>		PlanarImg arg,
		double value)
	Inconvertible type: net.imagej.DefaultDataset => net.imglib2.img.planar.PlanarImg<net.imglib2.type.numeric.real.DoubleType, net.imglib2.img.basictypeaccess.array.DoubleArray>
31. 	(PlanarImg arg) =
	net.imagej.ops.math.ConstantToPlanarImage$AddFloat(
==>		PlanarImg arg,
		float value)
	Inconvertible type: net.imagej.DefaultDataset => net.imglib2.img.planar.PlanarImg<net.imglib2.type.numeric.real.FloatType, net.imglib2.img.basictypeaccess.array.FloatArray>
32. 	(PlanarImg arg) =
	net.imagej.ops.math.ConstantToPlanarImage$AddInt(
==>		PlanarImg arg,
		int value)
	Inconvertible type: net.imagej.DefaultDataset => net.imglib2.img.planar.PlanarImg<net.imglib2.type.numeric.integer.IntType, net.imglib2.img.basictypeaccess.array.IntArray>
33. 	(PlanarImg arg) =
	net.imagej.ops.math.ConstantToPlanarImage$AddLong(
==>		PlanarImg arg,
		long value)
	Inconvertible type: net.imagej.DefaultDataset => net.imglib2.img.planar.PlanarImg<net.imglib2.type.numeric.integer.LongType, net.imglib2.img.basictypeaccess.array.LongArray>
34. 	(PlanarImg arg) =
	net.imagej.ops.math.ConstantToPlanarImage$AddShort(
==>		PlanarImg arg,
		short value)
	Inconvertible type: net.imagej.DefaultDataset => net.imglib2.img.planar.PlanarImg<net.imglib2.type.numeric.integer.ShortType, net.imglib2.img.basictypeaccess.array.ShortArray>
35. 	(PlanarImg arg) =
	net.imagej.ops.math.ConstantToPlanarImage$AddUnsignedByte(
==>		PlanarImg arg,
		byte value)
	Inconvertible type: net.imagej.DefaultDataset => net.imglib2.img.planar.PlanarImg<net.imglib2.type.numeric.integer.UnsignedByteType, net.imglib2.img.basictypeaccess.array.ByteArray>
36. 	(PlanarImg arg) =
	net.imagej.ops.math.ConstantToPlanarImage$AddUnsignedInt(
==>		PlanarImg arg,
		int value)
	Inconvertible type: net.imagej.DefaultDataset => net.imglib2.img.planar.PlanarImg<net.imglib2.type.numeric.integer.UnsignedIntType, net.imglib2.img.basictypeaccess.array.IntArray>
37. 	(PlanarImg arg) =
	net.imagej.ops.math.ConstantToPlanarImage$AddUnsignedLong(
==>		PlanarImg arg,
		long value)
	Inconvertible type: net.imagej.DefaultDataset => net.imglib2.img.planar.PlanarImg<net.imglib2.type.numeric.integer.UnsignedLongType, net.imglib2.img.basictypeaccess.array.LongArray>
38. 	(PlanarImg arg) =
	net.imagej.ops.math.ConstantToPlanarImage$AddUnsignedShort(
==>		PlanarImg arg,
		short value)
	Inconvertible type: net.imagej.DefaultDataset => net.imglib2.img.planar.PlanarImg<net.imglib2.type.numeric.integer.UnsignedShortType, net.imglib2.img.basictypeaccess.array.ShortArray>
39. 	(IterableInterval out?) =
	net.imagej.ops.math.IIToRAIOutputII$Add(
		IterableInterval out?,
		IterableInterval in1,
==>		RandomAccessibleInterval in2)
	Inconvertible type: java.lang.Integer => net.imglib2.RandomAccessibleInterval<capture of ? extends net.imglib2.type.numeric.NumericType<capture of ?>>
40. 	(RandomAccessibleInterval out) =
	net.imagej.ops.math.ConstantToIIOutputRAI$Add(
		RandomAccessibleInterval out,
		IterableInterval in,
		NumericType value)
	Not enough arguments: 2 < 3

	at net.imagej.ops.DefaultOpMatchingService.singleMatch(DefaultOpMatchingService.java:432)
	at net.imagej.ops.DefaultOpMatchingService.findMatch(DefaultOpMatchingService.java:97)
	at net.imagej.ops.DefaultOpMatchingService.findMatch(DefaultOpMatchingService.java:83)
	at net.imagej.ops.OpEnvironment.module(OpEnvironment.java:253)
	at net.imagej.ops.OpEnvironment.run(OpEnvironment.java:136)
	at net.imagej.ops.OpListingModule.run(OpListingModule.java:68)
``

@gselzer
Copy link
Collaborator

gselzer commented Dec 15, 2022

While the OpListing issue is fixed, this issue now has the same issue as #51. It really sucks that Ops doesn't expose the generics...

@ctrueden what's the best way forward here?

@gselzer
Copy link
Collaborator

gselzer commented May 31, 2023

Closing this as duplicate of imagej/imagej-ops#648. You can reproduce in Fiji using the following script, yielding the following error:

#@ Img input
#@ OpService ops

import net.imglib2.type.numeric.integer.IntType

ops.math().add(input, new IntType(10))

Error:

java.lang.ClassCastException: net.imglib2.type.numeric.integer.IntType cannot be cast to net.imglib2.type.numeric.integer.UnsignedByteType
	at net.imglib2.type.numeric.integer.UnsignedByteType.add(UnsignedByteType.java:50)
	at net.imagej.ops.math.ConstantToIIOutputII$Add.compute(ConstantToIIOutputII.java:85)
	at net.imagej.ops.math.ConstantToIIOutputII$Add.compute(ConstantToIIOutputII.java:58)
	at net.imagej.ops.special.hybrid.UnaryHybridCF.calculate(UnaryHybridCF.java:61)
	at net.imagej.ops.special.hybrid.UnaryHybridCF.run(UnaryHybridCF.java:71)
	at net.imagej.ops.special.hybrid.UnaryHybridCFI.run(UnaryHybridCFI.java:66)
	at net.imagej.ops.special.hybrid.UnaryHybridCFI.run(UnaryHybridCFI.java:80)
	at org.scijava.command.CommandModule.run(CommandModule.java:196)
	at net.imagej.ops.OpEnvironment.run(OpEnvironment.java:960)
	at net.imagej.ops.OpEnvironment.run(OpEnvironment.java:157)
	at net.imagej.ops.math.MathNamespace.add(MathNamespace.java:257)
	at net.imagej.ops.math.MathNamespace$add.call(Unknown Source)
	at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:47)
	at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:125)
	at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:148)
	at New_.run(New_.groovy:6)
	at org.codehaus.groovy.jsr223.GroovyScriptEngineImpl.eval(GroovyScriptEngineImpl.java:317)
	at org.codehaus.groovy.jsr223.GroovyScriptEngineImpl.eval(GroovyScriptEngineImpl.java:155)
	at org.scijava.plugins.scripting.groovy.GroovyScriptLanguage$1.eval(GroovyScriptLanguage.java:97)
	at javax.script.AbstractScriptEngine.eval(AbstractScriptEngine.java:264)
	at org.scijava.script.ScriptModule.run(ScriptModule.java:164)
	at org.scijava.module.ModuleRunner.run(ModuleRunner.java:163)
	at org.scijava.module.ModuleRunner.call(ModuleRunner.java:124)
	at org.scijava.module.ModuleRunner.call(ModuleRunner.java:63)
	at org.scijava.thread.DefaultThreadService.lambda$wrap$2(DefaultThreadService.java:225)
	at java.util.concurrent.FutureTask.run(FutureTask.java:266)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at java.lang.Thread.run(Thread.java:750)

@gselzer gselzer closed this as not planned Won't fix, can't repro, duplicate, stale May 31, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants