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
{{ message }}
Repository navigation
Commit 767bc8f
Browse filesBrowse the repository at this point in the historyBrowse files
Revise PLUGIN_SYSTEM.md to reflect class-based processor discovery (including PROCESSOR_DISCOVERABLE behavior) instead of registry-driven discovery, and document legacy registration compatibility. Clarify GUI control-gating semantics, socket processor expectations, and modern instructions for creating and configuring custom processors.
This repository includes a **plugin-style processor system** that lets the GUI discover and instantiate **DLCLive processors** dynamically.
4
4
5
-
Processors are Python classes (typically subclasses of `dlclive.Processor`) that can optionally:
5
+
Processors are Python classes that subclass `dlclive.processor.Processor`, directly or indirectly, and can optionally:
6
6
7
-
-receive pose estimates during inference (via`process(pose, **kwargs)`),
8
-
-broadcast pose-derived data to external clients (e.g., for experiment control),
9
-
-expose metadata so the GUI can list them and (optionally) build simple parameter UIs.
7
+
-Receive pose estimates during inference through`process(pose, **kwargs)`
8
+
-Broadcast pose-derived data, for example for experiment control
9
+
-Expose metadata so the GUI can list them and support processor configuration
10
10
11
-
> **Security / control note:** The GUI should treat processors as **optional, user-controlled extensions**. In our current design, the GUI exposes an opt-in toggle (recommended label: **“Allow processor control”**) that gates whether processor plugins are instantiated and whether the GUI reads/acts on processor state.
12
-
13
-
---
11
+
> The GUI should treat processors as **optional, user-controlled extensions**.
12
+
> In our current design, the GUI exposes an opt-in toggle, **Allow processor-based control**, that controls whether processor plugins are instantiated and whether the GUI reads or acts on processor state.
14
13
15
14
## Overview
16
15
17
16
### Useful files
18
17
19
-
-`dlclivegui/processors/dlc_processor_socket.py` — Example socket-based processor base class
20
-
-`dlclivegui/processors/examples.py` — Example processor implementations (e.g., One-Euro filter)
21
-
-`dlclivegui/processors/processor_utils.py` — Scanning + instantiation helpers used by the GUI
22
-
23
-
---
18
+
-`dlclivegui/processors/dlc_processor_socket.py`: Example socket-based processor base class
19
+
-`dlclivegui/processors/examples.py`: Example processor implementations, such as One-Euro filtering
20
+
-`dlclivegui/processors/processor_utils.py`: Scanning and instantiation helpers used by the GUI
24
21
25
22
## Architecture
26
23
27
-
### 1) Processor registry (module-level)
24
+
### 1) Processor class discovery
28
25
29
-
A typical processor module defines a registry and a decorator. The decorator registers classes into `PROCESSOR_REGISTRY` using either `PROCESSOR_ID` (if present) or the class name.
26
+
A processor module defines one or more classes that subclass `dlclive.processor.Processor`, directly or indirectly.
30
27
31
-
```python
32
-
# Registry for GUI discovery
33
-
PROCESSOR_REGISTRY= {}
28
+
The GUI discovers eligible processor classes by inspecting the imported module.
Only processor classes defined in the scanned module are included. Processor classes imported from another module are ignored to avoid duplicate entries.
44
+
45
+
Reusable base classes that should not appear in the GUI can explicitly opt out:
46
+
47
+
```python
48
+
classBaseProcessorSocket(Processor):
49
+
PROCESSOR_DISCOVERABLE=False
50
+
```
51
+
52
+
Concrete subclasses of a non-discoverable base class remain discoverable by default.
53
+
51
54
### 2) Processor metadata
52
55
53
56
Each processor class should define metadata attributes to help GUI discovery:
## GUI integration & the “Allow processor control” gate
144
+
Earlier processor modules may still import and use:
156
145
157
-
### Recommended behavior
146
+
```python
147
+
from dlclivegui.processors importPROCESSOR_REGISTRY, register_processor
148
+
```
158
149
159
-
To keep processor behavior explicit and opt-in, the GUI provides a toggle (**Allow processor-based control**) with these semantics:
150
+
The registry and decorator remain temporarily available for compatibility with existing processor modules. However:
160
151
161
-
-**Disabled (default):**
162
-
- the GUI does **not instantiate** any processor plugin;
163
-
- the GUI does **not read or act** on processor state (connections, recording flags, remote commands);
164
-
- inference runs with `processor=None`.
165
-
-*processor code may be imported by the discovery process*
152
+
- GUI discovery does not use `PROCESSOR_REGISTRY`
153
+
- GUI discovery does not call `get_available_processors()`
154
+
- Decorating a class is not required for discovery
155
+
- An existing decorated class remains discoverable because the decorator returns the original class
166
156
167
-
-**Enabled:**
168
-
- the GUI may instantiate the selected processor and (optionally) reflect processor state in the UI.
169
-
- the processor will be used by the `DLCLive` instance during inference.
157
+
New processor modules should rely on subclass discovery instead of defining a registry or discovery function.
170
158
171
-
This lets users decide whether they want to run processor plugins and whether those plugins may influence UI/recording behavior.
159
+
## GUI integration & enabling custom processors
172
160
173
-
> We recommend users to follow this design patter when designing their own processors
174
-
> to help ensure predictable behavior and clear user control over processor-based features.<br>
175
-
> **We are not responsible for any unexpected behavior caused by custom processors,**
176
-
> **and the examples are provided as-is with no guarantees.**
161
+
### Recommended behavior
177
162
178
-
---
163
+
To keep processor behavior explicit and opt-in, the GUI provides an **Allow processor-based control** toggle with these effects:
179
164
180
-
## Socket-based processors (example base class)
165
+
-**Disabled by default:**
166
+
- The GUI does **not instantiate** any processor plugin
167
+
- The GUI does **not read or act** on processor state, such as connections, recording flags, or remote commands
168
+
- Inference runs with `processor=None`
169
+
- Processor code may still be imported by the discovery process
181
170
182
-
The built-in `BaseProcessorSocket` (in `dlc_processor_socket.py`) demonstrates a simple approach for:
171
+
-**Enabled:**
172
+
- The GUI may instantiate the selected processor and reflect processor state in the UI
173
+
- The processor is used by the `DLCLive` instance during inference
183
174
184
-
- accepting multiple clients,
185
-
- receiving control messages (e.g., start/stop recording),
186
-
- broadcasting payloads to connected clients,
187
-
- cleaning up reliably on shutdown.
175
+
This lets users decide whether they want to run processor plugins and whether those plugins may influence UI or recording behavior.
188
176
189
-
### Key points
177
+
> We recommend that users follow this design pattern when creating processors to help ensure predictable behavior and clear user control over processor-based features.
178
+
> **We are not responsible for unexpected behavior caused by custom processors, and the examples are provided as-is with no guarantees.**
190
179
191
-
- Socket server is optional: `BaseProcessorSocket` supports `start_server(...)`.
192
-
- Connections are tracked in `self.conns`.
193
-
-`broadcast(payload)` sends to all clients; failing clients are dropped.
194
-
-`stop()` closes clients and listener, joins threads, and attempts to wake `accept()` during shutdown.
180
+
## Socket-based processors
195
181
196
-
> **Tip:** If you publish processors for others to use, keep module import side-effect free (define classes/functions only).
182
+
The built-in `BaseProcessorSocket` in `dlc_processor_socket.py` demonstrates a simple approach for:
197
183
198
-
---
184
+
- Accepting multiple clients
185
+
- Receiving control messages, such as start and stop recording,
186
+
- Broadcasting payloads to connected clients,
187
+
- Cleaning up reliably on shutdown.
199
188
200
-
## Adding a new processor
189
+
`BaseProcessorSocket` is a reusable base class and is not shown as a selectable processor in the GUI:
201
190
202
-
1) Create a new module file in a processor folder (or inside `dlclivegui/processors/`).
191
+
```python
192
+
PROCESSOR_DISCOVERABLE=False
193
+
```
203
194
204
-
2) Define a processor class and metadata:
195
+
Concrete subclasses defined in processor modules are discovered normally.
205
196
206
-
```python
207
-
from dlclive import Processor
208
-
from dlclivegui.processors import register_processor, PROCESSOR_REGISTRY
- The socket server is optional: `BaseProcessorSocket` supports `start_server(...)`.
200
+
- Connections are tracked in `self.conns`.
201
+
-`broadcast(payload)` sends to all clients, and failing clients are dropped.
202
+
-`stop()` closes clients and the listener, joins threads, and attempts to wake `accept()` during shutdown.
217
203
218
-
defprocess(self, pose, **kwargs):
219
-
# Do something with pose
220
-
return pose
204
+
> **Tip:** If you publish processors for others to use, keep module imports side-effect free where possible. Define classes and functions during import, and initialize sockets, hardware, or other resources when the processor is instantiated.
1. Create a new module file in a processor folder or inside `dlclivegui/processors/`.
209
+
210
+
2. Define a processor class and metadata:
211
+
```python
212
+
from dlclive.processor import Processor
213
+
214
+
classMyNewProcessor(Processor):
215
+
PROCESSOR_NAME="My New Processor"
216
+
PROCESSOR_DESCRIPTION="Does something useful"
217
+
PROCESSOR_PARAMS= {
218
+
"my_param": {
219
+
"type": "bool",
220
+
"default": True,
221
+
"description": "Enable optional behavior",
222
+
}
230
223
}
231
-
for name, clsinPROCESSOR_REGISTRY.items()
232
-
}
233
-
```
234
224
235
-
3) Refresh processors in the GUI, select your processor, and start inference (with processor control enabled if required).
225
+
def__init__(self, my_param: bool=True):
226
+
super().__init__()
227
+
self.my_param = my_param
228
+
229
+
defprocess(self, pose, **kwargs):
230
+
# Do something with pose
231
+
return pose
232
+
```
233
+
No registration decorator, module-level registry, or`get_available_processors()` function is required.
236
234
237
-
---
235
+
3. Refresh processors in the GUI, select your processor, and start inference with processor control enabled if required.
238
236
239
237
## Parameter schema types
240
238
241
239
Supported `PROCESSOR_PARAMS` types:
242
240
243
-
-`"bool"` — checkbox
244
-
-`"int"` — integer input
245
-
-`"float"` — float input
246
-
-`"str"` — string input
247
-
-`"bytes"` — string that gets encoded to bytes
248
-
-`"tuple"` — tuple (e.g., `(host, port)`)
249
-
-`"dict"` — dictionary
250
-
-`"list"` — list
241
+
-`"bool"`: checkbox
242
+
-`"int"`: integer input
243
+
-`"float"`:floatinput
244
+
-`"str"`: string input
245
+
-`"bytes"`: string that gets encoded to bytes
246
+
-`"tuple"`: tuple, for example `(host, port)`
247
+
-`"dict"`: dictionary
248
+
-`"list"`:list
251
249
252
-
---
250
+
The processor constructor remains the base definition of accepted arguments and values.
253
251
254
252
## Notes on external processors
255
253
256
-
External processors are arbitrary Python code. Only load processors you trust.
257
-
258
-
259
-
260
-
## License
254
+
External processors are arbitrary Python code and are imported during discovery. Only load processors you trust.
261
255
262
-
This project is distributed under its project license.
263
-
See `LICENSE` in the repository.
256
+
Where possible, processor modules should avoid import-time side effects and initialize files, sockets, hardware, or other resources only when the processor is instantiated.
0 commit comments