machinevision-toolbox-python 2.3.0__py3-none-any.whl → 2.4.0__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.
- {machinevision_toolbox_python-2.3.0.dist-info → machinevision_toolbox_python-2.4.0.dist-info}/METADATA +5 -5
- {machinevision_toolbox_python-2.3.0.dist-info → machinevision_toolbox_python-2.4.0.dist-info}/RECORD +21 -21
- machinevisiontoolbox/BagOfWords.py +13 -1
- machinevisiontoolbox/BundleAdjust.py +11 -1
- machinevisiontoolbox/Camera.py +101 -18
- machinevisiontoolbox/ImageConstants.py +27 -6
- machinevisiontoolbox/ImageCore.py +69 -8
- machinevisiontoolbox/ImageIO.py +8 -0
- machinevisiontoolbox/ImagePointFeatures.py +13 -2
- machinevisiontoolbox/ImageWholeFeatures.py +18 -5
- machinevisiontoolbox/Kernel.py +29 -0
- machinevisiontoolbox/PointCloud.py +17 -4
- machinevisiontoolbox/Sources.py +4 -5
- machinevisiontoolbox/base/__init__.py +1 -0
- machinevisiontoolbox/base/color.py +42 -16
- machinevisiontoolbox/base/imageio.py +541 -443
- machinevisiontoolbox/base/types.py +14 -0
- machinevisiontoolbox/bin/mvtbtool.py +2 -1
- {machinevision_toolbox_python-2.3.0.dist-info → machinevision_toolbox_python-2.4.0.dist-info}/WHEEL +0 -0
- {machinevision_toolbox_python-2.3.0.dist-info → machinevision_toolbox_python-2.4.0.dist-info}/entry_points.txt +0 -0
- {machinevision_toolbox_python-2.3.0.dist-info → machinevision_toolbox_python-2.4.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: machinevision-toolbox-python
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.4.0
|
|
4
4
|
Summary: Object-oriented machine vision for Python.
|
|
5
5
|
Project-URL: Homepage, https://github.com/petercorke/machinevision-toolbox-python
|
|
6
6
|
Project-URL: Bug Tracker, https://github.com/pypa/sampleproject/issues
|
|
@@ -23,8 +23,8 @@ Requires-Dist: ansitable
|
|
|
23
23
|
Requires-Dist: matplotlib
|
|
24
24
|
Requires-Dist: mvtb-data
|
|
25
25
|
Requires-Dist: numpy
|
|
26
|
-
Requires-Dist: opencv-contrib-python<
|
|
27
|
-
Requires-Dist: opencv-python<
|
|
26
|
+
Requires-Dist: opencv-contrib-python<6.0.0,>=4.5
|
|
27
|
+
Requires-Dist: opencv-python<6.0.0,>=4.5
|
|
28
28
|
Requires-Dist: pgraph-python
|
|
29
29
|
Requires-Dist: pillow
|
|
30
30
|
Requires-Dist: requests
|
|
@@ -35,7 +35,7 @@ Provides-Extra: all
|
|
|
35
35
|
Requires-Dist: ipympl; extra == 'all'
|
|
36
36
|
Requires-Dist: ipython; extra == 'all'
|
|
37
37
|
Requires-Dist: ipywidgets; extra == 'all'
|
|
38
|
-
Requires-Dist: open3d
|
|
38
|
+
Requires-Dist: open3d; (python_version < '3.13') and extra == 'all'
|
|
39
39
|
Requires-Dist: pygments; extra == 'all'
|
|
40
40
|
Requires-Dist: pytesseract; extra == 'all'
|
|
41
41
|
Requires-Dist: rosbags; extra == 'all'
|
|
@@ -64,7 +64,7 @@ Requires-Dist: ipywidgets; extra == 'jupyter'
|
|
|
64
64
|
Provides-Extra: ocr
|
|
65
65
|
Requires-Dist: pytesseract; extra == 'ocr'
|
|
66
66
|
Provides-Extra: open3d
|
|
67
|
-
Requires-Dist: open3d
|
|
67
|
+
Requires-Dist: open3d; (python_version < '3.13') and extra == 'open3d'
|
|
68
68
|
Provides-Extra: ros
|
|
69
69
|
Requires-Dist: rosbags; extra == 'ros'
|
|
70
70
|
Requires-Dist: roslibpy; extra == 'ros'
|
{machinevision_toolbox_python-2.3.0.dist-info → machinevision_toolbox_python-2.4.0.dist-info}/RECORD
RENAMED
|
@@ -1,25 +1,25 @@
|
|
|
1
|
-
machinevisiontoolbox/BagOfWords.py,sha256=
|
|
2
|
-
machinevisiontoolbox/BundleAdjust.py,sha256=
|
|
3
|
-
machinevisiontoolbox/Camera.py,sha256=
|
|
1
|
+
machinevisiontoolbox/BagOfWords.py,sha256=dfl-9v_CJmXyf2LUQnGUQBw7QWiQSw1qe8jN3fjE_30,19808
|
|
2
|
+
machinevisiontoolbox/BundleAdjust.py,sha256=07i0dxuKiLHthUWsLw0DU6hxvRXpMTZMSbipP9YVsYc,37498
|
|
3
|
+
machinevisiontoolbox/Camera.py,sha256=fHqCIyyF_QchRv-luZaz5nocxkcL_7c-n4rQSAh4qOk,143841
|
|
4
4
|
machinevisiontoolbox/ImageBlobs.py,sha256=TsXaJpqUN1WrPg0zHRmLQvP3wUBxcHHUBt9qDggjVnE,89447
|
|
5
5
|
machinevisiontoolbox/ImageColor.py,sha256=fueFYDNlvqSJPgcKsG5v2wBxaW7mYNa6OA-_rK-UJK0,17577
|
|
6
|
-
machinevisiontoolbox/ImageConstants.py,sha256=
|
|
7
|
-
machinevisiontoolbox/ImageCore.py,sha256=
|
|
6
|
+
machinevisiontoolbox/ImageConstants.py,sha256=TGzo62YR2_CFozena3ReZdTnuRHY_spQlgsMJoDa8RE,39154
|
|
7
|
+
machinevisiontoolbox/ImageCore.py,sha256=pQSJ3x-hmsTWvar0FpkGn4ZHi6fncy9d4We4fOr_ktA,143486
|
|
8
8
|
machinevisiontoolbox/ImageFiducials.py,sha256=wrCevhrkgdHzBMMRoDqADZE9OOD7L6nQCd3UDA_e3rU,26903
|
|
9
|
-
machinevisiontoolbox/ImageIO.py,sha256=
|
|
9
|
+
machinevisiontoolbox/ImageIO.py,sha256=BA-szxEJy6LSeq4DdBF845wmy3Nq2s_YjRko_dteWXA,27434
|
|
10
10
|
machinevisiontoolbox/ImageLineFeatures.py,sha256=Kq0rCzPER2eB9MhEBNn6hAHpGY-G_WPZd657RmuRryA,10855
|
|
11
11
|
machinevisiontoolbox/ImageMorph.py,sha256=WioUD2Jr9Ejnk7Let4Vf3iTFz5DhMkZ7l_ZBCbvgl4U,30237
|
|
12
12
|
machinevisiontoolbox/ImageMultiview.py,sha256=swzNsxX39dijPdPa-a4LFOl7T7liTcQeP7NJf5ODGQU,15605
|
|
13
|
-
machinevisiontoolbox/ImagePointFeatures.py,sha256=
|
|
13
|
+
machinevisiontoolbox/ImagePointFeatures.py,sha256=mf1GN37ZOvZRMhX25WAP9Twrymgk8yn1e7XXHkoq5-k,78807
|
|
14
14
|
machinevisiontoolbox/ImageProcessing.py,sha256=HBR6cbTlgfAdOiqXU8DPSFKZQXWTBIVufCyRaifky7g,51669
|
|
15
15
|
machinevisiontoolbox/ImageRegionFeatures.py,sha256=nZAdKFSpxmMZKwlbF13MOxBIUwXvPvDtIxNJUyp7pvg,16092
|
|
16
16
|
machinevisiontoolbox/ImageReshape.py,sha256=F8eNrJ1to6d5jrIJeT1D2RqX-7EnARrQ03UGUCvljXg,50204
|
|
17
17
|
machinevisiontoolbox/ImageSpatial.py,sha256=nzgScDprv7UQXhKcOps80gKjx4Ve6y498lCRm21sEtw,55622
|
|
18
18
|
machinevisiontoolbox/ImageTensor.py,sha256=ohNbThdsQid2AQbDIHaCoxj00tpRggFWv0TTpIHAJQk,9006
|
|
19
|
-
machinevisiontoolbox/ImageWholeFeatures.py,sha256=
|
|
20
|
-
machinevisiontoolbox/Kernel.py,sha256=
|
|
21
|
-
machinevisiontoolbox/PointCloud.py,sha256=
|
|
22
|
-
machinevisiontoolbox/Sources.py,sha256=
|
|
19
|
+
machinevisiontoolbox/ImageWholeFeatures.py,sha256=eLOZdG2OTyHUjkk6W2Wmp9mYOwqEFvfxQcFmGZgwX1s,63457
|
|
20
|
+
machinevisiontoolbox/Kernel.py,sha256=erTDpPsB38bNHvtBfijjf-9zIW57YM-Bg2ltlFCu6Mg,22607
|
|
21
|
+
machinevisiontoolbox/PointCloud.py,sha256=uwa-Y6aIV3_c6iIrfR60VkiCfPst6cSZntEXPhe2z2s,25819
|
|
22
|
+
machinevisiontoolbox/Sources.py,sha256=Odr3ome26j306JNYdak86RcutWT46chQwnSPsHNTScU,152656
|
|
23
23
|
machinevisiontoolbox/VisualServo.py,sha256=2q_szKZo51McduLUMnN8-O6foLyLAR-o0h10-UDWKPY,60947
|
|
24
24
|
machinevisiontoolbox/__init__.py,sha256=ocn82vw6o7pvUKSfebBwPKyKTiK-jMR8FK3SEtpDFVM,3871
|
|
25
25
|
machinevisiontoolbox/_image_typing.py,sha256=FjdzZpuxEH9nBrj8gb-tkwUZDjpCNH92sQS3meemCVE,7286
|
|
@@ -27,21 +27,21 @@ machinevisiontoolbox/camera_derivatives.py,sha256=ysaP8PvgeBpFf8e37AmYQTW28SiFBK
|
|
|
27
27
|
machinevisiontoolbox/decorators.py,sha256=6wEoLFozK2RFcGb9QrLy2bAw4sjkbm_2q3PpnIS9Rtk,2623
|
|
28
28
|
machinevisiontoolbox/fiducial.py,sha256=syC7iIz95nYBTr_njo4ZzLXOyAdAIqoAZq_wRVhyUHQ,1374
|
|
29
29
|
machinevisiontoolbox/mvtb_types.py,sha256=YD2P9mDXhz75GgC7Cyeer8luBpSHckkrLZ0GDVvGOnE,728
|
|
30
|
-
machinevisiontoolbox/base/__init__.py,sha256=
|
|
31
|
-
machinevisiontoolbox/base/color.py,sha256
|
|
30
|
+
machinevisiontoolbox/base/__init__.py,sha256=RvRL9e_FMqBordxwHDqX44YGN38AD4m4fHfkEXslNK4,1666
|
|
31
|
+
machinevisiontoolbox/base/color.py,sha256=-8bjXwkgfRamNs-jZnFsbqpGKR_dN4rCrLqxrsUYjN8,59492
|
|
32
32
|
machinevisiontoolbox/base/data.py,sha256=PF1HyrYQ-EDR-0heHMG1oDxs63AtQshNWbUvN8GOKsY,6130
|
|
33
33
|
machinevisiontoolbox/base/findpeaks.py,sha256=Ag0UIw2CPVioB356Pvj-_k_wjxMODGpsW7qdkt6UvEs,11251
|
|
34
34
|
machinevisiontoolbox/base/graphics.py,sha256=R8hbf_jK3mvq_dEoATxkjcbxUJvoSMovNbrk3W4BQe8,36974
|
|
35
|
-
machinevisiontoolbox/base/imageio.py,sha256=
|
|
35
|
+
machinevisiontoolbox/base/imageio.py,sha256=uQceV4CDDhXrWMKgB4hhu3DxeJjEOKE2QJ8fatd4-MA,55592
|
|
36
36
|
machinevisiontoolbox/base/meshgrid.py,sha256=Bo2GIlk0tRT2CV5sP5I9i-mZy-6c3BPozLOuOiEVHT0,3162
|
|
37
37
|
machinevisiontoolbox/base/shapes.py,sha256=d-DmB_9iEsHyC6AT6lv2LMHvb-PMqHTXtehqMKk3aRQ,11430
|
|
38
|
-
machinevisiontoolbox/base/types.py,sha256=
|
|
38
|
+
machinevisiontoolbox/base/types.py,sha256=AkeDiJOh1nUmfhcfE-ZS_cISIPYIcSEJxzzWKkvkgTw,5687
|
|
39
39
|
machinevisiontoolbox/base/version.py,sha256=uSKKfk9fOu5XF_21Kzfh3N111U-39mobNwiRyelOqcU,976
|
|
40
40
|
machinevisiontoolbox/bin/_bintools.py,sha256=WwzyOqXEv4T-TB2GMBym4pGU2NACM5ACDCg7exGRl0I,1918
|
|
41
41
|
machinevisiontoolbox/bin/_iminteractive.py,sha256=9fh2QcuO9RFfO9uGA05MePbe6cUtlh-F19z2zbG4pLA,17063
|
|
42
42
|
machinevisiontoolbox/bin/bagtool.py,sha256=b-Ap2AynpXe0FtcydyfkXIgHDaumAkGBJgDZ6lgdZBU,12359
|
|
43
43
|
machinevisiontoolbox/bin/imtool.py,sha256=la9m9LuMnthJzXxjMRWqGY_l7rJUZS5wiVgZsw84SO4,7897
|
|
44
|
-
machinevisiontoolbox/bin/mvtbtool.py,sha256=
|
|
44
|
+
machinevisiontoolbox/bin/mvtbtool.py,sha256=7-fhz11-TIUCTPeDlpCxnpM7rONWcHRBTi80b537-nY,13871
|
|
45
45
|
machinevisiontoolbox/bin/ocrtool.py,sha256=HtkbLnjij40oy83nBtZlbJSFJ2pn7lcQAmOifvCLw0I,4719
|
|
46
46
|
machinevisiontoolbox/bin/tagtool.py,sha256=io-NCIMdMc5zIFYMa6U2ITTv2T-Q8Y0bwh_pB1qZ4IQ,8523
|
|
47
47
|
machinevisiontoolbox/blocks/__init__.py,sha256=EsdfK2WxYLSo0SA5HSmlgsivLT1kDnoH9v-RTBDnWwc,142
|
|
@@ -51,8 +51,8 @@ machinevisiontoolbox/blocks/Icons/camera.png,sha256=CGcTB_llMoucN-rfnP6ZP_7AfE_9
|
|
|
51
51
|
machinevisiontoolbox/blocks/Icons/estpose_p.png,sha256=1XrwL4OkotGWMwKmhnar3tt8cQWUdZiO7PxXD354T-E,4320
|
|
52
52
|
machinevisiontoolbox/blocks/Icons/imageplane.png,sha256=34FYQMzkq6foxvRU8pn0KkcHxpHRvsJRJSYKfNu13Eg,3902
|
|
53
53
|
machinevisiontoolbox/blocks/Icons/visjac_p.png,sha256=8X023l-C9Y5DfAN8wCdWYjSjq6wp1vRzuZ85sUbwxv8,3819
|
|
54
|
-
machinevision_toolbox_python-2.
|
|
55
|
-
machinevision_toolbox_python-2.
|
|
56
|
-
machinevision_toolbox_python-2.
|
|
57
|
-
machinevision_toolbox_python-2.
|
|
58
|
-
machinevision_toolbox_python-2.
|
|
54
|
+
machinevision_toolbox_python-2.4.0.dist-info/METADATA,sha256=LQ2UVCMI5WioCEYdVK_NQnCgjhjI-4JBSChJ2e6cZDE,21791
|
|
55
|
+
machinevision_toolbox_python-2.4.0.dist-info/WHEEL,sha256=zOwg4jB6zX2kU910N-cMawjivD6tO8NEWvE12je1bVk,87
|
|
56
|
+
machinevision_toolbox_python-2.4.0.dist-info/entry_points.txt,sha256=R0sRa7JufKCG1VPkgo5KcL7j7pW4-E0Ch2Og4AqmXg4,310
|
|
57
|
+
machinevision_toolbox_python-2.4.0.dist-info/licenses/LICENSE,sha256=niCzY_CzbIMKuUZlUkGimjrKp0vkLAtcwFmoSuSKrdw,1068
|
|
58
|
+
machinevision_toolbox_python-2.4.0.dist-info/RECORD,,
|
|
@@ -58,6 +58,13 @@ class BagOfWords:
|
|
|
58
58
|
of image features is assigned labels from the ``.centroids`` any with a
|
|
59
59
|
label greater that ``.nstopwords`` is a stop word and can be discarded.
|
|
60
60
|
|
|
61
|
+
.. note:: Removing stop words can leave a remaining word that never
|
|
62
|
+
occurs in any image, giving it an inverse document frequency (idf)
|
|
63
|
+
of ``inf`` (``log(N / 0)``). This is harmless -- every place idf is
|
|
64
|
+
used to compute a word-frequency vector, the corresponding word
|
|
65
|
+
count is also always zero there, so the ``0 * inf`` result is
|
|
66
|
+
``nan`` and gets replaced with 0 before the vector is returned.
|
|
67
|
+
|
|
61
68
|
:reference:
|
|
62
69
|
- Video Google: a text retrieval approach to object matching in videos
|
|
63
70
|
J.Sivic and A.Zisserman,
|
|
@@ -139,7 +146,12 @@ class BagOfWords:
|
|
|
139
146
|
# total number of occurrences of word i
|
|
140
147
|
# multiple occurrences in the one image count only as one
|
|
141
148
|
ni = (W > 0).sum(axis=1)
|
|
142
|
-
|
|
149
|
+
# a word that never occurs in any image (ni=0, possible after
|
|
150
|
+
# stopword removal) gives idf=inf here; harmless since nid is
|
|
151
|
+
# always 0 at that same position below, so 0*inf->nan, already
|
|
152
|
+
# suppressed by the invalid="ignore" there.
|
|
153
|
+
with np.errstate(divide="ignore"):
|
|
154
|
+
idf = np.log(N / ni)
|
|
143
155
|
|
|
144
156
|
M = []
|
|
145
157
|
|
|
@@ -405,7 +405,8 @@ if pgraph_installed:
|
|
|
405
405
|
|
|
406
406
|
:seealso: :meth:`add_view` :meth:`add_landmark`
|
|
407
407
|
"""
|
|
408
|
-
|
|
408
|
+
if len(uv) != 2:
|
|
409
|
+
raise ValueError("uv must be a 2-vector")
|
|
409
410
|
|
|
410
411
|
edge = Observation(viewpoint, landmark, uv.flatten()) # create edge object
|
|
411
412
|
e = viewpoint.connect(landmark, edge=edge) # connect nodes with it
|
|
@@ -562,6 +563,8 @@ if pgraph_installed:
|
|
|
562
563
|
:type iterations: int, optional
|
|
563
564
|
:param verbose: show Levenberg-Marquadt status, defaults to False
|
|
564
565
|
:type verbose: bool, optional
|
|
566
|
+
:raises ValueError: no landmark projections have been added to
|
|
567
|
+
the graph
|
|
565
568
|
:return: optimized state vector
|
|
566
569
|
:rtype: ndarray(N)
|
|
567
570
|
|
|
@@ -578,6 +581,13 @@ if pgraph_installed:
|
|
|
578
581
|
|
|
579
582
|
self.update_index()
|
|
580
583
|
|
|
584
|
+
if self.g.ne == 0:
|
|
585
|
+
raise ValueError(
|
|
586
|
+
"no landmark projections in the graph -- nothing to "
|
|
587
|
+
"optimize (add_projection() was never called, or every "
|
|
588
|
+
"candidate landmark was discarded)"
|
|
589
|
+
)
|
|
590
|
+
|
|
581
591
|
if x is None:
|
|
582
592
|
x = self.getstate()
|
|
583
593
|
x0 = x
|
machinevisiontoolbox/Camera.py
CHANGED
|
@@ -350,7 +350,8 @@ class CameraBase(ABC):
|
|
|
350
350
|
|
|
351
351
|
:seealso: :meth:`nv` :meth:`width` :meth:`imagesize`
|
|
352
352
|
"""
|
|
353
|
-
|
|
353
|
+
if self._imagesize is None:
|
|
354
|
+
raise ValueError("imagesize not set")
|
|
354
355
|
return self._imagesize[0]
|
|
355
356
|
|
|
356
357
|
@property
|
|
@@ -370,7 +371,8 @@ class CameraBase(ABC):
|
|
|
370
371
|
|
|
371
372
|
:seealso: :meth:`nu` :meth:`height` :meth:`imagesize`
|
|
372
373
|
"""
|
|
373
|
-
|
|
374
|
+
if self._imagesize is None:
|
|
375
|
+
raise ValueError("imagesize not set")
|
|
374
376
|
return self._imagesize[1]
|
|
375
377
|
|
|
376
378
|
@property
|
|
@@ -390,7 +392,8 @@ class CameraBase(ABC):
|
|
|
390
392
|
|
|
391
393
|
:seealso: :meth:`nu` :meth:`height`
|
|
392
394
|
"""
|
|
393
|
-
|
|
395
|
+
if self._imagesize is None:
|
|
396
|
+
raise ValueError("imagesize not set")
|
|
394
397
|
return self._imagesize[0]
|
|
395
398
|
|
|
396
399
|
@property
|
|
@@ -410,7 +413,8 @@ class CameraBase(ABC):
|
|
|
410
413
|
|
|
411
414
|
:seealso: :meth:`nv` :meth:`width`
|
|
412
415
|
"""
|
|
413
|
-
|
|
416
|
+
if self._imagesize is None:
|
|
417
|
+
raise ValueError("imagesize not set")
|
|
414
418
|
return self._imagesize[1]
|
|
415
419
|
|
|
416
420
|
@property
|
|
@@ -1152,7 +1156,7 @@ class CameraBase(ABC):
|
|
|
1152
1156
|
"""
|
|
1153
1157
|
Plot 3D camera icon in world view (base method)
|
|
1154
1158
|
|
|
1155
|
-
:param pose: camera pose
|
|
1159
|
+
:param pose: camera pose, required when called unbound (see below)
|
|
1156
1160
|
:type pose: :class:`~spatialmath.pose3d.SE3`
|
|
1157
1161
|
:param scale: scale factor, defaults to 1
|
|
1158
1162
|
:type scale: float
|
|
@@ -1170,6 +1174,8 @@ class CameraBase(ABC):
|
|
|
1170
1174
|
:type projection: str, optional
|
|
1171
1175
|
:param ax: axes to draw in, defaults to current 3D axes
|
|
1172
1176
|
:type ax: :class:`~matplotlib.Axes3D`, optional
|
|
1177
|
+
:raises ValueError: called unbound (eg. ``CentralCamera.plot(...)``)
|
|
1178
|
+
without a ``pose``
|
|
1173
1179
|
:return: axes drawn into
|
|
1174
1180
|
:rtype: :class:`~matplotlib.Axes3D`
|
|
1175
1181
|
|
|
@@ -1177,8 +1183,19 @@ class CameraBase(ABC):
|
|
|
1177
1183
|
plot. Two icons are supported: the traditional frustum, and a
|
|
1178
1184
|
simplistic camera comprising a box and cylinder.
|
|
1179
1185
|
|
|
1180
|
-
|
|
1181
|
-
|
|
1186
|
+
This method can be called two ways:
|
|
1187
|
+
|
|
1188
|
+
- **Bound**, ``camera.plot(...)`` -- draws an icon for ``camera``.
|
|
1189
|
+
``pose`` defaults to the instance's own pose if not given.
|
|
1190
|
+
- **Unbound**, ``CentralCamera.plot(pose=..., ...)`` -- called on
|
|
1191
|
+
the class itself, with no camera instance involved (``self`` is
|
|
1192
|
+
``None``). Useful for drawing icons at a set of poses that don't
|
|
1193
|
+
correspond to any single camera instance -- e.g. plotting the
|
|
1194
|
+
calibration frames returned by :meth:`images2C`. ``pose`` is
|
|
1195
|
+
required in this form.
|
|
1196
|
+
|
|
1197
|
+
.. note:: If ``pose`` is not given, it defaults to the pose of
|
|
1198
|
+
``self`` -- this only applies to the bound form.
|
|
1182
1199
|
"""
|
|
1183
1200
|
|
|
1184
1201
|
# if (fig is None) and (ax is None):
|
|
@@ -1195,6 +1212,11 @@ class CameraBase(ABC):
|
|
|
1195
1212
|
ax = smb.axes_logic(ax, 3, projection=projection)
|
|
1196
1213
|
|
|
1197
1214
|
if pose is None:
|
|
1215
|
+
if self is None:
|
|
1216
|
+
raise ValueError(
|
|
1217
|
+
"pose is required when plot() is called unbound, eg. "
|
|
1218
|
+
"CentralCamera.plot(pose=...)"
|
|
1219
|
+
)
|
|
1198
1220
|
pose = self.pose
|
|
1199
1221
|
|
|
1200
1222
|
# Matplotlib >=3.10 can raise while autoscaling ragged 3D line
|
|
@@ -1216,26 +1238,27 @@ class CameraBase(ABC):
|
|
|
1216
1238
|
try:
|
|
1217
1239
|
# draw camera-like object:
|
|
1218
1240
|
if shape == "frustum":
|
|
1219
|
-
|
|
1241
|
+
CameraBase._plot_frustum(ax, pose, scale, alpha)
|
|
1220
1242
|
|
|
1221
1243
|
elif shape == "camera":
|
|
1222
|
-
|
|
1244
|
+
CameraBase._plot_camera_icon(ax, pose, scale, solid, color, alpha, label)
|
|
1223
1245
|
|
|
1224
1246
|
if frame is True:
|
|
1225
|
-
|
|
1247
|
+
pose.plot(
|
|
1226
1248
|
length=scale * 1.5,
|
|
1227
1249
|
style="line",
|
|
1228
1250
|
color=color,
|
|
1229
1251
|
flo=(0.07, 0, -0.01),
|
|
1230
1252
|
)
|
|
1231
1253
|
elif frame is not False:
|
|
1232
|
-
|
|
1254
|
+
pose.plot(**frame)
|
|
1233
1255
|
finally:
|
|
1234
1256
|
ax.add_collection3d = _orig_add_collection3d
|
|
1235
1257
|
|
|
1236
1258
|
return ax
|
|
1237
1259
|
|
|
1238
|
-
|
|
1260
|
+
@staticmethod
|
|
1261
|
+
def _plot_frustum(ax: Axes, pose: SE3, scale: float, alpha: float) -> None:
|
|
1239
1262
|
"""Draw the ``shape="frustum"`` camera icon into ``ax``."""
|
|
1240
1263
|
# TODO make this kwargs or optional inputs
|
|
1241
1264
|
# side colors:
|
|
@@ -1279,8 +1302,8 @@ class CameraBase(ABC):
|
|
|
1279
1302
|
poly = Poly3DCollection(points, facecolors=["r", "g", "r", "y"], alpha=alpha)
|
|
1280
1303
|
ax.add_collection3d(poly)
|
|
1281
1304
|
|
|
1305
|
+
@staticmethod
|
|
1282
1306
|
def _plot_camera_icon(
|
|
1283
|
-
self,
|
|
1284
1307
|
ax: Axes,
|
|
1285
1308
|
pose: SE3,
|
|
1286
1309
|
scale: float,
|
|
@@ -2684,8 +2707,41 @@ class CentralCamera(CameraBase):
|
|
|
2684
2707
|
:type p2: ndarray(2,N)
|
|
2685
2708
|
:param method: algorithm '7p', '8p' [default], 'ransac', 'lmeds'
|
|
2686
2709
|
:type method: str, optional
|
|
2687
|
-
:param
|
|
2688
|
-
|
|
2710
|
+
:param residual: also return the reprojection residual, defaults to True
|
|
2711
|
+
:type residual: bool, optional
|
|
2712
|
+
:param seed: seed the RNG used by 'ransac'/'lmeds' for repeatable
|
|
2713
|
+
results, defaults to None (not seeded)
|
|
2714
|
+
:type seed: int, optional
|
|
2715
|
+
:param kwargs: passed through to ``cv2.findFundamentalMat``.
|
|
2716
|
+
``ransacReprojThreshold`` (float, defaults to 3.0) applies only
|
|
2717
|
+
when ``method='ransac'`` -- max distance in pixels from a point
|
|
2718
|
+
to its epipolar line for the point to still count as an inlier;
|
|
2719
|
+
``method='lmeds'`` ignores it entirely (verified empirically --
|
|
2720
|
+
identical result regardless of value). ``confidence`` (float in
|
|
2721
|
+
(0,1), defaults to 0.99) and ``maxIters`` (int, defaults to
|
|
2722
|
+
1000) apply to either ``method='ransac'`` or ``method='lmeds'``
|
|
2723
|
+
-- ``method`` only ever selects one algorithm at a time (a
|
|
2724
|
+
combined "ransac+lmeds" method isn't a real OpenCV feature;
|
|
2725
|
+
verified empirically that OR-ing the two method flags together
|
|
2726
|
+
doesn't error, but silently reduces to plain LMedS -- an
|
|
2727
|
+
implementation accident, not a documented hybrid mode).
|
|
2728
|
+
Defaults verified empirically 2026-08-16 (identical inlier
|
|
2729
|
+
masks with/without them explicit, same RNG seed) -- not
|
|
2730
|
+
otherwise documented by the ``cv2`` Python bindings. Same
|
|
2731
|
+
parameter set and defaults on OpenCV 4.10 and 5.0.
|
|
2732
|
+
|
|
2733
|
+
.. warning:: ``maxIters`` only works when ``ransacReprojThreshold``
|
|
2734
|
+
and ``confidence`` are *also* given explicitly -- passing
|
|
2735
|
+
only ``maxIters=`` hits a ``cv2.findFundamentalMat``
|
|
2736
|
+
overload-resolution failure (a confusing low-level OpenCV
|
|
2737
|
+
error, not a clear Python one), since the 3-argument
|
|
2738
|
+
overload this method otherwise uses has no ``maxIters``
|
|
2739
|
+
parameter at all.
|
|
2740
|
+
:type kwargs: dict, optional
|
|
2741
|
+
:raises ValueError: fewer than the minimum number of point
|
|
2742
|
+
correspondences for ``method`` (7 for '7p', 8 otherwise), or
|
|
2743
|
+
the points are too degenerate (eg. coplanar, collinear) for
|
|
2744
|
+
``cv2.findFundamentalMat`` to estimate F from
|
|
2689
2745
|
:return: fundamental matrix and residual
|
|
2690
2746
|
:rtype: ndarray(3,3), float
|
|
2691
2747
|
|
|
@@ -2719,13 +2775,40 @@ class CentralCamera(CameraBase):
|
|
|
2719
2775
|
"ransac": cv2.FM_RANSAC,
|
|
2720
2776
|
"lmeds": cv2.FM_LMEDS,
|
|
2721
2777
|
}
|
|
2778
|
+
min_points = 7 if method == "7p" else 8
|
|
2779
|
+
if p1.shape[1] < min_points:
|
|
2780
|
+
raise ValueError(
|
|
2781
|
+
f"points2F() needs at least {min_points} point correspondences "
|
|
2782
|
+
f"for method={method!r}, got {p1.shape[1]}"
|
|
2783
|
+
)
|
|
2722
2784
|
|
|
2723
2785
|
if seed is not None:
|
|
2724
2786
|
cv2.setRNGSeed(seed)
|
|
2725
2787
|
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2788
|
+
try:
|
|
2789
|
+
F, mask = cv2.findFundamentalMat(
|
|
2790
|
+
points1=p1.T, points2=p2.T, method=points2F_dict[method], **kwargs
|
|
2791
|
+
)
|
|
2792
|
+
except cv2.error as e:
|
|
2793
|
+
# on some degenerate point configurations cv2.findFundamentalMat
|
|
2794
|
+
# raises a raw C++ assertion (eg. a Mat rowRange error) instead
|
|
2795
|
+
# of returning None -- same underlying cause as the F is None
|
|
2796
|
+
# case below, just surfaced differently by OpenCV
|
|
2797
|
+
raise ValueError(
|
|
2798
|
+
f"cv2.findFundamentalMat could not estimate F from "
|
|
2799
|
+
f"{p1.shape[1]} point correspondences using method={method!r} "
|
|
2800
|
+
f"-- points are likely too degenerate (eg. coplanar, collinear): {e}"
|
|
2801
|
+
) from e
|
|
2802
|
+
|
|
2803
|
+
if F is None or mask is None:
|
|
2804
|
+
# already know p1.shape[1] >= min_points (checked above), so a
|
|
2805
|
+
# failure here means the points themselves are degenerate (eg.
|
|
2806
|
+
# coplanar, collinear), not a raw-count problem
|
|
2807
|
+
raise ValueError(
|
|
2808
|
+
f"cv2.findFundamentalMat could not estimate F from "
|
|
2809
|
+
f"{p1.shape[1]} point correspondences using method={method!r} "
|
|
2810
|
+
"-- points are likely too degenerate (eg. coplanar, collinear)"
|
|
2811
|
+
)
|
|
2729
2812
|
|
|
2730
2813
|
mask = mask.ravel().astype(bool)
|
|
2731
2814
|
|
|
@@ -23,7 +23,7 @@ from spatialmath import base as smb
|
|
|
23
23
|
from spatialmath.base import islistof, isscalar
|
|
24
24
|
|
|
25
25
|
# from numpy.lib.arraysetops import isin
|
|
26
|
-
from machinevisiontoolbox.base import float_image, int_image, name2color
|
|
26
|
+
from machinevisiontoolbox.base import DTYPE_ALIASES, float_image, int_image, name2color
|
|
27
27
|
from machinevisiontoolbox.base.imageio import convert, idisp, iread, iwrite
|
|
28
28
|
from machinevisiontoolbox.Kernel import Kernel
|
|
29
29
|
from machinevisiontoolbox.mvtb_types import Dtype
|
|
@@ -110,6 +110,9 @@ def _resolve_pattern_options(
|
|
|
110
110
|
else:
|
|
111
111
|
dtype = default_dtype
|
|
112
112
|
|
|
113
|
+
if isinstance(dtype, str):
|
|
114
|
+
dtype = DTYPE_ALIASES.get(dtype, dtype)
|
|
115
|
+
|
|
113
116
|
if colororder is None and like is not None and like.iscolor:
|
|
114
117
|
colororder = like.colororder_str.replace(":", "")
|
|
115
118
|
|
|
@@ -121,7 +124,7 @@ def _pattern_image(cls, image: np.ndarray, colororder: str | None):
|
|
|
121
124
|
image = np.repeat(
|
|
122
125
|
image[..., np.newaxis], len(cls.colordict(colororder)), axis=2
|
|
123
126
|
)
|
|
124
|
-
return cls(image, colororder=colororder)
|
|
127
|
+
return cls(image, colororder=colororder, dtype=True)
|
|
125
128
|
|
|
126
129
|
|
|
127
130
|
class ImageConstantsMixin(_ImageBase if TYPE_CHECKING else object):
|
|
@@ -145,6 +148,8 @@ class ImageConstantsMixin(_ImageBase if TYPE_CHECKING else object):
|
|
|
145
148
|
:type colororder: str
|
|
146
149
|
:param dtype: NumPy datatype, defaults to 'uint8'
|
|
147
150
|
:type dtype: str or NumPy dtype, optional
|
|
151
|
+
|
|
152
|
+
|dtype_aliases|
|
|
148
153
|
:param like: template image supplying default ``size`` and ``colororder``
|
|
149
154
|
when those are not given explicitly
|
|
150
155
|
:type like: :class:`Image` or None, optional
|
|
@@ -179,7 +184,7 @@ class ImageConstantsMixin(_ImageBase if TYPE_CHECKING else object):
|
|
|
179
184
|
)
|
|
180
185
|
|
|
181
186
|
shape = _getshape(cls, None, None, colororder, size)
|
|
182
|
-
return cls(np.zeros(shape, dtype=dtype), colororder=colororder)
|
|
187
|
+
return cls(np.zeros(shape, dtype=dtype), colororder=colororder, dtype=True)
|
|
183
188
|
|
|
184
189
|
@classmethod
|
|
185
190
|
def Constant(
|
|
@@ -202,6 +207,8 @@ class ImageConstantsMixin(_ImageBase if TYPE_CHECKING else object):
|
|
|
202
207
|
:type colororder: str
|
|
203
208
|
:param dtype: NumPy datatype, defaults to 'uint8'
|
|
204
209
|
:type dtype: str or NumPy dtype, optional
|
|
210
|
+
|
|
211
|
+
|dtype_aliases|
|
|
205
212
|
:param like: template image supplying default ``size``, ``dtype`` and
|
|
206
213
|
``colororder`` when those are not given explicitly
|
|
207
214
|
:type like: :class:`Image` or None, optional
|
|
@@ -258,11 +265,11 @@ class ImageConstantsMixin(_ImageBase if TYPE_CHECKING else object):
|
|
|
258
265
|
planes = []
|
|
259
266
|
for bg in value:
|
|
260
267
|
planes.append(np.full(shape[:2], bg, dtype=dtype))
|
|
261
|
-
return cls(np.stack(planes, axis=2), colororder=colororder)
|
|
268
|
+
return cls(np.stack(planes, axis=2), colororder=colororder, dtype=True)
|
|
262
269
|
|
|
263
270
|
else:
|
|
264
271
|
# scalar
|
|
265
|
-
return cls(np.full(shape, value, dtype=dtype))
|
|
272
|
+
return cls(np.full(shape, value, dtype=dtype), dtype=True)
|
|
266
273
|
|
|
267
274
|
@classmethod
|
|
268
275
|
def String(
|
|
@@ -406,6 +413,8 @@ class ImageConstantsMixin(_ImageBase if TYPE_CHECKING else object):
|
|
|
406
413
|
:type colororder: str
|
|
407
414
|
:param dtype: NumPy datatype, defaults to 'uint8'
|
|
408
415
|
:type dtype: str or NumPy dtype, optional
|
|
416
|
+
|
|
417
|
+
|dtype_aliases|
|
|
409
418
|
:param maxval: maximum value for random values, defaults to None
|
|
410
419
|
:type maxval: same as ``dtype``, optional
|
|
411
420
|
:param pdf: probability density function for pixel values, defaults to None
|
|
@@ -535,7 +544,7 @@ class ImageConstantsMixin(_ImageBase if TYPE_CHECKING else object):
|
|
|
535
544
|
else:
|
|
536
545
|
raise ValueError("pdf must be a 1D or 2D array")
|
|
537
546
|
|
|
538
|
-
return cls(im, colororder=colororder)
|
|
547
|
+
return cls(im, colororder=colororder, dtype=True)
|
|
539
548
|
|
|
540
549
|
@classmethod
|
|
541
550
|
def Squares(
|
|
@@ -562,6 +571,8 @@ class ImageConstantsMixin(_ImageBase if TYPE_CHECKING else object):
|
|
|
562
571
|
:type bg: int, optional
|
|
563
572
|
:param dtype: NumPy datatype, defaults to 'uint8'
|
|
564
573
|
:type dtype: str or NumPy dtype, optional
|
|
574
|
+
|
|
575
|
+
|dtype_aliases|
|
|
565
576
|
:param colororder: color plane names for the output image, defaults to None
|
|
566
577
|
:type colororder: str or None, optional
|
|
567
578
|
:param like: template image supplying default ``size``, ``dtype`` and
|
|
@@ -654,6 +665,8 @@ class ImageConstantsMixin(_ImageBase if TYPE_CHECKING else object):
|
|
|
654
665
|
:type bg: int, optional
|
|
655
666
|
:param dtype: NumPy datatype, defaults to 'uint8'
|
|
656
667
|
:type dtype: str or NumPy dtype, optional
|
|
668
|
+
|
|
669
|
+
|dtype_aliases|
|
|
657
670
|
:param colororder: color plane names for the output image, defaults to None
|
|
658
671
|
:type colororder: str or None, optional
|
|
659
672
|
:param like: template image supplying default ``size``, ``dtype`` and
|
|
@@ -746,6 +759,8 @@ class ImageConstantsMixin(_ImageBase if TYPE_CHECKING else object):
|
|
|
746
759
|
:type cycles: int, optional
|
|
747
760
|
:param dtype: NumPy datatype, defaults to 'float32'
|
|
748
761
|
:type dtype: str or NumPy dtype, optional
|
|
762
|
+
|
|
763
|
+
|dtype_aliases|
|
|
749
764
|
:param colororder: color plane names for the output image, defaults to None
|
|
750
765
|
:type colororder: str or None, optional
|
|
751
766
|
:param like: template image supplying default ``size``, ``dtype`` and
|
|
@@ -842,6 +857,8 @@ class ImageConstantsMixin(_ImageBase if TYPE_CHECKING else object):
|
|
|
842
857
|
:type cycles: int, optional
|
|
843
858
|
:param dtype: NumPy datatype, defaults to 'float32'
|
|
844
859
|
:type dtype: str or NumPy dtype, optional
|
|
860
|
+
|
|
861
|
+
|dtype_aliases|
|
|
845
862
|
:param colororder: color plane names for the output image, defaults to None
|
|
846
863
|
:type colororder: str or None, optional
|
|
847
864
|
:param like: template image supplying default ``size``, ``dtype`` and
|
|
@@ -922,6 +939,8 @@ class ImageConstantsMixin(_ImageBase if TYPE_CHECKING else object):
|
|
|
922
939
|
:type square: int, optional
|
|
923
940
|
:param dtype: image data type, defaults to "uint8"
|
|
924
941
|
:type dtype: str or NumPy dtype, optional
|
|
942
|
+
|
|
943
|
+
|dtype_aliases|
|
|
925
944
|
:param colororder: color plane names for the output image, defaults to None
|
|
926
945
|
:type colororder: str or None, optional
|
|
927
946
|
:param like: template image supplying default ``size``, ``dtype`` and
|
|
@@ -1012,6 +1031,8 @@ class ImageConstantsMixin(_ImageBase if TYPE_CHECKING else object):
|
|
|
1012
1031
|
:type shift: int, optional
|
|
1013
1032
|
:param dtype: image data type, defaults to "uint8"
|
|
1014
1033
|
:type dtype: str or NumPy dtype, optional
|
|
1034
|
+
|
|
1035
|
+
|dtype_aliases|
|
|
1015
1036
|
:param colororder: color plane names for the output image, defaults to None
|
|
1016
1037
|
:type colororder: str or None, optional
|
|
1017
1038
|
:param like: template image supplying default ``size``, ``dtype`` and
|