interactive-pipe 0.8.8__tar.gz → 0.8.9__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.9}/PKG-INFO +150 -118
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/pyproject.toml +33 -8
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/readme.md +146 -110
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/src/interactive_pipe/__init__.py +25 -15
- interactive_pipe-0.8.9/src/interactive_pipe/core/backend.py +12 -0
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/src/interactive_pipe/core/cache.py +2 -6
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/src/interactive_pipe/core/context.py +17 -17
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/src/interactive_pipe/core/engine.py +10 -17
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/src/interactive_pipe/core/filter.py +23 -42
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/src/interactive_pipe/core/graph.py +36 -21
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/src/interactive_pipe/core/pipeline.py +44 -52
- interactive_pipe-0.8.9/src/interactive_pipe/core/signature.py +10 -0
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/src/interactive_pipe/data_objects/audio.py +16 -14
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/src/interactive_pipe/data_objects/curves.py +55 -73
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/src/interactive_pipe/data_objects/data.py +18 -22
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/src/interactive_pipe/data_objects/image.py +11 -5
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/src/interactive_pipe/data_objects/parameters.py +7 -12
- interactive_pipe-0.8.9/src/interactive_pipe/data_objects/table.py +328 -0
- interactive_pipe-0.8.9/src/interactive_pipe/graphical/gradio_control.py +201 -0
- interactive_pipe-0.8.9/src/interactive_pipe/graphical/gradio_gui.py +651 -0
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/src/interactive_pipe/graphical/gui.py +64 -35
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/src/interactive_pipe/graphical/mpl_control.py +56 -37
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/src/interactive_pipe/graphical/mpl_gui.py +41 -41
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/src/interactive_pipe/graphical/mpl_window.py +23 -10
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/src/interactive_pipe/graphical/nb_control.py +52 -36
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/src/interactive_pipe/graphical/nb_gui.py +7 -7
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/src/interactive_pipe/graphical/qt_control.py +153 -99
- interactive_pipe-0.8.9/src/interactive_pipe/graphical/qt_gui.py +981 -0
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/src/interactive_pipe/graphical/window.py +10 -7
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/src/interactive_pipe/headless/control.py +87 -44
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/src/interactive_pipe/headless/keyboard.py +27 -15
- interactive_pipe-0.8.9/src/interactive_pipe/headless/panel.py +141 -0
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/src/interactive_pipe/headless/pipeline.py +68 -79
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/src/interactive_pipe/helper/choose_backend.py +7 -5
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/src/interactive_pipe/helper/control_abbreviation.py +62 -72
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/src/interactive_pipe/helper/filter_decorator.py +16 -14
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/src/interactive_pipe/helper/keyword_args_analyzer.py +15 -26
- interactive_pipe-0.8.9/src/interactive_pipe/helper/pipeline_decorator.py +71 -0
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.9/src/interactive_pipe.egg-info}/PKG-INFO +150 -118
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/src/interactive_pipe.egg-info/SOURCES.txt +5 -1
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/src/interactive_pipe.egg-info/requires.txt +4 -7
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/test/test_context.py +47 -35
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/test/test_context_compatibility.py +11 -24
- interactive_pipe-0.8.9/test/test_control_abbreviations.py +83 -0
- interactive_pipe-0.8.9/test/test_controller.py +112 -0
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/test/test_core.py +1 -3
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/test/test_core_exceptions.py +9 -10
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/test/test_curves.py +2 -4
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/test/test_data_objects_exceptions.py +7 -7
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/test/test_decorator.py +2 -1
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/test/test_edge_cases.py +17 -6
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/test/test_engine.py +6 -13
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/test/test_filter.py +1 -3
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/test/test_graphical_exceptions.py +33 -29
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/test/test_headless.py +7 -11
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/test/test_headless_exceptions.py +4 -5
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/test/test_image.py +2 -2
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/test/test_legacy_warnings.py +17 -39
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/test/test_mutable_defaults.py +1 -0
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/test/test_recorder.py +7 -6
- interactive_pipe-0.8.9/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.9}/LICENSE +0 -0
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/setup.cfg +0 -0
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/src/interactive_pipe/core/__init__.py +0 -0
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/src/interactive_pipe/data_objects/__init__.py +0 -0
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/src/interactive_pipe/graphical/__init__.py +0 -0
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/src/interactive_pipe/headless/__init__.py +0 -0
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/src/interactive_pipe/helper/__init__.py +0 -0
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/src/interactive_pipe/helper/_private.py +0 -0
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/src/interactive_pipe.egg-info/dependency_links.txt +0 -0
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/src/interactive_pipe.egg-info/top_level.txt +0 -0
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/test/test_cache.py +0 -0
- {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/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.9
|
|
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.9**
|
|
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,28 @@ 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 | ✅ | ➖ | ➖ | ✅ |
|
|
166
162
|
|
|
167
163
|
|
|
168
164
|
|
|
@@ -189,7 +185,7 @@ Shortcuts while using the GUI (QT & matplotlib backends)
|
|
|
189
185
|
|
|
190
186
|
|
|
191
187
|
|
|
192
|
-
##
|
|
188
|
+
## 🚀 Ultra short code
|
|
193
189
|
|
|
194
190
|
|
|
195
191
|
*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 +198,9 @@ By design:
|
|
|
202
198
|
- keyword arguments are the parameters which can be later turned into interactive widgets.
|
|
203
199
|
- output buffers are simply returned like you'd do in a regular function.
|
|
204
200
|
|
|
205
|
-
We use the `@interactive()`
|
|
201
|
+
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
202
|
|
|
207
|
-
The syntax to turn keyword arguments into sliders is pretty simple
|
|
203
|
+
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
204
|
|
|
209
205
|
Finally, we need to the glue to combo these filters. This is where the sample_pipeline function comes in.
|
|
210
206
|
|
|
@@ -215,8 +211,11 @@ By decorating it with `@interactive_pipeline(gui="qt")`, calling this function w
|
|
|
215
211
|
from interactive_pipe import interactive, interactive_pipeline
|
|
216
212
|
import numpy as np
|
|
217
213
|
|
|
218
|
-
@interactive(
|
|
219
|
-
|
|
214
|
+
@interactive(
|
|
215
|
+
coeff=(1., [0.5, 2.], "exposure"),
|
|
216
|
+
bias=(0., [-0.2, 0.2])
|
|
217
|
+
)
|
|
218
|
+
def exposure(img, coeff=1., bias=0.):
|
|
220
219
|
'''Applies a multiplication by coeff & adds a constant bias to the image'''
|
|
221
220
|
# In the GUI, the coeff will be labelled as "exposure".
|
|
222
221
|
# As the default tuple provided to bias does not end up with a string,
|
|
@@ -224,15 +223,15 @@ def exposure(img, coeff = (1., [0.5, 2.], "exposure"), bias=(0., [-0.2, 0.2])):
|
|
|
224
223
|
return img*coeff + bias
|
|
225
224
|
|
|
226
225
|
|
|
227
|
-
@interactive()
|
|
228
|
-
def black_and_white(img, bnw=
|
|
226
|
+
@interactive(bnw=(True, "black and white"))
|
|
227
|
+
def black_and_white(img, bnw=True):
|
|
229
228
|
'''Averages the 3 color channels (Black & White) if bnw=True
|
|
230
229
|
'''
|
|
231
230
|
# Special mention for booleans: using a tuple like (True,) allows creating the tick box.
|
|
232
231
|
return np.repeat(np.expand_dims(np.average(img, axis=-1), -1), img.shape[-1], axis=-1) if bnw else img
|
|
233
232
|
|
|
234
|
-
@interactive()
|
|
235
|
-
def blend(img0, img1, blend_coeff=
|
|
233
|
+
@interactive(blend_coeff=(0.5, [0., 1.]))
|
|
234
|
+
def blend(img0, img1, blend_coeff=0.5):
|
|
236
235
|
'''Blends between two image.
|
|
237
236
|
- when blend_coeff=0 -> image 0 [slider to the left ]
|
|
238
237
|
- when blend_coeff=1 -> image 1 [slider to the right]
|
|
@@ -252,25 +251,25 @@ if __name__ == '__main__':
|
|
|
252
251
|
sample_pipeline(input_image)
|
|
253
252
|
|
|
254
253
|
```
|
|
255
|
-
|
|
254
|
+
❤️ This code shall display you a GUI with three images. The middle one is the result of the blend
|
|
256
255
|
|
|
257
256
|
|
|
258
257
|
|
|
259
258
|
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
|
|
259
|
+
- If you write `@interactive()` with `def blend(img0, img1, blend_coeff=0.5):`, blend_coeff will simply not be a slider on the GUI.
|
|
260
|
+
- If you write `@interactive(blend_coeff=[0., 1.])` in the decorator, blend_coeff will be a slider initialized to 0.5
|
|
261
|
+
- 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
262
|
|
|
264
263
|
-----------
|
|
265
|
-
##
|
|
264
|
+
## 💡 Some more tips
|
|
266
265
|
|
|
267
266
|
```python
|
|
268
|
-
from interactive_pipe import interactive, interactive_pipeline
|
|
267
|
+
from interactive_pipe import interactive, interactive_pipeline, context
|
|
269
268
|
import numpy as np
|
|
270
269
|
|
|
271
270
|
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=
|
|
271
|
+
@interactive(color_choice=["red", "green", "blue", "gray"])
|
|
272
|
+
def generate_flat_colored_image(color_choice="red"):
|
|
274
273
|
'''Generate a constant colorful image
|
|
275
274
|
'''
|
|
276
275
|
flat_array = np.array(COLOR_DICT.get(color_choice)) * np.ones((64, 64, 3))
|
|
@@ -282,15 +281,15 @@ def generate_flat_colored_image(color_choice=["red", "green", "blue", "gray"], c
|
|
|
282
281
|
- 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
282
|
----------
|
|
284
283
|
|
|
285
|
-
|
|
286
|
-
Yes, using the
|
|
287
|
-
- Check carefully how we stored the image average of the flat image in
|
|
284
|
+
💡 Can filters communicate together?
|
|
285
|
+
Yes, using the `context` proxy from `interactive_pipe`.
|
|
286
|
+
- Check carefully how we stored the image average of the flat image in context.
|
|
288
287
|
- This value will be available to other filters.
|
|
289
288
|
`special_image_slice` is going to use that value to set the half bottom image to dark in case the average is high.
|
|
290
289
|
|
|
291
290
|
```python
|
|
292
|
-
|
|
293
|
-
|
|
291
|
+
def special_image_slice(img):
|
|
292
|
+
out_img = img.copy()
|
|
294
293
|
if context["avg"] > 0.4:
|
|
295
294
|
out_img[out_img.shape[0]//2:, ...] = 0.
|
|
296
295
|
return out_img
|
|
@@ -299,8 +298,8 @@ def special_image_slice(img, context={}):
|
|
|
299
298
|
|
|
300
299
|
|
|
301
300
|
```python
|
|
302
|
-
@interactive()
|
|
303
|
-
def switch_image(img1, img2, img3, image_index=
|
|
301
|
+
@interactive(image_index=(0, [0, 2], None, ["pagedown", "pageup", True]))
|
|
302
|
+
def switch_image(img1, img2, img3, image_index=0):
|
|
304
303
|
'''Switch between 3 images
|
|
305
304
|
'''
|
|
306
305
|
return [img1, img2, img3][image_index]
|
|
@@ -308,8 +307,8 @@ def switch_image(img1, img2, img3, image_index=(0, [0, 2], None, ["pagedown", "p
|
|
|
308
307
|
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
308
|
|
|
310
309
|
```python
|
|
311
|
-
@interactive()
|
|
312
|
-
def black_top_image_slice(img, top_slice_black=
|
|
310
|
+
@interactive(top_slice_black=(True, "special", "k"))
|
|
311
|
+
def black_top_image_slice(img, top_slice_black=True):
|
|
313
312
|
out_img = img.copy()
|
|
314
313
|
if top_slice_black:
|
|
315
314
|
out_img[:out_img.shape[0]//2, ...] = 0.
|
|
@@ -332,6 +331,38 @@ if __name__ == '__main__':
|
|
|
332
331
|
|
|
333
332
|
### Release Notes
|
|
334
333
|
|
|
334
|
+
|
|
335
|
+
#### Version 0.8.9 (February 2026)
|
|
336
|
+
|
|
337
|
+
**New Features:**
|
|
338
|
+
- **Panel System**: Control panel layout and organization
|
|
339
|
+
- Flexible panel positioning (left, right, top, bottom)
|
|
340
|
+
- Detached control panels for separate windows
|
|
341
|
+
- Nested panels and subpanels support
|
|
342
|
+
- Grouped controls within panels
|
|
343
|
+
- Improved spacing and borders for better visual organization
|
|
344
|
+
- Full backend support (Qt, Gradio, matplotlib, notebook)
|
|
345
|
+
|
|
346
|
+
- **Table Data Type**: Display tabular data natively
|
|
347
|
+
- Core Table functionality without external dependencies
|
|
348
|
+
- Optional pandas DataFrame support for advanced use cases
|
|
349
|
+
- Rendering support across all backends (Qt, Gradio, matplotlib)
|
|
350
|
+
- Headerless tables option
|
|
351
|
+
|
|
352
|
+
- **TimeControl Enhancements**: Better time-based parameter control
|
|
353
|
+
- Improved slider help display
|
|
354
|
+
- Additional demos showcasing time-based animations
|
|
355
|
+
|
|
356
|
+
**API Improvements:**
|
|
357
|
+
- Context support at pipeline initialization
|
|
358
|
+
- Backend selection via enum (string format still supported)
|
|
359
|
+
- Graph visualization for GUI pipelines (press `G`)
|
|
360
|
+
|
|
361
|
+
**Deprecations:**
|
|
362
|
+
- Inline syntax deprecated (use decorator syntax instead)
|
|
363
|
+
- `output_canvas` argument removed
|
|
364
|
+
- Context aliases (`global_params`, `states` etc...) deprecated at initialization
|
|
365
|
+
|
|
335
366
|
#### Version 0.8.8 (January 2026)
|
|
336
367
|
|
|
337
368
|
**New Features:**
|
|
@@ -357,27 +388,34 @@ if __name__ == '__main__':
|
|
|
357
388
|
- Fixed pytest failures for optional dependencies in CI
|
|
358
389
|
- Fixed various edge cases in error handling
|
|
359
390
|
|
|
360
|
-
**Migration
|
|
361
|
-
- Use `global_params=SharedContext.injected()` to let code interpreters know that there's no need to pass this parameter.
|
|
391
|
+
**Migration from old `context={}` or `global_params={}` patterns:**
|
|
362
392
|
|
|
363
393
|
```python
|
|
364
|
-
#
|
|
365
|
-
|
|
366
|
-
global_params["brightness"] = brightness
|
|
367
|
-
return img * brightness
|
|
394
|
+
# OLD (deprecated) - using context={} or global_params={}
|
|
395
|
+
from interactive_pipe import interactive
|
|
368
396
|
|
|
369
|
-
|
|
370
|
-
def apply_brightness(img:np.ndarray, brightness: float = 0.5, global_params
|
|
371
|
-
global_params["brightness"] = brightness
|
|
397
|
+
@interactive(brightness=(0.5, [0., 1.]))
|
|
398
|
+
def apply_brightness(img:np.ndarray, brightness: float = 0.5, global_params={}):
|
|
399
|
+
global_params["brightness"] = brightness # Storing shared data
|
|
400
|
+
global_params["__output_styles"]["output"] = {"title": "Brightened"} # Setting layout
|
|
372
401
|
return img * brightness
|
|
373
402
|
|
|
374
|
-
#
|
|
375
|
-
from interactive_pipe import context
|
|
403
|
+
# NEW (recommended) - using context and layout proxies
|
|
404
|
+
from interactive_pipe import interactive, context, layout
|
|
405
|
+
|
|
406
|
+
@interactive(brightness=(0.5, [0., 1.]))
|
|
376
407
|
def apply_brightness(img:np.ndarray, brightness: float = 0.5):
|
|
377
|
-
context
|
|
408
|
+
context["brightness"] = brightness # or: context.brightness = brightness
|
|
409
|
+
layout.style("output", title="Brightened")
|
|
378
410
|
return img * brightness
|
|
379
411
|
```
|
|
380
412
|
|
|
413
|
+
The new API provides:
|
|
414
|
+
- `context` - For sharing data between filters (replaces `global_params["key"]`)
|
|
415
|
+
- `layout` - For controlling output display (replaces `global_params["__output_styles"]`)
|
|
416
|
+
- `audio` - For audio playback control
|
|
417
|
+
- `get_context()` - Get the shared context dictionary directly
|
|
418
|
+
|
|
381
419
|
|
|
382
420
|
|
|
383
421
|
|
|
@@ -395,7 +433,7 @@ def apply_brightness(img:np.ndarray, brightness: float = 0.5):
|
|
|
395
433
|
|
|
396
434
|
|
|
397
435
|
### FAQ
|
|
398
|
-
-
|
|
436
|
+
- ❓ What is the recommended way to access shared context?
|
|
399
437
|
> **New in v0.8.8**: Use the clean context API for direct access:
|
|
400
438
|
> ```python
|
|
401
439
|
> from interactive_pipe import context, layout, audio, get_context
|
|
@@ -409,7 +447,7 @@ def apply_brightness(img:np.ndarray, brightness: float = 0.5):
|
|
|
409
447
|
> return img
|
|
410
448
|
> ```
|
|
411
449
|
|
|
412
|
-
-
|
|
450
|
+
- ❓ 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
451
|
|
|
414
452
|
> Use the `layout` helper to control image arrangement and styling:
|
|
415
453
|
> ```python
|
|
@@ -434,16 +472,16 @@ def apply_brightness(img:np.ndarray, brightness: float = 0.5):
|
|
|
434
472
|
> return result
|
|
435
473
|
> ```
|
|
436
474
|
|
|
437
|
-
-
|
|
475
|
+
- ❓ Do I have to remove `KeyboardSlider` when using gradio or notebook backends?
|
|
438
476
|
> No, don't worry, these will be mapped back to regular sliders!
|
|
439
|
-
-
|
|
440
|
-
>
|
|
477
|
+
- ❓ How do I play audio live?
|
|
478
|
+
> 🔊 Inside a processing block, write the audio file to disk and use the audio helper:
|
|
441
479
|
> ```python
|
|
442
480
|
> from interactive_pipe import audio
|
|
443
481
|
> audio.set_audio(audio_file) # New clean API (v0.8.8)
|
|
444
482
|
> # or legacy: context["__set_audio"](audio_file)
|
|
445
483
|
> ```
|
|
446
|
-
-
|
|
484
|
+
- ❓ Do I have to decorate my processing block using the `@interactive`
|
|
447
485
|
> 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
486
|
```python
|
|
449
487
|
@interactive(angle=(0., [-360., 360.]))
|
|
@@ -465,11 +503,11 @@ from core_filter import processing_block
|
|
|
465
503
|
def add_interactivity():
|
|
466
504
|
interactive(angle=(0., [-360., 360.]))(processing_block)
|
|
467
505
|
```
|
|
468
|
-
-
|
|
469
|
-
> Yes, headless mode is supported.
|
|
506
|
+
- ❓ Can I call the pipeline in a command line/batch fashion?
|
|
507
|
+
> Yes, headless mode is supported. 🔜 documentation needed.
|
|
470
508
|
|
|
471
509
|
|
|
472
|
-
-
|
|
510
|
+
- ❓ Can I use inplace operations?
|
|
473
511
|
> 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
512
|
```python
|
|
475
513
|
# Don't do that!
|
|
@@ -477,38 +515,18 @@ def bad_processing_block(inp):
|
|
|
477
515
|
inp+=1
|
|
478
516
|
```
|
|
479
517
|
|
|
480
|
-
-
|
|
518
|
+
- ❓ Is there a difference between `global_params` and `context` ?
|
|
481
519
|
> 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
|
-
>
|
|
520
|
+
> ⚠️ The old `global_params={}` / `context={}` keyword argument style still works for backwards compatibility but is deprecated.
|
|
483
521
|
|
|
484
522
|
|
|
485
523
|
|
|
486
524
|
# Roadmap and todos
|
|
487
|
-
|
|
525
|
+
🐛 Want to contribute or interested in adding new features? Enter a new [Github issue](https://github.com/balthazarneveu/interactive_pipe/issues)
|
|
488
526
|
|
|
489
|
-
|
|
527
|
+
🎁 Want to dig into the code? Take a look at [code_architecture.md](/code_architecture.md)
|
|
490
528
|
|
|
491
|
-
## Short term roadmap
|
|
492
|
-
- Backport previous features
|
|
493
|
-
- Image class support in interactive pipe (Heatmaps/Float images)
|
|
494
529
|
|
|
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
530
|
|
|
513
531
|
----------------------
|
|
514
532
|
|
|
@@ -516,30 +534,44 @@ def bad_processing_block(inp):
|
|
|
516
534
|
|
|
517
535
|
#### Code quality checks
|
|
518
536
|
|
|
519
|
-
Before committing, ensure your code passes the linters and tests. The CI runs these checks automatically:
|
|
537
|
+
Before committing, ensure your code passes the linters, type checker, and tests. The CI runs these checks automatically:
|
|
520
538
|
|
|
521
539
|
**What CI does:**
|
|
522
|
-
- **
|
|
523
|
-
- **
|
|
540
|
+
- **Ruff formatting check** (`.github/workflows/ruff-format.yaml`): Runs `ruff format --check` to verify code formatting
|
|
541
|
+
- **Ruff linting** (`.github/workflows/ruff-lint.yaml`): Runs `ruff check` to check code quality (replaces flake8)
|
|
542
|
+
- **Pyright type checking** (`.github/workflows/pyright.yaml`): Runs `pyright` for static type checking (informational, non-blocking)
|
|
524
543
|
- **Pytest tests** (`.github/workflows/pytest.yaml`): Runs `pytest` on Python 3.9, 3.10, and 3.11
|
|
525
544
|
|
|
526
545
|
**Local commands (match CI):**
|
|
527
546
|
|
|
528
547
|
```bash
|
|
529
|
-
# Install
|
|
530
|
-
pip install
|
|
531
|
-
pip install -e ".[pytest]"
|
|
548
|
+
# Install development tools and test dependencies
|
|
549
|
+
pip install -e ".[dev,pytest]"
|
|
532
550
|
|
|
533
|
-
#
|
|
534
|
-
|
|
551
|
+
# Format code (Ruff) - matches CI
|
|
552
|
+
ruff format .
|
|
535
553
|
|
|
536
|
-
#
|
|
537
|
-
|
|
554
|
+
# Check formatting (Ruff) - matches CI
|
|
555
|
+
ruff format --check .
|
|
538
556
|
|
|
539
|
-
#
|
|
540
|
-
|
|
557
|
+
# Lint code (Ruff) - matches CI (auto-fixes when possible)
|
|
558
|
+
ruff check .
|
|
559
|
+
|
|
560
|
+
# Auto-fix linting issues (Ruff)
|
|
561
|
+
ruff check --fix .
|
|
562
|
+
|
|
563
|
+
# Type check (Pyright) - matches CI (informational)
|
|
564
|
+
pyright src/
|
|
541
565
|
|
|
542
566
|
# Run tests (pytest) - matches CI
|
|
543
567
|
pytest
|
|
568
|
+
|
|
569
|
+
# Pre-commit checklist (run all before committing)
|
|
570
|
+
ruff format .
|
|
571
|
+
ruff check --fix .
|
|
572
|
+
pyright src/ # Optional, won't block commit
|
|
573
|
+
pytest
|
|
544
574
|
```
|
|
545
575
|
|
|
576
|
+
**Note:** Ruff replaces both Black (formatting) and Flake8 (linting) in a single, faster tool. Pyright provides static type checking to catch type errors early.
|
|
577
|
+
|
|
@@ -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.9"
|
|
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
|