monochrome 2025.1.17__py3-none-macosx_10_9_universal2.whl
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.
- monochrome/__init__.py +23 -0
- monochrome/__main__.py +6 -0
- monochrome/_version.py +16 -0
- monochrome/data/Monochrome.app/Contents/Info.plist +73 -0
- monochrome/data/Monochrome.app/Contents/MacOS/Monochrome +0 -0
- monochrome/data/Monochrome.app/Contents/Resources/Monochrome.icns +0 -0
- monochrome/data/Monochrome.app/Contents/Resources/npy-logo.icns +0 -0
- monochrome/fbs/Array3DataChunkf.py +90 -0
- monochrome/fbs/Array3DataChunku16.py +90 -0
- monochrome/fbs/Array3DataChunku8.py +90 -0
- monochrome/fbs/Array3Meta.py +271 -0
- monochrome/fbs/Array3MetaFlow.py +121 -0
- monochrome/fbs/ArrayDataType.py +9 -0
- monochrome/fbs/BitRange.py +17 -0
- monochrome/fbs/CloseVideo.py +52 -0
- monochrome/fbs/Color.py +47 -0
- monochrome/fbs/ColorMap.py +15 -0
- monochrome/fbs/Data.py +17 -0
- monochrome/fbs/DictEntry.py +65 -0
- monochrome/fbs/Filepaths.py +70 -0
- monochrome/fbs/OpacityFunction.py +15 -0
- monochrome/fbs/PointsVideo.py +173 -0
- monochrome/fbs/Quit.py +38 -0
- monochrome/fbs/Root.py +68 -0
- monochrome/fbs/VideoExport.py +143 -0
- monochrome/fbs/VideoExportFormat.py +8 -0
- monochrome/fbs/__init__.py +0 -0
- monochrome/ipc.py +624 -0
- monochrome-2025.1.17.dist-info/METADATA +166 -0
- monochrome-2025.1.17.dist-info/RECORD +33 -0
- monochrome-2025.1.17.dist-info/WHEEL +5 -0
- monochrome-2025.1.17.dist-info/entry_points.txt +3 -0
- monochrome-2025.1.17.dist-info/licenses/LICENSE.md +19 -0
monochrome/ipc.py
ADDED
@@ -0,0 +1,624 @@
|
|
1
|
+
import os
|
2
|
+
import socket
|
3
|
+
import subprocess
|
4
|
+
import sys
|
5
|
+
import time
|
6
|
+
from pathlib import Path
|
7
|
+
from typing import Dict, List, Optional, Text, Union
|
8
|
+
|
9
|
+
import flatbuffers
|
10
|
+
import numpy as np
|
11
|
+
|
12
|
+
from .fbs import (Array3DataChunkf, Array3DataChunku8, Array3DataChunku16,
|
13
|
+
Array3Meta, Array3MetaFlow, CloseVideo, Filepaths,
|
14
|
+
PointsVideo, Root, VideoExport)
|
15
|
+
from .fbs.ArrayDataType import ArrayDataType
|
16
|
+
from .fbs.BitRange import BitRange
|
17
|
+
from .fbs.Color import CreateColor
|
18
|
+
from .fbs.ColorMap import ColorMap
|
19
|
+
from .fbs.Data import Data
|
20
|
+
from .fbs.VideoExportFormat import VideoExportFormat
|
21
|
+
from .fbs.DictEntry import (DictEntryAddKey, DictEntryAddVal, DictEntryEnd,
|
22
|
+
DictEntryStart)
|
23
|
+
from .fbs.OpacityFunction import OpacityFunction
|
24
|
+
|
25
|
+
if sys.platform == 'win32':
|
26
|
+
MONOCHROME_BIN_PATH = Path(__file__).parent / 'data' / 'bin' / 'Monochrome.exe'
|
27
|
+
elif sys.platform == 'darwin':
|
28
|
+
MONOCHROME_BIN_PATH = Path(__file__).parent / 'data' / 'Monochrome.app'
|
29
|
+
else:
|
30
|
+
MONOCHROME_BIN_PATH = Path(__file__).parent / 'data' / 'bin' / 'Monochrome'
|
31
|
+
|
32
|
+
USE_TCP = sys.platform in ['win32', 'cygwin']
|
33
|
+
TCP_IP, TCP_PORT = '127.0.0.1', 4864
|
34
|
+
# OSX doesn't support abstract UNIX domain sockets
|
35
|
+
ABSTRACT_DOMAIN_SOCKET_SUPPORTED = sys.platform != 'darwin'
|
36
|
+
if sys.platform != 'win32':
|
37
|
+
SOCK_PATH = f'\0Monochrome{os.getuid()}' if ABSTRACT_DOMAIN_SOCKET_SUPPORTED else f'/tmp/Monochrome{os.getuid()}.s'
|
38
|
+
else:
|
39
|
+
SOCK_PATH = None
|
40
|
+
MAX_BUFFER_SIZE = 16352
|
41
|
+
|
42
|
+
|
43
|
+
def start_monochrome(speed: Optional[float] = None,
|
44
|
+
display_fps: Optional[int] = None,
|
45
|
+
scale: Optional[float] = None,
|
46
|
+
fliph: bool = False,
|
47
|
+
flipv: bool = False,
|
48
|
+
**kwargs):
|
49
|
+
"""
|
50
|
+
Start bundled Monochrome executable with the given settings.
|
51
|
+
"""
|
52
|
+
if sys.platform != 'darwin':
|
53
|
+
args = [str(MONOCHROME_BIN_PATH)]
|
54
|
+
else:
|
55
|
+
args = ['open', '-a', str(MONOCHROME_BIN_PATH), '--args']
|
56
|
+
if speed:
|
57
|
+
args.append('--speed')
|
58
|
+
args.append(str(speed))
|
59
|
+
if display_fps:
|
60
|
+
args.append('--display_fps')
|
61
|
+
args.append(str(display_fps))
|
62
|
+
if scale:
|
63
|
+
args.append('--scale')
|
64
|
+
args.append(str(scale))
|
65
|
+
if fliph:
|
66
|
+
args.append('--fliph')
|
67
|
+
if flipv:
|
68
|
+
args.append('--flipv')
|
69
|
+
for key, val in kwargs.items():
|
70
|
+
args.append(f'--{key}')
|
71
|
+
args.append(str(val))
|
72
|
+
subprocess.Popen(args, start_new_session=True)
|
73
|
+
|
74
|
+
|
75
|
+
def console_entrypoint():
|
76
|
+
if sys.platform != 'darwin':
|
77
|
+
args = [str(MONOCHROME_BIN_PATH)]
|
78
|
+
else:
|
79
|
+
args = ['open', '-a', str(MONOCHROME_BIN_PATH), '--args']
|
80
|
+
args.extend(sys.argv[1:])
|
81
|
+
subprocess.Popen(args).wait()
|
82
|
+
|
83
|
+
|
84
|
+
def _create_socket():
|
85
|
+
if USE_TCP:
|
86
|
+
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
87
|
+
s.connect((TCP_IP, TCP_PORT))
|
88
|
+
else:
|
89
|
+
if not ABSTRACT_DOMAIN_SOCKET_SUPPORTED and not Path(SOCK_PATH).exists():
|
90
|
+
raise ConnectionRefusedError("No socket found, please start Monochrome")
|
91
|
+
s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
92
|
+
s.connect(SOCK_PATH)
|
93
|
+
return s
|
94
|
+
|
95
|
+
|
96
|
+
def create_socket():
|
97
|
+
s = None
|
98
|
+
try:
|
99
|
+
s = _create_socket()
|
100
|
+
except ConnectionRefusedError:
|
101
|
+
if not USE_TCP and not ABSTRACT_DOMAIN_SOCKET_SUPPORTED:
|
102
|
+
Path(SOCK_PATH).unlink(missing_ok=True)
|
103
|
+
start_monochrome()
|
104
|
+
waiting = True
|
105
|
+
timeout = time.time() + 5
|
106
|
+
while waiting and time.time() < timeout:
|
107
|
+
try:
|
108
|
+
s = _create_socket()
|
109
|
+
waiting = False
|
110
|
+
except ConnectionRefusedError:
|
111
|
+
pass
|
112
|
+
if waiting:
|
113
|
+
raise ConnectionRefusedError("Could not connect to Monochrome")
|
114
|
+
return s
|
115
|
+
|
116
|
+
|
117
|
+
def get_color(builder, color):
|
118
|
+
if color is None:
|
119
|
+
return None
|
120
|
+
|
121
|
+
try:
|
122
|
+
import matplotlib.colors as mcolors
|
123
|
+
color = mcolors.to_rgba(color)
|
124
|
+
except ImportError:
|
125
|
+
if isinstance(color, str):
|
126
|
+
print("ERROR: unable to import matplotlib, please install it")
|
127
|
+
color = (0.0, 0.0, 0.0, 1.0)
|
128
|
+
return CreateColor(builder, color)
|
129
|
+
|
130
|
+
|
131
|
+
def build_root(builder, data_type, data):
|
132
|
+
Root.Start(builder)
|
133
|
+
Root.AddDataType(builder, data_type)
|
134
|
+
Root.AddData(builder, data)
|
135
|
+
root = Root.End(builder)
|
136
|
+
return root
|
137
|
+
|
138
|
+
|
139
|
+
def create_filepaths_msg(paths):
|
140
|
+
builder = flatbuffers.Builder(512)
|
141
|
+
paths_fb = [builder.CreateString(s) for s in paths]
|
142
|
+
Filepaths.StartFileVector(builder, len(paths_fb))
|
143
|
+
for p in paths_fb:
|
144
|
+
builder.PrependSOffsetTRelative(p)
|
145
|
+
vec = builder.EndVector()
|
146
|
+
Filepaths.Start(builder)
|
147
|
+
Filepaths.AddFile(builder, vec)
|
148
|
+
fp = Filepaths.End(builder)
|
149
|
+
root = build_root(builder, Data.Filepaths, fp)
|
150
|
+
builder.FinishSizePrefixed(root)
|
151
|
+
buf = builder.Output()
|
152
|
+
return buf
|
153
|
+
|
154
|
+
|
155
|
+
def create_pointsvideo_msg(points_py, name, parent_name=None, color=None, point_size=None):
|
156
|
+
builder = flatbuffers.Builder(1024)
|
157
|
+
name_fb = builder.CreateString(name)
|
158
|
+
parent_fb = builder.CreateString(parent_name) if parent_name else None
|
159
|
+
|
160
|
+
flat = []
|
161
|
+
indexes = []
|
162
|
+
for frame in points_py:
|
163
|
+
for point in frame:
|
164
|
+
flat.append(point[0])
|
165
|
+
flat.append(point[1])
|
166
|
+
indexes.append(len(flat))
|
167
|
+
flat = np.array(flat, dtype=np.float32)
|
168
|
+
indexs = np.array(indexes, dtype=np.uint32)
|
169
|
+
flat_fb = builder.CreateNumpyVector(flat)
|
170
|
+
indexes_fb = builder.CreateNumpyVector(indexs)
|
171
|
+
|
172
|
+
PointsVideo.Start(builder)
|
173
|
+
PointsVideo.AddName(builder, name_fb)
|
174
|
+
if parent_fb:
|
175
|
+
PointsVideo.AddParentName(builder, parent_fb)
|
176
|
+
if color:
|
177
|
+
PointsVideo.AddColor(builder, get_color(builder, color))
|
178
|
+
if point_size:
|
179
|
+
PointsVideo.AddPointSize(builder, point_size)
|
180
|
+
PointsVideo.AddPointsData(builder, flat_fb)
|
181
|
+
PointsVideo.AddTimeIdxs(builder, indexes_fb)
|
182
|
+
fp = PointsVideo.End(builder)
|
183
|
+
root = build_root(builder, Data.PointsVideo, fp)
|
184
|
+
builder.FinishSizePrefixed(root)
|
185
|
+
buf = builder.Output()
|
186
|
+
return buf
|
187
|
+
|
188
|
+
def create_array3meta_msg(type: ArrayDataType, name, shape, duration=0., fps=0., date="", comment="",
|
189
|
+
bitrange=BitRange.AUTODETECT, cmap=ColorMap.DEFAULT, parent_name=None, opacity=None,
|
190
|
+
metadata=None, vmin=None, vmax=None):
|
191
|
+
builder = flatbuffers.Builder(1024)
|
192
|
+
name_fb = builder.CreateString(name)
|
193
|
+
parent_fb = builder.CreateString(parent_name) if parent_name is not None else None
|
194
|
+
date_fb = builder.CreateString(date)
|
195
|
+
comment_fb = builder.CreateString(comment)
|
196
|
+
if metadata:
|
197
|
+
metadata = [(builder.CreateString(key), builder.CreateString(val)) for key, val in metadata.items()]
|
198
|
+
metaData_fbs = []
|
199
|
+
for key, val in metadata:
|
200
|
+
DictEntryStart(builder)
|
201
|
+
DictEntryAddKey(builder, key)
|
202
|
+
DictEntryAddVal(builder, val)
|
203
|
+
metaData_fbs.append(DictEntryEnd(builder))
|
204
|
+
Array3Meta.Array3MetaStartMetadataVector(builder, len(metadata))
|
205
|
+
for e in metaData_fbs:
|
206
|
+
builder.PrependUOffsetTRelative(e)
|
207
|
+
metadata = builder.EndVector()
|
208
|
+
Array3Meta.Start(builder)
|
209
|
+
Array3Meta.AddType(builder, type)
|
210
|
+
Array3Meta.AddNx(builder, shape[2])
|
211
|
+
Array3Meta.AddNy(builder, shape[1])
|
212
|
+
Array3Meta.AddNt(builder, shape[0])
|
213
|
+
Array3Meta.AddBitrange(builder, bitrange)
|
214
|
+
Array3Meta.AddCmap(builder, cmap)
|
215
|
+
if vmin is not None:
|
216
|
+
Array3Meta.AddVmin(builder, vmin)
|
217
|
+
if vmax is not None:
|
218
|
+
Array3Meta.AddVmax(builder, vmax)
|
219
|
+
if opacity is not None:
|
220
|
+
Array3Meta.AddOpacity(builder, opacity)
|
221
|
+
Array3Meta.AddName(builder, name_fb)
|
222
|
+
if parent_fb:
|
223
|
+
Array3Meta.AddParentName(builder, parent_fb)
|
224
|
+
Array3Meta.AddDuration(builder, duration)
|
225
|
+
Array3Meta.AddFps(builder, fps)
|
226
|
+
Array3Meta.AddDate(builder, date_fb)
|
227
|
+
Array3Meta.AddComment(builder, comment_fb)
|
228
|
+
if metadata:
|
229
|
+
Array3Meta.AddMetadata(builder, metadata)
|
230
|
+
d = Array3Meta.End(builder)
|
231
|
+
|
232
|
+
root = build_root(builder, Data.Array3Meta, d)
|
233
|
+
builder.FinishSizePrefixed(root)
|
234
|
+
buf = builder.Output()
|
235
|
+
return buf
|
236
|
+
|
237
|
+
|
238
|
+
def create_array3metaflow_msg(shape, parent_name=None, name="", color=None):
|
239
|
+
if parent_name is None:
|
240
|
+
parent_name = ""
|
241
|
+
builder = flatbuffers.Builder(1024)
|
242
|
+
name_fb = builder.CreateString(name)
|
243
|
+
parent_fb = builder.CreateString(parent_name)
|
244
|
+
Array3MetaFlow.Start(builder)
|
245
|
+
Array3MetaFlow.AddNx(builder, shape[2])
|
246
|
+
Array3MetaFlow.AddNy(builder, shape[1])
|
247
|
+
Array3MetaFlow.AddNt(builder, shape[0])
|
248
|
+
Array3MetaFlow.AddName(builder, name_fb)
|
249
|
+
Array3MetaFlow.AddParentName(builder, parent_fb)
|
250
|
+
if color:
|
251
|
+
Array3MetaFlow.AddColor(builder, get_color(builder, color))
|
252
|
+
d = Array3MetaFlow.End(builder)
|
253
|
+
|
254
|
+
root = build_root(builder, Data.Array3MetaFlow, d)
|
255
|
+
builder.FinishSizePrefixed(root)
|
256
|
+
buf = builder.Output()
|
257
|
+
return buf
|
258
|
+
|
259
|
+
|
260
|
+
def create_array3dataf_msg(array, idx=0):
|
261
|
+
builder = flatbuffers.Builder(65536)
|
262
|
+
data = builder.CreateNumpyVector(array)
|
263
|
+
Array3DataChunkf.Start(builder)
|
264
|
+
Array3DataChunkf.AddStartidx(builder, idx)
|
265
|
+
Array3DataChunkf.AddData(builder, data)
|
266
|
+
d = Array3DataChunkf.End(builder)
|
267
|
+
|
268
|
+
root = build_root(builder, Data.Array3DataChunkf, d)
|
269
|
+
builder.FinishSizePrefixed(root)
|
270
|
+
buf = builder.Output()
|
271
|
+
return buf
|
272
|
+
|
273
|
+
|
274
|
+
def create_array3datau8_msg(array, idx=0):
|
275
|
+
builder = flatbuffers.Builder(65536)
|
276
|
+
data = builder.CreateNumpyVector(array)
|
277
|
+
Array3DataChunku8.Start(builder)
|
278
|
+
Array3DataChunku8.AddStartidx(builder, idx)
|
279
|
+
Array3DataChunku8.AddData(builder, data)
|
280
|
+
d = Array3DataChunku8.End(builder)
|
281
|
+
|
282
|
+
root = build_root(builder, Data.Array3DataChunku8, d)
|
283
|
+
builder.FinishSizePrefixed(root)
|
284
|
+
buf = builder.Output()
|
285
|
+
return buf
|
286
|
+
|
287
|
+
|
288
|
+
def create_array3datau16_msg(array, idx=0):
|
289
|
+
builder = flatbuffers.Builder(65536)
|
290
|
+
data = builder.CreateNumpyVector(array)
|
291
|
+
Array3DataChunku16.Start(builder)
|
292
|
+
Array3DataChunku16.AddStartidx(builder, idx)
|
293
|
+
Array3DataChunku16.AddData(builder, data)
|
294
|
+
d = Array3DataChunku16.End(builder)
|
295
|
+
|
296
|
+
root = build_root(builder, Data.Array3DataChunku16, d)
|
297
|
+
builder.FinishSizePrefixed(root)
|
298
|
+
buf = builder.Output()
|
299
|
+
return buf
|
300
|
+
|
301
|
+
|
302
|
+
def show_file(filepath: Union[Text, Path]):
|
303
|
+
"""
|
304
|
+
Load a file in Monochrome.
|
305
|
+
|
306
|
+
Parameters
|
307
|
+
----------
|
308
|
+
filepath : str or Path
|
309
|
+
Path to the file to be loaded
|
310
|
+
"""
|
311
|
+
show_files([filepath])
|
312
|
+
|
313
|
+
|
314
|
+
def show_files(paths: List[Union[Text, Path]]):
|
315
|
+
"""
|
316
|
+
Load multiple files in Monochrome.
|
317
|
+
|
318
|
+
Parameters
|
319
|
+
----------
|
320
|
+
paths : List[str or Path]
|
321
|
+
List of paths to the files to be loaded
|
322
|
+
"""
|
323
|
+
paths = [Path(path) for path in paths]
|
324
|
+
if not all([path.exists() for path in paths]):
|
325
|
+
raise FileNotFoundError(f"One of more files of {paths} do not exist")
|
326
|
+
paths = [str(path.absolute()) for path in paths]
|
327
|
+
s = create_socket()
|
328
|
+
buf = create_filepaths_msg(paths)
|
329
|
+
s.sendall(buf)
|
330
|
+
|
331
|
+
|
332
|
+
def show_points(points, name: Text = "", parent: Optional[Text] = None, color=None,
|
333
|
+
point_size: Optional[float] = None):
|
334
|
+
"""
|
335
|
+
Show a list of points for each frame in Monochrome.
|
336
|
+
|
337
|
+
Parameters
|
338
|
+
----------
|
339
|
+
points : List[List[Tuple[float, float]]]
|
340
|
+
A list of list of points (x, y). The outer list elements are the frames, the inner list is the list of points for a specific frame.
|
341
|
+
name : str
|
342
|
+
Optional description
|
343
|
+
parent : str
|
344
|
+
Name of the video onto which the points will be displayed. If none is given the last loaded video will be used.
|
345
|
+
color : str or tuple
|
346
|
+
Matplotlib color (either string like 'black' or rgba tuple)
|
347
|
+
point_size : float
|
348
|
+
Size of points in image pixels
|
349
|
+
"""
|
350
|
+
|
351
|
+
name = str(name)
|
352
|
+
s = create_socket()
|
353
|
+
buf = create_pointsvideo_msg(points, name, parent, color, point_size)
|
354
|
+
s.sendall(buf)
|
355
|
+
|
356
|
+
|
357
|
+
def show_image(array: np.ndarray,
|
358
|
+
name: Text = "",
|
359
|
+
cmap: Union[ColorMap, Text] = ColorMap.DEFAULT,
|
360
|
+
vmin: Optional[float] = None,
|
361
|
+
vmax: Optional[float] = None,
|
362
|
+
bitrange: Union[BitRange, Text] = BitRange.AUTODETECT,
|
363
|
+
parent: Optional[Text] = None,
|
364
|
+
opacity: Optional[OpacityFunction] = None,
|
365
|
+
comment: Text = "",
|
366
|
+
metadata: Optional[Dict] = None):
|
367
|
+
"""
|
368
|
+
Show an image in Monochrome.
|
369
|
+
|
370
|
+
Alias for :func:`show_video`.
|
371
|
+
"""
|
372
|
+
return show_video(array, name=name, cmap=cmap, vmin=vmin, vmax=vmax, bitrange=bitrange, parent=parent, opacity=opacity, comment=comment, metadata=metadata)
|
373
|
+
|
374
|
+
|
375
|
+
def show_video(array: np.ndarray,
|
376
|
+
name: Text = "",
|
377
|
+
cmap: Union[ColorMap, Text] = ColorMap.DEFAULT,
|
378
|
+
vmin: Optional[float] = None,
|
379
|
+
vmax: Optional[float] = None,
|
380
|
+
bitrange: Union[BitRange, Text] = BitRange.AUTODETECT,
|
381
|
+
parent: Optional[Text] = None,
|
382
|
+
opacity: Optional[OpacityFunction] = None,
|
383
|
+
comment: Text = "",
|
384
|
+
metadata: Optional[Dict] = None):
|
385
|
+
"""
|
386
|
+
Play a video or open a image in Monochrome.
|
387
|
+
Arrays of dtype np.float, np.uint8, and np.uint16 are natively supported by Monochrome.
|
388
|
+
Arrays with other dtypes will be converted to np.float
|
389
|
+
|
390
|
+
Parameters
|
391
|
+
----------
|
392
|
+
array : np.ndarray
|
393
|
+
The video to be displayed. The array should have the shape (T, H, W) or (H, W).
|
394
|
+
name : str
|
395
|
+
Name of the video
|
396
|
+
cmap : str or ColorMap
|
397
|
+
Colormap for the video. One of 'default' (autodetect), 'gray', 'hsv', 'blackbody', 'viridis', 'PRGn', 'PRGn_pos', 'PRGn_neg', 'RdBu'.
|
398
|
+
vmin : float
|
399
|
+
Minimum value for the colormap. Default is None.
|
400
|
+
vmax : float
|
401
|
+
Maximum value for the colormap. Default is None.
|
402
|
+
bitrange : str or BitRange
|
403
|
+
Valuerange for the video. One of 'autodetect', 'MinMax' 'uint8', 'uint10', 'uint12', 'uint16', 'float' (for [0,1]), 'diff' (for [-1, 1]), 'phase' (for [0, 2*pi]), or 'phase_diff (for [-pi, pi])'. Default is 'autodetect'.
|
404
|
+
parent : str
|
405
|
+
Name of the parent video
|
406
|
+
opacity : OpacityFunction
|
407
|
+
Opacity function for alpha blending if video is a layer. One of 'linear', 'linear_r', 'centered', 1.0, 0.75, 0.5, 0.25, or 0.0. Default is `opacity=1.0`.
|
408
|
+
comment : str
|
409
|
+
Comment to be displayed
|
410
|
+
metadata : dict
|
411
|
+
Additional metadata to be displayed
|
412
|
+
"""
|
413
|
+
|
414
|
+
array = np.squeeze(array)
|
415
|
+
if array.ndim == 2:
|
416
|
+
# assume that it is a 2D image
|
417
|
+
array = np.expand_dims(array, 0)
|
418
|
+
elif array.ndim != 3:
|
419
|
+
raise ValueError("array is not two- or three-dimensional")
|
420
|
+
|
421
|
+
if array.dtype == np.float32:
|
422
|
+
dtype = ArrayDataType.FLOAT
|
423
|
+
elif array.dtype == np.uint8:
|
424
|
+
dtype = ArrayDataType.UINT8
|
425
|
+
elif array.dtype == np.uint16:
|
426
|
+
dtype = ArrayDataType.UINT16
|
427
|
+
else:
|
428
|
+
if np.iscomplexobj(array):
|
429
|
+
raise ValueError("Complex arrays not supported")
|
430
|
+
else:
|
431
|
+
array = array.astype(np.float32)
|
432
|
+
dtype = ArrayDataType.FLOAT
|
433
|
+
|
434
|
+
name = str(name)
|
435
|
+
|
436
|
+
if isinstance(cmap, str):
|
437
|
+
cmap = getattr(ColorMap, cmap.upper())
|
438
|
+
if isinstance(bitrange, str):
|
439
|
+
bitrange = getattr(BitRange, bitrange.upper())
|
440
|
+
if isinstance(opacity, str):
|
441
|
+
try:
|
442
|
+
opacity = float(opacity)
|
443
|
+
except ValueError:
|
444
|
+
pass
|
445
|
+
if isinstance(opacity, (int, float)):
|
446
|
+
if opacity == 1:
|
447
|
+
opacity = OpacityFunction.FIXED_100
|
448
|
+
elif opacity == 0.75:
|
449
|
+
opacity = OpacityFunction.FIXED_75
|
450
|
+
elif opacity == 0.5:
|
451
|
+
opacity = OpacityFunction.FIXED_50
|
452
|
+
elif opacity == 0.25:
|
453
|
+
opacity = OpacityFunction.FIXED_25
|
454
|
+
elif opacity == 0:
|
455
|
+
opacity = OpacityFunction.FIXED_0
|
456
|
+
else:
|
457
|
+
raise ValueError("Invalid opacity value")
|
458
|
+
if isinstance(opacity, str):
|
459
|
+
opacity = getattr(OpacityFunction, opacity.upper())
|
460
|
+
|
461
|
+
s = create_socket()
|
462
|
+
buf = create_array3meta_msg(dtype, name, array.shape, comment=comment, bitrange=bitrange, cmap=cmap,
|
463
|
+
parent_name=parent, opacity=opacity, metadata=metadata, vmin=vmin, vmax=vmax)
|
464
|
+
s.sendall(buf)
|
465
|
+
|
466
|
+
flat = array.flatten()
|
467
|
+
length = flat.size
|
468
|
+
max_size = MAX_BUFFER_SIZE
|
469
|
+
for idx in range(0, length, max_size):
|
470
|
+
end = length if idx + max_size > length else idx + max_size
|
471
|
+
if array.dtype == np.float32:
|
472
|
+
buf = create_array3dataf_msg(flat[idx:end], idx)
|
473
|
+
elif array.dtype == np.uint8:
|
474
|
+
buf = create_array3datau8_msg(flat[idx:end], idx)
|
475
|
+
elif array.dtype == np.uint16:
|
476
|
+
buf = create_array3datau16_msg(flat[idx:end], idx)
|
477
|
+
else:
|
478
|
+
raise NotImplementedError("Unkown dtype")
|
479
|
+
s.sendall(buf)
|
480
|
+
|
481
|
+
|
482
|
+
def show_layer(array: np.ndarray, name: Text = "", parent: Optional[Text] = None, opacity: Optional[OpacityFunction] = None, **kwargs):
|
483
|
+
"""
|
484
|
+
Add a layer to the parent video in Monochrome.
|
485
|
+
|
486
|
+
Parameters
|
487
|
+
----------
|
488
|
+
array : np.ndarray
|
489
|
+
The layer to be displayed. The array should have the shape (T, H, W) or (H, W).
|
490
|
+
name : str
|
491
|
+
Name of the layer
|
492
|
+
parent : str
|
493
|
+
Name of the parent video, if None the last loaded video will be used
|
494
|
+
opacity : OpacityFunction
|
495
|
+
Opacity function for alpha blending. One of 'linear', 'linear_r', 'centered', 1.0, 0.75, 0.5, 0.25, or 0.0. Default is `opacity=1.0`.
|
496
|
+
kwargs : dict
|
497
|
+
Additional arguments to be passed to :func:`show_video`
|
498
|
+
"""
|
499
|
+
if parent is None:
|
500
|
+
parent = ""
|
501
|
+
show_video(array, name=name, parent=parent, opacity=opacity, **kwargs)
|
502
|
+
|
503
|
+
|
504
|
+
def show_flow(flow_uv: np.ndarray, name: Text = "", parent: Optional[Text] = None, color=None):
|
505
|
+
"""
|
506
|
+
Visualize optical flow in Monochrome.
|
507
|
+
|
508
|
+
Parameters
|
509
|
+
----------
|
510
|
+
flow_uv : np.ndarray
|
511
|
+
Optical flow field of shape (T, H, W, 2)
|
512
|
+
name : str
|
513
|
+
Name of the flow
|
514
|
+
parent : str
|
515
|
+
Name of the parent video, if None the last loaded video will be used
|
516
|
+
color : str or tuple
|
517
|
+
Matplotlib color (either string like 'black' or rgba tuple)
|
518
|
+
"""
|
519
|
+
if flow_uv.ndim != 4:
|
520
|
+
raise ValueError("array is not four-dimensional")
|
521
|
+
if flow_uv.dtype != np.float32:
|
522
|
+
raise ValueError("array is not floating type")
|
523
|
+
if flow_uv.shape[3] != 2:
|
524
|
+
raise ValueError("flow should be of shape [T, H, W, 2]")
|
525
|
+
name = str(name)
|
526
|
+
|
527
|
+
s = create_socket()
|
528
|
+
shape = (flow_uv.shape[0] * 2, flow_uv.shape[1], flow_uv.shape[2])
|
529
|
+
buf = create_array3metaflow_msg(shape, parent, name, color)
|
530
|
+
s.sendall(buf)
|
531
|
+
|
532
|
+
flat = flow_uv.flatten()
|
533
|
+
length = flat.size
|
534
|
+
max_size = MAX_BUFFER_SIZE
|
535
|
+
for idx in range(0, length, max_size):
|
536
|
+
end = length if idx + max_size > length else idx + max_size
|
537
|
+
buf = create_array3dataf_msg(flat[idx:end], idx)
|
538
|
+
s.sendall(buf)
|
539
|
+
|
540
|
+
|
541
|
+
def show(array_or_path: Union[str, Path, np.ndarray], *args, **kwargs):
|
542
|
+
"""Autodetect the type of the input and show it in Monochrome."""
|
543
|
+
if isinstance(array_or_path, np.ndarray):
|
544
|
+
if array_or_path.ndim == 4 and array_or_path.shape[3] == 2:
|
545
|
+
return show_flow(array_or_path, *args, **kwargs)
|
546
|
+
else:
|
547
|
+
return show_video(array_or_path, *args, **kwargs)
|
548
|
+
elif isinstance(array_or_path, str) or isinstance(array_or_path, Path):
|
549
|
+
return show_file(array_or_path)
|
550
|
+
else:
|
551
|
+
raise ValueError("array_or_path has to be numpy array or string")
|
552
|
+
|
553
|
+
def export_video(filepath, name="", fps=30, t_start=0, t_end=-1, description="", close_after_completion=False):
|
554
|
+
"""Export a video displayed in Monochrome to a .mp4 file.
|
555
|
+
|
556
|
+
NOTE: Monochrome exports the video as rendered in the window, i.e. the video will have the same resolution as
|
557
|
+
the video window and all the layers/points/... will be merged into a single video.
|
558
|
+
|
559
|
+
Parameters
|
560
|
+
----------
|
561
|
+
filepath : str
|
562
|
+
Path to the output .mp4 file
|
563
|
+
name : str
|
564
|
+
Name of the video to be exported
|
565
|
+
fps : int
|
566
|
+
Frames per second of the output video
|
567
|
+
t_start : int
|
568
|
+
Start frame of the output video
|
569
|
+
t_end : int
|
570
|
+
End frame of the output video, -1 for the last frame
|
571
|
+
description : str
|
572
|
+
Description of the video to embed in the .mp4 file
|
573
|
+
close_after_completion : bool
|
574
|
+
Close the video in Monochrome after the export is completed
|
575
|
+
"""
|
576
|
+
s = create_socket()
|
577
|
+
builder = flatbuffers.Builder(512)
|
578
|
+
name_fb = builder.CreateString(name)
|
579
|
+
filepath_fb = builder.CreateString(str(Path(filepath).absolute()))
|
580
|
+
description_fb = builder.CreateString(description)
|
581
|
+
|
582
|
+
VideoExport.Start(builder)
|
583
|
+
VideoExport.AddRecording(builder, name_fb)
|
584
|
+
VideoExport.AddFilepath(builder, filepath_fb)
|
585
|
+
VideoExport.AddDescription(builder, description_fb)
|
586
|
+
VideoExport.AddFormat(builder, VideoExportFormat.FFMPEG)
|
587
|
+
VideoExport.AddFps(builder, fps)
|
588
|
+
VideoExport.AddTStart(builder, t_start)
|
589
|
+
VideoExport.AddTEnd(builder, t_end)
|
590
|
+
VideoExport.AddCloseAfterCompletion(builder, close_after_completion)
|
591
|
+
fp = VideoExport.End(builder)
|
592
|
+
root = build_root(builder, Data.VideoExport, fp)
|
593
|
+
builder.FinishSizePrefixed(root)
|
594
|
+
buf = builder.Output()
|
595
|
+
s.sendall(buf)
|
596
|
+
|
597
|
+
def close_video(name=""):
|
598
|
+
"""Close a video in Monochrome.
|
599
|
+
|
600
|
+
Parameters
|
601
|
+
----------
|
602
|
+
name : str
|
603
|
+
Name of the video to be closed. If empty, the last loaded video will be closed.
|
604
|
+
"""
|
605
|
+
s = create_socket()
|
606
|
+
builder = flatbuffers.Builder(512)
|
607
|
+
name_fb = builder.CreateString(name)
|
608
|
+
|
609
|
+
CloseVideo.Start(builder)
|
610
|
+
CloseVideo.AddName(builder, name_fb)
|
611
|
+
fp = CloseVideo.End(builder)
|
612
|
+
root = build_root(builder, Data.CloseVideo, fp)
|
613
|
+
builder.FinishSizePrefixed(root)
|
614
|
+
buf = builder.Output()
|
615
|
+
s.sendall(buf)
|
616
|
+
|
617
|
+
def quit():
|
618
|
+
"""Quit Monochrome, terminating the process."""
|
619
|
+
s = create_socket()
|
620
|
+
builder = flatbuffers.Builder(512)
|
621
|
+
root = build_root(builder, Data.Quit, 0)
|
622
|
+
builder.FinishSizePrefixed(root)
|
623
|
+
buf = builder.Output()
|
624
|
+
s.sendall(buf)
|