From 72f0b185e8390ada9b7f0a3c92e358e7e7ab094c Mon Sep 17 00:00:00 2001 From: Thanos Date: Thu, 10 Sep 2020 17:28:29 +0000 Subject: [PATCH 01/43] Add K3d widgets for PointCloud, TriMesh and TexturedTriMesh --- menpo3d/visualize/__init__.py | 5 +- menpo3d/visualize/base.py | 10 + menpo3d/visualize/viewk3dwidgets.py | 530 ++++++++++++++++++++++++++++ 3 files changed, 544 insertions(+), 1 deletion(-) create mode 100644 menpo3d/visualize/viewk3dwidgets.py diff --git a/menpo3d/visualize/__init__.py b/menpo3d/visualize/__init__.py index 87e7761..d771412 100644 --- a/menpo3d/visualize/__init__.py +++ b/menpo3d/visualize/__init__.py @@ -5,4 +5,7 @@ ColouredTriMeshViewer3d, TexturedTriMeshViewer3d, LandmarkViewer3d, -) + TriMeshInlineViewer3d, + PointGraphInlineViewer3d, + TexturedTriMeshInlineViewer3d, + diff --git a/menpo3d/visualize/base.py b/menpo3d/visualize/base.py index 696c898..e273127 100644 --- a/menpo3d/visualize/base.py +++ b/menpo3d/visualize/base.py @@ -7,9 +7,19 @@ MayaviColouredTriMeshViewer3d, ) +from .viewk3dwidgets import ( + K3dwidgetsTriMeshViewer3d, + K3dwidgetsPointGraphViewer3d, + K3dwidgetsTexturedTriMeshViewer3d, +) + PointGraphViewer3d = MayaviPointGraphViewer3d TriMeshViewer3d = MayaviTriMeshViewer3d TexturedTriMeshViewer3d = MayaviTexturedTriMeshViewer3d ColouredTriMeshViewer3d = MayaviColouredTriMeshViewer3d LandmarkViewer3d = MayaviLandmarkViewer3d VectorViewer3d = MayaviVectorViewer3d + +TriMeshInlineViewer3d = K3dwidgetsTriMeshViewer3d +TexturedTriMeshInlineViewer3d = K3dwidgetsTexturedTriMeshViewer3d +PointGraphInlineViewer3d = K3dwidgetsPointGraphViewer3d diff --git a/menpo3d/visualize/viewk3dwidgets.py b/menpo3d/visualize/viewk3dwidgets.py new file mode 100644 index 0000000..2438a76 --- /dev/null +++ b/menpo3d/visualize/viewk3dwidgets.py @@ -0,0 +1,530 @@ +import numpy as np +from menpo.visualize import Renderer +# from menpo.shape import TriMesh +# from ..vtkutils import trimesh_to_vtk +from k3d import Plot, mesh as k3d_mesh, points as k3d_points +from io import BytesIO +# The colour map used for all lines and markers +GLOBAL_CMAP = 'jet' + + +def _parse_marker_size(marker_size, points): + if marker_size is None: + from menpo.shape import PointCloud + pc = PointCloud(points, copy=False) + # This is the way that mayavi automatically computes the scale factor + # in case the user passes scale_factor = 'auto'. We use it for both + # the marker_size as well as the numbers_size. + xyz_min, xyz_max = pc.bounds() + x_min, y_min, z_min = xyz_min + x_max, y_max, z_max = xyz_max + distance = np.sqrt(((x_max - x_min) ** 2 + + (y_max - y_min) ** 2 + + (z_max - z_min) ** 2) / + (4 * pc.n_points ** 0.33)) + if distance == 0: + marker_size = 1 + else: + marker_size = 0.1 * distance + return marker_size + + +def _parse_colour(colour): + from matplotlib.colors import rgb2hex + if isinstance(colour, int): + return colour + else: + return int(rgb2hex(colour)[1:], base=16) + + +def _check_colours_list(render_flag, colours_list, n_objects, error_str): + from menpo.visualize.viewmatplotlib import sample_colours_from_colourmap + if render_flag: + if colours_list is None: + # sample colours from jet colour map + colours_list = sample_colours_from_colourmap(n_objects, + GLOBAL_CMAP) + if isinstance(colours_list, list): + if len(colours_list) == 1: + colours_list[0] = _parse_colour(colours_list[0]) + colours_list *= n_objects + elif len(colours_list) != n_objects: + raise ValueError(error_str) + else: + colours_list = [_parse_colour(colours_list)] * n_objects + else: + colours_list = [None] * n_objects + return colours_list + + +# def _set_numbering(figure, centers, render_numbering=True, numbers_size=None, +# numbers_colour='k'): +# import mayavi.mlab as mlab +# numbers_colour = _parse_colour(numbers_colour) +# numbers_size = _parse_marker_size(numbers_size, centers) +# if render_numbering: +# for k, p in enumerate(centers): +# mlab.text3d(p[0], p[1], p[2], str(k), figure=figure, +# scale=numbers_size, orient_to_camera=True, +# color=numbers_colour, line_width=2) + +class K3dwidgetsRenderer(Plot, Renderer): + """ Abstract class for performing visualizations using K3dwidgets. + + Parameters + ---------- + figure_id : str or `None` + A figure name or `None`. + new_figure : bool + If `True`, creates a new figure on the cell. + """ + def __init__(self, figure_id, new_figure): + super(K3dwidgetsRenderer, self).__init__() + self.figure_id = figure_id + self.new_figure = new_figure + self.grid_visible = False +# self._supported_ext = ['png', 'jpg', 'bmp', 'tiff', # 2D +# 'ps', 'eps', 'pdf', # 2D +# 'rib', 'oogl', 'iv', 'vrml', 'obj'] # 3D +# n_ext = len(self._supported_ext) +# func_list = [lambda obj, fp, **kwargs: mlab.savefig(fp.name, **obj)] * n_ext +# self._extensions_map = dict(zip(['.' + s for s in self._supported_ext], +# func_list)) + # To store actors for clearing + + def get_figure(self): + r""" + Gets the figure specified by the combination of `self.figure_id` and + `self.new_figure`. If `self.figure_id == None` then `mlab.gcf()` + is used. `self.figure_id` is also set to the correct id of the figure + if a new figure is created. + + Returns + ------- + figure : Mayavi figure object + The figure we will be rendering on. + """ + # return self.figure + pass + +# def save_figure(self, filename, format='png', size=None, +# magnification='auto', overwrite=False): +# r""" +# Method for saving the figure of the current `figure_id` to file. +# +# Parameters +# ---------- +# filename : `str` or `file`-like object +# The string path or file-like object to save the figure at/into. +# format : `str` +# The format to use. This must match the file path if the file path is +# a `str`. +# size : `tuple` of `int` or ``None``, optional +# The size of the image created (unless magnification is set, +# in which case it is the size of the window used for rendering). If +# ``None``, then the figure size is used. +# magnification : `double` or ``'auto'``, optional +# The magnification is the scaling between the pixels on the screen, +# and the pixels in the file saved. If you do not specify it, it will +# be calculated so that the file is saved with the specified size. +# If you specify a magnification, Mayavi will use the given size as a +# screen size, and the file size will be ``magnification * size``. +# If ``'auto'``, then the magnification will be set automatically. +# overwrite : `bool`, optional +# If ``True``, the file will be overwritten if it already exists. +# """ +# from menpo.io.output.base import _export +# savefig_args = {'size': size, 'figure': self.figure, +# 'magnification': magnification} +# # Use the export code so that we have a consistent interface +# _export(savefig_args, filename, self._extensions_map, format, +# overwrite=overwrite) + + @property + def _width(self): + r""" + The width of the scene in pixels. An underscore has been added in the + begining of the name due to conflict with K3d Plot class + :type: `int` + """ + pass + + @property + def _height(self): + r""" + The height of the scene in pixels. An underscore has been added in the + begining of the name due to conflict with K3d Plot class + + :type: `int` + """ + pass + + @property + def modelview_matrix(self): + r""" + Retrieves the modelview matrix for this scene. + + :type: ``(4, 4)`` `ndarray` + """ + # camera = self.figure.scene.camera + # return camera.view_transform_matrix.to_array().astype(np.float32) + pass + + @property + def projection_matrix(self): + r""" + Retrieves the projection matrix for this scene. + + :type: ``(4, 4)`` `ndarray` + """ +# scene = self.figure.scene +# camera = scene.camera +# scene_size = tuple(scene.get_size()) +# aspect_ratio = float(scene_size[0]) / float(scene_size[1]) +# p = camera.get_projection_transform_matrix( +# aspect_ratio, -1, 1).to_array().astype(np.float32) +# return p + pass + + @property + def renderer_settings(self): + r""" + Returns all the information required to construct an identical + renderer to this one. + + Returns + ------- + settings : `dict` + The dictionary with the following keys: + + * ``'width'`` (`int`) : The width of the scene. + * ``'height'`` (`int`) : The height of the scene. + * ``'model_matrix'`` (`ndarray`) : The model array (identity). + * ``'view_matrix'`` (`ndarray`) : The view array. + * ``'projection_matrix'`` (`ndarray`) : The projection array. + + """ + return {'width': self.width, + 'height': self.height, + 'model_matrix': np.eye(4, dtype=np.float32), + 'view_matrix': self.modelview_matrix, + 'projection_matrix': self.projection_matrix} + + def force_draw(self): + r""" + Method for forcing the current figure to render. This is useful for + the widgets animation. + """ + self.render() + + +class K3dwidgetsVectorViewer3d(K3dwidgetsRenderer): + def __init__(self, figure_id, new_figure, points, vectors): + super(K3dwidgetsVectorViewer3d, self).__init__(figure_id, new_figure) + self.points = points + self.vectors = vectors + + def render(self, colour='r', line_width=2, marker_style='2darrow', + marker_resolution=8, marker_size=None, step=None, alpha=1.0): + marker_size = _parse_marker_size(marker_size, self.points) + colour = _parse_colour(colour) +# mlab.quiver3d(self.points[:, 0], self.points[:, 1], self.points[:, 2], +# self.vectors[:, 0], self.vectors[:, 1], self.vectors[:, 2], +# figure=self.figure, color=colour, mask_points=step, +# line_width=line_width, mode=marker_style, +# resolution=marker_resolution, opacity=alpha, +# scale_factor=marker_size) + return self + + +class K3dwidgetsPointGraphViewer3d(K3dwidgetsRenderer): + def __init__(self, figure_id, new_figure, points, edges): + super(K3dwidgetsPointGraphViewer3d, self).__init__(figure_id, + new_figure) + self.points = points.astype(np.float32) + self.edges = edges + + def _render(self, render_lines=True, line_colour='r', line_width=2, + render_markers=True, marker_style='flat', marker_size=10, + marker_colour='g', marker_resolution=8, step=None, alpha=1.0, + render_numbering=False, numbers_colour='k', numbers_size=None): + + # Render the lines if requested + # TODO + if render_lines: + line_colour = _parse_colour(line_colour) + # Render the markers if requested + if render_markers: + marker_size = _parse_marker_size(marker_size, self.points) + marker_colour = _parse_colour(marker_colour) + widg_to_draw = self + + if not self.new_figure: + for widg in self.widgets.values(): + if isinstance(widg, K3dwidgetsRenderer): + if widg.figure_id == self.figure_id and widg.model_id != self.model_id: + widg_to_draw = widg + break + + if marker_style == 'sphere': + marker_style = 'flat' + + default_camera = [-0.16031231203819687, + 0.09455110637470637, + 2.8537626738058663, + 0.00039440393447875977, + -0.15653744339942932, + 0.5779531598091125, + -0.02452392741576587, + 0.9981297233524523, + -0.05599671726525722] + + if widg_to_draw is self: + widg_to_draw.camera = default_camera + + points_to_add = k3d_points(self.points, color=marker_colour, + point_size=marker_size, + shader=marker_style) + widg_to_draw += points_to_add + + # set numbering +# _set_numbering(self.figure, self.points, numbers_size=numbers_size, +# render_numbering=render_numbering, +# numbers_colour=numbers_colour) +# + return widg_to_draw + + +class K3dwidgetsTriMeshViewer3d(K3dwidgetsRenderer): + def __init__(self, figure_id, new_figure, points, trilist, landmarks=None): + super(K3dwidgetsTriMeshViewer3d, self).__init__(figure_id, new_figure) + self.points = points + self.trilist = trilist + self.landmarks = landmarks + + def _render_mesh(self, mesh_type, line_width, colour, marker_size, + marker_resolution, marker_style, step, alpha): + marker_size = _parse_marker_size(marker_size, self.points) + colour = _parse_colour(colour) + + widg_to_draw = self + if not self.new_figure: + for widg in self.widgets.values(): + if isinstance(widg, K3dwidgetsRenderer): + if widg.figure_id == self.figure_id and widg.model_id != self.model_id: + widg_to_draw = widg + break + + mesh_to_add = k3d_mesh(self.points.astype(np.float32), + self.trilist.flatten().astype(np.uint32), + flat_shading=False, color=colour, side='double') + widg_to_draw += mesh_to_add + + if hasattr(self.landmarks, 'points'): + points_to_add = k3d_points(self.landmarks.points, color=0x00FF00, + point_size=marker_size, + shader='mesh') + widg_to_draw += points_to_add + + # TODO + # Why the following atributes don't change + self.camera = [-0.02, -0.12, 3.32, + 0.00, -0.16, 0.58, + 0.02, 1.00, 0.04] + + widg_to_draw.lighting = 0 + return widg_to_draw + + def _render(self, mesh_type='wireframe', line_width=2, colour='r', + marker_style='sphere', marker_size=None, marker_resolution=8, + normals=None, normals_colour='k', normals_line_width=2, + normals_marker_style='2darrow', normals_marker_size=None, + normals_marker_resolution=8, step=None, alpha=1.0): + + if normals is not None: + K3dwidgetsVectorViewer3d(self.figure_id, False, + self.points, normals).render( + colour=normals_colour, line_width=normals_line_width, step=step, + marker_style=normals_marker_style, + marker_resolution=normals_marker_resolution, + marker_size=normals_marker_size, alpha=alpha) + return self._render_mesh(mesh_type, line_width, colour, marker_size, + marker_resolution, marker_style, step, alpha) + + +class K3dwidgetsTexturedTriMeshViewer3d(K3dwidgetsRenderer): + def __init__(self, figure_id, new_figure, points, trilist, texture, + tcoords, landmarks): + super(K3dwidgetsTexturedTriMeshViewer3d, self).__init__(figure_id, + new_figure) + self.points = points + self.trilist = trilist + self.texture = texture + self.tcoords = tcoords + self.landmarks = landmarks + + def _render_mesh(self, mesh_type='surface', ambient_light=0.0, + specular_light=0.0, alpha=1.0): + + widg_to_draw = self + if not self.new_figure: + for widg in self.widgets.values(): + if isinstance(widg, K3dwidgetsRenderer): + if widg.figure_id == self.figure_id and widg.model_id != self.model_id: + widg_to_draw = widg + break + + uvs = self.tcoords.points + tmp_img = self.texture.mirror(axis=0).as_PILImage() + img_byte_arr = BytesIO() + tmp_img.save(img_byte_arr, format='PNG') + texture = img_byte_arr.getvalue() + texture_file_format = 'png' + + mesh_to_add = k3d_mesh(self.points.astype(np.float32), + self.trilist.flatten().astype(np.uint32), + flat_shading=False, + color=0xFFFFFF, side='front', texture=texture, + uvs=uvs, + texture_file_format=texture_file_format) + + widg_to_draw += mesh_to_add + + if hasattr(self.landmarks, 'points'): + marker_size = _parse_marker_size(None, self.points) + points_to_add = k3d_points(self.landmarks.points, color=0x00FF00, + point_size=marker_size, + shader='mesh') + widg_to_draw += points_to_add + + self.camera = [-0.02, -0.12, 3.32, + 0.00, -0.16, 0.58, + 0.02, 1.00, 0.04] + + return widg_to_draw + + def _render(self, mesh_type='surface', ambient_light=0.0, + specular_light=0.0, normals=None, normals_colour='k', + normals_line_width=2, normals_marker_style='2darrow', + normals_marker_resolution=8, normals_marker_size=None, + step=None, alpha=1.0): + + if normals is not None: + K3dwidgetsVectorViewer3d(self.figure_id, False, + self.points, normals).render( + colour=normals_colour, line_width=normals_line_width, step=step, + marker_style=normals_marker_style, + marker_resolution=normals_marker_resolution, + marker_size=normals_marker_size, alpha=alpha) + + self._render_mesh(mesh_type=mesh_type, ambient_light=ambient_light, + specular_light=specular_light, alpha=alpha) + return self + + +class K3dwidgetsColouredTriMeshViewer3d(K3dwidgetsRenderer): + def __init__(self, figure_id, new_figure, points, trilist, + colour_per_point): + super(K3dwidgetsColouredTriMeshViewer3d, self).__init__(figure_id, + new_figure) + self.points = points + self.trilist = trilist + self.colour_per_point = colour_per_point + self._actors = [] + + def _render_mesh(self, mesh_type='surface', ambient_light=0.0, + specular_light=0.0, alpha=1.0): + from tvtk.api import tvtk + pd = tvtk.PolyData() + pd.points = self.points + pd.polys = self.trilist + pd.point_data.scalars = (self.colour_per_point * 255.).astype(np.uint8) + mapper = tvtk.PolyDataMapper() + mapper.set_input_data(pd) + p = tvtk.Property(representation=mesh_type, opacity=alpha, + ambient=ambient_light, specular=specular_light) + actor = tvtk.Actor(mapper=mapper, property=p) + self.figure.scene.add_actors(actor) + self._actors.append(actor) + + def render(self, mesh_type='surface', ambient_light=0.0, specular_light=0.0, + normals=None, normals_colour='k', normals_line_width=2, + normals_marker_style='2darrow', normals_marker_resolution=8, + normals_marker_size=None, step=None, alpha=1.0): + if normals is not None: + K3dwidgetsVectorViewer3d(self.figure_id, False, + self.points, normals).render( + colour=normals_colour, line_width=normals_line_width, step=step, + marker_style=normals_marker_style, + marker_resolution=normals_marker_resolution, + marker_size=normals_marker_size, alpha=alpha) + self._render_mesh(mesh_type=mesh_type, ambient_light=ambient_light, + specular_light=specular_light, alpha=alpha) + return self + + +class K3dwidgetsSurfaceViewer3d(K3dwidgetsRenderer): + def __init__(self, figure_id, new_figure, values, mask=None): + super(K3dwidgetsSurfaceViewer3d, self).__init__(figure_id, new_figure) + if mask is not None: + values[~mask] = np.nan + self.values = values + + def render(self, colour=(1, 0, 0), line_width=2, step=None, + marker_style='2darrow', marker_resolution=8, marker_size=0.05, + alpha=1.0): + # warp_scale = kwargs.get('warp_scale', 'auto') + # mlab.surf(self.values, warp_scale=warp_scale) + return self + + +class K3dwidgetsLandmarkViewer3d(K3dwidgetsRenderer): + def __init__(self, figure_id, new_figure, group, landmark_group): + super(K3dwidgetsLandmarkViewer3d, self).__init__(figure_id, new_figure) + self.group = group + self.landmark_group = landmark_group + + def render(self, render_lines=True, line_colour='r', line_width=2, + render_markers=True, marker_style='sphere', marker_size=None, + marker_colour='r', marker_resolution=8, step=None, alpha=1.0, + render_numbering=False, numbers_colour='k', numbers_size=None): + # Regarding the labels colours, we may get passed either no colours (in + # which case we generate random colours) or a single colour to colour + # all the labels with + # TODO: All marker and line options could be defined as lists... + n_labels = self.landmark_group.n_labels + line_colour = _check_colours_list( + render_lines, line_colour, n_labels, + 'Must pass a list of line colours with length n_labels or a single ' + 'line colour for all labels.') + marker_colour = _check_colours_list( + render_markers, marker_colour, n_labels, + 'Must pass a list of marker colours with length n_labels or a ' + 'single marker face colour for all labels.') + marker_size = _parse_marker_size(marker_size, self.landmark_group.points) + numbers_size = _parse_marker_size(numbers_size, + self.landmark_group.points) + + # get pointcloud of each label + sub_pointclouds = self._build_sub_pointclouds() + + # for each pointcloud + # disabling the rendering greatly speeds up this for loop + self.figure.scene.disable_render = True + for i, (label, pc) in enumerate(sub_pointclouds): + # render pointcloud + pc.view(figure_id=self.figure_id, new_figure=False, + render_lines=render_lines, line_colour=line_colour[i], + line_width=line_width, render_markers=render_markers, + marker_style=marker_style, marker_size=marker_size, + marker_colour=marker_colour[i], + marker_resolution=marker_resolution, step=step, + alpha=alpha, render_numbering=render_numbering, + numbers_colour=numbers_colour, numbers_size=numbers_size) + self.figure.scene.disable_render = False + + return self + + def _build_sub_pointclouds(self): + return [(label, self.landmark_group.get_label(label)) + for label in self.landmark_group.labels] From 887424512424f3c38a92f78c256fc9803945622c Mon Sep 17 00:00:00 2001 From: Thanos Date: Fri, 11 Sep 2020 12:17:39 +0000 Subject: [PATCH 02/43] Add k3d dependency --- setup.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 1e8e5f2..fa862e2 100644 --- a/setup.py +++ b/setup.py @@ -100,7 +100,7 @@ def build_extension_from_pyx(pyx_path, extra_sources_paths=None): cythonize = no_cythonize warnings.warn( - "Unable to import Cython - attempting to build using the " + "Unabl "pre-compiled C++ files." ) @@ -111,7 +111,8 @@ def build_extension_from_pyx(pyx_path, extra_sources_paths=None): version, cmdclass = get_version_and_cmdclass("menpo3d") -install_requires = ["menpo>=0.9.0,<0.12.0", "mayavi>=4.7.0", "moderngl>=5.6.*,<6.0"] +install_requires = ["menpo>=0.9.0,<0.12.0", "mayavi>=4.7.0", + "moderngl>=5.6.*,<6.0", 'k3d<=2.9.2'] setup( name="menpo3d", From 82658d9318850386f0ce1f426fb0bd50fd4ba540 Mon Sep 17 00:00:00 2001 From: Thanos Date: Fri, 11 Sep 2020 21:28:22 +0000 Subject: [PATCH 03/43] Add Landmark Inline Viewer --- .../0_Introduction_to_K3d_Widgets.ipynb | 246 ++++++++++++++++++ menpo3d/visualize/__init__.py | 16 +- menpo3d/visualize/base.py | 13 +- menpo3d/visualize/viewk3dwidgets.py | 69 +++-- 4 files changed, 292 insertions(+), 52 deletions(-) create mode 100644 menpo3d/examples/0_Introduction_to_K3d_Widgets.ipynb diff --git a/menpo3d/examples/0_Introduction_to_K3d_Widgets.ipynb b/menpo3d/examples/0_Introduction_to_K3d_Widgets.ipynb new file mode 100644 index 0000000..c743e4b --- /dev/null +++ b/menpo3d/examples/0_Introduction_to_K3d_Widgets.ipynb @@ -0,0 +1,246 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "import menpo3d.io as m3io\n", + "from menpo.shape import PointCloud" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

Load the data (Mesh and Landmarks)

" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "mesh = m3io.import_mesh('../data/james.obj')" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "lms = m3io.import_landmark_file('../data/james.ljson')['LJSON']" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/athanasiosp/miniconda3/envs/my_menpo/lib/python3.5/site-packages/traittypes/traittypes.py:101: UserWarning: Given trait value dtype \"float64\" does not match required type \"float32\". A coerced copy has been created.\n", + " np.dtype(self.dtype).name))\n" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "cf7e2afc2d98478b9f4a001622a362e0", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "K3dwidgetsLandmarkViewer3d(antialias=3, axes=['x', 'y', 'z'], axes_helper=1.0, background_color=16777215, came…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "lms.view(inline=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

Show the mesh

" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "0bde20db4f22458b93c2ad4d45511043", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "K3dwidgetsTexturedTriMeshViewer3d(antialias=3, axes=['x', 'y', 'z'], axes_helper=1.0, background_color=1677721…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "mesh.view(inline=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

Show the mesh and landmarks

" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "f3c3263493f14257a1564943b1fcc96a", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "K3dwidgetsTexturedTriMeshViewer3d(antialias=3, axes=['x', 'y', 'z'], axes_helper=1.0, background_color=1677721…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "mesh.view(inline=True, figure_id='James')" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "lms_poincloud = PointCloud(lms.points)\n", + "lms_poincloud.view(inline=True, figure_id='James',new_figure=False)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

Show a mesh that has landmarks

" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "mesh.landmarks = lms" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/athanasiosp/miniconda3/envs/my_menpo/lib/python3.5/site-packages/traittypes/traittypes.py:101: UserWarning: Given trait value dtype \"float64\" does not match required type \"float32\". A coerced copy has been created.\n", + " np.dtype(self.dtype).name))\n" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "f4e859e218c948e0b521ac9d7038c7c4", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "K3dwidgetsLandmarkViewer3d(antialias=3, axes=['x', 'y', 'z'], axes_helper=1.0, background_color=16777215, came…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "mesh.landmarks.view(inline=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/athanasiosp/miniconda3/envs/my_menpo/lib/python3.5/site-packages/traittypes/traittypes.py:101: UserWarning: Given trait value dtype \"float64\" does not match required type \"float32\". A coerced copy has been created.\n", + " np.dtype(self.dtype).name))\n" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "7cb144c560464c8493d05e3ef8c25c6c", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "K3dwidgetsTexturedTriMeshViewer3d(antialias=3, axes=['x', 'y', 'z'], axes_helper=1.0, background_color=1677721…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "mesh.view(inline=True,figure_id='test')" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.5.6" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/menpo3d/visualize/__init__.py b/menpo3d/visualize/__init__.py index d771412..3a4e826 100644 --- a/menpo3d/visualize/__init__.py +++ b/menpo3d/visualize/__init__.py @@ -1,11 +1,5 @@ -from .base import ( - PointGraphViewer3d, - TriMeshViewer3d, - VectorViewer3d, - ColouredTriMeshViewer3d, - TexturedTriMeshViewer3d, - LandmarkViewer3d, - TriMeshInlineViewer3d, - PointGraphInlineViewer3d, - TexturedTriMeshInlineViewer3d, - +from .base import (PointGraphViewer3d, TriMeshViewer3d, VectorViewer3d, + ColouredTriMeshViewer3d, TexturedTriMeshViewer3d, + LandmarkViewer3d, TriMeshInlineViewer3d, + PointGraphInlineViewer3d, TexturedTriMeshInlineViewer3d, + LandmarkInlineViewer3d) diff --git a/menpo3d/visualize/base.py b/menpo3d/visualize/base.py index e273127..02ccd99 100644 --- a/menpo3d/visualize/base.py +++ b/menpo3d/visualize/base.py @@ -7,11 +7,13 @@ MayaviColouredTriMeshViewer3d, ) -from .viewk3dwidgets import ( - K3dwidgetsTriMeshViewer3d, - K3dwidgetsPointGraphViewer3d, - K3dwidgetsTexturedTriMeshViewer3d, -) +# from .viewitkwidgets import (ItkwidgetsTriMeshViewer3d, +# ItkwidgetsPointGraphViewer3d) + +from .viewk3dwidgets import (K3dwidgetsTriMeshViewer3d, + K3dwidgetsPointGraphViewer3d, + K3dwidgetsLandmarkViewer3d, + K3dwidgetsTexturedTriMeshViewer3d) PointGraphViewer3d = MayaviPointGraphViewer3d TriMeshViewer3d = MayaviTriMeshViewer3d @@ -22,4 +24,5 @@ TriMeshInlineViewer3d = K3dwidgetsTriMeshViewer3d TexturedTriMeshInlineViewer3d = K3dwidgetsTexturedTriMeshViewer3d +LandmarkInlineViewer3d = K3dwidgetsLandmarkViewer3d PointGraphInlineViewer3d = K3dwidgetsPointGraphViewer3d diff --git a/menpo3d/visualize/viewk3dwidgets.py b/menpo3d/visualize/viewk3dwidgets.py index 2438a76..531f51d 100644 --- a/menpo3d/visualize/viewk3dwidgets.py +++ b/menpo3d/visualize/viewk3dwidgets.py @@ -44,6 +44,8 @@ def _check_colours_list(render_flag, colours_list, n_objects, error_str): # sample colours from jet colour map colours_list = sample_colours_from_colourmap(n_objects, GLOBAL_CMAP) + colours_list = list(map(_parse_colour, colours_list)) + if isinstance(colours_list, list): if len(colours_list) == 1: colours_list[0] = _parse_colour(colours_list[0]) @@ -53,7 +55,7 @@ def _check_colours_list(render_flag, colours_list, n_objects, error_str): else: colours_list = [_parse_colour(colours_list)] * n_objects else: - colours_list = [None] * n_objects + colours_list = [0x00FF00] * n_objects return colours_list @@ -83,14 +85,6 @@ def __init__(self, figure_id, new_figure): self.figure_id = figure_id self.new_figure = new_figure self.grid_visible = False -# self._supported_ext = ['png', 'jpg', 'bmp', 'tiff', # 2D -# 'ps', 'eps', 'pdf', # 2D -# 'rib', 'oogl', 'iv', 'vrml', 'obj'] # 3D -# n_ext = len(self._supported_ext) -# func_list = [lambda obj, fp, **kwargs: mlab.savefig(fp.name, **obj)] * n_ext -# self._extensions_map = dict(zip(['.' + s for s in self._supported_ext], -# func_list)) - # To store actors for clearing def get_figure(self): r""" @@ -267,7 +261,7 @@ def _render(self, render_lines=True, line_colour='r', line_width=2, break if marker_style == 'sphere': - marker_style = 'flat' + marker_style = 'mesh' default_camera = [-0.16031231203819687, 0.09455110637470637, @@ -321,12 +315,14 @@ def _render_mesh(self, mesh_type, line_width, colour, marker_size, widg_to_draw += mesh_to_add if hasattr(self.landmarks, 'points'): - points_to_add = k3d_points(self.landmarks.points, color=0x00FF00, - point_size=marker_size, - shader='mesh') - widg_to_draw += points_to_add - - # TODO + self.landmarks.view(inline=True, new_figure=False, + figure_id=self.figure_id) +# points_to_add = k3d_points(self.landmarks.points, color=0x00FF00, +# point_size=marker_size, +# shader='mesh') +# widg_to_draw += points_to_add +# + # TODO # Why the following atributes don't change self.camera = [-0.02, -0.12, 3.32, 0.00, -0.16, 0.58, @@ -391,11 +387,8 @@ def _render_mesh(self, mesh_type='surface', ambient_light=0.0, widg_to_draw += mesh_to_add if hasattr(self.landmarks, 'points'): - marker_size = _parse_marker_size(None, self.points) - points_to_add = k3d_points(self.landmarks.points, color=0x00FF00, - point_size=marker_size, - shader='mesh') - widg_to_draw += points_to_add + self.landmarks.view(inline=True, new_figure=False, + figure_id=self.figure_id) self.camera = [-0.02, -0.12, 3.32, 0.00, -0.16, 0.58, @@ -484,7 +477,7 @@ def __init__(self, figure_id, new_figure, group, landmark_group): self.group = group self.landmark_group = landmark_group - def render(self, render_lines=True, line_colour='r', line_width=2, + def _render(self, render_lines=True, line_colour='r', line_width=2, render_markers=True, marker_style='sphere', marker_size=None, marker_colour='r', marker_resolution=8, step=None, alpha=1.0, render_numbering=False, numbers_colour='k', numbers_size=None): @@ -508,22 +501,26 @@ def render(self, render_lines=True, line_colour='r', line_width=2, # get pointcloud of each label sub_pointclouds = self._build_sub_pointclouds() - # for each pointcloud - # disabling the rendering greatly speeds up this for loop - self.figure.scene.disable_render = True + widg_to_draw = self + + if not self.new_figure: + for widg in self.widgets.values(): + if isinstance(widg, K3dwidgetsRenderer): + if widg.figure_id == self.figure_id and widg.model_id != self.model_id: + widg_to_draw = widg + break + + if marker_style == 'sphere': + marker_style = 'mesh' + for i, (label, pc) in enumerate(sub_pointclouds): - # render pointcloud - pc.view(figure_id=self.figure_id, new_figure=False, - render_lines=render_lines, line_colour=line_colour[i], - line_width=line_width, render_markers=render_markers, - marker_style=marker_style, marker_size=marker_size, - marker_colour=marker_colour[i], - marker_resolution=marker_resolution, step=step, - alpha=alpha, render_numbering=render_numbering, - numbers_colour=numbers_colour, numbers_size=numbers_size) - self.figure.scene.disable_render = False + # add pointcloud - return self + points_to_add = k3d_points(pc.points, color=marker_colour[i], + point_size=marker_size, + shader=marker_style) + widg_to_draw += points_to_add + return widg_to_draw def _build_sub_pointclouds(self): return [(label, self.landmark_group.get_label(label)) From fe2c79ccf371171921f85f3ca3a41e54a600bdb2 Mon Sep 17 00:00:00 2001 From: Thanos Date: Mon, 14 Sep 2020 12:07:24 +0000 Subject: [PATCH 04/43] - Add K3d inline viewer in LandmarkerViewer LabelledPointUndirectedGraph) - Add checks for figure_id - Automatic naming for nonename figure_id - Update introduction notebook --- .../0_Introduction_to_K3d_Widgets.ipynb | 195 +++++++++++++----- menpo3d/visualize/viewk3dwidgets.py | 37 ++++ 2 files changed, 175 insertions(+), 57 deletions(-) diff --git a/menpo3d/examples/0_Introduction_to_K3d_Widgets.ipynb b/menpo3d/examples/0_Introduction_to_K3d_Widgets.ipynb index c743e4b..6508646 100644 --- a/menpo3d/examples/0_Introduction_to_K3d_Widgets.ipynb +++ b/menpo3d/examples/0_Introduction_to_K3d_Widgets.ipynb @@ -2,9 +2,18 @@ "cells": [ { "cell_type": "code", - "execution_count": 2, + "execution_count": 1, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/athanasiosp/gits/menpo/menpo/image/base.py:32: UserWarning: Falling back to scipy interpolation for affine warps\n", + " warn(\"Falling back to scipy interpolation for affine warps\")\n" + ] + } + ], "source": [ "import menpo3d.io as m3io\n", "from menpo.shape import PointCloud" @@ -19,54 +28,14 @@ }, { "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "mesh = m3io.import_mesh('../data/james.obj')" - ] - }, - { - "cell_type": "code", - "execution_count": 4, + "execution_count": 2, "metadata": {}, "outputs": [], "source": [ + "mesh = m3io.import_mesh('../data/james.obj')\n", "lms = m3io.import_landmark_file('../data/james.ljson')['LJSON']" ] }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/home/athanasiosp/miniconda3/envs/my_menpo/lib/python3.5/site-packages/traittypes/traittypes.py:101: UserWarning: Given trait value dtype \"float64\" does not match required type \"float32\". A coerced copy has been created.\n", - " np.dtype(self.dtype).name))\n" - ] - }, - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "cf7e2afc2d98478b9f4a001622a362e0", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "K3dwidgetsLandmarkViewer3d(antialias=3, axes=['x', 'y', 'z'], axes_helper=1.0, background_color=16777215, came…" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "lms.view(inline=True)" - ] - }, { "cell_type": "markdown", "metadata": {}, @@ -76,13 +45,13 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "0bde20db4f22458b93c2ad4d45511043", + "model_id": "f792ef7ea0d34740a3be9a2104447d53", "version_major": 2, "version_minor": 0 }, @@ -95,7 +64,14 @@ } ], "source": [ - "mesh.view(inline=True)" + "# Default values for TriMesh, TextureMesh viewer are\n", + "# figure_id None\n", + "# new_figure True\n", + "# in that case an automatic figure_id will be given\n", + "# with 'Figure_{n}' format\n", + "# n will be an increased integer starting from zero\n", + "\n", + "mesh.view(inline=True,) #wait a bit before magic happens" ] }, { @@ -107,7 +83,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 4, "metadata": { "scrolled": true }, @@ -115,7 +91,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "f3c3263493f14257a1564943b1fcc96a", + "model_id": "073f224f65ea4f61b07e50c5a73f8486", "version_major": 2, "version_minor": 0 }, @@ -133,14 +109,25 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 5, "metadata": {}, "outputs": [], "source": [ + "# Add landmarks to figure with id James\n", "lms_poincloud = PointCloud(lms.points)\n", "lms_poincloud.view(inline=True, figure_id='James',new_figure=False)" ] }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "# Add landmarks to figure with id Figure_0\n", + "lms_poincloud.view(inline=True, figure_id='Figure_0', new_figure=False)" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -150,7 +137,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 7, "metadata": {}, "outputs": [], "source": [ @@ -159,7 +146,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 8, "metadata": {}, "outputs": [ { @@ -173,7 +160,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "f4e859e218c948e0b521ac9d7038c7c4", + "model_id": "85e40b3b4889470bb8551e6a9800bb17", "version_major": 2, "version_minor": 0 }, @@ -186,12 +173,15 @@ } ], "source": [ - "mesh.landmarks.view(inline=True)" + "# the new figure will have figure_is Figure_1\n", + "# Note the difference between plotting a pointcloud\n", + "# and landmarks \n", + "mesh.landmarks.view(inline=True, new_figure=True)" ] }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 9, "metadata": {}, "outputs": [ { @@ -205,7 +195,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "7cb144c560464c8493d05e3ef8c25c6c", + "model_id": "9eb4fc665afa46088aef86383b1f787b", "version_major": 2, "version_minor": 0 }, @@ -218,7 +208,98 @@ } ], "source": [ - "mesh.view(inline=True,figure_id='test')" + "# The mesh has now landmarks, so they would be plotted as well\n", + "# the figure id is now Figure_2\n", + "mesh.view(inline=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

Fail cases

" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "ename": "ValueError", + "evalue": "You cannot plot a figure with no id and new figure False", + "output_type": "error", + "traceback": [ + "\u001b[0;31m--------------------------------------------\u001b[0m", + "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[0;31m# figure_id = None and new_figure=False, so it could not\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[0;31m# find a figure with id None\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 4\u001b[0;31m \u001b[0mlms\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mview\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0minline\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mTrue\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m/home/athanasiosp/gits/menpo/menpo/shape/labelled.py\u001b[0m in \u001b[0;36m_view_3d\u001b[0;34m(self, with_labels, without_labels, group, figure_id, new_figure, render_lines, line_colour, line_width, render_markers, marker_style, marker_size, marker_colour, marker_resolution, step, alpha, render_numbering, numbers_colour, numbers_size, inline)\u001b[0m\n\u001b[1;32m 856\u001b[0m \u001b[0mlmark_group\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m \u001b[0;31m# Fall through\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 857\u001b[0m landmark_viewer = LandmarkInlineViewer3d(\n\u001b[0;32m--> 858\u001b[0;31m \u001b[0mfigure_id\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnew_figure\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mgroup\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mlmark_group\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 859\u001b[0m )\n\u001b[1;32m 860\u001b[0m return landmark_viewer._render(\n", + "\u001b[0;32m/home/athanasiosp/gits/menpo3d/menpo3d/visualize/viewk3dwidgets.py\u001b[0m in \u001b[0;36m__init__\u001b[0;34m(self, figure_id, new_figure, group, landmark_group)\u001b[0m\n\u001b[1;32m 511\u001b[0m \u001b[0;32mclass\u001b[0m \u001b[0mK3dwidgetsLandmarkViewer3d\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mK3dwidgetsRenderer\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 512\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m__init__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mfigure_id\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnew_figure\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mgroup\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mlandmark_group\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 513\u001b[0;31m \u001b[0msuper\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mK3dwidgetsLandmarkViewer3d\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__init__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfigure_id\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnew_figure\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 514\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mgroup\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mgroup\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 515\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mlandmark_group\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mlandmark_group\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/home/athanasiosp/gits/menpo3d/menpo3d/visualize/viewk3dwidgets.py\u001b[0m in \u001b[0;36m__init__\u001b[0;34m(self, figure_id, new_figure)\u001b[0m\n\u001b[1;32m 103\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 104\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mremove_widget\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 105\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'You cannot plot a figure with no id and new figure False'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 106\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 107\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mnew_figure\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mValueError\u001b[0m: You cannot plot a figure with no id and new figure False" + ] + } + ], + "source": [ + "# It should fail, as default values for landmarker viewer are\n", + "# figure_id = None and new_figure=False, so it could not\n", + "# find a figure with id None\n", + "lms.view(inline=True,)" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "ename": "ValueError", + "evalue": "Figure id is already given", + "output_type": "error", + "traceback": [ + "\u001b[0;31m--------------------------------------------\u001b[0m", + "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0;31m# It should fail, as we have already had a figure with id\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[0;31m# James and we cannot create a new one with the same figure_id\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 3\u001b[0;31m \u001b[0mmesh\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mview\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0minline\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mTrue\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mfigure_id\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m'James'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnew_figure\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mTrue\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m/home/athanasiosp/gits/menpo/menpo/shape/mesh/textured.py\u001b[0m in \u001b[0;36m_view_3d\u001b[0;34m(self, figure_id, new_figure, render_texture, mesh_type, ambient_light, specular_light, colour, line_width, normals, normals_colour, normals_line_width, normals_marker_style, normals_marker_resolution, normals_marker_size, step, alpha, inline)\u001b[0m\n\u001b[1;32m 383\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtexture\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 384\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtcoords\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 385\u001b[0;31m self.landmarks)\n\u001b[0m\u001b[1;32m 386\u001b[0m render_return = renderer._render(\n\u001b[1;32m 387\u001b[0m \u001b[0mmesh_type\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mmesh_type\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/home/athanasiosp/gits/menpo3d/menpo3d/visualize/viewk3dwidgets.py\u001b[0m in \u001b[0;36m__init__\u001b[0;34m(self, figure_id, new_figure, points, trilist, texture, tcoords, landmarks)\u001b[0m\n\u001b[1;32m 389\u001b[0m tcoords, landmarks):\n\u001b[1;32m 390\u001b[0m super(K3dwidgetsTexturedTriMeshViewer3d, self).__init__(figure_id,\n\u001b[0;32m--> 391\u001b[0;31m new_figure)\n\u001b[0m\u001b[1;32m 392\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mpoints\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mpoints\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 393\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtrilist\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtrilist\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/home/athanasiosp/gits/menpo3d/menpo3d/visualize/viewk3dwidgets.py\u001b[0m in \u001b[0;36m__init__\u001b[0;34m(self, figure_id, new_figure)\u001b[0m\n\u001b[1;32m 110\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mx\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfigure_id\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0mfigure_id\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 111\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mremove_widget\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 112\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'Figure id is already given'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 113\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 114\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfigure_id\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mfigure_id\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mValueError\u001b[0m: Figure id is already given" + ] + } + ], + "source": [ + "# It should fail, as we have already had a figure with id \n", + "# James and we cannot create a new one with the same figure_id\n", + "mesh.view(inline=True, figure_id='James', new_figure=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

Testing

" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from ipywidgets import Widget\n", + "from menpo3d.visualize.viewk3dwidgets import K3dwidgetsRenderer" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "for x in Widget.widgets.values():\n", + " #print(type(x),x.model_id)\n", + " if isinstance(x, K3dwidgetsRenderer):\n", + " print(type(x),x.model_id, x.figure_id)" ] } ], diff --git a/menpo3d/visualize/viewk3dwidgets.py b/menpo3d/visualize/viewk3dwidgets.py index 531f51d..3d51d82 100644 --- a/menpo3d/visualize/viewk3dwidgets.py +++ b/menpo3d/visualize/viewk3dwidgets.py @@ -82,10 +82,47 @@ class K3dwidgetsRenderer(Plot, Renderer): """ def __init__(self, figure_id, new_figure): super(K3dwidgetsRenderer, self).__init__() + if figure_id is None: + if new_figure: + # A new figure is created but with no figure_id + # we should create an id of 'Figure_n form' + list_ids = [] + for x in self.widgets.values(): + if isinstance(x, K3dwidgetsRenderer) and x is not self: + if x.figure_id is not None and 'Figure_' in str(x.figure_id): + try: + n_figure_id = int(x.figure_id.split('Figure_')[1]) + except ValueError: + continue + list_ids.append(n_figure_id) + if len(list_ids): + figure_id = 'Figure_{}'.format(sorted(list_ids)[-1] + 1) + else: + figure_id = 'Figure_0' + + else: + self.remove_widget() + raise ValueError('You cannot plot a figure with no id and new figure False') + else: + if new_figure: + for x in self.widgets.values(): + if isinstance(x, K3dwidgetsRenderer) and x is not self: + if x.figure_id == figure_id: + self.remove_widget() + raise ValueError('Figure id is already given') + self.figure_id = figure_id self.new_figure = new_figure self.grid_visible = False + def remove_widget(self): + super(K3dwidgetsRenderer, self).close() + # copy from close from ipywidgets.widget.Widget + self.widgets.pop(self.model_id, None) + self.comm.close() + self.comm = None + self._repr_mimebundle_ = None + def get_figure(self): r""" Gets the figure specified by the combination of `self.figure_id` and From 8d20422071dcd36ed89ea6132d0d914a42fd7058 Mon Sep 17 00:00:00 2001 From: Thanos Date: Wed, 16 Sep 2020 10:40:49 +0000 Subject: [PATCH 05/43] Add view method for 3D Shape Model - Use K3D and LinearModelParametersWidget from menpowidget - Support of landmarks in shape model --- menpo3d/visualize/__init__.py | 2 +- menpo3d/visualize/base.py | 4 +- menpo3d/visualize/viewk3dwidgets.py | 127 +++++++++++++++++++++------- 3 files changed, 102 insertions(+), 31 deletions(-) diff --git a/menpo3d/visualize/__init__.py b/menpo3d/visualize/__init__.py index 3a4e826..5e4249f 100644 --- a/menpo3d/visualize/__init__.py +++ b/menpo3d/visualize/__init__.py @@ -2,4 +2,4 @@ ColouredTriMeshViewer3d, TexturedTriMeshViewer3d, LandmarkViewer3d, TriMeshInlineViewer3d, PointGraphInlineViewer3d, TexturedTriMeshInlineViewer3d, - LandmarkInlineViewer3d) + LandmarkInlineViewer3d, PCAModelInlineViewer3d) diff --git a/menpo3d/visualize/base.py b/menpo3d/visualize/base.py index 02ccd99..55bd705 100644 --- a/menpo3d/visualize/base.py +++ b/menpo3d/visualize/base.py @@ -13,7 +13,8 @@ from .viewk3dwidgets import (K3dwidgetsTriMeshViewer3d, K3dwidgetsPointGraphViewer3d, K3dwidgetsLandmarkViewer3d, - K3dwidgetsTexturedTriMeshViewer3d) + K3dwidgetsTexturedTriMeshViewer3d, + K3dwidgetsPCAModelViewer3d) PointGraphViewer3d = MayaviPointGraphViewer3d TriMeshViewer3d = MayaviTriMeshViewer3d @@ -26,3 +27,4 @@ TexturedTriMeshInlineViewer3d = K3dwidgetsTexturedTriMeshViewer3d LandmarkInlineViewer3d = K3dwidgetsLandmarkViewer3d PointGraphInlineViewer3d = K3dwidgetsPointGraphViewer3d +PCAModelInlineViewer3d = K3dwidgetsPCAModelViewer3d diff --git a/menpo3d/visualize/viewk3dwidgets.py b/menpo3d/visualize/viewk3dwidgets.py index 3d51d82..786c91b 100644 --- a/menpo3d/visualize/viewk3dwidgets.py +++ b/menpo3d/visualize/viewk3dwidgets.py @@ -4,6 +4,8 @@ # from ..vtkutils import trimesh_to_vtk from k3d import Plot, mesh as k3d_mesh, points as k3d_points from io import BytesIO +from ipywidgets import GridBox, Layout +from menpowidgets.options import LinearModelParametersWidget # The colour map used for all lines and markers GLOBAL_CMAP = 'jet' @@ -59,6 +61,36 @@ def _check_colours_list(render_flag, colours_list, n_objects, error_str): return colours_list +def _check_figure_id(obj, figure_id, new_figure): + if figure_id is None: + if new_figure: + # A new figure is created but with no figure_id + # we should create an id of 'Figure_n form' + list_ids = [] + for x in obj.widgets.values(): + if hasattr(x, 'figure_id') and x is not obj: + if x.figure_id is not None and 'Figure_' in str(x.figure_id): + try: + n_figure_id = int(x.figure_id.split('Figure_')[1]) + except ValueError: + continue + list_ids.append(n_figure_id) + if len(list_ids): + figure_id = 'Figure_{}'.format(sorted(list_ids)[-1] + 1) + else: + figure_id = 'Figure_0' + + else: + obj.remove_widget() + raise ValueError('You cannot plot a figure with no id and new figure False') + else: + if new_figure: + for x in obj.widgets.values(): + if hasattr(x, 'figure_id') and x is not obj: + if x.figure_id == figure_id: + obj.remove_widget() + raise ValueError('Figure id is already given') + return figure_id # def _set_numbering(figure, centers, render_numbering=True, numbers_size=None, # numbers_colour='k'): # import mayavi.mlab as mlab @@ -70,6 +102,7 @@ def _check_colours_list(render_flag, colours_list, n_objects, error_str): # scale=numbers_size, orient_to_camera=True, # color=numbers_colour, line_width=2) + class K3dwidgetsRenderer(Plot, Renderer): """ Abstract class for performing visualizations using K3dwidgets. @@ -82,36 +115,8 @@ class K3dwidgetsRenderer(Plot, Renderer): """ def __init__(self, figure_id, new_figure): super(K3dwidgetsRenderer, self).__init__() - if figure_id is None: - if new_figure: - # A new figure is created but with no figure_id - # we should create an id of 'Figure_n form' - list_ids = [] - for x in self.widgets.values(): - if isinstance(x, K3dwidgetsRenderer) and x is not self: - if x.figure_id is not None and 'Figure_' in str(x.figure_id): - try: - n_figure_id = int(x.figure_id.split('Figure_')[1]) - except ValueError: - continue - list_ids.append(n_figure_id) - if len(list_ids): - figure_id = 'Figure_{}'.format(sorted(list_ids)[-1] + 1) - else: - figure_id = 'Figure_0' - else: - self.remove_widget() - raise ValueError('You cannot plot a figure with no id and new figure False') - else: - if new_figure: - for x in self.widgets.values(): - if isinstance(x, K3dwidgetsRenderer) and x is not self: - if x.figure_id == figure_id: - self.remove_widget() - raise ValueError('Figure id is already given') - - self.figure_id = figure_id + self.figure_id = _check_figure_id(self, figure_id, new_figure) self.new_figure = new_figure self.grid_visible = False @@ -562,3 +567,67 @@ def _render(self, render_lines=True, line_colour='r', line_width=2, def _build_sub_pointclouds(self): return [(label, self.landmark_group.get_label(label)) for label in self.landmark_group.labels] + + +class K3dwidgetsPCAModelViewer3d(GridBox):#, K3dwidgetsRenderer): + def __init__(self, figure_id, new_figure, points, trilist, + components, eigenvalues, n_parameters, parameters_bound, + landmarks_indices, widget_style): + + #self.figure_id = figure_id #self.check_figure_id(figure_id, new_figure) + self.figure_id = _check_figure_id(self, figure_id, new_figure) + self.new_figure = new_figure + self.points = points + self.trilist = trilist + self.components = components + self.eigenvalues = eigenvalues + self.n_parameters = n_parameters + self.landmarks_indices = landmarks_indices + self.layout = Layout(grid_template_columns='1fr 1fr') + self.wid = LinearModelParametersWidget(n_parameters=n_parameters, + render_function=self.render_function, + params_str='Parameter ', + mode='multiple', + params_bounds=parameters_bound, + plot_variance_visible=False, + style=widget_style) + self.mesh_window = K3dwidgetsTriMeshViewer3d(self.figure_id, False, + self.points, self.trilist) + super(K3dwidgetsPCAModelViewer3d, self).__init__(children=[self.wid, self.mesh_window], + layout=Layout(grid_template_columns='1fr 1fr')) + + def _render_mesh(self, mesh_type, line_width, colour, marker_size, + marker_resolution, marker_style, step, alpha): + marker_size = _parse_marker_size(marker_size, self.points) + colour = _parse_colour(colour) + + mesh_to_add = k3d_mesh(self.points.astype(np.float32), + self.trilist.flatten().astype(np.uint32), + flat_shading=False, color=colour, + name='Instance', side='double') + + self.mesh_window += mesh_to_add + + if self.landmarks_indices is not None: + landmarks_to_add = k3d_points(self.points[self.landmarks_indices].astype(np.float32), + color=0x00FF00, name='landmarks', + point_size=marker_size, shader='mesh') + self.mesh_window += landmarks_to_add + return self + + def render_function(self, change): + mesh = self.points + (self.components[:self.n_parameters, :].T@(self.wid.selected_values*self.eigenvalues[:self.n_parameters]**0.5)).reshape(-1,3) + self.mesh_window.objects[0].vertices = mesh + if self.landmarks_indices is not None: + self.mesh_window.objects[1].positions = mesh[self.landmarks_indices] + + def _render(self, mesh_type='wireframe', line_width=2, colour='r', + marker_style='sphere', marker_size=None, marker_resolution=8, + normals=None, normals_colour='k', normals_line_width=2, + normals_marker_resolution=8, step=None, alpha=1.0): + + return self._render_mesh(mesh_type, line_width, colour, marker_size, + marker_resolution, marker_style, step, alpha) + + def remove_widget(self): + super(K3dwidgetsPCAModelViewer3d, self).close() From 0798abbe8a845a3b25914a4f06500cc297ffeb52 Mon Sep 17 00:00:00 2001 From: Thanos Date: Wed, 16 Sep 2020 10:48:11 +0000 Subject: [PATCH 06/43] Update Introduction notebook, move outside of build package --- examples/0_Introduction_to_K3d_Widgets.ipynb | 269 ++++++++++++++ .../0_Introduction_to_K3d_Widgets.ipynb | 327 ------------------ 2 files changed, 269 insertions(+), 327 deletions(-) create mode 100644 examples/0_Introduction_to_K3d_Widgets.ipynb delete mode 100644 menpo3d/examples/0_Introduction_to_K3d_Widgets.ipynb diff --git a/examples/0_Introduction_to_K3d_Widgets.ipynb b/examples/0_Introduction_to_K3d_Widgets.ipynb new file mode 100644 index 0000000..1157604 --- /dev/null +++ b/examples/0_Introduction_to_K3d_Widgets.ipynb @@ -0,0 +1,269 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import menpo3d.io as m3io\n", + "import menpo.io as mio\n", + "from menpo.shape import PointCloud" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

Load the data (Mesh and Landmarks)

" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "mesh = m3io.import_mesh('../data/james.obj')\n", + "lms = m3io.import_landmark_file('../data/james.ljson')['LJSON']" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

Show the mesh

" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Default values for TriMesh, TextureMesh viewer are\n", + "# figure_id None\n", + "# new_figure True\n", + "# in that case an automatic figure_id will be given\n", + "# with 'Figure_{n}' format\n", + "# n will be an increased integer starting from zero\n", + "\n", + "mesh.view(inline=True,) #wait a bit before magic happens" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

Show the mesh and landmarks

" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "scrolled": false + }, + "outputs": [], + "source": [ + "mesh.view(inline=True, figure_id='James')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Add landmarks to figure with id James\n", + "lms_poincloud = PointCloud(lms.points)\n", + "lms_poincloud.view(inline=True, figure_id='James',new_figure=False)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Add landmarks to figure with id Figure_0\n", + "lms_poincloud.view(inline=True, figure_id='Figure_0', new_figure=False)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

Show a mesh that has landmarks

" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "mesh.landmarks = lms" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# the new figure will have figure_id Figure_1\n", + "# Note the difference between plotting a pointcloud\n", + "# and landmarks \n", + "mesh.landmarks.view(inline=True, new_figure=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# The mesh has now landmarks, so they would be plotted as well\n", + "# the figure id is now Figure_2\n", + "mesh.view(inline=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

Load a Morphable Model

" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Load model and its landmarks indices \n", + "model = mio.import_pickle('/data/models/3DMD_all_all_all_158.pkl')\n", + "lms_indices = [21868, 22404, 22298, 22327, 43430, 45175, 46312, 47132, 47911, 48692,\n", + " 49737, 51376, 53136, 32516, 32616, 32205, 32701, 38910, 39396, 39693,\n", + " 39934, 40131, 40843, 41006, 41179, 41430, 13399, 8161, 8172, 8179, 8185,\n", + " 5622, 6881, 8202, 9403, 10764, 1831, 3887, 5049, 6214, 4805, 3643, 9955,\n", + " 11095, 12255, 14197, 12397, 11366, 5779, 6024, 7014, 8215, 9294, 10267,\n", + " 10922, 9556, 8836, 8236, 7636, 6794, 5905, 7264, 8223, 9063, 10404, 8828,\n", + " 8228, 7509]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "scrolled": false + }, + "outputs": [], + "source": [ + "model.view(inline=True, figure_id='Model', n_parameters=10, landmarks_indices=lms_indices)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "model.view(widget_style='')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

Fail cases

" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# It should fail if the previous cells have been executed, as default values for landmarker viewer are\n", + "# figure_id = None and new_figure=False, so it could not\n", + "# find a figure with id None\n", + "lms.view(inline=True,)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# It should fail if the previous cells have been executed, as we have already had a figure with id \n", + "# James and we cannot create a new one with the same figure_id\n", + "mesh.view(inline=True, figure_id='James', new_figure=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# It should fail if the previous cells have been executed, as we have already had a figure with id \n", + "# Model and we cannot create a new one with the same figure_id\n", + "model.view(inline=True, figure_id='Model')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

Testing

" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from ipywidgets import Widget\n", + "from menpo3d.visualize.viewk3dwidgets import K3dwidgetsRenderer" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "for x in Widget.widgets.values():\n", + " # print(type(x),x.model_id)\n", + " #if isinstance(x, K3dwidgetsRenderer):\n", + " if hasattr(x,'figure_id'):\n", + " print(type(x),x.model_id, x.figure_id)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.5.6" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/menpo3d/examples/0_Introduction_to_K3d_Widgets.ipynb b/menpo3d/examples/0_Introduction_to_K3d_Widgets.ipynb deleted file mode 100644 index 6508646..0000000 --- a/menpo3d/examples/0_Introduction_to_K3d_Widgets.ipynb +++ /dev/null @@ -1,327 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/home/athanasiosp/gits/menpo/menpo/image/base.py:32: UserWarning: Falling back to scipy interpolation for affine warps\n", - " warn(\"Falling back to scipy interpolation for affine warps\")\n" - ] - } - ], - "source": [ - "import menpo3d.io as m3io\n", - "from menpo.shape import PointCloud" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "

Load the data (Mesh and Landmarks)

" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "mesh = m3io.import_mesh('../data/james.obj')\n", - "lms = m3io.import_landmark_file('../data/james.ljson')['LJSON']" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "

Show the mesh

" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "f792ef7ea0d34740a3be9a2104447d53", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "K3dwidgetsTexturedTriMeshViewer3d(antialias=3, axes=['x', 'y', 'z'], axes_helper=1.0, background_color=1677721…" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# Default values for TriMesh, TextureMesh viewer are\n", - "# figure_id None\n", - "# new_figure True\n", - "# in that case an automatic figure_id will be given\n", - "# with 'Figure_{n}' format\n", - "# n will be an increased integer starting from zero\n", - "\n", - "mesh.view(inline=True,) #wait a bit before magic happens" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "

Show the mesh and landmarks

" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": { - "scrolled": true - }, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "073f224f65ea4f61b07e50c5a73f8486", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "K3dwidgetsTexturedTriMeshViewer3d(antialias=3, axes=['x', 'y', 'z'], axes_helper=1.0, background_color=1677721…" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "mesh.view(inline=True, figure_id='James')" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [], - "source": [ - "# Add landmarks to figure with id James\n", - "lms_poincloud = PointCloud(lms.points)\n", - "lms_poincloud.view(inline=True, figure_id='James',new_figure=False)" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [], - "source": [ - "# Add landmarks to figure with id Figure_0\n", - "lms_poincloud.view(inline=True, figure_id='Figure_0', new_figure=False)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "

Show a mesh that has landmarks

" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [], - "source": [ - "mesh.landmarks = lms" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/home/athanasiosp/miniconda3/envs/my_menpo/lib/python3.5/site-packages/traittypes/traittypes.py:101: UserWarning: Given trait value dtype \"float64\" does not match required type \"float32\". A coerced copy has been created.\n", - " np.dtype(self.dtype).name))\n" - ] - }, - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "85e40b3b4889470bb8551e6a9800bb17", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "K3dwidgetsLandmarkViewer3d(antialias=3, axes=['x', 'y', 'z'], axes_helper=1.0, background_color=16777215, came…" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# the new figure will have figure_is Figure_1\n", - "# Note the difference between plotting a pointcloud\n", - "# and landmarks \n", - "mesh.landmarks.view(inline=True, new_figure=True)" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/home/athanasiosp/miniconda3/envs/my_menpo/lib/python3.5/site-packages/traittypes/traittypes.py:101: UserWarning: Given trait value dtype \"float64\" does not match required type \"float32\". A coerced copy has been created.\n", - " np.dtype(self.dtype).name))\n" - ] - }, - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "9eb4fc665afa46088aef86383b1f787b", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "K3dwidgetsTexturedTriMeshViewer3d(antialias=3, axes=['x', 'y', 'z'], axes_helper=1.0, background_color=1677721…" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# The mesh has now landmarks, so they would be plotted as well\n", - "# the figure id is now Figure_2\n", - "mesh.view(inline=True)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "

Fail cases

" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [ - { - "ename": "ValueError", - "evalue": "You cannot plot a figure with no id and new figure False", - "output_type": "error", - "traceback": [ - "\u001b[0;31m--------------------------------------------\u001b[0m", - "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[0;31m# figure_id = None and new_figure=False, so it could not\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[0;31m# find a figure with id None\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 4\u001b[0;31m \u001b[0mlms\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mview\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0minline\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mTrue\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", - "\u001b[0;32m/home/athanasiosp/gits/menpo/menpo/shape/labelled.py\u001b[0m in \u001b[0;36m_view_3d\u001b[0;34m(self, with_labels, without_labels, group, figure_id, new_figure, render_lines, line_colour, line_width, render_markers, marker_style, marker_size, marker_colour, marker_resolution, step, alpha, render_numbering, numbers_colour, numbers_size, inline)\u001b[0m\n\u001b[1;32m 856\u001b[0m \u001b[0mlmark_group\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m \u001b[0;31m# Fall through\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 857\u001b[0m landmark_viewer = LandmarkInlineViewer3d(\n\u001b[0;32m--> 858\u001b[0;31m \u001b[0mfigure_id\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnew_figure\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mgroup\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mlmark_group\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 859\u001b[0m )\n\u001b[1;32m 860\u001b[0m return landmark_viewer._render(\n", - "\u001b[0;32m/home/athanasiosp/gits/menpo3d/menpo3d/visualize/viewk3dwidgets.py\u001b[0m in \u001b[0;36m__init__\u001b[0;34m(self, figure_id, new_figure, group, landmark_group)\u001b[0m\n\u001b[1;32m 511\u001b[0m \u001b[0;32mclass\u001b[0m \u001b[0mK3dwidgetsLandmarkViewer3d\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mK3dwidgetsRenderer\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 512\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m__init__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mfigure_id\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnew_figure\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mgroup\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mlandmark_group\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 513\u001b[0;31m \u001b[0msuper\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mK3dwidgetsLandmarkViewer3d\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__init__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfigure_id\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnew_figure\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 514\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mgroup\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mgroup\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 515\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mlandmark_group\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mlandmark_group\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/home/athanasiosp/gits/menpo3d/menpo3d/visualize/viewk3dwidgets.py\u001b[0m in \u001b[0;36m__init__\u001b[0;34m(self, figure_id, new_figure)\u001b[0m\n\u001b[1;32m 103\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 104\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mremove_widget\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 105\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'You cannot plot a figure with no id and new figure False'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 106\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 107\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mnew_figure\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;31mValueError\u001b[0m: You cannot plot a figure with no id and new figure False" - ] - } - ], - "source": [ - "# It should fail, as default values for landmarker viewer are\n", - "# figure_id = None and new_figure=False, so it could not\n", - "# find a figure with id None\n", - "lms.view(inline=True,)" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "ename": "ValueError", - "evalue": "Figure id is already given", - "output_type": "error", - "traceback": [ - "\u001b[0;31m--------------------------------------------\u001b[0m", - "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0;31m# It should fail, as we have already had a figure with id\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[0;31m# James and we cannot create a new one with the same figure_id\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 3\u001b[0;31m \u001b[0mmesh\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mview\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0minline\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mTrue\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mfigure_id\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m'James'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnew_figure\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mTrue\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", - "\u001b[0;32m/home/athanasiosp/gits/menpo/menpo/shape/mesh/textured.py\u001b[0m in \u001b[0;36m_view_3d\u001b[0;34m(self, figure_id, new_figure, render_texture, mesh_type, ambient_light, specular_light, colour, line_width, normals, normals_colour, normals_line_width, normals_marker_style, normals_marker_resolution, normals_marker_size, step, alpha, inline)\u001b[0m\n\u001b[1;32m 383\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtexture\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 384\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtcoords\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 385\u001b[0;31m self.landmarks)\n\u001b[0m\u001b[1;32m 386\u001b[0m render_return = renderer._render(\n\u001b[1;32m 387\u001b[0m \u001b[0mmesh_type\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mmesh_type\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/home/athanasiosp/gits/menpo3d/menpo3d/visualize/viewk3dwidgets.py\u001b[0m in \u001b[0;36m__init__\u001b[0;34m(self, figure_id, new_figure, points, trilist, texture, tcoords, landmarks)\u001b[0m\n\u001b[1;32m 389\u001b[0m tcoords, landmarks):\n\u001b[1;32m 390\u001b[0m super(K3dwidgetsTexturedTriMeshViewer3d, self).__init__(figure_id,\n\u001b[0;32m--> 391\u001b[0;31m new_figure)\n\u001b[0m\u001b[1;32m 392\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mpoints\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mpoints\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 393\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtrilist\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtrilist\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/home/athanasiosp/gits/menpo3d/menpo3d/visualize/viewk3dwidgets.py\u001b[0m in \u001b[0;36m__init__\u001b[0;34m(self, figure_id, new_figure)\u001b[0m\n\u001b[1;32m 110\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mx\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfigure_id\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0mfigure_id\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 111\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mremove_widget\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 112\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'Figure id is already given'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 113\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 114\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfigure_id\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mfigure_id\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;31mValueError\u001b[0m: Figure id is already given" - ] - } - ], - "source": [ - "# It should fail, as we have already had a figure with id \n", - "# James and we cannot create a new one with the same figure_id\n", - "mesh.view(inline=True, figure_id='James', new_figure=True)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "

Testing

" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from ipywidgets import Widget\n", - "from menpo3d.visualize.viewk3dwidgets import K3dwidgetsRenderer" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "for x in Widget.widgets.values():\n", - " #print(type(x),x.model_id)\n", - " if isinstance(x, K3dwidgetsRenderer):\n", - " print(type(x),x.model_id, x.figure_id)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.5.6" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} From 5ec2eef5678c7e40845ff781fbc2609bb653f792 Mon Sep 17 00:00:00 2001 From: Thanos Date: Wed, 16 Sep 2020 15:26:32 +0000 Subject: [PATCH 07/43] Update Introduction K3d. New paths for loading data --- examples/0_Introduction_to_K3d_Widgets.ipynb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/0_Introduction_to_K3d_Widgets.ipynb b/examples/0_Introduction_to_K3d_Widgets.ipynb index 1157604..a3e4348 100644 --- a/examples/0_Introduction_to_K3d_Widgets.ipynb +++ b/examples/0_Introduction_to_K3d_Widgets.ipynb @@ -24,8 +24,8 @@ "metadata": {}, "outputs": [], "source": [ - "mesh = m3io.import_mesh('../data/james.obj')\n", - "lms = m3io.import_landmark_file('../data/james.ljson')['LJSON']" + "mesh = m3io.import_mesh('../menpo3d/data/james.obj')\n", + "lms = m3io.import_landmark_file('../menpo3d/data/james.ljson')['LJSON']" ] }, { From 2bc393a4ba326aeabda0e2183de8621c1d7569cc Mon Sep 17 00:00:00 2001 From: Thanos Date: Wed, 16 Sep 2020 15:27:27 +0000 Subject: [PATCH 08/43] Update Introduction K3d. New paths for loading data --- examples/0_Introduction_to_K3d_Widgets.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/0_Introduction_to_K3d_Widgets.ipynb b/examples/0_Introduction_to_K3d_Widgets.ipynb index a3e4348..83c779f 100644 --- a/examples/0_Introduction_to_K3d_Widgets.ipynb +++ b/examples/0_Introduction_to_K3d_Widgets.ipynb @@ -143,7 +143,7 @@ "outputs": [], "source": [ "# Load model and its landmarks indices \n", - "model = mio.import_pickle('/data/models/3DMD_all_all_all_158.pkl')\n", + "model = mio.import_pickle('../menpo3d/data/3DMD_all_all_all_10.pkl')['model']\n", "lms_indices = [21868, 22404, 22298, 22327, 43430, 45175, 46312, 47132, 47911, 48692,\n", " 49737, 51376, 53136, 32516, 32616, 32205, 32701, 38910, 39396, 39693,\n", " 39934, 40131, 40843, 41006, 41179, 41430, 13399, 8161, 8172, 8179, 8185,\n", From 4fb71e75f7bb0bed845f2705bdbe50602befb58e Mon Sep 17 00:00:00 2001 From: Thanos Date: Mon, 5 Oct 2020 21:08:16 +0000 Subject: [PATCH 09/43] Remove menpowidgets dependency. However, it is needed to install menpowidgets if someone wants to view the models --- menpo3d/visualize/viewk3dwidgets.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/menpo3d/visualize/viewk3dwidgets.py b/menpo3d/visualize/viewk3dwidgets.py index 786c91b..87d3e47 100644 --- a/menpo3d/visualize/viewk3dwidgets.py +++ b/menpo3d/visualize/viewk3dwidgets.py @@ -5,7 +5,6 @@ from k3d import Plot, mesh as k3d_mesh, points as k3d_points from io import BytesIO from ipywidgets import GridBox, Layout -from menpowidgets.options import LinearModelParametersWidget # The colour map used for all lines and markers GLOBAL_CMAP = 'jet' @@ -363,7 +362,7 @@ def _render_mesh(self, mesh_type, line_width, colour, marker_size, # point_size=marker_size, # shader='mesh') # widg_to_draw += points_to_add -# +# # TODO # Why the following atributes don't change self.camera = [-0.02, -0.12, 3.32, @@ -520,9 +519,9 @@ def __init__(self, figure_id, new_figure, group, landmark_group): self.landmark_group = landmark_group def _render(self, render_lines=True, line_colour='r', line_width=2, - render_markers=True, marker_style='sphere', marker_size=None, - marker_colour='r', marker_resolution=8, step=None, alpha=1.0, - render_numbering=False, numbers_colour='k', numbers_size=None): + render_markers=True, marker_style='sphere', marker_size=None, + marker_colour='r', marker_resolution=8, step=None, alpha=1.0, + render_numbering=False, numbers_colour='k', numbers_size=None): # Regarding the labels colours, we may get passed either no colours (in # which case we generate random colours) or a single colour to colour # all the labels with @@ -569,12 +568,16 @@ def _build_sub_pointclouds(self): for label in self.landmark_group.labels] -class K3dwidgetsPCAModelViewer3d(GridBox):#, K3dwidgetsRenderer): +class K3dwidgetsPCAModelViewer3d(GridBox): def __init__(self, figure_id, new_figure, points, trilist, components, eigenvalues, n_parameters, parameters_bound, landmarks_indices, widget_style): - #self.figure_id = figure_id #self.check_figure_id(figure_id, new_figure) + try: + from menpowidgets.options import LinearModelParametersWidget + except ImportError as e: + from menpo.visualize import MenpowidgetsMissingError + raise MenpowidgetsMissingError(e) self.figure_id = _check_figure_id(self, figure_id, new_figure) self.new_figure = new_figure self.points = points From dbf4d3f85ed0ef0f3ed21c79f89ca32f0b7381d3 Mon Sep 17 00:00:00 2001 From: Thanos Date: Mon, 5 Oct 2020 23:12:51 +0000 Subject: [PATCH 10/43] Add dict_figures, list_figures, clear_figures in k3dwidgets --- menpo3d/visualize/viewk3dwidgets.py | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/menpo3d/visualize/viewk3dwidgets.py b/menpo3d/visualize/viewk3dwidgets.py index 87d3e47..d68b952 100644 --- a/menpo3d/visualize/viewk3dwidgets.py +++ b/menpo3d/visualize/viewk3dwidgets.py @@ -1,14 +1,32 @@ import numpy as np from menpo.visualize import Renderer -# from menpo.shape import TriMesh -# from ..vtkutils import trimesh_to_vtk from k3d import Plot, mesh as k3d_mesh, points as k3d_points from io import BytesIO from ipywidgets import GridBox, Layout +from collections import defaultdict # The colour map used for all lines and markers GLOBAL_CMAP = 'jet' +def dict_figures(): + dict_fig = defaultdict(list) + for x in Widget.widgets.values(): + if hasattr(x, 'figure_id'): + dict_fig[x.figure_id].append(x.model_id) + return dict_fig + + +def list_figures(): + list_figures = list(dict_figure.keys()) + for figure_id in list_figures: + print(figure_id) + + +def clear_figure(figure_id=None): + # TODO remove figures, clear memory + dict_fig = dict_figures() + + def _parse_marker_size(marker_size, points): if marker_size is None: from menpo.shape import PointCloud From e57ff4cf0b4abf78e3f29f3f1e6024b086ef6299 Mon Sep 17 00:00:00 2001 From: Thanos Date: Mon, 5 Oct 2020 23:18:33 +0000 Subject: [PATCH 11/43] Debugging of some code in viewk3dwidgets; --- menpo3d/visualize/viewk3dwidgets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/menpo3d/visualize/viewk3dwidgets.py b/menpo3d/visualize/viewk3dwidgets.py index d68b952..c1e8311 100644 --- a/menpo3d/visualize/viewk3dwidgets.py +++ b/menpo3d/visualize/viewk3dwidgets.py @@ -17,7 +17,7 @@ def dict_figures(): def list_figures(): - list_figures = list(dict_figure.keys()) + list_figures = list(dict_figures().keys()) for figure_id in list_figures: print(figure_id) From 167d2900069bac77536ff3e6b252258c84631d09 Mon Sep 17 00:00:00 2001 From: Thanos Date: Mon, 5 Oct 2020 23:25:29 +0000 Subject: [PATCH 12/43] Debugging in viewk3dwidgets --- menpo3d/visualize/viewk3dwidgets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/menpo3d/visualize/viewk3dwidgets.py b/menpo3d/visualize/viewk3dwidgets.py index c1e8311..b32492b 100644 --- a/menpo3d/visualize/viewk3dwidgets.py +++ b/menpo3d/visualize/viewk3dwidgets.py @@ -2,7 +2,7 @@ from menpo.visualize import Renderer from k3d import Plot, mesh as k3d_mesh, points as k3d_points from io import BytesIO -from ipywidgets import GridBox, Layout +from ipywidgets import GridBox, Layout, Widget from collections import defaultdict # The colour map used for all lines and markers GLOBAL_CMAP = 'jet' From f39dbd114ea30c9d063a49c897d97f47182d432b Mon Sep 17 00:00:00 2001 From: Thanos Date: Wed, 7 Oct 2020 15:28:42 +0000 Subject: [PATCH 13/43] Add heatmap method as a k3dwidget. Move definition of heatmap using mayavi from menpo.shape.mesh to viewmayavi --- menpo3d/visualize/__init__.py | 3 +- menpo3d/visualize/base.py | 13 +++--- menpo3d/visualize/viewk3dwidgets.py | 69 +++++++++++++++++++++++++---- menpo3d/visualize/viewmayavi.py | 54 ++++++++++++++++++++-- 4 files changed, 119 insertions(+), 20 deletions(-) diff --git a/menpo3d/visualize/__init__.py b/menpo3d/visualize/__init__.py index 5e4249f..6ed4bf8 100644 --- a/menpo3d/visualize/__init__.py +++ b/menpo3d/visualize/__init__.py @@ -2,4 +2,5 @@ ColouredTriMeshViewer3d, TexturedTriMeshViewer3d, LandmarkViewer3d, TriMeshInlineViewer3d, PointGraphInlineViewer3d, TexturedTriMeshInlineViewer3d, - LandmarkInlineViewer3d, PCAModelInlineViewer3d) + LandmarkInlineViewer3d, HeatmapInlineViewer3d, + PCAModelInlineViewer3d) diff --git a/menpo3d/visualize/base.py b/menpo3d/visualize/base.py index 55bd705..41b42ee 100644 --- a/menpo3d/visualize/base.py +++ b/menpo3d/visualize/base.py @@ -1,11 +1,7 @@ from .viewmayavi import ( - MayaviTriMeshViewer3d, - MayaviPointGraphViewer3d, - MayaviTexturedTriMeshViewer3d, - MayaviLandmarkViewer3d, - MayaviVectorViewer3d, - MayaviColouredTriMeshViewer3d, -) + MayaviTriMeshViewer3d, MayaviPointGraphViewer3d, + MayaviTexturedTriMeshViewer3d, MayaviLandmarkViewer3d, + MayaviVectorViewer3d, MayaviColouredTriMeshViewer3d, MayaviHeatmapViewer3d) # from .viewitkwidgets import (ItkwidgetsTriMeshViewer3d, # ItkwidgetsPointGraphViewer3d) @@ -14,6 +10,7 @@ K3dwidgetsPointGraphViewer3d, K3dwidgetsLandmarkViewer3d, K3dwidgetsTexturedTriMeshViewer3d, + K3dwidgetsHeatmapViewer3d, K3dwidgetsPCAModelViewer3d) PointGraphViewer3d = MayaviPointGraphViewer3d @@ -22,9 +19,11 @@ ColouredTriMeshViewer3d = MayaviColouredTriMeshViewer3d LandmarkViewer3d = MayaviLandmarkViewer3d VectorViewer3d = MayaviVectorViewer3d +HeatmapViewer3d = MayaviHeatmapViewer3d TriMeshInlineViewer3d = K3dwidgetsTriMeshViewer3d TexturedTriMeshInlineViewer3d = K3dwidgetsTexturedTriMeshViewer3d LandmarkInlineViewer3d = K3dwidgetsLandmarkViewer3d PointGraphInlineViewer3d = K3dwidgetsPointGraphViewer3d +HeatmapInlineViewer3d = K3dwidgetsHeatmapViewer3d PCAModelInlineViewer3d = K3dwidgetsPCAModelViewer3d diff --git a/menpo3d/visualize/viewk3dwidgets.py b/menpo3d/visualize/viewk3dwidgets.py index b32492b..956724b 100644 --- a/menpo3d/visualize/viewk3dwidgets.py +++ b/menpo3d/visualize/viewk3dwidgets.py @@ -1,6 +1,7 @@ import numpy as np from menpo.visualize import Renderer -from k3d import Plot, mesh as k3d_mesh, points as k3d_points +from k3d import Plot, mesh as k3d_mesh, points as k3d_points,text as k3d_text +from k3d.colormaps import matplotlib_color_maps from io import BytesIO from ipywidgets import GridBox, Layout, Widget from collections import defaultdict @@ -376,11 +377,6 @@ def _render_mesh(self, mesh_type, line_width, colour, marker_size, if hasattr(self.landmarks, 'points'): self.landmarks.view(inline=True, new_figure=False, figure_id=self.figure_id) -# points_to_add = k3d_points(self.landmarks.points, color=0x00FF00, -# point_size=marker_size, -# shader='mesh') -# widg_to_draw += points_to_add -# # TODO # Why the following atributes don't change self.camera = [-0.02, -0.12, 3.32, @@ -575,7 +571,8 @@ def _render(self, render_lines=True, line_colour='r', line_width=2, for i, (label, pc) in enumerate(sub_pointclouds): # add pointcloud - points_to_add = k3d_points(pc.points, color=marker_colour[i], + points_to_add = k3d_points(pc.points.astype(np.float32), + color=marker_colour[i], point_size=marker_size, shader=marker_style) widg_to_draw += points_to_add @@ -586,6 +583,62 @@ def _build_sub_pointclouds(self): for label in self.landmark_group.labels] +class K3dwidgetsHeatmapViewer3d(K3dwidgetsRenderer): + def __init__(self, figure_id, new_figure, points, trilist, landmarks=None): + super(K3dwidgetsHeatmapViewer3d, self).__init__(figure_id, new_figure) + self.points = points + self.trilist = trilist + self.landmarks = landmarks + + def _render_mesh(self, distances_between_meshes, type_cmap, + scalar_range, show_statistics=False): + + marker_size = _parse_marker_size(None, self.points) + + widg_to_draw = self + if not self.new_figure: + for widg in self.widgets.values(): + if isinstance(widg, K3dwidgetsRenderer): + if widg.figure_id == self.figure_id and widg.model_id != self.model_id: + widg_to_draw = widg + break + try: + color_map = getattr(matplotlib_color_maps, type_cmap) + except AttributeError: + print('Could not find colormap {}. Hot_r is going to be used instead'.format(type_cmap)) + color_map = getattr(matplotlib_color_maps, 'hot_r') + + mesh_to_add = k3d_mesh(self.points.astype(np.float32), + self.trilist.flatten().astype(np.uint32), + color_map=color_map, + attribute=distances_between_meshes, + color_range=scalar_range + ) + widg_to_draw += mesh_to_add + + if hasattr(self.landmarks, 'points'): + self.landmarks.view(inline=True, new_figure=False, + figure_id=self.figure_id) + + if show_statistics: + text = '\\begin{{matrix}} \\mu & {:.3} \\\\ \\sigma^2 & {:.3} \\\\ \\max & {:.3} \\end{{matrix}}'\ + .format(distances_between_meshes.mean(), + distances_between_meshes.std(), + distances_between_meshes.max()) + min_b = np.min(self.points, axis=0) + max_b = np.max(self.points, axis=0) + text_position = (max_b-min_b)/2 + widg_to_draw += k3d_text(text, position=text_position, + color=0xff0000, size=1) + + return widg_to_draw + + def _render(self, distances_between_meshes, type_cmap='hot_r', + scalar_range=[0, 2], show_statistics=False): + return self._render_mesh(distances_between_meshes, type_cmap, + scalar_range, show_statistics) + + class K3dwidgetsPCAModelViewer3d(GridBox): def __init__(self, figure_id, new_figure, points, trilist, components, eigenvalues, n_parameters, parameters_bound, @@ -637,7 +690,7 @@ def _render_mesh(self, mesh_type, line_width, colour, marker_size, return self def render_function(self, change): - mesh = self.points + (self.components[:self.n_parameters, :].T@(self.wid.selected_values*self.eigenvalues[:self.n_parameters]**0.5)).reshape(-1,3) + mesh = self.points + (self.components[:self.n_parameters, :].T@(self.wid.selected_values*self.eigenvalues[:self.n_parameters]**0.5)).reshape(-1, 3) self.mesh_window.objects[0].vertices = mesh if self.landmarks_indices is not None: self.mesh_window.objects[1].positions = mesh[self.landmarks_indices] diff --git a/menpo3d/visualize/viewmayavi.py b/menpo3d/visualize/viewmayavi.py index a72f1f3..a76bd3e 100644 --- a/menpo3d/visualize/viewmayavi.py +++ b/menpo3d/visualize/viewmayavi.py @@ -717,7 +717,53 @@ def render( return self def _build_sub_pointclouds(self): - return [ - (label, self.landmark_group.get_label(label)) - for label in self.landmark_group.labels - ] + return [(label, self.landmark_group.get_label(label)) + for label in self.landmark_group.labels] + + +class MayaviHeatmapViewer3d(MayaviRenderer): + def __init__(self, figure_id, new_figure, points, trilist): + super(MayaviHeatmapViewer3d, self).__init__(figure_id, new_figure) + self.points = points + self.trilist = trilist + + def _render_mesh(self, scaled_distances_between_meshes, + type_cmap, scalar_range, show_statistics): + from mayavi import mlab + # v = mlab.figure(figure=figure_name, size=size, + # bgcolor=(1, 1, 1), fgcolor=(0, 0, 0)) + src = mlab.pipeline.triangular_mesh_source(self.points[:, 0], + self.points[:, 1], + self.points[:, 2], + self.trilist, + scalars=scaled_distances_between_meshes) + surf = mlab.pipeline.surface(src, colormap=type_cmap) + # When font size bug resolved, uncomment + # cb=mlab.colorbar(title='Distances in mm', + # orientation='vertical', nb_labels=5) + # cb.title_text_property.font_size = 20 + # cb.label_text_property.font_family = 'times' + # cb.label_text_property.font_size=10 + cb = mlab.colorbar(orientation='vertical', nb_labels=5) + cb.data_range = scalar_range + cb.scalar_bar_representation.position = [0.8, 0.15] + cb.scalar_bar_representation.position2 = [0.15, 0.7] + text = mlab.text(0.8, 0.85, 'Distances in mm') + text.width = 0.20 + if show_statistics: + text2 = mlab.text(0.5, 0.02, + 'Mean error {:.3}mm \nMax error {:.3}mm \ + '.format(scaled_distances_between_meshes.mean(), + scaled_distances_between_meshes.max())) + text2.width = 0.20 + surf.module_manager.scalar_lut_manager.reverse_lut = True + # perhaps we shouud usew kwargs + # if camera_settings is None: + mlab.gcf().scene.z_plus_view() + + def render(self, scaled_distances_between_meshes, type_cmap='hot', + scalar_range=(0, 2), show_statistics=False): + + self._render_mesh(scaled_distances_between_meshes, type_cmap, + scalar_range, show_statistics) + return self From 2880362fb0808faa25456e9d7984c556b68f9223 Mon Sep 17 00:00:00 2001 From: Thanos Date: Thu, 8 Oct 2020 09:53:50 +0000 Subject: [PATCH 14/43] Add list_figures, dict_figures, clear_figure in __init__, so they can be imported from menpo3d.visualize --- menpo3d/visualize/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/menpo3d/visualize/__init__.py b/menpo3d/visualize/__init__.py index 6ed4bf8..488587a 100644 --- a/menpo3d/visualize/__init__.py +++ b/menpo3d/visualize/__init__.py @@ -4,3 +4,5 @@ PointGraphInlineViewer3d, TexturedTriMeshInlineViewer3d, LandmarkInlineViewer3d, HeatmapInlineViewer3d, PCAModelInlineViewer3d) + +from .viewk3dwidgets import (list_figures, clear_figure, dict_figures) From 507c034d0856bc21beee306e6dd3df86ddd3e343 Mon Sep 17 00:00:00 2001 From: Thanos Date: Thu, 8 Oct 2020 09:58:02 +0000 Subject: [PATCH 15/43] Update 0_Introduction_to_K3d_Widgets notebook with heatmap example and list_figures, dict_figures --- examples/0_Introduction_to_K3d_Widgets.ipynb | 147 ++++++++++++++++--- 1 file changed, 129 insertions(+), 18 deletions(-) diff --git a/examples/0_Introduction_to_K3d_Widgets.ipynb b/examples/0_Introduction_to_K3d_Widgets.ipynb index 83c779f..c0e7107 100644 --- a/examples/0_Introduction_to_K3d_Widgets.ipynb +++ b/examples/0_Introduction_to_K3d_Widgets.ipynb @@ -8,14 +8,16 @@ "source": [ "import menpo3d.io as m3io\n", "import menpo.io as mio\n", - "from menpo.shape import PointCloud" + "from menpo.shape import PointCloud\n", + "from menpo.landmark import face_ibug_68_to_face_ibug_68\n", + "import numpy as np" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "

Load the data (Mesh and Landmarks)

" + "

Load the data (Mesh, landmarks and model)

" ] }, { @@ -25,14 +27,24 @@ "outputs": [], "source": [ "mesh = m3io.import_mesh('../menpo3d/data/james.obj')\n", - "lms = m3io.import_landmark_file('../menpo3d/data/james.ljson')['LJSON']" + "lms = m3io.import_landmark_file('../menpo3d/data/james.ljson')['LJSON']\n", + "\n", + "# Load model and its landmarks indices \n", + "model = mio.import_pickle('../menpo3d/data/3DMD_all_all_all_10.pkl')['model']\n", + "lms_indices = [21868, 22404, 22298, 22327, 43430, 45175, 46312, 47132, 47911, 48692,\n", + " 49737, 51376, 53136, 32516, 32616, 32205, 32701, 38910, 39396, 39693,\n", + " 39934, 40131, 40843, 41006, 41179, 41430, 13399, 8161, 8172, 8179, 8185,\n", + " 5622, 6881, 8202, 9403, 10764, 1831, 3887, 5049, 6214, 4805, 3643, 9955,\n", + " 11095, 12255, 14197, 12397, 11366, 5779, 6024, 7014, 8215, 9294, 10267,\n", + " 10922, 9556, 8836, 8236, 7636, 6794, 5905, 7264, 8223, 9063, 10404, 8828,\n", + " 8228, 7509]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "

Show the mesh

" + "

Create new random instances

" ] }, { @@ -40,6 +52,28 @@ "execution_count": null, "metadata": {}, "outputs": [], + "source": [ + "cov = np.diag(model.eigenvalues)\n", + "model_mean = model.mean()\n", + "synthetic_weights = np.random.multivariate_normal(np.zeros(model.n_active_components),\n", + " cov, 1000)\n", + "random_mesh = model.instance(synthetic_weights[5])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

Show the mesh

" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "scrolled": true + }, + "outputs": [], "source": [ "# Default values for TriMesh, TextureMesh viewer are\n", "# figure_id None\n", @@ -133,7 +167,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "

Load a Morphable Model

" + "

HeatMaps

" ] }, { @@ -142,15 +176,38 @@ "metadata": {}, "outputs": [], "source": [ - "# Load model and its landmarks indices \n", - "model = mio.import_pickle('../menpo3d/data/3DMD_all_all_all_10.pkl')['model']\n", - "lms_indices = [21868, 22404, 22298, 22327, 43430, 45175, 46312, 47132, 47911, 48692,\n", - " 49737, 51376, 53136, 32516, 32616, 32205, 32701, 38910, 39396, 39693,\n", - " 39934, 40131, 40843, 41006, 41179, 41430, 13399, 8161, 8172, 8179, 8185,\n", - " 5622, 6881, 8202, 9403, 10764, 1831, 3887, 5049, 6214, 4805, 3643, 9955,\n", - " 11095, 12255, 14197, 12397, 11366, 5779, 6024, 7014, 8215, 9294, 10267,\n", - " 10922, 9556, 8836, 8236, 7636, 6794, 5905, 7264, 8223, 9063, 10404, 8828,\n", - " 8228, 7509]" + "# Heatmap between a random mesh and mean mesh\n", + "random_mesh.heatmap(model_mean, inline=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Heatmap with statistics \n", + "# Be careful, since we have already drawn a heatmap between\n", + "# random and mean, we should use another name for figure\n", + "random_mesh.heatmap(model_mean, inline=True, show_statistics=True, figure_id='Heatmap2')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Heatmap with landmarks\n", + "random_mesh.landmarks = face_ibug_68_to_face_ibug_68(PointCloud(random_mesh.points[lms_indices]))\n", + "random_mesh.heatmap(model_mean, inline=True, show_statistics=True, figure_id='Heatmap3')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

View a Morphable Model

" ] }, { @@ -177,7 +234,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "

Fail cases

" + "

Fail cases (supposed you have already executed all the above cells)

" ] }, { @@ -214,6 +271,51 @@ "model.view(inline=True, figure_id='Model')" ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# You have already created a heatmap between random_mesh and model_mean\n", + "\n", + "random_mesh.heatmap(model_mean, inline=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

Additional functions

" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from menpo3d.visualize import list_figures, dict_figures" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "list_figures()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "dict_figures()" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -238,11 +340,20 @@ "outputs": [], "source": [ "for x in Widget.widgets.values():\n", - " # print(type(x),x.model_id)\n", - " #if isinstance(x, K3dwidgetsRenderer):\n", + " print(type(x),x.model_id)\n", + "# if isinstance(x, K3dwidgetsRenderer):\n", " if hasattr(x,'figure_id'):\n", " print(type(x),x.model_id, x.figure_id)" ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "list_figures()" + ] } ], "metadata": { @@ -261,7 +372,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.5.6" + "version": "3.7.9" } }, "nbformat": 4, From cc54b490289452ba9bdd684384c95c4fe8bacca4 Mon Sep 17 00:00:00 2001 From: Thanos Date: Fri, 9 Oct 2020 16:16:22 +0000 Subject: [PATCH 16/43] Add VectorInlineViewer3d. Move the widget check to the abstract classs --- menpo3d/visualize/__init__.py | 4 +- menpo3d/visualize/base.py | 2 + menpo3d/visualize/viewk3dwidgets.py | 76 ++++++++++++++--------------- 3 files changed, 40 insertions(+), 42 deletions(-) diff --git a/menpo3d/visualize/__init__.py b/menpo3d/visualize/__init__.py index 488587a..aa14c14 100644 --- a/menpo3d/visualize/__init__.py +++ b/menpo3d/visualize/__init__.py @@ -2,7 +2,7 @@ ColouredTriMeshViewer3d, TexturedTriMeshViewer3d, LandmarkViewer3d, TriMeshInlineViewer3d, PointGraphInlineViewer3d, TexturedTriMeshInlineViewer3d, - LandmarkInlineViewer3d, HeatmapInlineViewer3d, - PCAModelInlineViewer3d) + LandmarkInlineViewer3d, VectorInlineViewer3d, + HeatmapInlineViewer3d, PCAModelInlineViewer3d) from .viewk3dwidgets import (list_figures, clear_figure, dict_figures) diff --git a/menpo3d/visualize/base.py b/menpo3d/visualize/base.py index 41b42ee..ea09694 100644 --- a/menpo3d/visualize/base.py +++ b/menpo3d/visualize/base.py @@ -8,6 +8,7 @@ from .viewk3dwidgets import (K3dwidgetsTriMeshViewer3d, K3dwidgetsPointGraphViewer3d, + K3dwidgetsVectorViewer3d, K3dwidgetsLandmarkViewer3d, K3dwidgetsTexturedTriMeshViewer3d, K3dwidgetsHeatmapViewer3d, @@ -25,5 +26,6 @@ TexturedTriMeshInlineViewer3d = K3dwidgetsTexturedTriMeshViewer3d LandmarkInlineViewer3d = K3dwidgetsLandmarkViewer3d PointGraphInlineViewer3d = K3dwidgetsPointGraphViewer3d +VectorInlineViewer3d = K3dwidgetsVectorViewer3d HeatmapInlineViewer3d = K3dwidgetsHeatmapViewer3d PCAModelInlineViewer3d = K3dwidgetsPCAModelViewer3d diff --git a/menpo3d/visualize/viewk3dwidgets.py b/menpo3d/visualize/viewk3dwidgets.py index 956724b..4b2bb0a 100644 --- a/menpo3d/visualize/viewk3dwidgets.py +++ b/menpo3d/visualize/viewk3dwidgets.py @@ -1,6 +1,7 @@ import numpy as np from menpo.visualize import Renderer -from k3d import Plot, mesh as k3d_mesh, points as k3d_points,text as k3d_text +from k3d import (Plot, mesh as k3d_mesh, points as k3d_points, + text as k3d_text, vectors as k3d_vectors) from k3d.colormaps import matplotlib_color_maps from io import BytesIO from ipywidgets import GridBox, Layout, Widget @@ -138,6 +139,16 @@ def __init__(self, figure_id, new_figure): self.new_figure = new_figure self.grid_visible = False + def _render(self): + widg_to_draw = self + if not self.new_figure: + for widg in self.widgets.values(): + if isinstance(widg, K3dwidgetsRenderer): + if widg.figure_id == self.figure_id and widg.model_id != self.model_id: + widg_to_draw = widg + return widg_to_draw + return widg_to_draw + def remove_widget(self): super(K3dwidgetsRenderer, self).close() # copy from close from ipywidgets.widget.Widget @@ -275,19 +286,19 @@ def force_draw(self): class K3dwidgetsVectorViewer3d(K3dwidgetsRenderer): def __init__(self, figure_id, new_figure, points, vectors): super(K3dwidgetsVectorViewer3d, self).__init__(figure_id, new_figure) - self.points = points - self.vectors = vectors + non_zero_indices = np.unique(np.nonzero(vectors.reshape(-1,3))[0]) + self.points = points[non_zero_indices].astype(np.float32) + self.vectors = vectors[non_zero_indices].astype(np.float32) - def render(self, colour='r', line_width=2, marker_style='2darrow', - marker_resolution=8, marker_size=None, step=None, alpha=1.0): + def _render(self, colour='r', line_width=2, marker_size=None): marker_size = _parse_marker_size(marker_size, self.points) colour = _parse_colour(colour) -# mlab.quiver3d(self.points[:, 0], self.points[:, 1], self.points[:, 2], -# self.vectors[:, 0], self.vectors[:, 1], self.vectors[:, 2], -# figure=self.figure, color=colour, mask_points=step, -# line_width=line_width, mode=marker_style, -# resolution=marker_resolution, opacity=alpha, -# scale_factor=marker_size) + + widg_to_draw = super(K3dwidgetsVectorViewer3d, self)._render() + vectors_to_add = k3d_vectors(self.points, self.vectors, + color=colour, head_size=marker_size, + line_width=line_width) + widg_to_draw += vectors_to_add return self @@ -356,19 +367,11 @@ def __init__(self, figure_id, new_figure, points, trilist, landmarks=None): self.trilist = trilist self.landmarks = landmarks - def _render_mesh(self, mesh_type, line_width, colour, marker_size, - marker_resolution, marker_style, step, alpha): + def _render_mesh(self, line_width, colour, marker_style, marker_size): marker_size = _parse_marker_size(marker_size, self.points) colour = _parse_colour(colour) - widg_to_draw = self - if not self.new_figure: - for widg in self.widgets.values(): - if isinstance(widg, K3dwidgetsRenderer): - if widg.figure_id == self.figure_id and widg.model_id != self.model_id: - widg_to_draw = widg - break - + widg_to_draw = super(K3dwidgetsTriMeshViewer3d, self)._render() mesh_to_add = k3d_mesh(self.points.astype(np.float32), self.trilist.flatten().astype(np.uint32), flat_shading=False, color=colour, side='double') @@ -386,22 +389,21 @@ def _render_mesh(self, mesh_type, line_width, colour, marker_size, widg_to_draw.lighting = 0 return widg_to_draw - def _render(self, mesh_type='wireframe', line_width=2, colour='r', - marker_style='sphere', marker_size=None, marker_resolution=8, + def _render(self, line_width=2, colour='r', + marker_style='sphere', marker_size=None, normals=None, normals_colour='k', normals_line_width=2, - normals_marker_style='2darrow', normals_marker_size=None, - normals_marker_resolution=8, step=None, alpha=1.0): + normals_marker_size=None): + widg_to_draw = self._render_mesh(line_width, colour, + marker_style, marker_size) if normals is not None: - K3dwidgetsVectorViewer3d(self.figure_id, False, - self.points, normals).render( - colour=normals_colour, line_width=normals_line_width, step=step, - marker_style=normals_marker_style, - marker_resolution=normals_marker_resolution, - marker_size=normals_marker_size, alpha=alpha) - return self._render_mesh(mesh_type, line_width, colour, marker_size, - marker_resolution, marker_style, step, alpha) + print('Mpika {}'.format(self.figure_id)) + K3dwidgetsVectorViewer3d(self.figure_id, False, + self.points, normals)._render(colour=normals_colour, + line_width=normals_line_width, + marker_size=normals_marker_size) + return widg_to_draw class K3dwidgetsTexturedTriMeshViewer3d(K3dwidgetsRenderer): def __init__(self, figure_id, new_figure, points, trilist, texture, @@ -417,13 +419,7 @@ def __init__(self, figure_id, new_figure, points, trilist, texture, def _render_mesh(self, mesh_type='surface', ambient_light=0.0, specular_light=0.0, alpha=1.0): - widg_to_draw = self - if not self.new_figure: - for widg in self.widgets.values(): - if isinstance(widg, K3dwidgetsRenderer): - if widg.figure_id == self.figure_id and widg.model_id != self.model_id: - widg_to_draw = widg - break + widg_to_draw = super(K3dwidgetsTexturedTriMeshViewer3d, self)._render() uvs = self.tcoords.points tmp_img = self.texture.mirror(axis=0).as_PILImage() From 453ac54fc081bd0d09f023f7225ee7f15638148f Mon Sep 17 00:00:00 2001 From: Thanos Date: Sun, 11 Oct 2020 22:48:44 +0000 Subject: [PATCH 17/43] Add K3dGraphViewer and K3dNormalViewer class. Add numbering at landmarks, graphs. --- menpo3d/visualize/__init__.py | 5 +- menpo3d/visualize/viewk3dwidgets.py | 112 ++++++++++++++-------------- 2 files changed, 60 insertions(+), 57 deletions(-) diff --git a/menpo3d/visualize/__init__.py b/menpo3d/visualize/__init__.py index aa14c14..1263431 100644 --- a/menpo3d/visualize/__init__.py +++ b/menpo3d/visualize/__init__.py @@ -2,7 +2,8 @@ ColouredTriMeshViewer3d, TexturedTriMeshViewer3d, LandmarkViewer3d, TriMeshInlineViewer3d, PointGraphInlineViewer3d, TexturedTriMeshInlineViewer3d, - LandmarkInlineViewer3d, VectorInlineViewer3d, - HeatmapInlineViewer3d, PCAModelInlineViewer3d) + LandmarkInlineViewer3d, PointGraphInlineViewer3d, + VectorInlineViewer3d, HeatmapInlineViewer3d, + PCAModelInlineViewer3d) from .viewk3dwidgets import (list_figures, clear_figure, dict_figures) diff --git a/menpo3d/visualize/viewk3dwidgets.py b/menpo3d/visualize/viewk3dwidgets.py index 4b2bb0a..21b3ebc 100644 --- a/menpo3d/visualize/viewk3dwidgets.py +++ b/menpo3d/visualize/viewk3dwidgets.py @@ -1,7 +1,8 @@ import numpy as np from menpo.visualize import Renderer from k3d import (Plot, mesh as k3d_mesh, points as k3d_points, - text as k3d_text, vectors as k3d_vectors) + text as k3d_text, vectors as k3d_vectors, + line as k3d_line) from k3d.colormaps import matplotlib_color_maps from io import BytesIO from ipywidgets import GridBox, Layout, Widget @@ -110,16 +111,6 @@ def _check_figure_id(obj, figure_id, new_figure): obj.remove_widget() raise ValueError('Figure id is already given') return figure_id -# def _set_numbering(figure, centers, render_numbering=True, numbers_size=None, -# numbers_colour='k'): -# import mayavi.mlab as mlab -# numbers_colour = _parse_colour(numbers_colour) -# numbers_size = _parse_marker_size(numbers_size, centers) -# if render_numbering: -# for k, p in enumerate(centers): -# mlab.text3d(p[0], p[1], p[2], str(k), figure=figure, -# scale=numbers_size, orient_to_camera=True, -# color=numbers_colour, line_width=2) class K3dwidgetsRenderer(Plot, Renderer): @@ -286,7 +277,7 @@ def force_draw(self): class K3dwidgetsVectorViewer3d(K3dwidgetsRenderer): def __init__(self, figure_id, new_figure, points, vectors): super(K3dwidgetsVectorViewer3d, self).__init__(figure_id, new_figure) - non_zero_indices = np.unique(np.nonzero(vectors.reshape(-1,3))[0]) + non_zero_indices = np.unique(np.nonzero(vectors.reshape(-1, 3))[0]) self.points = points[non_zero_indices].astype(np.float32) self.vectors = vectors[non_zero_indices].astype(np.float32) @@ -311,52 +302,61 @@ def __init__(self, figure_id, new_figure, points, edges): def _render(self, render_lines=True, line_colour='r', line_width=2, render_markers=True, marker_style='flat', marker_size=10, - marker_colour='g', marker_resolution=8, step=None, alpha=1.0, - render_numbering=False, numbers_colour='k', numbers_size=None): + marker_colour='g', render_numbering=False, + numbers_colour='k', numbers_size=None): # Render the lines if requested # TODO if render_lines: - line_colour = _parse_colour(line_colour) + if isinstance(line_colour, list): + line_colour = [_parse_colour(i_color) for i_color in + line_colour] + else: + line_colour = _parse_colour(line_colour) + # Render the markers if requested if render_markers: marker_size = _parse_marker_size(marker_size, self.points) marker_colour = _parse_colour(marker_colour) - widg_to_draw = self - - if not self.new_figure: - for widg in self.widgets.values(): - if isinstance(widg, K3dwidgetsRenderer): - if widg.figure_id == self.figure_id and widg.model_id != self.model_id: - widg_to_draw = widg - break + widg_to_draw = super(K3dwidgetsPointGraphViewer3d, self)._render() if marker_style == 'sphere': marker_style = 'mesh' - default_camera = [-0.16031231203819687, - 0.09455110637470637, - 2.8537626738058663, - 0.00039440393447875977, - -0.15653744339942932, - 0.5779531598091125, - -0.02452392741576587, - 0.9981297233524523, - -0.05599671726525722] - - if widg_to_draw is self: - widg_to_draw.camera = default_camera - points_to_add = k3d_points(self.points, color=marker_colour, point_size=marker_size, shader=marker_style) + lines_to_add = None + for edge in self.edges: + if isinstance(line_colour, list): + if len(line_colour): + color_this_line = line_colour.pop() + else: + color_this_line = 0xFF0000 + else: + color_this_line = line_colour + + if lines_to_add is None: + lines_to_add = k3d_line(self.points[edge], + color=color_this_line) + else: + lines_to_add += k3d_line(self.points[edge], + color=color_this_line) + + if render_numbering: + text_to_add = None + for i, point in enumerate(self.points): + if text_to_add is None: + text_to_add = k3d_text(str(i), position=point, + label_box=False) + else: + text_to_add += k3d_text(str(i), position=point, + label_box=False) + widg_to_draw += text_to_add + + widg_to_draw += lines_to_add widg_to_draw += points_to_add - # set numbering -# _set_numbering(self.figure, self.points, numbers_size=numbers_size, -# render_numbering=render_numbering, -# numbers_colour=numbers_colour) -# return widg_to_draw @@ -397,14 +397,14 @@ def _render(self, line_width=2, colour='r', widg_to_draw = self._render_mesh(line_width, colour, marker_style, marker_size) if normals is not None: - print('Mpika {}'.format(self.figure_id)) - K3dwidgetsVectorViewer3d(self.figure_id, False, + K3dwidgetsVectorViewer3d(self.figure_id, False, self.points, normals)._render(colour=normals_colour, - line_width=normals_line_width, - marker_size=normals_marker_size) + line_width=normals_line_width, + marker_size=normals_marker_size) return widg_to_draw + class K3dwidgetsTexturedTriMeshViewer3d(K3dwidgetsRenderer): def __init__(self, figure_id, new_figure, points, trilist, texture, tcoords, landmarks): @@ -552,26 +552,28 @@ def _render(self, render_lines=True, line_colour='r', line_width=2, # get pointcloud of each label sub_pointclouds = self._build_sub_pointclouds() - widg_to_draw = self - - if not self.new_figure: - for widg in self.widgets.values(): - if isinstance(widg, K3dwidgetsRenderer): - if widg.figure_id == self.figure_id and widg.model_id != self.model_id: - widg_to_draw = widg - break + widg_to_draw = super(K3dwidgetsLandmarkViewer3d, self)._render() if marker_style == 'sphere': marker_style = 'mesh' for i, (label, pc) in enumerate(sub_pointclouds): - # add pointcloud - points_to_add = k3d_points(pc.points.astype(np.float32), color=marker_colour[i], point_size=marker_size, shader=marker_style) widg_to_draw += points_to_add + if render_numbering: + text_to_add = None + for i, point in enumerate(self.landmark_group.points): + if text_to_add is None: + text_to_add = k3d_text(str(i), position=point, + label_box=False) + else: + text_to_add += k3d_text(str(i), position=point, + label_box=False) + widg_to_draw += text_to_add + return widg_to_draw def _build_sub_pointclouds(self): From 1fcbf1b13ce156edce5db60307bfa598c3a2f61e Mon Sep 17 00:00:00 2001 From: Thanos Date: Sun, 11 Oct 2020 22:51:32 +0000 Subject: [PATCH 18/43] Add graph viewer, normals viewer and numbering examples --- examples/0_Introduction_to_K3d_Widgets.ipynb | 151 +++++++++++++++++-- 1 file changed, 142 insertions(+), 9 deletions(-) diff --git a/examples/0_Introduction_to_K3d_Widgets.ipynb b/examples/0_Introduction_to_K3d_Widgets.ipynb index c0e7107..ddb106f 100644 --- a/examples/0_Introduction_to_K3d_Widgets.ipynb +++ b/examples/0_Introduction_to_K3d_Widgets.ipynb @@ -71,7 +71,7 @@ "cell_type": "code", "execution_count": null, "metadata": { - "scrolled": true + "scrolled": false }, "outputs": [], "source": [ @@ -81,8 +81,7 @@ "# in that case an automatic figure_id will be given\n", "# with 'Figure_{n}' format\n", "# n will be an increased integer starting from zero\n", - "\n", - "mesh.view(inline=True,) #wait a bit before magic happens" + "mesh.view(inline=True,) # wait a bit before magic happens" ] }, { @@ -146,10 +145,7 @@ "metadata": {}, "outputs": [], "source": [ - "# the new figure will have figure_id Figure_1\n", - "# Note the difference between plotting a pointcloud\n", - "# and landmarks \n", - "mesh.landmarks.view(inline=True, new_figure=True)" + "lms.view(inline=True, new_figure=True, render_numbering=True)" ] }, { @@ -203,6 +199,115 @@ "random_mesh.heatmap(model_mean, inline=True, show_statistics=True, figure_id='Heatmap3')" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

Show Normals

" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "pts = random_mesh.points[lms_indices]\n", + "vrt = np.zeros((random_mesh.n_points,3))\n", + "vrt[lms_indices] = random_mesh.vertex_normals()[lms_indices]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "vrt_ = vrt[lms_indices]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "random_mesh.view(inline=True, normals=vrt, \n", + " normals_line_width = 0.01,\n", + " figure_id='Normals')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "random_mesh.landmarks = face_ibug_68_to_face_ibug_68(random_mesh.points[lms_indices])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "random_mesh.landmarks.view(inline=True, figure_id='Normals', new_figure=False)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

Show Surface

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

Show Graphs

" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from menpo.shape import PointUndirectedGraph \n", + "import numpy as np\n", + "points = np.array([[10, 30, 10], [0, 20, 11], [20, 20, 11], [0, 10, 12], [20, 10, 12], [0, 0, 12]]) \n", + "edges = np.array([[0, 1], [1, 0], [0, 2], [2, 0], [1, 2], [2, 1], \n", + " [1, 3], [3, 1], [2, 4], [4, 2], [3, 4], [4, 3],[3, 5], [5, 3]]) \n", + "colors = [\n", + " 0xff,\n", + " 0xffff,\n", + " 0xff00ff,\n", + " 0x00ffff,\n", + " 0xffff00,]\n", + "\n", + "graph = PointUndirectedGraph.init_from_edges(points, edges) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "graph.view(inline=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "graph.view(inline=True, line_colour=colors, render_numbering=True)" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -278,7 +383,6 @@ "outputs": [], "source": [ "# You have already created a heatmap between random_mesh and model_mean\n", - "\n", "random_mesh.heatmap(model_mean, inline=True)" ] }, @@ -354,6 +458,35 @@ "source": [ "list_figures()" ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import k3d" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "origins = lms.points\n", + "vectors2 = origins+.5\n", + "colors = [0xff0000, 0x0000ff, 0x0000ff, 0xff0000, 0x0000ff, 0xff0000]\n", + "\n", + "plot = k3d.plot()\n", + "vectors = k3d.vectors(origins, vectors2, head_size=100,)#, colors=colors, labels=[], label_size=1.5)\n", + "\n", + "plot += vectors\n", + "plot += k3d.points(origins, point_size=10, color=100)\n", + "\n", + "plot.display()" + ] } ], "metadata": { @@ -372,7 +505,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.9" + "version": "3.5.6" } }, "nbformat": 4, From cac0a51750bce7a5d315b0eae2684c6036fabe6d Mon Sep 17 00:00:00 2001 From: Thanos Date: Mon, 12 Oct 2020 08:43:48 +0000 Subject: [PATCH 19/43] Checking and creation of widg_to_draw is happening in K3dRenderer for all child classes --- menpo3d/visualize/viewk3dwidgets.py | 61 ++++++++++++----------------- 1 file changed, 26 insertions(+), 35 deletions(-) diff --git a/menpo3d/visualize/viewk3dwidgets.py b/menpo3d/visualize/viewk3dwidgets.py index 21b3ebc..a2c16a3 100644 --- a/menpo3d/visualize/viewk3dwidgets.py +++ b/menpo3d/visualize/viewk3dwidgets.py @@ -147,6 +147,11 @@ def remove_widget(self): self.comm.close() self.comm = None self._repr_mimebundle_ = None + # TODO + # Why the following atributes don't change + self.camera = [-0.02, -0.12, 3.32, + 0.00, -0.16, 0.58, + 0.02, 1.00, 0.04] def get_figure(self): r""" @@ -305,8 +310,8 @@ def _render(self, render_lines=True, line_colour='r', line_width=2, marker_colour='g', render_numbering=False, numbers_colour='k', numbers_size=None): + widg_to_draw = super(K3dwidgetsPointGraphViewer3d, self)._render() # Render the lines if requested - # TODO if render_lines: if isinstance(line_colour, list): line_colour = [_parse_colour(i_color) for i_color in @@ -314,18 +319,6 @@ def _render(self, render_lines=True, line_colour='r', line_width=2, else: line_colour = _parse_colour(line_colour) - # Render the markers if requested - if render_markers: - marker_size = _parse_marker_size(marker_size, self.points) - marker_colour = _parse_colour(marker_colour) - widg_to_draw = super(K3dwidgetsPointGraphViewer3d, self)._render() - - if marker_style == 'sphere': - marker_style = 'mesh' - - points_to_add = k3d_points(self.points, color=marker_colour, - point_size=marker_size, - shader=marker_style) lines_to_add = None for edge in self.edges: if isinstance(line_colour, list): @@ -342,21 +335,32 @@ def _render(self, render_lines=True, line_colour='r', line_width=2, else: lines_to_add += k3d_line(self.points[edge], color=color_this_line) + widg_to_draw += lines_to_add + + # Render the markers if requested + if render_markers: + marker_size = _parse_marker_size(marker_size, self.points) + marker_colour = _parse_colour(marker_colour) + + if marker_style == 'sphere': + marker_style = 'mesh' + + points_to_add = k3d_points(self.points, color=marker_colour, + point_size=marker_size, + shader=marker_style) + widg_to_draw += points_to_add if render_numbering: text_to_add = None for i, point in enumerate(self.points): if text_to_add is None: text_to_add = k3d_text(str(i), position=point, - label_box=False) + label_box=False) else: text_to_add += k3d_text(str(i), position=point, - label_box=False) + label_box=False) widg_to_draw += text_to_add - widg_to_draw += lines_to_add - widg_to_draw += points_to_add - return widg_to_draw @@ -380,13 +384,6 @@ def _render_mesh(self, line_width, colour, marker_style, marker_size): if hasattr(self.landmarks, 'points'): self.landmarks.view(inline=True, new_figure=False, figure_id=self.figure_id) - # TODO - # Why the following atributes don't change - self.camera = [-0.02, -0.12, 3.32, - 0.00, -0.16, 0.58, - 0.02, 1.00, 0.04] - - widg_to_draw.lighting = 0 return widg_to_draw def _render(self, line_width=2, colour='r', @@ -568,12 +565,11 @@ def _render(self, render_lines=True, line_colour='r', line_width=2, for i, point in enumerate(self.landmark_group.points): if text_to_add is None: text_to_add = k3d_text(str(i), position=point, - label_box=False) + label_box=False) else: text_to_add += k3d_text(str(i), position=point, - label_box=False) + label_box=False) widg_to_draw += text_to_add - return widg_to_draw def _build_sub_pointclouds(self): @@ -593,13 +589,8 @@ def _render_mesh(self, distances_between_meshes, type_cmap, marker_size = _parse_marker_size(None, self.points) - widg_to_draw = self - if not self.new_figure: - for widg in self.widgets.values(): - if isinstance(widg, K3dwidgetsRenderer): - if widg.figure_id == self.figure_id and widg.model_id != self.model_id: - widg_to_draw = widg - break + widg_to_draw = super(K3dwidgetsHeatmapViewer3d, self)._render() + try: color_map = getattr(matplotlib_color_maps, type_cmap) except AttributeError: From 33351004e1224b7486d435d7a95fda055d36df7d Mon Sep 17 00:00:00 2001 From: Thanos Date: Mon, 12 Oct 2020 11:21:13 +0000 Subject: [PATCH 20/43] One more check was added in order to plot in the same figure. All points are now casted as np.float32 in the initialization. --- menpo3d/visualize/viewk3dwidgets.py | 39 +++++++++++++++-------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/menpo3d/visualize/viewk3dwidgets.py b/menpo3d/visualize/viewk3dwidgets.py index a2c16a3..a9a9e0a 100644 --- a/menpo3d/visualize/viewk3dwidgets.py +++ b/menpo3d/visualize/viewk3dwidgets.py @@ -135,7 +135,7 @@ def _render(self): if not self.new_figure: for widg in self.widgets.values(): if isinstance(widg, K3dwidgetsRenderer): - if widg.figure_id == self.figure_id and widg.model_id != self.model_id: + if widg.figure_id == self.figure_id and widg.model_id != self.model_id and widg.new_figure: widg_to_draw = widg return widg_to_draw return widg_to_draw @@ -295,7 +295,7 @@ def _render(self, colour='r', line_width=2, marker_size=None): color=colour, head_size=marker_size, line_width=line_width) widg_to_draw += vectors_to_add - return self + return widg_to_draw class K3dwidgetsPointGraphViewer3d(K3dwidgetsRenderer): @@ -367,8 +367,8 @@ def _render(self, render_lines=True, line_colour='r', line_width=2, class K3dwidgetsTriMeshViewer3d(K3dwidgetsRenderer): def __init__(self, figure_id, new_figure, points, trilist, landmarks=None): super(K3dwidgetsTriMeshViewer3d, self).__init__(figure_id, new_figure) - self.points = points - self.trilist = trilist + self.points = points.astype(np.float32) + self.trilist = trilist.astype(np.uint32) self.landmarks = landmarks def _render_mesh(self, line_width, colour, marker_style, marker_size): @@ -376,8 +376,7 @@ def _render_mesh(self, line_width, colour, marker_style, marker_size): colour = _parse_colour(colour) widg_to_draw = super(K3dwidgetsTriMeshViewer3d, self)._render() - mesh_to_add = k3d_mesh(self.points.astype(np.float32), - self.trilist.flatten().astype(np.uint32), + mesh_to_add = k3d_mesh(self.points, self.trilist.flatten(), flat_shading=False, color=colour, side='double') widg_to_draw += mesh_to_add @@ -394,10 +393,12 @@ def _render(self, line_width=2, colour='r', widg_to_draw = self._render_mesh(line_width, colour, marker_style, marker_size) if normals is not None: - K3dwidgetsVectorViewer3d(self.figure_id, False, - self.points, normals)._render(colour=normals_colour, - line_width=normals_line_width, - marker_size=normals_marker_size) + tmp_normals_widget = K3dwidgetsVectorViewer3d(self.figure_id, + False, self.points, + normals) + tmp_normals_widget._render(colour=normals_colour, + line_width=normals_line_width, + marker_size=normals_marker_size) return widg_to_draw @@ -640,11 +641,11 @@ def __init__(self, figure_id, new_figure, points, trilist, raise MenpowidgetsMissingError(e) self.figure_id = _check_figure_id(self, figure_id, new_figure) self.new_figure = new_figure - self.points = points - self.trilist = trilist - self.components = components - self.eigenvalues = eigenvalues - self.n_parameters = n_parameters + self.points = points.astype(np.float32) + self.trilist = trilist.astype(np.uint32) + self.components = components.astype(np.float32) + self.eigenvalues = eigenvalues.astype(np.float32) + self.n_parameters = n_parameters.astype(np.float32) self.landmarks_indices = landmarks_indices self.layout = Layout(grid_template_columns='1fr 1fr') self.wid = LinearModelParametersWidget(n_parameters=n_parameters, @@ -664,17 +665,17 @@ def _render_mesh(self, mesh_type, line_width, colour, marker_size, marker_size = _parse_marker_size(marker_size, self.points) colour = _parse_colour(colour) - mesh_to_add = k3d_mesh(self.points.astype(np.float32), - self.trilist.flatten().astype(np.uint32), + mesh_to_add = k3d_mesh(self.points, self.trilist.flatten(), flat_shading=False, color=colour, name='Instance', side='double') self.mesh_window += mesh_to_add if self.landmarks_indices is not None: - landmarks_to_add = k3d_points(self.points[self.landmarks_indices].astype(np.float32), + landmarks_to_add = k3d_points(self.points[self.landmarks_indices], color=0x00FF00, name='landmarks', - point_size=marker_size, shader='mesh') + point_size=marker_size, + shader='mesh') self.mesh_window += landmarks_to_add return self From b58fb5afdc72f5634b37562552380b3a2060aebf Mon Sep 17 00:00:00 2001 From: Thanos Date: Mon, 12 Oct 2020 16:38:47 +0000 Subject: [PATCH 21/43] Add ColouredTriMeshInlineViewer3d. TextureTriMesh can be rendered now without texture. Small bugs fixed --- menpo3d/visualize/__init__.py | 1 + menpo3d/visualize/base.py | 2 + menpo3d/visualize/viewk3dwidgets.py | 65 +++++++++++++---------------- 3 files changed, 32 insertions(+), 36 deletions(-) diff --git a/menpo3d/visualize/__init__.py b/menpo3d/visualize/__init__.py index 1263431..107ee31 100644 --- a/menpo3d/visualize/__init__.py +++ b/menpo3d/visualize/__init__.py @@ -1,6 +1,7 @@ from .base import (PointGraphViewer3d, TriMeshViewer3d, VectorViewer3d, ColouredTriMeshViewer3d, TexturedTriMeshViewer3d, LandmarkViewer3d, TriMeshInlineViewer3d, + ColouredTriMeshInlineViewer3d, PointGraphInlineViewer3d, TexturedTriMeshInlineViewer3d, LandmarkInlineViewer3d, PointGraphInlineViewer3d, VectorInlineViewer3d, HeatmapInlineViewer3d, diff --git a/menpo3d/visualize/base.py b/menpo3d/visualize/base.py index ea09694..6b00eaf 100644 --- a/menpo3d/visualize/base.py +++ b/menpo3d/visualize/base.py @@ -11,6 +11,7 @@ K3dwidgetsVectorViewer3d, K3dwidgetsLandmarkViewer3d, K3dwidgetsTexturedTriMeshViewer3d, + K3dwidgetsColouredTriMeshViewer3d, K3dwidgetsHeatmapViewer3d, K3dwidgetsPCAModelViewer3d) @@ -29,3 +30,4 @@ VectorInlineViewer3d = K3dwidgetsVectorViewer3d HeatmapInlineViewer3d = K3dwidgetsHeatmapViewer3d PCAModelInlineViewer3d = K3dwidgetsPCAModelViewer3d +ColouredTriMeshInlineViewer3d = K3dwidgetsColouredTriMeshViewer3d diff --git a/menpo3d/visualize/viewk3dwidgets.py b/menpo3d/visualize/viewk3dwidgets.py index a9a9e0a..685a6a0 100644 --- a/menpo3d/visualize/viewk3dwidgets.py +++ b/menpo3d/visualize/viewk3dwidgets.py @@ -445,26 +445,23 @@ def _render_mesh(self, mesh_type='surface', ambient_light=0.0, return widg_to_draw - def _render(self, mesh_type='surface', ambient_light=0.0, - specular_light=0.0, normals=None, normals_colour='k', - normals_line_width=2, normals_marker_style='2darrow', - normals_marker_resolution=8, normals_marker_size=None, - step=None, alpha=1.0): + def _render(self, normals=None, normals_colour='k', + normals_line_width=2, normals_marker_size=None): if normals is not None: - K3dwidgetsVectorViewer3d(self.figure_id, False, - self.points, normals).render( - colour=normals_colour, line_width=normals_line_width, step=step, - marker_style=normals_marker_style, - marker_resolution=normals_marker_resolution, - marker_size=normals_marker_size, alpha=alpha) + tmp_normals_widget = K3dwidgetsVectorViewer3d(self.figure_id, + False, self.points, + normals) + tmp_normals_widget._render(colour=normals_colour, + line_width=normals_line_width, + marker_size=normals_marker_size) - self._render_mesh(mesh_type=mesh_type, ambient_light=ambient_light, - specular_light=specular_light, alpha=alpha) + self._render_mesh() return self class K3dwidgetsColouredTriMeshViewer3d(K3dwidgetsRenderer): + # TODO def __init__(self, figure_id, new_figure, points, trilist, colour_per_point): super(K3dwidgetsColouredTriMeshViewer3d, self).__init__(figure_id, @@ -472,27 +469,24 @@ def __init__(self, figure_id, new_figure, points, trilist, self.points = points self.trilist = trilist self.colour_per_point = colour_per_point - self._actors = [] + self.colorbar_object_id = False - def _render_mesh(self, mesh_type='surface', ambient_light=0.0, - specular_light=0.0, alpha=1.0): - from tvtk.api import tvtk - pd = tvtk.PolyData() - pd.points = self.points - pd.polys = self.trilist - pd.point_data.scalars = (self.colour_per_point * 255.).astype(np.uint8) - mapper = tvtk.PolyDataMapper() - mapper.set_input_data(pd) - p = tvtk.Property(representation=mesh_type, opacity=alpha, - ambient=ambient_light, specular=specular_light) - actor = tvtk.Actor(mapper=mapper, property=p) - self.figure.scene.add_actors(actor) - self._actors.append(actor) - - def render(self, mesh_type='surface', ambient_light=0.0, specular_light=0.0, - normals=None, normals_colour='k', normals_line_width=2, - normals_marker_style='2darrow', normals_marker_resolution=8, - normals_marker_size=None, step=None, alpha=1.0): + def _render_mesh(self): + widg_to_draw = super(K3dwidgetsColouredTriMeshViewer3d, self)._render() + + mesh_to_add = k3d_mesh(self.points.astype(np.float32), + self.trilist.flatten().astype(np.uint32), + attribute=self.colour_per_point, + ) + widg_to_draw += mesh_to_add + + #if hasattr(self.landmarks, 'points'): + # self.landmarks.view(inline=True, new_figure=False, + # figure_id=self.figure_id) + + + def _render(self, normals=None, normals_colour='k', normals_line_width=2, + normals_marker_size=None): if normals is not None: K3dwidgetsVectorViewer3d(self.figure_id, False, self.points, normals).render( @@ -500,8 +494,7 @@ def render(self, mesh_type='surface', ambient_light=0.0, specular_light=0.0, marker_style=normals_marker_style, marker_resolution=normals_marker_resolution, marker_size=normals_marker_size, alpha=alpha) - self._render_mesh(mesh_type=mesh_type, ambient_light=ambient_light, - specular_light=specular_light, alpha=alpha) + self._render_mesh() return self @@ -645,7 +638,7 @@ def __init__(self, figure_id, new_figure, points, trilist, self.trilist = trilist.astype(np.uint32) self.components = components.astype(np.float32) self.eigenvalues = eigenvalues.astype(np.float32) - self.n_parameters = n_parameters.astype(np.float32) + self.n_parameters = n_parameters self.landmarks_indices = landmarks_indices self.layout = Layout(grid_template_columns='1fr 1fr') self.wid = LinearModelParametersWidget(n_parameters=n_parameters, From 04892b7134fa4d2fe2e12285f6ffe79002af955e Mon Sep 17 00:00:00 2001 From: Thanos Date: Tue, 13 Oct 2020 10:15:11 +0000 Subject: [PATCH 22/43] Add one more check for plotting an inline figure. If new_figure is False and the figure_id does not exist, then we raise an exception --- menpo3d/visualize/viewk3dwidgets.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/menpo3d/visualize/viewk3dwidgets.py b/menpo3d/visualize/viewk3dwidgets.py index 685a6a0..892b2c7 100644 --- a/menpo3d/visualize/viewk3dwidgets.py +++ b/menpo3d/visualize/viewk3dwidgets.py @@ -129,6 +129,9 @@ def __init__(self, figure_id, new_figure): self.figure_id = _check_figure_id(self, figure_id, new_figure) self.new_figure = new_figure self.grid_visible = False + self.camera = [-0.02, -0.12, 3.32, + 0.00, -0.16, 0.58, + 0.02, 1.00, 0.04] def _render(self): widg_to_draw = self @@ -138,6 +141,9 @@ def _render(self): if widg.figure_id == self.figure_id and widg.model_id != self.model_id and widg.new_figure: widg_to_draw = widg return widg_to_draw + self.remove_widget() + raise Exception('Figure with id {} was not found '.format(self.figure_id)) + return widg_to_draw def remove_widget(self): @@ -149,9 +155,6 @@ def remove_widget(self): self._repr_mimebundle_ = None # TODO # Why the following atributes don't change - self.camera = [-0.02, -0.12, 3.32, - 0.00, -0.16, 0.58, - 0.02, 1.00, 0.04] def get_figure(self): r""" @@ -484,7 +487,6 @@ def _render_mesh(self): # self.landmarks.view(inline=True, new_figure=False, # figure_id=self.figure_id) - def _render(self, normals=None, normals_colour='k', normals_line_width=2, normals_marker_size=None): if normals is not None: From 62d3d52562b790946fb538d4098ddeb07bc66ab4 Mon Sep 17 00:00:00 2001 From: Thanos Date: Tue, 13 Oct 2020 10:16:43 +0000 Subject: [PATCH 23/43] Update Introduction with more examples for failure --- examples/0_Introduction_to_K3d_Widgets.ipynb | 384 +++++++++++++++---- 1 file changed, 308 insertions(+), 76 deletions(-) diff --git a/examples/0_Introduction_to_K3d_Widgets.ipynb b/examples/0_Introduction_to_K3d_Widgets.ipynb index ddb106f..5a6377f 100644 --- a/examples/0_Introduction_to_K3d_Widgets.ipynb +++ b/examples/0_Introduction_to_K3d_Widgets.ipynb @@ -2,13 +2,13 @@ "cells": [ { "cell_type": "code", - "execution_count": null, + "execution_count": 30, "metadata": {}, "outputs": [], "source": [ "import menpo3d.io as m3io\n", "import menpo.io as mio\n", - "from menpo.shape import PointCloud\n", + "from menpo.shape import PointCloud, ColouredTriMesh\n", "from menpo.landmark import face_ibug_68_to_face_ibug_68\n", "import numpy as np" ] @@ -22,7 +22,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "metadata": {}, "outputs": [], "source": [ @@ -49,7 +49,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 21, "metadata": {}, "outputs": [], "source": [ @@ -69,11 +69,26 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 6, "metadata": { "scrolled": false }, - "outputs": [], + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "a5fe810bb84d49ceb32e27429e4a3ef8", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "K3dwidgetsTexturedTriMeshViewer3d(antialias=3, axes=['x', 'y', 'z'], axes_helper=1.0, background_color=1677721…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "# Default values for TriMesh, TextureMesh viewer are\n", "# figure_id None\n", @@ -81,7 +96,7 @@ "# in that case an automatic figure_id will be given\n", "# with 'Figure_{n}' format\n", "# n will be an increased integer starting from zero\n", - "mesh.view(inline=True,) # wait a bit before magic happens" + "mesh.view(inline=True) # wait a bit before magic happens" ] }, { @@ -93,18 +108,33 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 4, "metadata": { "scrolled": false }, - "outputs": [], + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "9e03f85fa6f74d75b24532f05d0a3a9d", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "K3dwidgetsTexturedTriMeshViewer3d(antialias=3, axes=['x', 'y', 'z'], axes_helper=1.0, background_color=1677721…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "mesh.view(inline=True, figure_id='James')" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 7, "metadata": {}, "outputs": [], "source": [ @@ -115,7 +145,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 8, "metadata": {}, "outputs": [], "source": [ @@ -132,7 +162,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 9, "metadata": {}, "outputs": [], "source": [ @@ -141,18 +171,48 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 10, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "f566fcbda8084286953ca39cf64de2c8", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "K3dwidgetsLandmarkViewer3d(antialias=3, axes=['x', 'y', 'z'], axes_helper=1.0, background_color=16777215, came…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "lms.view(inline=True, new_figure=True, render_numbering=True)" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 11, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "c5c1db3ea2ea43799656ae81ed933fdc", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "K3dwidgetsTexturedTriMeshViewer3d(antialias=3, axes=['x', 'y', 'z'], axes_helper=1.0, background_color=1677721…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "# The mesh has now landmarks, so they would be plotted as well\n", "# the figure id is now Figure_2\n", @@ -168,9 +228,24 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 14, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "19c45e65f1be427dbb8ef1c4167d2d7d", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "K3dwidgetsHeatmapViewer3d(antialias=3, axes=['x', 'y', 'z'], axes_helper=1.0, background_color=16777215, camer…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "# Heatmap between a random mesh and mean mesh\n", "random_mesh.heatmap(model_mean, inline=True)" @@ -178,9 +253,24 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 15, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "93e3e8a47b574afa9cfb4a27b1197e98", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "K3dwidgetsHeatmapViewer3d(antialias=3, axes=['x', 'y', 'z'], axes_helper=1.0, background_color=16777215, camer…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "# Heatmap with statistics \n", "# Be careful, since we have already drawn a heatmap between\n", @@ -190,9 +280,24 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 16, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "5d9af15ad323436b88068e7f7ee27250", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "K3dwidgetsHeatmapViewer3d(antialias=3, axes=['x', 'y', 'z'], axes_helper=1.0, background_color=16777215, camer…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "# Heatmap with landmarks\n", "random_mesh.landmarks = face_ibug_68_to_face_ibug_68(PointCloud(random_mesh.points[lms_indices]))\n", @@ -208,58 +313,135 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 17, "metadata": {}, "outputs": [], "source": [ "pts = random_mesh.points[lms_indices]\n", "vrt = np.zeros((random_mesh.n_points,3))\n", - "vrt[lms_indices] = random_mesh.vertex_normals()[lms_indices]" + "vrt[lms_indices] = random_mesh.vertex_normals()[lms_indices] / 5" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 23, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "44077fc658924ffbbdc3865ccce32de2", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "K3dwidgetsTriMeshViewer3d(antialias=3, axes=['x', 'y', 'z'], axes_helper=1.0, background_color=16777215, camer…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ - "vrt_ = vrt[lms_indices]" + "random_mesh.view(inline=True, normals=vrt, \n", + " normals_marker_size= 0.5,\n", + " normals_line_width = 0.01,\n", + " figure_id='Normals')" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 22, "metadata": {}, "outputs": [], "source": [ - "random_mesh.view(inline=True, normals=vrt, \n", - " normals_line_width = 0.01,\n", - " figure_id='Normals')" + "random_mesh_landmarks = face_ibug_68_to_face_ibug_68(random_mesh.points[lms_indices])" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 24, "metadata": {}, "outputs": [], "source": [ - "random_mesh.landmarks = face_ibug_68_to_face_ibug_68(random_mesh.points[lms_indices])" + "random_mesh_landmarks.view(inline=True, figure_id='Normals', new_figure=False)" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 25, + "metadata": { + "scrolled": false + }, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "c51bd921707d469fab10ee45632b3ebc", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "K3dwidgetsTriMeshViewer3d(antialias=3, axes=['x', 'y', 'z'], axes_helper=1.0, background_color=16777215, camer…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "random_mesh.landmarks = random_mesh_landmarks\n", + "random_mesh.view(inline=True, normals=vrt, \n", + " normals_marker_size= 0.5,\n", + " normals_line_width = 0.01)" + ] + }, + { + "cell_type": "markdown", "metadata": {}, - "outputs": [], "source": [ - "random_mesh.landmarks.view(inline=True, figure_id='Normals', new_figure=False)" + "

Show Surface

" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "

Show Surface

" + "

Show ColouredTriMesh

\n" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": {}, + "outputs": [], + "source": [ + "colors = np.random.rand(random_mesh.n_points)" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "ce7a7573afda4c97ba8c23cabddbda00", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "K3dwidgetsColouredTriMeshViewer3d(antialias=3, axes=['x', 'y', 'z'], axes_helper=1.0, background_color=1677721…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "new_mesh = ColouredTriMesh(random_mesh.points, random_mesh.trilist, colours=colors)\n", + "new_mesh.view(inline=True)" ] }, { @@ -271,7 +453,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 35, "metadata": {}, "outputs": [], "source": [ @@ -292,18 +474,48 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 36, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "c156658475d54fb6b6421ee105fc4f7d", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "K3dwidgetsPointGraphViewer3d(antialias=3, axes=['x', 'y', 'z'], axes_helper=1.0, background_color=16777215, ca…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "graph.view(inline=True)" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 37, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "460de629759b4aab8b6059ce88d58e19", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "K3dwidgetsPointGraphViewer3d(antialias=3, axes=['x', 'y', 'z'], axes_helper=1.0, background_color=16777215, ca…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "graph.view(inline=True, line_colour=colors, render_numbering=True)" ] @@ -317,20 +529,58 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 38, "metadata": { "scrolled": false }, - "outputs": [], + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "08b762604afc44979ac836789c5078c0", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "K3dwidgetsPCAModelViewer3d(children=(LinearModelParametersWidget(children=(VBox(box_style='info', children=(VB…" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/athanasiosp/miniconda3/envs/my_menpo/lib/python3.5/site-packages/traittypes/traittypes.py:101: UserWarning: Given trait value dtype \"float64\" does not match required type \"float32\". A coerced copy has been created.\n", + " np.dtype(self.dtype).name))\n" + ] + } + ], "source": [ "model.view(inline=True, figure_id='Model', n_parameters=10, landmarks_indices=lms_indices)" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 39, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "4ca4b7de1633432bba050bdc6168a3b8", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "K3dwidgetsPCAModelViewer3d(children=(LinearModelParametersWidget(children=(VBox(children=(VBox(children=(HBox(…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "model.view(widget_style='')" ] @@ -383,7 +633,18 @@ "outputs": [], "source": [ "# You have already created a heatmap between random_mesh and model_mean\n", - "random_mesh.heatmap(model_mean, inline=True)" + "random_mesh.heatmap(model_mean, inline=True)`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# If you draw a new figure, the command should be the last one in the cell, preferably on its own cell\n", + "lms.view(inline=True, new_figure=True)\n", + "print('Hello World')\n" ] }, { @@ -458,35 +719,6 @@ "source": [ "list_figures()" ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import k3d" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "\n", - "origins = lms.points\n", - "vectors2 = origins+.5\n", - "colors = [0xff0000, 0x0000ff, 0x0000ff, 0xff0000, 0x0000ff, 0xff0000]\n", - "\n", - "plot = k3d.plot()\n", - "vectors = k3d.vectors(origins, vectors2, head_size=100,)#, colors=colors, labels=[], label_size=1.5)\n", - "\n", - "plot += vectors\n", - "plot += k3d.points(origins, point_size=10, color=100)\n", - "\n", - "plot.display()" - ] } ], "metadata": { From 8354ceee65ab13d4ee9afeae6d7850f2ebd88df2 Mon Sep 17 00:00:00 2001 From: Thanos Date: Fri, 16 Oct 2020 16:30:13 +0000 Subject: [PATCH 24/43] Add landmark support in K3dwidgetsColouredTriMeshViewer3d. List of selected_values is casted as numpy.arrays with type float32 in render_function in K3dwidgetsPCAModelViewer3d --- menpo3d/visualize/viewk3dwidgets.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/menpo3d/visualize/viewk3dwidgets.py b/menpo3d/visualize/viewk3dwidgets.py index 892b2c7..0d86cc5 100644 --- a/menpo3d/visualize/viewk3dwidgets.py +++ b/menpo3d/visualize/viewk3dwidgets.py @@ -466,13 +466,14 @@ def _render(self, normals=None, normals_colour='k', class K3dwidgetsColouredTriMeshViewer3d(K3dwidgetsRenderer): # TODO def __init__(self, figure_id, new_figure, points, trilist, - colour_per_point): + colour_per_point, landmarks): super(K3dwidgetsColouredTriMeshViewer3d, self).__init__(figure_id, new_figure) self.points = points self.trilist = trilist self.colour_per_point = colour_per_point self.colorbar_object_id = False + self.landmarks = landmarks def _render_mesh(self): widg_to_draw = super(K3dwidgetsColouredTriMeshViewer3d, self)._render() @@ -483,12 +484,12 @@ def _render_mesh(self): ) widg_to_draw += mesh_to_add - #if hasattr(self.landmarks, 'points'): - # self.landmarks.view(inline=True, new_figure=False, - # figure_id=self.figure_id) + if hasattr(self.landmarks, 'points'): + self.landmarks.view(inline=True, new_figure=False, + figure_id=self.figure_id) def _render(self, normals=None, normals_colour='k', normals_line_width=2, - normals_marker_size=None): + normals_marker_size=None): if normals is not None: K3dwidgetsVectorViewer3d(self.figure_id, False, self.points, normals).render( @@ -634,6 +635,7 @@ def __init__(self, figure_id, new_figure, points, trilist, except ImportError as e: from menpo.visualize import MenpowidgetsMissingError raise MenpowidgetsMissingError(e) + self.figure_id = _check_figure_id(self, figure_id, new_figure) self.new_figure = new_figure self.points = points.astype(np.float32) @@ -675,7 +677,11 @@ def _render_mesh(self, mesh_type, line_width, colour, marker_size, return self def render_function(self, change): - mesh = self.points + (self.components[:self.n_parameters, :].T@(self.wid.selected_values*self.eigenvalues[:self.n_parameters]**0.5)).reshape(-1, 3) + weights = np.asarray(self.wid.selected_values).astype(np.float32) + weighted_eigenvalues = weights * self.eigenvalues[:self.n_parameters]**0.5 + new_instance = (self.components[:self.n_parameters, :].T@weighted_eigenvalues).reshape(-1, 3) + mesh = self.points + new_instance + self.mesh_window.objects[0].vertices = mesh if self.landmarks_indices is not None: self.mesh_window.objects[1].positions = mesh[self.landmarks_indices] From 933a4f94097b457727249bf37e7b9e4a7a8335ee Mon Sep 17 00:00:00 2001 From: Thanos Date: Wed, 18 Nov 2020 11:55:52 +0000 Subject: [PATCH 25/43] Update 0_Introduction_to_K3d_Widgets --- examples/0_Introduction_to_K3d_Widgets.ipynb | 420 ++++++++----------- 1 file changed, 164 insertions(+), 256 deletions(-) diff --git a/examples/0_Introduction_to_K3d_Widgets.ipynb b/examples/0_Introduction_to_K3d_Widgets.ipynb index 5a6377f..612da32 100644 --- a/examples/0_Introduction_to_K3d_Widgets.ipynb +++ b/examples/0_Introduction_to_K3d_Widgets.ipynb @@ -2,9 +2,18 @@ "cells": [ { "cell_type": "code", - "execution_count": 30, + "execution_count": 1, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/athanasiosp/gits/menpo/menpo/image/base.py:32: UserWarning: Falling back to scipy interpolation for affine warps\n", + " warn(\"Falling back to scipy interpolation for affine warps\")\n" + ] + } + ], "source": [ "import menpo3d.io as m3io\n", "import menpo.io as mio\n", @@ -49,7 +58,7 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 3, "metadata": {}, "outputs": [], "source": [ @@ -69,7 +78,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 4, "metadata": { "scrolled": false }, @@ -77,7 +86,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "a5fe810bb84d49ceb32e27429e4a3ef8", + "model_id": "dc863eb9632a4b229e016d1fc0703d82", "version_major": 2, "version_minor": 0 }, @@ -108,35 +117,35 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": null, "metadata": { "scrolled": false }, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "9e03f85fa6f74d75b24532f05d0a3a9d", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "K3dwidgetsTexturedTriMeshViewer3d(antialias=3, axes=['x', 'y', 'z'], axes_helper=1.0, background_color=1677721…" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ - "mesh.view(inline=True, figure_id='James')" + "mesh.view(inline=True, figure_id='James2')" ] }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 6, "metadata": {}, - "outputs": [], + "outputs": [ + { + "ename": "Exception", + "evalue": "Figure with id James was not found ", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mException\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0;31m# Add landmarks to figure with id James\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[0mlms_poincloud\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mPointCloud\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mlms\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mpoints\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 3\u001b[0;31m \u001b[0mlms_poincloud\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mview\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0minline\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mTrue\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mfigure_id\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m'James'\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mnew_figure\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mFalse\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m/home/athanasiosp/gits/menpo/menpo/shape/pointcloud.py\u001b[0m in \u001b[0;36m_view_3d\u001b[0;34m(self, figure_id, new_figure, render_markers, marker_style, marker_size, marker_colour, marker_resolution, step, alpha, render_numbering, numbers_colour, numbers_size, inline, **kwargs)\u001b[0m\n\u001b[1;32m 1164\u001b[0m \u001b[0mrender_numbering\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mrender_numbering\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1165\u001b[0m \u001b[0mnumbers_colour\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mnumbers_colour\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1166\u001b[0;31m \u001b[0mnumbers_size\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mnumbers_size\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1167\u001b[0m )\n\u001b[1;32m 1168\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mrender_return\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0mrenderer\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/home/athanasiosp/gits/menpo3d/menpo3d/visualize/viewk3dwidgets.py\u001b[0m in \u001b[0;36m_render\u001b[0;34m(self, render_lines, line_colour, line_width, render_markers, marker_style, marker_size, marker_colour, render_numbering, numbers_colour, numbers_size)\u001b[0m\n\u001b[1;32m 314\u001b[0m numbers_colour='k', numbers_size=None):\n\u001b[1;32m 315\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 316\u001b[0;31m \u001b[0mwidg_to_draw\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0msuper\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mK3dwidgetsPointGraphViewer3d\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_render\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 317\u001b[0m \u001b[0;31m# Render the lines if requested\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 318\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mrender_lines\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/home/athanasiosp/gits/menpo3d/menpo3d/visualize/viewk3dwidgets.py\u001b[0m in \u001b[0;36m_render\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 143\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mwidg_to_draw\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 144\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mremove_widget\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 145\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mException\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'Figure with id {} was not found '\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mformat\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfigure_id\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 146\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 147\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mwidg_to_draw\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mException\u001b[0m: Figure with id James was not found " + ] + } + ], "source": [ "# Add landmarks to figure with id James\n", "lms_poincloud = PointCloud(lms.points)\n", @@ -145,7 +154,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 7, "metadata": {}, "outputs": [], "source": [ @@ -162,7 +171,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -171,52 +180,32 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "f566fcbda8084286953ca39cf64de2c8", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "K3dwidgetsLandmarkViewer3d(antialias=3, axes=['x', 'y', 'z'], axes_helper=1.0, background_color=16777215, came…" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "lms.view(inline=True, new_figure=True, render_numbering=True)" ] }, { "cell_type": "code", - "execution_count": 11, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "c5c1db3ea2ea43799656ae81ed933fdc", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "K3dwidgetsTexturedTriMeshViewer3d(antialias=3, axes=['x', 'y', 'z'], axes_helper=1.0, background_color=1677721…" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "# The mesh has now landmarks, so they would be plotted as well\n", "# the figure id is now Figure_2\n", - "mesh.view(inline=True)" + "mesh.view(inline=True,)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Show the TexturedMesh without texture\n", + "mesh.view(inline=True, render_texture=False)" ] }, { @@ -228,24 +217,9 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "19c45e65f1be427dbb8ef1c4167d2d7d", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "K3dwidgetsHeatmapViewer3d(antialias=3, axes=['x', 'y', 'z'], axes_helper=1.0, background_color=16777215, camer…" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "# Heatmap between a random mesh and mean mesh\n", "random_mesh.heatmap(model_mean, inline=True)" @@ -253,24 +227,9 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "93e3e8a47b574afa9cfb4a27b1197e98", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "K3dwidgetsHeatmapViewer3d(antialias=3, axes=['x', 'y', 'z'], axes_helper=1.0, background_color=16777215, camer…" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "# Heatmap with statistics \n", "# Be careful, since we have already drawn a heatmap between\n", @@ -280,28 +239,23 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "5d9af15ad323436b88068e7f7ee27250", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "K3dwidgetsHeatmapViewer3d(antialias=3, axes=['x', 'y', 'z'], axes_helper=1.0, background_color=16777215, camer…" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "# Heatmap with landmarks\n", - "random_mesh.landmarks = face_ibug_68_to_face_ibug_68(PointCloud(random_mesh.points[lms_indices]))\n", - "random_mesh.heatmap(model_mean, inline=True, show_statistics=True, figure_id='Heatmap3')" + "model_mean.landmarks = face_ibug_68_to_face_ibug_68(PointCloud(model_mean.points[lms_indices]))\n", + "model_mean.heatmap(random_mesh, inline=True, show_statistics=True, figure_id='Heatmap3')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "random_mesh_landmarks = face_ibug_68_to_face_ibug_68(random_mesh.points[lms_indices])\n", + "random_mesh_landmarks.view(inline=True, new_figure=False, figure_id='Heatmap2')" ] }, { @@ -313,7 +267,7 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -324,24 +278,9 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "44077fc658924ffbbdc3865ccce32de2", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "K3dwidgetsTriMeshViewer3d(antialias=3, axes=['x', 'y', 'z'], axes_helper=1.0, background_color=16777215, camer…" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "random_mesh.view(inline=True, normals=vrt, \n", " normals_marker_size= 0.5,\n", @@ -351,16 +290,7 @@ }, { "cell_type": "code", - "execution_count": 22, - "metadata": {}, - "outputs": [], - "source": [ - "random_mesh_landmarks = face_ibug_68_to_face_ibug_68(random_mesh.points[lms_indices])" - ] - }, - { - "cell_type": "code", - "execution_count": 24, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -369,26 +299,11 @@ }, { "cell_type": "code", - "execution_count": 25, + "execution_count": null, "metadata": { "scrolled": false }, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "c51bd921707d469fab10ee45632b3ebc", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "K3dwidgetsTriMeshViewer3d(antialias=3, axes=['x', 'y', 'z'], axes_helper=1.0, background_color=16777215, camer…" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "random_mesh.landmarks = random_mesh_landmarks\n", "random_mesh.view(inline=True, normals=vrt, \n", @@ -412,7 +327,7 @@ }, { "cell_type": "code", - "execution_count": 28, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -421,29 +336,38 @@ }, { "cell_type": "code", - "execution_count": 34, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "ce7a7573afda4c97ba8c23cabddbda00", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "K3dwidgetsColouredTriMeshViewer3d(antialias=3, axes=['x', 'y', 'z'], axes_helper=1.0, background_color=1677721…" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], + "source": [ + "colors[0:1000]= 0.1\n", + "colors[1000:10000]= 0.2\n", + "colors[1000:10000]= 0.4\n", + "colors[10000:30000]= 0.6\n", + "colors[30000:40000]= 0.8\n", + "colors[40000:50000]= 0.8\n", + "colors[50000:]= 1" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "new_mesh = ColouredTriMesh(random_mesh.points, random_mesh.trilist, colours=colors)\n", "new_mesh.view(inline=True)" ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "new_mesh.landmarks = random_mesh_landmarks" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -453,7 +377,7 @@ }, { "cell_type": "code", - "execution_count": 35, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -474,48 +398,18 @@ }, { "cell_type": "code", - "execution_count": 36, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "c156658475d54fb6b6421ee105fc4f7d", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "K3dwidgetsPointGraphViewer3d(antialias=3, axes=['x', 'y', 'z'], axes_helper=1.0, background_color=16777215, ca…" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "graph.view(inline=True)" ] }, { "cell_type": "code", - "execution_count": 37, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "460de629759b4aab8b6059ce88d58e19", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "K3dwidgetsPointGraphViewer3d(antialias=3, axes=['x', 'y', 'z'], axes_helper=1.0, background_color=16777215, ca…" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "graph.view(inline=True, line_colour=colors, render_numbering=True)" ] @@ -529,62 +423,76 @@ }, { "cell_type": "code", - "execution_count": 38, + "execution_count": null, "metadata": { "scrolled": false }, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "08b762604afc44979ac836789c5078c0", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "K3dwidgetsPCAModelViewer3d(children=(LinearModelParametersWidget(children=(VBox(box_style='info', children=(VB…" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/home/athanasiosp/miniconda3/envs/my_menpo/lib/python3.5/site-packages/traittypes/traittypes.py:101: UserWarning: Given trait value dtype \"float64\" does not match required type \"float32\". A coerced copy has been created.\n", - " np.dtype(self.dtype).name))\n" - ] - } - ], + "outputs": [], "source": [ "model.view(inline=True, figure_id='Model', n_parameters=10, landmarks_indices=lms_indices)" ] }, { "cell_type": "code", - "execution_count": 39, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "4ca4b7de1633432bba050bdc6168a3b8", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "K3dwidgetsPCAModelViewer3d(children=(LinearModelParametersWidget(children=(VBox(children=(VBox(children=(HBox(…" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "model.view(widget_style='')" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

Load big meshes

" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "mesh = m3io.import_mesh('/data/meshes/mesh/33_plain.obj')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "mesh.view(inline=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "mesh_hq = m3io.import_mesh('/data/meshes/mesh/HQ_mesh_alex.obj')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "mesh_hq" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "mesh_hq.view(inline=True)" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -644,7 +552,7 @@ "source": [ "# If you draw a new figure, the command should be the last one in the cell, preferably on its own cell\n", "lms.view(inline=True, new_figure=True)\n", - "print('Hello World')\n" + "print('Hello World')" ] }, { From a65fb09c82af87e75acc6b591b14daba28cfef82 Mon Sep 17 00:00:00 2001 From: Thanos Date: Fri, 20 Nov 2020 10:12:04 +0000 Subject: [PATCH 26/43] Update 0_Introduction_to_K3d_Widgets.ipynb --- examples/0_Introduction_to_K3d_Widgets.ipynb | 63 ++++---------------- 1 file changed, 12 insertions(+), 51 deletions(-) diff --git a/examples/0_Introduction_to_K3d_Widgets.ipynb b/examples/0_Introduction_to_K3d_Widgets.ipynb index 612da32..50342f8 100644 --- a/examples/0_Introduction_to_K3d_Widgets.ipynb +++ b/examples/0_Introduction_to_K3d_Widgets.ipynb @@ -2,18 +2,9 @@ "cells": [ { "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/home/athanasiosp/gits/menpo/menpo/image/base.py:32: UserWarning: Falling back to scipy interpolation for affine warps\n", - " warn(\"Falling back to scipy interpolation for affine warps\")\n" - ] - } - ], + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "import menpo3d.io as m3io\n", "import menpo.io as mio\n", @@ -31,7 +22,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -58,7 +49,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -78,26 +69,11 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": null, "metadata": { "scrolled": false }, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "dc863eb9632a4b229e016d1fc0703d82", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "K3dwidgetsTexturedTriMeshViewer3d(antialias=3, axes=['x', 'y', 'z'], axes_helper=1.0, background_color=1677721…" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "# Default values for TriMesh, TextureMesh viewer are\n", "# figure_id None\n", @@ -123,29 +99,14 @@ }, "outputs": [], "source": [ - "mesh.view(inline=True, figure_id='James2')" + "mesh.view(inline=True, figure_id='James')" ] }, { "cell_type": "code", - "execution_count": 6, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "ename": "Exception", - "evalue": "Figure with id James was not found ", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mException\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0;31m# Add landmarks to figure with id James\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[0mlms_poincloud\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mPointCloud\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mlms\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mpoints\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 3\u001b[0;31m \u001b[0mlms_poincloud\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mview\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0minline\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mTrue\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mfigure_id\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m'James'\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mnew_figure\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mFalse\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", - "\u001b[0;32m/home/athanasiosp/gits/menpo/menpo/shape/pointcloud.py\u001b[0m in \u001b[0;36m_view_3d\u001b[0;34m(self, figure_id, new_figure, render_markers, marker_style, marker_size, marker_colour, marker_resolution, step, alpha, render_numbering, numbers_colour, numbers_size, inline, **kwargs)\u001b[0m\n\u001b[1;32m 1164\u001b[0m \u001b[0mrender_numbering\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mrender_numbering\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1165\u001b[0m \u001b[0mnumbers_colour\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mnumbers_colour\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1166\u001b[0;31m \u001b[0mnumbers_size\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mnumbers_size\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1167\u001b[0m )\n\u001b[1;32m 1168\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mrender_return\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0mrenderer\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/home/athanasiosp/gits/menpo3d/menpo3d/visualize/viewk3dwidgets.py\u001b[0m in \u001b[0;36m_render\u001b[0;34m(self, render_lines, line_colour, line_width, render_markers, marker_style, marker_size, marker_colour, render_numbering, numbers_colour, numbers_size)\u001b[0m\n\u001b[1;32m 314\u001b[0m numbers_colour='k', numbers_size=None):\n\u001b[1;32m 315\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 316\u001b[0;31m \u001b[0mwidg_to_draw\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0msuper\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mK3dwidgetsPointGraphViewer3d\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_render\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 317\u001b[0m \u001b[0;31m# Render the lines if requested\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 318\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mrender_lines\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/home/athanasiosp/gits/menpo3d/menpo3d/visualize/viewk3dwidgets.py\u001b[0m in \u001b[0;36m_render\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 143\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mwidg_to_draw\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 144\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mremove_widget\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 145\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mException\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'Figure with id {} was not found '\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mformat\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfigure_id\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 146\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 147\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mwidg_to_draw\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;31mException\u001b[0m: Figure with id James was not found " - ] - } - ], + "outputs": [], "source": [ "# Add landmarks to figure with id James\n", "lms_poincloud = PointCloud(lms.points)\n", @@ -154,7 +115,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -645,7 +606,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.5.6" + "version": "3.7.7" } }, "nbformat": 4, From 7f743815a0e290c1446bbbe7c495859e0065102e Mon Sep 17 00:00:00 2001 From: Thanos Date: Wed, 25 Nov 2020 11:13:39 +0000 Subject: [PATCH 27/43] Camera_auto_fit is false and the new camera posiition is defined by the distance from the mesh and z-axis is towards us. Ref #4 --- menpo3d/visualize/viewk3dwidgets.py | 62 ++++++++++++++++++++--------- 1 file changed, 44 insertions(+), 18 deletions(-) diff --git a/menpo3d/visualize/viewk3dwidgets.py b/menpo3d/visualize/viewk3dwidgets.py index 0d86cc5..d090ba4 100644 --- a/menpo3d/visualize/viewk3dwidgets.py +++ b/menpo3d/visualize/viewk3dwidgets.py @@ -30,20 +30,25 @@ def clear_figure(figure_id=None): dict_fig = dict_figures() +def _calc_distance(points): + from menpo.shape import PointCloud + pc = PointCloud(points, copy=False) + # This is the way that mayavi automatically computes the scale factor + # in case the user passes scale_factor = 'auto'. We use it for both + # the marker_size as well as the numbers_size. + xyz_min, xyz_max = pc.bounds() + x_min, y_min, z_min = xyz_min + x_max, y_max, z_max = xyz_max + distance = np.sqrt(((x_max - x_min) ** 2 + + (y_max - y_min) ** 2 + + (z_max - z_min) ** 2) / + (4 * pc.n_points ** 0.33)) + return distance + + def _parse_marker_size(marker_size, points): + distance = _calc_distance(points) if marker_size is None: - from menpo.shape import PointCloud - pc = PointCloud(points, copy=False) - # This is the way that mayavi automatically computes the scale factor - # in case the user passes scale_factor = 'auto'. We use it for both - # the marker_size as well as the numbers_size. - xyz_min, xyz_max = pc.bounds() - x_min, y_min, z_min = xyz_min - x_max, y_max, z_max = xyz_max - distance = np.sqrt(((x_max - x_min) ** 2 + - (y_max - y_min) ** 2 + - (z_max - z_min) ** 2) / - (4 * pc.n_points ** 0.33)) if distance == 0: marker_size = 1 else: @@ -81,6 +86,17 @@ def _check_colours_list(render_flag, colours_list, n_objects, error_str): return colours_list +def _calc_camera_position(points): + from menpo.shape import PointCloud + + pc = PointCloud(points, copy=False) + bounds = pc.bounding_box().points + distance = np.max(bounds[1::2] - bounds[::2]) * 2.0 + camera = [0, 0, distance, 0, 0, 0, 0, 1, 0] + + return camera + + def _check_figure_id(obj, figure_id, new_figure): if figure_id is None: if new_figure: @@ -129,9 +145,6 @@ def __init__(self, figure_id, new_figure): self.figure_id = _check_figure_id(self, figure_id, new_figure) self.new_figure = new_figure self.grid_visible = False - self.camera = [-0.02, -0.12, 3.32, - 0.00, -0.16, 0.58, - 0.02, 1.00, 0.04] def _render(self): widg_to_draw = self @@ -403,6 +416,9 @@ def _render(self, line_width=2, colour='r', line_width=normals_line_width, marker_size=normals_marker_size) + widg_to_draw.camera = _calc_camera_position(self.points) + widg_to_draw.camera_auto_fit = False + return widg_to_draw @@ -442,9 +458,8 @@ def _render_mesh(self, mesh_type='surface', ambient_light=0.0, self.landmarks.view(inline=True, new_figure=False, figure_id=self.figure_id) - self.camera = [-0.02, -0.12, 3.32, - 0.00, -0.16, 0.58, - 0.02, 1.00, 0.04] + widg_to_draw.camera = _calc_camera_position(self.points) + widg_to_draw.camera_auto_fit = False return widg_to_draw @@ -487,6 +502,8 @@ def _render_mesh(self): if hasattr(self.landmarks, 'points'): self.landmarks.view(inline=True, new_figure=False, figure_id=self.figure_id) + widg_to_draw.camera = _calc_camera_position(self.points) + widg_to_draw.camera_auto_fit = False def _render(self, normals=None, normals_colour='k', normals_line_width=2, normals_marker_size=None): @@ -567,6 +584,9 @@ def _render(self, render_lines=True, line_colour='r', line_width=2, text_to_add += k3d_text(str(i), position=point, label_box=False) widg_to_draw += text_to_add + # widg_to_draw.camera = _calc_camera_position(pc.points) + # widg_to_draw.camera_auto_fit = False + return widg_to_draw def _build_sub_pointclouds(self): @@ -617,6 +637,9 @@ def _render_mesh(self, distances_between_meshes, type_cmap, widg_to_draw += k3d_text(text, position=text_position, color=0xff0000, size=1) + widg_to_draw.camera = _calc_camera_position(self.points) + widg_to_draw.camera_auto_fit = False + return widg_to_draw def _render(self, distances_between_meshes, type_cmap='hot_r', @@ -674,6 +697,9 @@ def _render_mesh(self, mesh_type, line_width, colour, marker_size, point_size=marker_size, shader='mesh') self.mesh_window += landmarks_to_add + self.mesh_window.camera = _calc_camera_position(self.points) + self.mesh_window.camera_auto_fit = False + return self def render_function(self, change): From 2a4f876a56d99be9f36d758e86f84917b3221bad Mon Sep 17 00:00:00 2001 From: Thanos Date: Thu, 26 Nov 2020 15:36:37 +0000 Subject: [PATCH 28/43] mesh_type argument in view method for trimesh is enabled in K3dwidgets. Default value is wireframe with opacity 0.3. Other option is surface. #7 --- menpo3d/visualize/viewk3dwidgets.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/menpo3d/visualize/viewk3dwidgets.py b/menpo3d/visualize/viewk3dwidgets.py index d090ba4..d5c123c 100644 --- a/menpo3d/visualize/viewk3dwidgets.py +++ b/menpo3d/visualize/viewk3dwidgets.py @@ -387,13 +387,22 @@ def __init__(self, figure_id, new_figure, points, trilist, landmarks=None): self.trilist = trilist.astype(np.uint32) self.landmarks = landmarks - def _render_mesh(self, line_width, colour, marker_style, marker_size): + def _render_mesh(self, line_width, colour, mesh_type, + marker_style, marker_size): marker_size = _parse_marker_size(marker_size, self.points) colour = _parse_colour(colour) widg_to_draw = super(K3dwidgetsTriMeshViewer3d, self)._render() + wireframe = False + opacity = 1.0 + if mesh_type == 'wireframe': + wireframe = True + opacity = 0.3 + mesh_to_add = k3d_mesh(self.points, self.trilist.flatten(), - flat_shading=False, color=colour, side='double') + flat_shading=False, opacity=opacity, + color=colour, wireframe=wireframe, + side='double') widg_to_draw += mesh_to_add if hasattr(self.landmarks, 'points'): @@ -401,12 +410,12 @@ def _render_mesh(self, line_width, colour, marker_style, marker_size): figure_id=self.figure_id) return widg_to_draw - def _render(self, line_width=2, colour='r', + def _render(self, line_width=2, colour='r', mesh_type='surface', marker_style='sphere', marker_size=None, normals=None, normals_colour='k', normals_line_width=2, normals_marker_size=None): - widg_to_draw = self._render_mesh(line_width, colour, + widg_to_draw = self._render_mesh(line_width, colour, mesh_type, marker_style, marker_size) if normals is not None: tmp_normals_widget = K3dwidgetsVectorViewer3d(self.figure_id, From f48b3134440ff6181226854e5334a3ecaa19bada Mon Sep 17 00:00:00 2001 From: Thanos Date: Fri, 27 Nov 2020 19:06:04 +0000 Subject: [PATCH 29/43] Add K3dwidgetIdentity class - K3dwidgetIdentity has a list of figure_ids and a dictionary with keys figure_id and values model_id. - If a figure does not have a figure_id and new_figure is false, then we check the list of figure_ids. If empty, we raise an exception. Otherwise, we plot in the last figure of figure_id. Address in part Issue #6 --- menpo3d/visualize/viewk3dwidgets.py | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/menpo3d/visualize/viewk3dwidgets.py b/menpo3d/visualize/viewk3dwidgets.py index d5c123c..8ef17de 100644 --- a/menpo3d/visualize/viewk3dwidgets.py +++ b/menpo3d/visualize/viewk3dwidgets.py @@ -1,5 +1,4 @@ import numpy as np -from menpo.visualize import Renderer from k3d import (Plot, mesh as k3d_mesh, points as k3d_points, text as k3d_text, vectors as k3d_vectors, line as k3d_line) @@ -117,8 +116,11 @@ def _check_figure_id(obj, figure_id, new_figure): figure_id = 'Figure_0' else: - obj.remove_widget() - raise ValueError('You cannot plot a figure with no id and new figure False') + if len(obj.list_figures_ids): + figure_id = obj.list_figures_ids[-1] + else: + obj.remove_widget() + raise ValueError('You cannot plot a figure with no id and new figure False') else: if new_figure: for x in obj.widgets.values(): @@ -126,10 +128,21 @@ def _check_figure_id(obj, figure_id, new_figure): if x.figure_id == figure_id: obj.remove_widget() raise ValueError('Figure id is already given') + else: + return figure_id + + obj.list_figures_ids.append(figure_id) + if hasattr(obj, 'model_id'): + obj.dict_figure_id_to_model_id[figure_id] = obj.model_id return figure_id -class K3dwidgetsRenderer(Plot, Renderer): +class K3dwidgetIdentity(): + list_figures_ids = [] + dict_figure_id_to_model_id = {} + + +class K3dwidgetsRenderer(Plot, K3dwidgetIdentity): """ Abstract class for performing visualizations using K3dwidgets. Parameters @@ -139,6 +152,8 @@ class K3dwidgetsRenderer(Plot, Renderer): new_figure : bool If `True`, creates a new figure on the cell. """ + # list_figures_ids = [] + def __init__(self, figure_id, new_figure): super(K3dwidgetsRenderer, self).__init__() @@ -166,8 +181,6 @@ def remove_widget(self): self.comm.close() self.comm = None self._repr_mimebundle_ = None - # TODO - # Why the following atributes don't change def get_figure(self): r""" @@ -657,7 +670,7 @@ def _render(self, distances_between_meshes, type_cmap='hot_r', scalar_range, show_statistics) -class K3dwidgetsPCAModelViewer3d(GridBox): +class K3dwidgetsPCAModelViewer3d(GridBox, K3dwidgetIdentity): def __init__(self, figure_id, new_figure, points, trilist, components, eigenvalues, n_parameters, parameters_bound, landmarks_indices, widget_style): @@ -689,6 +702,8 @@ def __init__(self, figure_id, new_figure, points, trilist, super(K3dwidgetsPCAModelViewer3d, self).__init__(children=[self.wid, self.mesh_window], layout=Layout(grid_template_columns='1fr 1fr')) + self.dict_figure_id_to_model_id[figure_id] = self.model_id + def _render_mesh(self, mesh_type, line_width, colour, marker_size, marker_resolution, marker_style, step, alpha): marker_size = _parse_marker_size(marker_size, self.points) From 30f7a6bd578657f6dac0113a83a142ac17b4c304 Mon Sep 17 00:00:00 2001 From: Thanos Date: Tue, 1 Dec 2020 14:27:59 +0000 Subject: [PATCH 30/43] Update example 0_Introduction_to_K3d_Widgets --- examples/0_Introduction_to_K3d_Widgets.ipynb | 121 ++++++++++++------- 1 file changed, 80 insertions(+), 41 deletions(-) diff --git a/examples/0_Introduction_to_K3d_Widgets.ipynb b/examples/0_Introduction_to_K3d_Widgets.ipynb index 50342f8..74006b3 100644 --- a/examples/0_Introduction_to_K3d_Widgets.ipynb +++ b/examples/0_Introduction_to_K3d_Widgets.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "metadata": {}, "outputs": [], "source": [ @@ -22,7 +22,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "metadata": {}, "outputs": [], "source": [ @@ -49,7 +49,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 3, "metadata": {}, "outputs": [], "source": [ @@ -69,11 +69,26 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 4, "metadata": { "scrolled": false }, - "outputs": [], + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "c839daf918024d3ab9f3da93d85fbc7a", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "K3dwidgetsTexturedTriMeshViewer3d(antialias=3, axes=['x', 'y', 'z'], axes_helper=1.0, background_color=1677721…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "# Default values for TriMesh, TextureMesh viewer are\n", "# figure_id None\n", @@ -81,7 +96,7 @@ "# in that case an automatic figure_id will be given\n", "# with 'Figure_{n}' format\n", "# n will be an increased integer starting from zero\n", - "mesh.view(inline=True) # wait a bit before magic happens" + "mesh.view() # wait a bit before magic happens" ] }, { @@ -93,24 +108,39 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 5, "metadata": { "scrolled": false }, - "outputs": [], + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "2094e5cd97684f82b73f0aaebbc62a3f", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "K3dwidgetsTexturedTriMeshViewer3d(antialias=3, axes=['x', 'y', 'z'], axes_helper=1.0, background_color=1677721…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ - "mesh.view(inline=True, figure_id='James')" + "mesh.view(figure_id='James')" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "# Add landmarks to figure with id James\n", "lms_poincloud = PointCloud(lms.points)\n", - "lms_poincloud.view(inline=True, figure_id='James',new_figure=False)" + "lms_poincloud.view(figure_id='James',new_figure=False)" ] }, { @@ -120,7 +150,7 @@ "outputs": [], "source": [ "# Add landmarks to figure with id Figure_0\n", - "lms_poincloud.view(inline=True, figure_id='Figure_0', new_figure=False)" + "lms_poincloud.view(figure_id='Figure_0', new_figure=False)" ] }, { @@ -145,7 +175,7 @@ "metadata": {}, "outputs": [], "source": [ - "lms.view(inline=True, new_figure=True, render_numbering=True)" + "lms.view(new_figure=True, render_numbering=True)" ] }, { @@ -156,7 +186,7 @@ "source": [ "# The mesh has now landmarks, so they would be plotted as well\n", "# the figure id is now Figure_2\n", - "mesh.view(inline=True,)" + "mesh.view()" ] }, { @@ -166,7 +196,25 @@ "outputs": [], "source": [ "# Show the TexturedMesh without texture\n", - "mesh.view(inline=True, render_texture=False)" + "mesh.view(render_texture=False)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "mesh.view(render_texture=False, mesh_type='wireframe')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "random_mesh.view(mesh_type='surface')" ] }, { @@ -183,7 +231,7 @@ "outputs": [], "source": [ "# Heatmap between a random mesh and mean mesh\n", - "random_mesh.heatmap(model_mean, inline=True)" + "random_mesh.heatmap(model_mean)" ] }, { @@ -195,7 +243,7 @@ "# Heatmap with statistics \n", "# Be careful, since we have already drawn a heatmap between\n", "# random and mean, we should use another name for figure\n", - "random_mesh.heatmap(model_mean, inline=True, show_statistics=True, figure_id='Heatmap2')" + "random_mesh.heatmap(model_mean, show_statistics=True, figure_id='Heatmap2')" ] }, { @@ -243,7 +291,7 @@ "metadata": {}, "outputs": [], "source": [ - "random_mesh.view(inline=True, normals=vrt, \n", + "random_mesh.view(normals=vrt, \n", " normals_marker_size= 0.5,\n", " normals_line_width = 0.01,\n", " figure_id='Normals')" @@ -255,7 +303,7 @@ "metadata": {}, "outputs": [], "source": [ - "random_mesh_landmarks.view(inline=True, figure_id='Normals', new_figure=False)" + "random_mesh_landmarks.view(figure_id='Normals', new_figure=False)" ] }, { @@ -267,7 +315,7 @@ "outputs": [], "source": [ "random_mesh.landmarks = random_mesh_landmarks\n", - "random_mesh.view(inline=True, normals=vrt, \n", + "random_mesh.view( normals=vrt, \n", " normals_marker_size= 0.5,\n", " normals_line_width = 0.01)" ] @@ -317,7 +365,7 @@ "outputs": [], "source": [ "new_mesh = ColouredTriMesh(random_mesh.points, random_mesh.trilist, colours=colors)\n", - "new_mesh.view(inline=True)" + "new_mesh.view()" ] }, { @@ -363,7 +411,7 @@ "metadata": {}, "outputs": [], "source": [ - "graph.view(inline=True)" + "graph.view()" ] }, { @@ -372,7 +420,7 @@ "metadata": {}, "outputs": [], "source": [ - "graph.view(inline=True, line_colour=colors, render_numbering=True)" + "graph.view(line_colour=colors, render_numbering=True)" ] }, { @@ -390,7 +438,7 @@ }, "outputs": [], "source": [ - "model.view(inline=True, figure_id='Model', n_parameters=10, landmarks_indices=lms_indices)" + "model.view(figure_id='Model', n_parameters=10, landmarks_indices=lms_indices)" ] }, { @@ -399,7 +447,9 @@ "metadata": {}, "outputs": [], "source": [ - "model.view(widget_style='')" + "random_mesh.view()\n", + "lms.view(new_figure=False)\n", + "mesh.view(new_figure=False)" ] }, { @@ -424,7 +474,7 @@ "metadata": {}, "outputs": [], "source": [ - "mesh.view(inline=True)" + "mesh.view( )" ] }, { @@ -451,7 +501,7 @@ "metadata": {}, "outputs": [], "source": [ - "mesh_hq.view(inline=True)" + "mesh_hq.view()" ] }, { @@ -470,7 +520,7 @@ "# It should fail if the previous cells have been executed, as default values for landmarker viewer are\n", "# figure_id = None and new_figure=False, so it could not\n", "# find a figure with id None\n", - "lms.view(inline=True,)" + "lms.view()" ] }, { @@ -481,7 +531,7 @@ "source": [ "# It should fail if the previous cells have been executed, as we have already had a figure with id \n", "# James and we cannot create a new one with the same figure_id\n", - "mesh.view(inline=True, figure_id='James', new_figure=True)" + "mesh.view(figure_id='James', new_figure=True)" ] }, { @@ -505,17 +555,6 @@ "random_mesh.heatmap(model_mean, inline=True)`" ] }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# If you draw a new figure, the command should be the last one in the cell, preferably on its own cell\n", - "lms.view(inline=True, new_figure=True)\n", - "print('Hello World')" - ] - }, { "cell_type": "markdown", "metadata": {}, @@ -606,7 +645,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.7" + "version": "3.8.5" } }, "nbformat": 4, From 297aaf0f45d894713a222ecb84544d5d60b74dab Mon Sep 17 00:00:00 2001 From: Thanos Date: Wed, 16 Dec 2020 18:52:58 +0000 Subject: [PATCH 31/43] K3dwidgetsPCAModelViewer3d supports PointClouds as well. Address issue #9 --- menpo3d/visualize/viewk3dwidgets.py | 35 ++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/menpo3d/visualize/viewk3dwidgets.py b/menpo3d/visualize/viewk3dwidgets.py index 8ef17de..a1384c6 100644 --- a/menpo3d/visualize/viewk3dwidgets.py +++ b/menpo3d/visualize/viewk3dwidgets.py @@ -152,7 +152,7 @@ class K3dwidgetsRenderer(Plot, K3dwidgetIdentity): new_figure : bool If `True`, creates a new figure on the cell. """ - # list_figures_ids = [] + # list_figures_ids = [] def __init__(self, figure_id, new_figure): super(K3dwidgetsRenderer, self).__init__() @@ -341,7 +341,7 @@ def _render(self, render_lines=True, line_colour='r', line_width=2, widg_to_draw = super(K3dwidgetsPointGraphViewer3d, self)._render() # Render the lines if requested - if render_lines: + if render_lines and self.edges is not None: if isinstance(line_colour, list): line_colour = [_parse_colour(i_color) for i_color in line_colour] @@ -684,7 +684,10 @@ def __init__(self, figure_id, new_figure, points, trilist, self.figure_id = _check_figure_id(self, figure_id, new_figure) self.new_figure = new_figure self.points = points.astype(np.float32) - self.trilist = trilist.astype(np.uint32) + if trilist is None: + self.trilist = None + else: + self.trilist = trilist.astype(np.uint32) self.components = components.astype(np.float32) self.eigenvalues = eigenvalues.astype(np.float32) self.n_parameters = n_parameters @@ -697,7 +700,11 @@ def __init__(self, figure_id, new_figure, points, trilist, params_bounds=parameters_bound, plot_variance_visible=False, style=widget_style) - self.mesh_window = K3dwidgetsTriMeshViewer3d(self.figure_id, False, + if self.trilist is None: + self.mesh_window = K3dwidgetsPointGraphViewer3d(self.figure_id, False, + self.points, self.trilist) + else: + self.mesh_window = K3dwidgetsTriMeshViewer3d(self.figure_id, False, self.points, self.trilist) super(K3dwidgetsPCAModelViewer3d, self).__init__(children=[self.wid, self.mesh_window], layout=Layout(grid_template_columns='1fr 1fr')) @@ -709,9 +716,14 @@ def _render_mesh(self, mesh_type, line_width, colour, marker_size, marker_size = _parse_marker_size(marker_size, self.points) colour = _parse_colour(colour) - mesh_to_add = k3d_mesh(self.points, self.trilist.flatten(), - flat_shading=False, color=colour, - name='Instance', side='double') + if self.trilist is None: + mesh_to_add = k3d_points(self.points, color=colour, + point_size=marker_size, + shader='3dSpecular') + else: + mesh_to_add = k3d_mesh(self.points, self.trilist.flatten(), + flat_shading=False, color=colour, + name='Instance', side='double') self.mesh_window += mesh_to_add @@ -730,11 +742,14 @@ def render_function(self, change): weights = np.asarray(self.wid.selected_values).astype(np.float32) weighted_eigenvalues = weights * self.eigenvalues[:self.n_parameters]**0.5 new_instance = (self.components[:self.n_parameters, :].T@weighted_eigenvalues).reshape(-1, 3) - mesh = self.points + new_instance + new_points = self.points + new_instance - self.mesh_window.objects[0].vertices = mesh + if self.trilist is None: + self.mesh_window.objects[0].positions = new_points + else: + self.mesh_window.objects[0].vertices = new_points if self.landmarks_indices is not None: - self.mesh_window.objects[1].positions = mesh[self.landmarks_indices] + self.mesh_window.objects[1].positions = new_points[self.landmarks_indices] def _render(self, mesh_type='wireframe', line_width=2, colour='r', marker_style='sphere', marker_size=None, marker_resolution=8, From 5f915f07edb2913cd41a82bf0842166d335307b6 Mon Sep 17 00:00:00 2001 From: Thanos Date: Wed, 16 Dec 2020 18:59:54 +0000 Subject: [PATCH 32/43] Remove commented deprecated commands --- menpo3d/visualize/base.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/menpo3d/visualize/base.py b/menpo3d/visualize/base.py index 6b00eaf..b7f524c 100644 --- a/menpo3d/visualize/base.py +++ b/menpo3d/visualize/base.py @@ -3,8 +3,6 @@ MayaviTexturedTriMeshViewer3d, MayaviLandmarkViewer3d, MayaviVectorViewer3d, MayaviColouredTriMeshViewer3d, MayaviHeatmapViewer3d) -# from .viewitkwidgets import (ItkwidgetsTriMeshViewer3d, -# ItkwidgetsPointGraphViewer3d) from .viewk3dwidgets import (K3dwidgetsTriMeshViewer3d, K3dwidgetsPointGraphViewer3d, From 3b4157aaae446b5e9cda70bae377a38dfcac6f7e Mon Sep 17 00:00:00 2001 From: Thanos Date: Sun, 17 Jan 2021 17:02:26 +0000 Subject: [PATCH 33/43] Add MayaviHeatmapViewer3d in import list in base.py --- menpo3d/visualize/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/menpo3d/visualize/__init__.py b/menpo3d/visualize/__init__.py index 107ee31..326fbf6 100644 --- a/menpo3d/visualize/__init__.py +++ b/menpo3d/visualize/__init__.py @@ -1,7 +1,7 @@ from .base import (PointGraphViewer3d, TriMeshViewer3d, VectorViewer3d, ColouredTriMeshViewer3d, TexturedTriMeshViewer3d, - LandmarkViewer3d, TriMeshInlineViewer3d, - ColouredTriMeshInlineViewer3d, + LandmarkViewer3d, MayaviHeatmapViewer3d, + TriMeshInlineViewer3d, ColouredTriMeshInlineViewer3d, PointGraphInlineViewer3d, TexturedTriMeshInlineViewer3d, LandmarkInlineViewer3d, PointGraphInlineViewer3d, VectorInlineViewer3d, HeatmapInlineViewer3d, From c8b83b5f2bfbfd997ab188a9369ffef4647b4acf Mon Sep 17 00:00:00 2001 From: Thanos Date: Sun, 17 Jan 2021 17:09:16 +0000 Subject: [PATCH 34/43] Fix error in import Heatmap3d in visualize/__init__.py --- menpo3d/visualize/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/menpo3d/visualize/__init__.py b/menpo3d/visualize/__init__.py index 326fbf6..d221a22 100644 --- a/menpo3d/visualize/__init__.py +++ b/menpo3d/visualize/__init__.py @@ -1,6 +1,6 @@ from .base import (PointGraphViewer3d, TriMeshViewer3d, VectorViewer3d, ColouredTriMeshViewer3d, TexturedTriMeshViewer3d, - LandmarkViewer3d, MayaviHeatmapViewer3d, + LandmarkViewer3d, HeatmapViewer3d, TriMeshInlineViewer3d, ColouredTriMeshInlineViewer3d, PointGraphInlineViewer3d, TexturedTriMeshInlineViewer3d, LandmarkInlineViewer3d, PointGraphInlineViewer3d, From f1970a2ede8a750477602d88b8bddf809188b922 Mon Sep 17 00:00:00 2001 From: Thanos Date: Wed, 20 Jan 2021 09:51:00 +0000 Subject: [PATCH 35/43] Fix mayavi plotting for landmarks. --- menpo3d/visualize/viewmayavi.py | 26 +++++++++----------------- 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/menpo3d/visualize/viewmayavi.py b/menpo3d/visualize/viewmayavi.py index a76bd3e..5fac21f 100644 --- a/menpo3d/visualize/viewmayavi.py +++ b/menpo3d/visualize/viewmayavi.py @@ -695,23 +695,15 @@ def render( self.figure.scene.disable_render = True for i, (label, pc) in enumerate(sub_pointclouds): # render pointcloud - pc.view( - figure_id=self.figure_id, - new_figure=False, - render_lines=render_lines, - line_colour=line_colour[i], - line_width=line_width, - render_markers=render_markers, - marker_style=marker_style, - marker_size=marker_size, - marker_colour=marker_colour[i], - marker_resolution=marker_resolution, - step=step, - alpha=alpha, - render_numbering=render_numbering, - numbers_colour=numbers_colour, - numbers_size=numbers_size, - ) + pc.view(figure_id=self.figure_id, new_figure=False, + render_lines=render_lines, line_colour=line_colour[i], + line_width=line_width, render_markers=render_markers, + marker_style=marker_style, marker_size=marker_size, + marker_colour=marker_colour[i], + marker_resolution=marker_resolution, step=step, + alpha=alpha, render_numbering=render_numbering, + numbers_colour=numbers_colour, numbers_size=numbers_size, + inline=False) self.figure.scene.disable_render = False return self From d686fa8209ea93fd7fd51a03dbdc793c5bbf59d7 Mon Sep 17 00:00:00 2001 From: Thanos Date: Thu, 28 Jan 2021 15:09:32 +0000 Subject: [PATCH 36/43] Changes in viewk3dwidgets: rgbint and K3dwidgetsPointGraphViewer3d - Add rgbint function - K3dwidgetsPointGraphViewer3d supports colours for each point - K3dwidgetsPointGraphViewer3d will use as a shader the 3dSpecular if the number of points is greater than 1000 --- menpo3d/visualize/viewk3dwidgets.py | 60 ++++++++++++++++++++++++++++- 1 file changed, 58 insertions(+), 2 deletions(-) diff --git a/menpo3d/visualize/viewk3dwidgets.py b/menpo3d/visualize/viewk3dwidgets.py index a1384c6..e482454 100644 --- a/menpo3d/visualize/viewk3dwidgets.py +++ b/menpo3d/visualize/viewk3dwidgets.py @@ -45,6 +45,45 @@ def _calc_distance(points): return distance +def rgb2int(rgb_array, keep_alpha=False): + """ + Convert rgb_array to an int color + + Parameters + ---------- + rgb_array: ndarray + An RGBA array + keep_alpha: bool + If True, the alpha value is also used + Returns + -------- + A ndarray with an int color value for each point + """ + + type_error_message = "RGB shape should be (num_points,3) or (num_points,3)" + if isinstance(rgb_array, np.ndarray): + if len(rgb_array.shape) != 2: + raise TypeError(type_error_message) + if rgb_array.shape[1] != 3 and rgb_array.shape[1] != 4: + print(rgb_array.shape[1]) + raise TypeError(type_error_message) + else: + raise TypeError("RGB shape should be numpy ndarray") + + if not keep_alpha: + rgb_array = rgb_array[:, :3] + + num_points, num_colors = rgb_array.shape + if rgb_array.dtype in (np.float32, np.float64): + rgb_array = np.asarray(np.round(255*rgb_array), dtype='uint32') + # TODO + # check for overfloat + if num_colors == 4: + return ((rgb_array[:, 0] << 32) + (rgb_array[:, 1] << 16) + + (rgb_array[:, 2] << 8) + rgb_array[:, 3]) + + return ((rgb_array[:, 0] << 16) + (rgb_array[:, 1] << 8) + rgb_array[:, 2]) + def _parse_marker_size(marker_size, points): distance = _calc_distance(points) if marker_size is None: @@ -337,7 +376,8 @@ def __init__(self, figure_id, new_figure, points, edges): def _render(self, render_lines=True, line_colour='r', line_width=2, render_markers=True, marker_style='flat', marker_size=10, marker_colour='g', render_numbering=False, - numbers_colour='k', numbers_size=None): + numbers_colour='k', numbers_size=None, + colours=[], keep_alpha=False): widg_to_draw = super(K3dwidgetsPointGraphViewer3d, self)._render() # Render the lines if requested @@ -369,16 +409,32 @@ def _render(self, render_lines=True, line_colour='r', line_width=2, # Render the markers if requested if render_markers: marker_size = _parse_marker_size(marker_size, self.points) + if len(colours) != 0: + colours = rgb2int(colours, keep_alpha) + marker_colour = 'w' + marker_colour = _parse_colour(marker_colour) if marker_style == 'sphere': marker_style = 'mesh' - points_to_add = k3d_points(self.points, color=marker_colour, + # When the number of points is greater than 1000, it is recommended + # to use fast shaders: flat, 3d or 3dSpecular. + # The mesh shader generates much bigger overhead, + # but it has a properly triangularized sphere + # representing each point. + if self.points.shape[0] > 1000: + marker_style = '3dSpecular' + + points_to_add = k3d_points(self.points, colors=colours, + color=marker_colour, point_size=marker_size, shader=marker_style) widg_to_draw += points_to_add + # TODO + # A class of k3d.texts that groups all texts should be created + # Till then, we go that way if render_numbering: text_to_add = None for i, point in enumerate(self.points): From 856be8202e1ee7741ab40a732de09f5fa43f03cc Mon Sep 17 00:00:00 2001 From: Thanos Date: Fri, 29 Jan 2021 12:44:47 +0000 Subject: [PATCH 37/43] Remove dependency on menpowidgets package - Create menpowidgets.py where LinearModelParametersWidget is defined (copy from menpowidgets package) - Modify viek3dwidgets to call LinearModelParametersWidget from the abovementioned file. --- menpo3d/visualize/menpowidgets.py | 828 ++++++++++++++++++++++++++++ menpo3d/visualize/viewk3dwidgets.py | 6 +- 2 files changed, 829 insertions(+), 5 deletions(-) create mode 100644 menpo3d/visualize/menpowidgets.py diff --git a/menpo3d/visualize/menpowidgets.py b/menpo3d/visualize/menpowidgets.py new file mode 100644 index 0000000..749dba2 --- /dev/null +++ b/menpo3d/visualize/menpowidgets.py @@ -0,0 +1,828 @@ +from collections import OrderedDict +from time import sleep +from IPython import get_ipython +from ipywidgets import Box +import ipywidgets +from traitlets.traitlets import List + +# The below classes have been copied from +# the deprecated menpowidgets package +# MenpoWidget can be found in abstract.py +# LinearModelParametersWidget in options.py +class MenpoWidget(Box): + r""" + Base class for defining a Menpo widget. + + The widget has a `selected_values` trait that can be used in order to + inspect any changes that occur to its children. It also has functionality + for adding, removing, replacing or calling the handler callback function of + the `selected_values` trait. + + Parameters + ---------- + children : `list` of `ipywidgets` + The `list` of `ipywidgets` objects to be set as children in the + `ipywidgets.Box`. + trait : `traitlets.TraitType` subclass + The type of the `selected_values` object that gets added as a trait + in the widget. Possible options from `traitlets` are {``Int``, ``Float``, + ``Dict``, ``List``, ``Tuple``}. + trait_initial_value : `int` or `float` or `dict` or `list` or `tuple` + The initial value of the `selected_values` trait. + render_function : `callable` or ``None``, optional + The render function that behaves as a callback handler of the + `selected_values` trait for the `change` event. Its signature can be + ``render_function()`` or ``render_function(change)``, where ``change`` + is a `dict` with the following keys: + + - ``owner`` : the `HasTraits` instance + - ``old`` : the old value of the modified trait attribute + - ``new`` : the new value of the modified trait attribute + - ``name`` : the name of the modified trait attribute. + - ``type`` : ``'change'`` + + If ``None``, then nothing is added. + """ + def __init__(self, children, trait, trait_initial_value, + render_function=None): + # Create box object + super(MenpoWidget, self).__init__(children=children) + + # Add trait for selected values + selected_values = trait(default_value=trait_initial_value) + selected_values_trait = {'selected_values': selected_values} + self.add_traits(**selected_values_trait) + self.selected_values = trait_initial_value + + # Set render function + self._render_function = None + self.add_render_function(render_function) + + def add_render_function(self, render_function): + r""" + Method that adds the provided `render_function()` as a callback handler + to the `selected_values` trait of the widget. The given function is + also stored in `self._render_function`. + + Parameters + ---------- + render_function : `callable` or ``None``, optional + The render function that behaves as a callback handler of the + `selected_values` trait for the `change` event. Its signature can be + ``render_function()`` or ``render_function(change)``, where + ``change`` is a `dict` with the following keys: + + - ``owner`` : the `HasTraits` instance + - ``old`` : the old value of the modified trait attribute + - ``new`` : the new value of the modified trait attribute + - ``name`` : the name of the modified trait attribute. + - ``type`` : ``'change'`` + + If ``None``, then nothing is added. + """ + self._render_function = render_function + if self._render_function is not None: + self.observe(self._render_function, names='selected_values', + type='change') + + def remove_render_function(self): + r""" + Method that removes the current `self._render_function()` as a callback + handler to the `selected_values` trait of the widget and sets + ``self._render_function = None``. + """ + if self._render_function is not None: + self.unobserve(self._render_function, names='selected_values', + type='change') + self._render_function = None + + def replace_render_function(self, render_function): + r""" + Method that replaces the current `self._render_function()` with the + given `render_function()` as a callback handler to the `selected_values` + trait of the widget. + + Parameters + ---------- + render_function : `callable` or ``None``, optional + The render function that behaves as a callback handler of the + `selected_values` trait for the `change` event. Its signature can be + ``render_function()`` or ``render_function(change)``, where + ``change`` is a `dict` with the following keys: + + - ``owner`` : the `HasTraits` instance + - ``old`` : the old value of the modified trait attribute + - ``new`` : the new value of the modified trait attribute + - ``name`` : the name of the modified trait attribute. + - ``type`` : ``'change'`` + + If ``None``, then nothing is added. + """ + # remove old function + self.remove_render_function() + + # add new function + self.add_render_function(render_function) + + def call_render_function(self, old_value, new_value, type_value='change'): + r""" + Method that calls the existing `render_function()` callback handler. + + Parameters + ---------- + old_value : `int` or `float` or `dict` or `list` or `tuple` + The old `selected_values` value. + new_value : `int` or `float` or `dict` or `list` or `tuple` + The new `selected_values` value. + type_value : `str`, optional + The trait event type. + """ + if self._render_function is not None: + change_dict = {'type': 'change', 'old': old_value, + 'name': type_value, 'new': new_value, + 'owner': self.__str__()} + self._render_function(change_dict) + + +class LinearModelParametersWidget(MenpoWidget): + r""" + Creates a widget for selecting parameters values when visualizing a linear + model (e.g. PCA model). + + Note that: + + * To update the state of the widget, please refer to the + :meth:`set_widget_state` method. + * The selected values are stored in the ``self.selected_values`` `trait` + which is a `list`. + * To set the styling of this widget please refer to the + :meth:`predefined_style` method. + * To update the handler callback functions of the widget, please refer to + the :meth:`replace_render_function` and :meth:`replace_variance_function` + methods. + + Parameters + ---------- + n_parameters : `int` + The `list` of initial parameters values. + render_function : `callable` or ``None``, optional + The render function that is executed when a widgets' value changes. + It must have signature ``render_function(change)`` where ``change`` is + a `dict` with the following keys: + + * ``type`` : The type of notification (normally ``'change'``). + * ``owner`` : the `HasTraits` instance + * ``old`` : the old value of the modified trait attribute + * ``new`` : the new value of the modified trait attribute + * ``name`` : the name of the modified trait attribute. + + If ``None``, then nothing is assigned. + mode : ``{'single', 'multiple'}``, optional + If ``'single'``, only a single slider is constructed along with a + dropdown menu that allows the parameter selection. + If ``'multiple'``, a slider is constructed for each parameter. + params_str : `str`, optional + The string that will be used as description of the slider(s). The final + description has the form ``"{}{}".format(params_str, p)``, where ``p`` + is the parameter number. + params_bounds : (`float`, `float`), optional + The minimum and maximum bounds, in std units, for the sliders. + params_step : `float`, optional + The step, in std units, of the sliders. + plot_variance_visible : `bool`, optional + Defines whether the button for plotting the variance will be visible + upon construction. + plot_variance_function : `callable` or ``None``, optional + The plot function that is executed when the plot variance button is + clicked. If ``None``, then nothing is assigned. + animation_visible : `bool`, optional + Defines whether the animation options will be visible. + loop_enabled : `bool`, optional + If ``True``, then the repeat mode of the animation is enabled. + interval : `float`, optional + The interval between the animation progress in seconds. + interval_step : `float`, optional + The interval step (in seconds) that is applied when fast + forward/backward buttons are pressed. + animation_step : `float`, optional + The parameters step that is applied when animation is enabled. + style : `str` (see below), optional + Sets a predefined style at the widget. Possible options are: + + ============= ================== + Style Description + ============= ================== + ``'success'`` Green-based style + ``'info'`` Blue-based style + ``'warning'`` Yellow-based style + ``'danger'`` Red-based style + ``''`` No style + ============= ================== + + continuous_update : `bool`, optional + If ``True``, then the render function is called while moving a + slider's handle. If ``False``, then the the function is called only + when the handle (mouse click) is released. + + Example + ------- + Let's create a linear model parameters values widget and then update its + state. Firstly, we need to import it: + + >>> from menpowidgets.options import LinearModelParametersWidget + + Now let's define a render function that will get called on every widget + change and will dynamically print the selected parameters: + + >>> from menpo.visualize import print_dynamic + >>> def render_function(change): + >>> s = "Selected parameters: {}".format(wid.selected_values) + >>> print_dynamic(s) + + Create the widget with some initial options and display it: + + >>> wid = LinearModelParametersWidget(n_parameters=5, + >>> render_function=render_function, + >>> params_str='Parameter ', + >>> mode='multiple', + >>> params_bounds=(-3., 3.), + >>> plot_variance_visible=True, + >>> style='info') + >>> wid + + By moving the sliders, the printed message gets updated. Finally, let's + change the widget status with a new set of options: + + >>> wid.set_widget_state(n_parameters=10, params_str='', + >>> params_step=0.1, params_bounds=(-10, 10), + >>> plot_variance_visible=False, + >>> allow_callback=True) + """ + def __init__(self, n_parameters, render_function=None, mode='multiple', + params_str='Parameter ', params_bounds=(-3., 3.), + params_step=0.1, plot_variance_visible=True, + plot_variance_function=None, animation_visible=True, + loop_enabled=False, interval=0., interval_step=0.05, + animation_step=0.5, style='', continuous_update=False): + + # Get the kernel to use it later in order to make sure that the widgets' + # traits changes are passed during a while-loop + self.kernel = get_ipython().kernel + + # If only one slider requested, then set mode to multiple + if n_parameters == 1: + mode = 'multiple' + + # Create children + if mode == 'multiple': + self.sliders = [] + self.parameters_children = [] + for p in range(n_parameters): + slider_title = ipywidgets.HTML( + value="{}{}".format(params_str, p)) + slider_wid = ipywidgets.FloatSlider( + description='', min=params_bounds[0], max=params_bounds[1], + step=params_step, value=0., + continuous_update=continuous_update, + layout=ipywidgets.Layout(width='8cm')) + tmp = ipywidgets.HBox([slider_title, slider_wid]) + tmp.layout.align_items = 'center' + self.sliders.append(slider_wid) + self.parameters_children.append(tmp) + self.parameters_wid = ipywidgets.VBox(self.parameters_children) + self.parameters_wid.layout.align_items = 'flex-end' + else: + vals = OrderedDict() + for p in range(n_parameters): + vals["{}{}".format(params_str, p)] = p + self.slider = ipywidgets.FloatSlider( + description='', min=params_bounds[0], max=params_bounds[1], + step=params_step, value=0., readout=True, + layout=ipywidgets.Layout(width='8cm'), + continuous_update=continuous_update) + self.dropdown_params = ipywidgets.Dropdown( + options=vals, layout=ipywidgets.Layout(width='3cm')) + self.dropdown_params.layout.margin = '0px 10px 0px 0px' + self.parameters_wid = ipywidgets.HBox([self.dropdown_params, + self.slider]) + self.parameters_wid.layout.margin = '0px 0px 10px 0px' + self.plot_button = ipywidgets.Button( + description='Variance', layout=ipywidgets.Layout(width='80px')) + self.plot_button.layout.display = ( + 'inline' if plot_variance_visible else 'none') + self.reset_button = ipywidgets.Button( + description='Reset', layout=ipywidgets.Layout(width='80px')) + self.plot_and_reset = ipywidgets.HBox([self.reset_button, + self.plot_button]) + self.play_button = ipywidgets.Button( + icon='play', description='', tooltip='Play animation', + layout=ipywidgets.Layout(width='40px')) + self.stop_button = ipywidgets.Button( + icon='stop', description='', tooltip='Stop animation', + layout=ipywidgets.Layout(width='40px')) + self.fast_forward_button = ipywidgets.Button( + icon='fast-forward', description='', + layout=ipywidgets.Layout(width='40px'), + tooltip='Increase animation speed') + self.fast_backward_button = ipywidgets.Button( + icon='fast-backward', description='', + layout=ipywidgets.Layout(width='40px'), + tooltip='Decrease animation speed') + loop_icon = 'repeat' if loop_enabled else 'long-arrow-right' + self.loop_toggle = ipywidgets.ToggleButton( + icon=loop_icon, description='', value=loop_enabled, + layout=ipywidgets.Layout(width='40px'), tooltip='Repeat animation') + self.animation_buttons = ipywidgets.HBox( + [self.play_button, self.stop_button, self.loop_toggle, + self.fast_backward_button, self.fast_forward_button]) + self.animation_buttons.layout.display = ( + 'flex' if animation_visible else 'none') + self.animation_buttons.layout.margin = '0px 15px 0px 0px' + self.buttons_box = ipywidgets.HBox([self.animation_buttons, + self.plot_and_reset]) + self.container = ipywidgets.VBox([self.parameters_wid, + self.buttons_box]) + + # Create final widget + super(LinearModelParametersWidget, self).__init__( + [self.container], List, [0.] * n_parameters, + render_function=render_function) + + # Assign output + self.n_parameters = n_parameters + self.mode = mode + self.params_str = params_str + self.params_bounds = params_bounds + self.params_step = params_step + self.plot_variance_visible = plot_variance_visible + self.loop_enabled = loop_enabled + self.continuous_update = continuous_update + self.interval = interval + self.interval_step = interval_step + self.animation_step = animation_step + self.animation_visible = animation_visible + self.please_stop = False + + # Set style + self.predefined_style(style) + + # Set functionality + if mode == 'single': + # Assign slider value to parameters values list + def save_slider_value(change): + current_parameters = list(self.selected_values) + current_parameters[self.dropdown_params.value] = change['new'] + self.selected_values = current_parameters + self.slider.observe(save_slider_value, names='value', type='change') + + # Set correct value to slider when drop down menu value changes + def set_slider_value(change): + # Temporarily remove render callback + render_fun = self._render_function + self.remove_render_function() + # Set slider value + self.slider.value = self.selected_values[change['new']] + # Re-assign render callback + self.add_render_function(render_fun) + self.dropdown_params.observe(set_slider_value, names='value', + type='change') + else: + # Assign saving values and main plotting function to all sliders + for w in self.sliders: + w.observe(self._save_slider_value_from_id, names='value', + type='change') + + def reset_parameters(name): + # Keep old value + old_value = self.selected_values + + # Temporarily remove render callback + render_fun = self._render_function + self.remove_render_function() + + # Set parameters to 0 + self.selected_values = [0.0] * self.n_parameters + if mode == 'multiple': + for ww in self.sliders: + ww.value = 0. + else: + self.parameters_wid.children[0].value = 0 + self.parameters_wid.children[1].value = 0. + + # Re-assign render callback and trigger it + self.add_render_function(render_fun) + self.call_render_function(old_value, self.selected_values) + self.reset_button.on_click(reset_parameters) + + # Set functionality + def loop_pressed(change): + if change['new']: + self.loop_toggle.icon = 'repeat' + else: + self.loop_toggle.icon = 'long-arrow-right' + self.kernel.do_one_iteration() + self.loop_toggle.observe(loop_pressed, names='value', type='change') + + def fast_forward_pressed(name): + tmp = self.interval + tmp -= self.interval_step + if tmp < 0: + tmp = 0 + self.interval = tmp + self.kernel.do_one_iteration() + self.fast_forward_button.on_click(fast_forward_pressed) + + def fast_backward_pressed(name): + self.interval += self.interval_step + self.kernel.do_one_iteration() + self.fast_backward_button.on_click(fast_backward_pressed) + + def animate(change): + reset_parameters('') + self.please_stop = False + self.reset_button.disabled = True + self.plot_button.disabled = True + if mode == 'multiple': + n_sliders = self.n_parameters + slider_id = 0 + while slider_id < n_sliders: + # animate from 0 to min + slider_val = 0. + while slider_val > self.params_bounds[0]: + # Run IPython iteration. + self.kernel.do_one_iteration() + + # Check stop flag + if self.please_stop: + break + + # update slider value + slider_val -= self.animation_step + + # set value + self.sliders[slider_id].value = slider_val + + # wait + sleep(self.interval) + + # Run IPython iteration. + self.kernel.do_one_iteration() + + # animate from min to max + slider_val = self.params_bounds[0] + while slider_val < self.params_bounds[1]: + # Run IPython iteration. + self.kernel.do_one_iteration() + + # Check stop flag + if self.please_stop: + break + + # update slider value + slider_val += self.animation_step + + # set value + self.sliders[slider_id].value = slider_val + + # wait + sleep(self.interval) + + # Run IPython iteration. + self.kernel.do_one_iteration() + + # animate from max to 0 + slider_val = self.params_bounds[1] + while slider_val > 0.: + # Run IPython iteration. + self.kernel.do_one_iteration() + + # Check stop flag + if self.please_stop: + break + + # update slider value + slider_val -= self.animation_step + + # set value + self.sliders[slider_id].value = slider_val + + # wait + sleep(self.interval) + + # Run IPython iteration. + self.kernel.do_one_iteration() + + # reset value + self.sliders[slider_id].value = 0. + + # Check stop flag + if self.please_stop: + break + + # update slider id + if self.loop_toggle.value and slider_id == n_sliders - 1: + slider_id = 0 + else: + slider_id += 1 + + if not self.loop_toggle.value and slider_id >= n_sliders: + self.stop_animation() + else: + n_sliders = self.n_parameters + slider_id = 0 + self.please_stop = False + while slider_id < n_sliders: + # set dropdown value + self.parameters_wid.children[0].value = slider_id + + # animate from 0 to min + slider_val = 0. + while slider_val > self.params_bounds[0]: + # Run IPython iteration. + self.kernel.do_one_iteration() + + # Check stop flag + if self.please_stop: + break + + # update slider value + slider_val -= self.animation_step + + # set value + self.parameters_wid.children[1].value = slider_val + + # wait + sleep(self.interval) + + # Run IPython iteration. + self.kernel.do_one_iteration() + + # animate from min to max + slider_val = self.params_bounds[0] + while slider_val < self.params_bounds[1]: + # Run IPython iteration. + self.kernel.do_one_iteration() + + # Check stop flag + if self.please_stop: + break + + # update slider value + slider_val += self.animation_step + + # set value + self.parameters_wid.children[1].value = slider_val + + # wait + sleep(self.interval) + + # Run IPython iteration. + self.kernel.do_one_iteration() + + # animate from max to 0 + slider_val = self.params_bounds[1] + while slider_val > 0.: + # Run IPython iteration. + self.kernel.do_one_iteration() + + # Check stop flag + if self.please_stop: + break + + # update slider value + slider_val -= self.animation_step + + # set value + self.parameters_wid.children[1].value = slider_val + + # wait + sleep(self.interval) + + # Run IPython iteration. + self.kernel.do_one_iteration() + + # reset value + self.parameters_wid.children[1].value = 0. + + # Check stop flag + if self.please_stop: + break + + # update slider id + if self.loop_toggle.value and slider_id == n_sliders - 1: + slider_id = 0 + else: + slider_id += 1 + self.reset_button.disabled = False + self.plot_button.disabled = False + self.play_button.on_click(animate) + + def stop_pressed(_): + self.stop_animation() + self.stop_button.on_click(stop_pressed) + + # Set plot variance function + self._variance_function = None + self.add_variance_function(plot_variance_function) + + def _save_slider_value_from_id(self, change): + current_parameters = list(self.selected_values) + i = self.sliders.index(change['owner']) + current_parameters[i] = change['new'] + self.selected_values = current_parameters + + def predefined_style(self, style): + r""" + Function that sets a predefined style on the widget. + + Parameters + ---------- + style : `str` (see below) + Style options: + + ============= ================== + Style Description + ============= ================== + ``'success'`` Green-based style + ``'info'`` Blue-based style + ``'warning'`` Yellow-based style + ``'danger'`` Red-based style + ``''`` No style + ============= ================== + """ + self.container.box_style = style + self.container.border = '0px' + self.play_button.button_style = 'success' + self.stop_button.button_style = 'danger' + self.fast_forward_button.button_style = 'info' + self.fast_backward_button.button_style = 'info' + self.loop_toggle.button_style = 'warning' + self.reset_button.button_style = 'danger' + self.plot_button.button_style = 'primary' + + def stop_animation(self): + r""" + Method that stops an active annotation. + """ + self.please_stop = True + + def add_variance_function(self, variance_function): + r""" + Method that adds a `variance_function()` to the `Variance` button of the + widget. The given function is also stored in `self._variance_function`. + + Parameters + ---------- + variance_function : `callable` or ``None``, optional + The variance function that behaves as a callback. If ``None``, + then nothing is added. + """ + self._variance_function = variance_function + if self._variance_function is not None: + self.plot_button.on_click(self._variance_function) + + def remove_variance_function(self): + r""" + Method that removes the current `self._variance_function()` from + the `Variance` button of the widget and sets + ``self._variance_function = None``. + """ + self.plot_button.on_click(self._variance_function, remove=True) + self._variance_function = None + + def replace_variance_function(self, variance_function): + r""" + Method that replaces the current `self._variance_function()` of the + `Variance` button of the widget with the given `variance_function()`. + + Parameters + ---------- + variance_function : `callable` or ``None``, optional + The variance function that behaves as a callback. If ``None``, + then nothing happens. + """ + # remove old function + self.remove_variance_function() + + # add new function + self.add_variance_function(variance_function) + + def set_widget_state(self, n_parameters=None, params_str=None, + params_bounds=None, params_step=None, + plot_variance_visible=True, animation_step=0.5, + allow_callback=True): + r""" + Method that updates the state of the widget with a new set of options. + + Parameters + ---------- + n_parameters : `int` + The `list` of initial parameters values. + params_str : `str`, optional + The string that will be used as description of the slider(s). The + final description has the form ``"{}{}".format(params_str, p)``, + where ``p`` is the parameter number. + params_bounds : (`float`, `float`), optional + The minimum and maximum bounds, in std units, for the sliders. + params_step : `float`, optional + The step, in std units, of the sliders. + plot_variance_visible : `bool`, optional + Defines whether the button for plotting the variance will be visible + upon construction. + animation_step : `float`, optional + The parameters step that is applied when animation is enabled. + allow_callback : `bool`, optional + If ``True``, it allows triggering of any callback functions. + """ + # Keep old value + old_value = self.selected_values + + # Temporarily remove render callback + render_function = self._render_function + self.remove_render_function() + + # Parse given options + if n_parameters is None: + n_parameters = self.n_parameters + if params_str is None: + params_str = '' + if params_bounds is None: + params_bounds = self.params_bounds + if params_step is None: + params_step = self.params_step + + # Set plot variance visibility + self.plot_button.layout.visibility = ( + 'visible' if plot_variance_visible else 'hidden') + self.animation_step = animation_step + + # Update widget + if n_parameters == self.n_parameters: + # The number of parameters hasn't changed + if self.mode == 'multiple': + for p, sl in enumerate(self.sliders): + self.parameters_children[p].children[0].value = \ + "{}{}".format(params_str, p) + sl.min = params_bounds[0] + sl.max = params_bounds[1] + sl.step = params_step + else: + self.slider.min = params_bounds[0] + self.slider.max = params_bounds[1] + self.slider.step = params_step + if not params_str == '': + vals = OrderedDict() + for p in range(n_parameters): + vals["{}{}".format(params_str, p)] = p + self.dropdown_params.options = vals + else: + # The number of parameters has changed + self.selected_values = [0.] * n_parameters + if self.mode == 'multiple': + # Create new sliders + self.sliders = [] + self.parameters_children = [] + for p in range(n_parameters): + slider_title = ipywidgets.HTML( + value="{}{}".format(params_str, p)) + slider_wid = ipywidgets.FloatSlider( + description='', min=params_bounds[0], + max=params_bounds[1], + step=params_step, value=0., width='8cm', + continuous_update=self.continuous_update) + tmp = ipywidgets.HBox([slider_title, slider_wid]) + tmp.layout.align_items = 'center' + self.sliders.append(slider_wid) + self.parameters_children.append(tmp) + self.parameters_wid.children = self.parameters_children + + # Assign saving values and main plotting function to all sliders + for w in self.sliders: + w.observe(self._save_slider_value_from_id, names='value', + type='change') + else: + self.slider.min = params_bounds[0] + self.slider.max = params_bounds[1] + self.slider.step = params_step + vals = OrderedDict() + for p in range(n_parameters): + vals["{}{}".format(params_str, p)] = p + if self.dropdown_params.value == 0 and n_parameters > 1: + self.dropdown_params.value = 1 + self.dropdown_params.value = 0 + self.dropdown_params.options = vals + self.slider.value = 0. + + # Re-assign render callback + self.add_render_function(render_function) + + # Assign new selected options + self.n_parameters = n_parameters + self.params_str = params_str + self.params_bounds = params_bounds + self.params_step = params_step + self.plot_variance_visible = plot_variance_visible + + # trigger render function if allowed + if allow_callback: + self.call_render_function(old_value, self.selected_values) diff --git a/menpo3d/visualize/viewk3dwidgets.py b/menpo3d/visualize/viewk3dwidgets.py index e482454..f59007f 100644 --- a/menpo3d/visualize/viewk3dwidgets.py +++ b/menpo3d/visualize/viewk3dwidgets.py @@ -731,11 +731,7 @@ def __init__(self, figure_id, new_figure, points, trilist, components, eigenvalues, n_parameters, parameters_bound, landmarks_indices, widget_style): - try: - from menpowidgets.options import LinearModelParametersWidget - except ImportError as e: - from menpo.visualize import MenpowidgetsMissingError - raise MenpowidgetsMissingError(e) + from .menpowidgets import LinearModelParametersWidget self.figure_id = _check_figure_id(self, figure_id, new_figure) self.new_figure = new_figure From d3f34a166bce808eefb1ab9531ee712434a4fbb4 Mon Sep 17 00:00:00 2001 From: Thanos Date: Wed, 3 Feb 2021 09:34:45 +0000 Subject: [PATCH 38/43] Add PointCloud with colours example in 0_Introduction_to_K3d_Widgets --- examples/0_Introduction_to_K3d_Widgets.ipynb | 87 +++++++++++--------- 1 file changed, 49 insertions(+), 38 deletions(-) diff --git a/examples/0_Introduction_to_K3d_Widgets.ipynb b/examples/0_Introduction_to_K3d_Widgets.ipynb index 74006b3..cff64e9 100644 --- a/examples/0_Introduction_to_K3d_Widgets.ipynb +++ b/examples/0_Introduction_to_K3d_Widgets.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -22,7 +22,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -49,7 +49,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -69,26 +69,11 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": null, "metadata": { "scrolled": false }, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "c839daf918024d3ab9f3da93d85fbc7a", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "K3dwidgetsTexturedTriMeshViewer3d(antialias=3, axes=['x', 'y', 'z'], axes_helper=1.0, background_color=1677721…" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "# Default values for TriMesh, TextureMesh viewer are\n", "# figure_id None\n", @@ -108,33 +93,18 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": null, "metadata": { "scrolled": false }, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "2094e5cd97684f82b73f0aaebbc62a3f", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "K3dwidgetsTexturedTriMeshViewer3d(antialias=3, axes=['x', 'y', 'z'], axes_helper=1.0, background_color=1677721…" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "mesh.view(figure_id='James')" ] }, { "cell_type": "code", - "execution_count": 6, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -320,6 +290,47 @@ " normals_line_width = 0.01)" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

Show PointCloud with colours

" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from IPython.utils.tempdir import TemporaryWorkingDirectory\n", + "with TemporaryWorkingDirectory() as tmpdir:\n", + " !mkdir -p ./data/PittsburghBridge\n", + " !wget -P ./data/PittsburghBridge https://dl.fbaipublicfiles.com/pytorch3d/data/PittsburghBridge/pointcloud.npz\n", + " pointcloud = np.load('./data/PittsburghBridge/pointcloud.npz')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "points = pointcloud['verts']\n", + "colours = pointcloud['rgb']\n", + "\n", + "coloured_pointcloud = PointCloud(points)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "coloured_pointcloud.view(colours=colours)" + ] + }, { "cell_type": "markdown", "metadata": {}, From 2a97c75e1b2fb2a7b7eda7146125ab64af862409 Mon Sep 17 00:00:00 2001 From: Thanos Date: Thu, 25 Feb 2021 17:52:43 +0000 Subject: [PATCH 39/43] Fix default value for colours argument in K3dwidgetsPointGraphViewer3d --- menpo3d/visualize/viewk3dwidgets.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/menpo3d/visualize/viewk3dwidgets.py b/menpo3d/visualize/viewk3dwidgets.py index f59007f..a3cad95 100644 --- a/menpo3d/visualize/viewk3dwidgets.py +++ b/menpo3d/visualize/viewk3dwidgets.py @@ -60,7 +60,7 @@ def rgb2int(rgb_array, keep_alpha=False): A ndarray with an int color value for each point """ - type_error_message = "RGB shape should be (num_points,3) or (num_points,3)" + type_error_message = "RGB shape should be (num_points,3) or (num_points,4)" if isinstance(rgb_array, np.ndarray): if len(rgb_array.shape) != 2: raise TypeError(type_error_message) @@ -377,7 +377,7 @@ def _render(self, render_lines=True, line_colour='r', line_width=2, render_markers=True, marker_style='flat', marker_size=10, marker_colour='g', render_numbering=False, numbers_colour='k', numbers_size=None, - colours=[], keep_alpha=False): + colours=None, keep_alpha=False): widg_to_draw = super(K3dwidgetsPointGraphViewer3d, self)._render() # Render the lines if requested @@ -409,9 +409,11 @@ def _render(self, render_lines=True, line_colour='r', line_width=2, # Render the markers if requested if render_markers: marker_size = _parse_marker_size(marker_size, self.points) - if len(colours) != 0: + if colours is not None: colours = rgb2int(colours, keep_alpha) marker_colour = 'w' + else: + colours = [] marker_colour = _parse_colour(marker_colour) From 1fb1983d21f273446f4da4af3ac9077eb61caf46 Mon Sep 17 00:00:00 2001 From: Thanos Date: Thu, 25 Feb 2021 19:12:24 +0000 Subject: [PATCH 40/43] Various changes to fix code in viewk3dwidgets --- menpo3d/visualize/viewk3dwidgets.py | 108 ++++++++++------------------ 1 file changed, 38 insertions(+), 70 deletions(-) diff --git a/menpo3d/visualize/viewk3dwidgets.py b/menpo3d/visualize/viewk3dwidgets.py index a3cad95..4ab84c8 100644 --- a/menpo3d/visualize/viewk3dwidgets.py +++ b/menpo3d/visualize/viewk3dwidgets.py @@ -236,55 +236,31 @@ def get_figure(self): # return self.figure pass -# def save_figure(self, filename, format='png', size=None, -# magnification='auto', overwrite=False): -# r""" -# Method for saving the figure of the current `figure_id` to file. -# -# Parameters -# ---------- -# filename : `str` or `file`-like object -# The string path or file-like object to save the figure at/into. -# format : `str` -# The format to use. This must match the file path if the file path is -# a `str`. -# size : `tuple` of `int` or ``None``, optional -# The size of the image created (unless magnification is set, -# in which case it is the size of the window used for rendering). If -# ``None``, then the figure size is used. -# magnification : `double` or ``'auto'``, optional -# The magnification is the scaling between the pixels on the screen, -# and the pixels in the file saved. If you do not specify it, it will -# be calculated so that the file is saved with the specified size. -# If you specify a magnification, Mayavi will use the given size as a -# screen size, and the file size will be ``magnification * size``. -# If ``'auto'``, then the magnification will be set automatically. -# overwrite : `bool`, optional -# If ``True``, the file will be overwritten if it already exists. -# """ -# from menpo.io.output.base import _export -# savefig_args = {'size': size, 'figure': self.figure, -# 'magnification': magnification} -# # Use the export code so that we have a consistent interface -# _export(savefig_args, filename, self._extensions_map, format, -# overwrite=overwrite) - - @property - def _width(self): - r""" - The width of the scene in pixels. An underscore has been added in the - begining of the name due to conflict with K3d Plot class - :type: `int` - """ - pass - - @property - def _height(self): + def save_figure(self, filename, format='png', size=None, + magnification='auto', overwrite=False): r""" - The height of the scene in pixels. An underscore has been added in the - begining of the name due to conflict with K3d Plot class - - :type: `int` + Method for saving the figure of the current `figure_id` to file. + + Parameters + ---------- + filename : `str` or `file`-like object + The string path or file-like object to save the figure at/into. + format : `str` + The format to use. This must match the file path if the file path is + a `str`. + size : `tuple` of `int` or ``None``, optional + The size of the image created (unless magnification is set, + in which case it is the size of the window used for rendering). If + ``None``, then the figure size is used. + magnification : `double` or ``'auto'``, optional + The magnification is the scaling between the pixels on the screen, + and the pixels in the file saved. If you do not specify it, it will + be calculated so that the file is saved with the specified size. + If you specify a magnification, Mayavi will use the given size as a + screen size, and the file size will be ``magnification * size``. + If ``'auto'``, then the magnification will be set automatically. + overwrite : `bool`, optional + If ``True``, the file will be overwritten if it already exists. """ pass @@ -295,8 +271,6 @@ def modelview_matrix(self): :type: ``(4, 4)`` `ndarray` """ - # camera = self.figure.scene.camera - # return camera.view_transform_matrix.to_array().astype(np.float32) pass @property @@ -306,13 +280,6 @@ def projection_matrix(self): :type: ``(4, 4)`` `ndarray` """ -# scene = self.figure.scene -# camera = scene.camera -# scene_size = tuple(scene.get_size()) -# aspect_ratio = float(scene_size[0]) / float(scene_size[1]) -# p = camera.get_projection_transform_matrix( -# aspect_ratio, -1, 1).to_array().astype(np.float32) -# return p pass @property @@ -333,11 +300,7 @@ def renderer_settings(self): * ``'projection_matrix'`` (`ndarray`) : The projection array. """ - return {'width': self.width, - 'height': self.height, - 'model_matrix': np.eye(4, dtype=np.float32), - 'view_matrix': self.modelview_matrix, - 'projection_matrix': self.projection_matrix} + pass def force_draw(self): r""" @@ -417,6 +380,8 @@ def _render(self, render_lines=True, line_colour='r', line_width=2, marker_colour = _parse_colour(marker_colour) + # In order to be compatible with mayavi, we just change the + # default value for marker_style to mesh if marker_style == 'sphere': marker_style = 'mesh' @@ -439,13 +404,15 @@ def _render(self, render_lines=True, line_colour='r', line_width=2, # Till then, we go that way if render_numbering: text_to_add = None + + numbers_colour = _parse_colour(numbers_colour) for i, point in enumerate(self.points): if text_to_add is None: - text_to_add = k3d_text(str(i), position=point, - label_box=False) + text_to_add = k3d_text(str(i), color=numbers_colour, + position=point, label_box=False) else: - text_to_add += k3d_text(str(i), position=point, - label_box=False) + text_to_add += k3d_text(str(i), color=numbers_colour, + position=point, label_box=False) widg_to_draw += text_to_add return widg_to_draw @@ -656,13 +623,14 @@ def _render(self, render_lines=True, line_colour='r', line_width=2, widg_to_draw += points_to_add if render_numbering: text_to_add = None + numbers_colour = _parse_colour(numbers_colour) for i, point in enumerate(self.landmark_group.points): if text_to_add is None: - text_to_add = k3d_text(str(i), position=point, - label_box=False) + text_to_add = k3d_text(str(i), color=numbers_colour, + position=point, label_box=False) else: - text_to_add += k3d_text(str(i), position=point, - label_box=False) + text_to_add += k3d_text(str(i), color=numbers_colour, + position=point, label_box=False) widg_to_draw += text_to_add # widg_to_draw.camera = _calc_camera_position(pc.points) # widg_to_draw.camera_auto_fit = False From eb26b610a7dee6c06581f6cf96ba7a4ea59be83b Mon Sep 17 00:00:00 2001 From: Thanos Date: Fri, 26 Feb 2021 17:48:16 +0000 Subject: [PATCH 41/43] Removing and tidying parameters for view_3d inline methods --- menpo3d/visualize/viewk3dwidgets.py | 64 +++++++++++++++-------------- 1 file changed, 34 insertions(+), 30 deletions(-) diff --git a/menpo3d/visualize/viewk3dwidgets.py b/menpo3d/visualize/viewk3dwidgets.py index 4ab84c8..ff958a6 100644 --- a/menpo3d/visualize/viewk3dwidgets.py +++ b/menpo3d/visualize/viewk3dwidgets.py @@ -338,7 +338,7 @@ def __init__(self, figure_id, new_figure, points, edges): def _render(self, render_lines=True, line_colour='r', line_width=2, render_markers=True, marker_style='flat', marker_size=10, - marker_colour='g', render_numbering=False, + marker_colour='g', alpha=1.0, render_numbering=False, numbers_colour='k', numbers_size=None, colours=None, keep_alpha=False): @@ -396,6 +396,7 @@ def _render(self, render_lines=True, line_colour='r', line_width=2, points_to_add = k3d_points(self.points, colors=colours, color=marker_colour, point_size=marker_size, + opacity=alpha, shader=marker_style) widg_to_draw += points_to_add @@ -426,13 +427,13 @@ def __init__(self, figure_id, new_figure, points, trilist, landmarks=None): self.landmarks = landmarks def _render_mesh(self, line_width, colour, mesh_type, - marker_style, marker_size): + marker_style, marker_size, alpha=1.0): marker_size = _parse_marker_size(marker_size, self.points) colour = _parse_colour(colour) widg_to_draw = super(K3dwidgetsTriMeshViewer3d, self)._render() wireframe = False - opacity = 1.0 + opacity = alpha if mesh_type == 'wireframe': wireframe = True opacity = 0.3 @@ -444,17 +445,20 @@ def _render_mesh(self, line_width, colour, mesh_type, widg_to_draw += mesh_to_add if hasattr(self.landmarks, 'points'): - self.landmarks.view(inline=True, new_figure=False, - figure_id=self.figure_id) + self.landmarks.view(figure_id=self.figure_id, + new_figure=False, + marker_style=marker_style, + marker_size=marker_size, + inline=True) return widg_to_draw def _render(self, line_width=2, colour='r', mesh_type='surface', - marker_style='sphere', marker_size=None, + marker_style='mesh', marker_size=None, normals=None, normals_colour='k', normals_line_width=2, - normals_marker_size=None): + normals_marker_size=None, alpha=1.0): widg_to_draw = self._render_mesh(line_width, colour, mesh_type, - marker_style, marker_size) + marker_style, marker_size, alpha) if normals is not None: tmp_normals_widget = K3dwidgetsVectorViewer3d(self.figure_id, False, self.points, @@ -556,11 +560,9 @@ def _render(self, normals=None, normals_colour='k', normals_line_width=2, normals_marker_size=None): if normals is not None: K3dwidgetsVectorViewer3d(self.figure_id, False, - self.points, normals).render( - colour=normals_colour, line_width=normals_line_width, step=step, - marker_style=normals_marker_style, - marker_resolution=normals_marker_resolution, - marker_size=normals_marker_size, alpha=alpha) + self.points, normals)._render( + colour=normals_colour, line_width=normals_line_width, + marker_size=normals_marker_size) self._render_mesh() return self @@ -587,9 +589,9 @@ def __init__(self, figure_id, new_figure, group, landmark_group): self.landmark_group = landmark_group def _render(self, render_lines=True, line_colour='r', line_width=2, - render_markers=True, marker_style='sphere', marker_size=None, - marker_colour='r', marker_resolution=8, step=None, alpha=1.0, - render_numbering=False, numbers_colour='k', numbers_size=None): + render_markers=True, marker_style='mesh', marker_size=None, + marker_colour='r', alpha=1.0, render_numbering=False, + numbers_colour='k', numbers_size=None): # Regarding the labels colours, we may get passed either no colours (in # which case we generate random colours) or a single colour to colour # all the labels with @@ -597,13 +599,14 @@ def _render(self, render_lines=True, line_colour='r', line_width=2, n_labels = self.landmark_group.n_labels line_colour = _check_colours_list( render_lines, line_colour, n_labels, - 'Must pass a list of line colours with length n_labels or a single ' + 'Must pass a list of line colours with length n_labels or a single' 'line colour for all labels.') marker_colour = _check_colours_list( render_markers, marker_colour, n_labels, 'Must pass a list of marker colours with length n_labels or a ' 'single marker face colour for all labels.') - marker_size = _parse_marker_size(marker_size, self.landmark_group.points) + marker_size = _parse_marker_size(marker_size, + self.landmark_group.points) numbers_size = _parse_marker_size(numbers_size, self.landmark_group.points) @@ -653,7 +656,6 @@ def _render_mesh(self, distances_between_meshes, type_cmap, scalar_range, show_statistics=False): marker_size = _parse_marker_size(None, self.points) - widg_to_draw = super(K3dwidgetsHeatmapViewer3d, self)._render() try: @@ -671,8 +673,10 @@ def _render_mesh(self, distances_between_meshes, type_cmap, widg_to_draw += mesh_to_add if hasattr(self.landmarks, 'points'): - self.landmarks.view(inline=True, new_figure=False, - figure_id=self.figure_id) + self.landmarks.view(figure_id=self.figure_id, + new_figure=False, + marker_size=marker_size, + inline=True) if show_statistics: text = '\\begin{{matrix}} \\mu & {:.3} \\\\ \\sigma^2 & {:.3} \\\\ \\max & {:.3} \\end{{matrix}}'\ @@ -733,19 +737,21 @@ def __init__(self, figure_id, new_figure, points, trilist, self.dict_figure_id_to_model_id[figure_id] = self.model_id - def _render_mesh(self, mesh_type, line_width, colour, marker_size, - marker_resolution, marker_style, step, alpha): + def _render_mesh(self, mesh_type, line_width, colour, + marker_size, marker_style, alpha): marker_size = _parse_marker_size(marker_size, self.points) colour = _parse_colour(colour) if self.trilist is None: mesh_to_add = k3d_points(self.points, color=colour, + opacity=alpha, point_size=marker_size, shader='3dSpecular') else: mesh_to_add = k3d_mesh(self.points, self.trilist.flatten(), - flat_shading=False, color=colour, - name='Instance', side='double') + flat_shading=False, opacity=alpha, + color=colour, name='Instance', + side='double') self.mesh_window += mesh_to_add @@ -774,12 +780,10 @@ def render_function(self, change): self.mesh_window.objects[1].positions = new_points[self.landmarks_indices] def _render(self, mesh_type='wireframe', line_width=2, colour='r', - marker_style='sphere', marker_size=None, marker_resolution=8, - normals=None, normals_colour='k', normals_line_width=2, - normals_marker_resolution=8, step=None, alpha=1.0): + marker_style='mesh', marker_size=None, alpha=1.0): - return self._render_mesh(mesh_type, line_width, colour, marker_size, - marker_resolution, marker_style, step, alpha) + return self._render_mesh(mesh_type, line_width, colour, + marker_size, marker_style, alpha) def remove_widget(self): super(K3dwidgetsPCAModelViewer3d, self).close() From 0fdc518596c5aafbeb1461a6d8c65602dd4167d4 Mon Sep 17 00:00:00 2001 From: apapaion Date: Thu, 27 Oct 2022 09:40:22 +0100 Subject: [PATCH 42/43] fix bug in setup, add version requirement for ipywidget --- setup.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/setup.py b/setup.py index fa862e2..4306821 100644 --- a/setup.py +++ b/setup.py @@ -100,7 +100,7 @@ def build_extension_from_pyx(pyx_path, extra_sources_paths=None): cythonize = no_cythonize warnings.warn( - "Unabl + "Unable to import Cython - attempting to build using the " "pre-compiled C++ files." ) @@ -111,8 +111,8 @@ def build_extension_from_pyx(pyx_path, extra_sources_paths=None): version, cmdclass = get_version_and_cmdclass("menpo3d") -install_requires = ["menpo>=0.9.0,<0.12.0", "mayavi>=4.7.0", - "moderngl>=5.6.*,<6.0", 'k3d<=2.9.2'] +install_requires = ["menpo>=0.9.0,<0.12.0", "mayavi>=4.7.0", + "moderngl>=5.6.*,<6.0", "k3d<=2.9.2", "ipywidgets<8"] setup( name="menpo3d", From 36f6021ba67ad9733f92a5f0e09425bc2e8e73c9 Mon Sep 17 00:00:00 2001 From: Thanos Date: Fri, 10 May 2024 23:42:48 +0100 Subject: [PATCH 43/43] Fix bug in setup.py --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 4306821..5498039 100644 --- a/setup.py +++ b/setup.py @@ -112,7 +112,7 @@ def build_extension_from_pyx(pyx_path, extra_sources_paths=None): version, cmdclass = get_version_and_cmdclass("menpo3d") install_requires = ["menpo>=0.9.0,<0.12.0", "mayavi>=4.7.0", - "moderngl>=5.6.*,<6.0", "k3d<=2.9.2", "ipywidgets<8"] + "moderngl>=5.6,<6.0", "k3d<=2.9.2", "ipywidgets<8"] setup( name="menpo3d",