interactive-pipe 0.7.6__tar.gz → 0.7.8__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.
Files changed (62) hide show
  1. {interactive_pipe-0.7.6/src/interactive_pipe.egg-info → interactive_pipe-0.7.8}/PKG-INFO +6 -6
  2. {interactive_pipe-0.7.6 → interactive_pipe-0.7.8}/pyproject.toml +4 -4
  3. {interactive_pipe-0.7.6 → interactive_pipe-0.7.8}/readme.md +2 -2
  4. interactive_pipe-0.7.8/src/interactive_pipe/data_objects/audio.py +89 -0
  5. {interactive_pipe-0.7.6 → interactive_pipe-0.7.8}/src/interactive_pipe/graphical/gradio_gui.py +14 -5
  6. {interactive_pipe-0.7.6 → interactive_pipe-0.7.8}/src/interactive_pipe/graphical/mpl_window.py +11 -4
  7. {interactive_pipe-0.7.6 → interactive_pipe-0.7.8}/src/interactive_pipe/graphical/qt_gui.py +20 -4
  8. {interactive_pipe-0.7.6 → interactive_pipe-0.7.8/src/interactive_pipe.egg-info}/PKG-INFO +6 -6
  9. {interactive_pipe-0.7.6 → interactive_pipe-0.7.8}/src/interactive_pipe.egg-info/requires.txt +3 -3
  10. interactive_pipe-0.7.6/src/interactive_pipe/data_objects/audio.py +0 -35
  11. {interactive_pipe-0.7.6 → interactive_pipe-0.7.8}/LICENSE +0 -0
  12. {interactive_pipe-0.7.6 → interactive_pipe-0.7.8}/setup.cfg +0 -0
  13. {interactive_pipe-0.7.6 → interactive_pipe-0.7.8}/src/interactive_pipe/__init__.py +0 -0
  14. {interactive_pipe-0.7.6 → interactive_pipe-0.7.8}/src/interactive_pipe/core/__init__.py +0 -0
  15. {interactive_pipe-0.7.6 → interactive_pipe-0.7.8}/src/interactive_pipe/core/cache.py +0 -0
  16. {interactive_pipe-0.7.6 → interactive_pipe-0.7.8}/src/interactive_pipe/core/engine.py +0 -0
  17. {interactive_pipe-0.7.6 → interactive_pipe-0.7.8}/src/interactive_pipe/core/filter.py +0 -0
  18. {interactive_pipe-0.7.6 → interactive_pipe-0.7.8}/src/interactive_pipe/core/graph.py +0 -0
  19. {interactive_pipe-0.7.6 → interactive_pipe-0.7.8}/src/interactive_pipe/core/pipeline.py +0 -0
  20. {interactive_pipe-0.7.6 → interactive_pipe-0.7.8}/src/interactive_pipe/core/signature.py +0 -0
  21. {interactive_pipe-0.7.6 → interactive_pipe-0.7.8}/src/interactive_pipe/data_objects/__init__.py +0 -0
  22. {interactive_pipe-0.7.6 → interactive_pipe-0.7.8}/src/interactive_pipe/data_objects/curves.py +0 -0
  23. {interactive_pipe-0.7.6 → interactive_pipe-0.7.8}/src/interactive_pipe/data_objects/data.py +0 -0
  24. {interactive_pipe-0.7.6 → interactive_pipe-0.7.8}/src/interactive_pipe/data_objects/image.py +0 -0
  25. {interactive_pipe-0.7.6 → interactive_pipe-0.7.8}/src/interactive_pipe/data_objects/parameters.py +0 -0
  26. {interactive_pipe-0.7.6 → interactive_pipe-0.7.8}/src/interactive_pipe/graphical/__init__.py +0 -0
  27. {interactive_pipe-0.7.6 → interactive_pipe-0.7.8}/src/interactive_pipe/graphical/gradio_control.py +0 -0
  28. {interactive_pipe-0.7.6 → interactive_pipe-0.7.8}/src/interactive_pipe/graphical/gui.py +0 -0
  29. {interactive_pipe-0.7.6 → interactive_pipe-0.7.8}/src/interactive_pipe/graphical/mpl_control.py +0 -0
  30. {interactive_pipe-0.7.6 → interactive_pipe-0.7.8}/src/interactive_pipe/graphical/mpl_gui.py +0 -0
  31. {interactive_pipe-0.7.6 → interactive_pipe-0.7.8}/src/interactive_pipe/graphical/nb_control.py +0 -0
  32. {interactive_pipe-0.7.6 → interactive_pipe-0.7.8}/src/interactive_pipe/graphical/nb_gui.py +0 -0
  33. {interactive_pipe-0.7.6 → interactive_pipe-0.7.8}/src/interactive_pipe/graphical/qt_control.py +0 -0
  34. {interactive_pipe-0.7.6 → interactive_pipe-0.7.8}/src/interactive_pipe/graphical/window.py +0 -0
  35. {interactive_pipe-0.7.6 → interactive_pipe-0.7.8}/src/interactive_pipe/headless/__init__.py +0 -0
  36. {interactive_pipe-0.7.6 → interactive_pipe-0.7.8}/src/interactive_pipe/headless/control.py +0 -0
  37. {interactive_pipe-0.7.6 → interactive_pipe-0.7.8}/src/interactive_pipe/headless/keyboard.py +0 -0
  38. {interactive_pipe-0.7.6 → interactive_pipe-0.7.8}/src/interactive_pipe/headless/pipeline.py +0 -0
  39. {interactive_pipe-0.7.6 → interactive_pipe-0.7.8}/src/interactive_pipe/helper/__init__.py +0 -0
  40. {interactive_pipe-0.7.6 → interactive_pipe-0.7.8}/src/interactive_pipe/helper/_private.py +0 -0
  41. {interactive_pipe-0.7.6 → interactive_pipe-0.7.8}/src/interactive_pipe/helper/choose_backend.py +0 -0
  42. {interactive_pipe-0.7.6 → interactive_pipe-0.7.8}/src/interactive_pipe/helper/control_abbreviation.py +0 -0
  43. {interactive_pipe-0.7.6 → interactive_pipe-0.7.8}/src/interactive_pipe/helper/filter_decorator.py +0 -0
  44. {interactive_pipe-0.7.6 → interactive_pipe-0.7.8}/src/interactive_pipe/helper/keyword_args_analyzer.py +0 -0
  45. {interactive_pipe-0.7.6 → interactive_pipe-0.7.8}/src/interactive_pipe/helper/pipeline_decorator.py +0 -0
  46. {interactive_pipe-0.7.6 → interactive_pipe-0.7.8}/src/interactive_pipe/thirdparty/images_openai_api.py +0 -0
  47. {interactive_pipe-0.7.6 → interactive_pipe-0.7.8}/src/interactive_pipe/thirdparty/music_spotify.py +0 -0
  48. {interactive_pipe-0.7.6 → interactive_pipe-0.7.8}/src/interactive_pipe.egg-info/SOURCES.txt +0 -0
  49. {interactive_pipe-0.7.6 → interactive_pipe-0.7.8}/src/interactive_pipe.egg-info/dependency_links.txt +0 -0
  50. {interactive_pipe-0.7.6 → interactive_pipe-0.7.8}/src/interactive_pipe.egg-info/top_level.txt +0 -0
  51. {interactive_pipe-0.7.6 → interactive_pipe-0.7.8}/test/test_cache.py +0 -0
  52. {interactive_pipe-0.7.6 → interactive_pipe-0.7.8}/test/test_control_abbreviations.py +0 -0
  53. {interactive_pipe-0.7.6 → interactive_pipe-0.7.8}/test/test_controller.py +0 -0
  54. {interactive_pipe-0.7.6 → interactive_pipe-0.7.8}/test/test_core.py +0 -0
  55. {interactive_pipe-0.7.6 → interactive_pipe-0.7.8}/test/test_curves.py +0 -0
  56. {interactive_pipe-0.7.6 → interactive_pipe-0.7.8}/test/test_decorator.py +0 -0
  57. {interactive_pipe-0.7.6 → interactive_pipe-0.7.8}/test/test_engine.py +0 -0
  58. {interactive_pipe-0.7.6 → interactive_pipe-0.7.8}/test/test_filter.py +0 -0
  59. {interactive_pipe-0.7.6 → interactive_pipe-0.7.8}/test/test_headless.py +0 -0
  60. {interactive_pipe-0.7.6 → interactive_pipe-0.7.8}/test/test_image.py +0 -0
  61. {interactive_pipe-0.7.6 → interactive_pipe-0.7.8}/test/test_parameters.py +0 -0
  62. {interactive_pipe-0.7.6 → interactive_pipe-0.7.8}/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.6
