descriptron-vision 2.0.2__tar.gz → 2.0.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {descriptron_vision-2.0.2 → descriptron_vision-2.0.4}/PKG-INFO +2 -2
- descriptron_vision-2.0.4/pyproject.toml +64 -0
- descriptron_vision-2.0.4/pyproject.toml.bak_pre_204_20260924_165335 +64 -0
- descriptron_vision-2.0.4/src/descriptron_vision/cli.py +66 -0
- {descriptron_vision-2.0.2 → descriptron_vision-2.0.4}/src/descriptron_vision/tools/dinov3_landmark_transfer_v51.py +40 -5
- descriptron_vision-2.0.4/src/descriptron_vision/tools/dinov3_landmark_transfer_v52.py +1019 -0
- {descriptron_vision-2.0.2 → descriptron_vision-2.0.4}/src/descriptron_vision/tools/sam2_pal_batch_v21.py +167 -4
- {descriptron_vision-2.0.2 → descriptron_vision-2.0.4}/.gitignore +0 -0
- {descriptron_vision-2.0.2 → descriptron_vision-2.0.4}/LICENSE +0 -0
- {descriptron_vision-2.0.2 → descriptron_vision-2.0.4}/NOTICE +0 -0
- {descriptron_vision-2.0.2 → descriptron_vision-2.0.4}/README.md +0 -0
- /descriptron_vision-2.0.2/pyproject.toml → /descriptron_vision-2.0.4/pyproject.toml.bak_pre_203_20260924_145320 +0 -0
- {descriptron_vision-2.0.2 → descriptron_vision-2.0.4}/src/descriptron_vision/__init__.py +0 -0
- /descriptron_vision-2.0.2/src/descriptron_vision/cli.py → /descriptron_vision-2.0.4/src/descriptron_vision/cli.py.bak_pre_v52_20260924_142454 +0 -0
- {descriptron_vision-2.0.2 → descriptron_vision-2.0.4}/src/descriptron_vision/tools/biosyslit_rag_retrieval.py +0 -0
- {descriptron_vision-2.0.2 → descriptron_vision-2.0.4}/src/descriptron_vision/tools/measurement_script_to_try_after_kpts_prediction_measure_kpts_V35.py +0 -0
- {descriptron_vision-2.0.2 → descriptron_vision-2.0.4}/src/descriptron_vision/tools/torchvision_det/README.md +0 -0
- {descriptron_vision-2.0.2 → descriptron_vision-2.0.4}/src/descriptron_vision/tools/torchvision_det/d2_predict_fold_v1.py +0 -0
- {descriptron_vision-2.0.2 → descriptron_vision-2.0.4}/src/descriptron_vision/tools/torchvision_det/fix_coco_image_sizes_v1.py +0 -0
- {descriptron_vision-2.0.2 → descriptron_vision-2.0.4}/src/descriptron_vision/tools/torchvision_det/render_detections_v1.py +0 -0
- {descriptron_vision-2.0.2 → descriptron_vision-2.0.4}/src/descriptron_vision/tools/torchvision_det/run_20k_comparison.sh +0 -0
- {descriptron_vision-2.0.2 → descriptron_vision-2.0.4}/src/descriptron_vision/tools/torchvision_det/tv_coco_dataset.py +0 -0
- {descriptron_vision-2.0.2 → descriptron_vision-2.0.4}/src/descriptron_vision/tools/torchvision_det/tv_predict_v1.py +0 -0
- {descriptron_vision-2.0.2 → descriptron_vision-2.0.4}/src/descriptron_vision/tools/torchvision_det/tv_sweep_v1.py +0 -0
- {descriptron_vision-2.0.2 → descriptron_vision-2.0.4}/src/descriptron_vision/tools/torchvision_det/tv_train_v1.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: descriptron-vision
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.4
|
|
4
4
|
Summary: Mask and landmark prediction for Descriptron: torchvision detectors, SAM2-PAL propagation, DINOv3 landmark transfer
|
|
5
5
|
Project-URL: Homepage, https://github.com/alexrvandam/Descriptron
|
|
6
6
|
Author: Alex R. Van Dam
|
|
@@ -13,7 +13,7 @@ Classifier: Operating System :: OS Independent
|
|
|
13
13
|
Classifier: Programming Language :: Python :: 3
|
|
14
14
|
Classifier: Topic :: Scientific/Engineering :: Image Recognition
|
|
15
15
|
Requires-Python: >=3.9
|
|
16
|
-
Requires-Dist: descriptron-core==2.0.
|
|
16
|
+
Requires-Dist: descriptron-core==2.0.4
|
|
17
17
|
Requires-Dist: einops>=0.7
|
|
18
18
|
Requires-Dist: timm>=0.9
|
|
19
19
|
Requires-Dist: torch>=2.3
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "descriptron-vision"
|
|
7
|
+
version = "2.0.4"
|
|
8
|
+
description = "Mask and landmark prediction for Descriptron: torchvision detectors, SAM2-PAL propagation, DINOv3 landmark transfer"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
license = { text = "Apache-2.0" }
|
|
12
|
+
authors = [{ name = "Alex R. Van Dam" }]
|
|
13
|
+
classifiers = [
|
|
14
|
+
"Programming Language :: Python :: 3",
|
|
15
|
+
"Topic :: Scientific/Engineering :: Image Recognition",
|
|
16
|
+
"Intended Audience :: Science/Research",
|
|
17
|
+
"License :: OSI Approved :: Apache Software License",
|
|
18
|
+
"Operating System :: OS Independent",
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
# torch and torchvision are ordinary wheels on Linux, macOS and Windows, so this
|
|
22
|
+
# installs without a compiler. On a Mac the torchvision detectors can use the
|
|
23
|
+
# Apple GPU through PyTorch's MPS device; Detectron2 cannot, which is why the
|
|
24
|
+
# torchvision backend exists.
|
|
25
|
+
#
|
|
26
|
+
# For CUDA, install torch from PyTorch's own index FIRST, then this package:
|
|
27
|
+
# pip install torch torchvision --index-url https://download.pytorch.org/whl/cu121
|
|
28
|
+
dependencies = [
|
|
29
|
+
"descriptron-core==2.0.4",
|
|
30
|
+
# Capped deliberately. Unbounded, pip installs torch 2.14 with ~3 GB of CUDA 13
|
|
31
|
+
# wheels — not the torch 2.4.0+cu121 this system was validated on, and a version
|
|
32
|
+
# in which the AMP calls below are already deprecated. Raise the cap only after
|
|
33
|
+
# testing, not before.
|
|
34
|
+
"torch>=2.3", # torch.amp.GradScaler("cuda") in tv_train_v1 needs 2.3
|
|
35
|
+
"torchvision>=0.18",
|
|
36
|
+
"transformers>=4.38", # Florence-2 scale-bar reading
|
|
37
|
+
"timm>=0.9",
|
|
38
|
+
"einops>=0.7",
|
|
39
|
+
"tqdm>=4.65",
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
[project.optional-dependencies]
|
|
43
|
+
# SAM2 is not on PyPI and PyPI rejects a git dependency in package metadata, so
|
|
44
|
+
# it cannot be declared here. It is imported lazily and the error message gives
|
|
45
|
+
# the install command:
|
|
46
|
+
# pip install git+https://github.com/facebookresearch/segment-anything-2
|
|
47
|
+
# Detectron2 is the same story, and additionally needs a compiler; see the
|
|
48
|
+
# Docker image if you would rather not build it.
|
|
49
|
+
ocr = ["easyocr>=1.7"]
|
|
50
|
+
dev = ["pytest>=7", "build>=1", "twine>=5"]
|
|
51
|
+
|
|
52
|
+
[project.scripts]
|
|
53
|
+
descriptron-train = "descriptron_vision.cli:train"
|
|
54
|
+
descriptron-predict = "descriptron_vision.cli:predict"
|
|
55
|
+
descriptron-sweep = "descriptron_vision.cli:sweep"
|
|
56
|
+
descriptron-sam2pal = "descriptron_vision.cli:sam2_pal"
|
|
57
|
+
descriptron-dinoland = "descriptron_vision.cli:dinoland"
|
|
58
|
+
descriptron-measure = "descriptron_vision.cli:measure"
|
|
59
|
+
|
|
60
|
+
[project.urls]
|
|
61
|
+
Homepage = "https://github.com/alexrvandam/Descriptron"
|
|
62
|
+
|
|
63
|
+
[tool.hatch.build.targets.wheel]
|
|
64
|
+
packages = ["src/descriptron_vision"]
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "descriptron-vision"
|
|
7
|
+
version = "2.0.3"
|
|
8
|
+
description = "Mask and landmark prediction for Descriptron: torchvision detectors, SAM2-PAL propagation, DINOv3 landmark transfer"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
license = { text = "Apache-2.0" }
|
|
12
|
+
authors = [{ name = "Alex R. Van Dam" }]
|
|
13
|
+
classifiers = [
|
|
14
|
+
"Programming Language :: Python :: 3",
|
|
15
|
+
"Topic :: Scientific/Engineering :: Image Recognition",
|
|
16
|
+
"Intended Audience :: Science/Research",
|
|
17
|
+
"License :: OSI Approved :: Apache Software License",
|
|
18
|
+
"Operating System :: OS Independent",
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
# torch and torchvision are ordinary wheels on Linux, macOS and Windows, so this
|
|
22
|
+
# installs without a compiler. On a Mac the torchvision detectors can use the
|
|
23
|
+
# Apple GPU through PyTorch's MPS device; Detectron2 cannot, which is why the
|
|
24
|
+
# torchvision backend exists.
|
|
25
|
+
#
|
|
26
|
+
# For CUDA, install torch from PyTorch's own index FIRST, then this package:
|
|
27
|
+
# pip install torch torchvision --index-url https://download.pytorch.org/whl/cu121
|
|
28
|
+
dependencies = [
|
|
29
|
+
"descriptron-core==2.0.3",
|
|
30
|
+
# Capped deliberately. Unbounded, pip installs torch 2.14 with ~3 GB of CUDA 13
|
|
31
|
+
# wheels — not the torch 2.4.0+cu121 this system was validated on, and a version
|
|
32
|
+
# in which the AMP calls below are already deprecated. Raise the cap only after
|
|
33
|
+
# testing, not before.
|
|
34
|
+
"torch>=2.3", # torch.amp.GradScaler("cuda") in tv_train_v1 needs 2.3
|
|
35
|
+
"torchvision>=0.18",
|
|
36
|
+
"transformers>=4.38", # Florence-2 scale-bar reading
|
|
37
|
+
"timm>=0.9",
|
|
38
|
+
"einops>=0.7",
|
|
39
|
+
"tqdm>=4.65",
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
[project.optional-dependencies]
|
|
43
|
+
# SAM2 is not on PyPI and PyPI rejects a git dependency in package metadata, so
|
|
44
|
+
# it cannot be declared here. It is imported lazily and the error message gives
|
|
45
|
+
# the install command:
|
|
46
|
+
# pip install git+https://github.com/facebookresearch/segment-anything-2
|
|
47
|
+
# Detectron2 is the same story, and additionally needs a compiler; see the
|
|
48
|
+
# Docker image if you would rather not build it.
|
|
49
|
+
ocr = ["easyocr>=1.7"]
|
|
50
|
+
dev = ["pytest>=7", "build>=1", "twine>=5"]
|
|
51
|
+
|
|
52
|
+
[project.scripts]
|
|
53
|
+
descriptron-train = "descriptron_vision.cli:train"
|
|
54
|
+
descriptron-predict = "descriptron_vision.cli:predict"
|
|
55
|
+
descriptron-sweep = "descriptron_vision.cli:sweep"
|
|
56
|
+
descriptron-sam2pal = "descriptron_vision.cli:sam2_pal"
|
|
57
|
+
descriptron-dinoland = "descriptron_vision.cli:dinoland"
|
|
58
|
+
descriptron-measure = "descriptron_vision.cli:measure"
|
|
59
|
+
|
|
60
|
+
[project.urls]
|
|
61
|
+
Homepage = "https://github.com/alexrvandam/Descriptron"
|
|
62
|
+
|
|
63
|
+
[tool.hatch.build.targets.wheel]
|
|
64
|
+
packages = ["src/descriptron_vision"]
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"""
|
|
2
|
+
descriptron_vision.cli — detectors, propagation and landmark transfer
|
|
3
|
+
=====================================================================
|
|
4
|
+
|
|
5
|
+
Same dispatch as `descriptron_core.cli`. The two dependencies that are not on
|
|
6
|
+
PyPI — SAM2 and Detectron2 — are not declared anywhere: PyPI rejects a git
|
|
7
|
+
dependency in package metadata. They are imported lazily by the programs that
|
|
8
|
+
need them, and `require()` below turns the resulting ImportError into the exact
|
|
9
|
+
command to run.
|
|
10
|
+
"""
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import sys
|
|
14
|
+
|
|
15
|
+
from descriptron_core.cli import run as _run_core # noqa: F401 (shared behaviour)
|
|
16
|
+
from descriptron_core.cli import data_path # noqa: F401
|
|
17
|
+
|
|
18
|
+
import os
|
|
19
|
+
import runpy
|
|
20
|
+
from importlib.resources import files
|
|
21
|
+
from pathlib import Path
|
|
22
|
+
|
|
23
|
+
INSTALL_HINTS = {
|
|
24
|
+
"sam2": ("SAM2 is not on PyPI, so it cannot be a declared dependency.\n"
|
|
25
|
+
" pip install git+https://github.com/facebookresearch/segment-anything-2"),
|
|
26
|
+
"detectron2": ("Detectron2 is not on PyPI and must be compiled:\n"
|
|
27
|
+
" pip install git+https://github.com/facebookresearch/detectron2\n"
|
|
28
|
+
"It needs a C++/CUDA toolchain and has no macOS wheels. If you would\n"
|
|
29
|
+
"rather not build it, the Docker image carries it ready to run, and the\n"
|
|
30
|
+
"torchvision backend needs none of this."),
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def require(module: str):
|
|
35
|
+
"""Import an optional heavyweight dependency, or explain how to get it."""
|
|
36
|
+
try:
|
|
37
|
+
return __import__(module)
|
|
38
|
+
except ImportError as exc:
|
|
39
|
+
hint = INSTALL_HINTS.get(module, f" pip install {module}")
|
|
40
|
+
raise SystemExit(f"{module} is required for this step but is not installed.\n"
|
|
41
|
+
f"{hint}\n\n(original error: {exc})") from exc
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def tools_dir() -> Path:
|
|
45
|
+
return Path(str(files("descriptron_vision") / "tools"))
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _run(script: str, argv: list[str], subdir: str | None = None) -> None:
|
|
49
|
+
base = tools_dir() / subdir if subdir else tools_dir()
|
|
50
|
+
path = base / script
|
|
51
|
+
if not path.exists():
|
|
52
|
+
raise SystemExit(f"{path} is missing — was sync_sources.py run before building?")
|
|
53
|
+
sys.path.insert(0, str(path.parent))
|
|
54
|
+
sys.path.insert(0, str(tools_dir()))
|
|
55
|
+
os.environ.setdefault("DESCRIPTRON_DATA", str(data_path()))
|
|
56
|
+
sys.argv = [str(path), *argv]
|
|
57
|
+
runpy.run_path(str(path), run_name="__main__")
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def train(): _run("tv_train_v1.py", sys.argv[1:], "torchvision_det")
|
|
61
|
+
def predict(): _run("tv_predict_v1.py", sys.argv[1:], "torchvision_det")
|
|
62
|
+
def sweep(): _run("tv_sweep_v1.py", sys.argv[1:], "torchvision_det")
|
|
63
|
+
def sam2_pal(): _run("sam2_pal_batch_v21.py", sys.argv[1:])
|
|
64
|
+
def dinoland(): _run("dinov3_landmark_transfer_v52.py", sys.argv[1:]) # v52 = v51 + --orientation_search
|
|
65
|
+
def measure():
|
|
66
|
+
_run("measurement_script_to_try_after_kpts_prediction_measure_kpts_V35.py", sys.argv[1:])
|
|
@@ -639,6 +639,12 @@ def main():
|
|
|
639
639
|
"padding) or on the 512x512 letterboxed tensor the model saw")
|
|
640
640
|
ap.add_argument("--fig_per_page", type=int, default=12,
|
|
641
641
|
help="panels per preview page; 170 targets in one figure is unusable")
|
|
642
|
+
ap.add_argument("--mirror_refs", action="store_true",
|
|
643
|
+
help="mirror-aware transfer: add a left-right flipped copy of every reference "
|
|
644
|
+
"(image flipped, landmark x mirrored, landmark numbers kept) and, per target, "
|
|
645
|
+
"keep only the handedness group whose references match best (share of "
|
|
646
|
+
"landmarks accepted). Needed when some specimens are photographed as mirror "
|
|
647
|
+
"images; the two handednesses are never mixed in one vote.")
|
|
642
648
|
ap.add_argument("--emit_refs", action="store_true",
|
|
643
649
|
help="also write one COCO file per target under <outdir>/per_image/ and "
|
|
644
650
|
"list the fully-confirmed ones in refs_confirmed.txt, so a checked "
|
|
@@ -688,6 +694,26 @@ def main():
|
|
|
688
694
|
print(f"reference {fname}: {len(present)}/{len(ids)} landmarks present")
|
|
689
695
|
print(f"{len(refs)} reference(s); landmark ids {slot_ids}")
|
|
690
696
|
|
|
697
|
+
# ---- mirror-aware mode: handedness of each reference, plus flipped copies ----
|
|
698
|
+
# Handedness = sign of the signed area (shoelace) of the landmarks in their fixed
|
|
699
|
+
# numbering order: a mirror image reverses it. A flipped reference keeps its landmark
|
|
700
|
+
# numbers, so its votes stay in the same slots.
|
|
701
|
+
def _handedness(pts):
|
|
702
|
+
x, y = pts[:, 0], pts[:, 1]
|
|
703
|
+
return 1 if 0.5 * np.sum(x * np.roll(y, -1) - np.roll(x, -1) * y) >= 0 else -1
|
|
704
|
+
ref_hand = [_handedness(r[3]) for r in refs]
|
|
705
|
+
if a.mirror_refs:
|
|
706
|
+
for (rname, _fa, mk, pts, present, im), hnd in list(zip(refs, ref_hand)):
|
|
707
|
+
im_f = im.transpose(Image.FLIP_LEFT_RIGHT)
|
|
708
|
+
pts_f = pts.copy()
|
|
709
|
+
pts_f[:, 0] = bb.img_dim - pts_f[:, 0]
|
|
710
|
+
refs.append((rname + " [flipped]", bb.embed(im_f, LAYER, "none", a.facet),
|
|
711
|
+
mk[:, ::-1].copy(), pts_f, present, im_f))
|
|
712
|
+
ref_hand.append(-hnd)
|
|
713
|
+
print(f"mirror-aware: {len(refs)} references "
|
|
714
|
+
f"({ref_hand.count(1)} one handedness, {ref_hand.count(-1)} the other)")
|
|
715
|
+
two_hands = a.mirror_refs and len(set(ref_hand)) == 2
|
|
716
|
+
|
|
691
717
|
targets = sorted(_glob.glob(a.batch_glob))
|
|
692
718
|
ref_names = {os.path.basename(r[0]).replace('_keypoints.json', '') for r in refs}
|
|
693
719
|
targets = [t for t in targets if os.path.abspath(t) != os.path.abspath(a.imgA)]
|
|
@@ -701,16 +727,24 @@ def main():
|
|
|
701
727
|
bb, rawB, LAYER, tp, a.mask_dir)
|
|
702
728
|
fb = bb.embed(imgB, LAYER, "none", a.facet)
|
|
703
729
|
|
|
704
|
-
|
|
705
|
-
for rname, fa, maskA_r, pts, present, _im in refs:
|
|
730
|
+
group_votes, group_score = {}, {}
|
|
731
|
+
for gi, ((rname, fa, maskA_r, pts, present, _im), hnd) in enumerate(zip(refs, ref_hand)):
|
|
732
|
+
g = hnd if two_hands else 0
|
|
733
|
+
votes = group_votes.setdefault(g, {k: [] for k in range(len(slot_ids))})
|
|
706
734
|
A = None
|
|
707
735
|
if a.align == "feature":
|
|
708
736
|
A = feature_affine(fa, fb, maskA_r, maskB, bb.patch, bb.grid)
|
|
709
737
|
if A is None:
|
|
710
738
|
A = pca_affine(maskA_r, maskB)
|
|
711
739
|
r = transfer(fa, fb, pts, A, bb.patch, bb.grid, radius_patches=a.radius)
|
|
740
|
+
group_score.setdefault(g, []).append(float(np.mean(r.accepted)))
|
|
712
741
|
for j, slot in enumerate(present):
|
|
713
742
|
votes[slot].append((r.pred[j], bool(r.accepted[j])))
|
|
743
|
+
# RANSAC fits rotation/scale/shift only, never a reflection, so references of the
|
|
744
|
+
# wrong handedness get most landmarks rejected: the higher mean accepted share wins
|
|
745
|
+
chosen = max(group_votes, key=lambda g: np.mean(group_score[g]))
|
|
746
|
+
votes = group_votes[chosen]
|
|
747
|
+
hand_used = chosen if two_hands else None
|
|
714
748
|
|
|
715
749
|
# ---- consensus: median of the votes, spread = agreement ------------
|
|
716
750
|
cons = {}
|
|
@@ -726,7 +760,7 @@ def main():
|
|
|
726
760
|
|
|
727
761
|
results.append((os.path.basename(tp).rsplit('.', 1)[0], imgB, maskB,
|
|
728
762
|
cons, (W0, H0), os.path.basename(tp),
|
|
729
|
-
(tscale, tox, toy, tcx, tcy), tp))
|
|
763
|
+
(tscale, tox, toy, tcx, tcy), tp, hand_used))
|
|
730
764
|
agree = sum(1 for k in cons if cons[k][0] is not None and cons[k][1] > 0)
|
|
731
765
|
print(f"{os.path.basename(tp)[:44]:<46} confirmed {agree}/{len(slot_ids)}")
|
|
732
766
|
|
|
@@ -745,7 +779,7 @@ def main():
|
|
|
745
779
|
os.makedirs(per_image_dir, exist_ok=True)
|
|
746
780
|
confirmed_refs = []
|
|
747
781
|
anns_by_name = {} # name -> annotation, for the preview
|
|
748
|
-
for nm, imgB, mkB, cons, (W0, H0), fname, (tscale, tox, toy, tcx, tcy), tp in results:
|
|
782
|
+
for nm, imgB, mkB, cons, (W0, H0), fname, (tscale, tox, toy, tcx, tcy), tp, hand_used in results:
|
|
749
783
|
kp, nvis, detail = [], 0, []
|
|
750
784
|
xs, ys = [], []
|
|
751
785
|
for k, sid in enumerate(slot_ids):
|
|
@@ -770,6 +804,7 @@ def main():
|
|
|
770
804
|
"bbox": bbox, "iscrowd": 0,
|
|
771
805
|
"point_order": slot_ids,
|
|
772
806
|
"n_references": len(refs),
|
|
807
|
+
"handedness_used": hand_used,
|
|
773
808
|
"landmark_detail": detail}
|
|
774
809
|
coco["images"].append(img_entry)
|
|
775
810
|
coco["annotations"].append(ann)
|
|
@@ -807,7 +842,7 @@ def main():
|
|
|
807
842
|
n = len(page); cols = min(3, n); rows = int(np.ceil(n / cols))
|
|
808
843
|
fig, axes = plt.subplots(rows, cols, figsize=(4.0 * cols, 4.2 * rows), dpi=150)
|
|
809
844
|
axes = np.atleast_1d(axes).ravel()
|
|
810
|
-
for ax_, (nm, imgB, mkB, cons, (W0, H0), _fn, _lb, tp) in zip(axes, page):
|
|
845
|
+
for ax_, (nm, imgB, mkB, cons, (W0, H0), _fn, _lb, tp, _hand) in zip(axes, page):
|
|
811
846
|
if a.preview == "original":
|
|
812
847
|
ax_.imshow(Image.open(tp).convert("RGB"))
|
|
813
848
|
kpx = anns_by_name[nm]["keypoints"]
|