machinevision-toolbox-python 2.3.0__py3-none-any.whl → 2.4.1__py3-none-any.whl

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.
@@ -8,6 +8,20 @@ import numpy as np
8
8
 
9
9
  from machinevisiontoolbox.mvtb_types import Dtype
10
10
 
11
+ #: Short-name aliases for dtype strings accepted throughout the Toolbox
12
+ #: (e.g. Image(..., dtype='float'), VideoFile(..., dtype='float')).
13
+ #: NumPy's own np.dtype('float') resolves to float64, not float32 -- these
14
+ #: aliases exist specifically so 'float'/'int' mean the Toolbox's own
15
+ #: default single-precision/8-bit types, not NumPy's C-double/C-long
16
+ #: defaults. Single source of truth: resolve any dtype string through this
17
+ #: table (dtype_alias.get(dtype, dtype)) before passing it to np.dtype().
18
+ DTYPE_ALIASES: dict[str, str] = {
19
+ "int": "uint8",
20
+ "float": "float32",
21
+ "double": "float64",
22
+ "half": "float16",
23
+ }
24
+
11
25
 
12
26
  def int_image(
13
27
  image: np.ndarray, intclass: Dtype = "uint8", maxintval: int | None = None
@@ -391,7 +391,8 @@ def main():
391
391
  code = [
392
392
  f"%matplotlib{' '+args.backend if args.backend is not None else ''}",
393
393
  "import matplotlib.pyplot as plt",
394
- "_precision = %precision %.3g;",
394
+ "_prec = get_ipython().run_line_magic('precision', '%.3g'); "
395
+ "print(f'Default numeric formatting: {_prec}')",
395
396
  ]
396
397
  if args.base:
397
398
  code.append("from machinevisiontoolbox.base import *")
@@ -0,0 +1,28 @@
1
+ # Block icons
2
+
3
+ Icons for the bdsim blocks in this toolbox. They follow the conventions of
4
+ [bdsim's `Icons` folder](https://github.com/petercorke/bdsim/blob/main/src/bdsim/blocks/Icons/README.md):
5
+ 250x250 RGBA PNG, black "ink" on a transparent background, named as the
6
+ lower-case version of the block's class name.
7
+
8
+ | Icon | How it is made |
9
+ |---|---|
10
+ | `visjac_p.png`, `estpose_p.png` | TeX math, rendered by `make_icons.py` (below). The files in git were made with `bdtex2icon` from bdsim, which needs LaTeX; `make_icons.py` makes the same glyphs, with slightly different size and placement. |
11
+ | `camera.png`, `imageplane.png` | not made by `make_icons.py` |
12
+
13
+ ## Creating icons from TeX
14
+
15
+ ```
16
+ python make_icons.py # all icons in the table at the top of the script
17
+ python make_icons.py visjac_p # one icon
18
+ python make_icons.py --outdir /tmp # write somewhere else, e.g. to compare
19
+ ```
20
+
21
+ This needs only matplotlib and Pillow, which this toolbox already depends on,
22
+ and no LaTeX installation. To add an icon, add a line to the `ICONS` table in
23
+ `make_icons.py`. The math is rendered by matplotlib's `mathtext`, which
24
+ implements a subset of TeX and does not know the custom macros of the RVC
25
+ notation file (`\mat`, `\pose`, ...), so write those expanded, for example
26
+ `\mathbf{J}_p` rather than `\mat{J}_p`.
27
+
28
+ Running the script overwrites the icons of the same name in this folder.
@@ -0,0 +1,111 @@
1
+ #!/usr/bin/env python
2
+ """
3
+ Create block icons from TeX math, using only matplotlib and Pillow.
4
+
5
+ Run it from anywhere to (re)create the icons listed in :data:`ICONS`::
6
+
7
+ $ python make_icons.py # all icons, written next to this script
8
+ $ python make_icons.py visjac_p # just one
9
+ $ python make_icons.py --outdir /tmp/icons # somewhere else
10
+
11
+ Each icon is a 250x250 RGBA PNG: black "ink" on a transparent background, the
12
+ size and style used by the bdsim block icons (see the README in this folder).
13
+
14
+ The math is rendered by matplotlib's ``mathtext`` engine with the Computer
15
+ Modern font set, so no LaTeX installation is needed. ``mathtext`` implements a
16
+ subset of TeX, and not the custom macros (``\\mat``, ``\\pose``, ...) from the
17
+ ``rvc-notation`` file that ``bdtex2icon`` (part of bdsim) uses, so the entries
18
+ in :data:`ICONS` are written with those macros expanded.
19
+ """
20
+
21
+ import argparse
22
+ import io
23
+ from pathlib import Path
24
+
25
+ import matplotlib
26
+
27
+ matplotlib.use("Agg")
28
+
29
+ import numpy as np
30
+ from matplotlib import mathtext, rc_context
31
+ from PIL import Image
32
+
33
+ IMSIZE = 250
34
+
35
+ #: icon file stem -> TeX math (without the surrounding ``$``)
36
+ ICONS: dict[str, str] = {
37
+ "visjac_p": r"\mathbf{J}_p",
38
+ "estpose_p": r"\xi(p, P)",
39
+ }
40
+
41
+
42
+ def tex_icon(
43
+ tex: str, path: Path, size: int = IMSIZE, margin: float = 0.08, dpi: int = 600
44
+ ) -> None:
45
+ """Render TeX math to a square, transparent, black-ink PNG icon.
46
+
47
+ :param tex: TeX math expression, without the surrounding ``$``
48
+ :param path: output file
49
+ :param size: width and height of the icon in pixels, defaults to 250
50
+ :param margin: blank border as a fraction of ``size``, on each side,
51
+ defaults to 0.08
52
+ :param dpi: resolution of the intermediate rendering, defaults to 600
53
+
54
+ The expression is rendered, cropped to its ink, scaled to fit inside the
55
+ margin, and centered on a transparent canvas.
56
+ """
57
+ buf = io.BytesIO()
58
+ with rc_context({"mathtext.fontset": "cm"}): # Computer Modern, as LaTeX
59
+ mathtext.math_to_image(f"${tex}$", buf, dpi=dpi, format="png", color="black")
60
+
61
+ # math_to_image saves a figure, so it is black ink on an *opaque white*
62
+ # background. The ink is black, so how dark a pixel is gives its coverage,
63
+ # and that is all the icon needs: black everywhere, with this as the alpha.
64
+ # Working on one channel also avoids mixing colors into anti-aliased edges.
65
+ alpha = 255 - np.asarray(Image.open(buf).convert("L"))
66
+ ys, xs = np.nonzero(alpha)
67
+ ink = Image.fromarray(alpha[ys.min() : ys.max() + 1, xs.min() : xs.max() + 1])
68
+
69
+ scale = size * (1 - 2 * margin) / max(ink.size)
70
+ ink = ink.resize(
71
+ (max(1, round(ink.width * scale)), max(1, round(ink.height * scale))),
72
+ Image.LANCZOS,
73
+ )
74
+
75
+ canvas = Image.new("L", (size, size), 0)
76
+ canvas.paste(ink, ((size - ink.width) // 2, (size - ink.height) // 2))
77
+
78
+ icon = Image.new("RGBA", (size, size), (0, 0, 0, 0))
79
+ icon.putalpha(canvas)
80
+ icon.save(path)
81
+
82
+
83
+ def main() -> None:
84
+ parser = argparse.ArgumentParser(description=__doc__.strip().splitlines()[0])
85
+ parser.add_argument(
86
+ "names",
87
+ nargs="*",
88
+ metavar="name",
89
+ help=f"icons to create, one or more of {', '.join(ICONS)}; default is all",
90
+ )
91
+ parser.add_argument(
92
+ "--outdir",
93
+ type=Path,
94
+ default=Path(__file__).resolve().parent,
95
+ help="output directory, defaults to the directory containing this script",
96
+ )
97
+ args = parser.parse_args()
98
+
99
+ unknown = [n for n in args.names if n not in ICONS]
100
+ if unknown:
101
+ parser.error(f"unknown icon(s) {unknown}, choose from {list(ICONS)}")
102
+
103
+ args.outdir.mkdir(parents=True, exist_ok=True)
104
+ for name in args.names or ICONS:
105
+ path = args.outdir / f"{name}.png"
106
+ tex_icon(ICONS[name], path)
107
+ print(f"Written: {path}")
108
+
109
+
110
+ if __name__ == "__main__":
111
+ main()