3
+ Version: 0.7.8
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
@@ -19,7 +19,7 @@ Requires-Dist: PyQt6>=6.5.2
19
19
  Requires-Dist: PyQt6_sip>=13.5.2
20
20
  Requires-Dist: opencv_python_headless>=4.8.1.78
21
21
  Requires-Dist: pytest>=6.2.5
22
- Requires-Dist: ipywidgets>=8.0.7
22
+ Requires-Dist: ipywidgets>=7.7.1
23
23
  Requires-Dist: pandas
24
24
  Requires-Dist: gradio
25
25
  Provides-Extra: qt6
@@ -29,7 +29,7 @@ Provides-Extra: qt5
29
29
  Requires-Dist: PyQt5>=5.15.9; extra == "qt5"
30
30
  Requires-Dist: PyQt5_sip>=12.12.1; extra == "qt5"
31
31
  Provides-Extra: notebook
32
- Requires-Dist: ipywidgets>=8.0.7; extra == "notebook"
32
+ Requires-Dist: ipywidgets>=7.7.1; extra == "notebook"
33
33
  Provides-Extra: pytest
34
34
  Requires-Dist: opencv_python_headless>=4.8.1.78; extra == "pytest"
35
35
  Requires-Dist: pytest>=6.2.5; extra == "pytest"
