You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: CHANGELOG.md
+33Lines changed: 33 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,3 +1,36 @@
1
+
## v2.0.0 - 19/04/2026
2
+
3
+
### Added
4
+
* Added model manager with tests
5
+
* Added clustering package, with incremental clustering
6
+
* Added `HNSWIndex` to embeddings/ package
7
+
* Added classification package
8
+
* Added token max length to TextEmbeddingProvider
9
+
* Made tokenizers internal
10
+
* Added SmartScanException
11
+
* Added update method to `IEmbeddingStore`
12
+
* Added save method to `IEmbeddingStore`
13
+
14
+
### Changed
15
+
* Renamed ModelSource with ModelAssetSource and added core/models package
16
+
* Renamed updatePrototype to updatePrototypeEmbedding
17
+
* Renamed filterIds to ids in `IEmbeddingStore` query method and use Set instead of Long
18
+
* Return number of items removed in `IEmbeddingStore` remove method
19
+
* Return number of items added in `IEmbeddingStore` add method
20
+
* Refactor `FileEmbeddingStore` to use a persistent file offset index with in-place updates
21
+
*`FileEmbeddingStore` remove method now only removes items from cache and removal are not automatically persisted, call save method to persist removals. Add and update methods both remain write-though.
22
+
* MinSDK changed to 28 (previously 30).
23
+
* Removed embedBatch method from `IEmbeddingProvider` and added as util method instead in embeddings/ package
24
+
* Remove detector/face package and added contents to detectors/
25
+
26
+
### Fixed
27
+
* Filter ids before running query in `FileEmbeddingStore`
SmartScanSdk is a modular Android SDK that powers the **SmartScan app**. It provides tools for:
23
+
SmartScanSdk is an Android library that powers the **SmartScan app**, providing tools for:
27
24
28
-
* Image & video processing
29
25
* On-device ML inference
30
-
* Semantic media indexing and search
26
+
* Semantic search
27
+
* Indexing
28
+
* Embedding storage
29
+
* Incremental clustering
30
+
* ANN Search / HNSW Index
31
31
* Few shot classification
32
+
* Image & video processing
32
33
* Efficient batch processing
34
+
* Model management
33
35
34
36
35
37
> **Note:** The SDK is designed to be flexible, but its primary use is for the SmartScan app and other apps I am developing. It is also subject to rapid experimental changes.
36
38
37
39
---
38
40
41
+
## Installation
42
+
43
+
Add the JitPack repository to your build file (settings.gradle)
// Requires model to be in raw resources at e.g res/raw/image_encoder_quant_int8.onnx
80
-
val imageEmbedder =ClipImageEmbedder(context, ResourceId(R.raw.image_encoder_quant_int8))
118
+
// downloaded model
119
+
val imageEmbedder =ModelManager.getImageEmbedder(application, ModelName.DINOV2_SMALL)
120
+
121
+
// bundled model
122
+
val imageEmbedder =ClipImageEmbedder(application, ModelAssetSource.Resource(R.raw.clip_image_encoder_quant))
81
123
82
124
val embedding = imageEmbedder.embed(bitmap)
83
125
@@ -87,23 +129,23 @@ val embedding = imageEmbedder.embed(bitmap)
87
129
**Batch Example:**
88
130
89
131
```kotlin
90
-
val images:List<Bitmap>=...
91
-
val embeddings =imageEmbedder.embedBatch(images)
132
+
val images= listOf<Bitmap>()
133
+
val embeddings = embedBatch(context, imageEmbedder, images)
92
134
```
93
135
94
136
### Indexing
95
137
96
-
To get started with indexing media quickly, you can use the provided `ImageIndex` and `VideoIndexer` classes as shown below. You can optionally create your own indexers (including for text related data) by implementing the `BatchProcessor` interface. See docs for more details.
138
+
To get started with indexing media quickly, you can use the provided `ImageIndex` and `VideoIndexer` classes as shown below. You can optionally create your own indexers (including for text related data) by extending the `BatchProcessor`. See [docs](docs/core/processors.md) for more details.
97
139
98
140
#### Image Indexing
99
141
100
142
Index images to enable similarity search. The index is saved as a binary file and managed with a FileEmbeddingStore.
101
-
> **Important**: During indexing the MediaStore Id is used to as the id in the `Embedding` which is stored. This can later be used for retrieval.
143
+
> **Important**: During indexing the MediaStore Id is used to as the id in the `StoredEmbedding` which is stored. This can later be used for retrieval.
102
144
103
145
104
146
```kotlin
105
147
val imageEmbedder =ClipImageEmbedder(context, ResourceId(R.raw.image_encoder_quant_int8))
106
-
val imageStore =FileEmbeddingStore(File(context.filesDir, "image_index.bin"), imageEmbedder.embeddingDim, useCache =false) // cache not needed for indexing
148
+
val imageStore =FileEmbeddingStore(File(context.filesDir, "image_index.bin"), imageEmbedder.embeddingDim)
107
149
val imageIndexer =ImageIndexer(imageEmbedder, context=context, listener =null, store = imageStore) //optionally pass a listener to handle events
108
150
val ids = getImageIds() // placeholder function to get MediaStore image ids
109
151
imageIndexer.run(ids)
@@ -112,10 +154,11 @@ imageIndexer.run(ids)
112
154
#### Video Indexing
113
155
114
156
Index videos to enable similarity search. The index is saved as a binary file and managed with a FileEmbeddingStore.
157
+
> **Important**: During indexing the MediaStore Id is used to as the id in the `StoredEmbedding` which is stored. This can later be used for retrieval.
115
158
116
159
```kotlin
117
160
val imageEmbedder =ClipImageEmbedder(context, ResourceId(R.raw.image_encoder_quant_int8))
118
-
val videoStore =FileEmbeddingStore(File(context.filesDir, "video_index.bin"), imageEmbedder.embeddingDim, useCache =false )
161
+
val videoStore =FileEmbeddingStore(File(context.filesDir, "video_index.bin"), imageEmbedder.embeddingDim )
119
162
val videoIndexer =VideoIndexer(imageEmbedder, context=context, listener =null, store = videoStore, width =ClipConfig.IMAGE_SIZE_X, height =ClipConfig.IMAGE_SIZE_Y)
120
163
val ids = getVideoIds() // placeholder function to get MediaStore video ids
121
164
videoIndexer.run(ids)
@@ -128,91 +171,46 @@ Below shows how to search using both text queries and an image. The returns resu
128
171
#### Text-to-Image Search
129
172
130
173
```kotlin
131
-
val imageStore =FileEmbeddingStore(File(context.filesDir, "image_index.bin"), imageEmbedder.embeddingDim, useCache =false) // cache not needed for indexing
132
-
val imageRetriever =FileEmbeddingRetriever(imageStore)
133
-
val textEmbedder =ClipTextEmbedder(context, ResourceId(R.raw.text_encoder_quant_int8))
174
+
val imageStore =FileEmbeddingStore(File(context.filesDir, "image_index.bin"), imageEmbedder.embeddingDim)
134
175
val query ="my search query"
135
176
val embedding = textEmbedder.embed(query)
136
177
val topK =20
137
178
val similarityThreshold =0.2f
138
-
val results =retriever.query(embedding, topK, similarityThreshold)
val imageStore =FileEmbeddingStore(File(context.filesDir, "image_index.bin"), imageEmbedder.embeddingDim, useCache =false) // cache not needed for indexing
146
-
val imageRetriever =FileEmbeddingRetriever(imageStore)
147
-
val imageEmbedder =ClipImageEmbedder(context, ResourceId(R.raw.image_encoder_quant_int8))
186
+
val imageStore =FileEmbeddingStore(File(context.filesDir, "image_index.bin"), imageEmbedder.embeddingDim)
148
187
val embedding = imageEmbedder.embed(bitmap)
149
188
val topK =20
150
189
val similarityThreshold =0.2f
151
-
val results = retriever.query(embedding, topK, similarityThreshold)
152
-
153
-
```
154
-
155
-
---
156
-
157
-
## Key Structure
158
-
190
+
val results = imageStore.query(embedding, topK, similarityThreshold)
159
191
```
160
-
SmartScanSdk/
161
-
├─ core/ # Essential functionality
162
-
│ ├─ data/ # Data classes and processor interfaces
163
-
│ ├─ embeddings/ # Embedding utilities and file-based stores
164
-
│ ├─ indexers/ # Image and video indexers
165
-
│ ├─ media/ # Media helpers (image/video utils)
166
-
│ └─ processors/ # Batch processing and memory helpers
167
-
│
168
-
└─ ml/ # On-device ML infrastructure + models
169
-
├─ data/ # Model loaders and data classes
170
-
└─ models/ # Base ML models and providers
171
-
└─ providers/
172
-
└─ embeddings/ # Embedding providers
173
-
├─ clip/ # CLIP image & text embedder
174
-
└─ FewShotClassifier.kt # Few-shot classifier
175
-
176
-
├─ build.gradle
177
-
└─ settings.gradle
178
-
```
179
-
180
-
**Notes:**
181
-
182
-
*`core` and `ml` are standalone Gradle modules.
183
-
* Both are set up for **Maven publishing**.
184
-
* The structure replaces the old `core` and `extensions` module in versions ≤1.0.4
185
192
186
-
---
193
+
#### ANN Search (HNSW Index)
187
194
188
-
## Installation
189
-
190
-
Add the JitPack repository to your build file (settings.gradle)
***core** → minimal runtime: shared interfaces, data classes, embeddings, media helpers, processor execution, and efficient batch/concurrent processing.
223
221
***ml** → ML infrastructure and models: model loaders, base models, embedding providers (e.g., CLIP), and few-shot classifiers. Optional or experimental ML-related features can be added under `ml/providers`.
224
222
225
-
This structure replaces the old `core` and `extensions` modules from versions 1.0.4 and below. It provides more clarity and allows consumers to use core non-ML functionality independently. For the most part, the code itself remains unchanged; only the file organization has been updated. Documentation will be updated shortly.
226
-
227
-
---
228
-
229
-
### Constraints
230
-
231
-
* Full index must be loaded in-memory on Android (no native vector DB support).
232
-
* Some users have 40K+ images, so fast processing and loading are critical.
233
-
* Balance speed and memory/CPU use across devices (1GB–8GB memory range).
234
-
* Concurrency improves speed but increases CPU usage, heat, and battery drain.
235
-
236
-
To mitigate the constraints described above, all bulk ml relate processing is done using dynamic, concurrent batch processing via the use of `BatchProcessor`, which uses available memory to self-adjust concurrency between batches
237
-
238
-
### Model
239
-
240
-
Supports models stored locally or bundled in the app.
241
-
242
223
---
243
224
244
225
### Embedding Storage
245
226
246
-
The SDK only provides a file based implementation of `IEmbeddingStore`, `FileEmbeddingStore` (in core) because the following benchmarks below show much better performance for the loading of embeddings
227
+
The SDK only provides a file based implementation of `EmbeddingStore`, `FileEmbeddingStore` (in core) because the following benchmarks below show much better performance for loading embeddings in comparison to Room.
247
228
248
229
#### **Benchmark Summary**
249
230
231
+
File-based memory-mapped loading is significantly faster and scales better.
232
+
250
233
**Real-Life Test Results**
251
234
252
235
| Embeddings | Room Time (ms) | File Time (ms) |
@@ -267,14 +250,13 @@ The SDK only provides a file based implementation of `IEmbeddingStore`, `FileEmb
0 commit comments