We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
`TensorboardFolderTracker` missing Image import
## 🐛 Bug

Currently the `TensorboardFolderTracker` will crash if there are any images that need to be parsed. This is because currently `aim.Image` is only imported during type checking, however `_process_tb_event` attempts to create an `Image` instance, without access

### To reproduce

- Create a tensorboard events file that contains an image
- Create a run using the `aim.ext.tensorboard_tracker.tracker.Run` runner.
- Observe the console

### Expected behavior

Images are converted and added to the RocksDB database.

### Environment

- Aim Version 3.17.3
- Python version 3.10.5
- pip version 22.0.4
- OS Ubuntu
- Any other relevant information



--- 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/ext/tensorboard_tracker/tracker.py`
Content:
```
1 from tensorboard.backend.event_processing.directory_watcher import DirectoryWatcher
2 from tensorboard.backend.event_processing import event_file_loader
3 import tensorflow as tf
4 from tensorboard.util import tensor_util
5 import time
6 import threading
7 from pathlib import Path
8 import logging
9 import os
10 import weakref
11 import queue
12 
13 from typing import TYPE_CHECKING, Any
14 
15 if TYPE_CHECKING:
16     from aim import Audio, Image
17 
18 
19 class TensorboardTracker:
20     def __init__(self, tracker, sync_tensorboard_log_dir: str) -> None:
21         self.tracker = tracker
22         self.sync_tensorboard_log_dir = sync_tensorboard_log_dir
23         self.tensorboard_folder_watchers = []
24         self._thread = threading.Thread(target=self._monitor_eventfiles, daemon=True)
25         self.directories_track_status = {}
26         self._shutdown = False
27         self._started = False
28         self._watcher_queue = queue.Queue()
29 
30     def _monitor_eventfiles(self):
31         while True:
32             if self._shutdown:
33                 break
34             for event_file in set(Path(self.sync_tensorboard_log_dir).rglob("*.tfevents*")):
35                 dir = str(event_file.parent.absolute())
36                 if dir not in self.directories_track_status:
37                     self.directories_track_status[dir] = "NOT_STARTED"
38             for dir, status in self.directories_track_status.items():
39                 if status == "NOT_STARTED":
40                     tensorboard_folder_watcher = TensorboardFolderTracker(dir, self._watcher_queue)
41                     tensorboard_folder_watcher.start()
42                     self.tensorboard_folder_watchers.append(tensorboard_folder_watcher)
43                     self.directories_track_status[dir] = "STARTED"
44             time.sleep(5)
45 
46     def start(self):
47         if self._started:
48             return
49         self._started = True
50         self._thread.start()
51         self._consumer = TensorboardEventConsumer(
52             self._watcher_queue, self.tracker
53         )
54         self._consumer.start()
55 
56     def stop(self):
57         if not self._started:
58             return
59         self._shutdown = True
60         self._thread.join()
61         for tensorboard_folder_watcher in self.tensorboard_folder_watchers:
62             tensorboard_folder_watcher.stop()
63         self._consumer.stop()
64 
65     def close(self):
66         """Interface to make compatible with Resource AutoClean"""
67         self.stop()
68 
69 
70 class TensorboardFolderTracker:
71     def __init__(self, tensorboard_event_folder: str, queue: queue.Queue) -> None:
72         self.queue = queue
73         self.supported_plugins = ("images", "scalars")
74         self.unsupported_plugin_noticed = False
75         self.folder_name = os.path.basename(tensorboard_event_folder)
76         self._thread = threading.Thread(target=self._process_event)
77         self._generator = DirectoryWatcher(tensorboard_event_folder, event_file_loader.EventFileLoader)
78         self._shutdown = False
79         self._started = False
80 
81     def start(self):
82         if self._started:
83             return
84         self._started = True
85         self._thread.start()
86 
87     def stop(self):
88         if not self._started:
89             return
90         self._shutdown = True
91         self._thread.join()
92 
93     def _process_event(self):
94         while True:
95             if self._shutdown:
96                 break
97             for event in self._generator.Load():
98                 self._process_tb_event(event)
99             time.sleep(1)
100 
101     def _process_tb_event(self, event):
102         def create_ndarray(tensor):
103             res = tensor_util.make_ndarray(tensor)
104             if res.dtype == "object":
105                 return None
106             else:
107                 return res
108 
109         step = event.step
110         fail_count = 0
111         _err_info = None
112 
113         for value in event.summary.value:
114             tag = value.tag
115             plugin_name = value.metadata.plugin_data.plugin_name
116             if len(plugin_name) > 0 and plugin_name not in self.supported_plugins:
117                 if not self.unsupported_plugin_noticed:
118                     logging.warning(
119                         "Found unsupported plugin type({}) in the log file. "
120                         "Data for these wont be processed. "
121                         "Supported plugin types are: {}".format(plugin_name, ", ".join(self.supported_plugins)),
122                     )
123                     self.unsupported_plugin_noticed = True
124                 continue
125             track_val = None
126             try:
127                 if value.HasField("tensor"):
128                     # TODO: [MV] check the case when audios are passed via tensor
129                     if plugin_name == "images":
130                         tensor = value.tensor.string_val[2:]
131                         track_val = [Image(tf.image.decode_image(t).numpy()) for t in tensor]
132                         if len(track_val) == 1:
133                             track_val = track_val[0]
134                     elif plugin_name == "scalars" or plugin_name == "":
135                         track_val = create_ndarray(value.tensor)
136                     else:
137                         track_val = value.tensor.float_val[0]
138                 elif value.HasField("simple_value"):
139                     track_val = value.simple_value
140                 elif value.HasField("image"):
141                     track_val = Image(tf.image.decode_image(value.image.encoded_image_string).numpy())
142                 elif value.HasField("audio"):
143                     tf_audio, sample_rate = tf.audio.decode_wav(value.audio.encoded_audio_string)
144                     track_val = Audio(tf_audio.numpy(), rate=sample_rate)
145 
146             except RuntimeError as exc:
147                 # catch all the nasty failures
148                 fail_count += 1
149                 if not _err_info:
150                     _err_info = str(exc)
151                 continue
152 
153             if track_val is not None:
154                 self.queue.put(TensorboardEvent(track_val, tag, step, context={'entry': self.folder_name}))
155         if fail_count:
156             logging.warning(f"Failed to process {fail_count} entries. First exception: {_err_info}")
157 
158 
159 class TensorboardEvent:
160 
161     def __init__(self, value: Any, name: str, step: int, context: dict) -> None:
162         self.value = value
163         self.name = name
164         self.step = step
165         self.context = context
166 
167 
168 class TensorboardEventConsumer:
169 
170     def __init__(self, queue: queue.Queue, tracker) -> None:
171         self._tracker = weakref.ref(tracker)
172         self._queue = queue
173         self._thread = threading.Thread(target=self._process_events, daemon=True)
174         self._shutdown = False
175         self._started = False
176 
177     def start(self):
178         if self._started:
179             return
180         self._started = True
181         self._thread.start()
182 
183     def _process_events(self):
184         while True:
185             try:
186                 event = self._queue.get(True, 1)
187                 if event:
188                     self._tracker()(event.value, event.name, event.step, context=event.context)
189             except queue.Empty:
190                 event = None
191                 if self._shutdown:
192                     break
193 
194     def stop(self):
195         if not self._started:
196             return
197         self._shutdown = True
198         self._thread.join()
199
```
--- 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/ext/tensorboard_tracker/tracker.py b/aim/ext/tensorboard_tracker/tracker.py
--- a/aim/ext/tensorboard_tracker/tracker.py
+++ b/aim/ext/tensorboard_tracker/tracker.py
@@ -10,10 +10,8 @@
 import weakref
 import queue
 
-from typing import TYPE_CHECKING, Any
-
-if TYPE_CHECKING:
-    from aim import Audio, Image
+from typing import Any
+from aim import Audio, Image
 
 
 class TensorboardTracker:

{"golden_diff": "diff --git a/aim/ext/tensorboard_tracker/tracker.py b/aim/ext/tensorboard_tracker/tracker.py\n--- a/aim/ext/tensorboard_tracker/tracker.py\n+++ b/aim/ext/tensorboard_tracker/tracker.py\n@@ -10,10 +10,8 @@\n import weakref\n import queue\n \n-from typing import TYPE_CHECKING, Any\n-\n-if TYPE_CHECKING:\n-    from aim import Audio, Image\n+from typing import Any\n+from aim import Audio, Image\n \n \n class TensorboardTracker:\n", "issue": "`TensorboardFolderTracker` missing Image import\n## \ud83d\udc1b Bug\r\n\r\nCurrently the `TensorboardFolderTracker` will crash if there are any images that need to be parsed. This is because currently `aim.Image` is only imported during type checking, however `_process_tb_event` attempts to create an `Image` instance, without access\r\n\r\n### To reproduce\r\n\r\n- Create a tensorboard events file that contains an image\r\n- Create a run using the `aim.ext.tensorboard_tracker.tracker.Run` runner.\r\n- Observe the console\r\n\r\n### Expected behavior\r\n\r\nImages are converted and added to the RocksDB database.\r\n\r\n### Environment\r\n\r\n- Aim Version 3.17.3\r\n- Python version 3.10.5\r\n- pip version 22.0.4\r\n- OS Ubuntu\r\n- Any other relevant information\r\n\r\n\n", "before_files": [{"content": "from tensorboard.backend.event_processing.directory_watcher import DirectoryWatcher\nfrom tensorboard.backend.event_processing import event_file_loader\nimport tensorflow as tf\nfrom tensorboard.util import tensor_util\nimport time\nimport threading\nfrom pathlib import Path\nimport logging\nimport os\nimport weakref\nimport queue\n\nfrom typing import TYPE_CHECKING, Any\n\nif TYPE_CHECKING:\n    from aim import Audio, Image\n\n\nclass TensorboardTracker:\n    def __init__(self, tracker, sync_tensorboard_log_dir: str) -> None:\n        self.tracker = tracker\n        self.sync_tensorboard_log_dir = sync_tensorboard_log_dir\n        self.tensorboard_folder_watchers = []\n        self._thread = threading.Thread(target=self._monitor_eventfiles, daemon=True)\n        self.directories_track_status = {}\n        self._shutdown = False\n        self._started = False\n        self._watcher_queue = queue.Queue()\n\n    def _monitor_eventfiles(self):\n        while True:\n            if self._shutdown:\n                break\n            for event_file in set(Path(self.sync_tensorboard_log_dir).rglob(\"*.tfevents*\")):\n                dir = str(event_file.parent.absolute())\n                if dir not in self.directories_track_status:\n                    self.directories_track_status[dir] = \"NOT_STARTED\"\n            for dir, status in self.directories_track_status.items():\n                if status == \"NOT_STARTED\":\n                    tensorboard_folder_watcher = TensorboardFolderTracker(dir, self._watcher_queue)\n                    tensorboard_folder_watcher.start()\n                    self.tensorboard_folder_watchers.append(tensorboard_folder_watcher)\n                    self.directories_track_status[dir] = \"STARTED\"\n            time.sleep(5)\n\n    def start(self):\n        if self._started:\n            return\n        self._started = True\n        self._thread.start()\n        self._consumer = TensorboardEventConsumer(\n            self._watcher_queue, self.tracker\n        )\n        self._consumer.start()\n\n    def stop(self):\n        if not self._started:\n            return\n        self._shutdown = True\n        self._thread.join()\n        for tensorboard_folder_watcher in self.tensorboard_folder_watchers:\n            tensorboard_folder_watcher.stop()\n        self._consumer.stop()\n\n    def close(self):\n        \"\"\"Interface to make compatible with Resource AutoClean\"\"\"\n        self.stop()\n\n\nclass TensorboardFolderTracker:\n    def __init__(self, tensorboard_event_folder: str, queue: queue.Queue) -> None:\n        self.queue = queue\n        self.supported_plugins = (\"images\", \"scalars\")\n        self.unsupported_plugin_noticed = False\n        self.folder_name = os.path.basename(tensorboard_event_folder)\n        self._thread = threading.Thread(target=self._process_event)\n        self._generator = DirectoryWatcher(tensorboard_event_folder, event_file_loader.EventFileLoader)\n        self._shutdown = False\n        self._started = False\n\n    def start(self):\n        if self._started:\n            return\n        self._started = True\n        self._thread.start()\n\n    def stop(self):\n        if not self._started:\n            return\n        self._shutdown = True\n        self._thread.join()\n\n    def _process_event(self):\n        while True:\n            if self._shutdown:\n                break\n            for event in self._generator.Load():\n                self._process_tb_event(event)\n            time.sleep(1)\n\n    def _process_tb_event(self, event):\n        def create_ndarray(tensor):\n            res = tensor_util.make_ndarray(tensor)\n            if res.dtype == \"object\":\n                return None\n            else:\n                return res\n\n        step = event.step\n        fail_count = 0\n        _err_info = None\n\n        for value in event.summary.value:\n            tag = value.tag\n            plugin_name = value.metadata.plugin_data.plugin_name\n            if len(plugin_name) > 0 and plugin_name not in self.supported_plugins:\n                if not self.unsupported_plugin_noticed:\n                    logging.warning(\n                        \"Found unsupported plugin type({}) in the log file. \"\n                        \"Data for these wont be processed. \"\n                        \"Supported plugin types are: {}\".format(plugin_name, \", \".join(self.supported_plugins)),\n                    )\n                    self.unsupported_plugin_noticed = True\n                continue\n            track_val = None\n            try:\n                if value.HasField(\"tensor\"):\n                    # TODO: [MV] check the case when audios are passed via tensor\n                    if plugin_name == \"images\":\n                        tensor = value.tensor.string_val[2:]\n                        track_val = [Image(tf.image.decode_image(t).numpy()) for t in tensor]\n                        if len(track_val) == 1:\n                            track_val = track_val[0]\n                    elif plugin_name == \"scalars\" or plugin_name == \"\":\n                        track_val = create_ndarray(value.tensor)\n                    else:\n                        track_val = value.tensor.float_val[0]\n                elif value.HasField(\"simple_value\"):\n                    track_val = value.simple_value\n                elif value.HasField(\"image\"):\n                    track_val = Image(tf.image.decode_image(value.image.encoded_image_string).numpy())\n                elif value.HasField(\"audio\"):\n                    tf_audio, sample_rate = tf.audio.decode_wav(value.audio.encoded_audio_string)\n                    track_val = Audio(tf_audio.numpy(), rate=sample_rate)\n\n            except RuntimeError as exc:\n                # catch all the nasty failures\n                fail_count += 1\n                if not _err_info:\n                    _err_info = str(exc)\n                continue\n\n            if track_val is not None:\n                self.queue.put(TensorboardEvent(track_val, tag, step, context={'entry': self.folder_name}))\n        if fail_count:\n            logging.warning(f\"Failed to process {fail_count} entries. First exception: {_err_info}\")\n\n\nclass TensorboardEvent:\n\n    def __init__(self, value: Any, name: str, step: int, context: dict) -> None:\n        self.value = value\n        self.name = name\n        self.step = step\n        self.context = context\n\n\nclass TensorboardEventConsumer:\n\n    def __init__(self, queue: queue.Queue, tracker) -> None:\n        self._tracker = weakref.ref(tracker)\n        self._queue = queue\n        self._thread = threading.Thread(target=self._process_events, daemon=True)\n        self._shutdown = False\n        self._started = False\n\n    def start(self):\n        if self._started:\n            return\n        self._started = True\n        self._thread.start()\n\n    def _process_events(self):\n        while True:\n            try:\n                event = self._queue.get(True, 1)\n                if event:\n                    self._tracker()(event.value, event.name, event.step, context=event.context)\n            except queue.Empty:\n                event = None\n                if self._shutdown:\n                    break\n\n    def stop(self):\n        if not self._started:\n            return\n        self._shutdown = True\n        self._thread.join()\n", "path": "aim/ext/tensorboard_tracker/tracker.py"}], "after_files": [{"content": "from tensorboard.backend.event_processing.directory_watcher import DirectoryWatcher\nfrom tensorboard.backend.event_processing import event_file_loader\nimport tensorflow as tf\nfrom tensorboard.util import tensor_util\nimport time\nimport threading\nfrom pathlib import Path\nimport logging\nimport os\nimport weakref\nimport queue\n\nfrom typing import Any\nfrom aim import Audio, Image\n\n\nclass TensorboardTracker:\n    def __init__(self, tracker, sync_tensorboard_log_dir: str) -> None:\n        self.tracker = tracker\n        self.sync_tensorboard_log_dir = sync_tensorboard_log_dir\n        self.tensorboard_folder_watchers = []\n        self._thread = threading.Thread(target=self._monitor_eventfiles, daemon=True)\n        self.directories_track_status = {}\n        self._shutdown = False\n        self._started = False\n        self._watcher_queue = queue.Queue()\n\n    def _monitor_eventfiles(self):\n        while True:\n            if self._shutdown:\n                break\n            for event_file in set(Path(self.sync_tensorboard_log_dir).rglob(\"*.tfevents*\")):\n                dir = str(event_file.parent.absolute())\n                if dir not in self.directories_track_status:\n                    self.directories_track_status[dir] = \"NOT_STARTED\"\n            for dir, status in self.directories_track_status.items():\n                if status == \"NOT_STARTED\":\n                    tensorboard_folder_watcher = TensorboardFolderTracker(dir, self._watcher_queue)\n                    tensorboard_folder_watcher.start()\n                    self.tensorboard_folder_watchers.append(tensorboard_folder_watcher)\n                    self.directories_track_status[dir] = \"STARTED\"\n            time.sleep(5)\n\n    def start(self):\n        if self._started:\n            return\n        self._started = True\n        self._thread.start()\n        self._consumer = TensorboardEventConsumer(\n            self._watcher_queue, self.tracker\n        )\n        self._consumer.start()\n\n    def stop(self):\n        if not self._started:\n            return\n        self._shutdown = True\n        self._thread.join()\n        for tensorboard_folder_watcher in self.tensorboard_folder_watchers:\n            tensorboard_folder_watcher.stop()\n        self._consumer.stop()\n\n    def close(self):\n        \"\"\"Interface to make compatible with Resource AutoClean\"\"\"\n        self.stop()\n\n\nclass TensorboardFolderTracker:\n    def __init__(self, tensorboard_event_folder: str, queue: queue.Queue) -> None:\n        self.queue = queue\n        self.supported_plugins = (\"images\", \"scalars\")\n        self.unsupported_plugin_noticed = False\n        self.folder_name = os.path.basename(tensorboard_event_folder)\n        self._thread = threading.Thread(target=self._process_event)\n        self._generator = DirectoryWatcher(tensorboard_event_folder, event_file_loader.EventFileLoader)\n        self._shutdown = False\n        self._started = False\n\n    def start(self):\n        if self._started:\n            return\n        self._started = True\n        self._thread.start()\n\n    def stop(self):\n        if not self._started:\n            return\n        self._shutdown = True\n        self._thread.join()\n\n    def _process_event(self):\n        while True:\n            if self._shutdown:\n                break\n            for event in self._generator.Load():\n                self._process_tb_event(event)\n            time.sleep(1)\n\n    def _process_tb_event(self, event):\n        def create_ndarray(tensor):\n            res = tensor_util.make_ndarray(tensor)\n            if res.dtype == \"object\":\n                return None\n            else:\n                return res\n\n        step = event.step\n        fail_count = 0\n        _err_info = None\n\n        for value in event.summary.value:\n            tag = value.tag\n            plugin_name = value.metadata.plugin_data.plugin_name\n            if len(plugin_name) > 0 and plugin_name not in self.supported_plugins:\n                if not self.unsupported_plugin_noticed:\n                    logging.warning(\n                        \"Found unsupported plugin type({}) in the log file. \"\n                        \"Data for these wont be processed. \"\n                        \"Supported plugin types are: {}\".format(plugin_name, \", \".join(self.supported_plugins)),\n                    )\n                    self.unsupported_plugin_noticed = True\n                continue\n            track_val = None\n            try:\n                if value.HasField(\"tensor\"):\n                    # TODO: [MV] check the case when audios are passed via tensor\n                    if plugin_name == \"images\":\n                        tensor = value.tensor.string_val[2:]\n                        track_val = [Image(tf.image.decode_image(t).numpy()) for t in tensor]\n                        if len(track_val) == 1:\n                            track_val = track_val[0]\n                    elif plugin_name == \"scalars\" or plugin_name == \"\":\n                        track_val = create_ndarray(value.tensor)\n                    else:\n                        track_val = value.tensor.float_val[0]\n                elif value.HasField(\"simple_value\"):\n                    track_val = value.simple_value\n                elif value.HasField(\"image\"):\n                    track_val = Image(tf.image.decode_image(value.image.encoded_image_string).numpy())\n                elif value.HasField(\"audio\"):\n                    tf_audio, sample_rate = tf.audio.decode_wav(value.audio.encoded_audio_string)\n                    track_val = Audio(tf_audio.numpy(), rate=sample_rate)\n\n            except RuntimeError as exc:\n                # catch all the nasty failures\n                fail_count += 1\n                if not _err_info:\n                    _err_info = str(exc)\n                continue\n\n            if track_val is not None:\n                self.queue.put(TensorboardEvent(track_val, tag, step, context={'entry': self.folder_name}))\n        if fail_count:\n            logging.warning(f\"Failed to process {fail_count} entries. First exception: {_err_info}\")\n\n\nclass TensorboardEvent:\n\n    def __init__(self, value: Any, name: str, step: int, context: dict) -> None:\n        self.value = value\n        self.name = name\n        self.step = step\n        self.context = context\n\n\nclass TensorboardEventConsumer:\n\n    def __init__(self, queue: queue.Queue, tracker) -> None:\n        self._tracker = weakref.ref(tracker)\n        self._queue = queue\n        self._thread = threading.Thread(target=self._process_events, daemon=True)\n        self._shutdown = False\n        self._started = False\n\n    def start(self):\n        if self._started:\n            return\n        self._started = True\n        self._thread.start()\n\n    def _process_events(self):\n        while True:\n            try:\n                event = self._queue.get(True, 1)\n                if event:\n                    self._tracker()(event.value, event.name, event.step, context=event.context)\n            except queue.Empty:\n                event = None\n                if self._shutdown:\n                    break\n\n    def stop(self):\n        if not self._started:\n            return\n        self._shutdown = True\n        self._thread.join()\n", "path": "aim/ext/tensorboard_tracker/tracker.py"}]}