Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -69,14 +69,19 @@ jobs:
Pkg.develop(PackageSpec(path=pwd()))
Pkg.instantiate()
- uses: julia-actions/julia-buildpkg@v1
with:
prefix: xvfb-run
- uses: julia-actions/julia-docdeploy@v1
with:
prefix: xvfb-run
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
DOCUMENTER_KEY: ${{ secrets.DOCUMENTER_KEY }}
- name: Run doctests
shell: julia --project=docs --color=yes {0}
shell: xvfb-run julia --project=docs --color=yes {0}
run: |
using Documenter: DocMeta, doctest
using CounterMarking
DocMeta.setdocmeta!(CounterMarking, :DocTestSetup, :(using CounterMarking); recursive=true)
doctest(CounterMarking)

20 changes: 12 additions & 8 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,34 @@ version = "1.0.0"

[deps]
FileIO = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549"
Glob = "c27321d9-0574-5035-807b-f59d2c89b15c"
Gtk4 = "9db2cae5-386f-4011-9d63-a5602296539b"
GtkObservables = "8710efd8-4ad6-11eb-33ea-2d5ceb25a41c"
ImageCore = "a09fc81d-aa75-5fe9-8630-4744c3626534"
ImageIO = "82e4d734-157c-48bb-816b-45c225c6df19"
ImageMorphology = "787d08f9-d448-5407-9aad-5290dd7ab264"
ImageSegmentation = "80713f31-8817-5129-9cf8-209ff8fb23e1"
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"

[weakdeps]
ImageView = "86fae568-95e7-573e-a6b2-d8a6b900c9ef"

[extensions]
CounterMarkingImageViewExt = "ImageView"
JLD2 = "033835bb-8acc-5ee8-8aae-3f567f8a3819"
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
XLSX = "fdbf4ff8-1666-58a4-91e7-1b58723a45e0"

[compat]
FileIO = "1"
Glob = "1"
Gtk4 = "0.7"
GtkObservables = "2"
ImageCore = "0.10"
ImageIO = "0.6"
ImageMorphology = "0.4"
ImageSegmentation = "1.9"
ImageView = "0.12"
JLD2 = "0.5"
XLSX = "0.10"
julia = "1.10"

[extras]
ImageView = "86fae568-95e7-573e-a6b2-d8a6b900c9ef"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

[targets]
test = ["ImageView", "Test"]
test = ["Test"]
1 change: 1 addition & 0 deletions docs/make.jl
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ makedocs(;
),
pages=[
"Home" => "index.md",
"Reference" => "reference.md",
],
)

Expand Down
Binary file added docs/src/assets/Picture.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/src/assets/gui.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
213 changes: 209 additions & 4 deletions docs/src/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,216 @@ CurrentModule = CounterMarking

# CounterMarking

