From 800cabf396d8e0a13c08f4a9f98f4d433e45db73 Mon Sep 17 00:00:00 2001 From: normanm Date: Tue, 19 Sep 2023 11:02:08 +0200 Subject: [PATCH 1/5] visdom single connection --- dvis/dvis.py | 7 ++++++- dvis/dvis_client.py | 15 ++++++++++++--- setup.py | 6 +++--- test.py | 4 ++++ 4 files changed, 25 insertions(+), 7 deletions(-) diff --git a/dvis/dvis.py b/dvis/dvis.py index 38e9b68..d61498a 100644 --- a/dvis/dvis.py +++ b/dvis/dvis.py @@ -50,7 +50,12 @@ def convert_to_nd(data): return np.array(data) except: pass - raise Exception("Data type not understood") + try: + data = np.array(data) + print(f"Converted to np.ndarray: {data.shape}") + return data + except: + raise Exception("Data type not understood") def matplot2PIL(fig): diff --git a/dvis/dvis_client.py b/dvis/dvis_client.py index aa559be..6fbf5e3 100644 --- a/dvis/dvis_client.py +++ b/dvis/dvis_client.py @@ -13,6 +13,8 @@ PORT = 5001 VIS_PORT = 4999 +visdom_instance = None + def set_port(port): global PORT PORT = port @@ -26,6 +28,12 @@ def encode_to_base64(x): return x return base64.b64encode(x).decode("utf8") +def _get_visdom_instance(): + global visdom_instance + if visdom_instance is None: + visdom_instance = visdom.Visdom(port=VIS_PORT) + return visdom_instance + def send2server(data, data_format, size, color, layers, t, name="", meta_data=None, vis_conf=None, shape="v", compression="gzip", sub_format=None): if data is not None: @@ -38,7 +46,8 @@ def send2server(data, data_format, size, color, layers, t, name="", meta_data=No else: print("Sending group") if data_format in ["hwc", "img", "seq"]: - vis = visdom.Visdom(port=VIS_PORT) + vis = _get_visdom_instance() + if data_format == "seq": for img in data: vis.image(img, opts=dict(store_history=True), win=name) @@ -49,7 +58,7 @@ def send2server(data, data_format, size, color, layers, t, name="", meta_data=No data[np.all(data == 255, 2)] = np.array(color) vis.image(data.transpose(2, 0, 1), opts={"caption": name}) elif data_format in ["gif"]: - vis = visdom.Visdom(port=VIS_PORT) + vis = _get_visdom_instance() vis.text(f'', opts={"caption": name}) else: if compression == "pkl": @@ -97,7 +106,7 @@ def send2server(data, data_format, size, color, layers, t, name="", meta_data=No def send_plotly(data, layout): - vis = visdom.Visdom(port=VIS_PORT) + vis = _get_visdom_instance() print(f"Sending plolty {data['type']}") vis._send({"data": [data], "layout": layout}) diff --git a/setup.py b/setup.py index f9af9eb..54c8e76 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ setuptools.setup( name="dvis", - version="0.8.8.1", + version="0.8.9.0", author="Norman Müller", author_email="norman.mueller@tum.de", url="https://github.com/SirWyver/dvis", @@ -24,8 +24,8 @@ "dvis-server=dvis.dvis_cli:dvis_server_cli"], }, install_requires=[ - "flask>=2.3.2", - "flask_socketio>=5.3.4", + #"Flask>=2.1.2", + #"flask_socketio>=5.3.4", "numpy", "pillow", "trimesh", diff --git a/test.py b/test.py index bbb68ef..3149f0d 100644 --- a/test.py +++ b/test.py @@ -1,6 +1,10 @@ import numpy as np from dvis import dvis +from PIL import Image +dvis(port=5010) +dvis(vis_port=5011) +dvis(Image.fromarray(np.random.randint(0,255,size=(300,200,3)).astype(np.uint8))) imgseq = np.random.rand(4,200,200,1) From 66ca7218da3adc0c690271feaa57e88ea916bf81 Mon Sep 17 00:00:00 2001 From: normanm Date: Mon, 16 Oct 2023 19:14:05 +0200 Subject: [PATCH 2/5] size argument for sequential data --- dvis/dvis.py | 25 ++++++++++++++++++------- dvis/utils.py | 7 ++++--- requirements.txt | 2 +- setup.py | 4 ++-- 4 files changed, 25 insertions(+), 13 deletions(-) diff --git a/dvis/dvis.py b/dvis/dvis.py index d61498a..7412172 100644 --- a/dvis/dvis.py +++ b/dvis/dvis.py @@ -372,7 +372,7 @@ def dvis_mesh_pc(data, vs=1, c=0, l=0, t=None, name=None, meta=None, ms=None, vi shape=shape, ) -def _image_size(img, s, is_bool=False): +def _image_resize(img, s, is_bool=False): if s!=1: if isinstance(s, (int,float)): if s < 10: @@ -406,11 +406,11 @@ def dvis_img(data, vs=1, c=0, l=[0], t=None, name=None, meta=None, vis_conf=None fn =f"tmp.{suffix}" Connection(hostname).get(remote_path,fn) img = Image.open(fn) - data = np.array(_image_size(img)) + data = np.array(_image_resize(img)) os.remove(fn) else: img = Image.open(fn) - data = np.array(_image_size(img)) + data = np.array(_image_resize(img)) if meta is None: meta = {} meta["obj_path"] = fn @@ -452,7 +452,7 @@ def dvis_img(data, vs=1, c=0, l=[0], t=None, name=None, meta=None, vis_conf=None data = (data[...,:3]*255).astype(np.uint8) if s!=1: img = Image.fromarray(data) - img = _image_size(img,s, is_bool=is_bool) + img = _image_resize(img,s, is_bool=is_bool) data = np.array(img) if is_bool: data = data.astype(np.uint8) @@ -773,7 +773,7 @@ def dvis_transform(data, s=1, c=0, l=[0], t=0, name="transform", meta=None, vis_ compression="pkl", ) -def dvis_seq(data, vs=1, c=0, l=[0], t=None, name=None, meta=None, vis_conf=None, cm='default', fmt='seq', mi=None, ma=None): +def dvis_seq(data, vs=1, c=0, l=[0], t=None, name=None, meta=None, vis_conf=None, cm='default', fmt='seq', mi=None, ma=None,s=1): data = convert_to_nd(data) sub_format = None if len(data.shape)==3 or data.shape[-1] == 1: @@ -785,7 +785,7 @@ def dvis_seq(data, vs=1, c=0, l=[0], t=None, name=None, meta=None, vis_conf=None elif sub_format == 'xyr': # range image with # remap default to jet - data = np.stack(visualize_range(data[i], cm=("jet" if cm=='default' else cm), mi=mi, ma=ma) for i in range(data.shape[0])) + data = np.stack(visualize_range(data[i], cm=("jet" if cm=='default' else cm), mi=mi, ma=ma,verbose=i==0) for i in range(data.shape[0])) if data.shape[-1] in [3,4]: # T H W C data = data.transpose(0,3,1,2) if name is None: @@ -811,6 +811,17 @@ def dvis_seq(data, vs=1, c=0, l=[0], t=None, name=None, meta=None, vis_conf=None data[...,:3] = data[...,:3] * alpha_mask + (1.0 - c) * (1-alpha_mask) data = (data[...,:3]*255).astype(np.uint8) data = data.transpose(0,3,1,2) + + if s!=1: + is_bool = data.dtype == bool + resized_data = [] + for img in data: + img = Image.fromarray(img.transpose(1,2,0)) + img = _image_resize(img,s, is_bool=is_bool) + resized_data.append(np.array(img).transpose(2,0,1)) + data = np.stack(resized_data) + if is_bool: + data = data.astype(np.uint8) send2server(data=data, data_format="seq", size=vs, color=c, layers=l, t=t, name=name, meta_data=meta, vis_conf=vis_conf, sub_format=sub_format) @@ -1139,7 +1150,7 @@ def dvis( elif fmt == "cam": dvis_cam(data, name) elif fmt == "seq": - dvis_seq(data, vs, c, l, t, name, meta, vis_conf, fmt=fmt, cm=kwargs.get("cm",'default'), mi=kwargs.get('mi'), ma=kwargs.get('ma')) + dvis_seq(data, vs, c, l, t, name, meta, vis_conf, fmt=fmt, cm=kwargs.get("cm",'default'), mi=kwargs.get('mi'), ma=kwargs.get('ma'),s=s) elif fmt == "cmd": dvis_cmd(data) diff --git a/dvis/utils.py b/dvis/utils.py index 041963a..bdfa20e 100644 --- a/dvis/utils.py +++ b/dvis/utils.py @@ -110,7 +110,7 @@ def get_color_batched(col_vals, cm=None): ) -def visualize_range(cont_label, img_ijs=None, H=None, W=None, cm="jet", mi=None, ma=None): +def visualize_range(cont_label, img_ijs=None, H=None, W=None, cm="jet", mi=None, ma=None,verbose=True): """ cont_label: (H, W) or (N,) or (H,W,1) """ @@ -131,7 +131,7 @@ def visualize_range(cont_label, img_ijs=None, H=None, W=None, cm="jet", mi=None, # convert invalid cont_label vals to 0 x[np.isinf(x)] = 0 x[np.isneginf(x)] = 0 - use_auto_range = ((mi is None) or (ma is None)) + use_auto_range = ((mi is None) and (ma is None)) if mi is None: mi = np.min(x) # get minimum cont_label if ma is None: @@ -139,7 +139,8 @@ def visualize_range(cont_label, img_ijs=None, H=None, W=None, cm="jet", mi=None, if use_auto_range and (mi >= 0 and ma <= 1): mi, ma = 0.0, 1.0 - print(f"Set range: {mi} - {ma}") + if verbose: + print(f"Set range: {mi} - {ma}") x = np.clip(x, mi, ma) x = (x - mi) / max(ma - mi, 1e-8) # normalize to 0~1 x = np.clip((255 * x).astype(np.uint8), 0, 255) diff --git a/requirements.txt b/requirements.txt index c91085a..14056b9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,7 +9,7 @@ transforms3d flask>=2.3.2 flask_socketio>=5.3.4 numpy -pillow +pillow>=10.0.0 trimesh simple-websocket>=0.2.0 python-socketio>=4.6.1 diff --git a/setup.py b/setup.py index 54c8e76..ea03fa9 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ setuptools.setup( name="dvis", - version="0.8.9.0", + version="0.9.0.0", author="Norman Müller", author_email="norman.mueller@tum.de", url="https://github.com/SirWyver/dvis", @@ -27,7 +27,7 @@ #"Flask>=2.1.2", #"flask_socketio>=5.3.4", "numpy", - "pillow", + "pillow>=10.0", "trimesh", "simple-websocket>=0.2.0", "python-socketio>=4.6.1", From d4cd5c4a325e0fa14e3515d6050d483aa2a2f20f Mon Sep 17 00:00:00 2001 From: normanm Date: Tue, 17 Oct 2023 12:21:20 +0200 Subject: [PATCH 3/5] pathlib support --- dvis/dvis.py | 7 ++++--- setup.py | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/dvis/dvis.py b/dvis/dvis.py index 7412172..053aece 100644 --- a/dvis/dvis.py +++ b/dvis/dvis.py @@ -406,11 +406,11 @@ def dvis_img(data, vs=1, c=0, l=[0], t=None, name=None, meta=None, vis_conf=None fn =f"tmp.{suffix}" Connection(hostname).get(remote_path,fn) img = Image.open(fn) - data = np.array(_image_resize(img)) + data = np.array(_image_resize(img,s=s)) os.remove(fn) else: img = Image.open(fn) - data = np.array(_image_resize(img)) + data = np.array(_image_resize(img,s=s)) if meta is None: meta = {} meta["obj_path"] = fn @@ -840,7 +840,8 @@ def _infer_format(data): or (hasattr(matplotlib, "figure") and isinstance(data, matplotlib.figure.Figure)) ): fmt = "img" - elif isinstance(data, str): + elif isinstance(data, (Path, str)): + data = str(data) suffix = data.split(".")[-1] if suffix in ["jpeg", "jpg", "png", "gif"]: fmt = "img" diff --git a/setup.py b/setup.py index ea03fa9..b5565b2 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ setuptools.setup( name="dvis", - version="0.9.0.0", + version="0.9.0.1", author="Norman Müller", author_email="norman.mueller@tum.de", url="https://github.com/SirWyver/dvis", From 218f9a40d7b4f5f85dd64125d65811ed560e40e0 Mon Sep 17 00:00:00 2001 From: normanm Date: Mon, 23 Oct 2023 17:46:09 +0200 Subject: [PATCH 4/5] python3.11 gzip fix --- dvis/dvis.py | 2 +- dvis/dvis_client.py | 2 +- requirements.txt | 3 +-- setup.py | 2 +- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/dvis/dvis.py b/dvis/dvis.py index 053aece..4c75624 100644 --- a/dvis/dvis.py +++ b/dvis/dvis.py @@ -866,7 +866,7 @@ def _infer_format(data): # fmt = "cwhl" else: raise IOError("Data format %s not understood" % str(data.shape)) - elif len(data.shape) == 3: + if len(data.shape) == 3: if (data.shape[0] == 3 or data.shape[2] ==3) or (data.shape[0] == 4 or data.shape[2] ==4): # assume image for convenience fmt = "img" diff --git a/dvis/dvis_client.py b/dvis/dvis_client.py index 6fbf5e3..09913da 100644 --- a/dvis/dvis_client.py +++ b/dvis/dvis_client.py @@ -67,7 +67,7 @@ def send2server(data, data_format, size, color, layers, t, name="", meta_data=No else: data = pickle.dumps(data) elif compression == "gzip": - data = gzip.compress(data.astype(np.float32).copy(order="C")) + data = gzip.compress(data.astype(np.float32).copy(order="C"), mtime=0) elif compression in ["glb", "obj"]: # meshes tm = data if compression == "glb": diff --git a/requirements.txt b/requirements.txt index 14056b9..9b1a9e5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -15,6 +15,5 @@ simple-websocket>=0.2.0 python-socketio>=4.6.1 eventlet jinja2>=3.0.2 -itsdangerous==2.0.1 -werkzeug==2.0.3 +werkzeug>=2.3.6 opencv-python diff --git a/setup.py b/setup.py index b5565b2..e846400 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ setuptools.setup( name="dvis", - version="0.9.0.1", + version="0.9.0.2", author="Norman Müller", author_email="norman.mueller@tum.de", url="https://github.com/SirWyver/dvis", From 6fbe82e1bce6efb1caab001fc0e42c437e9f0bca Mon Sep 17 00:00:00 2001 From: normanm Date: Wed, 15 Nov 2023 15:56:54 +0100 Subject: [PATCH 5/5] visdom clear --- dvis.egg-info/PKG-INFO | 145 ++++++++++++++++++ dvis.egg-info/SOURCES.txt | 14 ++ dvis.egg-info/dependency_links.txt | 1 + dvis.egg-info/entry_points.txt | 4 + dvis.egg-info/requires.txt | 9 ++ dvis.egg-info/top_level.txt | 1 + dvis/__pycache__/__init__.cpython-38.pyc | Bin 163 -> 171 bytes dvis/__pycache__/dvis.cpython-38.pyc | Bin 28774 -> 29233 bytes dvis/__pycache__/dvis_client.cpython-38.pyc | Bin 6895 -> 7203 bytes dvis/__pycache__/utils.cpython-38.pyc | Bin 4079 -> 4104 bytes dvis/dvis.py | 6 +- dvis/dvis_client.py | 4 + .../register_mirror.cpython-38.pyc | Bin 1671 -> 1679 bytes setup.py | 2 +- test.py | 12 +- 15 files changed, 195 insertions(+), 3 deletions(-) create mode 100644 dvis.egg-info/PKG-INFO create mode 100644 dvis.egg-info/SOURCES.txt create mode 100644 dvis.egg-info/dependency_links.txt create mode 100644 dvis.egg-info/entry_points.txt create mode 100644 dvis.egg-info/requires.txt create mode 100644 dvis.egg-info/top_level.txt diff --git a/dvis.egg-info/PKG-INFO b/dvis.egg-info/PKG-INFO new file mode 100644 index 0000000..6ce402f --- /dev/null +++ b/dvis.egg-info/PKG-INFO @@ -0,0 +1,145 @@ +Metadata-Version: 2.1 +Name: dvis +Version: 0.9.0.2 +Summary: The best web-based visualizer +Home-page: https://github.com/SirWyver/dvis +Author: Norman Müller +Author-email: norman.mueller@tum.de +License: UNKNOWN +Platform: UNKNOWN +Classifier: Programming Language :: Python :: 3 +Classifier: License :: OSI Approved :: MIT License +Classifier: Operating System :: OS Independent +Requires-Python: >=3.6 +Description-Content-Type: text/markdown + +

+

DVIS: 3D Visualizations made easy

+

Visualize your data with just one line of code

+

Python -> Browser

+ + +

+ + + + +

+ +# 💻 Usage +```python +from dvis import dvis + +dvis("mesh.obj") # load file +dvis(point_cloud, vs=0.03) # point cloud with specific voxel size +dvis(bboxes,'bbox', c=3, name='my_boxes') # show colored boxes +dvis(np.array([0,0,0,1,2,3]), 'vec') # vector from origin to (1,2,3) +dvis(transform, 'transform') # display transformation +dvis(img, 'img') # display an image using visdom +``` +Check out more examples in `./examples` +``` +python examples/meshes.py +python examples/point_clouds.py +python examples/... +``` + + +# News +* 0.8.8.0: Bumped dependencies version, custom port support + ``` + dvis(port=) # set port the client is sending on + dvis(vis_port=) # set the port for visdom + ``` + +* 0.8.7.0: CLI for server: To start the server, use run + ``` + dvis-server [--no_visdom] + ``` +* 0.8.6.0: Histogram support using plotly + ``` + dvis(array, "hist", mi=0.1,ma=0.8, nbins=10, name="Example histogram") + ``` + +* 0.8.4.0: Label and range image support, auto-format for img + ``` + dvis(label_img [fmt='xyl']) # visualizses img of labels + dvis(depth_map [fmt='xyr', cm='jet']) # visualizes an image of continuous values using cv2 color map + dvis(heat_map [fmt='xyr', cm='hot']) + ``` + +# 🚀 Getting started + +## 1. Install the `dvis` package: +Via pypi: +``` +pip install dvis +``` +or from source: +``` +git clone git@github.com:SirWyver/dvis.git +cd dvis +pip install . +``` +## 2. Start the web server +``` +# From the dvis repository folder: +dvis-server +``` +or manually +``` +cd server +python server.py +``` + +Verify you can open http://localhost:5001/ and see something like this: + +

+ +

+ +Optionally, also start [visdom](https://github.com/fossasia/visdom) to display images/videos/charts: +``` +visdom -p 4999 +``` +The visdom server should be accessible at http://localhost:4999/. + +Try out the client +```python +import numpy as np +from dvis import dvis +dvis(np.random.rand(1000,6), s=0.03) # sends randomly colored 1000x3 point cloud to the 3d server +dvis("static/icon.png","img") # sends an image to the 2d server + +``` +Verify you can see a colored point cloud + +# 📖 Documentation +For an overview of available commands check out the [documentation](https://sirwyver.github.io/dvis_docu/) + +## Shotcuts +| Shortcut | Description | +|----------|---------------------------| +| **Editor** | | +| w | Translate | +| e | Rotate | +| r | Scale | +| z | Undo | +| f | Focus | +| **DVIS** | | +| v | Show/hide selected object | +| 1-5 | Toggle layer 1-5 | +| 0 | Toggle all layers | +| Shift + 0-5 | Show layer 0-5 add. | +| g | Show/hide grid & axes helper | +| n | Next keyframe | +| b | Previous keyframe | +| . | Next frame | +| , | Previous frame | +| t | Switch camera | +| [ | Download screenshot | + + + + + diff --git a/dvis.egg-info/SOURCES.txt b/dvis.egg-info/SOURCES.txt new file mode 100644 index 0000000..fa67633 --- /dev/null +++ b/dvis.egg-info/SOURCES.txt @@ -0,0 +1,14 @@ +README.md +setup.py +dvis/__init__.py +dvis/dvis.py +dvis/dvis_cli.py +dvis/dvis_client.py +dvis/dvis_client_old.py +dvis/utils.py +dvis.egg-info/PKG-INFO +dvis.egg-info/SOURCES.txt +dvis.egg-info/dependency_links.txt +dvis.egg-info/entry_points.txt +dvis.egg-info/requires.txt +dvis.egg-info/top_level.txt \ No newline at end of file diff --git a/dvis.egg-info/dependency_links.txt b/dvis.egg-info/dependency_links.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/dvis.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/dvis.egg-info/entry_points.txt b/dvis.egg-info/entry_points.txt new file mode 100644 index 0000000..8ec4b8a --- /dev/null +++ b/dvis.egg-info/entry_points.txt @@ -0,0 +1,4 @@ +[console_scripts] +dvis = dvis.dvis_cli:dvis_cli +dvis-server = dvis.dvis_cli:dvis_server_cli + diff --git a/dvis.egg-info/requires.txt b/dvis.egg-info/requires.txt new file mode 100644 index 0000000..9f5fce9 --- /dev/null +++ b/dvis.egg-info/requires.txt @@ -0,0 +1,9 @@ +numpy +pillow>=10.0 +trimesh +simple-websocket>=0.2.0 +python-socketio>=4.6.1 +eventlet +jinja2>=3.0.2 +werkzeug>=2.0.3 +opencv-python diff --git a/dvis.egg-info/top_level.txt b/dvis.egg-info/top_level.txt new file mode 100644 index 0000000..bc52aea --- /dev/null +++ b/dvis.egg-info/top_level.txt @@ -0,0 +1 @@ +dvis diff --git a/dvis/__pycache__/__init__.cpython-38.pyc b/dvis/__pycache__/__init__.cpython-38.pyc index 8e3ac9c1fc2410853bb84691f17cf0c8ba3d41eb..b350e380da4ecf27042a0a06284d075cfe139fb1 100644 GIT binary patch delta 37 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 diff --git a/dvis/__pycache__/dvis_client.cpython-38.pyc b/dvis/__pycache__/dvis_client.cpython-38.pyc index 3d99ff9bd1c84e305581b1a5b40ffc9395326f07..64340af52c17b806e58444f1b02a3baabfd20992 100644 GIT binary patch delta 3159 zcmZuzYit}>6`nh@J3ITdytiB{ql0)8O=R8xtE_>F)+{6Z?{+*!wt#jf_7 zbI(2J-gD3S?wS4P(I1?MJspjP6#TsN>9qOAz-zIr8e^~RpI>FAb_jrRN15q>AcXK4 zhcHC&*$GjI;WGh;Ar75)*i08BpbIrgI0D_!gPLwgK^iqZkbx{dQ*ab|p$})$&<{D( zWMBXW@tK7o7{+HW?12${_Q75ly`$`(>4!Y*Lq!gb!G1V^t^+Uz1=I|}aX19ys2KwG zl#)Lo_OY9>k-|c=ZWbC%UN1H3;)iUA#l`Q~0!xY`>W@~_ct%U7ld=%=F`h)JOv9ZY z4F{9>vw9G01(d466E>@;wgzmSl~9NKsZ-p*`Ml3;9Z zDCd=H>Dy5!Qqi5LjirL%I&*Xgt!qrVZa2=^ilf=an##Z5NZV{9=xCeFQBVpw3WTdF zL^jn^iu6}Du(VXxO%2){eUU-?y2_OMtnn7^*HP8)vsJFQ0*F*15OECGky`b$IUA08jU9G=tj&@Hri0rzR4;b&;`kLw#jZQjgU7d<-ekTxAb4KCeBG-fjt0@H z4+HwW-R)>=3}-oH=0?SGQx#J~OL z*q}I~PiC@6gFJKnb$hXH=2cglug$tzbGE_{h`ahhHYR?l{|RmH1=9L?v_+o$I&hU` zMUOFk=rAdc5D>*(gPYfvP0O}i-%``Ec>tC9c0NG{rwC4q_l?fSPLgx#W}iEwyPmsdB6D77@HWuIje!7Fk-5~delD6z^9JPHCS2=F;z{o99}k^_2DSS zvT6iJIWZYLJ0GW}E3Gt_?YzcQIOLC$&R?|52B1owe2(<91avufT+1+76_;vF zyS7rpsaDI-S)B0&G94B3q4$n|0Yz7{xyAFO+(&Q%;a0R@Ei`Wymr5(OW(hc@H6L~Z zt+9AMJk+r}%@@Si!s&@=TH+GH(*#!tiUiLSP}TU81WzI4!@NrJcKy2kZ~er7!+Y6v zF&rs8TSxmcL)iLI?&&AO^M3sdTNoZz}BL`Qll%ikjV7=mPzsv}$C z$Rb`7Jo@K7@@!{ulAl3v)f<+V>rkOw!rogi&71s!SdJanW&SRU4`Ra?Y1!QgBqHR4 zZb-Tmp=6i%SGShsXGxMa!#f+Q~$g#zQN2=B5P0~O-q_O5Jy-bPJtawK|s% z^4*Kl@;g#SLwS217k}tH+3_3N&HEeB$C8Y{`eAn>&DaSs-Zgw!7M?=Cr59{oDpx%| zP{)`?>Qg*!0GYlxTQlW|-xqhg23Nh};HCW92%kW`c7Z|5{Y6W`oPX?vWeal$kBlMXb^{scp^#s&iCgaX#6>ek-md_psNo zqP^nn9}TZ3pCRM31oYzZn%n0| zI!7?6%pyu&b?|3M^JoTTm*-b$VunB?c$VNf0@{>+g<$*r_-NVkAzr3eVfsz7k}Q)O z%V4NW@(J-+YKZ+^Jdrv%`W7nkI;X}tHN`VDO>ga~y#Ep1sQ6hbSCCem(l?bK_BbGe zmjU#k+VwY<>q{#hQLc&4QvIv6nwMwE-Y5;b2_gj4E9Lv$>&Nsq^c$wf80_xEGRf5+EWSQwqTeJ}O(iwJ1{5YLC=u-_Je*H!ubkUGk2kjK)vrvzr R8{tqw&XI-B&_hYX_di(ys%iiL delta 2971 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 Bf42Yt delta 409 zcmXX?Jxc>Y5WU&tcJKB+qIiTP7Fvn1v9Qp>P7tfJ2wGSvE~5D`@w~!fuStT~2!G>9h1d}-Q4@8XTmcoMe52OL7cFc?&1O8FkUCvHXWD2U4t5i359?N{u6X1?^NY;zgP5KuQuWxiXyzIEuLIYRe5_N44E@5P%PPC;0fBfP80z9iX he>*?G&nQEvlZIg!wGoG*;<lpK0D|h){TsOrnfP?{GxBp&_4D$Jauf4%H#;%8vH$?a_6pnp delta 32 mcmeC@ZRh0<<>lpK0D`|)_HE=gWMWg%FUrp^+3dyS$^rm|dI*>R diff --git a/setup.py b/setup.py index e846400..ec78aff 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ setuptools.setup( name="dvis", - version="0.9.0.2", + version="0.9.0.3", author="Norman Müller", author_email="norman.mueller@tum.de", url="https://github.com/SirWyver/dvis", diff --git a/test.py b/test.py index 3149f0d..7e14996 100644 --- a/test.py +++ b/test.py @@ -1,16 +1,26 @@ import numpy as np from dvis import dvis from PIL import Image +from pathlib import Path + dvis(port=5010) dvis(vis_port=5011) + + +dvis(Path("static/camera_path.gif")) +dvis("static/dvis_ui.png") +dvis(np.random.randint(0,55,(300,3))) + + dvis(Image.fromarray(np.random.randint(0,255,size=(300,200,3)).astype(np.uint8))) +dvis(np.random.rand(1,1,3,100,100)) imgseq = np.random.rand(4,200,200,1) +dvis(imgseq,'seq',s=100) dvis(imgseq, "hist", mi=0.1,ma=0.8, nbins=10, name="lol", c=2) # layout={"title": {"text": "lol"}}) -dvis(imgseq,'seq') dvis([x for x in imgseq]) dvis(imgseq, name='kaka')