interactive-pipe 0.7.4__tar.gz → 0.7.6__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.7.4/src/interactive_pipe.egg-info → interactive_pipe-0.7.6}/PKG-INFO +60 -14
- {interactive_pipe-0.7.4 → interactive_pipe-0.7.6}/pyproject.toml +1 -1
- {interactive_pipe-0.7.4 → interactive_pipe-0.7.6}/readme.md +59 -13
- {interactive_pipe-0.7.4 → interactive_pipe-0.7.6}/src/interactive_pipe/core/filter.py +15 -7
- {interactive_pipe-0.7.4 → interactive_pipe-0.7.6}/src/interactive_pipe/core/pipeline.py +32 -3
- {interactive_pipe-0.7.4 → interactive_pipe-0.7.6}/src/interactive_pipe/graphical/gradio_gui.py +19 -5
- interactive_pipe-0.7.6/src/interactive_pipe/helper/pipeline_decorator.py +57 -0
- {interactive_pipe-0.7.4 → interactive_pipe-0.7.6}/src/interactive_pipe/thirdparty/images_openai_api.py +9 -1
- {interactive_pipe-0.7.4 → interactive_pipe-0.7.6/src/interactive_pipe.egg-info}/PKG-INFO +60 -14
- {interactive_pipe-0.7.4 → interactive_pipe-0.7.6}/test/test_core.py +1 -1
- interactive_pipe-0.7.4/src/interactive_pipe/helper/pipeline_decorator.py +0 -34
- {interactive_pipe-0.7.4 → interactive_pipe-0.7.6}/LICENSE +0 -0
- {interactive_pipe-0.7.4 → interactive_pipe-0.7.6}/setup.cfg +0 -0
- {interactive_pipe-0.7.4 → interactive_pipe-0.7.6}/src/interactive_pipe/__init__.py +0 -0
- {interactive_pipe-0.7.4 → interactive_pipe-0.7.6}/src/interactive_pipe/core/__init__.py +0 -0
- {interactive_pipe-0.7.4 → interactive_pipe-0.7.6}/src/interactive_pipe/core/cache.py +0 -0
- {interactive_pipe-0.7.4 → interactive_pipe-0.7.6}/src/interactive_pipe/core/engine.py +0 -0
- {interactive_pipe-0.7.4 → interactive_pipe-0.7.6}/src/interactive_pipe/core/graph.py +0 -0
- {interactive_pipe-0.7.4 → interactive_pipe-0.7.6}/src/interactive_pipe/core/signature.py +0 -0
- {interactive_pipe-0.7.4 → interactive_pipe-0.7.6}/src/interactive_pipe/data_objects/__init__.py +0 -0
- {interactive_pipe-0.7.4 → interactive_pipe-0.7.6}/src/interactive_pipe/data_objects/audio.py +0 -0
- {interactive_pipe-0.7.4 → interactive_pipe-0.7.6}/src/interactive_pipe/data_objects/curves.py +0 -0
- {interactive_pipe-0.7.4 → interactive_pipe-0.7.6}/src/interactive_pipe/data_objects/data.py +0 -0
- {interactive_pipe-0.7.4 → interactive_pipe-0.7.6}/src/interactive_pipe/data_objects/image.py +0 -0
- {interactive_pipe-0.7.4 → interactive_pipe-0.7.6}/src/interactive_pipe/data_objects/parameters.py +0 -0
- {interactive_pipe-0.7.4 → interactive_pipe-0.7.6}/src/interactive_pipe/graphical/__init__.py +0 -0
- {interactive_pipe-0.7.4 → interactive_pipe-0.7.6}/src/interactive_pipe/graphical/gradio_control.py +0 -0
- {interactive_pipe-0.7.4 → interactive_pipe-0.7.6}/src/interactive_pipe/graphical/gui.py +0 -0
- {interactive_pipe-0.7.4 → interactive_pipe-0.7.6}/src/interactive_pipe/graphical/mpl_control.py +0 -0
- {interactive_pipe-0.7.4 → interactive_pipe-0.7.6}/src/interactive_pipe/graphical/mpl_gui.py +0 -0
- {interactive_pipe-0.7.4 → interactive_pipe-0.7.6}/src/interactive_pipe/graphical/mpl_window.py +0 -0
- {interactive_pipe-0.7.4 → interactive_pipe-0.7.6}/src/interactive_pipe/graphical/nb_control.py +0 -0
- {interactive_pipe-0.7.4 → interactive_pipe-0.7.6}/src/interactive_pipe/graphical/nb_gui.py +0 -0
- {interactive_pipe-0.7.4 → interactive_pipe-0.7.6}/src/interactive_pipe/graphical/qt_control.py +0 -0
- {interactive_pipe-0.7.4 → interactive_pipe-0.7.6}/src/interactive_pipe/graphical/qt_gui.py +0 -0
- {interactive_pipe-0.7.4 → interactive_pipe-0.7.6}/src/interactive_pipe/graphical/window.py +0 -0
- {interactive_pipe-0.7.4 → interactive_pipe-0.7.6}/src/interactive_pipe/headless/__init__.py +0 -0
- {interactive_pipe-0.7.4 → interactive_pipe-0.7.6}/src/interactive_pipe/headless/control.py +0 -0
- {interactive_pipe-0.7.4 → interactive_pipe-0.7.6}/src/interactive_pipe/headless/keyboard.py +0 -0
- {interactive_pipe-0.7.4 → interactive_pipe-0.7.6}/src/interactive_pipe/headless/pipeline.py +0 -0
- {interactive_pipe-0.7.4 → interactive_pipe-0.7.6}/src/interactive_pipe/helper/__init__.py +0 -0
- {interactive_pipe-0.7.4 → interactive_pipe-0.7.6}/src/interactive_pipe/helper/_private.py +0 -0
- {interactive_pipe-0.7.4 → interactive_pipe-0.7.6}/src/interactive_pipe/helper/choose_backend.py +0 -0
- {interactive_pipe-0.7.4 → interactive_pipe-0.7.6}/src/interactive_pipe/helper/control_abbreviation.py +0 -0
- {interactive_pipe-0.7.4 → interactive_pipe-0.7.6}/src/interactive_pipe/helper/filter_decorator.py +0 -0
- {interactive_pipe-0.7.4 → interactive_pipe-0.7.6}/src/interactive_pipe/helper/keyword_args_analyzer.py +0 -0
- {interactive_pipe-0.7.4 → interactive_pipe-0.7.6}/src/interactive_pipe/thirdparty/music_spotify.py +0 -0
- {interactive_pipe-0.7.4 → interactive_pipe-0.7.6}/src/interactive_pipe.egg-info/SOURCES.txt +0 -0
- {interactive_pipe-0.7.4 → interactive_pipe-0.7.6}/src/interactive_pipe.egg-info/dependency_links.txt +0 -0
- {interactive_pipe-0.7.4 → interactive_pipe-0.7.6}/src/interactive_pipe.egg-info/requires.txt +0 -0
- {interactive_pipe-0.7.4 → interactive_pipe-0.7.6}/src/interactive_pipe.egg-info/top_level.txt +0 -0
- {interactive_pipe-0.7.4 → interactive_pipe-0.7.6}/test/test_cache.py +0 -0
- {interactive_pipe-0.7.4 → interactive_pipe-0.7.6}/test/test_control_abbreviations.py +0 -0
- {interactive_pipe-0.7.4 → interactive_pipe-0.7.6}/test/test_controller.py +0 -0
- {interactive_pipe-0.7.4 → interactive_pipe-0.7.6}/test/test_curves.py +0 -0
- {interactive_pipe-0.7.4 → interactive_pipe-0.7.6}/test/test_decorator.py +0 -0
- {interactive_pipe-0.7.4 → interactive_pipe-0.7.6}/test/test_engine.py +0 -0
- {interactive_pipe-0.7.4 → interactive_pipe-0.7.6}/test/test_filter.py +0 -0
- {interactive_pipe-0.7.4 → interactive_pipe-0.7.6}/test/test_headless.py +0 -0
- {interactive_pipe-0.7.4 → interactive_pipe-0.7.6}/test/test_image.py +0 -0
- {interactive_pipe-0.7.4 → interactive_pipe-0.7.6}/test/test_parameters.py +0 -0
- {interactive_pipe-0.7.4 → interactive_pipe-0.7.6}/test/test_recorder.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: interactive_pipe
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.6
|
|
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
|
|
@@ -100,10 +100,13 @@ pip install -e ".[full]"
|
|
|
100
100
|
|
|
101
101
|
|
|
102
102
|
----------------------
|
|
103
|
+
## :scroll: Terminology
|
|
104
|
+
|
|
105
|
+

