Custom equation op

Tags: #<Tag:0x00007fd547b6c618>


Hi all

I am trying to apply a simple threshold to an image, with one wrinkle. I want the values over the threshold to remain the same, and values under to be 1. In matlab I would simply do the following…


In ops/imglib2 I can do this with a cursor and simple iteration, or a small custom op, and a map, but is there currently any way more concise? What do you think is the most concise way to do the above in ops??

The above use case, got me thinking about java 8 lambdas again. Last year I had written some ops to pass a java 8 lambda to the equation op (calibratedequation)

One of the road blocks was the fact that, at the time, we had not figured out the best way to represent calibrated equations.

I am thinking of re-doing that pull request, eliminate the calibration part, and just create a way to use lambdas in equations.

There could by x,y equations

ops.image().equation(image, (x, y) -> Math.pow(x, 2) + Math.pow(y, 2))

(For higher dimensionality, I don’t think you can use lambdas, as far as I know there is know there is no ternary java 8 operator.)

For intensity based equations… you could have something like…

ops.image().apply(image, i-> i<t?i:0);

Any thoughts?? Anything like this exist or underway already?? Where would be the best place to put the ‘intensity’ based equation,. to differentiate it from a ‘coordinates’ based equation??

Keeping image values above a certain threshold with OPS


ops.threshold().apply( image, new DoubleType( 0 ) )

do the trick (possibly on the inverted img)?

In FunImageJ I just use

(img/map-img #(cursor/set-val %1 (if (< (cursor/get-val %2) 1) true false)) mask input)

which is pretty much the same as doing the imglib2 cursor iteration you mention.

Isn’t this something that the ops map should also be responsible for? What about when you want coordinates and intensity? Are there enough cases that one only needs to access a single pixel’s intensity, or will other arguments keep getting requested such that a full-blown map is what is really needed?


If we define ‘maps’ as being intensity based, I guess my use case would just require a new map, that takes a unary lambda, i-> i<t?1:i);

“Equations” (as implemented so far with strings) are coordinate based and it would be nice to expand it to have

ops.image().equation(im, (x,y)-> x+y);

Now what if we want to process multiple pixels in our intensity formula?? In that case, I guess we need a filter, or some kind of Map working on an RAI. If we want a formula with ND coordinates, then the lambda won’t work anymore and we probably need an equation that takes in an array of coordinates. If we want some combination of intensity and coordinates, then we might need some new kind of hybrid equation/map.

Right now, I’m thinking through if it makes sense to use lambdas to handle some simple cases (unary intensity/binary x,y).

If it does, I think we can add some simple ops that take lambdas as input, and then handle the more complicated general cases later.


Wouldn’t a lambda over a cursor be more general purpose? Then it can be ND and intensity based, although it becomes more verbose.


What would lambda over a cursor code look like?? I guess, since I already know how to do this, my goal is not to figure out how implement any new functionality, but to to implement current functionality in a more concise and readable way.

If I just had access to a cursor, I could implement an arbitrary formula but (atleast for me) it would take a few lines…

double[] coords=new double[curs.numDimensions()];
double intensity=curs.get().getRealDouble();
// a great new formula that doesn't do anything really
double newValue = coords[0]*7+coords[1]/intensity; 

I guess you could make an op that took a ‘coordinates and intensity’ lambda.

ops.image().equation( im, (i, c)->c[0]*7+c[1]/i); 

and you could have just a ‘c’ one

ops.image().equation(im, c->c[0]+c[1]);

which would accomplish the same thing as the ```x,y``` one, but have N-dimensionality, though do we really even want to do n-dimensions with a lambda??  You might want to do checks for how many dimensions exist, and that may be better suited to making a little Op.


I’m really just playing devil’s advocate here. The more convenience the better. However, it just isn’t clear to me where the syntactic sugar would stop. Eventually one might as well just write a domain-specific language (which has been discussed on multiple occasions at this point).

What would a lambda over a cursor look like? It’d be the java version of:

(img/map-img #(cursor/set-val %1 (if (< (cursor/get-val %2) 1) true false)) mask input)

[Note: #() is a lambda with %1 and %2 as 2 arguments in Clojure], which is already what you do regularly with your imglib2 stuff as you’ve mentioned.

We could then do this all day long:

(defn map-intensity 
  [my-lambda img]
  (map-img (fn [cur] (cursor/set-val cur (my-lambda (.get cur)))) img)); (or (.get (.get cur)) if you need the primitive

(defn map-coords 
  [my-lambda img]
  (map-img (fn [cur] (let [pos (long-array (.numDimensions img))] 
                       (.localize cur pos)
                       (cursor/set-val cur (my-lambda pos))) 

Maybe there aren’t turtles all the way down, and all that is needed are intensity and coord map ops. Also, maybe the KNIME magic of putting some distributed processing underneath ops justifies implementing more of these mapping ops.

Anyway, sorry for the rant, Brian. Every now and then I just get the feeling like languages are getting reinvented inside ops. [Addendum: I do <3 ops though, use it regularly, think it is the right thing to do, and <3 the devs as well]


I guess @dietzc’s and @ctrueden’s work on a recent “hackathon” in Madison goes in that direction. One of the ideas is to make e.g. UnaryFunctionOps implement java.util.function.Function s.t. we can use more of Java’s built-in syntactic sugar for e.g. iterating.

Maybe one of them can chime in on this? @dietzc?


I think you can skip localizing into a new double[] every time, and instead do a one-liner like:

ops.image().equation( im, c -> c.getLongPosition(0)*7+c.getLongPosition(1)/c.get().getRealDouble());

And we could also support lambdas with RandomAccess in case you want to seek around to other pixels when the desired intensity value is not pixelwise independent. (Though I guess in that case, you’d probably pass two images: the input image, and output target, to avoid corrupting the image in place.)

It would be nice if ImgLib2 were less verbose with its method names here. (E.g. if getLongPosition were just pos.) But not much we can do about that now, except to also have the other lambda styles you mention. All of that is doable as new equation ops. Should be really easy to implement. Sounds like a really fun mini-project which I would love to do myself, but I have absolutely no time to work on it this month. @bnorthan If you try it and have any difficulties, please write back here and we can sort it out—I could spend a day in early August hacking on this if needed.

You can use lambdas with more than two arguments:

public class TriLambda {

	public interface TernaryFunction {
		String apply(String a, String b, String c);

	public static void main(final String... args) throws Exception {
		process((a, b, c) -> a + b + c);

	private static void process(TernaryFunction f) {
		final String[][] args = {
			{"happy ", "birthday", "!"},
			{"good ", "night ", "moon"}
		for (final String[] arg : args) {
			System.out.println(f.apply(arg[0], arg[1], arg[2]));


Thanks @ctrueden, I am hoping to have time to work a bit on this the next few weeks. I will keep you updated.

@kephale Thanks for the feedback, it is definitely not my intention to invent a new language inside ops. My motivation for adding to the equation namespace, is just to be able to write equations in new ways. There is already an equation that takes strings, making a new version that takes a lambda is a useful addition. How many versions should we make?? That’s a fair question. The motivation for the ‘x,y’ version, came from converting matlab code that uses meshgrid, ie I was thinking of things in terms of an x,y framework.