From 3b92ab8413c763aa401a1f78e5cc0a18d66adf25 Mon Sep 17 00:00:00 2001 From: apapaion Date: Fri, 13 Jul 2018 15:30:42 +0100 Subject: [PATCH 01/57] Comment fileno as it doesnt work with jupiter --- menpo3d/correspond/nicp.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/menpo3d/correspond/nicp.py b/menpo3d/correspond/nicp.py index 10551e8..9f360ad 100644 --- a/menpo3d/correspond/nicp.py +++ b/menpo3d/correspond/nicp.py @@ -41,7 +41,7 @@ def redirect_stdout(to): redirect_stdout(to=old_stdout) try: - + try: # First try the newer scikit-sparse namespace from sksparse.cholmod import cholesky_AAt @@ -54,8 +54,10 @@ def spsolve(sparse_X, dense_b): # wrap the cholesky call in a context manager that swallows the # low-level std-out to stop it from swamping our stdout (these low-level # prints come from METIS, but the solution behaves as normal) - with stdout_redirected(): - factor = cholesky_AAt(sparse_X.T) + # fileno doesnt seem to work when called by Jupyter + # comment by Thanos + # with stdout_redirected(): + factor = cholesky_AAt(sparse_X.T) return factor(sparse_X.T.dot(dense_b)).toarray() except ImportError: From 6d346451eb385ac5b8ed10e17af03f8d6f5aaa8c Mon Sep 17 00:00:00 2001 From: apapaion Date: Wed, 6 Feb 2019 14:21:51 +0000 Subject: [PATCH 02/57] Adda control if fileno is used. It causes error with Juputer UnsupportedOperation:fileno --- menpo3d/correspond/nicp.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/menpo3d/correspond/nicp.py b/menpo3d/correspond/nicp.py index 9f360ad..aa5ee81 100644 --- a/menpo3d/correspond/nicp.py +++ b/menpo3d/correspond/nicp.py @@ -56,8 +56,14 @@ def spsolve(sparse_X, dense_b): # prints come from METIS, but the solution behaves as normal) # fileno doesnt seem to work when called by Jupyter # comment by Thanos - # with stdout_redirected(): - factor = cholesky_AAt(sparse_X.T) + try: + __IPYTHON__ + except NameError: + with stdout_redirected(): + factor = cholesky_AAt(sparse_X.T) + else: + factor = cholesky_AAt(sparse_X.T) + return factor(sparse_X.T.dot(dense_b)).toarray() except ImportError: From 1505e03e27e1e8f5d1cf66fd30b990738607053f Mon Sep 17 00:00:00 2001 From: Thanos Papaioannou Date: Thu, 23 May 2019 16:05:50 +0100 Subject: [PATCH 03/57] The accepted version of menpo include 0.9 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index eefa95d..6eae463 100644 --- a/setup.py +++ b/setup.py @@ -73,7 +73,7 @@ def build_extension_from_pyx(pyx_path, extra_sources_paths=None): ] cython_exts = cythonize(cython_modules, quiet=True) -install_requires = ['menpo>=0.8,<0.9', +install_requires = ['menpo>=0.8,<=0.9', 'mayavi>=4.5.0'] setup(name='menpo3d', From 2d997641ff75973d1693a1d8f5be23cf792941bc Mon Sep 17 00:00:00 2001 From: Thanos Date: Fri, 15 Nov 2019 14:27:25 +0000 Subject: [PATCH 04/57] Add scikit-sparse dependency at setup. Needed for faster registration --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index 3caa0fc..1883766 100644 --- a/setup.py +++ b/setup.py @@ -86,6 +86,7 @@ def build_extension_from_pyx(pyx_path, extra_sources_paths=None): install_requires = ['menpo>=0.9.0,<0.11.0', 'mayavi>=4.7.0', + 'scikit-sparse>=0.3.1', 'moderngl>=5.5.*,<6.0'] setup(name='menpo3d', From dbb8445164435b946e188662b8630ac4b462f568 Mon Sep 17 00:00:00 2001 From: Thanos Date: Tue, 8 Sep 2020 13:00:49 +0000 Subject: [PATCH 05/57] Add itkwidgets option(inline) for view for TriMesh and PointCloud --- menpo3d/visualize/__init__.py | 3 +- menpo3d/visualize/base.py | 6 + menpo3d/visualize/viewitkwidgets.py | 536 ++++++++++++++++++++++++++++ setup.py | 2 +- 4 files changed, 545 insertions(+), 2 deletions(-) create mode 100644 menpo3d/visualize/viewitkwidgets.py diff --git a/menpo3d/visualize/__init__.py b/menpo3d/visualize/__init__.py index 31a1ab1..01baeb5 100644 --- a/menpo3d/visualize/__init__.py +++ b/menpo3d/visualize/__init__.py @@ -1,3 +1,4 @@ from .base import (PointGraphViewer3d, TriMeshViewer3d, VectorViewer3d, ColouredTriMeshViewer3d, TexturedTriMeshViewer3d, - LandmarkViewer3d) + LandmarkViewer3d, TriMeshInlineViewer3d, + PointGraphInlineViewer3d) diff --git a/menpo3d/visualize/base.py b/menpo3d/visualize/base.py index 294c00a..33219ea 100644 --- a/menpo3d/visualize/base.py +++ b/menpo3d/visualize/base.py @@ -3,9 +3,15 @@ MayaviTexturedTriMeshViewer3d, MayaviLandmarkViewer3d, MayaviVectorViewer3d, MayaviColouredTriMeshViewer3d) +from .viewitkwidgets import (ItkwidgetsTriMeshViewer3d, + ItkwidgetsPointGraphViewer3d) + PointGraphViewer3d = MayaviPointGraphViewer3d TriMeshViewer3d = MayaviTriMeshViewer3d TexturedTriMeshViewer3d = MayaviTexturedTriMeshViewer3d ColouredTriMeshViewer3d = MayaviColouredTriMeshViewer3d LandmarkViewer3d = MayaviLandmarkViewer3d VectorViewer3d = MayaviVectorViewer3d + +TriMeshInlineViewer3d = ItkwidgetsTriMeshViewer3d +PointGraphInlineViewer3d = ItkwidgetsPointGraphViewer3d diff --git a/menpo3d/visualize/viewitkwidgets.py b/menpo3d/visualize/viewitkwidgets.py new file mode 100644 index 0000000..1b94eee --- /dev/null +++ b/menpo3d/visualize/viewitkwidgets.py @@ -0,0 +1,536 @@ +import numpy as np +from menpo.visualize import Renderer +from menpo.shape import TriMesh +from ..vtkutils import trimesh_to_vtk +from itkwidgets import Viewer + + +# 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 ColorConverter + return ColorConverter().to_rgb(colour) + + +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 ItkwidgetsRenderer(Viewer, Renderer): + """ Abstract class for performing visualizations using Itkwidgets. + + Parameters + ---------- + figure_id : str or `None` + A figure name or `None`. `None` assumes we maintain the Mayavi + state machine and use `mlab.gcf()`. + new_figure : bool + If `True`, creates a new figure to render on. + """ + def __init__(self, figure_id, new_figure): + super(ItkwidgetsRenderer, self).__init__() + self.figure_id = figure_id + self.new_figure = new_figure +# 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 + self._actors = [] + + 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. + + :type: `int` + """ + pass + + @property + def height(self): + r""" + The height of the scene in pixels. + + :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. + """ + from pyface.api import GUI + _gui = GUI() + orig_val = _gui.busy + _gui.set_busy(busy=True) + _gui.set_busy(busy=orig_val) + _gui.process_events() + + +class ItkwidgetsVectorViewer3d(ItkwidgetsRenderer): + def __init__(self, figure_id, new_figure, points, vectors): + super(ItkwidgetsVectorViewer3d, 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 ItkwidgetsPointGraphViewer3d(ItkwidgetsRenderer): + def __init__(self, figure_id, new_figure, points, edges): + super(ItkwidgetsPointGraphViewer3d, self).__init__(figure_id, + new_figure) + self.points = points + self.trilist = edges + + def render(self, render_lines=True, line_colour='r', line_width=2, + render_markers=True, marker_style='spheres', marker_size=None, + 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 + if render_lines: + line_colour = _parse_colour(line_colour) + # TODO: Make step work for lines as well + # Create the points + if step is None: + step = 1 +# src = mlab.pipeline.scalar_scatter(self.points[:, 0], +# self.points[:, 1], +# self.points[:, 2]) +# # Connect them +# src.mlab_source.dataset.lines = self.edges +# # The stripper filter cleans up connected lines +# lines = mlab.pipeline.stripper(src) +# +# # Finally, display the set of lines +# mlab.pipeline.surface(lines, figure=self.figure, opacity=alpha, +# line_width=line_width, color=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, ItkwidgetsRenderer): + 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 = 'spheres' + + default_camera = np.asarray([[-0.27, 0.51, 5.06], + [-0.11, 0.32, 0.00], + [0.02, 1.0, -0.03]]).astype('float32') + if widg_to_draw is self: + widg_to_draw.camera = default_camera + + if widg_to_draw.point_sets is None: + widg_to_draw.point_sets = self.points.astype('float32') + widg_to_draw.point_set_sizes = np.asarray([marker_size]).astype('uint8') + widg_to_draw.point_set_representations = [marker_style] + widg_to_draw.point_set_colors = [marker_colour] + else: + widg_to_draw.point_sets = widg_to_draw.point_sets + [self.points.astype('float32')] + widg_to_draw.point_set_colors[-1] = np.asarray(marker_colour).reshape(-1,3).astype('float32') + widg_to_draw.point_set_sizes[-1] = np.asarray([marker_size]).astype('uint8') + widg_to_draw.point_set_representations[-1] = marker_style + # 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 ItkwidgetsTriMeshViewer3d(ItkwidgetsRenderer): + def __init__(self, figure_id, new_figure, points, trilist, landmarks=None): + super(ItkwidgetsTriMeshViewer3d, 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) + vtk_mesh = trimesh_to_vtk(TriMesh(self.points.astype('float32'), + self.trilist)) + + widg_to_draw = self + if not self.new_figure: + for widg in self.widgets.values(): + if isinstance(widg, ItkwidgetsRenderer): + if widg.figure_id == self.figure_id and widg.model_id != self.model_id: + widg_to_draw = widg + break + + if widg_to_draw.geometries is None: + widg_to_draw.geometries = [vtk_mesh] + widg_to_draw.geometry_colors = [colour] + else: + widg_to_draw.geometries = widg_to_draw.geometries + [vtk_mesh] + widg_to_draw.geometry_colors[-1] = colour + + #if self.geometries is None: + self.camera = np.asarray([[-0.27, 0.51, 5.06], + [-0.11, 0.32, 0.00], + [0.02, 1.0, -0.03]]).astype('float32') + if hasattr(self.landmarks, 'points'): + if widg_to_draw.point_sets is None: + widg_to_draw.point_sets = self.landmarks.points + widg_to_draw.point_set_sizes = [10] + widg_to_draw.point_set_representations = ['spheres'] + widg_to_draw.point_set_colors = [[1, 0, 0]] + else: + widg_to_draw.point_sets = widg_to_draw.point_sets + [self.landmarks.points] + widg_to_draw.point_set_sizes = widg_to_draw.point_set_sizes + [10] + widg_to_draw.point_set_representations[-1] = 'spheres' + + 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: + ItkwidgetsVectorViewer3d(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 ItkwidgetsTexturedTriMeshViewer3d(ItkwidgetsRenderer): + def __init__(self, figure_id, new_figure, points, trilist, texture, + tcoords_per_point): + super(ItkwidgetsTexturedTriMeshViewer3d, self).__init__(figure_id, + new_figure) + self.points = points + self.trilist = trilist + self.texture = texture + self.tcoords_per_point = tcoords_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.t_coords = self.tcoords_per_point + 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) + # Get the pixels from our image class which are [0, 1] and scale + # back to valid pixels. Then convert to tvtk ImageData. + texture = self.texture.pixels_with_channels_at_back(out_dtype=np.uint8) + if self.texture.n_channels == 1: + texture = np.stack([texture] * 3, axis=-1) + image_data = np.flipud(texture).ravel() + image_data = image_data.reshape([-1, 3]) + image = tvtk.ImageData() + image.point_data.scalars = image_data + image.dimensions = self.texture.width, self.texture.height, 1 + texture = tvtk.Texture() + texture.set_input_data(image) + actor.texture = texture + 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: + ItkwidgetsVectorViewer3d(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 ItkwidgetsColouredTriMeshViewer3d(ItkwidgetsRenderer): + def __init__(self, figure_id, new_figure, points, trilist, + colour_per_point): + super(ItkwidgetsColouredTriMeshViewer3d, 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: + ItkwidgetsVectorViewer3d(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 ItkwidgetsSurfaceViewer3d(ItkwidgetsRenderer): + def __init__(self, figure_id, new_figure, values, mask=None): + super(ItkwidgetsSurfaceViewer3d, 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 ItkwidgetsLandmarkViewer3d(ItkwidgetsRenderer): + def __init__(self, figure_id, new_figure, group, landmark_group): + super(ItkwidgetsLandmarkViewer3d, 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] diff --git a/setup.py b/setup.py index 1883766..8a2aabb 100644 --- a/setup.py +++ b/setup.py @@ -84,7 +84,7 @@ def build_extension_from_pyx(pyx_path, extra_sources_paths=None): ] cython_exts = cythonize(cython_modules, quiet=True) -install_requires = ['menpo>=0.9.0,<0.11.0', +install_requires = ['menpo>=0.8.0,<0.11.0', 'mayavi>=4.7.0', 'scikit-sparse>=0.3.1', 'moderngl>=5.5.*,<6.0'] From f0dec19126d96c211d6797a3a679e55eadcdc90b Mon Sep 17 00:00:00 2001 From: Thanos Date: Thu, 10 Sep 2020 17:28:29 +0000 Subject: [PATCH 06/57] Add K3d widgets for PointCloud, TriMesh and TexturedTriMesh --- menpo3d/visualize/__init__.py | 2 +- menpo3d/visualize/base.py | 13 +- menpo3d/visualize/viewk3dwidgets.py | 530 ++++++++++++++++++++++++++++ 3 files changed, 540 insertions(+), 5 deletions(-) create mode 100644 menpo3d/visualize/viewk3dwidgets.py diff --git a/menpo3d/visualize/__init__.py b/menpo3d/visualize/__init__.py index 01baeb5..69cd336 100644 --- a/menpo3d/visualize/__init__.py +++ b/menpo3d/visualize/__init__.py @@ -1,4 +1,4 @@ from .base import (PointGraphViewer3d, TriMeshViewer3d, VectorViewer3d, ColouredTriMeshViewer3d, TexturedTriMeshViewer3d, LandmarkViewer3d, TriMeshInlineViewer3d, - PointGraphInlineViewer3d) + PointGraphInlineViewer3d, TexturedTriMeshInlineViewer3d) diff --git a/menpo3d/visualize/base.py b/menpo3d/visualize/base.py index 33219ea..f4895ee 100644 --- a/menpo3d/visualize/base.py +++ b/menpo3d/visualize/base.py @@ -3,8 +3,12 @@ MayaviTexturedTriMeshViewer3d, MayaviLandmarkViewer3d, MayaviVectorViewer3d, MayaviColouredTriMeshViewer3d) -from .viewitkwidgets import (ItkwidgetsTriMeshViewer3d, - ItkwidgetsPointGraphViewer3d) +# from .viewitkwidgets import (ItkwidgetsTriMeshViewer3d, +# ItkwidgetsPointGraphViewer3d) + +from .viewk3dwidgets import (K3dwidgetsTriMeshViewer3d, + K3dwidgetsPointGraphViewer3d, + K3dwidgetsTexturedTriMeshViewer3d) PointGraphViewer3d = MayaviPointGraphViewer3d TriMeshViewer3d = MayaviTriMeshViewer3d @@ -13,5 +17,6 @@ LandmarkViewer3d = MayaviLandmarkViewer3d VectorViewer3d = MayaviVectorViewer3d -TriMeshInlineViewer3d = ItkwidgetsTriMeshViewer3d -PointGraphInlineViewer3d = ItkwidgetsPointGraphViewer3d +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 628c16f290efede85b4898a9daba56e8cb982457 Mon Sep 17 00:00:00 2001 From: Thanos Date: Fri, 11 Sep 2020 12:17:39 +0000 Subject: [PATCH 07/57] Add k3d dependency --- setup.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 8a2aabb..d3f0e8e 100644 --- a/setup.py +++ b/setup.py @@ -87,7 +87,8 @@ def build_extension_from_pyx(pyx_path, extra_sources_paths=None): install_requires = ['menpo>=0.8.0,<0.11.0', 'mayavi>=4.7.0', 'scikit-sparse>=0.3.1', - 'moderngl>=5.5.*,<6.0'] + 'moderngl>=5.5.*,<6.0', + 'k3d'] setup(name='menpo3d', version=versioneer.get_version(), From d318f161058c40c664742ed1aefdc24ee7334124 Mon Sep 17 00:00:00 2001 From: Thanos Date: Fri, 11 Sep 2020 14:10:56 +0000 Subject: [PATCH 08/57] Add examples folders with the first example for the K3dWidgets --- .../0_Introduction_to_K3d_Widgets.ipynb | 182 ++++++++++++++++++ menpo3d/visualize/viewk3dwidgets.py | 4 +- 2 files changed, 184 insertions(+), 2 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..26e64c6 --- /dev/null +++ b/menpo3d/examples/0_Introduction_to_K3d_Widgets.ipynb @@ -0,0 +1,182 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 6, + "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": 2, + "metadata": {}, + "outputs": [], + "source": [ + "lms = m3io.import_landmark_file('../data/james.ljson')['LJSON']" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