|
|
103
106
|
|
|
104
107
|
## :scroll: Features
|
|
105
108
|
|
|
106
|
-
**Version 0.7.
|
|
109
|
+
**Version 0.7.6**
|
|
107
110
|
- Modular multi-image processing filters
|
|
108
111
|
- Declarative: Easily make graphical user interface without having to learn anything about pyQt or matplotlib
|
|
109
112
|
- Support in jupyter notebooks
|
|
@@ -147,7 +150,7 @@ Shortcuts while using the GUI (QT & matplotlib backends)
|
|
|
147
150
|
| :star: | *PyQt / PySide* | *Matplotlib* | *Jupyter notebooks including Google collab* | *Gradio* |
|
|
148
151
|
|:-----: |:-----:|:------:|:----: |:----: |
|
|
149
152
|
| Backend name | `qt` | `mpl` | `nb`| `gradio` |
|
|
150
|
-
| Preview |  |  |  | |
|
|
153
|
+
| Preview |  |  |  | |
|
|
151
154
|
| Plot curves | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark:|
|
|
152
155
|
| Auto refreshed layout | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_minus_sign: |
|
|
153
156
|
| Keyboard shortcuts / fullscreen| :heavy_check_mark: | :heavy_check_mark: | :heavy_minus_sign: | :heavy_minus_sign: |
|
|
@@ -158,26 +161,29 @@ Shortcuts while using the GUI (QT & matplotlib backends)
|
|
|
158
161
|
|
|
159
162
|
|
|
160
163
|
# Tutorials
|
|
164
|
+
### [Main tutorial](https://huggingface.co/spaces/balthou/interactive-pipe-tutorial)
|
|
165
|
+

|
|
161
166
|
|
|
162
167
|
### Learn by examples
|
|
163
|
-
#### [Basic image processing](/demo/multi_image.py)
|
|
168
|
+
#### [Basic image processing (python code sample for PyQT GUI)](/demo/multi_image.py)
|
|
164
169
|
| GUI | Pipeline |
|
|
165
170
|
|:--:|:--:|
|
|
166
171
|
| |  |
|
|
167
172
|
|
|
168
173
|

|
|
169
174
|
|
|
170
|
-
#### [Speech exploration notebook](https://colab.research.google.com/drive/1mUX2FW0qflWn-v3nIx90P_KvRxnXlBpz#scrollTo=qDTaIwvaJQ6R)
|
|
175
|
+
#### [Speech exploration notebook (colab, signal processing)](https://colab.research.google.com/drive/1mUX2FW0qflWn-v3nIx90P_KvRxnXlBpz#scrollTo=qDTaIwvaJQ6R)
|
|
171
176
|

