We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Add matplotlib integration
We need let users to track matplotlib figures.
Need to investigate matplotlib library and understand figure's dump/load procedure and integrate it into our system.

Motivation: Make users more engaged and feel comfortable using aim for their projects

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `aim/sdk/objects/image.py`
Content:
```
1 import logging
2 import os.path
3 
4 from PIL import Image as PILImage
5 
6 from io import BytesIO
7 from itertools import chain, repeat
8 from typing import List
9 import numpy as np
10 
11 from aim.sdk.num_utils import inst_has_typename
12 from aim.storage.object import CustomObject
13 from aim.storage.types import BLOB
14 
15 logger = logging.getLogger(__name__)
16 
17 
18 @CustomObject.alias('aim.image')
19 class Image(CustomObject):
20     """Image object used to store image objects in Aim repository...
21 
22     Args:
23          image (:obj:): pillow `Image` object or `torch.Tensor` or `numpy.array` used to construct `aim.Image`.
24          caption (:obj:`str`, optional): Optional image caption. '' by default.
25          format (:obj: `str`, optional): Parameter for PIL's .save() method. 'png' by default.
26          quality (:obj: `int`, optional): Parameter for PIL's .save() method. 85 by default.
27          optimize (:obj: `bool`, optional): Parameter for PIL's .save() method. False by default.
28 
29          For more information on the format, quality and optimize parameters, refer to PIL documentation.
30 
31     Example of params to reduce quality of the image:
32         format='jpeg',
33         optimize=True,
34         quality=85
35     """
36 
37     FLAG_WARN_RGBA_RGB = False
38     AIM_NAME = 'aim.image'
39 
40     def __init__(self, image, caption: str = '', format='png', quality=90, optimize=False):
41         super().__init__()
42 
43         # normalize jpg
44         if format.lower() == 'jpg':
45             # PIL doesn't support 'jpg' key
46             format = 'jpeg'
47 
48         params = {
49             'format': format.lower(),
50             'quality': quality,
51             'optimize': optimize
52         }
53 
54         if isinstance(image, str):
55             self._from_file_path(image, params)
56         elif inst_has_typename(image, ['PIL', 'Image']):
57             self._from_pil_image(image, params)
58         elif inst_has_typename(image, ['torch', 'Tensor']):
59             self._from_torch_tensor(image, params)
60         elif inst_has_typename(image, ['tensorflow', 'Tensor']):
61             self._from_tf_tensor(image, params)
62         elif inst_has_typename(image, ['numpy', 'array']):
63             self._from_numpy_array(image, params)
64         else:
65             raise TypeError(f'Cannot convert to aim.Image. Unsupported type {type(image)}.')
66         self.caption = caption
67 
68     @property
69     def caption(self) -> str:
70         """Image caption, set by user.
71 
72             :getter: Returns image caption.
73             :setter: Sets image caption.
74             :type: string
75         """
76         return self.storage['caption']
77 
78     @caption.setter
79     def caption(self, value: str):
80         self.storage['caption'] = value
81 
82     @property
83     def format(self) -> str:
84         """Stored image format.
85 
86             :getter: Returns image format.
87             :type: string
88         """
89         return self.storage['format']
90 
91     @property
92     def width(self):
93         """Stored image width.
94 
95             :getter: Returns image width.
96             :type: string
97         """
98         return self.storage['width']
99 
100     @property
101     def height(self):
102         """Stored image height.
103 
104             :getter: Returns image height.
105             :type: string
106         """
107         return self.storage['height']
108 
109     @property
110     def size(self):
111         """Stored image size.
112 
113             :getter: Returns image (width, height) pair.
114             :type: string
115         """
116         return self.storage['width'], self.storage['height']
117 
118     def to_pil_image(self) -> PILImage.Image:
119         """Method to convert aim.Image to pillow Image"""
120         pil_img = PILImage.open(BytesIO(bytes(self.storage['data'])))
121         assert pil_img.size == self.size
122         return pil_img
123 
124     def json(self):
125         """Dump image metadata to a dict"""
126         return {
127             'caption': self.caption,
128             'format': self.format,
129             'width': self.width,
130             'height': self.height,
131         }
132 
133     def _from_pil_image(self, pil_image: PILImage.Image, params):
134         assert isinstance(pil_image, PILImage.Image)
135         img_container = BytesIO()
136 
137         try:
138             pil_image.save(img_container, **params)
139         except OSError as exc:
140             # The best way to approach this problem is to prepare PIL Image object before hitting this method.
141             # This block only handles case where RGBA/P/LA/PA mode is mandated to save in RGB
142             # PIL won't do that automatically, so we have to convert image to RGB before saving it.
143             # In addition - make transparency "white" before conversion otherwise it will be black.
144             if pil_image.mode not in ('RGBA', 'LA', 'PA', 'P'):
145                 raise
146             elif not Image.FLAG_WARN_RGBA_RGB:
147                 logger.warning(f'Failed to save the image due to the following error: {exc}')
148                 logger.warning(f'Attempting to convert mode "{pil_image.mode}" to "RGB"')
149                 Image.FLAG_WARN_RGBA_RGB = True
150 
151             alpha = pil_image.convert('RGBA').split()[-1]  # Get only alpha
152             background = PILImage.new('RGBA', pil_image.size, (255, 255, 255, 255))
153             background.paste(pil_image, mask=alpha)
154             pil_image = background.convert('RGB')
155 
156             # Retry
157             pil_image.save(img_container, **params)
158 
159         self.storage['data'] = BLOB(data=img_container.getvalue())
160         self.storage['source'] = 'PIL.Image'
161         self.storage['mode'] = pil_image.mode
162         self.storage['format'] = params['format']
163         self.storage['width'], self.storage['height'] = pil_image.size
164 
165     def _from_file_path(self, file_path, params):
166         if not os.path.isfile(file_path):
167             raise ValueError('Invalid image file path.')
168 
169         return self._from_pil_image(PILImage.open(file_path), params)
170 
171     def _from_numpy_array(self, array: np.ndarray, params):
172         if array.ndim not in {2, 3}:
173             raise ValueError('Cannot convert to aim.Image. array must have 2/3-D shape.')
174 
175         if array.ndim == 3 and array.shape[2] == 1:  # greyscale
176             pil_image = PILImage.fromarray(array[:, :, 0])
177         else:
178             pil_image = PILImage.fromarray(array)
179         self._from_pil_image(pil_image, params)
180 
181     def _from_torch_tensor(self, tensor, params):
182         try:
183             import torch
184             assert isinstance(tensor, torch.Tensor)
185         except (ImportError, AssertionError):
186             raise ValueError('Cannot convert from torch.Tensor')
187 
188         if tensor.ndim not in {2, 3}:
189             raise ValueError('Cannot convert to aim.Image. Tensor must have 2/3-D shape.')
190         if tensor.is_floating_point():
191             tensor = tensor.mul(255).byte()
192         array: np.ndarray = tensor.cpu().numpy()
193 
194         if array.ndim == 3:
195             channels = array.shape[0]
196             if channels == 1:
197                 pil_image = PILImage.fromarray(array[0, :, :])
198             else:
199                 # reverse order of channels: c,h,w => h,w,c
200                 pil_image = PILImage.fromarray(np.transpose(array, (1, 2, 0)))
201         else:
202             pil_image = PILImage.fromarray(array)
203         self._from_pil_image(pil_image, params)
204 
205     def _from_tf_tensor(self, tensor, params):
206         try:
207             import tensorflow as tf
208             assert isinstance(tensor, tf.Tensor)
209         except (ImportError, AssertionError):
210             raise ValueError('Cannot convert from tf.Tensor')
211 
212         if tensor.ndim not in {2, 3}:
213             raise ValueError('Cannot convert to aim.Image. Tensor must have 2/3-D shape.')
214 
215         if tensor.dtype.is_floating:
216             tensor = tf.cast(tf.math.scalar_mul(255.0, tensor), tf.uint8)
217         array: np.ndarray = tensor.numpy()
218 
219         if array.ndim == 3 and array.shape[2] == 1:  # greyscale
220             pil_image = PILImage.fromarray(array[:, :, 0])
221         else:
222             pil_image = PILImage.fromarray(array)
223         self._from_pil_image(pil_image, params)
224 
225     def __eq__(self, other):
226         if not isinstance(other, Image):
227             return False
228 
229         props = ['mode', 'format', 'width', 'height']
230         for p in props:
231             if self.storage[p] != other.storage[p]:
232                 return False
233 
234         return (self.storage['data'].data == other.storage['data'].data)
235 
236 
237 def convert_to_aim_image_list(images, labels=None) -> List[Image]:
238     aim_images = []
239     if labels is not None:
240         labels_it = chain(labels, repeat(''))
241     else:
242         labels_it = repeat('')
243     for img, lbl in zip(images, labels_it):
244         aim_img = Image(img, lbl if isinstance(lbl, str) else str(lbl.item()))
245         aim_images.append(aim_img)
246     return aim_images
247
```

