interactive-pipe 0.8.8__tar.gz → 0.8.10__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {interactive_pipe-0.8.8/src/interactive_pipe.egg-info → interactive_pipe-0.8.10}/PKG-INFO +155 -118
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.10}/pyproject.toml +33 -8
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.10}/readme.md +151 -110
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.10}/src/interactive_pipe/__init__.py +25 -15
- interactive_pipe-0.8.10/src/interactive_pipe/core/backend.py +12 -0
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.10}/src/interactive_pipe/core/cache.py +2 -6
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.10}/src/interactive_pipe/core/context.py +17 -17
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.10}/src/interactive_pipe/core/engine.py +10 -17
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.10}/src/interactive_pipe/core/filter.py +23 -42
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.10}/src/interactive_pipe/core/graph.py +65 -23
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.10}/src/interactive_pipe/core/pipeline.py +44 -52
- interactive_pipe-0.8.10/src/interactive_pipe/core/signature.py +10 -0
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.10}/src/interactive_pipe/data_objects/audio.py +16 -14
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.10}/src/interactive_pipe/data_objects/curves.py +55 -73
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.10}/src/interactive_pipe/data_objects/data.py +18 -22
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.10}/src/interactive_pipe/data_objects/image.py +11 -5
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.10}/src/interactive_pipe/data_objects/parameters.py +7 -12
- interactive_pipe-0.8.10/src/interactive_pipe/data_objects/table.py +328 -0
- interactive_pipe-0.8.10/src/interactive_pipe/graphical/gradio_control.py +201 -0
- interactive_pipe-0.8.10/src/interactive_pipe/graphical/gradio_gui.py +651 -0
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.10}/src/interactive_pipe/graphical/gui.py +64 -35
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.10}/src/interactive_pipe/graphical/mpl_control.py +56 -37
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.10}/src/interactive_pipe/graphical/mpl_gui.py +41 -41
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.10}/src/interactive_pipe/graphical/mpl_window.py +23 -10
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.10}/src/interactive_pipe/graphical/nb_control.py +52 -36
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.10}/src/interactive_pipe/graphical/nb_gui.py +8 -8
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.10}/src/interactive_pipe/graphical/qt_control.py +153 -99
- interactive_pipe-0.8.10/src/interactive_pipe/graphical/qt_gui.py +981 -0
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.10}/src/interactive_pipe/graphical/window.py +10 -7
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.10}/src/interactive_pipe/headless/control.py +87 -44
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.10}/src/interactive_pipe/headless/keyboard.py +27 -15
- interactive_pipe-0.8.10/src/interactive_pipe/headless/panel.py +141 -0
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.10}/src/interactive_pipe/headless/pipeline.py +116 -84
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.10}/src/interactive_pipe/helper/choose_backend.py +7 -5
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.10}/src/interactive_pipe/helper/control_abbreviation.py +62 -72
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.10}/src/interactive_pipe/helper/filter_decorator.py +16 -14
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.10}/src/interactive_pipe/helper/keyword_args_analyzer.py +15 -26
- interactive_pipe-0.8.10/src/interactive_pipe/helper/pipeline_decorator.py +71 -0
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.10/src/interactive_pipe.egg-info}/PKG-INFO +155 -118
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.10}/src/interactive_pipe.egg-info/SOURCES.txt +5 -1
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.10}/src/interactive_pipe.egg-info/requires.txt +4 -7
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.10}/test/test_context.py +47 -35
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.10}/test/test_context_compatibility.py +11 -24
- interactive_pipe-0.8.10/test/test_control_abbreviations.py +83 -0
- interactive_pipe-0.8.10/test/test_controller.py +112 -0
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.10}/test/test_core.py +1 -3
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.10}/test/test_core_exceptions.py +9 -10
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.10}/test/test_curves.py +2 -4
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.10}/test/test_data_objects_exceptions.py +7 -7
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.10}/test/test_decorator.py +2 -1
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.10}/test/test_edge_cases.py +17 -6
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.10}/test/test_engine.py +6 -13
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.10}/test/test_filter.py +1 -3
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.10}/test/test_graphical_exceptions.py +33 -29
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.10}/test/test_headless.py +7 -11
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.10}/test/test_headless_exceptions.py +4 -5
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.10}/test/test_image.py +2 -2
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.10}/test/test_legacy_warnings.py +17 -39
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.10}/test/test_mutable_defaults.py +1 -0
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.10}/test/test_recorder.py +7 -6
- interactive_pipe-0.8.10/test/test_table.py +397 -0
- interactive_pipe-0.8.8/src/interactive_pipe/core/signature.py +0 -18
- interactive_pipe-0.8.8/src/interactive_pipe/graphical/gradio_control.py +0 -168
- interactive_pipe-0.8.8/src/interactive_pipe/graphical/gradio_gui.py +0 -428
- interactive_pipe-0.8.8/src/interactive_pipe/graphical/qt_gui.py +0 -650
- interactive_pipe-0.8.8/src/interactive_pipe/helper/pipeline_decorator.py +0 -64
- interactive_pipe-0.8.8/test/test_control_abbreviations.py +0 -125
- interactive_pipe-0.8.8/test/test_controller.py +0 -33
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.10}/LICENSE +0 -0
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.10}/setup.cfg +0 -0
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.10}/src/interactive_pipe/core/__init__.py +0 -0
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.10}/src/interactive_pipe/data_objects/__init__.py +0 -0
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.10}/src/interactive_pipe/graphical/__init__.py +0 -0
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.10}/src/interactive_pipe/headless/__init__.py +0 -0
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.10}/src/interactive_pipe/helper/__init__.py +0 -0
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.10}/src/interactive_pipe/helper/_private.py +0 -0
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.10}/src/interactive_pipe.egg-info/dependency_links.txt +0 -0
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.10}/src/interactive_pipe.egg-info/top_level.txt +0 -0
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.10}/test/test_cache.py +0 -0
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.10}/test/test_parameters.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: interactive_pipe
|
|
3
|
-
Version: 0.8.
|
|
3
|
+
Version: 0.8.10
|
|
4
4
|
Summary: Library to create flexible interactive image processing pipelines and automatically add a graphical user interface without knowing anything about GUI coding!
|
|
5
5
|
Author-email: Balthazar Neveu <balthazarneveu@gmail.com>
|
|
6
6
|
Project-URL: Homepage, https://github.com/balthazarneveu/interactive_pipe
|
|
@@ -15,13 +15,6 @@ Requires-Dist: matplotlib>=3.5.3
|
|
|
15
15
|
Requires-Dist: numpy>=1.21.6
|
|
16
16
|
Requires-Dist: Pillow>=9.0.1
|
|
17
17
|
Requires-Dist: PyYAML>=5.4.1
|
|
18
|
-
Requires-Dist: PyQt6>=6.5.2
|
|
19
|
-
Requires-Dist: PyQt6_sip>=13.5.2
|
|
20
|
-
Requires-Dist: opencv_python_headless>=4.8.1.78
|
|
21
|
-
Requires-Dist: pytest>=6.2.5
|
|
22
|
-
Requires-Dist: ipywidgets>=7.7.1
|
|
23
|
-
Requires-Dist: pandas
|
|
24
|
-
Requires-Dist: gradio
|
|
25
18
|
Provides-Extra: qt6
|
|
26
19
|
Requires-Dist: PyQt6>=6.5.2; extra == "qt6"
|
|
27
20
|
Requires-Dist: PyQt6_sip>=13.5.2; extra == "qt6"
|
|
@@ -41,6 +34,9 @@ Requires-Dist: pytest>=6.2.5; extra == "full"
|
|
|
41
34
|
Requires-Dist: ipywidgets>=7.7.1; extra == "full"
|
|
42
35
|
Requires-Dist: pandas; extra == "full"
|
|
43
36
|
Requires-Dist: gradio; extra == "full"
|
|
37
|
+
Provides-Extra: dev
|
|
38
|
+
Requires-Dist: ruff>=0.9.0; extra == "dev"
|
|
39
|
+
Requires-Dist: pyright>=1.1.390; extra == "dev"
|
|
44
40
|
Dynamic: license-file
|
|
45
41
|
|
|
46
42
|
|
|
@@ -61,7 +57,7 @@ Dynamic: license-file
|
|
|
61
57
|
- Develop an algorithm while debugging visually with plots, while checking robustness & continuity to parameters change.
|
|
62
58
|
- Magically create a graphical interface to easily demonstrate a concept or simply tune your algorithm.
|
|
63
59
|
|
|
64
|
-
|
|
60
|
+
❤️ **You do not need to learn anything about making a graphical user interface (GUI)** ❤️
|
|
65
61
|
|
|
66
62
|
|
|
67
63
|
### Examples
|
|
@@ -86,13 +82,13 @@ pip install -e ".[full]"
|
|
|
86
82
|
----------------------
|
|
87
83
|
|
|
88
84
|
### Who is this for?
|
|
89
|
-
####
|
|
85
|
+
#### 🎓 Scientific education
|
|
90
86
|
- Demonstrate concepts by interacting with curves / images.
|
|
91
87
|
- Easy integration in Jupyter Notebooks (popular with Google Collab)
|
|
92
|
-
####
|
|
88
|
+
#### 🎁 DIY hobbyist
|
|
93
89
|
- You can also use the declarative nature of interactive pipe to make a graphical interface in a few lines of codes.
|
|
94
90
|
- For instance, it is possible to code a jukebox for a toddler on a RaspberryPi.
|
|
95
|
-
####
|
|
91
|
+
#### 📷 Engineering *(computer vision, image/signal processing)*
|
|
96
92
|
- While prototyping an algorithm or testing a neural network, you may be interested in making small experiments with visual checks. Instead of making a draft quick & dirty code that you'll never keep, you can use interactive pipe to show your team how your library works. A visual demo is always good, it shows that the algorithm is not buggy if anyone can play with it.
|
|
97
93
|
- Tune your algorithms with a graphical interface and save your parameters for later batch processing.
|
|
98
94
|
- Ready to batch under the hood, the processing engine can be ran without GUI (therefore allowing to use the same code for tuning & batch processing if needed).
|
|
@@ -102,31 +98,30 @@ pip install -e ".[full]"
|
|
|
102
98
|
|
|
103
99
|
|
|
104
100
|
----------------------
|
|
105
|
-
## :scroll: Terminology
|
|
106
101
|
|
|
107
|
-
|
|
102
|
+
## 📜 Features
|
|
108
103
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
**Version 0.8.8**
|
|
104
|
+
**Version 0.8.10**
|
|
112
105
|
- Modular multi-image processing filters
|
|
113
106
|
- Declarative: Easily make graphical user interface without having to learn anything about pyQt or matplotlib
|
|
114
107
|
- Support in jupyter notebooks
|
|
115
108
|
- Tuning sliders & check buttons with a GUI
|
|
116
109
|
- Cache intermediate results in RAM for much faster processing
|
|
117
110
|
- `KeyboardControl` : no slider on UI but exactly the same internal mechanism, update on key press.
|
|
118
|
-
- Support Curve plots (2D signals)
|
|
111
|
+
- Support Curve plots (2D signals).
|
|
119
112
|
- Gradio backend (+allows sharing with others).
|
|
120
113
|
- Audio support in Gradio (live audio or display several players by returning 1D numpy arrays)
|
|
121
114
|
- Circular sliders for Qt Backend
|
|
122
115
|
- Text prompt (`free_text=("Hello world!", None),`)
|
|
123
116
|
- TimeControl (possibility to play/pause time using an incrementing timer)
|
|
124
|
-
-
|
|
125
|
-
-
|
|
117
|
+
- 🆕 **Context API**: Direct access to shared context across filters via `get_context()`, `context`, `layout`, `audio`
|
|
118
|
+
- 🆕 MIT License
|
|
119
|
+
- 🆕 Panel **Panel System**: group the sliders in defined panels. allows fine control on GUI layout.
|
|
120
|
+
- 🆕 Support Table outputs
|
|
126
121
|
|
|
127
122
|
|
|
128
123
|
|
|
129
|
-
####
|
|
124
|
+
#### ⌨️ Keyboard shortcuts
|
|
130
125
|
Shortcuts while using the GUI (QT & matplotlib backends)
|
|
131
126
|
|
|
132
127
|
- `F1` to show the help shortcuts in the terminal
|
|
@@ -142,27 +137,31 @@ Shortcuts while using the GUI (QT & matplotlib backends)
|
|
|
142
137
|
|
|
143
138
|
# Status
|
|
144
139
|
- supported backends
|
|
145
|
-
-
|
|
146
|
-
-
|
|
147
|
-
-
|
|
148
|
-
-
|
|
140
|
+
- ✅ `gui='qt'` pyQt/pySide
|
|
141
|
+
- ✅ `gui='mpl'` matplotlib
|
|
142
|
+
- ✅ `gui='nb'` ipywidget for jupyter notebooks
|
|
143
|
+
- ✅ `gui='gradio'` gradio wrapping (+use `share_gradio_app=True` to share your app with others)
|
|
149
144
|
- tested platforms
|
|
150
|
-
-
|
|
151
|
-
-
|
|
152
|
-
-
|
|
145
|
+
- ✅ Linux (Ubuntu / KDE Neon)
|
|
146
|
+
- ✅ RapsberryPi
|
|
147
|
+
- ✅ On google collab (use `gui='nb'`)
|
|
153
148
|
|
|
154
149
|
|
|
155
150
|
|
|
156
|
-
|
|
|
151
|
+
| ⭐ | *PyQt / PySide* | *Matplotlib* | *Jupyter notebooks including Google collab* | *Gradio* |
|
|
157
152
|
|:-----: |:-----:|:------:|:----: |:----: |
|
|
158
153
|
| Backend name | `qt` | `mpl` | `nb`| `gradio` |
|
|
159
154
|
| Preview |  |  |  | |
|
|
160
|
-
| Plot curves |
|
|
161
|
-
|
|
|
162
|
-
| Keyboard shortcuts / fullscreen|
|
|
163
|
-
| Audio support |
|
|
164
|
-
| Image buttons|
|
|
165
|
-
| Circular slider|
|
|
155
|
+
| Plot curves | ✅ | ✅ | ✅ | ✅|
|
|
156
|
+
| Change layout | ✅ | ✅ | ✅ | ➖ |
|
|
157
|
+
| Keyboard shortcuts / fullscreen| ✅ | ✅ | ➖ | ➖ |
|
|
158
|
+
| Audio support | ✅ | ➖ | ➖ | ✅ |
|
|
159
|
+
| Image buttons| ✅ | ➖ | ➖ | ➖ |
|
|
160
|
+
| Circular slider| ✅ | ➖ | ➖ | ➖ |
|
|
161
|
+
| Collapsible Panels | ✅ | ➖ | ➖ | ✅ |
|
|
162
|
+
| Animations | ✅ | ➖ | ➖ | ➖ |
|
|
163
|
+
| Panels with sliders | ✅ | ➖ | ➖ | ✅ |
|
|
164
|
+
| Tables | ✅ | ✅ | ✅ | ✅|
|
|
166
165
|
|
|
167
166
|
|
|
168
167
|
|
|
@@ -189,7 +188,7 @@ Shortcuts while using the GUI (QT & matplotlib backends)
|
|
|
189
188
|
|
|
190
189
|
|
|
191
190
|
|
|
192
|
-
##
|
|
191
|
+
## 🚀 Ultra short code
|
|
193
192
|
|
|
194
193
|
|
|
195
194
|
*Since ipywidgets in notebooks are supported, the tutorial is also available in a [google collab notebook](https://colab.research.google.com/drive/1PZn8P_5TABVCugT3IcLespvZG-gxnFbO?usp=sharing)*
|
|
@@ -202,9 +201,9 @@ By design:
|
|
|
202
201
|
- keyword arguments are the parameters which can be later turned into interactive widgets.
|
|
203
202
|
- output buffers are simply returned like you'd do in a regular function.
|
|
204
203
|
|
|
205
|
-
We use the `@interactive()`
|
|
204
|
+
We use the `@interactive()` decorator to specify which parameters become interactive widgets. Parameters defined in the decorator as **tuple/list** will become graphical interactive widgets (slider, tick box, dropdown menu).
|
|
206
205
|
|
|
207
|
-
The syntax to turn keyword arguments into sliders is pretty simple
|
|
206
|
+
The syntax to turn keyword arguments into sliders is pretty simple: `@interactive(param=(default, [min, max], name))` will create a float slider for instance.
|
|
208
207
|
|
|
209
208
|
Finally, we need to the glue to combo these filters. This is where the sample_pipeline function comes in.
|
|
210
209
|
|
|
@@ -215,8 +214,11 @@ By decorating it with `@interactive_pipeline(gui="qt")`, calling this function w
|
|
|
215
214
|
from interactive_pipe import interactive, interactive_pipeline
|
|
216
215
|
import numpy as np
|
|
217
216
|
|
|
218
|
-
@interactive(
|
|
219
|
-
|
|
217
|
+
@interactive(
|
|
218
|
+
coeff=(1., [0.5, 2.], "exposure"),
|
|
219
|
+
bias=(0., [-0.2, 0.2])
|
|
220
|
+
)
|
|
221
|
+
def exposure(img, coeff=1., bias=0.):
|
|
220
222
|
'''Applies a multiplication by coeff & adds a constant bias to the image'''
|
|
221
223
|
# In the GUI, the coeff will be labelled as "exposure".
|
|
222
224
|
# As the default tuple provided to bias does not end up with a string,
|
|
@@ -224,15 +226,15 @@ def exposure(img, coeff = (1., [0.5, 2.], "exposure"), bias=(0., [-0.2, 0.2])):
|
|
|
224
226
|
return img*coeff + bias
|
|
225
227
|
|
|
226
228
|
|
|
227
|
-
@interactive()
|
|
228
|
-
def black_and_white(img, bnw=
|
|
229
|
+
@interactive(bnw=(True, "black and white"))
|
|
230
|
+
def black_and_white(img, bnw=True):
|
|
229
231
|
'''Averages the 3 color channels (Black & White) if bnw=True
|
|
230
232
|
'''
|
|
231
233
|
# Special mention for booleans: using a tuple like (True,) allows creating the tick box.
|
|
232
234
|
return np.repeat(np.expand_dims(np.average(img, axis=-1), -1), img.shape[-1], axis=-1) if bnw else img
|
|
233
235
|
|
|
234
|
-
@interactive()
|
|
235
|
-
def blend(img0, img1, blend_coeff=
|
|
236
|
+
@interactive(blend_coeff=(0.5, [0., 1.]))
|
|
237
|
+
def blend(img0, img1, blend_coeff=0.5):
|
|
236
238
|
'''Blends between two image.
|
|
237
239
|
- when blend_coeff=0 -> image 0 [slider to the left ]
|
|
238
240
|
- when blend_coeff=1 -> image 1 [slider to the right]
|
|
@@ -252,25 +254,25 @@ if __name__ == '__main__':
|
|
|
252
254
|
sample_pipeline(input_image)
|
|
253
255
|
|
|
254
256
|
```
|
|
255
|
-
|
|
257
|
+
❤️ This code shall display you a GUI with three images. The middle one is the result of the blend
|
|
256
258
|
|
|
257
259
|
|
|
258
260
|
|
|
259
261
|
Notes:
|
|
260
|
-
- If you write `def blend(img0, img1, blend_coeff=0.5):`, blend_coeff will simply not be a slider on the GUI
|
|
261
|
-
- If you write
|
|
262
|
-
- If you write
|
|
262
|
+
- If you write `@interactive()` with `def blend(img0, img1, blend_coeff=0.5):`, blend_coeff will simply not be a slider on the GUI.
|
|
263
|
+
- If you write `@interactive(blend_coeff=[0., 1.])` in the decorator, blend_coeff will be a slider initialized to 0.5
|
|
264
|
+
- If you write `@interactive(bnw=(True, "black and white", "k"))`, the checkbox will disappear and be replaced by a keypress event (press `k` to enable/disable black & white)
|
|
263
265
|
|
|
264
266
|
-----------
|
|
265
|
-
##
|
|
267
|
+
## 💡 Some more tips
|
|
266
268
|
|
|
267
269
|
```python
|
|
268
|
-
from interactive_pipe import interactive, interactive_pipeline
|
|
270
|
+
from interactive_pipe import interactive, interactive_pipeline, context
|
|
269
271
|
import numpy as np
|
|
270
272
|
|
|
271
273
|
COLOR_DICT = {"red": [1., 0., 0.], "green": [0., 1.,0.], "blue": [0., 0., 1.], "gray": [0.5, 0.5, 0.5]}
|
|
272
|
-
@interactive()
|
|
273
|
-
def generate_flat_colored_image(color_choice=
|
|
274
|
+
@interactive(color_choice=["red", "green", "blue", "gray"])
|
|
275
|
+
def generate_flat_colored_image(color_choice="red"):
|
|
274
276
|
'''Generate a constant colorful image
|
|
275
277
|
'''
|
|
276
278
|
flat_array = np.array(COLOR_DICT.get(color_choice)) * np.ones((64, 64, 3))
|
|
@@ -282,15 +284,15 @@ def generate_flat_colored_image(color_choice=["red", "green", "blue", "gray"], c
|
|
|
282
284
|
- The `color_choice` list will be turned into a nice dropdown menu. Default value here will be red as this is the first element of the list!
|
|
283
285
|
----------
|
|
284
286
|
|
|
285
|
-
|
|
286
|
-
Yes, using the
|
|
287
|
-
- Check carefully how we stored the image average of the flat image in
|
|
287
|
+
💡 Can filters communicate together?
|
|
288
|
+
Yes, using the `context` proxy from `interactive_pipe`.
|
|
289
|
+
- Check carefully how we stored the image average of the flat image in context.
|
|
288
290
|
- This value will be available to other filters.
|
|
289
291
|
`special_image_slice` is going to use that value to set the half bottom image to dark in case the average is high.
|
|
290
292
|
|
|
291
293
|
```python
|
|
292
|
-
|
|
293
|
-
|
|
294
|
+
def special_image_slice(img):
|
|
295
|
+
out_img = img.copy()
|
|
294
296
|
if context["avg"] > 0.4:
|
|
295
297
|
out_img[out_img.shape[0]//2:, ...] = 0.
|
|
296
298
|
return out_img
|
|
@@ -299,8 +301,8 @@ def special_image_slice(img, context={}):
|
|
|
299
301
|
|
|
300
302
|
|
|
301
303
|
```python
|
|
302
|
-
@interactive()
|
|
303
|
-
def switch_image(img1, img2, img3, image_index=
|
|
304
|
+
@interactive(image_index=(0, [0, 2], None, ["pagedown", "pageup", True]))
|
|
305
|
+
def switch_image(img1, img2, img3, image_index=0):
|
|
304
306
|
'''Switch between 3 images
|
|
305
307
|
'''
|
|
306
308
|
return [img1, img2, img3][image_index]
|
|
@@ -308,8 +310,8 @@ def switch_image(img1, img2, img3, image_index=(0, [0, 2], None, ["pagedown", "p
|
|
|
308
310
|
Note that you can create a filter to switch between several images. In `["pagedown", "pageup", True]`, True means that the image_index will wrap around. (it will return to 0 as soon as it goes above the maximum value of 2).
|
|
309
311
|
|
|
310
312
|
```python
|
|
311
|
-
@interactive()
|
|
312
|
-
def black_top_image_slice(img, top_slice_black=
|
|
313
|
+
@interactive(top_slice_black=(True, "special", "k"))
|
|
314
|
+
def black_top_image_slice(img, top_slice_black=True):
|
|
313
315
|
out_img = img.copy()
|
|
314
316
|
if top_slice_black:
|
|
315
317
|
out_img[:out_img.shape[0]//2, ...] = 0.
|
|
@@ -332,6 +334,40 @@ if __name__ == '__main__':
|
|
|
332
334
|
|
|
333
335
|
### Release Notes
|
|
334
336
|
|
|
337
|
+
|
|
338
|
+
#### Version 0.8.10 (March 2026)
|
|
339
|
+
- Bugfix for jupyter notebooks backends.
|
|
340
|
+
#### Version 0.8.9 (February 2026)
|
|
341
|
+
|
|
342
|
+
**New Features:**
|
|
343
|
+
- **Panel System**: Control panel layout and organization
|
|
344
|
+
- Flexible panel positioning (left, right, top, bottom)
|
|
345
|
+
- Detached control panels for separate windows
|
|
346
|
+
- Nested panels and subpanels support
|
|
347
|
+
- Grouped controls within panels
|
|
348
|
+
- Improved spacing and borders for better visual organization
|
|
349
|
+
- Full backend support (Qt, Gradio, matplotlib, notebook)
|
|
350
|
+
|
|
351
|
+
- **Table Data Type**: Display tabular data natively
|
|
352
|
+
- Core Table functionality without external dependencies
|
|
353
|
+
- Optional pandas DataFrame support for advanced use cases
|
|
354
|
+
- Rendering support across all backends (Qt, Gradio, matplotlib)
|
|
355
|
+
- Headerless tables option
|
|
356
|
+
|
|
357
|
+
- **TimeControl Enhancements**: Better time-based parameter control
|
|
358
|
+
- Improved slider help display
|
|
359
|
+
- Additional demos showcasing time-based animations
|
|
360
|
+
|
|
361
|
+
**API Improvements:**
|
|
362
|
+
- Context support at pipeline initialization
|
|
363
|
+
- Backend selection via enum (string format still supported)
|
|
364
|
+
- Graph visualization for GUI pipelines (press `G`)
|
|
365
|
+
|
|
366
|
+
**Deprecations:**
|
|
367
|
+
- Inline syntax deprecated (use decorator syntax instead)
|
|
368
|
+
- `output_canvas` argument removed
|
|
369
|
+
- Context aliases (`global_params`, `states` etc...) deprecated at initialization
|
|
370
|
+
|
|
335
371
|
#### Version 0.8.8 (January 2026)
|
|
336
372
|
|
|
337
373
|
**New Features:**
|
|
@@ -357,27 +393,34 @@ if __name__ == '__main__':
|
|
|
357
393
|
- Fixed pytest failures for optional dependencies in CI
|
|
358
394
|
- Fixed various edge cases in error handling
|
|
359
395
|
|
|
360
|
-
**Migration
|
|
361
|
-
- Use `global_params=SharedContext.injected()` to let code interpreters know that there's no need to pass this parameter.
|
|
396
|
+
**Migration from old `context={}` or `global_params={}` patterns:**
|
|
362
397
|
|
|
363
398
|
```python
|
|
364
|
-
#
|
|
365
|
-
|
|
366
|
-
global_params["brightness"] = brightness
|
|
367
|
-
return img * brightness
|
|
399
|
+
# OLD (deprecated) - using context={} or global_params={}
|
|
400
|
+
from interactive_pipe import interactive
|
|
368
401
|
|
|
369
|
-
|
|
370
|
-
def apply_brightness(img:np.ndarray, brightness: float = 0.5, global_params
|
|
371
|
-
global_params["brightness"] = brightness
|
|
402
|
+
@interactive(brightness=(0.5, [0., 1.]))
|
|
403
|
+
def apply_brightness(img:np.ndarray, brightness: float = 0.5, global_params={}):
|
|
404
|
+
global_params["brightness"] = brightness # Storing shared data
|
|
405
|
+
global_params["__output_styles"]["output"] = {"title": "Brightened"} # Setting layout
|
|
372
406
|
return img * brightness
|
|
373
407
|
|
|
374
|
-
#
|
|
375
|
-
from interactive_pipe import context
|
|
408
|
+
# NEW (recommended) - using context and layout proxies
|
|
409
|
+
from interactive_pipe import interactive, context, layout
|
|
410
|
+
|
|
411
|
+
@interactive(brightness=(0.5, [0., 1.]))
|
|
376
412
|
def apply_brightness(img:np.ndarray, brightness: float = 0.5):
|
|
377
|
-
context
|
|
413
|
+
context["brightness"] = brightness # or: context.brightness = brightness
|
|
414
|
+
layout.style("output", title="Brightened")
|
|
378
415
|
return img * brightness
|
|
379
416
|
```
|
|
380
417
|
|
|
418
|
+
The new API provides:
|
|
419
|
+
- `context` - For sharing data between filters (replaces `global_params["key"]`)
|
|
420
|
+
- `layout` - For controlling output display (replaces `global_params["__output_styles"]`)
|
|
421
|
+
- `audio` - For audio playback control
|
|
422
|
+
- `get_context()` - Get the shared context dictionary directly
|
|
423
|
+
|
|
381
424
|
|
|
382
425
|
|
|
383
426
|
|
|
@@ -395,7 +438,7 @@ def apply_brightness(img:np.ndarray, brightness: float = 0.5):
|
|
|
395
438
|
|
|
396
439
|
|
|
397
440
|
### FAQ
|
|
398
|
-
-
|
|
441
|
+
- ❓ What is the recommended way to access shared context?
|
|
399
442
|
> **New in v0.8.8**: Use the clean context API for direct access:
|
|
400
443
|
> ```python
|
|
401
444
|
> from interactive_pipe import context, layout, audio, get_context
|
|
@@ -409,7 +452,7 @@ def apply_brightness(img:np.ndarray, brightness: float = 0.5):
|
|
|
409
452
|
> return img
|
|
410
453
|
> ```
|
|
411
454
|
|
|
412
|
-
-
|
|
455
|
+
- ❓ How do I change the layout? *Can I change the grid layout of images live? (like you compare 2 images side by side and you want to start comparing 4 images in a 2x2 fashion for debugging purpose)*. It is possible with Qt backend.
|
|
413
456
|
|
|
414
457
|
> Use the `layout` helper to control image arrangement and styling:
|
|
415
458
|
> ```python
|
|
@@ -434,16 +477,16 @@ def apply_brightness(img:np.ndarray, brightness: float = 0.5):
|
|
|
434
477
|
> return result
|
|
435
478
|
> ```
|
|
436
479
|
|
|
437
|
-
-
|
|
480
|
+
- ❓ Do I have to remove `KeyboardSlider` when using gradio or notebook backends?
|
|
438
481
|
> No, don't worry, these will be mapped back to regular sliders!
|
|
439
|
-
-
|
|
440
|
-
>
|
|
482
|
+
- ❓ How do I play audio live?
|
|
483
|
+
> 🔊 Inside a processing block, write the audio file to disk and use the audio helper:
|
|
441
484
|
> ```python
|
|
442
485
|
> from interactive_pipe import audio
|
|
443
486
|
> audio.set_audio(audio_file) # New clean API (v0.8.8)
|
|
444
487
|
> # or legacy: context["__set_audio"](audio_file)
|
|
445
488
|
> ```
|
|
446
|
-
-
|
|
489
|
+
- ❓ Do I have to decorate my processing block using the `@interactive`
|
|
447
490
|
> If you use the `@` decoration style, your function won't be useable in a regular manner (wich may be problematic in a serious development environment)
|
|
448
491
|
```python
|
|
449
492
|
@interactive(angle=(0., [-360., 360.]))
|
|
@@ -465,11 +508,11 @@ from core_filter import processing_block
|
|
|
465
508
|
def add_interactivity():
|
|
466
509
|
interactive(angle=(0., [-360., 360.]))(processing_block)
|
|
467
510
|
```
|
|
468
|
-
-
|
|
469
|
-
> Yes, headless mode is supported.
|
|
511
|
+
- ❓ Can I call the pipeline in a command line/batch fashion?
|
|
512
|
+
> Yes, headless mode is supported. 🔜 documentation needed.
|
|
470
513
|
|
|
471
514
|
|
|
472
|
-
-
|
|
515
|
+
- ❓ Can I use inplace operations?
|
|
473
516
|
> Better avoid these in general. To avoid making extra copies, computing hashes everywhere and avoid loosing precious computation time, there are no checks that inputs are not modified in place.
|
|
474
517
|
```python
|
|
475
518
|
# Don't do that!
|
|
@@ -477,38 +520,18 @@ def bad_processing_block(inp):
|
|
|
477
520
|
inp+=1
|
|
478
521
|
```
|
|
479
522
|
|
|
480
|
-
-
|
|
523
|
+
- ❓ Is there a difference between `global_params` and `context` ?
|
|
481
524
|
> No, `global_params`, `global_parameters`, `global_state`, `global_context`, `context`, `state` all mean the same thing and are all supported for legacy reasons. `context` is the preferred wording. However, we now recommend using the clean context API (see above).
|
|
482
|
-
>
|
|
525
|
+
> ⚠️ The old `global_params={}` / `context={}` keyword argument style still works for backwards compatibility but is deprecated.
|
|
483
526
|
|
|
484
527
|
|
|
485
528
|
|
|
486
529
|
# Roadmap and todos
|
|
487
|
-
|
|
530
|
+
🐛 Want to contribute or interested in adding new features? Enter a new [Github issue](https://github.com/balthazarneveu/interactive_pipe/issues)
|
|
488
531
|
|
|
489
|
-
|
|
532
|
+
🎁 Want to dig into the code? Take a look at [code_architecture.md](/code_architecture.md)
|
|
490
533
|
|
|
491
|
-
## Short term roadmap
|
|
492
|
-
- Backport previous features
|
|
493
|
-
- Image class support in interactive pipe (Heatmaps/Float images)
|
|
494
534
|
|
|
495
|
-
## Long term roadmap
|
|
496
|
-
- Advanced feature
|
|
497
|
-
- Webcam based "slider" for dropdown menu (like "elephant" will trigget if an elephant is magically detected on the webcam)
|
|
498
|
-
- Animations/While loops/Video source (Time slider)
|
|
499
|
-
- Exploratory backends
|
|
500
|
-
- Create a [textual](https://github.com/Textualize/textual) backend for simplified GUI (probably no images displayed)
|
|
501
|
-
- Create a [Kivy](https://kivy.org/) backend
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
### Further examples
|
|
505
|
-
|
|
506
|
-
#### [Minimalistic pytorch based ISP ](https://github.com/balthazarneveu/interactive_pipe/tree/sample_interactive_isp/samples/isp)
|
|
507
|
-
[ISP means image signal processor](https://en.wikipedia.org/wiki/Image_processor)
|
|
508
|
-
|
|
509
|
-
:warning: Work in progess (no proper demosaicking, no denoiser, no tone mapping.)
|
|
510
|
-
|
|
511
|
-

|
|
512
535
|
|
|
513
536
|
----------------------
|
|
514
537
|
|
|
@@ -516,30 +539,44 @@ def bad_processing_block(inp):
|
|
|
516
539
|
|
|
517
540
|
#### Code quality checks
|
|
518
541
|
|
|
519
|
-
Before committing, ensure your code passes the linters and tests. The CI runs these checks automatically:
|
|
542
|
+
Before committing, ensure your code passes the linters, type checker, and tests. The CI runs these checks automatically:
|
|
520
543
|
|
|
521
544
|
**What CI does:**
|
|
522
|
-
- **
|
|
523
|
-
- **
|
|
545
|
+
- **Ruff formatting check** (`.github/workflows/ruff-format.yaml`): Runs `ruff format --check` to verify code formatting
|
|
546
|
+
- **Ruff linting** (`.github/workflows/ruff-lint.yaml`): Runs `ruff check` to check code quality (replaces flake8)
|
|
547
|
+
- **Pyright type checking** (`.github/workflows/pyright.yaml`): Runs `pyright` for static type checking (informational, non-blocking)
|
|
524
548
|
- **Pytest tests** (`.github/workflows/pytest.yaml`): Runs `pytest` on Python 3.9, 3.10, and 3.11
|
|
525
549
|
|
|
526
550
|
**Local commands (match CI):**
|
|
527
551
|
|
|
528
552
|
```bash
|
|
529
|
-
# Install
|
|
530
|
-
pip install
|
|
531
|
-
pip install -e ".[pytest]"
|
|
553
|
+
# Install development tools and test dependencies
|
|
554
|
+
pip install -e ".[dev,pytest]"
|
|
532
555
|
|
|
533
|
-
#
|
|
534
|
-
|
|
556
|
+
# Format code (Ruff) - matches CI
|
|
557
|
+
ruff format .
|
|
535
558
|
|
|
536
|
-
#
|
|
537
|
-
|
|
559
|
+
# Check formatting (Ruff) - matches CI
|
|
560
|
+
ruff format --check .
|
|
538
561
|
|
|
539
|
-
#
|
|
540
|
-
|
|
562
|
+
# Lint code (Ruff) - matches CI (auto-fixes when possible)
|
|
563
|
+
ruff check .
|
|
564
|
+
|
|
565
|
+
# Auto-fix linting issues (Ruff)
|
|
566
|
+
ruff check --fix .
|
|
567
|
+
|
|
568
|
+
# Type check (Pyright) - matches CI (informational)
|
|
569
|
+
pyright src/
|
|
541
570
|
|
|
542
571
|
# Run tests (pytest) - matches CI
|
|
543
572
|
pytest
|
|
573
|
+
|
|
574
|
+
# Pre-commit checklist (run all before committing)
|
|
575
|
+
ruff format .
|
|
576
|
+
ruff check --fix .
|
|
577
|
+
pyright src/ # Optional, won't block commit
|
|
578
|
+
pytest
|
|
544
579
|
```
|
|
545
580
|
|
|
581
|
+
**Note:** Ruff replaces both Black (formatting) and Flake8 (linting) in a single, faster tool. Pyright provides static type checking to catch type errors early.
|
|
582
|
+
|
|
@@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
|
|
|
5
5
|
|
|
6
6
|
[project]
|
|
7
7
|
name = "interactive_pipe"
|
|
8
|
-
version = "0.8.
|
|
8
|
+
version = "0.8.10"
|
|
9
9
|
authors = [
|
|
10
10
|
{ name="Balthazar Neveu", email="balthazarneveu@gmail.com" },
|
|
11
11
|
]
|
|
@@ -14,13 +14,6 @@ dependencies = [
|
|
|
14
14
|
"numpy>=1.21.6",
|
|
15
15
|
"Pillow>=9.0.1",
|
|
16
16
|
"PyYAML>=5.4.1",
|
|
17
|
-
"PyQt6>=6.5.2",
|
|
18
|
-
"PyQt6_sip>=13.5.2",
|
|
19
|
-
"opencv_python_headless>=4.8.1.78",
|
|
20
|
-
"pytest>=6.2.5",
|
|
21
|
-
"ipywidgets>=7.7.1",
|
|
22
|
-
"pandas",
|
|
23
|
-
"gradio",
|
|
24
17
|
]
|
|
25
18
|
|
|
26
19
|
description = "Library to create flexible interactive image processing pipelines and automatically add a graphical user interface without knowing anything about GUI coding!"
|
|
@@ -65,3 +58,35 @@ full=[
|
|
|
65
58
|
"pandas",
|
|
66
59
|
"gradio",
|
|
67
60
|
]
|
|
61
|
+
|
|
62
|
+
dev=[
|
|
63
|
+
"ruff>=0.9.0",
|
|
64
|
+
"pyright>=1.1.390",
|
|
65
|
+
]
|
|
66
|
+
|
|
67
|
+
[tool.ruff]
|
|
68
|
+
line-length = 120
|
|
69
|
+
target-version = "py39"
|
|
70
|
+
exclude = ["__init__.py", "*/__pycache__/*", "*venv*/*"]
|
|
71
|
+
|
|
72
|
+
[tool.ruff.lint]
|
|
73
|
+
select = ["E", "F", "W", "I"] # E/F/W = flake8 rules, I = isort
|
|
74
|
+
ignore = ["E203"] # E203: whitespace before ':' (conflicts with black formatting)
|
|
75
|
+
|
|
76
|
+
[tool.ruff.lint.isort]
|
|
77
|
+
# isort compatibility
|
|
78
|
+
|
|
79
|
+
[tool.ruff.format]
|
|
80
|
+
# Black-compatible formatting (default)
|
|
81
|
+
|
|
82
|
+
[tool.pyright]
|
|
83
|
+
pythonVersion = "3.9"
|
|
84
|
+
typeCheckingMode = "basic"
|
|
85
|
+
include = ["src"]
|
|
86
|
+
exclude = ["**/venv/**", "**/__pycache__/**", "test/**"]
|
|
87
|
+
reportMissingImports = "none" # Qt libs and other optional dependencies
|
|
88
|
+
reportMissingTypeStubs = false
|
|
89
|
+
reportUnknownMemberType = false
|
|
90
|
+
reportUnknownArgumentType = false
|
|
91
|
+
reportUnknownVariableType = false
|
|
92
|
+
reportPrivateUsage = false
|