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

Adding HowTos #77

Merged
merged 1 commit into from Nov 12, 2019
Merged

Adding HowTos #77

merged 1 commit into from Nov 12, 2019

Conversation

frauzufall
Copy link
Member

@frauzufall frauzufall commented Nov 10, 2019

  • the idea is to have one class per question (how to do X?)
  • each solution is represented as a static method
  • the solutions can directly be executed via main method

I made these because we are exploring rendering searchable ImageJ & Fiji Java HowTos on a webpage, collected from multiple sources, and I would enjoy discussing this in more detail and showing how far we got soon. But as a first step starting with a collection of HowTos here could already be helpful for the community. I am also curious if this motivates people to contribute questions & answers.

Happy for any kind of feedback / ideas for improvements of the format & the content!

* the idea is to have one class per question (how to do X?)
* each solution is represented as a static method
* the solutions can directly be executed via main method
@imagejan
Copy link
Member

Thanks for this initiative, @frauzufall!

Maybe we should also add something like this - a collection of minimal snippets - to the (Groovy) scripting documentation, complementing the .ipynb notebooks we currently have. While I've used in particular the Ops notebooks in the past to look up examples of certain ops, I sometimes find it hard to link to a (simple) example illustrating just a certain aspect/task. On the other hand, we also have the script templates (including some Java ones, btw) available in the script editor, and brought in by various components.

On a related note, and before this repository gets too bloated: what do others think about splitting imagej/tutorials into several components:

  • tutorials-java for the Maven projects and the HowTos from this PR
  • tutorials-scripting (or tutorials-groovy, tutorials-jython etc.) for BeakerX notebook examples as well as minimal script snippets
  • tutorials-python ot tutorials-pyimagej for Python3-kernel notebooks.

What do you think? Would it create too much of a maintenance burden? Would it on the other hand encourage contributions?

@haesleinhuepf
Copy link
Member

Hey @frauzufall,

thaaaanks for working on this! 😍 I think this should be merged right away.

However, as writing documentation and examples should not be the terminal point, I put some comments in the examples where I personally think the API needs to be improved. This is to start a discussion and has nothing to do with the quality of your examples. I love them!

Cheers,
Robert

// do something with ImageJ

// dispose ImageJ
ij.context().dispose();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't it make sense to have an ij.dispose(); which takes care of context and other stuff?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