Path: `aim/sdk/objects/figure.py`
Content:
```
1 from aim.sdk.num_utils import inst_has_typename
2 from aim.storage.object import CustomObject
3 from aim.storage.types import BLOB
4 
5 
6 @CustomObject.alias('aim.figure')
7 class Figure(CustomObject):
8     """Figure object used to store plotly figures in Aim repository.
9 
10     Args:
11          obj (:obj:): plotly figure object.
12     """
13 
14     AIM_NAME = 'aim.figure'
15 
16     def __init__(self, obj):
17         super().__init__()
18 
19         if inst_has_typename(obj, ['Figure', 'BaseFigure']):
20             self._prepare(obj)
21         else:
22             raise TypeError('Object is not a Plotly Figure instance')
23 
24     def _prepare(self, obj):
25         try:
26             from plotly.version import __version__ as plotly_version
27         except ModuleNotFoundError:
28             plotly_version = 'unknown'
29 
30         assert hasattr(obj, 'to_json')
31 
32         self.storage['source'] = 'plotly'
33         self.storage['version'] = plotly_version
34         self.storage['format'] = 'raw_json'
35         self.storage['data'] = BLOB(data=obj.to_json())
36 
37     def json(self):
38         blob_data = self.storage['data']
39         return blob_data.data
40 
41     def to_plotly_figure(self):
42         try:
43             from plotly.io import from_json
44         except ModuleNotFoundError:
45             raise ModuleNotFoundError('Could not find plotly in the installed modules.')
46 
47         return from_json(self.json())
48
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/aim/sdk/objects/figure.py b/aim/sdk/objects/figure.py
--- a/aim/sdk/objects/figure.py
+++ b/aim/sdk/objects/figure.py
@@ -5,10 +5,12 @@
 
 @CustomObject.alias('aim.figure')
 class Figure(CustomObject):
-    """Figure object used to store plotly figures in Aim repository.
+    """
+    Figure object can be used for storing Plotly or Matplotlib figures into Aim repository.
+    Core functionality is based on Plotly.
 
     Args:
-         obj (:obj:): plotly figure object.
+         obj (:obj:): plotly or matplotlib figure object.
     """
 
     AIM_NAME = 'aim.figure'
@@ -16,7 +18,9 @@
     def __init__(self, obj):
         super().__init__()
 
-        if inst_has_typename(obj, ['Figure', 'BaseFigure']):
+        if inst_has_typename(obj, ['matplotlib', 'Figure']):
+            self._from_matplotlib_figure(obj)
+        elif inst_has_typename(obj, ['plotly', 'Figure', 'BaseFigure']):
             self._prepare(obj)
         else:
             raise TypeError('Object is not a Plotly Figure instance')
@@ -34,6 +38,19 @@
         self.storage['format'] = 'raw_json'
         self.storage['data'] = BLOB(data=obj.to_json())
 
+    def _from_matplotlib_figure(self, obj):
+        try:
+            from plotly.tools import mpl_to_plotly
+        except ModuleNotFoundError:
+            raise ModuleNotFoundError('Plotly is required to track matplotlib figure.')
+
+        try:
+            plotly_obj = mpl_to_plotly(obj)
+        except ValueError as err:
+            raise ValueError(f'Failed to convert matplotlib figure to plotly figure: {err}')
+
+        return self._prepare(plotly_obj)
+
     def json(self):
         blob_data = self.storage['data']
         return blob_data.data
diff --git a/aim/sdk/objects/image.py b/aim/sdk/objects/image.py
--- a/aim/sdk/objects/image.py
+++ b/aim/sdk/objects/image.py
@@ -61,6 +61,8 @@
             self._from_tf_tensor(image, params)
         elif inst_has_typename(image, ['numpy', 'array']):
             self._from_numpy_array(image, params)
+        elif inst_has_typename(image, ['Figure', 'matplotlib', 'figure']):
+            self._from_matplotlib_figure(image, params)
         else:
             raise TypeError(f'Cannot convert to aim.Image. Unsupported type {type(image)}.')
         self.caption = caption
@@ -222,6 +224,18 @@
             pil_image = PILImage.fromarray(array)
         self._from_pil_image(pil_image, params)
 
+    def _from_matplotlib_figure(self, figure, params):
+        try:
+            assert hasattr(figure, 'savefig')
+        except AssertionError:
+            raise ValueError('Cannot convert from matplotlib figure.')
+
+        buffer = BytesIO()
+        figure.savefig(buffer)
+        buffer.seek(0)
+
+        return self._from_pil_image(PILImage.open(buffer), params)
+
     def __eq__(self, other):
         if not isinstance(other, Image):
             return False

{"golden_diff": "diff --git a/aim/sdk/objects/figure.py b/aim/sdk/objects/figure.py\n--- a/aim/sdk/objects/figure.py\n+++ b/aim/sdk/objects/figure.py\n@@ -5,10 +5,12 @@\n \n @CustomObject.alias('aim.figure')\n class Figure(CustomObject):\n-    \"\"\"Figure object used to store plotly figures in Aim repository.\n+    \"\"\"\n+    Figure object can be used for storing Plotly or Matplotlib figures into Aim repository.\n+    Core functionality is based on Plotly.\n \n     Args:\n-         obj (:obj:): plotly figure object.\n+         obj (:obj:): plotly or matplotlib figure object.\n     \"\"\"\n \n     AIM_NAME = 'aim.figure'\n@@ -16,7 +18,9 @@\n     def __init__(self, obj):\n         super().__init__()\n \n-        if inst_has_typename(obj, ['Figure', 'BaseFigure']):\n+        if inst_has_typename(obj, ['matplotlib', 'Figure']):\n+            self._from_matplotlib_figure(obj)\n+        elif inst_has_typename(obj, ['plotly', 'Figure', 'BaseFigure']):\n             self._prepare(obj)\n         else:\n             raise TypeError('Object is not a Plotly Figure instance')\n@@ -34,6 +38,19 @@\n         self.storage['format'] = 'raw_json'\n         self.storage['data'] = BLOB(data=obj.to_json())\n \n+    def _from_matplotlib_figure(self, obj):\n+        try:\n+            from plotly.tools import mpl_to_plotly\n+        except ModuleNotFoundError:\n+            raise ModuleNotFoundError('Plotly is required to track matplotlib figure.')\n+\n+        try:\n+            plotly_obj = mpl_to_plotly(obj)\n+        except ValueError as err:\n+            raise ValueError(f'Failed to convert matplotlib figure to plotly figure: {err}')\n+\n+        return self._prepare(plotly_obj)\n+\n     def json(self):\n         blob_data = self.storage['data']\n         return blob_data.data\ndiff --git a/aim/sdk/objects/image.py b/aim/sdk/objects/image.py\n--- a/aim/sdk/objects/image.py\n+++ b/aim/sdk/objects/image.py\n@@ -61,6 +61,8 @@\n             self._from_tf_tensor(image, params)\n         elif inst_has_typename(image, ['numpy', 'array']):\n             self._from_numpy_array(image, params)\n+        elif inst_has_typename(image, ['Figure', 'matplotlib', 'figure']):\n+            self._from_matplotlib_figure(image, params)\n         else:\n             raise TypeError(f'Cannot convert to aim.Image. Unsupported type {type(image)}.')\n         self.caption = caption\n@@ -222,6 +224,18 @@\n             pil_image = PILImage.fromarray(array)\n         self._from_pil_image(pil_image, params)\n \n+    def _from_matplotlib_figure(self, figure, params):\n+        try:\n+            assert hasattr(figure, 'savefig')\n+        except AssertionError:\n+            raise ValueError('Cannot convert from matplotlib figure.')\n+\n+        buffer = BytesIO()\n+        figure.savefig(buffer)\n+        buffer.seek(0)\n+\n+        return self._from_pil_image(PILImage.open(buffer), params)\n+\n     def __eq__(self, other):\n         if not isinstance(other, Image):\n             return False\n", "issue": "Add matplotlib integration\nWe need let users to track matplotlib figures.\r\nNeed to investigate matplotlib library and understand figure's dump/load procedure and integrate it into our system.\r\n\r\nMotivation: Make users more engaged and feel comfortable using aim for their projects\n", "before_files": [{"content": "import logging\nimport os.path\n\nfrom PIL import Image as PILImage\n\nfrom io import BytesIO\nfrom itertools import chain, repeat\nfrom typing import List\nimport numpy as np\n\nfrom aim.sdk.num_utils import inst_has_typename\nfrom aim.storage.object import CustomObject\nfrom aim.storage.types import BLOB\n\nlogger = logging.getLogger(__name__)\n\n\n@CustomObject.alias('aim.image')\nclass Image(CustomObject):\n    \"\"\"Image object used to store image objects in Aim repository...\n\n    Args:\n         image (:obj:): pillow `Image` object or `torch.Tensor` or `numpy.array` used to construct `aim.Image`.\n         caption (:obj:`str`, optional): Optional image caption. '' by default.\n         format (:obj: `str`, optional): Parameter for PIL's .save() method. 'png' by default.\n         quality (:obj: `int`, optional): Parameter for PIL's .save() method. 85 by default.\n         optimize (:obj: `bool`, optional): Parameter for PIL's .save() method. False by default.\n\n         For more information on the format, quality and optimize parameters, refer to PIL documentation.\n\n    Example of params to reduce quality of the image:\n        format='jpeg',\n        optimize=True,\n        quality=85\n    \"\"\"\n\n    FLAG_WARN_RGBA_RGB = False\n    AIM_NAME = 'aim.image'\n\n    def __init__(self, image, caption: str = '', format='png', quality=90, optimize=False):\n        super().__init__()\n\n        # normalize jpg\n        if format.lower() == 'jpg':\n            # PIL doesn't support 'jpg' key\n            format = 'jpeg'\n\n        params = {\n            'format': format.lower(),\n            'quality': quality,\n            'optimize': optimize\n        }\n\n        if isinstance(image, str):\n            self._from_file_path(image, params)\n        elif inst_has_typename(image, ['PIL', 'Image']):\n            self._from_pil_image(image, params)\n        elif inst_has_typename(image, ['torch', 'Tensor']):\n            self._from_torch_tensor(image, params)\n        elif inst_has_typename(image, ['tensorflow', 'Tensor']):\n            self._from_tf_tensor(image, params)\n        elif inst_has_typename(image, ['numpy', 'array']):\n            self._from_numpy_array(image, params)\n        else:\n            raise TypeError(f'Cannot convert to aim.Image. Unsupported type {type(image)}.')\n        self.caption = caption\n\n    @property\n    def caption(self) -> str:\n        \"\"\"Image caption, set by user.\n\n            :getter: Returns image caption.\n            :setter: Sets image caption.\n            :type: string\n        \"\"\"\n        return self.storage['caption']\n\n    @caption.setter\n    def caption(self, value: str):\n        self.storage['caption'] = value\n\n    @property\n    def format(self) -> str:\n        \"\"\"Stored image format.\n\n            :getter: Returns image format.\n            :type: string\n        \"\"\"\n        return self.storage['format']\n\n    @property\n    def width(self):\n        \"\"\"Stored image width.\n\n            :getter: Returns image width.\n            :type: string\n        \"\"\"\n        return self.storage['width']\n\n    @property\n    def height(self):\n        \"\"\"Stored image height.\n\n            :getter: Returns image height.\n            :type: string\n        \"\"\"\n        return self.storage['height']\n\n    @property\n    def size(self):\n        \"\"\"Stored image size.\n\n            :getter: Returns image (width, height) pair.\n            :type: string\n        \"\"\"\n        return self.storage['width'], self.storage['height']\n\n    def to_pil_image(self) -> PILImage.Image:\n        \"\"\"Method to convert aim.Image to pillow Image\"\"\"\n        pil_img = PILImage.open(BytesIO(bytes(self.storage['data'])))\n        assert pil_img.size == self.size\n        return pil_img\n\n    def json(self):\n        \"\"\"Dump image metadata to a dict\"\"\"\n        return {\n            'caption': self.caption,\n            'format': self.format,\n            'width': self.width,\n            'height': self.height,\n        }\n\n    def _from_pil_image(self, pil_image: PILImage.Image, params):\n        assert isinstance(pil_image, PILImage.Image)\n        img_container = BytesIO()\n\n        try:\n            pil_image.save(img_container, **params)\n        except OSError as exc:\n            # The best way to approach this problem is to prepare PIL Image object before hitting this method.\n            # This block only handles case where RGBA/P/LA/PA mode is mandated to save in RGB\n            # PIL won't do that automatically, so we have to convert image to RGB before saving it.\n            # In addition - make transparency \"white\" before conversion otherwise it will be black.\n            if pil_image.mode not in ('RGBA', 'LA', 'PA', 'P'):\n                raise\n            elif not Image.FLAG_WARN_RGBA_RGB:\n                logger.warning(f'Failed to save the image due to the following error: {exc}')\n                logger.warning(f'Attempting to convert mode \"{pil_image.mode}\" to \"RGB\"')\n                Image.FLAG_WARN_RGBA_RGB = True\n\n            alpha = pil_image.convert('RGBA').split()[-1]  # Get only alpha\n            background = PILImage.new('RGBA', pil_image.size, (255, 255, 255, 255))\n            background.paste(pil_image, mask=alpha)\n            pil_image = background.convert('RGB')\n\n            # Retry\n            pil_image.save(img_container, **params)\n\n        self.storage['data'] = BLOB(data=img_container.getvalue())\n        self.storage['source'] = 'PIL.Image'\n        self.storage['mode'] = pil_image.mode\n        self.storage['format'] = params['format']\n        self.storage['width'], self.storage['height'] = pil_image.size\n\n    def _from_file_path(self, file_path, params):\n        if not os.path.isfile(file_path):\n            raise ValueError('Invalid image file path.')\n\n        return self._from_pil_image(PILImage.open(file_path), params)\n\n    def _from_numpy_array(self, array: np.ndarray, params):\n        if array.ndim not in {2, 3}:\n            raise ValueError('Cannot convert to aim.Image. array must have 2/3-D shape.')\n\n        if array.ndim == 3 and array.shape[2] == 1:  # greyscale\n            pil_image = PILImage.fromarray(array[:, :, 0])\n        else:\n            pil_image = PILImage.fromarray(array)\n        self._from_pil_image(pil_image, params)\n\n    def _from_torch_tensor(self, tensor, params):\n        try:\n            import torch\n            assert isinstance(tensor, torch.Tensor)\n        except (ImportError, AssertionError):\n            raise ValueError('Cannot convert from torch.Tensor')\n\n        if tensor.ndim not in {2, 3}:\n            raise ValueError('Cannot convert to aim.Image. Tensor must have 2/3-D shape.')\n        if tensor.is_floating_point():\n            tensor = tensor.mul(255).byte()\n        array: np.ndarray = tensor.cpu().numpy()\n\n        if array.ndim == 3:\n            channels = array.shape[0]\n            if channels == 1:\n                pil_image = PILImage.fromarray(array[0, :, :])\n            else:\n                # reverse order of channels: c,h,w => h,w,c\n                pil_image = PILImage.fromarray(np.transpose(array, (1, 2, 0)))\n        else:\n            pil_image = PILImage.fromarray(array)\n        self._from_pil_image(pil_image, params)\n\n    def _from_tf_tensor(self, tensor, params):\n        try:\n            import tensorflow as tf\n            assert isinstance(tensor, tf.Tensor)\n        except (ImportError, AssertionError):\n            raise ValueError('Cannot convert from tf.Tensor')\n\n        if tensor.ndim not in {2, 3}:\n            raise ValueError('Cannot convert to aim.Image. Tensor must have 2/3-D shape.')\n\n        if tensor.dtype.is_floating:\n            tensor = tf.cast(tf.math.scalar_mul(255.0, tensor), tf.uint8)\n        array: np.ndarray = tensor.numpy()\n\n        if array.ndim == 3 and array.shape[2] == 1:  # greyscale\n            pil_image = PILImage.fromarray(array[:, :, 0])\n        else:\n            pil_image = PILImage.fromarray(array)\n        self._from_pil_image(pil_image, params)\n\n    def __eq__(self, other):\n        if not isinstance(other, Image):\n            return False\n\n        props = ['mode', 'format', 'width', 'height']\n        for p in props:\n            if self.storage[p] != other.storage[p]:\n                return False\n\n        return (self.storage['data'].data == other.storage['data'].data)\n\n\ndef convert_to_aim_image_list(images, labels=None) -> List[Image]:\n    aim_images = []\n    if labels is not None:\n        labels_it = chain(labels, repeat(''))\n    else:\n        labels_it = repeat('')\n    for img, lbl in zip(images, labels_it):\n        aim_img = Image(img, lbl if isinstance(lbl, str) else str(lbl.item()))\n        aim_images.append(aim_img)\n    return aim_images\n", "path": "aim/sdk/objects/image.py"}, {"content": "from aim.sdk.num_utils import inst_has_typename\nfrom aim.storage.object import CustomObject\nfrom aim.storage.types import BLOB\n\n\n@CustomObject.alias('aim.figure')\nclass Figure(CustomObject):\n    \"\"\"Figure object used to store plotly figures in Aim repository.\n\n    Args:\n         obj (:obj:): plotly figure object.\n    \"\"\"\n\n    AIM_NAME = 'aim.figure'\n\n    def __init__(self, obj):\n        super().__init__()\n\n        if inst_has_typename(obj, ['Figure', 'BaseFigure']):\n            self._prepare(obj)\n        else:\n            raise TypeError('Object is not a Plotly Figure instance')\n\n    def _prepare(self, obj):\n        try:\n            from plotly.version import __version__ as plotly_version\n        except ModuleNotFoundError:\n            plotly_version = 'unknown'\n\n        assert hasattr(obj, 'to_json')\n\n        self.storage['source'] = 'plotly'\n        self.storage['version'] = plotly_version\n        self.storage['format'] = 'raw_json'\n        self.storage['data'] = BLOB(data=obj.to_json())\n\n    def json(self):\n        blob_data = self.storage['data']\n        return blob_data.data\n\n    def to_plotly_figure(self):\n        try:\n            from plotly.io import from_json\n        except ModuleNotFoundError:\n            raise ModuleNotFoundError('Could not find plotly in the installed modules.')\n\n        return from_json(self.json())\n", "path": "aim/sdk/objects/figure.py"}], "after_files": [{"content": "import logging\nimport os.path\n\nfrom PIL import Image as PILImage\n\nfrom io import BytesIO\nfrom itertools import chain, repeat\nfrom typing import List\nimport numpy as np\n\nfrom aim.sdk.num_utils import inst_has_typename\nfrom aim.storage.object import CustomObject\nfrom aim.storage.types import BLOB\n\nlogger = logging.getLogger(__name__)\n\n\n@CustomObject.alias('aim.image')\nclass Image(CustomObject):\n    \"\"\"Image object used to store image objects in Aim repository...\n\n    Args:\n         image (:obj:): pillow `Image` object or `torch.Tensor` or `numpy.array` used to construct `aim.Image`.\n         caption (:obj:`str`, optional): Optional image caption. '' by default.\n         format (:obj: `str`, optional): Parameter for PIL's .save() method. 'png' by default.\n         quality (:obj: `int`, optional): Parameter for PIL's .save() method. 85 by default.\n         optimize (:obj: `bool`, optional): Parameter for PIL's .save() method. False by default.\n\n         For more information on the format, quality and optimize parameters, refer to PIL documentation.\n\n    Example of params to reduce quality of the image:\n        format='jpeg',\n        optimize=True,\n        quality=85\n    \"\"\"\n\n    FLAG_WARN_RGBA_RGB = False\n    AIM_NAME = 'aim.image'\n\n    def __init__(self, image, caption: str = '', format='png', quality=90, optimize=False):\n        super().__init__()\n\n        # normalize jpg\n        if format.lower() == 'jpg':\n            # PIL doesn't support 'jpg' key\n            format = 'jpeg'\n\n        params = {\n            'format': format.lower(),\n            'quality': quality,\n            'optimize': optimize\n        }\n\n        if isinstance(image, str):\n            self._from_file_path(image, params)\n        elif inst_has_typename(image, ['PIL', 'Image']):\n            self._from_pil_image(image, params)\n        elif inst_has_typename(image, ['torch', 'Tensor']):\n            self._from_torch_tensor(image, params)\n        elif inst_has_typename(image, ['tensorflow', 'Tensor']):\n            self._from_tf_tensor(image, params)\n        elif inst_has_typename(image, ['numpy', 'array']):\n            self._from_numpy_array(image, params)\n        elif inst_has_typename(image, ['Figure', 'matplotlib', 'figure']):\n            self._from_matplotlib_figure(image, params)\n        else:\n            raise TypeError(f'Cannot convert to aim.Image. Unsupported type {type(image)}.')\n        self.caption = caption\n\n    @property\n    def caption(self) -> str:\n        \"\"\"Image caption, set by user.\n\n            :getter: Returns image caption.\n            :setter: Sets image caption.\n            :type: string\n        \"\"\"\n        return self.storage['caption']\n\n    @caption.setter\n    def caption(self, value: str):\n        self.storage['caption'] = value\n\n    @property\n    def format(self) -> str:\n        \"\"\"Stored image format.\n\n            :getter: Returns image format.\n            :type: string\n        \"\"\"\n        return self.storage['format']\n\n    @property\n    def width(self):\n        \"\"\"Stored image width.\n\n            :getter: Returns image width.\n            :type: string\n        \"\"\"\n        return self.storage['width']\n\n    @property\n    def height(self):\n        \"\"\"Stored image height.\n\n            :getter: Returns image height.\n            :type: string\n        \"\"\"\n        return self.storage['height']\n\n    @property\n    def size(self):\n        \"\"\"Stored image size.\n\n            :getter: Returns image (width, height) pair.\n            :type: string\n        \"\"\"\n        return self.storage['width'], self.storage['height']\n\n    def to_pil_image(self) -> PILImage.Image:\n        \"\"\"Method to convert aim.Image to pillow Image\"\"\"\n        pil_img = PILImage.open(BytesIO(bytes(self.storage['data'])))\n        assert pil_img.size == self.size\n        return pil_img\n\n    def json(self):\n        \"\"\"Dump image metadata to a dict\"\"\"\n        return {\n            'caption': self.caption,\n            'format': self.format,\n            'width': self.width,\n            'height': self.height,\n        }\n\n    def _from_pil_image(self, pil_image: PILImage.Image, params):\n        assert isinstance(pil_image, PILImage.Image)\n        img_container = BytesIO()\n\n        try:\n            pil_image.save(img_container, **params)\n        except OSError as exc:\n            # The best way to approach this problem is to prepare PIL Image object before hitting this method.\n            # This block only handles case where RGBA/P/LA/PA mode is mandated to save in RGB\n            # PIL won't do that automatically, so we have to convert image to RGB before saving it.\n            # In addition - make transparency \"white\" before conversion otherwise it will be black.\n            if pil_image.mode not in ('RGBA', 'LA', 'PA', 'P'):\n                raise\n            elif not Image.FLAG_WARN_RGBA_RGB:\n                logger.warning(f'Failed to save the image due to the following error: {exc}')\n                logger.warning(f'Attempting to convert mode \"{pil_image.mode}\" to \"RGB\"')\n                Image.FLAG_WARN_RGBA_RGB = True\n\n            alpha = pil_image.convert('RGBA').split()[-1]  # Get only alpha\n            background = PILImage.new('RGBA', pil_image.size, (255, 255, 255, 255))\n            background.paste(pil_image, mask=alpha)\n            pil_image = background.convert('RGB')\n\n            # Retry\n            pil_image.save(img_container, **params)\n\n        self.storage['data'] = BLOB(data=img_container.getvalue())\n        self.storage['source'] = 'PIL.Image'\n        self.storage['mode'] = pil_image.mode\n        self.storage['format'] = params['format']\n        self.storage['width'], self.storage['height'] = pil_image.size\n\n    def _from_file_path(self, file_path, params):\n        if not os.path.isfile(file_path):\n            raise ValueError('Invalid image file path.')\n\n        return self._from_pil_image(PILImage.open(file_path), params)\n\n    def _from_numpy_array(self, array: np.ndarray, params):\n        if array.ndim not in {2, 3}:\n            raise ValueError('Cannot convert to aim.Image. array must have 2/3-D shape.')\n\n        if array.ndim == 3 and array.shape[2] == 1:  # greyscale\n            pil_image = PILImage.fromarray(array[:, :, 0])\n        else:\n            pil_image = PILImage.fromarray(array)\n        self._from_pil_image(pil_image, params)\n\n    def _from_torch_tensor(self, tensor, params):\n        try:\n            import torch\n            assert isinstance(tensor, torch.Tensor)\n        except (ImportError, AssertionError):\n            raise ValueError('Cannot convert from torch.Tensor')\n\n        if tensor.ndim not in {2, 3}:\n            raise ValueError('Cannot convert to aim.Image. Tensor must have 2/3-D shape.')\n        if tensor.is_floating_point():\n            tensor = tensor.mul(255).byte()\n        array: np.ndarray = tensor.cpu().numpy()\n\n        if array.ndim == 3:\n            channels = array.shape[0]\n            if channels == 1:\n                pil_image = PILImage.fromarray(array[0, :, :])\n            else:\n                # reverse order of channels: c,h,w => h,w,c\n                pil_image = PILImage.fromarray(np.transpose(array, (1, 2, 0)))\n        else:\n            pil_image = PILImage.fromarray(array)\n        self._from_pil_image(pil_image, params)\n\n    def _from_tf_tensor(self, tensor, params):\n        try:\n            import tensorflow as tf\n            assert isinstance(tensor, tf.Tensor)\n        except (ImportError, AssertionError):\n            raise ValueError('Cannot convert from tf.Tensor')\n\n        if tensor.ndim not in {2, 3}:\n            raise ValueError('Cannot convert to aim.Image. Tensor must have 2/3-D shape.')\n\n        if tensor.dtype.is_floating:\n            tensor = tf.cast(tf.math.scalar_mul(255.0, tensor), tf.uint8)\n        array: np.ndarray = tensor.numpy()\n\n        if array.ndim == 3 and array.shape[2] == 1:  # greyscale\n            pil_image = PILImage.fromarray(array[:, :, 0])\n        else:\n            pil_image = PILImage.fromarray(array)\n        self._from_pil_image(pil_image, params)\n\n    def _from_matplotlib_figure(self, figure, params):\n        try:\n            assert hasattr(figure, 'savefig')\n        except AssertionError:\n            raise ValueError('Cannot convert from matplotlib figure.')\n\n        buffer = BytesIO()\n        figure.savefig(buffer)\n        buffer.seek(0)\n\n        return self._from_pil_image(PILImage.open(buffer), params)\n\n    def __eq__(self, other):\n        if not isinstance(other, Image):\n            return False\n\n        props = ['mode', 'format', 'width', 'height']\n        for p in props:\n            if self.storage[p] != other.storage[p]:\n                return False\n\n        return (self.storage['data'].data == other.storage['data'].data)\n\n\ndef convert_to_aim_image_list(images, labels=None) -> List[Image]:\n    aim_images = []\n    if labels is not None:\n        labels_it = chain(labels, repeat(''))\n    else:\n        labels_it = repeat('')\n    for img, lbl in zip(images, labels_it):\n        aim_img = Image(img, lbl if isinstance(lbl, str) else str(lbl.item()))\n        aim_images.append(aim_img)\n    return aim_images\n", "path": "aim/sdk/objects/image.py"}, {"content": "from aim.sdk.num_utils import inst_has_typename\nfrom aim.storage.object import CustomObject\nfrom aim.storage.types import BLOB\n\n\n@CustomObject.alias('aim.figure')\nclass Figure(CustomObject):\n    \"\"\"\n    Figure object can be used for storing Plotly or Matplotlib figures into Aim repository.\n    Core functionality is based on Plotly.\n\n    Args:\n         obj (:obj:): plotly or matplotlib figure object.\n    \"\"\"\n\n    AIM_NAME = 'aim.figure'\n\n    def __init__(self, obj):\n        super().__init__()\n\n        if inst_has_typename(obj, ['matplotlib', 'Figure']):\n            self._from_matplotlib_figure(obj)\n        elif inst_has_typename(obj, ['plotly', 'Figure', 'BaseFigure']):\n            self._prepare(obj)\n        else:\n            raise TypeError('Object is not a Plotly Figure instance')\n\n    def _prepare(self, obj):\n        try:\n            from plotly.version import __version__ as plotly_version\n        except ModuleNotFoundError:\n            plotly_version = 'unknown'\n\n        assert hasattr(obj, 'to_json')\n\n        self.storage['source'] = 'plotly'\n        self.storage['version'] = plotly_version\n        self.storage['format'] = 'raw_json'\n        self.storage['data'] = BLOB(data=obj.to_json())\n\n    def _from_matplotlib_figure(self, obj):\n        try:\n            from plotly.tools import mpl_to_plotly\n        except ModuleNotFoundError:\n            raise ModuleNotFoundError('Plotly is required to track matplotlib figure.')\n\n        try:\n            plotly_obj = mpl_to_plotly(obj)\n        except ValueError as err:\n            raise ValueError(f'Failed to convert matplotlib figure to plotly figure: {err}')\n\n        return self._prepare(plotly_obj)\n\n    def json(self):\n        blob_data = self.storage['data']\n        return blob_data.data\n\n    def to_plotly_figure(self):\n        try:\n            from plotly.io import from_json\n        except ModuleNotFoundError:\n            raise ModuleNotFoundError('Could not find plotly in the installed modules.')\n\n        return from_json(self.json())\n", "path": "aim/sdk/objects/figure.py"}]}