interactive-pipe 0.7.6__tar.gz → 0.7.7__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.6/src/interactive_pipe.egg-info → interactive_pipe-0.7.7}/PKG-INFO +3 -10
- {interactive_pipe-0.7.6 → interactive_pipe-0.7.7}/pyproject.toml +2 -9
- {interactive_pipe-0.7.6 → interactive_pipe-0.7.7}/readme.md +2 -2
- interactive_pipe-0.7.7/src/interactive_pipe/data_objects/audio.py +89 -0
- {interactive_pipe-0.7.6 → interactive_pipe-0.7.7}/src/interactive_pipe/graphical/gradio_gui.py +14 -5
- {interactive_pipe-0.7.6 → interactive_pipe-0.7.7}/src/interactive_pipe/graphical/mpl_window.py +11 -4
- {interactive_pipe-0.7.6 → interactive_pipe-0.7.7}/src/interactive_pipe/graphical/qt_gui.py +20 -4
- {interactive_pipe-0.7.6 → interactive_pipe-0.7.7/src/interactive_pipe.egg-info}/PKG-INFO +3 -10
- {interactive_pipe-0.7.6 → interactive_pipe-0.7.7}/src/interactive_pipe.egg-info/requires.txt +0 -7
- interactive_pipe-0.7.6/src/interactive_pipe/data_objects/audio.py +0 -35
- {interactive_pipe-0.7.6 → interactive_pipe-0.7.7}/LICENSE +0 -0
- {interactive_pipe-0.7.6 → interactive_pipe-0.7.7}/setup.cfg +0 -0
- {interactive_pipe-0.7.6 → interactive_pipe-0.7.7}/src/interactive_pipe/__init__.py +0 -0
- {interactive_pipe-0.7.6 → interactive_pipe-0.7.7}/src/interactive_pipe/core/__init__.py +0 -0
- {interactive_pipe-0.7.6 → interactive_pipe-0.7.7}/src/interactive_pipe/core/cache.py +0 -0
- {interactive_pipe-0.7.6 → interactive_pipe-0.7.7}/src/interactive_pipe/core/engine.py +0 -0
- {interactive_pipe-0.7.6 → interactive_pipe-0.7.7}/src/interactive_pipe/core/filter.py +0 -0
- {interactive_pipe-0.7.6 → interactive_pipe-0.7.7}/src/interactive_pipe/core/graph.py +0 -0
- {interactive_pipe-0.7.6 → interactive_pipe-0.7.7}/src/interactive_pipe/core/pipeline.py +0 -0
- {interactive_pipe-0.7.6 → interactive_pipe-0.7.7}/src/interactive_pipe/core/signature.py +0 -0
- {interactive_pipe-0.7.6 → interactive_pipe-0.7.7}/src/interactive_pipe/data_objects/__init__.py +0 -0
- {interactive_pipe-0.7.6 → interactive_pipe-0.7.7}/src/interactive_pipe/data_objects/curves.py +0 -0
- {interactive_pipe-0.7.6 → interactive_pipe-0.7.7}/src/interactive_pipe/data_objects/data.py +0 -0
- {interactive_pipe-0.7.6 → interactive_pipe-0.7.7}/src/interactive_pipe/data_objects/image.py +0 -0
- {interactive_pipe-0.7.6 → interactive_pipe-0.7.7}/src/interactive_pipe/data_objects/parameters.py +0 -0
- {interactive_pipe-0.7.6 → interactive_pipe-0.7.7}/src/interactive_pipe/graphical/__init__.py +0 -0
- {interactive_pipe-0.7.6 → interactive_pipe-0.7.7}/src/interactive_pipe/graphical/gradio_control.py +0 -0
- {interactive_pipe-0.7.6 → interactive_pipe-0.7.7}/src/interactive_pipe/graphical/gui.py +0 -0
- {interactive_pipe-0.7.6 → interactive_pipe-0.7.7}/src/interactive_pipe/graphical/mpl_control.py +0 -0
- {interactive_pipe-0.7.6 → interactive_pipe-0.7.7}/src/interactive_pipe/graphical/mpl_gui.py +0 -0
- {interactive_pipe-0.7.6 → interactive_pipe-0.7.7}/src/interactive_pipe/graphical/nb_control.py +0 -0
- {interactive_pipe-0.7.6 → interactive_pipe-0.7.7}/src/interactive_pipe/graphical/nb_gui.py +0 -0
- {interactive_pipe-0.7.6 → interactive_pipe-0.7.7}/src/interactive_pipe/graphical/qt_control.py +0 -0
- {interactive_pipe-0.7.6 → interactive_pipe-0.7.7}/src/interactive_pipe/graphical/window.py +0 -0
- {interactive_pipe-0.7.6 → interactive_pipe-0.7.7}/src/interactive_pipe/headless/__init__.py +0 -0
- {interactive_pipe-0.7.6 → interactive_pipe-0.7.7}/src/interactive_pipe/headless/control.py +0 -0
- {interactive_pipe-0.7.6 → interactive_pipe-0.7.7}/src/interactive_pipe/headless/keyboard.py +0 -0
- {interactive_pipe-0.7.6 → interactive_pipe-0.7.7}/src/interactive_pipe/headless/pipeline.py +0 -0
- {interactive_pipe-0.7.6 → interactive_pipe-0.7.7}/src/interactive_pipe/helper/__init__.py +0 -0
- {interactive_pipe-0.7.6 → interactive_pipe-0.7.7}/src/interactive_pipe/helper/_private.py +0 -0
- {interactive_pipe-0.7.6 → interactive_pipe-0.7.7}/src/interactive_pipe/helper/choose_backend.py +0 -0
- {interactive_pipe-0.7.6 → interactive_pipe-0.7.7}/src/interactive_pipe/helper/control_abbreviation.py +0 -0
- {interactive_pipe-0.7.6 → interactive_pipe-0.7.7}/src/interactive_pipe/helper/filter_decorator.py +0 -0
- {interactive_pipe-0.7.6 → interactive_pipe-0.7.7}/src/interactive_pipe/helper/keyword_args_analyzer.py +0 -0
- {interactive_pipe-0.7.6 → interactive_pipe-0.7.7}/src/interactive_pipe/helper/pipeline_decorator.py +0 -0
- {interactive_pipe-0.7.6 → interactive_pipe-0.7.7}/src/interactive_pipe/thirdparty/images_openai_api.py +0 -0
- {interactive_pipe-0.7.6 → interactive_pipe-0.7.7}/src/interactive_pipe/thirdparty/music_spotify.py +0 -0
- {interactive_pipe-0.7.6 → interactive_pipe-0.7.7}/src/interactive_pipe.egg-info/SOURCES.txt +0 -0
- {interactive_pipe-0.7.6 → interactive_pipe-0.7.7}/src/interactive_pipe.egg-info/dependency_links.txt +0 -0
- {interactive_pipe-0.7.6 → interactive_pipe-0.7.7}/src/interactive_pipe.egg-info/top_level.txt +0 -0
- {interactive_pipe-0.7.6 → interactive_pipe-0.7.7}/test/test_cache.py +0 -0
- {interactive_pipe-0.7.6 → interactive_pipe-0.7.7}/test/test_control_abbreviations.py +0 -0
- {interactive_pipe-0.7.6 → interactive_pipe-0.7.7}/test/test_controller.py +0 -0
- {interactive_pipe-0.7.6 → interactive_pipe-0.7.7}/test/test_core.py +0 -0
- {interactive_pipe-0.7.6 → interactive_pipe-0.7.7}/test/test_curves.py +0 -0
- {interactive_pipe-0.7.6 → interactive_pipe-0.7.7}/test/test_decorator.py +0 -0
- {interactive_pipe-0.7.6 → interactive_pipe-0.7.7}/test/test_engine.py +0 -0
- {interactive_pipe-0.7.6 → interactive_pipe-0.7.7}/test/test_filter.py +0 -0
- {interactive_pipe-0.7.6 → interactive_pipe-0.7.7}/test/test_headless.py +0 -0
- {interactive_pipe-0.7.6 → interactive_pipe-0.7.7}/test/test_image.py +0 -0
- {interactive_pipe-0.7.6 → interactive_pipe-0.7.7}/test/test_parameters.py +0 -0
- {interactive_pipe-0.7.6 → interactive_pipe-0.7.7}/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.7
|
|
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>=8.0.7
|
|
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"
|
|
@@ -106,7 +99,7 @@ pip install -e ".[full]"
|
|
|
106
99
|
|
|
107
100
|
## :scroll: Features
|
|
108
101
|
|
|
109
|
-
**Version 0.7.
|
|
102
|
+
**Version 0.7.7**
|
|
110
103
|
- Modular multi-image processing filters
|
|
111
104
|
- Declarative: Easily make graphical user interface without having to learn anything about pyQt or matplotlib
|
|
112
105
|
- Support in jupyter notebooks
|
|
@@ -115,7 +108,7 @@ pip install -e ".[full]"
|
|
|
115
108
|
- `KeyboardControl` : no slider on UI but exactly the same internal mechanism, update on key press.
|
|
116
109
|
- Support Curve plots (2D signals)
|
|
117
110
|
- :new: gradio backend (+allows sharing with others).
|
|
118
|
-
- :new: Audio support in Gradio
|
|
111
|
+
- :new: Audio support in Gradio (live audio or display several players by returning 1D numpy arrays)
|
|
119
112
|
- :new: Circular sliders for Qt Backend
|
|
120
113
|
|
|
121
114
|
|
|
@@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
|
|
|
5
5
|
|
|
6
6
|
[project]
|
|
7
7
|
name = "interactive_pipe"
|
|
8
|
-
version = "0.7.
|
|
8
|
+
version = "0.7.7"
|
|
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>=8.0.7",
|
|
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!"
|
|
@@ -64,4 +57,4 @@ full=[
|
|
|
64
57
|
"ipywidgets>=8.0.7",
|
|
65
58
|
"pandas",
|
|
66
59
|
"gradio",
|
|
67
|
-
]
|
|
60
|
+
]
|
|
@@ -62,7 +62,7 @@ pip install -e ".[full]"
|
|
|
62
62
|
|
|
63
63
|
## :scroll: Features
|
|
64
64
|
|
|
65
|
-
**Version 0.7.
|
|
65
|
+
**Version 0.7.7**
|
|
66
66
|
- Modular multi-image processing filters
|
|
67
67
|
- Declarative: Easily make graphical user interface without having to learn anything about pyQt or matplotlib
|
|
68
68
|
- Support in jupyter notebooks
|
|
@@ -71,7 +71,7 @@ pip install -e ".[full]"
|
|
|
71
71
|
- `KeyboardControl` : no slider on UI but exactly the same internal mechanism, update on key press.
|
|
72
72
|
- Support Curve plots (2D signals)
|
|
73
73
|
- :new: gradio backend (+allows sharing with others).
|
|
74
|
-
- :new: Audio support in Gradio
|
|
74
|
+
- :new: Audio support in Gradio (live audio or display several players by returning 1D numpy arrays)
|
|
75
75
|
- :new: Circular sliders for Qt Backend
|
|
76
76
|
|
|
77
77
|
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from interactive_pipe.data_objects.data import Data
|
|
3
|
+
import numpy as np
|
|
4
|
+
from io import BytesIO
|
|
5
|
+
import base64
|
|
6
|
+
import logging
|
|
7
|
+
from typing import Union, Tuple
|
|
8
|
+
WAVIO_AVAILABLE = False
|
|
9
|
+
WAVIO = "wavio"
|
|
10
|
+
try:
|
|
11
|
+
WAVIO_AVAILABLE = True
|
|
12
|
+
import wavio
|
|
13
|
+
except ImportError:
|
|
14
|
+
logging.info("Cannot import wavio")
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def audio_to_html(audio: Union[None, str, Path, Tuple[int, np.ndarray]], controls=True) -> str:
|
|
18
|
+
if audio is None:
|
|
19
|
+
logging.debug("No audio to display")
|
|
20
|
+
return ""
|
|
21
|
+
if isinstance(audio, str) or isinstance(audio, Path):
|
|
22
|
+
audio_base64 = base64.b64encode(open(audio, "rb").read()).decode("utf-8")
|
|
23
|
+
elif isinstance(audio, tuple):
|
|
24
|
+
assert WAVIO_AVAILABLE, "wavio is not available"
|
|
25
|
+
assert len(audio) == 2, "audio tuple should have 2 elements: (rate, data)"
|
|
26
|
+
assert isinstance(audio[0], int), "audio[0] should be an integer"
|
|
27
|
+
assert isinstance(audio[1], np.ndarray), "audio[1] should be a numpy array"
|
|
28
|
+
audio_bytes = BytesIO()
|
|
29
|
+
wavio.write(audio_bytes, audio[1].astype(np.float32), audio[0], sampwidth=4)
|
|
30
|
+
audio_bytes.seek(0)
|
|
31
|
+
audio_base64 = base64.b64encode(audio_bytes.read()).decode("utf-8")
|
|
32
|
+
audio_player = f'<audio src="data:audio/mpeg;base64,{audio_base64}"'
|
|
33
|
+
if controls:
|
|
34
|
+
audio_player += ' controls'
|
|
35
|
+
audio_player += ' autoplay></audio>'
|
|
36
|
+
|
|
37
|
+
return audio_player
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class Audio(Data):
|
|
41
|
+
def __init__(self, data, sampling_rate: int = None, title="") -> None:
|
|
42
|
+
super().__init__(data)
|
|
43
|
+
self.title = title
|
|
44
|
+
self.path = None
|
|
45
|
+
self.sampling_rate = sampling_rate
|
|
46
|
+
|
|
47
|
+
def _set_file_extensions(self):
|
|
48
|
+
self.file_extensions = [".wav", ".mp3", ".mp4"]
|
|
49
|
+
|
|
50
|
+
def _save(self, path: Path, backend=None):
|
|
51
|
+
assert path is not None, "Save requires a path"
|
|
52
|
+
if self.title is not None:
|
|
53
|
+
self.path = self.append_with_stem(path, self.title)
|
|
54
|
+
else:
|
|
55
|
+
self.path = path
|
|
56
|
+
self.save_audio(self.data, self.path, self.sampling_rate, backend=backend)
|
|
57
|
+
|
|
58
|
+
def _load(self, path: Path, backend=None, title=None) -> Tuple[int, np.ndarray]:
|
|
59
|
+
if title is not None:
|
|
60
|
+
self.title = title
|
|
61
|
+
self.path = path
|
|
62
|
+
self.sampling_rate, audio_data = self.load_audio(path, backend=backend)
|
|
63
|
+
return self.sampling_rate, audio_data
|
|
64
|
+
|
|
65
|
+
@staticmethod
|
|
66
|
+
def save_audio(data, path: Path, sampling_rate=None, backend=None):
|
|
67
|
+
if backend is None:
|
|
68
|
+
backend = WAVIO
|
|
69
|
+
if backend == WAVIO:
|
|
70
|
+
assert WAVIO_AVAILABLE, "wavio is not available"
|
|
71
|
+
wavio.write(path, data, sampling_rate)
|
|
72
|
+
else:
|
|
73
|
+
raise NotImplementedError(f"Unknown backend: {backend}")
|
|
74
|
+
|
|
75
|
+
@staticmethod
|
|
76
|
+
def load_audio(path: Path, backend=None) -> np.ndarray:
|
|
77
|
+
if backend is None:
|
|
78
|
+
backend = WAVIO
|
|
79
|
+
if backend == WAVIO:
|
|
80
|
+
assert WAVIO_AVAILABLE, "wavio is not available"
|
|
81
|
+
audio = wavio.read(str(path))
|
|
82
|
+
return audio.rate, audio.data
|
|
83
|
+
else:
|
|
84
|
+
raise NotImplementedError(f"Unknown backend: {backend}")
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
if __name__ == "__main__":
|
|
88
|
+
audio_sample_path = Path("demo")/"audio"/"rabbit.mp4"
|
|
89
|
+
rate, data = Audio.load_audio(audio_sample_path)
|
{interactive_pipe-0.7.6 → interactive_pipe-0.7.7}/src/interactive_pipe/graphical/gradio_gui.py
RENAMED
|
@@ -57,8 +57,12 @@ class InteractivePipeGradio(InteractivePipeGUI):
|
|
|
57
57
|
title = self.window.image_canvas[idx][idy].get("title", f"{idx} {idy}")
|
|
58
58
|
title = title.replace("_", " ")
|
|
59
59
|
if isinstance(out_list[idx][idy], np.ndarray):
|
|
60
|
-
|
|
61
|
-
|
|
60
|
+
if len(out_list[idx][idy].shape) == 1:
|
|
61
|
+
out_list_gradio_containers.append(gr.Audio(label=title))
|
|
62
|
+
self.window.image_canvas[idx][idy]["type"] = "audio"
|
|
63
|
+
else:
|
|
64
|
+
out_list_gradio_containers.append(gr.Image(label=title))
|
|
65
|
+
self.window.image_canvas[idx][idy]["type"] = "image"
|
|
62
66
|
elif MPL_SUPPORT and isinstance(out_list[idx][idy], Curve):
|
|
63
67
|
# if out_list[idx][idy].title is not None:
|
|
64
68
|
# title = out_list[idx][idy].title
|
|
@@ -81,7 +85,7 @@ class InteractivePipeGradio(InteractivePipeGUI):
|
|
|
81
85
|
|
|
82
86
|
|
|
83
87
|
class MainWindow(InteractivePipeWindow):
|
|
84
|
-
def __init__(self, *args, controls=[], name="", pipeline: HeadlessPipeline = None, size=None, share_gradio_app=False, markdown_description=None, sliders_layout=None, sliders_per_row_layout=None, audio=False, **kwargs):
|
|
88
|
+
def __init__(self, *args, controls=[], name="", pipeline: HeadlessPipeline = None, size=None, share_gradio_app=False, markdown_description=None, sliders_layout=None, sliders_per_row_layout=None, audio=False, audio_sampling_rate: int =44100, **kwargs):
|
|
85
89
|
InteractivePipeWindow.__init__(
|
|
86
90
|
self, name=name, pipeline=pipeline, size=size)
|
|
87
91
|
self.markdown_description = markdown_description
|
|
@@ -93,6 +97,7 @@ class MainWindow(InteractivePipeWindow):
|
|
|
93
97
|
self.pipeline = pipeline
|
|
94
98
|
self.share_gradio_app = share_gradio_app
|
|
95
99
|
self.audio = audio
|
|
100
|
+
self.audio_sampling_rate = audio_sampling_rate
|
|
96
101
|
# Define the functions that will be called when the input changes for gradio. => gr.Interface(fn=process_fn)
|
|
97
102
|
|
|
98
103
|
def process_outputs_fn(out) -> tuple:
|
|
@@ -108,8 +113,12 @@ class MainWindow(InteractivePipeWindow):
|
|
|
108
113
|
Curve._plot_curve(curve.data, ax=ax)
|
|
109
114
|
flat_out.append(fig)
|
|
110
115
|
elif isinstance(out[idx][idy], np.ndarray):
|
|
111
|
-
|
|
112
|
-
|
|
116
|
+
if len(out[idx][idy].shape) == 1:
|
|
117
|
+
logging.debug(f"CONVERTING AUDIO {idx} {idy}")
|
|
118
|
+
flat_out.append((self.audio_sampling_rate, out[idx][idy]))
|
|
119
|
+
else:
|
|
120
|
+
logging.debug(f"CONVERTING IMAGE {idx} {idy}")
|
|
121
|
+
flat_out.append(self.convert_image(out[idx][idy]))
|
|
113
122
|
else:
|
|
114
123
|
raise NotImplementedError(
|
|
115
124
|
f"output type {type(out[idx][idy])} not suported")
|
{interactive_pipe-0.7.6 → interactive_pipe-0.7.7}/src/interactive_pipe/graphical/mpl_window.py
RENAMED
|
@@ -6,7 +6,7 @@ from interactive_pipe.data_objects.curves import Curve
|
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
class MatplotlibWindow(InteractivePipeWindow):
|
|
9
|
-
def __init__(self, controls=[], name="", pipeline=None, size=None, style: str = None, rc_params=None):
|
|
9
|
+
def __init__(self, controls=[], name="", pipeline=None, size=None, style: str = None, rc_params=None, markdown_description=None):
|
|
10
10
|
"""
|
|
11
11
|
style: dark_background, seaborn-v0_8-dark
|
|
12
12
|
https://matplotlib.org/stable/gallery/style_sheets/style_sheets_reference.html
|
|
@@ -26,6 +26,7 @@ class MatplotlibWindow(InteractivePipeWindow):
|
|
|
26
26
|
if rc_params is not None:
|
|
27
27
|
for key, val in rc_params.items():
|
|
28
28
|
plt.rcParams[key] = val
|
|
29
|
+
self.markdown_description = markdown_description # won't be used!
|
|
29
30
|
|
|
30
31
|
def add_image_placeholder(self, row, col):
|
|
31
32
|
nrows, ncols = np.array(self.image_canvas).shape
|
|
@@ -56,12 +57,18 @@ class MatplotlibWindow(InteractivePipeWindow):
|
|
|
56
57
|
data = ax_dict.get("data", None)
|
|
57
58
|
if data:
|
|
58
59
|
if isinstance(img, np.ndarray):
|
|
59
|
-
|
|
60
|
+
if len(img.shape) > 1:
|
|
61
|
+
data.set_data(img)
|
|
62
|
+
elif len(img.shape) == 1:
|
|
63
|
+
data.set_ydata(img)
|
|
60
64
|
elif isinstance(img, Curve):
|
|
61
65
|
img.update_plot(data, ax=ax_dict["ax"])
|
|
62
66
|
else:
|
|
63
67
|
if isinstance(img, np.ndarray):
|
|
64
|
-
|
|
68
|
+
if len(img.shape) > 1:
|
|
69
|
+
ax_dict["data"] = ax_dict["ax"].imshow(img)
|
|
70
|
+
elif len(img.shape) == 1:
|
|
71
|
+
ax_dict["data"] = Curve([img]).create_plot(ax=ax_dict["ax"])
|
|
65
72
|
elif isinstance(img, Curve):
|
|
66
73
|
ax_dict["data"] = img.create_plot(ax=ax_dict["ax"])
|
|
67
74
|
if not (isinstance(img, Curve) and img.data["title"] is not None):
|
|
@@ -78,7 +85,7 @@ class MatplotlibWindow(InteractivePipeWindow):
|
|
|
78
85
|
self.need_redraw = False
|
|
79
86
|
|
|
80
87
|
def convert_image(self, img):
|
|
81
|
-
if isinstance(img, np.ndarray):
|
|
88
|
+
if isinstance(img, np.ndarray) and len(img.shape) > 1:
|
|
82
89
|
return img.clip(0., 1.)
|
|
83
90
|
else:
|
|
84
91
|
return img
|
|
@@ -48,7 +48,7 @@ try:
|
|
|
48
48
|
from matplotlib.backends.backend_qtagg import (
|
|
49
49
|
FigureCanvas, NavigationToolbar2QT as NavigationToolbar)
|
|
50
50
|
from matplotlib.figure import Figure
|
|
51
|
-
from interactive_pipe.data_objects.curves import Curve
|
|
51
|
+
from interactive_pipe.data_objects.curves import Curve, SingleCurve
|
|
52
52
|
MPL_SUPPORT = True
|
|
53
53
|
except ImportError:
|
|
54
54
|
logging.warning("No support for Matplotlib widgets for Qt")
|
|
@@ -424,7 +424,23 @@ class MainWindow(QWidget, InteractivePipeWindow):
|
|
|
424
424
|
img_widget.setParent(None)
|
|
425
425
|
|
|
426
426
|
def update_image(self, image_array_original, row, col):
|
|
427
|
-
if isinstance(image_array_original, np.ndarray):
|
|
427
|
+
if isinstance(image_array_original, np.ndarray) and len(image_array_original.shape) == 1:
|
|
428
|
+
logging.warning("Audio playback not supported with 1D signal" +
|
|
429
|
+
"\nuse live audio instead while using Qt!" +
|
|
430
|
+
"\nuse instead: context['__set_audio'](audio_track)" +
|
|
431
|
+
"\nSee example here: https://github.com/balthazarneveu/interactive_pipe/blob/master/demo/jukebox.py")
|
|
432
|
+
logging.warning("We'll try to display the audio signal as an image instead")
|
|
433
|
+
image_array_original = Curve([
|
|
434
|
+
SingleCurve(
|
|
435
|
+
# x=np.linspace(0, image_array_original.shape[0]/44100, image_array_original.shape[0]),
|
|
436
|
+
y=image_array_original,
|
|
437
|
+
# style="k"
|
|
438
|
+
)
|
|
439
|
+
],
|
|
440
|
+
# xlabel="Time[s]",
|
|
441
|
+
ylabel="Amplitude",
|
|
442
|
+
)
|
|
443
|
+
elif isinstance(image_array_original, np.ndarray) and len(image_array_original.shape) > 1:
|
|
428
444
|
if len(image_array_original.shape) == 2:
|
|
429
445
|
# Consider black & white
|
|
430
446
|
image_array = image_array_original.copy()
|
|
@@ -445,7 +461,7 @@ class MainWindow(QWidget, InteractivePipeWindow):
|
|
|
445
461
|
pixmap = QPixmap.fromImage(image)
|
|
446
462
|
image_label = self.image_canvas[row][col]["image"]
|
|
447
463
|
image_label.setPixmap(pixmap)
|
|
448
|
-
|
|
464
|
+
if not isinstance(image_array_original, np.ndarray):
|
|
449
465
|
image_array = image_array_original
|
|
450
466
|
if MPL_SUPPORT and isinstance(image_array, Curve):
|
|
451
467
|
image_label = FigureCanvas(Figure(figsize=(10, 10)))
|
|
@@ -469,7 +485,7 @@ class MainWindow(QWidget, InteractivePipeWindow):
|
|
|
469
485
|
|
|
470
486
|
@staticmethod
|
|
471
487
|
def convert_image(out_im):
|
|
472
|
-
if isinstance(out_im, np.ndarray):
|
|
488
|
+
if isinstance(out_im, np.ndarray) and len(out_im.shape) > 1:
|
|
473
489
|
return (out_im.clip(0., 1.) * 255).astype(np.uint8)
|
|
474
490
|
else:
|
|
475
491
|
return out_im
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: interactive_pipe
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.7
|
|
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>=8.0.7
|
|
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"
|
|
@@ -106,7 +99,7 @@ pip install -e ".[full]"
|
|
|
106
99
|
|
|
107
100
|
## :scroll: Features
|
|
108
101
|
|
|
109
|
-
**Version 0.7.
|
|
102
|
+
**Version 0.7.7**
|
|
110
103
|
- Modular multi-image processing filters
|
|
111
104
|
- Declarative: Easily make graphical user interface without having to learn anything about pyQt or matplotlib
|
|
112
105
|
- Support in jupyter notebooks
|
|
@@ -115,7 +108,7 @@ pip install -e ".[full]"
|
|
|
115
108
|
- `KeyboardControl` : no slider on UI but exactly the same internal mechanism, update on key press.
|
|
116
109
|
- Support Curve plots (2D signals)
|
|
117
110
|
- :new: gradio backend (+allows sharing with others).
|
|
118
|
-
- :new: Audio support in Gradio
|
|
111
|
+
- :new: Audio support in Gradio (live audio or display several players by returning 1D numpy arrays)
|
|
119
112
|
- :new: Circular sliders for Qt Backend
|
|
120
113
|
|
|
121
114
|
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
from pathlib import Path
|
|
2
|
-
import numpy as np
|
|
3
|
-
from io import BytesIO
|
|
4
|
-
import base64
|
|
5
|
-
import logging
|
|
6
|
-
from typing import Union, Tuple
|
|
7
|
-
WAVIO_AVAILABLE = False
|
|
8
|
-
try:
|
|
9
|
-
WAVIO_AVAILABLE = True
|
|
10
|
-
import wavio
|
|
11
|
-
except ImportError:
|
|
12
|
-
logging.info("Cannot import wavio")
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
def audio_to_html(audio: Union[None, str, Path, Tuple[int, np.ndarray]], controls=True) -> str:
|
|
16
|
-
if audio is None:
|
|
17
|
-
logging.debug("No audio to display")
|
|
18
|
-
return ""
|
|
19
|
-
if isinstance(audio, str) or isinstance(audio, Path):
|
|
20
|
-
audio_base64 = base64.b64encode(open(audio, "rb").read()).decode("utf-8")
|
|
21
|
-
elif isinstance(audio, tuple):
|
|
22
|
-
assert WAVIO_AVAILABLE, "wavio is not available"
|
|
23
|
-
assert len(audio) == 2, "audio tuple should have 2 elements: (rate, data)"
|
|
24
|
-
assert isinstance(audio[0], int), "audio[0] should be an integer"
|
|
25
|
-
assert isinstance(audio[1], np.ndarray), "audio[1] should be a numpy array"
|
|
26
|
-
audio_bytes = BytesIO()
|
|
27
|
-
wavio.write(audio_bytes, audio[1].astype(np.float32), audio[0], sampwidth=4)
|
|
28
|
-
audio_bytes.seek(0)
|
|
29
|
-
audio_base64 = base64.b64encode(audio_bytes.read()).decode("utf-8")
|
|
30
|
-
audio_player = f'<audio src="data:audio/mpeg;base64,{audio_base64}"'
|
|
31
|
-
if controls:
|
|
32
|
-
audio_player += ' controls'
|
|
33
|
-
audio_player += ' autoplay></audio>'
|
|
34
|
-
|
|
35
|
-
return audio_player
|
|
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
|
{interactive_pipe-0.7.6 → interactive_pipe-0.7.7}/src/interactive_pipe/data_objects/__init__.py
RENAMED
|
File without changes
|
{interactive_pipe-0.7.6 → interactive_pipe-0.7.7}/src/interactive_pipe/data_objects/curves.py
RENAMED
|
File without changes
|
|
File without changes
|
{interactive_pipe-0.7.6 → interactive_pipe-0.7.7}/src/interactive_pipe/data_objects/image.py
RENAMED
|
File without changes
|
{interactive_pipe-0.7.6 → interactive_pipe-0.7.7}/src/interactive_pipe/data_objects/parameters.py
RENAMED
|
File without changes
|
{interactive_pipe-0.7.6 → interactive_pipe-0.7.7}/src/interactive_pipe/graphical/__init__.py
RENAMED
|
File without changes
|
{interactive_pipe-0.7.6 → interactive_pipe-0.7.7}/src/interactive_pipe/graphical/gradio_control.py
RENAMED
|
File without changes
|
|
File without changes
|
{interactive_pipe-0.7.6 → interactive_pipe-0.7.7}/src/interactive_pipe/graphical/mpl_control.py
RENAMED
|
File without changes
|
|
File without changes
|
{interactive_pipe-0.7.6 → interactive_pipe-0.7.7}/src/interactive_pipe/graphical/nb_control.py
RENAMED
|
File without changes
|
|
File without changes
|
{interactive_pipe-0.7.6 → interactive_pipe-0.7.7}/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
|
{interactive_pipe-0.7.6 → interactive_pipe-0.7.7}/src/interactive_pipe/helper/choose_backend.py
RENAMED
|
File without changes
|
|
File without changes
|
{interactive_pipe-0.7.6 → interactive_pipe-0.7.7}/src/interactive_pipe/helper/filter_decorator.py
RENAMED
|
File without changes
|
|
File without changes
|
{interactive_pipe-0.7.6 → interactive_pipe-0.7.7}/src/interactive_pipe/helper/pipeline_decorator.py
RENAMED
|
File without changes
|
|
File without changes
|
{interactive_pipe-0.7.6 → interactive_pipe-0.7.7}/src/interactive_pipe/thirdparty/music_spotify.py
RENAMED
|
File without changes
|
|
File without changes
|
{interactive_pipe-0.7.6 → interactive_pipe-0.7.7}/src/interactive_pipe.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{interactive_pipe-0.7.6 → interactive_pipe-0.7.7}/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
|
|
File without changes
|