private static void run() {

List<POM> poms = POM.getAllPOMs();
Optional<POM> imageJOpsPOM = poms.stream().filter(pom -> pom.getArtifactId().equalsIgnoreCase("imagej-ops")).findFirst();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a very long line which is hard to read. Is it possible to split it in several lines?

Copy link
Member

@ctrueden ctrueden Nov 11, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Java programmers—especially those keeping up with the newer functional capabilities—will like this construction. One easy way to make it slightly shorter would be to use equals instead of equalsIgnoreCase, since the case matters for Maven artifacts. And we could also bake in the get() of the Optional if we are confident the POM is present:

POM opsPOM = poms.stream().filter(pom -> pom.getArtifactId().equals("imagej-ops")).findFirst().get();

One way I like to write stream expressions in a nicer way is to break each stream operation to its own line. Something like this:

POM opsPOM = pom.stream()
  .filter(pom -> pom.getArtifactId().equals("imagej-ops"))
  .findFirst().get();

Finally, if we are OK with Google Guava, we can be shorter:

POM opsPOM = Iterables.find(pom -> pom.getArtifactId().equals("imagej-ops"));

I am not aware of a more concise way to write expressions like foo -> foo.getProperty().equals(constantExpression).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Java beginners - especially those who just started learning the ImageJ2-API because they are image analysts and no computer scientists - might be happy about a simple-to-use API with functions called like Pom pom = poms.search(String keyword).

Copy link
Member

@ctrueden ctrueden Nov 11, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Java beginners - especially those who just started learning the ImageJ2-API because they are image analysts and no computer scientists

Why would non-CS image analysts be using Java, when they could be using Groovy or Jython instead? In Groovy you can write:

POM.getAllPOMs().find{pom -> pom.getArtifactId().equals("imagej-ops")}

Or if you want a completely unstructured search of the XML:

pom = POM.getAllPOMs().find{pom -> pom.toString().contains("imagej-ops")}

Let's not target to teach people both Java and ImageJ at the same time.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's not target to teach people both Java and ImageJ at the same time.

Indeed! Let's keep examples simple 😉

Copy link
Member

@ctrueden ctrueden Nov 11, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why should we switch away from Java/Maven?

Because Java is harder to understand for newbies than Groovy and Python.

It appears to me the best platform for coding and deployment.

Not for new programmers. New programmers nearly always hate explicit typing, and especially static typing, which Java requires.

Compare Groovy:

def printPositions(image) {
  c = image.localizingCursor()
  while (c.hasNext()) {
    v = c.next()
    xPos = c.getLongPosition(0)
    yPos = c.getLongPosition(1)
    if (xPos == 0) println()
    print("\t(" + xPos + ", " + yPos + ") = " + v.getRealDouble())
  }
}

With Java:

public class PositionPrinter {
  public static <T extends RealType<T>> void printPositions(IterableInterval<T> image) {
    Cursor<T> c = image.localizingCursor();
    while (c.hasNext()) {
      T v = c.next();
      long xPos = c.getLongPosition(0);
      long yPos = c.getLongPosition(1);
      if (xPos == 0) System.out.println();
      System.out.print("\t(" + xPos + ", " + yPos + ") = " + v.getRealDouble());
    }
  }

Groovy allows us to largely ignore the types, and especially troublesome typing questions with complex answers like "what is an IterableInterval and "what is T" and "whoa! T extends RealType<T>? Are you serious?"

I say this coming from a position of love and respect for Java. It is a wonderful language and I personally 💖 static typing. But it seems patently obvious to me at this point that most programmers do not agree, especially hacker programmers. It's a big reason Python has pretty much won the "language wars" in scientific computing, at least for the next decade or two, despite the fact that (IMHO—and I admit this is hotly debatable) the Python runtime and surrounding tooling is an inferior platform to the JVM technically.

I think code like poms.stream().filter(pom -> pom.getArtifactId().equalsIgnoreCase("imagej-ops")).findFirst() should be hidden behind an API.

From the perspective of giving Java code to Java developers: I disagree. That is idiomatic Java, and every Java programmer should be (or become) familiar with it.

I do see your point that if we are teaching Java programming of ImageJ to people familiar with neither Java nor ImageJ, then it is potentially overwhelming. But again I ask: then why are we doing that? Why not teach ImageJ to new programmers using Groovy or Jython? And leave the Java out of it?

@frauzufall Correct me if I am wrong, but my understanding with these Java howtos is that you want to teach ImageJ to Java programmers. Not teach people to program Java and program ImageJ at the same time. No?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

terrifying-generics

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New programmers nearly always hate explicit typing, and especially static typing, which Java requires.

New Java programmers start crying because of happiness, whenever I show them CRTL+P and CTRL+B in IntelliJ (show parameters of a method and jump to source) because they don't have to search for code documentation and examples anymore. Script editor and notebooks are light years away from that.

Auto-completion is the other game-changer for them. Most of my complains are related to the not-discoverability of functionality in ImageJ2. If I want to split channels in ImageJ1, I type
image
or
image

How can one know how to do that in ImageJ2? How do you know you need to start typing Views?
image
What is the command inside Views for splitting channels? Where can people look this up?

How can one use auto-completion in Groovy? I know how to do it in Jython. But is there some auto-completion editor for groovy?

Again, I'm attending this discussion to suggest building up an easy-to-use API with proper documentation. I'm happy support efforts, but I have the feeling we don't have common sense about where the issues with the current API are.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@haesleinhuepf Views is also accessible via ops, so splitting channels would be a loop over ops.transform().hyperSliceView(img, dim, pos)? At least that's how I do it. I'll do a howto about that. One could write an op that simplifies this.

@ctrueden yes, I was thinking of teaching IJ2, not Java.

Copy link
Member

@ctrueden ctrueden Nov 12, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New Java programmers start crying because of happiness, whenever I show them CRTL+P and CTRL+B in IntelliJ (show parameters of a method and jump to source) because they don't have to search for code documentation and examples anymore. Script editor and notebooks are light years away from that.

@haesleinhuepf I wholeheartedly agree that the tools we gain from type safety are fantastic! This is why I personally love Java. But there are so many people in the scientific community who disagree, and claim that Java is all too complicated with a too-high learning curve. Maybe a compromise language in future could be Kotlin. Or maybe things will get a little easier once we switch to Java 11 and have access to the var keyword.

Regarding discoverability: I agree with @frauzufall that ImageJ2—especially the ImageJ gateway and ImageJ Ops—is intended to be a central discovery point for all this useful stuff. If you are going to criticize the ImgLib2 design of having these static utility classes that are not discoverable for new people, then Views is only the tip of the iceberg: we've got Intervals, IntervalIndexer, Regions, Masks, GeomMasks, RealViews, Converters, and more. I see ImgLib2 as the low-level supporting library, and ImageJ2 as the higher-level more user-friendly API here. We need to wrap as much of the goodness as possible into ImageJ ops and services.

* .. wrapping {@link Img} as {@link ImgPlus}:
*/
private static void imgToImgPlus() {
Img img = IO.openImgs("https://samples.fiji.sc/blobs.png").get(0);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be possible to add an IO.openImg(filename) which only opens one file? I'm wondering in what situation the command IO.openImgs(blobs.gif) actually can return several images....

Furthermore, what is the recommended strategy, ij.io().open() or 'IO.openImgs'? We should not confuse users.

Copy link
Member

@ctrueden ctrueden Nov 11, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Certainly it is possible for bioimage formats to contain multiple nD images. See e.g. Leica LIF. That said, it is extremely common for a data source to contain only one image.

Note that openImgs became simply open in SCIFIO 0.38.x (released but not yet propagated to pom-scijava). So the current API call is:

IO.open("/path/to/image").get(0)

We could change this to IO.openAll and then make IO.open return only the first image. If you think it's worth it, we should do it now, before IO.open is introduced into the wild!

what is the recommended strategy, ij.io().open() or 'IO.openImgs'?

Use IOService.open(...)—or if you require type-safe return type, DatasetIOService.open. If you do not have access to services—which would be because you are writing a program operating without an existing SciJava context—then you can call IO.open instead, which spins up its own context internally and then disposes it as soon as it feasibly can. But there will be overhead with doing that.

Personally, I'd rather not encourage people to use IO.open, because they'll start doing it even when they have a SciJava Context available.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm fine with removing the static examples from the howtos, since the idea is to help write an application with a SciJava context, right?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the idea is to help write an application with a SciJava context, right?

Mostly, I think so, yeah.

But there are always some people who want to do as much as possible without explicitly creating a context. So it is good to document the IO class for them. But I want to make it clear in the comment that this class is only for when you don't have and don't want a context.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But there are always some people who want to do as much as possible without explicitly creating a context.

🙋‍♂ 😉

ImageJ ij = new ImageJ();
Img<DoubleType> img = ij.op().create().img(new long[] { 20, 30, 40 });
ImgPlus<DoubleType> imgPlus = ij.op().create().imgPlus(img);
System.out.println(imgPlus);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Classic ImageJ users might wonder how to create "BLACK", "WHITE" and "RAMP" images. Is there something available for them in ImageJ2?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably that functionality should migrate into Ops at some point.

// draws circle of radius 30 at position [70, 98]
HyperSphere<DoubleType> hyperSphere = new HyperSphere<>(img, ra, 30);
for (DoubleType value : hyperSphere)
value.set(25);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a method like drawCircle(x, y, r) or drawSphere(x, y, z, rx, ry, rz) somewhere in ImageJ2? If not, where would be the right place to put it?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to encourage people to bake ROIs onto image data, overwriting raw values? What is the use case? I do think it is great to show how to iterate inside the bounds of a ROI. But maybe we could do something else like do some calculation from the samples inside instead?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this be shortened to:

new HyperSphere<>(img, ra, 30).forEach(v -> v.set(25));

?

Also: shouldn't we use the imglib2-roi API here instead of this thing from imglib2-algorithm?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would strongly advice API developers in making everything possible to users for a simple reason: If the user wants to draw a circle,
they
will find
a way
to draw
a circle.

Useful examples could be:

Img<DoubleType> img = ij.op().create().img(new long[] { 256, 254 });

// draws rectangle at position [50. 60] with a size of 20x20 px
Views.interval(img, Intervals.createMinSize( 50, 60, 20, 20 )).forEach(pixel -> pixel.set(25));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a method like drawRectangle(x, y, w, h) or drawCuboid(x, y, z, w, h, d) somewhere in ImageJ2? If not, where would be the right place to put it?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we can get imglib/imglib2-roi#50 merged, this will become possible:

Ellipsoid roi = GeomMasks.openEllipsoid(center, semiAxisLengths);
Regions.sample(roi, myImage).forEach(v -> v.set(25));

Along with that, we should improve the GeomMasks utility class to have more signatures including 2D and 3D as well as varargs/ellipses rather than arrays/brackets in cases where it is convenient and unambiguous.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wow! This looks amazing! Will change my comment above and remove the question about this :-) Great! Looking forward to give this a try ❤️

Img segmentedImg = (Img) ij.op().threshold().otsu(blurredImg);

//calculate connected components
ImgLabeling cca = ij.op().labeling().cca(segmentedImg, ConnectedComponents.StructuringElement.FOUR_CONNECTED);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be possible to rename cca to connectedComponentsAnalysis or particleAnalysis? Of course, we should deprecate cca.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can already use connectedComponents and connectedComponentAnalysis as aliases of this op. See Ops.list.

I don't think we should deprecate cca, it's so short and concise.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Names like cca are an imponderable for beginners. We should deprecate it and ban it from example code. 😉

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see your point. cca can also be confused with canonical correlation analysis.

For reference:

  • in Python CV2: cv2.connectedComponents(image)
  • in Python skimage: measure.label(image)
  • in MATLAB: bwlabel or bwconncomp

😕

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should not bother too much with what others did. We can just learn and do better ;-)

Actually, I would vote for connectedComponents as the analysis part (image -> numbers) doesn't really happen here...

Furthermore, I don't see the operations you mentioned. Are they part of the distribution already? @imagejan
image

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would you agree it makes sense to deprecate cca and introduce connectedComponents? I'm happy to file the PR.

Please don't, for the moment. The new iteration of Ops is nearly complete, and changes like that to the current ImageJ Ops may not make it into the new system. It is likely that the type-safe namespaces will no longer exist in the next version of Ops, in favor of smarter autocompletion for popular IDEs + the ImageJ Script Editor. I do agree that we want to do all we can to make autocompletion work well, and catch typos etc. as quickly and easily as possible, but I know of no viable way to keep the type-safe namespaces maintainable.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is nearly complete

I'd volunteer as tester 🙂

Loosing type safety is a big problem in Java generics wilderness, no?

Copy link
Member

@ctrueden ctrueden Nov 11, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd volunteer as tester 🙂

I will try to make a plan with @gselzer soon to get things polished up and ready for testing.

Here is a very quick status report on what has happened already:

  1. ImageJ Ops matching system redesigned by me and @Treiblesschorle and @MarcelWiedenmann and @gselzer. See https://github.com/scijava/scijava-ops
  2. ImageJ Ops algorithms ported in their entirety over to the new system by @gselzer; see https://github.com/scijava/scijava-ops/tree/migrate-imagej-ops (temporary branch—work will ultimately merge to imagej/imagej-ops, but not yet).
  3. Super-cool new builder-pattern end-user op matching API proposed by @frauzufall (Builderish pattern for calling ops scijava/scijava#32) and implemented by me and @gselzer; see https://github.com/scijava/scijava-ops/tree/builder-pattern and in particular the unit tests demonstrating it.

Much of what remains to do is documented at https://github.com/scijava/scijava-ops/issues. E.g. the auto-completion issue is scijava/scijava#35. But it may be a few more months until we are really ready for beta testers—e.g. there are dependency issues to sort with SciJava core library structure and forked packages. It is possible to start testing these things now, but I'm not sure we are ready yet to endure the "thousand cuts" of detailed small pieces of feedback—what's needed now/soon is for folks to check out the general design and see if there are any really serious issues that need to be rethought. And for anyone with bandwidth to understand the design deeply and think about design decisions, input on things like scijava/scijava#36 or even scijava/scijava#37 for anyone brave enough to also learn about KNIME CompGraph.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm happy to support with testing in late alpha or early beta stage.

Regarding the auto-complete issue (which is related to a recorder-issue), we might pull the rope together. I was recently implementing a new approach for auto-completing clijx.ops in the script editor, but also from IDEs. It's based on code-generators. Not perfect yet but very powerful.
image

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So should I already use ops in the howtos in this fashion:

ops.run("connectedComponents", image, structuringElement)

.. since the typesafe namespaces are probably gone in the next iteration? (I will miss them dearly)

// load and show saved image
Object savedTable = null;
try {
savedTable = ij.io().open(dest);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a way that ij.io().open returns a Table and not an Object?

Copy link
Member

@ctrueden ctrueden Nov 11, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@haesleinhuepf ij.io().open can return anything. Images, tables, whatever the chosen IOPlugin returns.

Are you asking if there is a type-safe way to write "load a table from this data source (e.g. file)—and if it's not a table, fail / throw an exception"? We have this for images with the DatasetIOService. There is no TableIOService yet but we could add this.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, actually I would say yes. However, the scenarios where loading tabels makes much sense in an Image-Processing Software are rare, no? I mean, most of my workflows end with saving tables. Hardly any workflows load tables...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@haesleinhuepf
Copy link
Member

I think that's it. Thanks again for your work @frauzufall . I'm so happy to see this collection of examples coming!

Cheers,
Robert

@frauzufall
Copy link
Member Author

Thanks @haesleinhuepf for reviewing the examples! It was also my hope that the API can benefit from these howtos by making it easier to spot improvements.

@imagejan thank you for your feedback! I started with a separate repository, but people have a hard time finding the tutorials already.. https://github.com/imagej/tutorials does not look too bloated to me yet and since the howtos are a separate module, they also have their own dependency management etc. So I would vote for keeping them here, but I am fine with both if more people think this should be it's own repo.

If making PRs here is a problem for some, they can create an issue with a suggestion (also if it's just a howto-question) and let me or others curate this collection. It would also be great to communicate this in the forum so anytime a (not too complex) question about IJ2 Java development is answered in the forum, we add the solution here.

@ctrueden
Copy link
Member

ctrueden commented Nov 11, 2019

I am in favor of keeping everything here in one repository. Then we just tell people to look here. As soon as we split things up, the discoverability problem resurfaces.

Relatedly: @frauzufall Could you remind me why we need both maven-projects and howtos? There is a lot of overlap. Can we make do with a single folder that contains all Java code examples? What are the reasons (technical and/or social) they have to be separate?

And finally: how can we keep these howtos snippets in sync with the Jupyter notebooks? Each howto could also be a standalone Jupyter notebook, illustrating these ideas in Groovy form. This will be easier for new people to learn than Java will. However, it seems very tricky to do any automated validation to keep Java and Groovy versions in sync. Maybe the CI could execute both versions of each howto with some special UserInterface plugin on the classpath that logs outputs and diffs them? I think it could be doable!

Copy link
Member

@ctrueden ctrueden left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks so much @frauzufall for working on this! I'd like to discuss a bit how we want to proceed strategically with howtos, notebooks, Java, Groovy and so forth. I tried to respond to the various API design questions raised, so there may be some more back and forth on that front as well. But the paramount decision we need to focus on is how to organize the information so it's easy for new people to find and digest, while covering all major target audiences, and hopefully easy for us to maintain and develop as time goes on. There are some conflicting requirements on these fronts, but we can seek compromises.

@frauzufall
Copy link
Member Author

Thanks @ctrueden for your feedback and for reviewing the code!

Relatedly: @frauzufall Could you remind me why we need both maven-projects and howtos? There is a lot of overlap. Can we make do with a single folder that contains all Java code examples? What are the reasons (technical and/or social) they have to be separate?

For me, the maven-projects are there to be copied when you start a new project. These howtos are different - they present a lot of information in a very condensed way. They should be easy to oversee and contain only small parts of information per example (e.g. I should also split up the conversion examples). One should be able to point someone who has a specific question to a specific resource.

I love how you guys are already discussing the API based on the existing examples - looks like this format has potential. Howtos are a good tool to get a feeling for the complexity, the benefits and the shortcomings of an API.

We discussed this quite a bit in the lab. We would love to get here (this is a screencast of a prototype in the browser, showing howtos in a database collected from maven artifacts):

howtoservicedemo

And finally: how can we keep these howtos snippets in sync with the Jupyter notebooks? Each howto could also be a standalone Jupyter notebook, illustrating these ideas in Groovy form.

My work these days is mostly Java and people ask me Java related questions, so reading or writing notebooks is impractical for me. Some examples are also quite Java specific. I want this collection for egoistic reasons, because my work benefits from it, and because this fits well to what we teach at the learnathon. We want more contributors to the Java codebase, right, and it took me way too long to figure out some of these solutions. I want to speed this up for others.

I agree though that there is a bigger audience for other languages. Maybe someone else could take the lead there. Cross language testing sounds like a fun project :)

Img img = IO.openImgs(Object.class.getResource("/blobs.png").getPath()).get(0);

// display image statically
ImageJFunctions.show(img);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Important to note in the comment that ImageJFunctions relies on ImageJ1.


private static void run() {
ImageJ ij = new ImageJ();
ij.ui().setHeadless(true);
Copy link
Member

@ctrueden ctrueden Nov 12, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is fine. But it's worth noting that calling System.setProperty("java.awt.headless", "true"); before spinning a new ImageJ is "more secure" in that context initialization code will already know that everything is headless. Behavior might differ between these two approaches.

Also, did you know you can pass command line arguments like so?

ImageJ ij = new ImageJ();
ij.launch("--headless"); // and whatever else you might want...

*
* @author Deborah Schmidt
*/
public class OpenAndShowImage {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feels expressly redundant to the load-and-display-dataset example in maven-projects. So much so that it makes me want to just delete the Maven project. There are several such projects that could/should just migrate into the howtos. No?

* See the CC0 1.0 Universal license for details:
* http://creativecommons.org/publicdomain/zero/1.0/
*
* This work was supported by the German Research Foundation (DFG) under the code JU3110/1-1 (Fiji Software Sustainability).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need this blurb?

}

/**
* .. get an instance of the {@link IOService} without using the {@link ImageJ} gateway
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we explain why one might want to do this? E.g., to reduce the dependency footprint? Or is that out of scope here?

@ctrueden
Copy link
Member

In the interest of cooperation and compromise, I have merged this project. Thanks again, @frauzufall—it is a treasure trove of good information. I look forward to building on it as time goes on. Historically I have often created gists for this sort of thing, so I'll try to commit new examples as howtos from now on.

I am disappointed that no one else is willing to go the Jupyter notebooks route. The community loves them. They automatically work as web pages online in a nice tutorial flow style—while also being runnable and changeable locally or in the cloud. And we could even do them in Java if you really wanted. I do not understand comments like "notebooks are impractical for me." Scripting code is more succinct and easier to understand for new people. The situation just feels absurd to me: on one hand we have new aspiring programmers (here's a good example) who have historically been told that to do powerful things in ImageJ requires writing a Java plugin, so they start trying to learn how to write ImageJ2 Java code, but then complain that the learning curve is way too steep... but when told to use an easier language besides Java, express reluctance. And on the other we have core developers funded to maintain the codebase who are unwilling to improve documentation and outreach beyond only Java code. I honestly find the situation baffling on both sides.

So I give up. Let's beef up the Java documentation. The howtos are great. I will pare down the maven-projects, moving most of them into howtos.

One idea I have for partially addressing the "language gap" is to try transpiling all the Java howtos into scripts on the fly. (This is probably easier than the reverse of scripts into Java.) So then when people are searching using @frauzufall's awesome web-based howtos filter, there could tabs for seeing the code in different language lenses, without us needing to explicitly maintain shallow forks of everything. We could even have the CI run all the transpiled code to improve the chances it works as written.

I will keep you posted if I manage to get anything like that working.

@frauzufall
Copy link
Member Author

Historically I have often created gists for this sort of thing, so I'll try to commit new examples as howtos from now on.

That'd be really cool, thank you!

I am disappointed that no one else is willing to go the Jupyter notebooks route. [..] And we could even do them in Java if you really wanted. I do not understand comments like "notebooks are impractical for me." Scripting code is more succinct and easier to understand for new people.

This is not about scripting. When I am searching for a solution / best practice in Java for ImageJ2, I am looking for examples in this language which I can run & debug in the IDE. Why would I want to start a notebook in this scenario?

So I give up. Let's beef up the Java documentation.
Improving one part of the documentation should not feel like giving up. Notebooks are great, howtos and maven-projects are great, they have different target audiences. It might make it easier to write notebooks in the future with these existing examples. The reason I never wrote a tutorial notebook is that my work so far did not include teaching people how to script.

It would be amazing to migrate examples from one language into the other. We can also chat about this in December. @fjug is pushing the Fiji-Python-bridges topic for the Hackathon, so the notebooks could become a bigger topic there.

Thank you for merging! I'll make a new PR with the changes suggested in the reviews here.

@ctrueden
Copy link
Member

ctrueden commented Nov 12, 2019

When I am searching for a solution / best practice in Java for ImageJ2, I am looking for examples in this language which I can run & debug in the IDE. Why would I want to start a notebook in this scenario?

Please understand that I am approaching this from the perspective of project maintainer who wants to do what's best for the whole community. I had a vain hope that developers at MPI-CBG would also be seeing the project through that lens. Why write a Jupyter notebook? Because many people like them and find them useful and instructive. Because they automatically become community documentation.

Instead, I feel rather like everyone in our community just wants to scratch their own itches without going the extra mile toward the greatest community impact. I am sorry for being critical—after all, you (@frauzufall) are in the top tier of those doing the most for the broader community! I was just trying to raise the point that IMHO, Java howtos are not the most generally helpful thing we could be doing for the project as a whole, compared to notebook howtos. Your response that the Java howtos are what's most effective to help you and your group is well taken, but corroborates my point that it is not embracing the maintainer's mantle of developing resources for the community as a whole.

Here is my thinking:

It is infeasible for us to provide comprehensive howtos in every supported language. (If we can figure out some best-effort transpilation across the most popular languages of Java, Jython and Groovy, that would largely mitigate this fact. But that will require exploratory development effort.)

So then, we must choose. Which language(s) do we want to use for our comprehensive documentation? If we want to focus on users new to programming, we should choose a script language, because it's easier for them. But if we want to focus on Java developers, then it is better to provide the tutorials in Java, since they are the ones most likely to contribute back to the core Java libraries.

Possible reasons to focus on Java developers:

  1. They are more likely to contribute back? In theory. But in practice, I have found that people—even Java developers—rarely if ever contribute back to the core libraries. They just want to learn and use the tool, and carry on.
  2. They are more inconvenienced than the new programmers by the language mismatch? Who is more inconvenienced by the tutorials being in a suboptimal language? The new programmer who has to adapt the Java code to Python or Groovy? Or the Java programmer who has to adapt the Groovy script to Java? My thinking has been that it's easier for the Java programmer to cut-and-paste from the notebooks, use their IDE to quick fix the types, and go—because the Java programmer has already achieved that higher bar of technical expertise. Whereas the new programmer, when cut-and-pasting from Java, needs to know enough to remove the types, and tweak the syntax (e.g. foreach loop syntax differs in each of these 3 languages).
  3. They are a larger demographic than new programmers? On the contrary, I think there are many fewer Java programmers in our community than new programmers or Python programmers or MATLAB programmers. Most scientist programmers are scripters, not software engineers.

Am I off-base with this? Is it really our goal to train everyone programming against ImageJ to be Java programmers?

Anyway, I think we can keep the Jupyter notebooks for the long-form tutorials, and use the Java howtos for the short, targeted reference tutorials. And most of the Java Maven projects can be eliminated in favor of howtos, though there are a few that only work as standalone Maven projects. Does that sound like an agreeable way forward to you?

@haesleinhuepf
Copy link
Member

haesleinhuepf commented Nov 13, 2019

Hey Curtis,

Instead, I feel rather like everyone in our community just wants to scratch their own itches without going the extra mile toward the greatest community impact.

I'm a bit sad reading your comment. We are trying sooo hard to make the ImageJ2 API more accessible to users and to make it easier to people to contribute plugins and code to the core.

Why write a Jupyter notebook? Because many people like them and find them useful and instructive. Because they automatically become community documentation.

I agree, people like notebooks because for documentation they are great. But analysts don't use ImageJ-based noteboooks for processing images. The either use macro/jython/groovy scripts or Java plugins. Thus, we should provide examples for this. If you know people who use noteboooks in daily practice for analysing image data, please bring them in the loop. I would love to learn from them how they work with notebooks.

My major complaint about notebooks is that examples starting with

%classpath config resolver scijava.public https://maven.scijava.org/content/groups/public
%%classpath add mvn
net.imagej imagej 2.0.0-rc-71
net.imagej imagej-notebook 0.7.1

are deterrend for new users who just want to apply a filter to an image and threshold it. We discussed that earlier. I think, @frauzufall's approach of generating an index of examples with a dynamic webpage is the game changer here. ❤️ This could become the moxygen for ImageJ2

Possible reasons to focus on Java developers:

  1. They are more likely to contribute back? In theory. But in practice, I have found that people—even Java developers—rarely if ever contribute back to the core libraries.

I can just speak for myself. I tried sooo hard to contribute. Again and again and again and again and it took me quite some time to finally become part of the core developers community. We need to make that easier. We should motivate young scientists in supporting us. This must be the major goal of the senior members of our community. If possible, we should consider lowering entry level bounds. The easier supporting us is, the more people will do it.

It is infeasible for us to provide comprehensive howtos in every supported language.

It
is
at
least
worth
a
try.
Think
out
of the
box.

Am I off-base with this? Is it really our goal to train everyone programming against ImageJ to be Java programmers?

IMHO, people can switch immediately from macro to Java. One language for calling plugins and one language for developing plugins. It could be as simple as that. Time will replace macro with c-python one day.

Anyway, I think we can keep the Jupyter notebooks for the long-form tutorials

We should! I'm happy to contribute some more workflow-oriented notebooks here as available in other places. Last time I tried, IJ-notebooks didn't support ImgLabellings and thus, it was hard to visualise how to do particle analysis.

How about focusing on notebooks on using pyimagej? There are also some issues to be resolved regarding user convenience. We need to solve them soonish as people are asking for
a solid ImageJ-Python bridge. Your help on these issues would be very much appreciated! We need the most-experienced developers like you in order to get this working properly - on windows, mac and linux. If pyimagej works without any installation or conversion pain, we don't need to be afraid of recent developments in the bio-image analysis community. We count on you! ;-)

Cheers,
Robert

@ctrueden
Copy link
Member

ctrueden commented Nov 13, 2019

analysts don't use ImageJ-based noteboooks for processing images. The either use macro/jython/groovy scripts or Java plugins. Thus, we should provide examples for this.

The notebooks are examples of scripts! Just because we use notebooks to provide a tutorial, does not mean people reading the tutorial need to care that they are in fact Jupyter notebooks. but the fact that they are Jupyter notebooks makes our lives much easier. They are web pages automatically. They can be run by CI and results validated to match the known outputs. And people who do know about notebooks can easily modify the code and play around with it.

If you know people who use noteboooks in daily practice for analysing image data, please bring them in the loop. I would love to learn from them how they work with notebooks.

Some of my colleagues at LOCI are using Jupyter regularly. Here is an example notebook using pyimagej and SimpleITK for image registration.

My major complaint about notebooks is that examples starting with ... are deterrend for new users

I feel that you are throwing the baby out with the bathwater. There must be a way we can hide or collapse or minimize that initialization cell. I find the tutorial notebooks generally super nice to read after that initial cell. And if not: wouldn't it make sense to start a conversation with the Jupyter developers, rather than just giving up? The benefits outweigh the disadvantage here.

It is infeasible for us to provide comprehensive howtos in every supported language.

It is at least worth a try. Think out of the box.

Very nice. But could you please explain how you did that? Do you use a tool to keep resources in sync? Or are you maintaining it all manually? How would you suggest we proceed with ImageJ2 documentation?

If you are maintaining it manually, then I reiterate my claim that it would be infeasible to provide comprehensive howtos in every supported language. Already we have 28 Java howtos, and ~4-7 major language options.

IMHO, people can switch immediately from macro to Java. One language for calling plugins and one language for developing plugins. It could be as simple as that.

There are many things you cannot do from macro. E.g. you cannot call ops from macro. Even with the Ext plugins idea you and I discussed, macro would continue to have severe limitations. And to be frank, the macro language is a maintenance nightmare and I do not want to extend its capabilities and then be responsible for keeping those extensions working, when we have so many awesome script languages already.

We count on you! ;-)

I appreciate the pep talk, but these projects need more than cheerleading. They need people fixing bugs, who are willing to roll up their sleeves and really learn how the code works and form educated opinions about discuss and decide on best ways forward, and then do it. So far, no one has helped with pyimagej (or indeed really any pieces of ImageJ core—with the exception of software engineers paid to do so, and @imagejan, who is a superstar) at that level—for reasons discussed in more detail below. Filing issues is fine and all, but if you wait for me to fix everything single-handedly, you will be waiting for a very long time. The very strong tendency I see in the developer community (ours, but also the broader one) is that people are vastly more comfortable inventing new things than fixing or improving existing things—especially when those existing things aren't already "near perfect." Which of course ImageJ2 certainly isn't.

Last time I tried, IJ-notebooks didn't support ImgLabellings and thus, it was hard to visualise how to do particle analysis.

Good example. Roll up sleeves. Make it support them. If you don't have time, and/or it's too much work, then I fully understand and sympathize—but then probably no one (with sufficient expertise) has time.

We should motivate young scientists in supporting us. This must be the major goal of the senior members of our community. If possible, we should consider lowering entry level bounds. The easier supporting us is, the more people will do it.

I used to agree with you, @haesleinhuepf. But after 10+ years of ImageJ2, I have concluded that programming on a large, high-quality Java software project—or maybe just ImgLib2 and SciJava, I don't know—is simply too daunting an endeavor for most non-specialists who don't have years of their time to devote to it. The learning curve for coding+Git+GitHub alone is already extremely steep for most people. Now throw Java into the mix with its static typing including generics; Maven and IDEs and versioning; dependency injection + inversion of control for extensibility (i.e. a plugin framework and application container); multiple interface composition and layer after layer of abstract class hierarchies; bytecode manipulation... 😱:hurtrealbad:

As you said in one of the issues linked above: "I would love to debug the thing myself but it's just too deep within ImageJ." You are smart, talented, knowledgeable, motivated and socially connected to the core developers—and yet still ultimately deterred from hacking on ImageJ core because of its complexity. The obvious conclusion is that ImageJ core is too complex for significant external contribution. There is even a subset of the ImageJ community who very obviously and explicitly rejects ImageJ2 for being too hard to understand, and will continue using ImageJ1 indefinitely.

On the other side of the coin, there are forces pushing for the ImageJ2 and SciJava design to become even more complex to handle emerging requirements of even bigger data, clean separation of workflow specification from execution, maximum control over the software's behavior, and even broader support for more usage scenarios (e.g. mixing ImageJ into a Python script!).

My feeling these days is: we'll never be able to simplify the system to the point where non-software-engineers are able to meaningfully contribute to core. The best we can hope for is to make ImageJ accessible enough for people to keep building more stuff on top of ImageJ and sharing it via update sites. In which case: why not target the tutorials at that larger audience, rather than some mythical "potential future core developer"?

@haesleinhuepf
Copy link
Member

Some of my colleagues at LOCI are using Jupyter regularly. Here is an example notebook using pyimagej and SimpleITK for image registration.

Great! And it's PyImageJ! - following my suggestion regarding on what to focus on.

I appreciate the pep talk, but these projects need more than cheerleading.

You may be the only person I know who can guide us through the deep ImageJ2 core. I'm just asking you to guide us so that we can fix issues ourselfes. For example this issue cannot wait for a SciJava-Ops release. My post contains a potential solution. Please guide me in building it in so that people can make use of GPU-accelerated image processing via pyimagej. Please guide me in how to deploy pyImageJ. My interest in that particular technology is so high that I'm considering helping you in maintaining it. But I need your guidance.

As you said in one of the issues linked above: "I would love to debug the thing myself but it's just too deep within ImageJ." You are smart, talented, knowledgeable, motivated and socially connected to the core developers—and yet still ultimately deterred from hacking on ImageJ core because of its complexity.

I'm deterred from hacking ImageJ core because of its unclear future directions. For example, I hear "ImageJ-Ops will be released next year." for four years now. Investing time in such a project is a big risk for someone at my career level. I have to choose projects where I can show significant progress every 1-2 years. Otherwise I cannot do open-source projects anymore.

There are many things you cannot do from macro. E.g. you cannot call ops from macro. Even with the Ext plugins idea you and I discussed, macro would continue to have severe limitations. And to be frank, the macro language is a maintenance nightmare and I do not want to extend its capabilities and then be responsible for keeping those extensions working, when we have so many awesome script languages already.

If Ops are not accessible to the users (neither via macro nor via the menu), they will not use them. It's as simple as that. Furthermore, thousands of non-CS people learned ImageJ programming using macro by watching what the recorder is doing. As long as the recorder and auto-completion don't work properly for any other language than macro, the people won't switch.

The best we can hope for is to make ImageJ accessible enough for people to keep building more stuff on top of ImageJ and sharing it via update sites. In which case: why not target the tutorials at that larger audience, rather than some mythical "potential future core developer"?

IMHO, the vast majority of ImageJ-users are no computer scientists anyway. Let's write documentation for them and make the ImageJ-API accessible to them. I think we will have automatically more contributors - for the core and for the API.

@ctrueden
Copy link
Member

You may be the only person I know who can guide us through the deep ImageJ2 core.

Bus factor 1, eh? That is definitely a problem we need to fix. I am hopeful we'll have a second full-time person on ImageJ2 by early next year, which will help. But it would be really great if there were at least one person at MPI who you'd consider an "expert" at ImageJ2 and could go to for help.

I'm just asking you to guide us so that we can fix issues ourselfes.

I am trying my best.

For example this issue cannot wait for a SciJava-Ops release. My post contains a potential solution. Please guide me in building it in

If it's that urgent, please respond on the issue thread. I was waiting for you to post benchmarks that narrow down the problem further. Right now we don't know if it's an issue with the ImageJ Ops matcher (that was speculation), or with the JIT upon first execution of certain code paths that load classes, or what. Does your my_rai_to_numpy method execute in <100ms on its first run in a clean JVM? It is not clear to me. It is not enough to just run three different methods once each in sequence and measure the times, due to the JIT. You do not need my personal help in writing a correct benchmark; there are multiple expert Java programmers in your building who know how to do that. For what it's worth, I asked on Gitter just now if there is a better ImgLib2-style one-liner for doing the image copy, for use from Python.

I'm deterred from hacking ImageJ core because of its unclear future directions. For example, I hear "ImageJ-Ops will be released next year." for four years now. Investing time in such a project is a big risk for someone at my career level.

Yep, you and everyone else. It's a big time investment (and therefore a big risk) for people at any career level. That's why I said "ImageJ2 core is too complex for external contributors"—because almost no one is willing to make that investment. But then no one helps unless they are paid, and development gets slower, making the risk/reward assessment look even worse. It's a vicious cycle. The only way I've seen it broken is to secure more money for the project. Shooting for substantial "community driven development" of niche core infrastructure is a fool's hope.

As long as the recorder and auto-completion don't work properly for any other language than macro, the people won't switch.

Yeah, we've discussed it before. I agree with you. But don't have time to work on it, and finish Ops, and manage pom-scijava, and respond to developer support requests on Gitter and the forum, and fix the ImageJ data model, and fix SCIFIO, and keep the ImageJ servers running securely, and make ImageJ work from Python, and from JavaScript, and explore how to make a Fiji that includes both ImageJ and Napari, and...

IMHO, the vast majority of ImageJ-users are no computer scientists anyway. Let's write documentation for them and make the ImageJ-API accessible to them.

100% agreed! 👍 Which is why I suggested teaching the ImageJ API using scripts rather than Java code in the first place.

I think we will have automatically more contributors - for the core and for the API.

That's the part I'm challenging here. More? Perhaps. +1, or +2. And occasional bug-fixers. But no one is e.g. going to make RoiManager work headless, unless it's their job. And certainly no one's going to fix the ImageJ2 data model as a side project.

It is infeasible for us to provide comprehensive howtos in every supported language.

It is at least worth a try. Think out of the box.

I still want to know how to you are managing the clij documentation. Manually? Or with tools? Which tools? Could they help the ImageJ2 project?

@haesleinhuepf
Copy link
Member

It is infeasible for us to provide comprehensive howtos in every supported language.

It is at least worth a try. Think out of the box.

I still want to know how to you are managing the clij documentation. Manually? Or with tools? Which tools? Could they help the ImageJ2 project?

Sure! The API reference is generated aiming to be similar to ImageJ1 documentation - simple and easy. I also added a way for linking examples from the API reference. The idea comes from matplotlib and scikit-image. Very convenient to see documented functions in action.

In CLIJ2 a similar strategy is supposed to generate javadoc compatible comments. I guess that's similar to how the namespaces in imagej-ops were built.

The example scripts are hand-written. Examples for JavaScript/Groovy/Beanshell are copy-pasted. Whenever a user asks for a new example I put it in the example repository and link it in the forum.

As CLIJs API is basically code-frozen and aimed to be backwards-compatible, the examples practically can not break. That reduces pressure during the year. Most issues may appear during a dedicated beta-testing period in May/June.

@imagejan
Copy link
Member

@ctrueden wrote:

There must be a way we can hide or collapse or minimize that initialization cell.

FWIW, you can put an initialization cell anywhere in the notebook (as used e.g. here and here), but this might be suboptimal because it breaks expectations with regards to execution order of cells.

When using RISE or Jupyterbook, you can use cell metadata to hide entire cells (i.e. both input and output).

@ctrueden
Copy link
Member

@imagejan It seems there are also a couple of extensions we might want to try:

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

Successfully merging this pull request may close these issues.

None yet

4 participants