trekr 0.1.0__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.
trekr-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Koen Freerks
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
trekr-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,102 @@
1
+ Metadata-Version: 2.4
2
+ Name: trekr
3
+ Version: 0.1.0
4
+ Summary: Checks for pose tracking: a window to find and fix label errors in DeepLabCut projects, the GPU against the PyTorch build, whether OpenCV decodes camera video with the right colours, and whether a video file changed.
5
+ Author: Koen Freerks
6
+ License-Expression: MIT
7
+ Classifier: Development Status :: 2 - Pre-Alpha
8
+ Classifier: Environment :: Console
9
+ Classifier: Environment :: X11 Applications :: Qt
10
+ Classifier: Intended Audience :: Science/Research
11
+ Classifier: Operating System :: POSIX :: Linux
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Topic :: Multimedia :: Video
14
+ Classifier: Topic :: Scientific/Engineering
15
+ Requires-Python: >=3.10
16
+ Description-Content-Type: text/markdown
17
+ License-File: LICENSE
18
+ Requires-Dist: numpy>=1.26
19
+ Requires-Dist: opencv-python-headless>=4.8
20
+ Requires-Dist: PySide6>=6.5
21
+ Requires-Dist: pyyaml>=6
22
+ Provides-Extra: test
23
+ Requires-Dist: pytest>=8; extra == "test"
24
+ Dynamic: license-file
25
+
26
+ # trekr
27
+
28
+ An early, small release of **TREKR**, a pose-tracking tool in development. This
29
+ release holds three checks that are useful on their own.
30
+
31
+ ```bash
32
+ pip install trekr
33
+ trekr labels PROJECT # check and fix DeepLabCut labels (a window)
34
+ trekr check
35
+ trekr videocheck VIDEO [--save REF.json | --against REF.json]
36
+ ```
37
+
38
+ ## `trekr labels`: find and fix label errors in a DeepLabCut project
39
+
40
+ ```bash
41
+ trekr labels path/to/my-dlc-project # the folder with config.yaml and labeled-data/
42
+ ```
43
+
44
+ A wrong label -- a leg point on the neighbour, two points swapped, a point dropped in
45
+ the background -- trains a network to be wrong, and among hundreds of labelled animals
46
+ it is hard to find by eye. Such an error is almost always one point while the rest of
47
+ the animal is right. `trekr labels` compares each point with the rest of its animal
48
+ through the distances between them (which do not change when the animal turns, or is
49
+ seen upside down), against the usual distances in your own labels, and lists the
50
+ animals with the point that fits worst first.
51
+
52
+ - **To check:** click an animal: the image opens on it with its worst point selected.
53
+ A score is in units of the labels' own spread: right points score about 1; errors
54
+ stand far above (on a tested project, most animals above about 10 had a wrong point).
55
+ - **Hover** a point to see which keypoint of which animal it is, and its score.
56
+ - **Editing:** drag a point to move it, right-click to remove it; pick a keypoint in
57
+ the list and click to place it; Ctrl+Z / Ctrl+Y. F5 checks again after edits.
58
+ - **Saving** (Ctrl+S) writes the edited points into `CollectedData_<scorer>.csv`;
59
+ every other byte of the file stays as it was, and the original is kept beside it
60
+ (`.orig`). DeepLabCut trains from the `.h5` file, which you make again from the CSV:
61
+
62
+ ```python
63
+ import deeplabcut
64
+ deeplabcut.convertcsv2h5("path/to/my-dlc-project/config.yaml", userfeedback=False)
65
+ ```
66
+
67
+ One kind of animal per project (DeepLabCut's unique bodyparts are kept as they are and
68
+ not checked); single- and multi-animal projects; the labelled frames are the PNG images
69
+ in `labeled-data/`, so no videos are needed. The skeleton is drawn from `config.yaml`.
70
+
71
+ ## `trekr check`: is this computer ready?
72
+
73
+ Every line says **OK**, **NOTE** (works, with a caveat) or **FAIL** (why, and what to
74
+ install); the exit status is 1 when anything fails.
75
+
76
+ - **The GPU against the PyTorch build** (if PyTorch is installed). A PyTorch build
77
+ carries compiled code for some GPU generations only, so a new card -- for example
78
+ the RTX 50 series -- can need a build for a newer CUDA. The check names the GPU's
79
+ architecture and the build's, and says which to install.
80
+ - **The NVIDIA driver against that CUDA version.**
81
+ - **Video decoding.** A tiny stored H.264 clip, encoded the way cameras write video
82
+ (BT.709 colour matrix, TV range), is decoded with the installed OpenCV and compared
83
+ with the frames a correct decoder gives. OpenCV 4.11 and 4.12 apply the wrong colour
84
+ matrix to such video: on the clip, red is off by 5.4 and blue by 2.5 grey levels on
85
+ average, up to 22. Every colour or brightness measured from video decoded that way
86
+ is shifted. OpenCV 4.13 and newer decode it exactly.
87
+
88
+ ## `trekr videocheck`: is a video file still the one you recorded?
89
+
90
+ ```bash
91
+ trekr videocheck run1.mp4 --save run1.fingerprint.json # once, next to your data
92
+ trekr videocheck run1.mp4 --against run1.fingerprint.json # later: the same file? (exit 2 if not)
93
+ ```
94
+
95
+ The fingerprint is the file's size and a SHA-256 of its first and last megabyte (fast
96
+ for any size; nothing is decoded), with the frame count, frame rate and size as OpenCV
97
+ reads them. A re-encode, a cut or a different file under the same name shows up, with
98
+ what changed.
99
+
100
+ ## Licence
101
+
102
+ MIT (see LICENSE).
trekr-0.1.0/README.md ADDED
@@ -0,0 +1,77 @@
1
+ # trekr
2
+
3
+ An early, small release of **TREKR**, a pose-tracking tool in development. This
4
+ release holds three checks that are useful on their own.
5
+
6
+ ```bash
7
+ pip install trekr
8
+ trekr labels PROJECT # check and fix DeepLabCut labels (a window)
9
+ trekr check
10
+ trekr videocheck VIDEO [--save REF.json | --against REF.json]
11
+ ```
12
+
13
+ ## `trekr labels`: find and fix label errors in a DeepLabCut project
14
+
15
+ ```bash
16
+ trekr labels path/to/my-dlc-project # the folder with config.yaml and labeled-data/
17
+ ```
18
+
19
+ A wrong label -- a leg point on the neighbour, two points swapped, a point dropped in
20
+ the background -- trains a network to be wrong, and among hundreds of labelled animals
21
+ it is hard to find by eye. Such an error is almost always one point while the rest of
22
+ the animal is right. `trekr labels` compares each point with the rest of its animal
23
+ through the distances between them (which do not change when the animal turns, or is
24
+ seen upside down), against the usual distances in your own labels, and lists the
25
+ animals with the point that fits worst first.
26
+
27
+ - **To check:** click an animal: the image opens on it with its worst point selected.
28
+ A score is in units of the labels' own spread: right points score about 1; errors
29
+ stand far above (on a tested project, most animals above about 10 had a wrong point).
30
+ - **Hover** a point to see which keypoint of which animal it is, and its score.
31
+ - **Editing:** drag a point to move it, right-click to remove it; pick a keypoint in
32
+ the list and click to place it; Ctrl+Z / Ctrl+Y. F5 checks again after edits.
33
+ - **Saving** (Ctrl+S) writes the edited points into `CollectedData_<scorer>.csv`;
34
+ every other byte of the file stays as it was, and the original is kept beside it
35
+ (`.orig`). DeepLabCut trains from the `.h5` file, which you make again from the CSV:
36
+
37
+ ```python
38
+ import deeplabcut
39
+ deeplabcut.convertcsv2h5("path/to/my-dlc-project/config.yaml", userfeedback=False)
40
+ ```
41
+
42
+ One kind of animal per project (DeepLabCut's unique bodyparts are kept as they are and
43
+ not checked); single- and multi-animal projects; the labelled frames are the PNG images
44
+ in `labeled-data/`, so no videos are needed. The skeleton is drawn from `config.yaml`.
45
+
46
+ ## `trekr check`: is this computer ready?
47
+
48
+ Every line says **OK**, **NOTE** (works, with a caveat) or **FAIL** (why, and what to
49
+ install); the exit status is 1 when anything fails.
50
+
51
+ - **The GPU against the PyTorch build** (if PyTorch is installed). A PyTorch build
52
+ carries compiled code for some GPU generations only, so a new card -- for example
53
+ the RTX 50 series -- can need a build for a newer CUDA. The check names the GPU's
54
+ architecture and the build's, and says which to install.
55
+ - **The NVIDIA driver against that CUDA version.**
56
+ - **Video decoding.** A tiny stored H.264 clip, encoded the way cameras write video
57
+ (BT.709 colour matrix, TV range), is decoded with the installed OpenCV and compared
58
+ with the frames a correct decoder gives. OpenCV 4.11 and 4.12 apply the wrong colour
59
+ matrix to such video: on the clip, red is off by 5.4 and blue by 2.5 grey levels on
60
+ average, up to 22. Every colour or brightness measured from video decoded that way
61
+ is shifted. OpenCV 4.13 and newer decode it exactly.
62
+
63
+ ## `trekr videocheck`: is a video file still the one you recorded?
64
+
65
+ ```bash
66
+ trekr videocheck run1.mp4 --save run1.fingerprint.json # once, next to your data
67
+ trekr videocheck run1.mp4 --against run1.fingerprint.json # later: the same file? (exit 2 if not)
68
+ ```
69
+
70
+ The fingerprint is the file's size and a SHA-256 of its first and last megabyte (fast
71
+ for any size; nothing is decoded), with the frame count, frame rate and size as OpenCV
72
+ reads them. A re-encode, a cut or a different file under the same name shows up, with
73
+ what changed.
74
+
75
+ ## Licence
76
+
77
+ MIT (see LICENSE).
@@ -0,0 +1,36 @@
1
+ [build-system]
2
+ requires = ["setuptools>=77"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "trekr"
7
+ version = "0.1.0"
8
+ description = "Checks for pose tracking: a window to find and fix label errors in DeepLabCut projects, the GPU against the PyTorch build, whether OpenCV decodes camera video with the right colours, and whether a video file changed."
9
+ readme = "README.md"
10
+ license = "MIT"
11
+ license-files = ["LICENSE"]
12
+ authors = [{ name = "Koen Freerks" }]
13
+ requires-python = ">=3.10"
14
+ dependencies = ["numpy>=1.26", "opencv-python-headless>=4.8", "PySide6>=6.5", "pyyaml>=6"]
15
+ classifiers = [
16
+ "Development Status :: 2 - Pre-Alpha",
17
+ "Environment :: Console",
18
+ "Environment :: X11 Applications :: Qt",
19
+ "Intended Audience :: Science/Research",
20
+ "Operating System :: POSIX :: Linux",
21
+ "Programming Language :: Python :: 3",
22
+ "Topic :: Multimedia :: Video",
23
+ "Topic :: Scientific/Engineering",
24
+ ]
25
+
26
+ [project.optional-dependencies]
27
+ test = ["pytest>=8"]
28
+
29
+ [project.scripts]
30
+ trekr = "trekr.cli:main"
31
+
32
+ [tool.setuptools.packages.find]
33
+ where = ["src"]
34
+
35
+ [tool.setuptools.package-data]
36
+ trekr = ["data/*"]
trekr-0.1.0/setup.cfg ADDED
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,3 @@
1
+ """TREKR, early release: checks for a computer, its video files, and pose labels."""
2
+
3
+ __version__ = "0.1.0"
@@ -0,0 +1,5 @@
1
+ """python -m trekr <command>: see cli.py."""
2
+
3
+ from .cli import main
4
+
5
+ raise SystemExit(main())
@@ -0,0 +1,44 @@
1
+ """
2
+ trekr check is this computer ready for GPU video analysis?
3
+ trekr videocheck VIDEO [--save REF.json | --against REF.json]
4
+ a video file's fingerprint; is it still the same?
5
+ trekr labels DLC_PROJECT check and edit DeepLabCut labels (a window)
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import argparse
11
+
12
+ from . import __version__
13
+
14
+
15
+ def main(argv=None):
16
+ ap = argparse.ArgumentParser(prog="trekr", description="TREKR (early release): checks for a computer, its "
17
+ "video files, and pose labels.")
18
+ ap.add_argument("--version", action="version", version=f"trekr {__version__}")
19
+ sub = ap.add_subparsers(dest="command", required=True, metavar="command")
20
+ sub.add_parser("check", help="the GPU against the PyTorch build, the driver, and whether OpenCV decodes camera "
21
+ "video with the right colours")
22
+ v = sub.add_parser("videocheck", help="a video file's fingerprint (size, SHA-256 of both ends, stream facts); "
23
+ "save it, or compare the file with a saved one")
24
+ v.add_argument("video")
25
+ g = v.add_mutually_exclusive_group()
26
+ g.add_argument("--save", metavar="REF.json", help="write the fingerprint to this file")
27
+ g.add_argument("--against", metavar="REF.json", help="compare with a saved fingerprint (exit 2 if changed)")
28
+ lab = sub.add_parser("labels", help="find and fix label errors in a DeepLabCut project: every labelled "
29
+ "animal by how well its points fit together, worst first (a window)")
30
+ lab.add_argument("project", help="a DeepLabCut project folder (with config.yaml and labeled-data/), or one "
31
+ "labeled-data folder")
32
+ args = ap.parse_args(argv)
33
+ if args.command == "labels":
34
+ from .labels.app import run
35
+ return run(args.project)
36
+ if args.command == "check":
37
+ from . import envcheck
38
+ return envcheck.run()
39
+ from . import videocheck
40
+ return videocheck.run(args.video, save=args.save, against=args.against)
41
+
42
+
43
+ if __name__ == "__main__":
44
+ raise SystemExit(main())
Binary file
@@ -0,0 +1,174 @@
1
+ """
2
+ `trekr check`: is this computer ready for GPU video analysis? Every line says
3
+
4
+ OK fine
5
+ NOTE works, with a caveat (what, and by how much)
6
+ FAIL will not work, or not correctly (why, and what to install)
7
+
8
+ 1. Python, NumPy, OpenCV and (if installed) PyTorch: their versions.
9
+ 2. Each GPU against the PyTorch build: a build carries code for some GPU generations
10
+ only, so a new card (e.g. the RTX 50 series) can need a newer build. And the NVIDIA
11
+ driver against the CUDA version the build was made for.
12
+ 3. Video decoding: a stored H.264 clip encoded as cameras write it (BT.709 colours,
13
+ TV range) against the frames a correct decoder gives. OpenCV 4.11 and 4.12 decode
14
+ such video with the wrong colour matrix: on the stored clip red is off by 5.4 and blue
15
+ by 2.5 grey levels on average, up to 22 (measured 2026-10-07; 4.13 and 5.0 decode it
16
+ exactly). That shifts every colour measured from the video.
17
+
18
+ The exit status is 1 when anything fails.
19
+ """
20
+
21
+ from __future__ import annotations
22
+
23
+ import ctypes
24
+ import platform
25
+ import re
26
+ import subprocess
27
+ import sys
28
+ from pathlib import Path
29
+
30
+ import numpy as np
31
+
32
+ DATA = Path(__file__).parent / "data"
33
+ CLIP = DATA / "clip.mp4"
34
+ CLIP_EXPECTED = DATA / "clip.expected.npz"
35
+ SAME_PIXELS = 0.5 # mean grey levels up to which a decoded frame counts as the same
36
+
37
+ OK, NOTE, FAIL = "OK", "NOTE", "FAIL"
38
+
39
+
40
+ def driver():
41
+ """(the NVIDIA driver's version, the newest CUDA it runs), each None if unknown."""
42
+ ver = cuda = None
43
+ try: # Linux: no program to start
44
+ m = re.search(r"Kernel Module(?: for \S+)?\s+(\d+(?:\.\d+)+)", Path("/proc/driver/nvidia/version").read_text())
45
+ ver = m.group(1) if m else None
46
+ except OSError:
47
+ pass
48
+ if ver is None:
49
+ try:
50
+ out = subprocess.run(["nvidia-smi", "--query-gpu=driver_version", "--format=csv,noheader"],
51
+ capture_output=True, text=True, timeout=10)
52
+ ver = (out.stdout.strip().splitlines() or [None])[0]
53
+ except (OSError, subprocess.SubprocessError):
54
+ pass
55
+ try:
56
+ lib = ctypes.CDLL("nvcuda.dll" if sys.platform == "win32" else "libcuda.so.1")
57
+ v = ctypes.c_int()
58
+ if lib.cuDriverGetVersion(ctypes.byref(v)) == 0 and v.value:
59
+ cuda = f"{v.value // 1000}.{v.value % 1000 // 10}"
60
+ except (OSError, AttributeError):
61
+ pass
62
+ return ver, cuda
63
+
64
+
65
+ def arch_support(capability, archs):
66
+ """How a PyTorch build (its arch list, e.g. ["sm_80", "sm_86", "compute_90"]) runs on a
67
+ GPU of this compute capability ((8, 9)): "native" (code for it, or for an earlier
68
+ minor of the same major), "jit" (only PTX of an earlier architecture: compiled when
69
+ first used), or None (it cannot run there)."""
70
+ major, minor = capability
71
+ native = jit = False
72
+ for a in archs:
73
+ m = re.fullmatch(r"(sm|compute)_(\d+)(\d)([a-z]?)", a)
74
+ if not m:
75
+ continue
76
+ kind, big, small, suffix = m.group(1), int(m.group(2)), int(m.group(3)), m.group(4)
77
+ if kind == "sm" and big == major and (small == minor or (small < minor and not suffix)):
78
+ native = True
79
+ elif kind == "compute" and not suffix and (big, small) <= (major, minor):
80
+ jit = True
81
+ return "native" if native else "jit" if jit else None
82
+
83
+
84
+ def _span(archs):
85
+ sm = sorted((a for a in archs if a.startswith("sm_")), key=lambda a: int(re.sub(r"\D", "", a)))
86
+ return f"{sm[0]}-{sm[-1]}" if sm else "no GPU"
87
+
88
+
89
+ def decoding():
90
+ """The stored clip as OpenCV decodes it here, against a correct decoder's frames:
91
+ {"frames" (None: cannot be read), "mean" (per channel, R G B), "max", "opencv", "reference"}."""
92
+ import cv2
93
+ e = np.load(CLIP_EXPECTED)
94
+ out = {"opencv": cv2.__version__, "reference": str(e["opencv"]), "frames": None, "mean": None, "max": None}
95
+ cap = cv2.VideoCapture(str(CLIP))
96
+ frames = []
97
+ try:
98
+ while len(frames) < len(e["frames"]):
99
+ ok, f = cap.read()
100
+ if not ok:
101
+ break
102
+ frames.append(cv2.cvtColor(f, cv2.COLOR_BGR2RGB))
103
+ finally:
104
+ cap.release()
105
+ if len(frames) != len(e["frames"]) or any(f.shape != e["frames"][0].shape for f in frames):
106
+ return out
107
+ d = np.abs(np.stack(frames).astype(int) - e["frames"].astype(int))
108
+ out.update(frames=len(frames), mean=d.mean((0, 1, 2)), max=int(d.max()))
109
+ return out
110
+
111
+
112
+ def run(out=None):
113
+ """Print the check; 0 when nothing fails, else 1."""
114
+ out = out or sys.stdout
115
+ fails = []
116
+
117
+ def say(level, text):
118
+ print(f"{level:5s} {text}", file=out)
119
+ if level == FAIL:
120
+ fails.append(text)
121
+
122
+ import cv2
123
+ say(OK, f"Python {platform.python_version()}, NumPy {np.__version__}, OpenCV {cv2.__version__}")
124
+ try:
125
+ import torch
126
+ except ImportError:
127
+ torch = None
128
+ say(NOTE, "PyTorch is not installed: the GPU check is skipped (pytorch.org, a build for your GPU's CUDA)")
129
+ if torch is not None:
130
+ if not torch.cuda.is_available():
131
+ say(FAIL, f"PyTorch {torch.__version__} sees no GPU it can use (built for "
132
+ f"{'CUDA ' + torch.version.cuda if torch.version.cuda else 'the CPU only'})")
133
+ else:
134
+ archs = torch.cuda.get_arch_list()
135
+ for i in range(torch.cuda.device_count()):
136
+ cap = torch.cuda.get_device_capability(i)
137
+ name = (f"GPU {i}: {torch.cuda.get_device_name(i)} (sm_{cap[0]}{cap[1]}, "
138
+ f"{torch.cuda.get_device_properties(i).total_memory / 2 ** 30:.0f} GB)")
139
+ how = arch_support(cap, archs)
140
+ if how == "native":
141
+ say(OK, f"{name}; PyTorch {torch.__version__} has code for {_span(archs)}")
142
+ elif how == "jit":
143
+ say(NOTE, f"{name}: this PyTorch has code for {_span(archs)} only; it is compiled for this "
144
+ f"GPU when first used (a slow start)")
145
+ else:
146
+ say(FAIL, f"{name}: this PyTorch has code for {_span(archs)} only. Install a PyTorch build "
147
+ f"whose architecture list includes sm_{cap[0]}{cap[1]} (pytorch.org; newer GPUs "
148
+ f"need builds for newer CUDA)")
149
+ ver, dcuda = driver()
150
+ rt = torch.version.cuda
151
+ if ver is None or dcuda is None or rt is None:
152
+ say(NOTE, f"NVIDIA driver {ver or 'unknown'}: the CUDA it runs is unknown")
153
+ else:
154
+ d, r = tuple(map(int, dcuda.split("."))), tuple(map(int, rt.split(".")))
155
+ if d[0] < r[0]:
156
+ say(FAIL, f"NVIDIA driver {ver} runs CUDA up to {dcuda}; this PyTorch is built for CUDA {rt}: "
157
+ f"update the driver, or install a PyTorch build for CUDA {d[0]}")
158
+ else:
159
+ say(OK, f"NVIDIA driver {ver} (CUDA {dcuda}); PyTorch built for CUDA {rt}"
160
+ + (" runs on it by CUDA minor-version compatibility" if d < r else ""))
161
+ dec = decoding()
162
+ if dec["frames"] is None:
163
+ say(FAIL, f"OpenCV {dec['opencv']} cannot decode H.264 video (the stored test clip): install "
164
+ f"opencv-python-headless from PyPI, which brings its own decoder")
165
+ elif dec["mean"].max() <= SAME_PIXELS:
166
+ say(OK, f"OpenCV {dec['opencv']} decodes camera video (H.264, BT.709) with the right colours")
167
+ else:
168
+ m = dec["mean"]
169
+ say(FAIL, f"OpenCV {dec['opencv']} decodes camera video (H.264 tagged BT.709) with other colours than a "
170
+ f"correct decoder (OpenCV {dec['reference']}): R {m[0]:.1f}, G {m[1]:.1f}, B {m[2]:.1f} grey "
171
+ f"levels off on average, up to {dec['max']}. OpenCV 4.11 and 4.12 apply the wrong colour "
172
+ f"matrix; use 4.13 or newer")
173
+ print(f"\nNot ready: {len(fails)} problem(s) above (FAIL)." if fails else "\nReady.", file=out)
174
+ return 1 if fails else 0
@@ -0,0 +1 @@
1
+ """`trekr labels`: find and fix label errors in a DeepLabCut project."""