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 8e3ac9c..b350e38 100644
Binary files a/dvis/__pycache__/__init__.cpython-38.pyc and b/dvis/__pycache__/__init__.cpython-38.pyc differ
diff --git a/dvis/__pycache__/dvis.cpython-38.pyc b/dvis/__pycache__/dvis.cpython-38.pyc
index a489595..0f20b48 100644
Binary files a/dvis/__pycache__/dvis.cpython-38.pyc and b/dvis/__pycache__/dvis.cpython-38.pyc differ
diff --git a/dvis/__pycache__/dvis_client.cpython-38.pyc b/dvis/__pycache__/dvis_client.cpython-38.pyc
index 3d99ff9..64340af 100644
Binary files a/dvis/__pycache__/dvis_client.cpython-38.pyc and b/dvis/__pycache__/dvis_client.cpython-38.pyc differ
diff --git a/dvis/__pycache__/utils.cpython-38.pyc b/dvis/__pycache__/utils.cpython-38.pyc
index b88913f..19f2102 100644
Binary files a/dvis/__pycache__/utils.cpython-38.pyc and b/dvis/__pycache__/utils.cpython-38.pyc differ
diff --git a/dvis/dvis.py b/dvis/dvis.py
index 38e9b68..6ea90b6 100644
--- a/dvis/dvis.py
+++ b/dvis/dvis.py
@@ -15,7 +15,7 @@
torch_float = None
torch_double = None
-from .dvis_client import send2server, send_payload2server
+from .dvis_client import send2server, send_payload2server, send_visdom_command
from .dvis_client import send_plotly, sendMesh2server, send_clear, send_config, sendTrack2server, sendCmd2server,sendPose2server,sendInject2server, send_objectKFState, sendCamImage2server
from .dvis_client import set_port, set_vis_port
import trimesh
@@ -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):
@@ -367,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:
@@ -401,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,s=s))
os.remove(fn)
else:
img = Image.open(fn)
- data = np.array(_image_size(img))
+ data = np.array(_image_resize(img,s=s))
if meta is None:
meta = {}
meta["obj_path"] = fn
@@ -447,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)
@@ -768,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:
@@ -780,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:
@@ -806,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)
@@ -824,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"
@@ -849,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"
@@ -1093,6 +1110,10 @@ def dvis(
if "vis_port" in kwargs:
set_vis_port(kwargs["vis_port"])
return
+ # special commands
+ if isinstance(data, str) and data in ["clear", "clean", "close"]:
+ send_visdom_command("close")
+ return
if isinstance(l, int):
l = [l]
@@ -1134,7 +1155,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/dvis_client.py b/dvis/dvis_client.py
index aa559be..2444570 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":
@@ -58,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":
@@ -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})
@@ -125,6 +134,10 @@ def send_payload2server(
"shape": shape,
},
)
+def send_visdom_command(cmd):
+ vis = _get_visdom_instance()
+ if cmd == "close":
+ vis.close()
def img_to_base64(image):
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..9b1a9e5 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -9,12 +9,11 @@ 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
eventlet
jinja2>=3.0.2
-itsdangerous==2.0.1
-werkzeug==2.0.3
+werkzeug>=2.3.6
opencv-python
diff --git a/server/__pycache__/register_mirror.cpython-38.pyc b/server/__pycache__/register_mirror.cpython-38.pyc
index 06c7425..11590af 100644
Binary files a/server/__pycache__/register_mirror.cpython-38.pyc and b/server/__pycache__/register_mirror.cpython-38.pyc differ
diff --git a/setup.py b/setup.py
index f9af9eb..ec78aff 100644
--- a/setup.py
+++ b/setup.py
@@ -5,7 +5,7 @@
setuptools.setup(
name="dvis",
- version="0.8.8.1",
+ version="0.9.0.3",
author="Norman Müller",
author_email="norman.mueller@tum.de",
url="https://github.com/SirWyver/dvis",
@@ -24,10 +24,10 @@
"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",
+ "pillow>=10.0",
"trimesh",
"simple-websocket>=0.2.0",
"python-socketio>=4.6.1",
diff --git a/test.py b/test.py
index bbb68ef..7e14996 100644
--- a/test.py
+++ b/test.py
@@ -1,12 +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')