|
1 | | -# Image Detection |
| 1 | +# ImageVision |
2 | 2 |
|
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. |
4 | 4 |
|
5 | | -The documentation can be found at [https://hexdocs.pm/image_detection](https://hexdocs.pm/image_detection). |
| 5 | +## Tasks |
6 | 6 |
|
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 | |
8 | 13 |
|
9 | | -`Image Detection` can be installed by adding `image_detection` to your list of dependencies in `mix.exs`: |
| 14 | +## Installation |
10 | 15 |
|
11 | 16 | ```elixir |
12 | 17 | def deps do |
13 | 18 | [ |
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"} |
15 | 26 | ] |
16 | 27 | end |
17 | 28 | ``` |
18 | 29 |
|
| 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 | + |
19 | 79 | ## Configuration |
20 | 80 |
|
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 | +``` |
22 | 88 |
|
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. |
24 | 90 |
|
25 | | -## Accessing the Yolo V8 model |
| 91 | +### Classification serving |
26 | 92 |
|
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: |
28 | 94 |
|
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 | +] |
33 | 111 | ``` |
34 | 112 |
|
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 |
36 | 118 |
|
37 | | -## References |
| 119 | +## Design |
38 | 120 |
|
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. |
43 | 122 |
|
| 123 | +See [CLAUDE.md](https://github.com/elixir-image/image_vision/blob/v0.2.0/CLAUDE.md) for the full design rationale. |
0 commit comments