[ANN] Announcing ImageTally.jl — Interactive image counting and landmark placement for scientific images

Hi Julia community!

I’m excited to share ImageTally.jl, a package I’ve been working on for interactive counting and tagging of objects in scientific images.


The backstory

During my Masters and PhD I worked on butterfly genetics and spent a lot of time doing image analysis and geometric morphometrics — placing landmarks on wing images in MorphoJ and running the analyses in R. It was painstaking work, and the tools available were functional but dated. MorphoJ is a Java application from the early 2000s that hasn’t changed much since. The workflow always felt like it deserved something better.

During my postdoc I started using Julia more extensively, and I had a project that brought the image counting problem into sharp focus. I was working with leafroller egg masses — these are remarkable structures where a moth can lay anywhere from a few dozen to a couple hundred eggs, all overlapping each other in a tight tiling pattern on a leaf surface. I developed staining techniques to improve the contrast in photographs and make individual eggs more visible. My workflow at the time was loading images in GIMP, adding colored points manually on each egg, then loading the point layer in Julia to count by color. It worked, but it was awkward and inflexible.

In February 2019 I came across this Discourse thread where Andrew McKay was building exactly the kind of tool I needed — a Makie-based image annotation solution. I responded the same day. We exchanged ideas, and I shared one of my egg mass images — deliberately choosing one of the simpler ones, quietly hoping someone would tell me automated segmentation was straightforward. Andrew’s response was diplomatic: “it does look like you have quite the challenge there.” A few days later he published ImageTrainer.jl, explicitly notifying me when it went public. It was a promising proof of concept — but it was never completed, and the Makie API has changed substantially in the six years since. I forked it, but never took it further at the time. I ended up using a Python tool to finish that project.

That conversation was in 2019. When I recently checked the Julia ecosystem again, the gap was still there — six years later. So I built ImageTally — starting fresh with modern Makie, a clean architecture, proper tests and documentation, and the lessons learned from that original effort.


What ImageTally does

ImageTally provides an interactive GLMakie-based GUI for manually counting and tagging objects in images, plus a full programmatic API for batch processing and data export.

Core features:

  • Interactive counting — left-click to place markers, right-click to delete, left-drag to move. Scroll to zoom, R to reset view
  • Multiple tags — define up to 10 named categories (e.g. “male”, “female”, “egg”, “parasitized”), each with its own color and marker shape
  • Session persistence — save and reload sessions as TOML files so work can be interrupted and resumed
  • CSV export — every counted point is exported with relative coordinates, pixel coordinates, and timestamp
  • Programmatic API — create and manipulate sessions entirely in code, no window needed
julia> using CSV, DataFrames

julia> df = CSV.read("/your/directory/saved_CSV_File.csv", DataFrame)
25×7 DataFrame
 Row │ id     tag        x_relative  y_relative  x_pixel  y_pixel  timestamp               
     │ Int64  String15   Float64     Float64     Int64    Int64    DateTime                
─────┼─────────────────────────────────────────────────────────────────────────────────────
   1 │     1  uneclosed    0.536547    0.7544       1629     3054  2026-04-13T14:14:02.024
   2 │     2  uneclosed    0.590601    0.56311      1793     2279  2026-04-13T14:14:06.924
   3 │     3  eclosed      0.395175    0.785505     1200     3180  2026-04-13T14:14:11.690
   4 │     4  eclosed      0.330726    0.668864     1004     2708  2026-04-13T14:14:12.491
   5 │     5  eclosed      0.436755    0.682861     1326     2764  2026-04-13T14:14:13.457
   6 │     6  eclosed      0.5511      0.642425     1673     2601  2026-04-13T14:14:14.591
   7 │     7  eclosed      0.494967    0.56311      1503     2279  2026-04-13T14:14:15.657
   8 │     8  eclosed      0.399333    0.60199      1212     2437  2026-04-13T14:14:16.290
  ⋮  │   ⋮        ⋮          ⋮           ⋮          ⋮        ⋮                ⋮
  19 │    19  eclosed      0.372306    0.396702     1130     1606  2026-04-13T14:14:26.223
  20 │    20  eclosed      0.463782    0.368708     1408     1493  2026-04-13T14:14:27.157
  21 │    21  uneclosed    0.284988    0.308055      865     1247  2026-04-13T14:14:32.458
  22 │    23  uneclosed    0.349437    0.238071     1061      964  2026-04-13T14:14:43.691
  23 │    24  eclosed      0.424281    0.266064     1288     1077  2026-04-13T14:14:46.890
  24 │    25  eclosed      0.395175    0.34227      1200     1386  2026-04-13T14:14:47.791
  25 │    26  eclosed      0.513678    0.328273     1560     1329  2026-04-13T14:14:48.590
                                                                        10 rows omitted


Quick start

julia

# Install (not yet in the General Registry — registration in process, three day mandatory waiting period!)
] add https://github.com/alejandromerchan/ImageTally.jl

# Also install GLMakie and FileIO for the GUI
] add GLMakie, FileIO

# Launch the counter
using GLMakie, FileIO
using ImageTally

fig, sess = launch_counter("path/to/image.jpg")