|
|
172
177
|
|
|
173
178
|
|
|
174
179
|
|
|
175
180
|
|
|
181
|
+
## :rocket: Ultra short code
|
|
182
|
+
|
|
176
183
|
|
|
177
|
-
# Tutorials
|
|
178
184
|
*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)*
|
|
179
185
|
|
|
180
|
-
|
|
186
|
+
|
|
181
187
|
Let's define 3 image processing very basic filters `exposure`, `black_and_white` & `blend`.
|
|
182
188
|
|
|
183
189
|
By design:
|
|
@@ -253,11 +259,11 @@ import numpy as np
|
|
|
253
259
|
|
|
254
260
|
COLOR_DICT = {"red": [1., 0., 0.], "green": [0., 1.,0.], "blue": [0., 0., 1.], "gray": [0.5, 0.5, 0.5]}
|
|
255
261
|
@interactive()
|
|
256
|
-
def generate_flat_colored_image(color_choice=["red", "green", "blue", "gray"],
|
|
262
|
+
def generate_flat_colored_image(color_choice=["red", "green", "blue", "gray"], context={}):
|
|
257
263
|
'''Generate a constant colorful image
|
|
258
264
|
'''
|
|
259
265
|
flat_array = np.array(COLOR_DICT.get(color_choice)) * np.ones((64, 64, 3))
|
|
260
|
-
|
|
266
|
+
context["avg"] = np.average(flat_array)
|
|
261
267
|
return flat_array
|
|
262
268
|
```
|
|
263
269
|
|
|
@@ -266,15 +272,15 @@ def generate_flat_colored_image(color_choice=["red", "green", "blue", "gray"], g
|
|
|
266
272
|
----------
|
|
267
273
|
|
|
268
274
|
:bulb: Can filters communicate together?
|
|
269
|
-
Yes, using the special keyword argument `
|
|
270
|
-
- Check carefully how we stored the image average of the flat image in
|
|
275
|
+
Yes, using the special keyword argument `context={}`.
|
|
276
|
+
- Check carefully how we stored the image average of the flat image in context.
|
|
271
277
|
- This value will be available to other filters.
|
|
272
278
|
`special_image_slice` is going to use that value to set the half bottom image to dark in case the average is high.
|
|
273
279
|
|
|
274
280
|
```python
|
|
275
281
|
@interactive()
|
|
276
|
-
def special_image_slice(img,
|
|
277
|
-
if
|
|
282
|
+
def special_image_slice(img, context={}):
|
|
283
|
+
if context["avg"] > 0.4:
|
|
278
284
|
out_img[out_img.shape[0]//2:, ...] = 0.
|
|
279
285
|
return out_img
|
|
280
286
|
```
|
|
@@ -292,7 +298,7 @@ Note that you can create a filter to switch between several images. In `["pagedo
|
|
|
292
298
|
|
|
293
299
|
```python
|
|
294
300
|
@interactive()
|
|
295
|
-
def black_top_image_slice(img, top_slice_black=(True, "special", "k"),
|
|
301
|
+
def black_top_image_slice(img, top_slice_black=(True, "special", "k"), context={}):
|
|
296
302
|
out_img = img.copy()
|
|
297
303
|
if top_slice_black:
|
|
298
304
|
out_img[:out_img.shape[0]//2, ...] = 0.
|
|
@@ -320,7 +326,47 @@ if __name__ == '__main__':
|
|
|
320
326
|
- September 2024: Gradio backend
|
|
321
327
|
|
|
322
328
|
|
|
329
|
+
### FAQ
|
|
330
|
+
- :question: Is there a difference between `global_params` and `context` ?
|
|
331
|
+
> 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.
|
|
332
|
+
- :question: Do I have to remove `KeyboardSlider` when using gradio or notebook backends?
|
|
333
|
+
> No, don't worry, these will be mapped back to regular sliders!
|
|
334
|
+
- :question: How do I play audio live?
|
|
335
|
+
> :sound: Inside a processing block, write the audio file to disk
|
|
336
|
+
> and use `context["__set_audio"](audio_file)`
|
|
337
|
+
- :question: Do I have to decorate my processing block using the `@interactive`
|
|
338
|
+
> 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)
|
|
339
|
+
```python
|
|
340
|
+
@interactive(angle=(0., [-360., 360.]))
|
|
341
|
+
def processing_block(angle=0.):
|
|
342
|
+
...
|
|
343
|
+
```
|
|
323
344
|
|
|
345
|
+
> An alternative is to decorate the processing block outside... in a file dedicated to interactivity for instance
|
|
346
|
+
```python
|
|
347
|
+
# core_filter.py
|
|
348
|
+
def processing_block(angle=0.):
|
|
349
|
+
...
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
```python
|
|
353
|
+
# graphical.py
|
|
354
|
+
from core_filter import processing_block
|
|
355
|
+
|
|
356
|
+
def add_interactivity():
|
|
357
|
+
interactive(angle=(0., [-360., 360.]))(processing_block)
|
|
358
|
+
```
|
|
359
|
+
- :question: Can I call the pipeline in a command line/batch fashion?
|
|
360
|
+
> Yes, headless mode is supported. :soon: documentation needed.
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
- :question: Can I use inplace operations?
|
|
364
|
+
> 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.
|
|
365
|
+
```python
|
|
366
|
+
# Don't do that!
|
|
367
|
+
def bad_processing_block(inp):
|
|
368
|
+
inp+=1
|
|
369
|
+
```
|
|
324
370
|
# Roadmap and todos
|
|
325
371
|
:bug: Want to contribute or interested in adding new features? Enter a new [Github issue](https://github.com/balthazarneveu/interactive_pipe/issues)
|
|
326
372
|
|
|
@@ -56,10 +56,13 @@ pip install -e ".[full]"
|
|
|
56
56
|
|
|
57
57
|
|
|
58
58
|
----------------------
|
|
59
|
+
## :scroll: Terminology
|
|
60
|
+
|
|
61
|
+

|
|
59
62
|
|
|
60
63
|
## :scroll: Features
|
|
61
64
|
|
|
62
|
-
**Version 0.7.
|
|
65
|
+
**Version 0.7.6**
|
|
63
66
|
- Modular multi-image processing filters
|
|
64
67
|
- Declarative: Easily make graphical user interface without having to learn anything about pyQt or matplotlib
|
|
65
68
|
- Support in jupyter notebooks
|
|
@@ -103,7 +106,7 @@ Shortcuts while using the GUI (QT & matplotlib backends)
|
|
|
103
106
|
| :star: | *PyQt / PySide* | *Matplotlib* | *Jupyter notebooks including Google collab* | *Gradio* |
|
|
104
107
|
|:-----: |:-----:|:------:|:----: |:----: |
|
|
105
108
|
| Backend name | `qt` | `mpl` | `nb`| `gradio` |
|
|
106
|
-
| Preview |  |  |  | |
|
|
109
|
+
| Preview |  |  |  | |
|
|
107
110
|
| Plot curves | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark:|
|
|
108
111
|
| Auto refreshed layout | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_minus_sign: |
|
|
109
112
|
| Keyboard shortcuts / fullscreen| :heavy_check_mark: | :heavy_check_mark: | :heavy_minus_sign: | :heavy_minus_sign: |
|
|
@@ -114,26 +117,29 @@ Shortcuts while using the GUI (QT & matplotlib backends)
|
|
|
114
117
|
|
|
115
118
|
|
|
116
119
|
# Tutorials
|
|
120
|
+
### [Main tutorial](https://huggingface.co/spaces/balthou/interactive-pipe-tutorial)
|
|
121
|
+

|
|
117
122
|
|
|
118
123
|
### Learn by examples
|
|
119
|
-
#### [Basic image processing](/demo/multi_image.py)
|
|
124
|
+
#### [Basic image processing (python code sample for PyQT GUI)](/demo/multi_image.py)
|
|
120
125
|
| GUI | Pipeline |
|
|
121
126
|
|:--:|:--:|
|
|
122
127
|
| |  |
|
|
123
128
|
|
|
124
129
|

|
|
125
130
|
|
|
126
|
-
#### [Speech exploration notebook](https://colab.research.google.com/drive/1mUX2FW0qflWn-v3nIx90P_KvRxnXlBpz#scrollTo=qDTaIwvaJQ6R)
|
|
131
|
+
#### [Speech exploration notebook (colab, signal processing)](https://colab.research.google.com/drive/1mUX2FW0qflWn-v3nIx90P_KvRxnXlBpz#scrollTo=qDTaIwvaJQ6R)
|
|
127
132
|

|
|
128
133
|
|
|
129
134
|
|
|
130
135
|
|
|
131
136
|
|
|
137
|
+
## :rocket: Ultra short code
|
|
138
|
+
|
|
132
139
|
|
|
133
|
-
# Tutorials
|
|
134
140
|
*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)*
|
|
135
141
|
|
|
136
|
-
|
|
142
|
+
|
|
137
143
|
Let's define 3 image processing very basic filters `exposure`, `black_and_white` & `blend`.
|
|
138
144
|
|
|
139
145
|
By design:
|
|
@@ -209,11 +215,11 @@ import numpy as np
|
|
|
209
215
|
|
|
210
216
|
COLOR_DICT = {"red": [1., 0., 0.], "green": [0., 1.,0.], "blue": [0., 0., 1.], "gray": [0.5, 0.5, 0.5]}
|
|
211
217
|
@interactive()
|
|
212
|
-
def generate_flat_colored_image(color_choice=["red", "green", "blue", "gray"],
|
|
218
|
+
def generate_flat_colored_image(color_choice=["red", "green", "blue", "gray"], context={}):
|
|
213
219
|
'''Generate a constant colorful image
|
|
214
220
|
'''
|
|
215
221
|
flat_array = np.array(COLOR_DICT.get(color_choice)) * np.ones((64, 64, 3))
|
|
216
|
-
|
|
222
|
+
context["avg"] = np.average(flat_array)
|
|
217
223
|
return flat_array
|
|
218
224
|
```
|
|
219
225
|
|
|
@@ -222,15 +228,15 @@ def generate_flat_colored_image(color_choice=["red", "green", "blue", "gray"], g
|
|
|
222
228
|
----------
|
|
223
229
|
|
|
224
230
|
:bulb: Can filters communicate together?
|
|
225
|
-
Yes, using the special keyword argument `
|
|
226
|
-
- Check carefully how we stored the image average of the flat image in
|
|
231
|
+
Yes, using the special keyword argument `context={}`.
|
|
232
|
+
- Check carefully how we stored the image average of the flat image in context.
|
|
227
233
|
- This value will be available to other filters.
|
|
228
234
|
`special_image_slice` is going to use that value to set the half bottom image to dark in case the average is high.
|
|
229
235
|
|
|
230
236
|
```python
|
|
231
237
|
@interactive()
|
|
232
|
-
def special_image_slice(img,
|
|
233
|
-
if
|
|
238
|
+
def special_image_slice(img, context={}):
|
|
239
|
+
if context["avg"] > 0.4:
|
|
234
240
|
out_img[out_img.shape[0]//2:, ...] = 0.
|
|
235
241
|
return out_img
|
|
236
242
|
```
|
|
@@ -248,7 +254,7 @@ Note that you can create a filter to switch between several images. In `["pagedo
|
|
|
248
254
|
|
|
249
255
|
```python
|
|
250
256
|
@interactive()
|
|
251
|
-
def black_top_image_slice(img, top_slice_black=(True, "special", "k"),
|
|
257
|
+
def black_top_image_slice(img, top_slice_black=(True, "special", "k"), context={}):
|
|
252
258
|
out_img = img.copy()
|
|
253
259
|
if top_slice_black:
|
|
254
260
|
out_img[:out_img.shape[0]//2, ...] = 0.
|
|
@@ -276,7 +282,47 @@ if __name__ == '__main__':
|
|
|
276
282
|
- September 2024: Gradio backend
|
|
277
283
|
|
|
278
284
|
|
|
285
|
+
### FAQ
|
|
286
|
+
- :question: Is there a difference between `global_params` and `context` ?
|
|
287
|
+
> 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.
|
|
288
|
+
- :question: Do I have to remove `KeyboardSlider` when using gradio or notebook backends?
|
|
289
|
+
> No, don't worry, these will be mapped back to regular sliders!
|
|
290
|
+
- :question: How do I play audio live?
|
|
291
|
+
> :sound: Inside a processing block, write the audio file to disk
|
|
292
|
+
> and use `context["__set_audio"](audio_file)`
|
|
293
|
+
- :question: Do I have to decorate my processing block using the `@interactive`
|
|
294
|
+
> 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)
|
|
295
|
+
```python
|
|
296
|
+
@interactive(angle=(0., [-360., 360.]))
|
|
297
|
+
def processing_block(angle=0.):
|
|
298
|
+
...
|
|
299
|
+
```
|
|
279
300
|
|
|
301
|
+
> An alternative is to decorate the processing block outside... in a file dedicated to interactivity for instance
|
|
302
|
+
```python
|
|
303
|
+
# core_filter.py
|
|
304
|
+
def processing_block(angle=0.):
|
|
305
|
+
...
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
```python
|
|
309
|
+
# graphical.py
|
|
310
|
+
from core_filter import processing_block
|
|
311
|
+
|
|
312
|
+
def add_interactivity():
|
|
313
|
+
interactive(angle=(0., [-360., 360.]))(processing_block)
|
|
314
|
+
```
|
|
315
|
+
- :question: Can I call the pipeline in a command line/batch fashion?
|
|
316
|
+
> Yes, headless mode is supported. :soon: documentation needed.
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
- :question: Can I use inplace operations?
|
|
320
|
+
> 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.
|
|
321
|
+
```python
|
|
322
|
+
# Don't do that!
|
|
323
|
+
def bad_processing_block(inp):
|
|
324
|
+
inp+=1
|
|
325
|
+
```
|
|
280
326
|
# Roadmap and todos
|
|
281
327
|
:bug: Want to contribute or interested in adding new features? Enter a new [Github issue](https://github.com/balthazarneveu/interactive_pipe/issues)
|
|
282
328
|
|
|
@@ -5,6 +5,8 @@ from typing import Callable, List, Optional, Union, Tuple, Any
|
|
|
5
5
|
from interactive_pipe.core.cache import CachedResults
|
|
6
6
|
from interactive_pipe.core.signature import analyze_apply_fn_signature
|
|
7
7
|
|
|
8
|
+
EQUIVALENT_STATE_KEYS = ["global_params", "global_parameters", "global_state", "global_context", "context", "state"]
|
|
9
|
+
|
|
8
10
|
|
|
9
11
|
class PureFilter:
|
|
10
12
|
def __init__(self, apply_fn: Optional[Callable] = None, name: Optional[str] = None, default_params: dict = {}):
|
|
@@ -12,6 +14,7 @@ class PureFilter:
|
|
|
12
14
|
self.__class__.__name__ if not apply_fn else apply_fn.__name__)
|
|
13
15
|
if apply_fn is not None:
|
|
14
16
|
self.apply = apply_fn
|
|
17
|
+
self._global_params = {}
|
|
15
18
|
self.__initialize_default_values() # initialize default values from .apply method
|
|
16
19
|
self.values = deepcopy(default_params)
|
|
17
20
|
|
|
@@ -40,8 +43,9 @@ class PureFilter:
|
|
|
40
43
|
assert not hasattr(self, "_values")
|
|
41
44
|
self.check_apply_signature()
|
|
42
45
|
self._values = self.__kwargs_names
|
|
43
|
-
|
|
44
|
-
self.
|
|
46
|
+
for global_key in EQUIVALENT_STATE_KEYS:
|
|
47
|
+
if global_key in self.__kwargs_names.keys():
|
|
48
|
+
self._values.pop(global_key)
|
|
45
49
|
|
|
46
50
|
@property
|
|
47
51
|
def values(self):
|
|
@@ -60,11 +64,15 @@ class PureFilter:
|
|
|
60
64
|
for key, val in self.values.items():
|
|
61
65
|
assert key in self.__kwargs_names.keys(
|
|
62
66
|
), f"{self.name} : {key} not in {self.__kwargs_names.keys()}"
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
67
|
+
global_key_found = False
|
|
68
|
+
for global_key in EQUIVALENT_STATE_KEYS:
|
|
69
|
+
if global_key in self.__kwargs_names.keys():
|
|
70
|
+
# special key to provide the context dictionary
|
|
71
|
+
out = self.apply(
|
|
72
|
+
*imgs, **{**{global_key: self.global_params}, **self.values})
|
|
73
|
+
global_key_found = True
|
|
74
|
+
break
|
|
75
|
+
if not global_key_found:
|
|
68
76
|
out = self.apply(*imgs, **self.values)
|
|
69
77
|
return out
|
|
70
78
|
|
|
@@ -11,13 +11,40 @@ class PipelineCore:
|
|
|
11
11
|
- optionally, some inputs to process
|
|
12
12
|
"""
|
|
13
13
|
|
|
14
|
-
def __init__(
|
|
14
|
+
def __init__(
|
|
15
|
+
self,
|
|
16
|
+
filters: List[FilterCore],
|
|
17
|
+
name="pipeline",
|
|
18
|
+
cache=False,
|
|
19
|
+
inputs: Optional[list] = None,
|
|
20
|
+
parameters: dict = {},
|
|
21
|
+
global_params: Optional[dict] = None,
|
|
22
|
+
global_parameters: Optional[dict] = None, # alias for global_params
|
|
23
|
+
global_state: Optional[dict] = None, # alias for global_params
|
|
24
|
+
global_context: Optional[dict] = None, # alias for global_params
|
|
25
|
+
context: Optional[dict] = None, # alias for global_params
|
|
26
|
+
state: Optional[dict] = None, # alias for global_params
|
|
27
|
+
outputs: Optional[list] = None,
|
|
28
|
+
safe_input_buffer_deepcopy: bool = True
|
|
29
|
+
):
|
|
15
30
|
if not all(isinstance(f, FilterCore) for f in filters):
|
|
16
31
|
raise ValueError(
|
|
17
32
|
f"All elements in 'filters' must be instances of 'Filter'. {[type(f) for f in filters]}")
|
|
18
33
|
self.filters = filters
|
|
19
34
|
self.engine = PipelineEngine(
|
|
20
35
|
cache, safe_input_buffer_deepcopy=safe_input_buffer_deepcopy)
|
|
36
|
+
if global_parameters is not None:
|
|
37
|
+
global_params = global_parameters
|
|
38
|
+
elif global_context is not None:
|
|
39
|
+
global_params = global_context
|
|
40
|
+
elif global_state is not None:
|
|
41
|
+
global_params = global_state
|
|
42
|
+
elif state is not None:
|
|
43
|
+
global_params = state
|
|
44
|
+
elif context is not None:
|
|
45
|
+
global_params = context
|
|
46
|
+
if global_params is None:
|
|
47
|
+
global_params = {}
|
|
21
48
|
self.global_params = global_params
|
|
22
49
|
for filter in self.filters:
|
|
23
50
|
# link each filter to global params
|
|
@@ -79,7 +106,8 @@ class PipelineCore:
|
|
|
79
106
|
"""
|
|
80
107
|
available_filters_names = [filt.name for filt in self.filters]
|
|
81
108
|
for filter_name in new_parameters.keys():
|
|
82
|
-
assert filter_name in available_filters_names, f"filter {filter_name}
|
|
109
|
+
assert filter_name in available_filters_names, f"filter {filter_name}" + \
|
|
110
|
+
f"does not exist {available_filters_names}"
|
|
83
111
|
self.filters[available_filters_names.index(
|
|
84
112
|
filter_name)].values = new_parameters[filter_name]
|
|
85
113
|
|
|
@@ -99,7 +127,8 @@ class PipelineCore:
|
|
|
99
127
|
elif isinstance(inputs, list) or isinstance(inputs, tuple):
|
|
100
128
|
# inputs is a list or a tuple
|
|
101
129
|
assert len(inputs) == len(
|
|
102
|
-
self.inputs_routing), f"wrong amount of inputs\nprovided {len(inputs)}
|
|
130
|
+
self.inputs_routing), f"wrong amount of inputs\nprovided {len(inputs)}" + \
|
|
131
|
+
f"inputs vs expected:{len(self.inputs_routing)}"
|
|
103
132
|
self.__inputs = {}
|
|
104
133
|
for idx, input_name in enumerate(self.inputs_routing):
|
|
105
134
|
self.__inputs[input_name] = inputs[idx]
|
{interactive_pipe-0.7.4 → interactive_pipe-0.7.6}/src/interactive_pipe/graphical/gradio_gui.py
RENAMED
|
@@ -51,14 +51,20 @@ class InteractivePipeGradio(InteractivePipeGUI):
|
|
|
51
51
|
out_list_gradio_containers = []
|
|
52
52
|
for idx in range(len(out_list)):
|
|
53
53
|
for idy in range(len(out_list[idx])):
|
|
54
|
+
if self.window.image_canvas[idx][idy] is None:
|
|
55
|
+
out_list_gradio_containers.append(gr.HTML())
|
|
56
|
+
continue
|
|
54
57
|
title = self.window.image_canvas[idx][idy].get("title", f"{idx} {idy}")
|
|
55
58
|
title = title.replace("_", " ")
|
|
56
59
|
if isinstance(out_list[idx][idy], np.ndarray):
|
|
57
60
|
out_list_gradio_containers.append(gr.Image(label=title))
|
|
61
|
+
self.window.image_canvas[idx][idy]["type"] = "image"
|
|
58
62
|
elif MPL_SUPPORT and isinstance(out_list[idx][idy], Curve):
|
|
59
63
|
# if out_list[idx][idy].title is not None:
|
|
60
64
|
# title = out_list[idx][idy].title
|
|
61
65
|
out_list_gradio_containers.append(gr.Plot(label=title))
|
|
66
|
+
self.window.image_canvas[idx][idy]["type"] = "curve"
|
|
67
|
+
# @TODO: https://github.com/balthazarneveu/interactive_pipe/issues/50 support audio!
|
|
62
68
|
else:
|
|
63
69
|
raise NotImplementedError(
|
|
64
70
|
f"output type {type(out_list[idx][idy])} not supported")
|
|
@@ -94,7 +100,9 @@ class MainWindow(InteractivePipeWindow):
|
|
|
94
100
|
for idx in range(len(out)):
|
|
95
101
|
if isinstance(out[idx], list):
|
|
96
102
|
for idy in range(len(out[idx])):
|
|
97
|
-
if
|
|
103
|
+
if out[idx][idy] is None:
|
|
104
|
+
flat_out.append("")
|
|
105
|
+
elif MPL_SUPPORT and isinstance(out[idx][idy], Curve):
|
|
98
106
|
curve = out[idx][idy]
|
|
99
107
|
fig, ax = plt.subplots()
|
|
100
108
|
Curve._plot_curve(curve.data, ax=ax)
|
|
@@ -104,10 +112,12 @@ class MainWindow(InteractivePipeWindow):
|
|
|
104
112
|
flat_out.append(self.convert_image(out[idx][idy]))
|
|
105
113
|
else:
|
|
106
114
|
raise NotImplementedError(
|
|
107
|
-
f"output type {type(out[idx][idy])} not
|
|
115
|
+
f"output type {type(out[idx][idy])} not suported")
|
|
108
116
|
else:
|
|
109
117
|
logging.info(f"CONVERTING IMAGE {idx} ")
|
|
110
118
|
flat_out.append(self.convert_image(out[idx]))
|
|
119
|
+
if len(flat_out) == 1:
|
|
120
|
+
return flat_out[0]
|
|
111
121
|
if out is None:
|
|
112
122
|
logging.warning("No output to display")
|
|
113
123
|
return
|
|
@@ -152,9 +162,13 @@ class MainWindow(InteractivePipeWindow):
|
|
|
152
162
|
with gr.Blocks() as io:
|
|
153
163
|
with gr.Row(variant="compact"):
|
|
154
164
|
gr.Markdown("### " + self.name.replace("_", " "))
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
165
|
+
for idy in range(len(self.image_canvas)):
|
|
166
|
+
with gr.Row():
|
|
167
|
+
for idx in range(len(self.image_canvas[idy])):
|
|
168
|
+
elem = outputs[idy * len(self.image_canvas[idy]) + idx]
|
|
169
|
+
if elem is not None:
|
|
170
|
+
elem.render()
|
|
171
|
+
|
|
158
172
|
if self.audio:
|
|
159
173
|
outputs.append(self.audio)
|
|
160
174
|
with gr.Row():
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
from interactive_pipe.headless.pipeline import HeadlessPipeline
|
|
2
|
+
from interactive_pipe.graphical.gui import InteractivePipeGUI
|
|
3
|
+
|
|
4
|
+
import functools
|
|
5
|
+
from typing import Callable, Union, Optional
|
|
6
|
+
from interactive_pipe.helper.choose_backend import get_interactive_pipeline_class
|
|
7
|
+
|
|
8
|
+
None
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def pipeline(pipeline_function: Callable, **kwargs) -> HeadlessPipeline:
|
|
12
|
+
return HeadlessPipeline.from_function(pipeline_function, **kwargs)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def interactive_pipeline(
|
|
16
|
+
gui="auto",
|
|
17
|
+
safe_input_buffer_deepcopy=True,
|
|
18
|
+
cache=False,
|
|
19
|
+
output_canvas=None,
|
|
20
|
+
global_params: Optional[dict] = None,
|
|
21
|
+
global_parameters: Optional[dict] = None, # alias for global_params
|
|
22
|
+
global_state: Optional[dict] = None, # alias for global_params
|
|
23
|
+
global_context: Optional[dict] = None, # alias for global_params
|
|
24
|
+
context: Optional[dict] = None, # alias for global_params
|
|
25
|
+
state: Optional[dict] = None, # alias for global_params
|
|
26
|
+
**kwargs_gui,
|
|
27
|
+
) -> Union[HeadlessPipeline, InteractivePipeGUI]:
|
|
28
|
+
"""@interactive_pipeline
|
|
29
|
+
|
|
30
|
+
Function decorator to add some controls @interactive_pipeline
|
|
31
|
+
"""
|
|
32
|
+
def wrapper(pipeline_function):
|
|
33
|
+
headless_pipeline = HeadlessPipeline.from_function(
|
|
34
|
+
pipeline_function,
|
|
35
|
+
safe_input_buffer_deepcopy=safe_input_buffer_deepcopy,
|
|
36
|
+
cache=cache,
|
|
37
|
+
global_params=global_params,
|
|
38
|
+
global_parameters=global_parameters, # alias for global_params
|
|
39
|
+
global_state=global_state, # alias for global_params
|
|
40
|
+
global_context=global_context, # alias for global_params
|
|
41
|
+
context=context, # alias for global_params
|
|
42
|
+
state=state, # alias for global_params
|
|
43
|
+
)
|
|
44
|
+
if output_canvas is not None:
|
|
45
|
+
headless_pipeline.outputs = output_canvas
|
|
46
|
+
if gui is None:
|
|
47
|
+
return headless_pipeline
|
|
48
|
+
else:
|
|
49
|
+
InteractivePipeGui = get_interactive_pipeline_class(gui)
|
|
50
|
+
gui_pipeline = InteractivePipeGui(
|
|
51
|
+
pipeline=headless_pipeline, name=pipeline_function.__name__, **kwargs_gui)
|
|
52
|
+
|
|
53
|
+
@ functools.wraps(pipeline_function)
|
|
54
|
+
def inner(*args, **kwargs):
|
|
55
|
+
return gui_pipeline.__call__(*args, **kwargs)
|
|
56
|
+
return inner
|
|
57
|
+
return wrapper
|
|
@@ -6,7 +6,12 @@ from typing import Union
|
|
|
6
6
|
from interactive_pipe.data_objects.parameters import Parameters
|
|
7
7
|
from interactive_pipe.data_objects.image import Image
|
|
8
8
|
import os
|
|
9
|
-
|
|
9
|
+
OPENAI_SUPPORT = True
|
|
10
|
+
try:
|
|
11
|
+
import openai
|
|
12
|
+
except ImportError:
|
|
13
|
+
OPENAI_SUPPORT = False
|
|
14
|
+
logging.warning("openai not installed, please install it with pip install openai")
|
|
10
15
|
|
|
11
16
|
|
|
12
17
|
class ImageFromPrompt(Image):
|
|
@@ -57,6 +62,7 @@ class ImageFromPrompt(Image):
|
|
|
57
62
|
|
|
58
63
|
@staticmethod
|
|
59
64
|
def login(api_key=None, organization=None):
|
|
65
|
+
assert OPENAI_SUPPORT, "openai not installed"
|
|
60
66
|
if api_key is not None:
|
|
61
67
|
openai.api_key = api_key
|
|
62
68
|
openai.organization = organization
|
|
@@ -75,6 +81,7 @@ class ImageFromPrompt(Image):
|
|
|
75
81
|
|
|
76
82
|
@staticmethod
|
|
77
83
|
def __check_login():
|
|
84
|
+
assert OPENAI_SUPPORT, "openai not installed"
|
|
78
85
|
assert openai.api_key is not None
|
|
79
86
|
try:
|
|
80
87
|
response = openai.Model.list()
|
|
@@ -95,6 +102,7 @@ class ImageFromPrompt(Image):
|
|
|
95
102
|
path = Path(path)
|
|
96
103
|
path = path.with_suffix(ImageFromPrompt.SUFFIX)
|
|
97
104
|
return path
|
|
105
|
+
assert OPENAI_SUPPORT, "openai not installed"
|
|
98
106
|
try:
|
|
99
107
|
# You need the API key at this moment
|
|
100
108
|
ImageFromPrompt.login(api_key=api_key)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: interactive_pipe
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.6
|
|
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
|
|
@@ -100,10 +100,13 @@ pip install -e ".[full]"
|
|
|
100
100
|
|
|
101
101
|
|
|
102
102
|
----------------------
|
|
103
|
+
## :scroll: Terminology
|
|
104
|
+
|
|
105
|
+

|
|
103
106
|
|
|
104
107
|
## :scroll: Features
|
|
105
108
|
|
|
106
|
-
**Version 0.7.
|
|
109
|
+
**Version 0.7.6**
|
|
107
110
|
- Modular multi-image processing filters
|
|
108
111
|
- Declarative: Easily make graphical user interface without having to learn anything about pyQt or matplotlib
|
|
109
112
|
- Support in jupyter notebooks
|
|
@@ -147,7 +150,7 @@ Shortcuts while using the GUI (QT & matplotlib backends)
|
|
|
147
150
|
| :star: | *PyQt / PySide* | *Matplotlib* | *Jupyter notebooks including Google collab* | *Gradio* |
|
|
148
151
|
|:-----: |:-----:|:------:|:----: |:----: |
|
|
149
152
|
| Backend name | `qt` | `mpl` | `nb`| `gradio` |
|
|
150
|
-
| Preview |  |  |  | |
|
|
153
|
+
| Preview |  |  |  | |
|
|
151
154
|
| Plot curves | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark:|
|
|
152
155
|
| Auto refreshed layout | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_minus_sign: |
|
|
153
156
|
| Keyboard shortcuts / fullscreen| :heavy_check_mark: | :heavy_check_mark: | :heavy_minus_sign: | :heavy_minus_sign: |
|
|
@@ -158,26 +161,29 @@ Shortcuts while using the GUI (QT & matplotlib backends)
|
|
|
158
161
|
|
|
159
162
|
|
|
160
163
|
# Tutorials
|
|
164
|
+
### [Main tutorial](https://huggingface.co/spaces/balthou/interactive-pipe-tutorial)
|
|
165
|
+

|
|
161
166
|
|
|
162
167
|
### Learn by examples
|
|
163
|
-
#### [Basic image processing](/demo/multi_image.py)
|
|
168
|
+
#### [Basic image processing (python code sample for PyQT GUI)](/demo/multi_image.py)
|
|
164
169
|
| GUI | Pipeline |
|
|
165
170
|
|:--:|:--:|
|
|
166
171
|
| |  |
|
|
167
172
|
|
|
168
173
|

|
|
169
174
|
|
|
170
|
-
#### [Speech exploration notebook](https://colab.research.google.com/drive/1mUX2FW0qflWn-v3nIx90P_KvRxnXlBpz#scrollTo=qDTaIwvaJQ6R)
|
|
175
|
+
#### [Speech exploration notebook (colab, signal processing)](https://colab.research.google.com/drive/1mUX2FW0qflWn-v3nIx90P_KvRxnXlBpz#scrollTo=qDTaIwvaJQ6R)
|
|
171
176
|

|
|
172
177
|
|
|
173
178
|
|
|
174
179
|
|
|
175
180
|
|
|
181
|
+
## :rocket: Ultra short code
|
|
182
|
+
|
|
176
183
|
|
|
177
|
-
# Tutorials
|
|
178
184
|
*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)*
|
|
179
185
|
|
|
180
|
-
|
|
186
|
+
|
|
181
187
|
Let's define 3 image processing very basic filters `exposure`, `black_and_white` & `blend`.
|
|
182
188
|
|
|
183
189
|
By design:
|
|
@@ -253,11 +259,11 @@ import numpy as np
|
|
|
253
259
|
|
|
254
260
|
COLOR_DICT = {"red": [1., 0., 0.], "green": [0., 1.,0.], "blue": [0., 0., 1.], "gray": [0.5, 0.5, 0.5]}
|
|
255
261
|
@interactive()
|
|
256
|
-
def generate_flat_colored_image(color_choice=["red", "green", "blue", "gray"],
|
|
262
|
+
def generate_flat_colored_image(color_choice=["red", "green", "blue", "gray"], context={}):
|
|
257
263
|
'''Generate a constant colorful image
|
|
258
264
|
'''
|
|
259
265
|
flat_array = np.array(COLOR_DICT.get(color_choice)) * np.ones((64, 64, 3))
|
|
260
|
-
|
|
266
|
+
context["avg"] = np.average(flat_array)
|
|
261
267
|
return flat_array
|
|
262
268
|
```
|
|
263
269
|
|
|
@@ -266,15 +272,15 @@ def generate_flat_colored_image(color_choice=["red", "green", "blue", "gray"], g
|
|
|
266
272
|
----------
|
|
267
273
|
|
|
268
274
|
:bulb: Can filters communicate together?
|
|
269
|
-
Yes, using the special keyword argument `
|
|
270
|
-
- Check carefully how we stored the image average of the flat image in
|
|
275
|
+
Yes, using the special keyword argument `context={}`.
|
|
276
|
+
- Check carefully how we stored the image average of the flat image in context.
|
|
271
277
|
- This value will be available to other filters.
|
|
272
278
|
`special_image_slice` is going to use that value to set the half bottom image to dark in case the average is high.
|
|
273
279
|
|
|
274
280
|
```python
|
|
275
281
|
@interactive()
|
|
276
|
-
def special_image_slice(img,
|
|
277
|
-
if
|
|
282
|
+
def special_image_slice(img, context={}):
|
|
283
|
+
if context["avg"] > 0.4:
|
|
278
284
|
out_img[out_img.shape[0]//2:, ...] = 0.
|
|
279
285
|
return out_img
|
|
280
286
|
```
|
|
@@ -292,7 +298,7 @@ Note that you can create a filter to switch between several images. In `["pagedo
|
|
|
292
298
|
|
|
293
299
|
```python
|
|
294
300
|
@interactive()
|
|
295
|
-
def black_top_image_slice(img, top_slice_black=(True, "special", "k"),
|
|
301
|
+
def black_top_image_slice(img, top_slice_black=(True, "special", "k"), context={}):
|
|
296
302
|
out_img = img.copy()
|
|
297
303
|
if top_slice_black:
|
|
298
304
|
out_img[:out_img.shape[0]//2, ...] = 0.
|
|
@@ -320,7 +326,47 @@ if __name__ == '__main__':
|
|
|
320
326
|
- September 2024: Gradio backend
|
|
321
327
|
|
|
322
328
|
|
|
329
|
+
### FAQ
|
|
330
|
+
- :question: Is there a difference between `global_params` and `context` ?
|
|
331
|
+
> 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.
|
|
332
|
+
- :question: Do I have to remove `KeyboardSlider` when using gradio or notebook backends?
|
|
333
|
+
> No, don't worry, these will be mapped back to regular sliders!
|
|
334
|
+
- :question: How do I play audio live?
|
|
335
|
+
> :sound: Inside a processing block, write the audio file to disk
|
|
336
|
+
> and use `context["__set_audio"](audio_file)`
|
|
337
|
+
- :question: Do I have to decorate my processing block using the `@interactive`
|
|
338
|
+
> 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)
|
|
339
|
+
```python
|
|
340
|
+
@interactive(angle=(0., [-360., 360.]))
|
|
341
|
+
def processing_block(angle=0.):
|
|
342
|
+
...
|
|
343
|
+
```
|
|
323
344
|
|
|
345
|
+
> An alternative is to decorate the processing block outside... in a file dedicated to interactivity for instance
|
|
346
|
+
```python
|
|
347
|
+
# core_filter.py
|
|
348
|
+
def processing_block(angle=0.):
|
|
349
|
+
...
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
```python
|
|
353
|
+
# graphical.py
|
|
354
|
+
from core_filter import processing_block
|
|
355
|
+
|
|
356
|
+
def add_interactivity():
|
|
357
|
+
interactive(angle=(0., [-360., 360.]))(processing_block)
|
|
358
|
+
```
|
|
359
|
+
- :question: Can I call the pipeline in a command line/batch fashion?
|
|
360
|
+
> Yes, headless mode is supported. :soon: documentation needed.
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
- :question: Can I use inplace operations?
|
|
364
|
+
> 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.
|
|
365
|
+
```python
|
|
366
|
+
# Don't do that!
|
|
367
|
+
def bad_processing_block(inp):
|
|
368
|
+
inp+=1
|
|
369
|
+
```
|
|
324
370
|
# Roadmap and todos
|
|
325
371
|
:bug: Want to contribute or interested in adding new features? Enter a new [Github issue](https://github.com/balthazarneveu/interactive_pipe/issues)
|
|
326
372
|
|
|
@@ -7,7 +7,7 @@ class MultiplyFilter(FilterCore):
|
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
def test_pure_filter():
|
|
10
|
-
def mult(img,
|
|
10
|
+
def mult(img, context={}, scalar=8, add=5):
|
|
11
11
|
return [[value * scalar + add for value in row] for row in img]
|
|
12
12
|
filt = PureFilter(apply_fn=mult, name="multiplication",
|
|
13
13
|
default_params={"scalar": 2, "add": 0})
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
from interactive_pipe.headless.pipeline import HeadlessPipeline
|
|
2
|
-
from interactive_pipe.graphical.gui import InteractivePipeGUI
|
|
3
|
-
|
|
4
|
-
import functools
|
|
5
|
-
from typing import Callable, Union
|
|
6
|
-
from interactive_pipe.helper.choose_backend import get_interactive_pipeline_class
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
def pipeline(pipeline_function: Callable, **kwargs) -> HeadlessPipeline:
|
|
10
|
-
return HeadlessPipeline.from_function(pipeline_function, **kwargs)
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
def interactive_pipeline(gui="auto", safe_input_buffer_deepcopy=True, cache=False, output_canvas=None, **kwargs_gui) -> Union[HeadlessPipeline, InteractivePipeGUI]:
|
|
14
|
-
"""@interactive_pipeline
|
|
15
|
-
|
|
16
|
-
Function decorator to add some controls @interactive_pipeline
|
|
17
|
-
"""
|
|
18
|
-
def wrapper(pipeline_function):
|
|
19
|
-
headless_pipeline = HeadlessPipeline.from_function(
|
|
20
|
-
pipeline_function, safe_input_buffer_deepcopy=safe_input_buffer_deepcopy, cache=cache)
|
|
21
|
-
if output_canvas is not None:
|
|
22
|
-
headless_pipeline.outputs = output_canvas
|
|
23
|
-
if gui is None:
|
|
24
|
-
return headless_pipeline
|
|
25
|
-
else:
|
|
26
|
-
InteractivePipeGui = get_interactive_pipeline_class(gui)
|
|
27
|
-
gui_pipeline = InteractivePipeGui(
|
|
28
|
-
pipeline=headless_pipeline, name=pipeline_function.__name__, **kwargs_gui)
|
|
29
|
-
|
|
30
|
-
@functools.wraps(pipeline_function)
|
|
31
|
-
def inner(*args, **kwargs):
|
|
32
|
-
return gui_pipeline.__call__(*args, **kwargs)
|
|
33
|
-
return inner
|
|
34
|
-
return wrapper
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{interactive_pipe-0.7.4 → interactive_pipe-0.7.6}/src/interactive_pipe/data_objects/__init__.py
RENAMED
|
File without changes
|
{interactive_pipe-0.7.4 → interactive_pipe-0.7.6}/src/interactive_pipe/data_objects/audio.py
RENAMED
|
File without changes
|
{interactive_pipe-0.7.4 → interactive_pipe-0.7.6}/src/interactive_pipe/data_objects/curves.py
RENAMED
|
File without changes
|
|
File without changes
|
{interactive_pipe-0.7.4 → interactive_pipe-0.7.6}/src/interactive_pipe/data_objects/image.py
RENAMED
|
File without changes
|
{interactive_pipe-0.7.4 → interactive_pipe-0.7.6}/src/interactive_pipe/data_objects/parameters.py
RENAMED
|
File without changes
|
{interactive_pipe-0.7.4 → interactive_pipe-0.7.6}/src/interactive_pipe/graphical/__init__.py
RENAMED
|
File without changes
|
{interactive_pipe-0.7.4 → interactive_pipe-0.7.6}/src/interactive_pipe/graphical/gradio_control.py
RENAMED
|
File without changes
|
|
File without changes
|
{interactive_pipe-0.7.4 → interactive_pipe-0.7.6}/src/interactive_pipe/graphical/mpl_control.py
RENAMED
|
File without changes
|
|
File without changes
|
{interactive_pipe-0.7.4 → interactive_pipe-0.7.6}/src/interactive_pipe/graphical/mpl_window.py
RENAMED
|
File without changes
|
{interactive_pipe-0.7.4 → interactive_pipe-0.7.6}/src/interactive_pipe/graphical/nb_control.py
RENAMED
|
File without changes
|
|
File without changes
|
{interactive_pipe-0.7.4 → interactive_pipe-0.7.6}/src/interactive_pipe/graphical/qt_control.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{interactive_pipe-0.7.4 → interactive_pipe-0.7.6}/src/interactive_pipe/helper/choose_backend.py
RENAMED
|
File without changes
|
|
File without changes
|
{interactive_pipe-0.7.4 → interactive_pipe-0.7.6}/src/interactive_pipe/helper/filter_decorator.py
RENAMED
|
File without changes
|
|
File without changes
|
{interactive_pipe-0.7.4 → interactive_pipe-0.7.6}/src/interactive_pipe/thirdparty/music_spotify.py
RENAMED
|
File without changes
|
|
File without changes
|
{interactive_pipe-0.7.4 → interactive_pipe-0.7.6}/src/interactive_pipe.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{interactive_pipe-0.7.4 → interactive_pipe-0.7.6}/src/interactive_pipe.egg-info/requires.txt
RENAMED
|
File without changes
|
{interactive_pipe-0.7.4 → interactive_pipe-0.7.6}/src/interactive_pipe.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|