|
| 1 | +import json |
1 | 2 | import os |
| 3 | +from pathlib import Path |
2 | 4 |
|
3 | 5 | import cv2 |
4 | 6 | import numpy as np |
5 | 7 | import pandas as pd |
6 | 8 | import pytest |
| 9 | +from PIL import Image |
7 | 10 | from qtpy.QtWidgets import QDockWidget |
8 | 11 | from skimage.io import imsave |
9 | 12 |
|
@@ -151,3 +154,78 @@ def video_path(tmp_path_factory): |
151 | 154 | writer.write(frame) |
152 | 155 | writer.release() |
153 | 156 | return output_path |
| 157 | + |
| 158 | + |
| 159 | +@pytest.fixture |
| 160 | +def superkeypoints_assets(tmp_path, monkeypatch): |
| 161 | + """ |
| 162 | + Create a fake module dir with the expected assets layout: |
| 163 | +
|
| 164 | + module_dir/_reader_fake.py -> patched as __file__ |
| 165 | + module_dir/assets/fake.json |
| 166 | + module_dir/assets/fake.jpg |
| 167 | +
|
| 168 | + This mirrors the code under test: |
| 169 | + Path(__file__).parent / "assets" / f"{super_animal}.json|.jpg" |
| 170 | + """ |
| 171 | + module_dir = tmp_path / "module" |
| 172 | + assets_dir = module_dir / "assets" |
| 173 | + assets_dir.mkdir(parents=True) |
| 174 | + |
| 175 | + super_animal = "fake" |
| 176 | + data = { |
| 177 | + "SK1": [10.0, 20.0], |
| 178 | + "SK2": [40.0, 60.0], |
| 179 | + } |
| 180 | + |
| 181 | + # JSON with superkeypoints coordinates |
| 182 | + (assets_dir / f"{super_animal}.json").write_text(json.dumps(data)) |
| 183 | + |
| 184 | + # Small 10x10 RGB diagram |
| 185 | + Image.new("RGB", (10, 10), "white").save(assets_dir / f"{super_animal}.jpg") |
| 186 | + |
| 187 | + # Patch the module's __file__ so that Path(__file__).parent == module_dir |
| 188 | + fake_module_file = module_dir / "_reader_fake.py" |
| 189 | + fake_module_file.write_text("# fake") |
| 190 | + monkeypatch.setattr("napari_deeplabcut._reader.__file__", str(fake_module_file)) |
| 191 | + |
| 192 | + return { |
| 193 | + "module_dir": module_dir, |
| 194 | + "assets_dir": assets_dir, |
| 195 | + "super_animal": super_animal, |
| 196 | + "data": data, |
| 197 | + } |
| 198 | + |
| 199 | + |
| 200 | +@pytest.fixture |
| 201 | +def mapped_points(points, superkeypoints_assets, config_path): |
| 202 | + """ |
| 203 | + Return a DLC Points layer that is ready for _map_keypoints(): |
| 204 | + - metadata['project'] is set (so the widget can write config.yaml) |
| 205 | + - metadata['tables'] contains a mapping for two real bodyparts -> SK1/SK2 |
| 206 | + - at least two rows have coordinates exactly on the SK1/SK2 positions |
| 207 | + and their labels are set to those bodyparts, guaranteeing a neighbor match. |
| 208 | + """ |
| 209 | + layer = points # DLC layer created via viewer.open(..., plugin="napari-deeplabcut") |
| 210 | + super_animal = superkeypoints_assets["super_animal"] |
| 211 | + superkpts = superkeypoints_assets["data"] |
| 212 | + |
| 213 | + # Required by _map_keypoints to locate and write config.yaml |
| 214 | + # NOTE: This relies on config_path pointing to a file directly under the |
| 215 | + # project directory, so that Path(config_path).parent is the project root. |
| 216 | + layer.metadata["project"] = str(Path(config_path).parent) |
| 217 | + header = layer.metadata["header"] |
| 218 | + bp1, bp2 = header.bodyparts[:2] |
| 219 | + |
| 220 | + # Inject a conversion table into metadata |
| 221 | + layer.metadata["tables"] = {super_animal: {bp1: "SK1", bp2: "SK2"}} |
| 222 | + |
| 223 | + # Ensure _map_keypoints finds matches: |
| 224 | + # Put the first two rows exactly on SK1/SK2 and set their labels accordingly. |
| 225 | + layer.data[0, 1:] = np.array(superkpts["SK1"], dtype=float) |
| 226 | + layer.properties["label"][0] = bp1 |
| 227 | + |
| 228 | + layer.data[1, 1:] = np.array(superkpts["SK2"], dtype=float) |
| 229 | + layer.properties["label"][1] = bp2 |
| 230 | + |
| 231 | + return layer, super_animal, bp1, bp2 |
0 commit comments