Skip to content

Commit 17ce3fd

Browse files
committed
Rennovate to newer models
1 parent 56dff82 commit 17ce3fd

20 files changed

Lines changed: 2490 additions & 415 deletions

.dialyzer_ignore_warnings

Whitespace-only changes.

CHANGELOG.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
# Changelog
22

3-
## Image Detection version 0.1.0
3+
## ImageVision v0.2.0
44

5-
This is the changelog for Image Detection version 0.1.0 released on ____, 2023. For older changelogs please consult the release tag on [GitHub](https://github.com/kipcole9/image/tags)
5+
This is the initial release of `image_vision` (formerly `image_detection`). It covers three vision tasks — classification, segmentation, and object detection — with permissively-licensed default models, automatic weight downloads, and a simple API designed for developers who are not ML experts.
66

7-
### Enhancements
7+
See the [README](https://github.com/elixir-image/image_vision/blob/v0.2.0/README.md) for the full feature overview and quick-start examples.
88

9-
* Initial version.
9+
## image_detection v0.1.0
1010

11+
Initial release of the experimental `image_detection` library. Provided YOLO v8-based object detection, ResNet-50 classification, and Stable Diffusion image generation.

CLAUDE.md

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
# CLAUDE.md — image_vision
2+
3+
## Project identity
4+
5+
`image_vision` (formerly `image_detection`) is a thin, opinionated wrapper around the Elixir ML ecosystem (Bumblebee, Ortex, Nx) that sits next to the [`image`](https://hex.pm/packages/image) library. It exposes three vision tasks — **classification**, **segmentation**, and **object detection** — through a small, friendly API.
6+
7+
## Audience
8+
9+
The target user is a working Elixir developer who:
10+
11+
* Wants to know "is this a cat?" or "how many people are in this photo?" or "give me the alpha mask for the foreground object".
12+
13+
* Is **not** an ML researcher. They should not need to know what a featurizer is, what a serving is, what shape a tensor must be, or which model variant to pick.
14+
15+
* Already uses [`image`](https://hex.pm/packages/image) (`Vix.Vips.Image`) for their image handling and expects this library to feel like a natural extension of it.
16+
17+
## Design priorities (in order)
18+
19+
1. **Developer ease of use.** The default call path is one function with one argument: `Image.Classification.labels(image)`, `Image.Segmentation.segment(image)`, `Image.Detection.detect(image)`. The user should never *have to* pick a model, configure a backend, manage a server, or download weights manually. Sensible defaults handle every step.
20+
21+
2. **Strong, opinionated defaults.** Each task has exactly one default model that is good enough for ~90% of use cases. Defaults are chosen for: permissive licensing (Apache 2.0 / MIT only), reasonable size (<500 MB), broad applicability, and proven quality. Power users can override every default through options, but they should not have to.
22+
23+
3. **Great documentation.** Every public function has the standard `### Arguments / ### Options / ### Returns / ### Examples` template (see `~/.claude/CLAUDE.md`). Module docs explain the *task* in plain language before the API. Doctest examples use real images from `test/support/images/`. Guides exist for the three common workflows (classify, segment, detect) and walk through what's happening at each step.
24+
25+
4. **Tight interop with `image`.** All public functions accept `t:Vix.Vips.Image.t/0` directly and return masks/overlays as `Vix.Vips.Image.t` where appropriate. Tensor conversion happens internally via `Image.to_nx/2` and `Image.from_nx/1`. The user should never need to touch Nx directly to use this library.
26+
27+
5. **Small install footprint.** Avoid eVision (huge — pulls in OpenCV). Use Ortex (~10–20 MB ORT shared library) for ONNX inference. ML deps are `optional: true` so `mix.exs` users opt into the runtime they want.
28+
29+
## What this library is *not*
30+
31+
* Not a research toolkit. If a user wants to swap encoder backbones or fine-tune a head, they should drop down to Bumblebee / Ortex directly.
32+
33+
* Not a generation library. Image generation (Stable Diffusion, etc.) lives elsewhere.
34+
35+
* Not a model zoo. We pick one default per task and document one or two upgrade paths. We do not enumerate every checkpoint on HuggingFace.
36+
37+
## Default models (April 2026)
38+
39+
| Task | Default | License | Runtime |
40+
|---|---|---|---|
41+
| Classification | `facebook/convnext-tiny-224` | Apache 2.0 | Bumblebee |
42+
| Embedding | `facebook/dinov2-base` | Apache 2.0 | Bumblebee |
43+
| Promptable segmentation | `facebook/sam2.1-hiera-tiny` (ONNX) | Apache 2.0 | Ortex |
44+
| Semantic / instance segmentation | `facebook/mask2former-swin-tiny-coco-instance` (ONNX) | MIT | Ortex |
45+
| Object detection | `PekingU/rtdetr_v2_r18vd` (ONNX) | Apache 2.0 | Ortex |
46+
47+
All defaults are permissive-licensed. We never default to AGPL/GPL (e.g. YOLOv8/11) or non-commercial (e.g. SegFormer's NVIDIA SSL, ConvNeXt-V2 Meta weights, DINOv3) models, even when they're more accurate.
48+
49+
## Model weight management
50+
51+
* ONNX weights for Ortex-backed tasks are auto-downloaded on first use from the configured HuggingFace repo, and cached on disk.
52+
53+
* Cache directory is configurable: `config :image_vision, :cache_dir, "/path/to/cache"`. Default falls back to `:filename.basedir(:user_cache, "image_vision")` (XDG-compliant per-user cache).
54+
55+
* Bumblebee-backed tasks defer to Bumblebee's own HF cache (controlled by `BUMBLEBEE_CACHE_DIR` and friends).
56+
57+
* Users who want fully offline operation can pre-download into the cache directory.
58+
59+
## Dependency posture
60+
61+
* `:image` is a hard dependency.
62+
63+
* `:bumblebee`, `:nx`, `:nx_image` are `optional: true` — required only for classification/embedding.
64+
65+
* `:ortex` is `optional: true` — required only for segmentation/detection.
66+
67+
* `:req` is required — used for ONNX weight downloads. Small, ~no transitive cost.
68+
69+
* No `:axon_onnx` (dormant, doesn't handle modern transformer vision graphs).
70+
71+
* No `:evision` (huge OpenCV footprint).
72+
73+
## Code style notes specific to this project
74+
75+
* Module structure follows the existing pattern: each task is its own top-level `Image.*` module (`Image.Classification`, `Image.Segmentation`, `Image.Detection`) so the user sees them as natural neighbours of `Image.Text`, `Image.Shape`, etc. from the `image` library.
76+
77+
* Compile-time gating (`if ImageVision.bumblebee_configured?(), do: defmodule ...`) lets the package compile cleanly when optional deps are absent.
78+
79+
* Use `Nx.select/3` (not `Nx.map/2`) for elementwise tensor operations — `Nx.map` does per-element backend transfers and is unusably slow.
80+
81+
* Always normalise via `NxImage.normalize/3` for ImageNet preprocessing. Don't hand-roll.
82+
83+
* When a serving is required (Bumblebee path), prefer the supervised-process pattern already in `lib/classification.ex`: child spec via `classifier/1`, optional autostart from app config.

README.md

Lines changed: 100 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,123 @@
1-
# Image Detection
1+
# ImageVision
22

3-
Image Detection is an [Image](https://hex.pm/packages/image)-based object detection library based upon the [YOLO V8](https://docs.ultralytics.com) ML model.
3+
`ImageVision` is a simple, opinionated image vision library for Elixir. It sits alongside the [`image`](https://hex.pm/packages/image) library and answers three questions about any image — **what's in it**, **where are the objects**, and **which pixels belong to which object** — with strong defaults and no ML expertise required.
44

5-
The documentation can be found at [https://hexdocs.pm/image_detection](https://hexdocs.pm/image_detection).
5+
## Tasks
66

7-
## Installation
7+
| Task | Module | What it does |
8+
|---|---|---|
9+
| Classification | [`Image.Classification`](https://hexdocs.pm/image_vision/Image.Classification.html) | Labels like `"sports car"` or `"Blenheim spaniel"` |
10+
| Embedding | [`Image.Classification`](https://hexdocs.pm/image_vision/Image.Classification.html) | 768-dim feature vector for similarity search |
11+
| Segmentation | [`Image.Segmentation`](https://hexdocs.pm/image_vision/Image.Segmentation.html) | Pixel masks — promptable ("cut out this object") or class-labeled ("every region in the image") |
12+
| Detection | [`Image.Detection`](https://hexdocs.pm/image_vision/Image.Detection.html) | Bounding boxes with class labels |
813

9-
`Image Detection` can be installed by adding `image_detection` to your list of dependencies in `mix.exs`:
14+
## Installation
1015

1116
```elixir
1217
def deps do
1318
[
14-
{:image_detection_, "~> 0.1.0"}
19+
{:image_vision, "~> 0.2"},
20+
# Nx backend for classification (pick one)
21+
{:exla, "~> 0.9"},
22+
# Required for classification
23+
{:bumblebee, "~> 0.6"},
24+
# Required for segmentation and detection
25+
{:ortex, "~> 0.1"}
1526
]
1627
end
1728
```
1829

30+
## Quick examples
31+
32+
```elixir
33+
# Classification — what's in this image?
34+
iex> puppy = Image.open!("puppy.jpg")
35+
iex> Image.Classification.labels(puppy)
36+
["Blenheim spaniel"]
37+
38+
# Embedding — feature vector for similarity search
39+
iex> Image.Classification.embed(puppy)
40+
#Nx.Tensor<f32[768]>
41+
42+
# Promptable segmentation — mask the object at the centre
43+
iex> %{mask: mask} = Image.Segmentation.segment(puppy)
44+
iex> cutout = Image.Segmentation.apply_mask!(puppy, mask)
45+
46+
# Promptable segmentation — mask the object at a specific point
47+
iex> %{mask: mask} = Image.Segmentation.segment(puppy, prompt: {:point, 320, 240})
48+
49+
# Class-labeled segmentation — every region
50+
iex> street = Image.open!("street.jpg")
51+
iex> segments = Image.Segmentation.segment_panoptic(street)
52+
iex> Enum.map(segments, & &1.label)
53+
["person", "car", "road", "sky"]
54+
55+
# Overlay coloured segments on the original image
56+
iex> overlay = Image.Segmentation.compose_overlay(street, segments)
57+
58+
# Object detection — bounding boxes with labels
59+
iex> detections = Image.Detection.detect(street)
60+
iex> hd(detections)
61+
%{label: "person", score: 0.94, box: {120, 45, 60, 180}}
62+
63+
# Draw bounding boxes on the image
64+
iex> annotated = Image.Detection.draw_bbox_with_labels(detections, street)
65+
```
66+
67+
## Default models
68+
69+
All defaults are permissively licensed (Apache 2.0 / MIT). Models are downloaded automatically on first call and cached to disk — no manual setup needed.
70+
71+
| Task | Model | License | Size |
72+
|---|---|---|---|
73+
| Classification | `facebook/convnext-tiny-224` | Apache 2.0 | ~110 MB |
74+
| Embedding | `facebook/dinov2-base` | Apache 2.0 | ~340 MB |
75+
| Promptable segmentation | `SharpAI/sam2-hiera-tiny-onnx` (SAM 2) | Apache 2.0 | ~150 MB |
76+
| Class-labeled segmentation | `Xenova/detr-resnet-50-panoptic` | Apache 2.0 | ~175 MB |
77+
| Object detection | `onnx-community/rtdetr_r50vd` (RT-DETR) | Apache 2.0 | ~175 MB |
78+
1979
## Configuration
2080

21-
THIS IS AN EXPERIMENTAL LIBRARY. Please do not use in production. Testing is not yet complete.
81+
### Model cache
82+
83+
ONNX models are cached in a per-user directory by default. Override in `config/runtime.exs`:
84+
85+
```elixir
86+
config :image_vision, :cache_dir, "/var/lib/my_app/models"
87+
```
2288

23-
The code is an adaptation of the livecoding demo by Hans Elias (@hansihe) at his [talk](https://www.youtube.com/watch?v=OsxGB6MbA8o&t=1s) at the Elixir Warsaw meetup in March 2023.
89+
The default is `~/Library/Caches/image_vision` on macOS and `~/.cache/image_vision` on Linux.
2490

25-
## Accessing the Yolo V8 model
91+
### Classification serving
2692

27-
The YOLO v8 model is GPL 3.0 licensed and must be built separately. Python 3.10 is required (apparently it won't work with 3.11).
93+
`Image.Classification` runs a supervised Bumblebee serving. It autostarts by default:
2894

29-
```bash
30-
% pip3.10 install ultralytics
31-
% pip3.10 install onnx
32-
% yolo export model=yolov8n.pt format=onnx imgsz=640
95+
```elixir
96+
# config/runtime.exs
97+
config :image_vision, :classifier,
98+
model: {:hf, "facebook/convnext-large-224-22k-1k"}, # larger model
99+
featurizer: {:hf, "facebook/convnext-large-224-22k-1k"},
100+
autostart: true
101+
```
102+
103+
Set `autostart: false` to manage the serving in your own supervision tree:
104+
105+
```elixir
106+
# application.ex
107+
children = [
108+
Image.Classification.classifier(),
109+
Image.Classification.embedder()
110+
]
33111
```
34112

35-
The "n" model is the smallest - we can maybe tolerate a larger one. And we need to find way to host the model or download the `.onnx` from somewhere.
113+
## Guides
114+
115+
- [Classification](https://github.com/elixir-image/image_vision/blob/v0.2.0/guides/classification.md) — classifying images and computing embeddings
116+
- [Segmentation](https://github.com/elixir-image/image_vision/blob/v0.2.0/guides/segmentation.md) — promptable and class-labeled segmentation
117+
- [Detection](https://github.com/elixir-image/image_vision/blob/v0.2.0/guides/detection.md) — bounding-box object detection
36118

37-
## References
119+
## Design
38120

39-
* The original talk by @hansihe is at https://www.youtube.com/watch?v=OsxGB6MbA8o&t=1s
40-
* The original models are stored at https://github.com/ultralytics/assets/releases
41-
* Learning: https://learnopencv.com/ultralytics-yolov8/
42-
* Exploration: https://medium.com/mlearning-ai/yolo-v8-the-real-state-of-the-art-eda6c86a1b90
121+
`ImageVision` is for developers who want answers, not ML configuration. The defaults are chosen for permissive licensing, broad applicability, and reasonable size — not raw benchmark ranking. Power users can override every default via options; most users never need to.
43122

123+
See [CLAUDE.md](https://github.com/elixir-image/image_vision/blob/v0.2.0/CLAUDE.md) for the full design rationale.

guides/classification.md

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
# Image Classification
2+
3+
`Image.Classification` answers "what's in this image?" and "how similar are these two images?".
4+
5+
## Getting labels
6+
7+
The simplest entry point is `labels/2`. It returns a list of human-readable labels for whatever is most prominent in the image:
8+
9+
```elixir
10+
iex> puppy = Image.open!("puppy.jpg")
11+
iex> Image.Classification.labels(puppy)
12+
["Blenheim spaniel"]
13+
14+
iex> car = Image.open!("lamborghini.jpg")
15+
iex> Image.Classification.labels(car)
16+
["sports car", "sport car"]
17+
```
18+
19+
Labels come from the model's training dataset (ImageNet-1k for the default ConvNeXt model — 1000 everyday categories). The default minimum confidence threshold is 0.5; adjust with `:min_score`:
20+
21+
```elixir
22+
iex> Image.Classification.labels(puppy, min_score: 0.8)
23+
["Blenheim spaniel"]
24+
25+
iex> Image.Classification.labels(puppy, min_score: 0.1)
26+
["Blenheim spaniel", "cocker spaniel", "papillon"]
27+
```
28+
29+
## Getting raw predictions with scores
30+
31+
`classify/2` returns the full prediction map including scores:
32+
33+
```elixir
34+
iex> %{predictions: preds} = Image.Classification.classify(puppy)
35+
iex> hd(preds)
36+
%{label: "Blenheim spaniel", score: 0.9327}
37+
```
38+
39+
## Computing embeddings
40+
41+
`embed/2` returns a 768-dimensional feature vector. Vectors for visually similar images will be close together in this space — useful for "find images like this one" or feeding into a custom classifier.
42+
43+
```elixir
44+
iex> v1 = Image.Classification.embed(puppy)
45+
iex> v2 = Image.Classification.embed(other_puppy)
46+
47+
# Cosine similarity: closer to 1.0 = more similar
48+
iex> cos_sim = Nx.dot(v1, v2) / (Nx.norm(v1) * Nx.norm(v2)) |> Nx.to_number()
49+
0.97
50+
```
51+
52+
## Configuration
53+
54+
The classification serving is autostarted when the `:image_vision` application starts. To use a larger, more accurate model, set it in `config/runtime.exs`:
55+
56+
```elixir
57+
config :image_vision, :classifier,
58+
model: {:hf, "facebook/convnext-large-224-22k-1k"},
59+
featurizer: {:hf, "facebook/convnext-large-224-22k-1k"}
60+
```
61+
62+
To manage the serving yourself (e.g. in an umbrella app):
63+
64+
```elixir
65+
# config/runtime.exs
66+
config :image_vision, :classifier, autostart: false
67+
68+
# application.ex
69+
children = [Image.Classification.classifier()]
70+
```
71+
72+
## Dependencies
73+
74+
Classification requires `:bumblebee`, `:nx`, and an Nx backend such as `:exla`. Add to `mix.exs`:
75+
76+
```elixir
77+
{:bumblebee, "~> 0.6"},
78+
{:exla, "~> 0.9"},
79+
```

0 commit comments

Comments
 (0)