Documentation for [CounterMarking](https://github.com/HolyLab/CounterMarking.jl).
[CounterMarking](https://github.com/HolyLab/CounterMarking.jl) analyzes experiments on [scent-marking in mice](https://www.sciencedirect.com/science/article/pii/S0003347287800167),
specifically images of urine countermarking visualized by the [ninhydrin reaction](https://pubs.acs.org/doi/full/10.1021/jf030490p):

![A ninhydrin-stained image](assets/Picture.png)

The yellow spot corresponds to a stimulus provided by the experimenter, and the small light-blue spots are deposited marks.

Tips on image quality:

- Put the stimulus near one of the four corners
- Ensure lighting is fairly uniform
- Try to ensure the entire image is of the filter paper, and that there aren't any black edges to the image (see lower left corner in the example above)
- Make sure that any extraneous marks (e.g., the black writing in the image above) are of a very different color from scent marks.

## Tutorial

### Installation and setup (one time setup)

There are several ways to organize your data, but one recommended approach is to have a parent "project" folder, and then store images collected on different days in subfolders named by date:

```sh
MyCounterMarkingFolder
Project.toml
2025-03-15/
2025-03-17/
...
```

We'll create the `Project.toml` for running the analysis. From the command line within `MyCounterMarkingFolder`, the steps below will:

- start Julia
- get into `pkg>` mode
- activate a new project
- install the packages you'll use

Here are the steps, starting from the OS command line:

```
MyCounterMarkingFolder$ julia
<banner shows up>

julia> ] # this enters pkg mode
pkg> activate .

pkg> add CounterMarking ImageView Glob FileIO ImageIO
```

This should create the `Project.toml` file in `MyCounterMarkingFolder`. If this succeeds, you shouldn't have to do this again.


From this point on, start Julia like this:

```sh
MyCounterMarkingFolder$ julia --project
```

and it will automatically "activate" this project and you'll have access to all those packages.

!!! tip
If you ever need to update the packages (e.g., to get any improvements
to `CounterMarking.jl`), you can update packages with `pkg> up`. See the
[Pkg documentation](https://pkgdocs.julialang.org/v1/getting-started/) for
more information.

## Processing with the GUI

From within `MyCounterMarkingFolder` created above, start Julia like this:

```sh
MyCounterMarkingFolder$ julia --project
```

Then load the packages:

```
julia> using CounterMarking, Glob
```

Then specify the images you want to process:

```
julia> gui("results_file_name", glob"Picture*.png")
```

This will save your results to `"results_file_name.xlsx"` and `"results_file_name.jld2"`.
The syntax `glob"pattern"` means "all files that match this pattern", where `*` means "one or more characters".
See [this tutorial](https://www.malikbrowne.com/blog/a-beginners-guide-glob-patterns/) for more information about glob syntax.
Alternatively, you can supply a list of files:

```
julia> gui("results_file_name", ["PictureA.png", "mouse7.png"])
```

However you launch it, you should see something like this:

![GUI](assets/gui.png)

On the top is the raw image. On the bottom is the segmented image; you should visually compare the two to check whether you're pleased with the quality of the segmentation.
(If not, click "Skip" to omit that file from analysis.)

If you like the segmentation, your tasks are:
- click on all the checkboxes with colors that correspond to urine spots. You'll notice that the stimulus spot is pre-clicked (you can correct its choice if it didn't pick correctly). Most of the time there will be only one you need to check, but you can click more than one.
In this example image, all the urine spots are marked red, so you'd check the box that has the red border. Leave the stimulus spot checked, too.
- click "Done & Next" to advance to the next image in the sequence

After it finishes cycling through all the images, it will save your results and close the window.

## Processing manually

### Step 1: start Julia with the right project

From within `MyCounterMarkingFolder` created above, start Julia like this:

```sh
MyCounterMarkingFolder$ julia --project
```

### Step 2: load the packages you'll need

From inside Julia, load the packages:

```
julia> using CounterMarking, ImageView, FileIO
```

CounterMarking is this package, used to perform and organize the analysis.
[ImageView](https://github.com/JuliaImages/ImageView.jl) is an image display tool.
[FileIO](https://github.com/JuliaIO/FileIO.jl) loads many different file formats, including images.

### Step 3: load a test image

If you want to use an image in one of the subfolders, use something like

```
julia> img = load("2025-03-15/picture1.png");
```

You'll need to replace the material inside quotes with the actual path and filename of your image.

Alternately, if you want to use the test image that comes with CounterMarking.jl, do the following:

```
julia> img = load(joinpath(pkgdir(CounterMarking), "docs", "src", "assets", "Picture.png"));
```

### Step 4: visualize the image

It's usually a good idea to visually check that what you're working with makes sense:

```
julia> dct = imshow(img);
```

Note that as you move your mouse cursor over the image, a little text box in the lower left updates with the position and information about the color of the pixel under your cursor.
That can occasionally be handy, especially for checking locations of spots.

If all looks as expected, you can close the window.

### Step 5: segment the image

We'll split this image into different regions:

```
julia> seg = segment_image(img)
Pruning segments smaller than 50 pixels
Segmented Image with:
labels map: 1220×2441 Matrix{Int64}
number of labels: 153

julia> dct = randshow(img, seg);
```

After the second command, [`randshow`](@ref), you'll see two images: the original at the top, and the "segmented" image below. This displays the different segments (regions) using a randomly-chosen color, which can be handy for checking how well the analysis did in identifying separate spots. You can alternatively use [`meanshow`](@ref) to show each segment using the average color of all pixels in that segment.

If you Ctrl-click and drag on the image, you'll zoom in on both images. This can be handy for inspecting fine details. Ctrl-double-click takes you back to the full view.

!!! tip
If you don't like how [`segment_image`](@ref) performed, read its documentation to learn about some of the options you have for controlling it.

### Step 6: save the spots to an Excel file

The columns marked "raw" correspond to pixel locations in the original image; the columns marked "UL" come from flipping the image to place the stimulus spot in the Upper Left of the image.
This way of "standardizing" the location makes certain analyses easier.

```
julia> writexlsx("mydata.xlsx", seg)
```

Optionally specify a full directory path, e.g.,

```
julia> writexlsx(raw"C:\Users\me\somefolder\mydata.xlsx", seg)
```

(You don't need `raw` on Linux or Mac, but it is helpful on Windows.)

### Step 7: process a whole directory of images at once

If you have many images in one folder, you can process them all using a single command:

```
julia> process_images("2025-03-15/results.xlsx", "2025-03-15/*.png")
```

### Step 8: create a "density map" of marks across multiple images

If you have many images collected under the same conditions (e.g., with different subject animals but the same stimuli), you can effectively overlay the entire collection of images:

```@index
```
julia> dmap = density_map("2025-03-15/maleU-*.png");

```@autodocs
Modules = [CounterMarking]
julia> dct = imshow(dmap);
```
8 changes: 8 additions & 0 deletions docs/src/reference.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Reference

```@index
```

```@autodocs
Modules = [CounterMarking]
```
40 changes: 0 additions & 40 deletions ext/CounterMarkingImageViewExt.jl

This file was deleted.

13 changes: 11 additions & 2 deletions src/CounterMarking.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,20 @@ using ImageCore
using ImageSegmentation
using ImageMorphology: label_components
using FileIO
using JLD2
using XLSX
using Glob
using Gtk4
using GtkObservables
using ImageView
using Random

export segment_image, stimulus_index, spots, Spot, upperleft
export randshow, meanshow
export writexlsx, process_images
export randshow, meanshow, gui

include("segment.jl")
include("stubs.jl")
include("xlxs.jl")
include("gui.jl")

end
Loading
Loading