From 83fb00ef8b393b0022073ee6b87c1259f8a01967 Mon Sep 17 00:00:00 2001 From: allegroai <> Date: Wed, 19 May 2021 15:23:34 +0300 Subject: [PATCH] Add report_matplotlib_figure(..., report_interactive=False) allowing to upload a matplotlib as a non-interactive (high quality png) plot --- clearml/backend_interface/metrics/reporter.py | 3 +- clearml/binding/matplotlib_bind.py | 32 +++++++++++++------ clearml/logger.py | 6 +++- 3 files changed, 30 insertions(+), 11 deletions(-) diff --git a/clearml/backend_interface/metrics/reporter.py b/clearml/backend_interface/metrics/reporter.py index dadc8f71..5830238a 100644 --- a/clearml/backend_interface/metrics/reporter.py +++ b/clearml/backend_interface/metrics/reporter.py @@ -264,7 +264,8 @@ class Reporter(InterfaceBase, AbstractContextManager, SetupUploadMixin, AsyncMan series=series, figure=figure, iter=iter, - force_save_as_image=force_save_as_image, + force_save_as_image=force_save_as_image if not isinstance(force_save_as_image, str) else False, + report_as_debug_sample=force_save_as_image if isinstance(force_save_as_image, str) else False, reporter=self, logger=logger, ) diff --git a/clearml/binding/matplotlib_bind.py b/clearml/binding/matplotlib_bind.py index af534464..09bde781 100644 --- a/clearml/binding/matplotlib_bind.py +++ b/clearml/binding/matplotlib_bind.py @@ -27,7 +27,7 @@ class PatchedMatplotlib: _global_image_counter_limit = None _last_iteration_plot_titles = {} _current_task = None - _support_image_plot = False + _support_image_plot = None _matplotlylib = None _plotly_renderer = None _lock_renderer = threading.RLock() @@ -124,13 +124,19 @@ class PatchedMatplotlib: pass # update api version - from ..backend_api import Session - PatchedMatplotlib._support_image_plot = Session.check_min_api_version('2.2') + PatchedMatplotlib._update_matplotlib_image_support() + # load plotly PatchedMatplotlib._update_plotly_renderers() return True + @staticmethod + def _update_matplotlib_image_support(): + if PatchedMatplotlib._support_image_plot is None: + from ..backend_api import Session + PatchedMatplotlib._support_image_plot = Session.check_min_api_version('2.2') + @staticmethod def _update_matplotlib_version(): if PatchedMatplotlib._matplot_major_version: @@ -152,6 +158,9 @@ class PatchedMatplotlib: except Exception: pass + # check backend support + PatchedMatplotlib._update_matplotlib_image_support() + @staticmethod def _update_plotly_renderers(): if PatchedMatplotlib._matplotlylib and PatchedMatplotlib._plotly_renderer: @@ -280,9 +289,11 @@ class PatchedMatplotlib: return ret @staticmethod - def report_figure(title, series, figure, iter, force_save_as_image=False, reporter=None, logger=None): + def report_figure(title, series, figure, iter, + force_save_as_image=False, report_as_debug_sample=False, reporter=None, logger=None): PatchedMatplotlib._report_figure( force_save_as_image=force_save_as_image, + report_as_debug_sample=report_as_debug_sample, specific_fig=figure.gcf() if hasattr(figure, 'gcf') else figure, title=title, series=series, @@ -294,6 +305,7 @@ class PatchedMatplotlib: @staticmethod def _report_figure( force_save_as_image=False, + report_as_debug_sample=False, stored_figure=None, set_active=True, specific_fig=None, @@ -336,7 +348,7 @@ class PatchedMatplotlib: if getattr(stored_figure, '_trains_is_imshow', None) is not None: # flag will be cleared when calling clf() (object will be replaced) stored_figure._trains_is_imshow = max(0, stored_figure._trains_is_imshow - 1) - force_save_as_image = True + report_as_debug_sample = True # get current figure mpl_fig = stored_figure.canvas.figure # plt.gcf() else: @@ -354,9 +366,11 @@ class PatchedMatplotlib: plotly_dict = None image_format = 'jpeg' fig_dpi = 300 - if force_save_as_image: + if force_save_as_image or report_as_debug_sample: # if this is an image, store as is. - fig_dpi = None + fig_dpi = None if report_as_debug_sample else 300 + force_save_as_image = force_save_as_image \ + if force_save_as_image and isinstance(force_save_as_image, str) else 'png' if isinstance(force_save_as_image, str): image_format = force_save_as_image else: @@ -487,8 +501,8 @@ class PatchedMatplotlib: last_iteration = iter if iter is not None else PatchedMatplotlib._get_last_iteration() - report_as_debug_sample = not plotly_dict and ( - force_save_as_image or not PatchedMatplotlib._support_image_plot) + report_as_debug_sample = report_as_debug_sample or ( + not plotly_dict and not PatchedMatplotlib._support_image_plot) if not title: if mpl_fig.texts: diff --git a/clearml/logger.py b/clearml/logger.py index dfae195d..5b68477d 100644 --- a/clearml/logger.py +++ b/clearml/logger.py @@ -987,6 +987,7 @@ class Logger(object): figure, # type: Union[MatplotlibFigure, pyplot] iteration=None, # type: Optional[int] report_image=False, # type: bool + report_interactive=True, # type: bool ): """ Report a ``matplotlib`` figure / plot directly @@ -999,6 +1000,9 @@ class Logger(object): :param MatplotlibFigure figure: A ``matplotlib`` Figure object :param report_image: Default False. If True the plot will be uploaded as a debug sample (png image), and will appear under the debug samples tab (instead of the Plots tab). + :param report_interactive: If True (default) it will try to convert the matplotlib into interactive + plot in the UI. If False the matplotlib is saved as is and will + be non-interactive (with the exception of zooming in/out) """ # if task was not started, we have to start it self._start_task_if_needed() @@ -1010,7 +1014,7 @@ class Logger(object): figure=figure, iter=iteration or 0, logger=self, - force_save_as_image='png' if report_image else False, + force_save_as_image=False if report_interactive else ('png' if report_image else True), ) def set_default_upload_destination(self, uri):