# Or resume a previous session
fig, sess = launch_counter("path/to/image.jpg"; session="image_session.toml")

Without GLMakie, the package works as a pure data processing tool:

julia

using ImageTally

# Load a saved session and analyze without opening a window
sess = load_session("my_count.toml")
count_by_tag(sess)      # Dict("egg" => 47, "parasitized" => 3)
total_count(sess)       # 50
export_csv(sess, "results.csv")
println(session_summary(sess))

Supported image formats

Tested formats include JPEG, PNG, TIFF (8-bit and 16-bit, color and grayscale), and BMP. Large images are supported — tested up to 18 megapixels with good interactive performance on a desktop GPU.

Platform note: The GLMakie GUI extension is officially tested on Linux. It may work on macOS and Windows but those platforms are not currently covered by CI.


Design philosophy

The package follows a strict separation between the logic layer and the GUI:

  • Core logic (src/) — pure Julia, depends only on Dates and TOML from stdlib. Fully testable without a display, works headlessly on servers and clusters
  • GUI (ext/GLMakieExt.jl) — activated automatically when GLMakie and FileIO are loaded, implemented as a package extension

This means a researcher can use ImageTally to count images interactively on their laptop, save sessions as TOML files, and then batch-process all sessions on a computing cluster without GLMakie ever being installed there.


A gap in the Julia ecosystem

Before starting this project I searched for existing Julia tools for interactive image counting or landmark digitization. I didn’t find anything that fit this use case — the closest were general image viewers without structured counting output, or plotting tools not designed for interactive annotation. If there are existing packages I missed, I sincerely apologize — please let me know in the comments and I’ll add them to the documentation.

For context, here is what exists in other languages and environments:

Python:

  • ClickPoints — a well-designed scientific image annotation tool published in Methods in Ecology and Evolution (2017). Supports point counting with categories, masks, tracking, and a Python API. Actively maintained and genuinely excellent for Python users.
  • labelme — general purpose annotation tool, more focused on machine learning applications than scientific counting workflows.

R:

  • geomorph — the standard package for geometric morphometrics and landmark-based shape analysis. Powerful for analysis but not designed for interactive image digitization.

Standalone:

  • MorphoJ — Java-based application for geometric morphometrics, widely used in evolutionary biology. Functional but largely unchanged since the early 2000s.
  • tpsDig2 — another Java application widely used for landmark digitization in morphometrics. Also aging.

The pattern is clear: the Python ecosystem has good general annotation tools, R has powerful analysis tools, and Java has the legacy morphometrics applications. ImageTally is an attempt to start filling that gap in Julia — beginning with the interactive counting and annotation layer that everything else builds on.


A note on development process

I want to be transparent: Claude Code was used extensively in developing ImageTally. I have a background in biology and consider myself an intermediate programmer, and this is probably one of the largest software projects I’ve ever done, so the use of AI agents has been very useful. However, I was involved at every step — testing the code interactively in the Julia REPL, guiding the architecture decisions, identifying and debugging errors, and providing the biological domain knowledge that shaped the tool’s design. The ideas, the use cases, the testing with real images, and the overall direction came from me. Claude Code was a very capable pair programmer, not an autonomous agent.

I think this kind of transparent disclosure is important as AI-assisted development becomes more common in the open source community.


Intended use cases

  • Entomology — counting insects on sticky traps, eggs on plant material, specimens in collection images
  • Microscopy — counting cells, nuclei, or other objects in histological sections
  • Ecology — counting organisms in field photographs, aerial surveys, camera trap images
  • Geometric morphometrics — placing landmarks on biological structures for shape analysis (fluctuating asymmetry, Procrustes analysis)
  • Any domain where manual object counting in images is needed and automation is unreliable or unavailable

What’s coming

ImageTally v0.1.0 is intentionally focused. Ideas on the roadmap for future versions:

  • Scale calibration — draw a line over a known scale bar, enter the real-world distance, and all subsequent measurements are automatically converted to physical units
  • Distance measurements — measure distances between points in real-world units, useful for taxonomy and morphometrics
  • Landmark mode — named/numbered landmarks with side designation (left/right) for geometric morphometrics workflows, with export compatible with R’s geomorph
  • Human-in-the-loop annotation — display predictions from automated detection models (SAM, YOLO) as a starting point for human verification

Longer term, I’m thinking about a small ecosystem of composable image analysis packages built on shared infrastructure — something the Julia ecosystem is particularly well suited for. But that depends entirely on whether this tool finds users and real use cases.


Links


Feedback, suggestions, bug reports, and pull requests are all very welcome. I’m especially curious to hear from anyone working in biological image analysis — whether ImageTally fits your workflow, what’s missing, and what would make it more useful.

21 Likes

This is really exciting and congrats @alejandromerchan ! I just want to CC @Jakub_Mitura @cncastillo, and @kevbonham as I know there would be massive interest from within the JuliaHealth and BioJulia ecosystems and how we might/could generalize these ideas to image tagging (I think Jakub actually has worked on something very similar too!).

Might be some fun things to explore here!

Cheers,

~ tcp :deciduous_tree:

4 Likes

Looks interesting!