@@ -38,7 +38,7 @@ Requires-Dist: PyQt6>=6.5.2; extra == "full"
38
38
  Requires-Dist: PyQt6_sip>=13.5.2; extra == "full"
39
39
  Requires-Dist: opencv_python_headless>=4.8.1.78; extra == "full"
40
40
  Requires-Dist: pytest>=6.2.5; extra == "full"
41
- Requires-Dist: ipywidgets>=8.0.7; extra == "full"
41
+ Requires-Dist: ipywidgets>=7.7.1; extra == "full"
42
42
  Requires-Dist: pandas; extra == "full"
43
43
  Requires-Dist: gradio; extra == "full"
44
44
 
@@ -106,7 +106,7 @@ pip install -e ".[full]"
106
106
 
107
107
  ## :scroll: Features
108
108
 
109
- **Version 0.7.6**
109
+ **Version 0.7.7**
110
110
  - Modular multi-image processing filters
111
111
  - Declarative: Easily make graphical user interface without having to learn anything about pyQt or matplotlib
112
112
  - Support in jupyter notebooks
@@ -115,7 +115,7 @@ pip install -e ".[full]"
115
115
  - `KeyboardControl` : no slider on UI but exactly the same internal mechanism, update on key press.
116
116
  - Support Curve plots (2D signals)
117
117
  - :new: gradio backend (+allows sharing with others).
118
- - :new: Audio support in Gradio
118
+ - :new: Audio support in Gradio (live audio or display several players by returning 1D numpy arrays)
119
119
  - :new: Circular sliders for Qt Backend
120
120
 
121
121
 
@@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
5
5
 
6
6
  [project]
7
7
  name = "interactive_pipe"
8
- version = "0.7.6"
8
+ version = "0.7.8"
9
9
  authors = [
10
10
  { name="Balthazar Neveu", email="balthazarneveu@gmail.com" },
11
11
  ]
@@ -18,7 +18,7 @@ dependencies = [
18
18
  "PyQt6_sip>=13.5.2",
19
19
  "opencv_python_headless>=4.8.1.78",
20
20
  "pytest>=6.2.5",
21
- "ipywidgets>=8.0.7",
21
+ "ipywidgets>=7.7.1",
22
22
  "pandas",
23
23
  "gradio",
24
24
  ]
@@ -48,7 +48,7 @@ qt5=[
48
48
  ]
49
49
 
50
50
  notebook=[
51
- "ipywidgets>=8.0.7",
51
+ "ipywidgets>=7.7.1",
52
52
  ]
53
53
 
