|
3 | 3 | from datetime import datetime
|
4 | 4 | from functools import partial
|
5 | 5 | from math import ceil, log10
|
| 6 | +import pandas as pd |
| 7 | +from pathlib import Path |
6 | 8 | from types import MethodType
|
7 | 9 | from typing import Optional, Sequence, Union
|
8 | 10 |
|
|
36 | 38 |
|
37 | 39 | from napari_deeplabcut import keypoints
|
38 | 40 | from napari_deeplabcut._reader import _load_config
|
39 |
| -from napari_deeplabcut._writer import _write_config, _write_image |
40 |
| -from napari_deeplabcut.misc import encode_categories, to_os_dir_sep |
| 41 | +from napari_deeplabcut._writer import _write_config, _write_image, _form_df |
| 42 | +from napari_deeplabcut.misc import ( |
| 43 | + encode_categories, |
| 44 | + to_os_dir_sep, |
| 45 | + guarantee_multiindex_rows, |
| 46 | +) |
41 | 47 |
|
42 | 48 |
|
43 | 49 | def _get_and_try_preferred_reader(
|
@@ -257,19 +263,41 @@ def _form_video_action_menu(self):
|
257 | 263 | return extract_button, crop_button
|
258 | 264 |
|
259 | 265 | def _extract_single_frame(self, *args):
|
260 |
| - layer = None |
261 |
| - for layer_ in self.viewer.layers: |
262 |
| - if isinstance(layer_, Image): |
263 |
| - layer = layer_ |
264 |
| - break |
265 |
| - if layer is not None: |
| 266 | + image_layer = None |
| 267 | + points_layer = None |
| 268 | + for layer in self.viewer.layers: |
| 269 | + if isinstance(layer, Image): |
| 270 | + image_layer = layer |
| 271 | + elif isinstance(layer, Points): |
| 272 | + points_layer = layer |
| 273 | + if image_layer is not None: |
266 | 274 | ind = self.viewer.dims.current_step[0]
|
267 |
| - frame = layer.data[ind] |
268 |
| - n_frames = layer.data.shape[0] |
| 275 | + frame = image_layer.data[ind] |
| 276 | + n_frames = image_layer.data.shape[0] |
269 | 277 | name = f"img{str(ind).zfill(int(ceil(log10(n_frames))))}.png"
|
270 |
| - output_path = os.path.join(layer.metadata["root"], name) |
| 278 | + output_path = os.path.join(image_layer.metadata["root"], name) |
271 | 279 | _write_image(frame, str(output_path))
|
272 | 280 |
|
| 281 | + # If annotations were loaded, they should be written to a machinefile.h5 file |
| 282 | + if points_layer is not None: |
| 283 | + df = _form_df( |
| 284 | + points_layer.data, |
| 285 | + { |
| 286 | + "metadata": points_layer.metadata, |
| 287 | + "properties": points_layer.properties, |
| 288 | + }, |
| 289 | + ) |
| 290 | + df = df.iloc[ind:ind + 1] |
| 291 | + df.index = pd.MultiIndex.from_tuples([Path(output_path).parts[-3:]]) |
| 292 | + filepath = os.path.join(image_layer.metadata["root"], "machinelabels-iter0.h5") |
| 293 | + if Path(filepath).is_file(): |
| 294 | + df_prev = pd.read_hdf(filepath) |
| 295 | + guarantee_multiindex_rows(df_prev) |
| 296 | + df = pd.concat([df_prev, df]) |
| 297 | + df = df[~df.index.duplicated(keep="first")] |
| 298 | + df.to_hdf(filepath, key="machinelabels") |
| 299 | + |
| 300 | + |
273 | 301 | def _store_crop_coordinates(self, *args):
|
274 | 302 | if not (project_path := self._images_meta.get("project")):
|
275 | 303 | return
|
|
0 commit comments