Show the mesh

" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "f166c86344804e30940dcd606beaaa6b", + "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": 5, + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "6cc0cac5dc5342bfac75d0af5597914f", + "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": 7, + "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": 10, + "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": "794fa83e48264e50801a177331151a83", + "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)" + ] + } + ], + "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/viewk3dwidgets.py b/menpo3d/visualize/viewk3dwidgets.py index 2438a76..77f44a5 100644 --- a/menpo3d/visualize/viewk3dwidgets.py +++ b/menpo3d/visualize/viewk3dwidgets.py @@ -326,13 +326,12 @@ def _render_mesh(self, mesh_type, line_width, colour, marker_size, shader='mesh') widg_to_draw += points_to_add - # TODO + # 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', @@ -362,6 +361,7 @@ def __init__(self, figure_id, new_figure, points, trilist, texture, self.texture = texture self.tcoords = tcoords self.landmarks = landmarks + self.lighting = 0 def _render_mesh(self, mesh_type='surface', ambient_light=0.0, specular_light=0.0, alpha=1.0): From 2bd57fc3410ce7a81502eee0c95fc1b06538126d Mon Sep 17 00:00:00 2001 From: Thanos Date: Fri, 11 Sep 2020 21:28:22 +0000 Subject: [PATCH 09/57] Add Landmark Inline Viewer --- .../0_Introduction_to_K3d_Widgets.ipynb | 84 ++++++++++++++++--- menpo3d/visualize/__init__.py | 3 +- menpo3d/visualize/base.py | 2 + menpo3d/visualize/viewk3dwidgets.py | 67 +++++++-------- 4 files changed, 110 insertions(+), 46 deletions(-) diff --git a/menpo3d/examples/0_Introduction_to_K3d_Widgets.ipynb b/menpo3d/examples/0_Introduction_to_K3d_Widgets.ipynb index 26e64c6..c743e4b 100644 --- a/menpo3d/examples/0_Introduction_to_K3d_Widgets.ipynb +++ b/menpo3d/examples/0_Introduction_to_K3d_Widgets.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": 6, + "execution_count": 2, "metadata": {}, "outputs": [], "source": [ @@ -28,13 +28,45 @@ }, { "cell_type": "code", - "execution_count": 2, + "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": {}, @@ -44,13 +76,13 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "f166c86344804e30940dcd606beaaa6b", + "model_id": "0bde20db4f22458b93c2ad4d45511043", "version_major": 2, "version_minor": 0 }, @@ -75,7 +107,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 7, "metadata": { "scrolled": true }, @@ -83,7 +115,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "6cc0cac5dc5342bfac75d0af5597914f", + "model_id": "f3c3263493f14257a1564943b1fcc96a", "version_major": 2, "version_minor": 0 }, @@ -101,7 +133,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 8, "metadata": {}, "outputs": [], "source": [ @@ -127,7 +159,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 14, "metadata": {}, "outputs": [ { @@ -141,7 +173,39 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "794fa83e48264e50801a177331151a83", + "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 }, @@ -154,7 +218,7 @@ } ], "source": [ - "mesh.view(inline=True)" + "mesh.view(inline=True,figure_id='test')" ] } ], diff --git a/menpo3d/visualize/__init__.py b/menpo3d/visualize/__init__.py index 69cd336..3a4e826 100644 --- a/menpo3d/visualize/__init__.py +++ b/menpo3d/visualize/__init__.py @@ -1,4 +1,5 @@ from .base import (PointGraphViewer3d, TriMeshViewer3d, VectorViewer3d, ColouredTriMeshViewer3d, TexturedTriMeshViewer3d, LandmarkViewer3d, TriMeshInlineViewer3d, - PointGraphInlineViewer3d, TexturedTriMeshInlineViewer3d) + PointGraphInlineViewer3d, TexturedTriMeshInlineViewer3d, + LandmarkInlineViewer3d) diff --git a/menpo3d/visualize/base.py b/menpo3d/visualize/base.py index f4895ee..6dfd779 100644 --- a/menpo3d/visualize/base.py +++ b/menpo3d/visualize/base.py @@ -8,6 +8,7 @@ from .viewk3dwidgets import (K3dwidgetsTriMeshViewer3d, K3dwidgetsPointGraphViewer3d, + K3dwidgetsLandmarkViewer3d, K3dwidgetsTexturedTriMeshViewer3d) PointGraphViewer3d = MayaviPointGraphViewer3d @@ -19,4 +20,5 @@ TriMeshInlineViewer3d = K3dwidgetsTriMeshViewer3d TexturedTriMeshInlineViewer3d = K3dwidgetsTexturedTriMeshViewer3d +LandmarkInlineViewer3d = K3dwidgetsLandmarkViewer3d PointGraphInlineViewer3d = K3dwidgetsPointGraphViewer3d diff --git a/menpo3d/visualize/viewk3dwidgets.py b/menpo3d/visualize/viewk3dwidgets.py index 77f44a5..7c1845b 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,11 +315,13 @@ 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 - + 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, @@ -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 badf6d608dc28739bcf4afda07a3761b26fc238b Mon Sep 17 00:00:00 2001 From: Thanos Date: Mon, 14 Sep 2020 12:07:24 +0000 Subject: [PATCH 10/57] - 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 7c1845b..714d01e 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 ac17cd0c526c2f23d77ceda652bbda0579270dd4 Mon Sep 17 00:00:00 2001 From: Thanos Date: Wed, 16 Sep 2020 10:40:49 +0000 Subject: [PATCH 11/57] 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 6dfd779..def86b2 100644 --- a/menpo3d/visualize/base.py +++ b/menpo3d/visualize/base.py @@ -9,7 +9,8 @@ from .viewk3dwidgets import (K3dwidgetsTriMeshViewer3d, K3dwidgetsPointGraphViewer3d, K3dwidgetsLandmarkViewer3d, - K3dwidgetsTexturedTriMeshViewer3d) + K3dwidgetsTexturedTriMeshViewer3d, + K3dwidgetsPCAModelViewer3d) PointGraphViewer3d = MayaviPointGraphViewer3d TriMeshViewer3d = MayaviTriMeshViewer3d @@ -22,3 +23,4 @@ TexturedTriMeshInlineViewer3d = K3dwidgetsTexturedTriMeshViewer3d LandmarkInlineViewer3d = K3dwidgetsLandmarkViewer3d PointGraphInlineViewer3d = K3dwidgetsPointGraphViewer3d +PCAModelInlineViewer3d = K3dwidgetsPCAModelViewer3d diff --git a/menpo3d/visualize/viewk3dwidgets.py b/menpo3d/visualize/viewk3dwidgets.py index 714d01e..200622f 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 1dd9f0c992c214b56f49665884783111036062a5 Mon Sep 17 00:00:00 2001 From: Thanos Date: Wed, 16 Sep 2020 10:48:11 +0000 Subject: [PATCH 12/57] 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 ec200df8d2c160b9a18a89efe0cfd88c57095ad0 Mon Sep 17 00:00:00 2001 From: Thanos Date: Wed, 16 Sep 2020 15:26:32 +0000 Subject: [PATCH 13/57] 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 98a371fdb8904f4c81005a13d951745795ec8fd2 Mon Sep 17 00:00:00 2001 From: Thanos Date: Wed, 16 Sep 2020 15:27:27 +0000 Subject: [PATCH 14/57] 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 d3767c27268217400d3c177395b61f9b03300e73 Mon Sep 17 00:00:00 2001 From: Thanos Date: Wed, 16 Sep 2020 15:28:25 +0000 Subject: [PATCH 15/57] Add a reduced 3DMM in data folder From 9237de47d83015031c123b3f99b8278b724db7f5 Mon Sep 17 00:00:00 2001 From: Thanos Date: Mon, 5 Oct 2020 21:08:16 +0000 Subject: [PATCH 16/57] 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 200622f..30197d8 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 7a399df48cf2be20d160aa93c1aa08d21df1011c Mon Sep 17 00:00:00 2001 From: Thanos Date: Mon, 5 Oct 2020 23:12:51 +0000 Subject: [PATCH 17/57] 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 30197d8..559a344 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 a82de414126fb54a054007ebc5b05ecf5ce75d04 Mon Sep 17 00:00:00 2001 From: Thanos Date: Mon, 5 Oct 2020 23:18:33 +0000 Subject: [PATCH 18/57] 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 559a344..5ccee97 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 57ad92b3d483f318190c327cfb2c4d6815389032 Mon Sep 17 00:00:00 2001 From: Thanos Date: Mon, 5 Oct 2020 23:25:29 +0000 Subject: [PATCH 19/57] 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 5ccee97..d91d3a2 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 8a5d73e428b586ab5f98eeff8b94ce7709628625 Mon Sep 17 00:00:00 2001 From: Thanos Date: Tue, 6 Oct 2020 09:58:38 +0000 Subject: [PATCH 20/57] Remove dependency on mayavi. If someone wants to use it, she should install it manually. As a consequence, we had to add dependency on vtk. --- setup.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index d3f0e8e..4851d3c 100644 --- a/setup.py +++ b/setup.py @@ -85,7 +85,8 @@ def build_extension_from_pyx(pyx_path, extra_sources_paths=None): cython_exts = cythonize(cython_modules, quiet=True) install_requires = ['menpo>=0.8.0,<0.11.0', - 'mayavi>=4.7.0', + 'vtk', + # 'mayavi>=4.7.0', 'scikit-sparse>=0.3.1', 'moderngl>=5.5.*,<6.0', 'k3d'] From 19d651dcf0a10b8776814c4a313521e2b500bbe9 Mon Sep 17 00:00:00 2001 From: Thanos Date: Wed, 7 Oct 2020 15:28:42 +0000 Subject: [PATCH 21/57] 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 | 5 ++- menpo3d/visualize/viewk3dwidgets.py | 69 +++++++++++++++++++++++++---- menpo3d/visualize/viewmayavi.py | 48 ++++++++++++++++++++ 4 files changed, 115 insertions(+), 10 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 def86b2..41b42ee 100644 --- a/menpo3d/visualize/base.py +++ b/menpo3d/visualize/base.py @@ -1,7 +1,7 @@ from .viewmayavi import ( MayaviTriMeshViewer3d, MayaviPointGraphViewer3d, MayaviTexturedTriMeshViewer3d, MayaviLandmarkViewer3d, - MayaviVectorViewer3d, MayaviColouredTriMeshViewer3d) + MayaviVectorViewer3d, MayaviColouredTriMeshViewer3d, MayaviHeatmapViewer3d) # from .viewitkwidgets import (ItkwidgetsTriMeshViewer3d, # ItkwidgetsPointGraphViewer3d) @@ -10,6 +10,7 @@ K3dwidgetsPointGraphViewer3d, K3dwidgetsLandmarkViewer3d, K3dwidgetsTexturedTriMeshViewer3d, + K3dwidgetsHeatmapViewer3d, K3dwidgetsPCAModelViewer3d) PointGraphViewer3d = MayaviPointGraphViewer3d @@ -18,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 d91d3a2..1be65d5 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 a88faa9..1c44d95 100644 --- a/menpo3d/visualize/viewmayavi.py +++ b/menpo3d/visualize/viewmayavi.py @@ -506,3 +506,51 @@ 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 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 2f5fc44c10539bdb55c9c18ee9f6c1d0253db374 Mon Sep 17 00:00:00 2001 From: Thanos Date: Thu, 8 Oct 2020 09:53:50 +0000 Subject: [PATCH 22/57] 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 f98708816d66e267cb92e34eda5aef360ee6916f Mon Sep 17 00:00:00 2001 From: Thanos Date: Thu, 8 Oct 2020 09:58:02 +0000 Subject: [PATCH 23/57] 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 d7e6d2d256e82d17c123346f9076247dcea29feb Mon Sep 17 00:00:00 2001 From: Thanos Date: Thu, 8 Oct 2020 12:15:21 +0000 Subject: [PATCH 24/57] Initialize spirals module. Add adj_trigs method --- menpo3d/spirals/__init__.py | 1 + menpo3d/spirals/base.py | 26 ++++++++++++++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 menpo3d/spirals/__init__.py create mode 100644 menpo3d/spirals/base.py diff --git a/menpo3d/spirals/__init__.py b/menpo3d/spirals/__init__.py new file mode 100644 index 0000000..f88a36b --- /dev/null +++ b/menpo3d/spirals/__init__.py @@ -0,0 +1 @@ +from .base import adj_trigs diff --git a/menpo3d/spirals/base.py b/menpo3d/spirals/base.py new file mode 100644 index 0000000..cc45100 --- /dev/null +++ b/menpo3d/spirals/base.py @@ -0,0 +1,26 @@ +def adj_trigs(list_meshes): + r""" + Return a list of adjancency lists and a list of faces per vertex list + for each mesh in the list + + Parameters + ---------- + list_meshes : list of TriMesh + + Returns + ------- + Adj: list + for each Trimesh in list_meshes, find its adjancency list + and add it in this list + Trigs: list + for each Trimesh in list_meshes, for each vertex, find its faces + (a list of (3,) lists) and add it in this list + """ + + Adj = [] + Trigs = [] + + for mesh in list_meshes: + Adj.append(mesh.as_pointgraph().get_adjacency_list()) + Trigs.append(mesh.list_faces_per_vertex()) + return Adj, Trigs From b411095fd255bc25bda0bc3d19d6c23aae027aa9 Mon Sep 17 00:00:00 2001 From: Thanos Date: Thu, 8 Oct 2020 14:44:40 +0000 Subject: [PATCH 25/57] Remove viewitkwidgets --- menpo3d/visualize/viewitkwidgets.py | 536 ---------------------------- 1 file changed, 536 deletions(-) delete mode 100644 menpo3d/visualize/viewitkwidgets.py diff --git a/menpo3d/visualize/viewitkwidgets.py b/menpo3d/visualize/viewitkwidgets.py deleted file mode 100644 index 1b94eee..0000000 --- a/menpo3d/visualize/viewitkwidgets.py +++ /dev/null @@ -1,536 +0,0 @@ -import numpy as np -from menpo.visualize import Renderer -from menpo.shape import TriMesh -from ..vtkutils import trimesh_to_vtk -from itkwidgets import Viewer - - -# 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 ColorConverter - return ColorConverter().to_rgb(colour) - - -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 ItkwidgetsRenderer(Viewer, Renderer): - """ Abstract class for performing visualizations using Itkwidgets. - - Parameters - ---------- - figure_id : str or `None` - A figure name or `None`. `None` assumes we maintain the Mayavi - state machine and use `mlab.gcf()`. - new_figure : bool - If `True`, creates a new figure to render on. - """ - def __init__(self, figure_id, new_figure): - super(ItkwidgetsRenderer, self).__init__() - self.figure_id = figure_id - self.new_figure = new_figure -# 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 - self._actors = [] - - 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. - - :type: `int` - """ - pass - - @property - def height(self): - r""" - The height of the scene in pixels. - - :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. - """ - from pyface.api import GUI - _gui = GUI() - orig_val = _gui.busy - _gui.set_busy(busy=True) - _gui.set_busy(busy=orig_val) - _gui.process_events() - - -class ItkwidgetsVectorViewer3d(ItkwidgetsRenderer): - def __init__(self, figure_id, new_figure, points, vectors): - super(ItkwidgetsVectorViewer3d, 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 ItkwidgetsPointGraphViewer3d(ItkwidgetsRenderer): - def __init__(self, figure_id, new_figure, points, edges): - super(ItkwidgetsPointGraphViewer3d, self).__init__(figure_id, - new_figure) - self.points = points - self.trilist = edges - - def render(self, render_lines=True, line_colour='r', line_width=2, - render_markers=True, marker_style='spheres', marker_size=None, - 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 - if render_lines: - line_colour = _parse_colour(line_colour) - # TODO: Make step work for lines as well - # Create the points - if step is None: - step = 1 -# src = mlab.pipeline.scalar_scatter(self.points[:, 0], -# self.points[:, 1], -# self.points[:, 2]) -# # Connect them -# src.mlab_source.dataset.lines = self.edges -# # The stripper filter cleans up connected lines -# lines = mlab.pipeline.stripper(src) -# -# # Finally, display the set of lines -# mlab.pipeline.surface(lines, figure=self.figure, opacity=alpha, -# line_width=line_width, color=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, ItkwidgetsRenderer): - 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 = 'spheres' - - default_camera = np.asarray([[-0.27, 0.51, 5.06], - [-0.11, 0.32, 0.00], - [0.02, 1.0, -0.03]]).astype('float32') - if widg_to_draw is self: - widg_to_draw.camera = default_camera - - if widg_to_draw.point_sets is None: - widg_to_draw.point_sets = self.points.astype('float32') - widg_to_draw.point_set_sizes = np.asarray([marker_size]).astype('uint8') - widg_to_draw.point_set_representations = [marker_style] - widg_to_draw.point_set_colors = [marker_colour] - else: - widg_to_draw.point_sets = widg_to_draw.point_sets + [self.points.astype('float32')] - widg_to_draw.point_set_colors[-1] = np.asarray(marker_colour).reshape(-1,3).astype('float32') - widg_to_draw.point_set_sizes[-1] = np.asarray([marker_size]).astype('uint8') - widg_to_draw.point_set_representations[-1] = marker_style - # 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 ItkwidgetsTriMeshViewer3d(ItkwidgetsRenderer): - def __init__(self, figure_id, new_figure, points, trilist, landmarks=None): - super(ItkwidgetsTriMeshViewer3d, 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) - vtk_mesh = trimesh_to_vtk(TriMesh(self.points.astype('float32'), - self.trilist)) - - widg_to_draw = self - if not self.new_figure: - for widg in self.widgets.values(): - if isinstance(widg, ItkwidgetsRenderer): - if widg.figure_id == self.figure_id and widg.model_id != self.model_id: - widg_to_draw = widg - break - - if widg_to_draw.geometries is None: - widg_to_draw.geometries = [vtk_mesh] - widg_to_draw.geometry_colors = [colour] - else: - widg_to_draw.geometries = widg_to_draw.geometries + [vtk_mesh] - widg_to_draw.geometry_colors[-1] = colour - - #if self.geometries is None: - self.camera = np.asarray([[-0.27, 0.51, 5.06], - [-0.11, 0.32, 0.00], - [0.02, 1.0, -0.03]]).astype('float32') - if hasattr(self.landmarks, 'points'): - if widg_to_draw.point_sets is None: - widg_to_draw.point_sets = self.landmarks.points - widg_to_draw.point_set_sizes = [10] - widg_to_draw.point_set_representations = ['spheres'] - widg_to_draw.point_set_colors = [[1, 0, 0]] - else: - widg_to_draw.point_sets = widg_to_draw.point_sets + [self.landmarks.points] - widg_to_draw.point_set_sizes = widg_to_draw.point_set_sizes + [10] - widg_to_draw.point_set_representations[-1] = 'spheres' - - 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: - ItkwidgetsVectorViewer3d(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 ItkwidgetsTexturedTriMeshViewer3d(ItkwidgetsRenderer): - def __init__(self, figure_id, new_figure, points, trilist, texture, - tcoords_per_point): - super(ItkwidgetsTexturedTriMeshViewer3d, self).__init__(figure_id, - new_figure) - self.points = points - self.trilist = trilist - self.texture = texture - self.tcoords_per_point = tcoords_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.t_coords = self.tcoords_per_point - 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) - # Get the pixels from our image class which are [0, 1] and scale - # back to valid pixels. Then convert to tvtk ImageData. - texture = self.texture.pixels_with_channels_at_back(out_dtype=np.uint8) - if self.texture.n_channels == 1: - texture = np.stack([texture] * 3, axis=-1) - image_data = np.flipud(texture).ravel() - image_data = image_data.reshape([-1, 3]) - image = tvtk.ImageData() - image.point_data.scalars = image_data - image.dimensions = self.texture.width, self.texture.height, 1 - texture = tvtk.Texture() - texture.set_input_data(image) - actor.texture = texture - 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: - ItkwidgetsVectorViewer3d(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 ItkwidgetsColouredTriMeshViewer3d(ItkwidgetsRenderer): - def __init__(self, figure_id, new_figure, points, trilist, - colour_per_point): - super(ItkwidgetsColouredTriMeshViewer3d, 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: - ItkwidgetsVectorViewer3d(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 ItkwidgetsSurfaceViewer3d(ItkwidgetsRenderer): - def __init__(self, figure_id, new_figure, values, mask=None): - super(ItkwidgetsSurfaceViewer3d, 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 ItkwidgetsLandmarkViewer3d(ItkwidgetsRenderer): - def __init__(self, figure_id, new_figure, group, landmark_group): - super(ItkwidgetsLandmarkViewer3d, 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 a6633db67f1a8c9e465db85f0d17685f0f95415d Mon Sep 17 00:00:00 2001 From: Thanos Date: Fri, 9 Oct 2020 16:16:22 +0000 Subject: [PATCH 26/57] 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 1be65d5..1cd4936 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') @@ -385,22 +388,21 @@ def _render_mesh(self, mesh_type, line_width, colour, marker_size, 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 73186f5be7c03487b677a1cda685323f8a038483 Mon Sep 17 00:00:00 2001 From: Thanos Date: Sun, 11 Oct 2020 22:48:44 +0000 Subject: [PATCH 27/57] 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 1cd4936..79f7c3d 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 @@ -396,14 +396,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 5d07cc9d4ed893df5e0541cd38e52c0211b932d6 Mon Sep 17 00:00:00 2001 From: Thanos Date: Sun, 11 Oct 2020 22:51:32 +0000 Subject: [PATCH 28/57] 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 be91bc694cd6012f732d803ad2c591e53691a768 Mon Sep 17 00:00:00 2001 From: Thanos Date: Mon, 12 Oct 2020 08:43:48 +0000 Subject: [PATCH 29/57] Checking and creation of widg_to_draw is happening in K3dRenderer for all child classes --- menpo3d/visualize/viewk3dwidgets.py | 60 +++++++++++++---------------- 1 file changed, 26 insertions(+), 34 deletions(-) diff --git a/menpo3d/visualize/viewk3dwidgets.py b/menpo3d/visualize/viewk3dwidgets.py index 79f7c3d..af9dbc2 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,12 +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] - return widg_to_draw def _render(self, line_width=2, colour='r', @@ -568,12 +566,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 +590,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 26437dc9d4553fa0698aabba33c229d6e812d5ad Mon Sep 17 00:00:00 2001 From: Thanos Date: Mon, 12 Oct 2020 11:21:13 +0000 Subject: [PATCH 30/57] 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 af9dbc2..c84ceb3 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 @@ -641,11 +642,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, @@ -665,17 +666,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 6fa44bc07580785cc2c377c12b879cd7236800ac Mon Sep 17 00:00:00 2001 From: Thanos Date: Mon, 12 Oct 2020 16:38:47 +0000 Subject: [PATCH 31/57] 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 c84ceb3..9547950 100644 --- a/menpo3d/visualize/viewk3dwidgets.py +++ b/menpo3d/visualize/viewk3dwidgets.py @@ -446,26 +446,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, @@ -473,27 +470,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( @@ -501,8 +495,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 @@ -646,7 +639,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 cb67ce8e1c4093da8e1fc9696bd699125dd966df Mon Sep 17 00:00:00 2001 From: Thanos Date: Tue, 13 Oct 2020 10:15:11 +0000 Subject: [PATCH 32/57] 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 9547950..97e5709 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""" @@ -485,7 +488,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 fbaa42e055248a12f802b6466256f73d1d8c21bc Mon Sep 17 00:00:00 2001 From: Thanos Date: Tue, 13 Oct 2020 10:16:43 +0000 Subject: [PATCH 33/57] 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 8498462d83bc3376408775224f2b883dde88c835 Mon Sep 17 00:00:00 2001 From: Thanos Date: Tue, 13 Oct 2020 13:41:10 +0000 Subject: [PATCH 34/57] decimate_mesh function added in vtkutils.py --- menpo3d/vtkutils.py | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/menpo3d/vtkutils.py b/menpo3d/vtkutils.py index 2fbe9f1..fc0a6e2 100644 --- a/menpo3d/vtkutils.py +++ b/menpo3d/vtkutils.py @@ -135,3 +135,43 @@ def _find_single_closest_point(self, point): self._sub_id, self._distance) return self._c_point[:], self._cell_id.get() + + +def decimate_mesh(mesh, reduction=0.75, type_reduction='quadric', **kwargs): + """ + Decimate this mesh specifying the percentage (0,1) of triangles to + be removed + + Parameters + ---------- + reduction: float (default: 0.75) + The percentage of triangles to be removed. + It should be in (0, 1) + + type_reduction : str (default: quadric) + The type of decimation as: + 'quadric' : Quadric decimation + 'progressive : Progressive decimation + Returns + ------- + mesh : :map:`TriMesh` + A new mesh that has been decimated. + """ + import vtk + if type_reduction == 'quadric': + decimate = vtk.vtkQuadricDecimation() + elif type_reduction == 'progressive': + decimate = vtk.vtkDecimatePro() + else: + raise Exception('Wrong type of reduction. It should be quadric or progressive') + + inputPolyData = trimesh_to_vtk(mesh) + decimate.SetInputData(inputPolyData) + decimate.SetTargetReduction(reduction) + + if kwargs.get('preserve_topology', False) and type_reduction == 'progressive': + decimate.PreserveTopologyOn() + + decimate.Update() + + return trimesh_from_vtk(decimate.GetOutput()) From 5e7fe2fea8c0d245bed1595a3b282405c98803f9 Mon Sep 17 00:00:00 2001 From: Thanos Date: Fri, 16 Oct 2020 16:30:13 +0000 Subject: [PATCH 35/57] 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 97e5709..5e6721e 100644 --- a/menpo3d/visualize/viewk3dwidgets.py +++ b/menpo3d/visualize/viewk3dwidgets.py @@ -467,13 +467,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() @@ -484,12 +485,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( @@ -635,6 +636,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) @@ -676,7 +678,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 55e1c14de24ab1ce4311efbaf2fda4a6d606dead Mon Sep 17 00:00:00 2001 From: Thanos Date: Wed, 18 Nov 2020 11:55:52 +0000 Subject: [PATCH 36/57] 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 484cab4e3151d16f69aa99a0885578f3d3813104 Mon Sep 17 00:00:00 2001 From: Thanos Date: Wed, 18 Nov 2020 15:46:42 +0000 Subject: [PATCH 37/57] Menpo requirement is now greater or equal to 0.9.0 in setup.py --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 4851d3c..0f1cc74 100644 --- a/setup.py +++ b/setup.py @@ -84,7 +84,7 @@ def build_extension_from_pyx(pyx_path, extra_sources_paths=None): ] cython_exts = cythonize(cython_modules, quiet=True) -install_requires = ['menpo>=0.8.0,<0.11.0', +install_requires = ['menpo>=0.9.0,<0.11.0', 'vtk', # 'mayavi>=4.7.0', 'scikit-sparse>=0.3.1', From 25ed92e06a0ab90745b1e9677183355ed6455c11 Mon Sep 17 00:00:00 2001 From: Athanasios Papaioannou 00570580 Date: Fri, 20 Nov 2020 09:47:18 +0000 Subject: [PATCH 38/57] Update README.md --- README.md | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index b724bca..41905f0 100644 --- a/README.md +++ b/README.md @@ -34,10 +34,14 @@ menpo3d adds support for viewing 3D objects through [Mayavi](http://code.enthought.com/projects/mayavi/), which is based on VTK. One of the main reasons menpo3d is a seperate project to the menpo core library is to isolate the more complex dependencies that this brings to the -project. 3D visualization is not yet supported in the browser, so we rely on -platform-specific viewing mechanisms like QT or WX. +project. ~~3D visualization is not yet supported in the browser, so we rely on +platform-specific viewing mechanisms like QT or WX.~~ In addition, menpo3d +supports 3D visualization in the browser using [K3D Jupyter](https://github.com/K3D-tools/K3D-jupyter) library which is a +Jupyter notebook extension for 3D visualization. -In order to view 3D items you will need to first use the `%matplotlib qt` + + +In order to view 3D items through mayavi you will need to first use the `%matplotlib qt` IPython magic command to set up QT for rendering (you do this instead of `%matplotlib inline` which is what is needed for using the usual Menpo Widgets). As a complete example, to view a mesh in IPython you @@ -71,3 +75,14 @@ Alternatively, try installing wxPython: conda install wxpython ``` and using `%matplotlib wx`. + + +In the case of K3D Jupyter visualization to view a mesh in Jupyter cell you +would run something like: +```python +import menpo3d +mesh = menpo3d.io.import_builtin_asset('james.obj') +``` +```python +mesh.view(inline=True) +``` From 8a6903f828826a692bcfb647a9352b1f75016ac6 Mon Sep 17 00:00:00 2001 From: Thanos Date: Fri, 20 Nov 2020 10:12:04 +0000 Subject: [PATCH 39/57] 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 d4da90d19ce4ca0a2ecdf84b67a5caba4612352d Mon Sep 17 00:00:00 2001 From: Thanos Date: Wed, 25 Nov 2020 11:13:39 +0000 Subject: [PATCH 40/57] 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 5e6721e..7f2ee38 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 @@ -443,9 +459,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 @@ -488,6 +503,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): @@ -568,6 +585,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): @@ -618,6 +638,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', @@ -675,6 +698,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 53667bbcb8c202ba8583f6fdfd047ee7952b321b Mon Sep 17 00:00:00 2001 From: Thanos Date: Thu, 26 Nov 2020 15:36:37 +0000 Subject: [PATCH 41/57] 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 7f2ee38..b34c96b 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 7beda9aa6d0da85fa0481f0808c29948167108f7 Mon Sep 17 00:00:00 2001 From: Thanos Date: Fri, 27 Nov 2020 19:06:04 +0000 Subject: [PATCH 42/57] 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 b34c96b..5b4170e 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""" @@ -658,7 +671,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): @@ -690,6 +703,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 f19938bc9bb6f030d4f3069e460f295382dcfd96 Mon Sep 17 00:00:00 2001 From: Thanos Date: Tue, 1 Dec 2020 14:27:59 +0000 Subject: [PATCH 43/57] 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 959b58c4f9786f69b85c24278346d8cff98ac3a8 Mon Sep 17 00:00:00 2001 From: Thanos Date: Wed, 16 Dec 2020 18:52:58 +0000 Subject: [PATCH 44/57] 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 5b4170e..6e685c8 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] @@ -685,7 +685,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 @@ -698,7 +701,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')) @@ -710,9 +717,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 @@ -731,11 +743,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 29a7ae1c697c31e9d2fde47b8565b757a704d260 Mon Sep 17 00:00:00 2001 From: Thanos Date: Wed, 16 Dec 2020 18:59:54 +0000 Subject: [PATCH 45/57] 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 146710f8fc1b3e68e97116b8d9d98e42c764c38f Mon Sep 17 00:00:00 2001 From: Thanos Date: Sun, 17 Jan 2021 17:02:26 +0000 Subject: [PATCH 46/57] 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 414b0e052b88d833e535e1d0361ba77097663769 Mon Sep 17 00:00:00 2001 From: Thanos Date: Sun, 17 Jan 2021 17:09:16 +0000 Subject: [PATCH 47/57] 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 612af7c46c43fb3c0ccede87353b9fedd57f0161 Mon Sep 17 00:00:00 2001 From: Thanos Date: Wed, 20 Jan 2021 09:51:00 +0000 Subject: [PATCH 48/57] Fix mayavi plotting for landmarks. --- menpo3d/visualize/viewmayavi.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/menpo3d/visualize/viewmayavi.py b/menpo3d/visualize/viewmayavi.py index 1c44d95..0c79cbd 100644 --- a/menpo3d/visualize/viewmayavi.py +++ b/menpo3d/visualize/viewmayavi.py @@ -498,7 +498,8 @@ def render(self, render_lines=True, line_colour='r', line_width=2, 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) + numbers_colour=numbers_colour, numbers_size=numbers_size, + inline=False) self.figure.scene.disable_render = False return self From 47875fd3ddc37bb7368ea02e5283b9142a34b860 Mon Sep 17 00:00:00 2001 From: Athanasios Papaioannou 00570580 Date: Wed, 20 Jan 2021 16:54:03 +0000 Subject: [PATCH 49/57] Changes in instal_requires in setup.py. Added more packages for windows --- setup.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 0f1cc74..036a7b4 100644 --- a/setup.py +++ b/setup.py @@ -86,11 +86,20 @@ def build_extension_from_pyx(pyx_path, extra_sources_paths=None): install_requires = ['menpo>=0.9.0,<0.11.0', 'vtk', - # 'mayavi>=4.7.0', 'scikit-sparse>=0.3.1', 'moderngl>=5.5.*,<6.0', + # jedo==0.17.2 is not technically necessary, + # but avoids incompatibilities with + # ipython 7.18 and 7.19 20 January 2021 + 'jedi==0.17.2', 'k3d'] +if IS_WIN: + install_requires.extend(['pywin32==225', + 'mayavi>=4.7.0', + 'pyqt5']) +print(install_requires) + setup(name='menpo3d', version=versioneer.get_version(), cmdclass=versioneer.get_cmdclass(), @@ -102,4 +111,4 @@ def build_extension_from_pyx(pyx_path, extra_sources_paths=None): install_requires=install_requires, tests_require=['pytest>=5.0', 'mock>=3.0'], ext_modules=cython_exts - ) + ) \ No newline at end of file From 2ca4ddf7665751f6f89ee3279a62bf4c165b8c55 Mon Sep 17 00:00:00 2001 From: Thanos Date: Thu, 28 Jan 2021 15:09:32 +0000 Subject: [PATCH 50/57] 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 6e685c8..fd86666 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 46115ac9e320569f08e78e9060c0273102a3833b Mon Sep 17 00:00:00 2001 From: Thanos Date: Fri, 29 Jan 2021 12:44:47 +0000 Subject: [PATCH 51/57] 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 fd86666..9c81044 100644 --- a/menpo3d/visualize/viewk3dwidgets.py +++ b/menpo3d/visualize/viewk3dwidgets.py @@ -732,11 +732,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 050adbb23a49fc31fe0e2071c6573af9e515c31b Mon Sep 17 00:00:00 2001 From: Thanos Date: Wed, 3 Feb 2021 09:34:45 +0000 Subject: [PATCH 52/57] 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 f097cbe3cf254e78e329971183dced6d1edbe1f3 Mon Sep 17 00:00:00 2001 From: Thanos Date: Mon, 15 Feb 2021 15:24:19 +0000 Subject: [PATCH 53/57] Add function generate_transform_matrices in spirals/base.py --- menpo3d/spirals/__init__.py | 2 +- menpo3d/spirals/base.py | 42 +++++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/menpo3d/spirals/__init__.py b/menpo3d/spirals/__init__.py index f88a36b..9582704 100644 --- a/menpo3d/spirals/__init__.py +++ b/menpo3d/spirals/__init__.py @@ -1 +1 @@ -from .base import adj_trigs +from .base import adj_trigs, generate_transform_matrices diff --git a/menpo3d/spirals/base.py b/menpo3d/spirals/base.py index cc45100..2747450 100644 --- a/menpo3d/spirals/base.py +++ b/menpo3d/spirals/base.py @@ -24,3 +24,45 @@ def adj_trigs(list_meshes): Adj.append(mesh.as_pointgraph().get_adjacency_list()) Trigs.append(mesh.list_faces_per_vertex()) return Adj, Trigs + +def generate_transform_matrices(reference_mesh, ds_factors, M=None): + """Return the lists of upsampling and downsampling matrices + for a list of meshes defined either by M list or computed using + the ds_factors + + Parameters: + -------------- + reference_mesh: TriMesh + The template mesh + ds_factors: list + A list with integers representing the factors of vertices to be kept + As our decimate method uses the vertices to be removed, we simply + transformed them. + M: list of TriMesh + A list of decimated meshes. If None, used ds_factor to compute this + list + + Returns: + ------------- + M: list of TriMesh + The list of the decimated matrices + D: list of sparse arrays + The list of downsampling sparce matrices + A: list of sparse arrays + The list of upsampling sparce matrices + """ + ds_factors = list(map(lambda x: 1-1.0/x, ds_factors)) + D, U = [], [] + + if M is None: + M = [] + M.append(reference_mesh) + for factor in ds_factors: + M.append(M[-1].decimate(factor)) + + current_mesh = reference_mesh + for current_mesh, next_mesh in zip(M, M[1:]): + U.append(current_mesh.upsampling_matrix(next_mesh)) + D.append(next_mesh.find_closest_vertices(current_mesh)) + + return M, D, U From bd64573d425cd0467ed621d3c758e5d0bb4d0547 Mon Sep 17 00:00:00 2001 From: Thanos Date: Thu, 25 Feb 2021 17:52:43 +0000 Subject: [PATCH 54/57] 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 9c81044..74f143e 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 6c4bd75e01cf86742d20caaee5e70b88518ec937 Mon Sep 17 00:00:00 2001 From: Thanos Date: Thu, 25 Feb 2021 19:12:24 +0000 Subject: [PATCH 55/57] 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 74f143e..5e8f8d8 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 @@ -657,13 +624,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 9ad80a5d554db678d9a94cbfe5c350d8878ef7b3 Mon Sep 17 00:00:00 2001 From: Thanos Date: Fri, 26 Feb 2021 17:48:16 +0000 Subject: [PATCH 56/57] 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 5e8f8d8..d555988 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, @@ -557,11 +561,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 @@ -588,9 +590,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 @@ -598,13 +600,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) @@ -654,7 +657,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: @@ -672,8 +674,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}}'\ @@ -734,19 +738,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 @@ -775,12 +781,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 71292454385e81263b500d3bc80a59d7402ab28c Mon Sep 17 00:00:00 2001 From: Thanos Date: Fri, 26 Feb 2021 18:00:52 +0000 Subject: [PATCH 57/57] Remove of 3DMM --- examples/0_Introduction_to_K3d_Widgets.ipynb | 128 +++++++------------ 1 file changed, 49 insertions(+), 79 deletions(-) diff --git a/examples/0_Introduction_to_K3d_Widgets.ipynb b/examples/0_Introduction_to_K3d_Widgets.ipynb index cff64e9..589a705 100644 --- a/examples/0_Introduction_to_K3d_Widgets.ipynb +++ b/examples/0_Introduction_to_K3d_Widgets.ipynb @@ -30,7 +30,7 @@ "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", + "model = mio.import_pickle('link_to_model')['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", @@ -145,7 +145,25 @@ "metadata": {}, "outputs": [], "source": [ - "lms.view(new_figure=True, render_numbering=True)" + "pc = PointCloud(lms.points)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "pc.view(new_figure=True, alpha=0.5, render_numbering=True, numbers_colour='g')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "lms.view(new_figure=True, render_numbering=True, numbers_colour='g' )" ] }, { @@ -197,7 +215,9 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "scrolled": false + }, "outputs": [], "source": [ "# Heatmap between a random mesh and mean mesh\n", @@ -224,7 +244,7 @@ "source": [ "# Heatmap with landmarks\n", "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')" + "model_mean.heatmap(random_mesh, inline=True, show_statistics=True)" ] }, { @@ -379,6 +399,18 @@ "new_mesh.view()" ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "new_mesh.view(normals=vrt, \n", + " normals_marker_size= 0.5,\n", + " normals_line_width = 0.01,\n", + " figure_id='Normals2')" + ] + }, { "cell_type": "code", "execution_count": null, @@ -419,7 +451,9 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "scrolled": true + }, "outputs": [], "source": [ "graph.view()" @@ -449,7 +483,8 @@ }, "outputs": [], "source": [ - "model.view(figure_id='Model', n_parameters=10, landmarks_indices=lms_indices)" + "model.view(figure_id='Model', n_parameters=10, \n", + " landmarks_indices=lms_indices, alpha=0.4)" ] }, { @@ -463,58 +498,6 @@ "mesh.view(new_figure=False)" ] }, - { - "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( )" - ] - }, - { - "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()" - ] - }, { "cell_type": "markdown", "metadata": {}, @@ -522,18 +505,6 @@ "

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

" ] }, - { - "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()" - ] - }, { "cell_type": "code", "execution_count": null, @@ -557,20 +528,19 @@ ] }, { - "cell_type": "code", - "execution_count": null, + "cell_type": "markdown", "metadata": {}, - "outputs": [], "source": [ - "# You have already created a heatmap between random_mesh and model_mean\n", - "random_mesh.heatmap(model_mean, inline=True)`" + "

Additional functions

" ] }, { - "cell_type": "markdown", + "cell_type": "code", + "execution_count": null, "metadata": {}, + "outputs": [], "source": [ - "

Additional functions

" + "from menpo3d.visualize import list_figures, dict_figures, " ] }, { @@ -579,7 +549,7 @@ "metadata": {}, "outputs": [], "source": [ - "from menpo3d.visualize import list_figures, dict_figures" + "tmp = mesh.view(figure_id='Figure Test')" ] }, { @@ -588,7 +558,7 @@ "metadata": {}, "outputs": [], "source": [ - "list_figures()" + "tmp.remove_widget()" ] }, {