54
54
  pytest=[
@@ -61,7 +61,7 @@ full=[
61
61
  "PyQt6_sip>=13.5.2",
62
62
  "opencv_python_headless>=4.8.1.78",
63
63
  "pytest>=6.2.5",
64
- "ipywidgets>=8.0.7",
64
+ "ipywidgets>=7.7.1",
65
65
  "pandas",
66
66
  "gradio",
67
67
  ]
@@ -62,7 +62,7 @@ pip install -e ".[full]"
62
62
 
63
63
  ## :scroll: Features
64
64
 
65
- **Version 0.7.6**
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)
@@ -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
- out_list_gradio_containers.append(gr.Image(label=title))
61
- self.window.image_canvas[idx][idy]["type"] = "image"
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
- logging.info(f"CONVERTING IMAGE {idx} {idy}")
112
- flat_out.append(self.convert_image(out[idx][idy]))
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")
@@ -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
- data.set_data(img)
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
- ax_dict["data"] = ax_dict["ax"].imshow(img)
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
- else:
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.6
3
+ Version: 0.7.8
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
@@ -19,7 +19,7 @@ Requires-Dist: PyQt6>=6.5.2
19
19
  Requires-Dist: PyQt6_sip>=13.5.2
20
20
  Requires-Dist: opencv_python_headless>=4.8.1.78
21
21
  Requires-Dist: pytest>=6.2.5
22
- Requires-Dist: ipywidgets>=8.0.7
22
+ Requires-Dist: ipywidgets>=7.7.1
23
23
  Requires-Dist: pandas
24
24
  Requires-Dist: gradio
25
25
  Provides-Extra: qt6
@@ -29,7 +29,7 @@ Provides-Extra: qt5
29
29
  Requires-Dist: PyQt5>=5.15.9; extra == "qt5"
30
30
  Requires-Dist: PyQt5_sip>=12.12.1; extra == "qt5"
31
31
  Provides-Extra: notebook
32
- Requires-Dist: ipywidgets>=8.0.7; extra == "notebook"
32
+ Requires-Dist: ipywidgets>=7.7.1; extra == "notebook"
33
33
  Provides-Extra: pytest
34
34
  Requires-Dist: opencv_python_headless>=4.8.1.78; extra == "pytest"
35
35
  Requires-Dist: pytest>=6.2.5; extra == "pytest"
@@ -38,7 +38,7 @@ Requires-Dist: PyQt6>=6.5.2; extra == "full"
38
38
  Requires-Dist: PyQt6_sip>=13.5.2; extra == "full"
39
39
  Requires-Dist: opencv_python_headless>=4.8.1.78; extra == "full"
40
40
  Requires-Dist: pytest>=6.2.5; extra == "full"
41
- Requires-Dist: ipywidgets>=8.0.7; extra == "full"
41
+ Requires-Dist: ipywidgets>=7.7.1; extra == "full"
42
42
  Requires-Dist: pandas; extra == "full"
43
43
  Requires-Dist: gradio; extra == "full"
44
44
 
@@ -106,7 +106,7 @@ pip install -e ".[full]"
106
106
 
107
107
  ## :scroll: Features
108
108
 
109
- **Version 0.7.6**
109
+ **Version 0.7.7**
110
110
  - Modular multi-image processing filters
111
111
  - Declarative: Easily make graphical user interface without having to learn anything about pyQt or matplotlib
112
112
  - Support in jupyter notebooks
@@ -115,7 +115,7 @@ pip install -e ".[full]"
115
115
  - `KeyboardControl` : no slider on UI but exactly the same internal mechanism, update on key press.
116
116
  - Support Curve plots (2D signals)
117
117
  - :new: gradio backend (+allows sharing with others).
118
- - :new: Audio support in Gradio
118
+ - :new: Audio support in Gradio (live audio or display several players by returning 1D numpy arrays)
119
119
  - :new: Circular sliders for Qt Backend
120
120
 
121
121
 
@@ -6,7 +6,7 @@ PyQt6>=6.5.2
6
6
  PyQt6_sip>=13.5.2
7
7
  opencv_python_headless>=4.8.1.78
8
8
  pytest>=6.2.5
9
- ipywidgets>=8.0.7
9
+ ipywidgets>=7.7.1
10
10
  pandas
11
11
  gradio
12
12
 
@@ -15,12 +15,12 @@ PyQt6>=6.5.2
15
15
  PyQt6_sip>=13.5.2
16
16
  opencv_python_headless>=4.8.1.78
17
17
  pytest>=6.2.5
18
- ipywidgets>=8.0.7
18
+ ipywidgets>=7.7.1
19
19
  pandas
20
20
  gradio
21
21
 
22
22
  [notebook]
23
- ipywidgets>=8.0.7
23
+ ipywidgets>=7.7.1
24
24
 
25
25
  [pytest]
26
26
  opencv_python_headless>=4.8.1.78
@@ -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