machinevision-toolbox-python 2.1.0__py3-none-any.whl → 2.3.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.1.0.dist-info → machinevision_toolbox_python-2.3.0.dist-info}/METADATA +14 -10
- {machinevision_toolbox_python-2.1.0.dist-info → machinevision_toolbox_python-2.3.0.dist-info}/RECORD +10 -10
- {machinevision_toolbox_python-2.1.0.dist-info → machinevision_toolbox_python-2.3.0.dist-info}/WHEEL +1 -1
- machinevisiontoolbox/ImageFiducials.py +62 -10
- machinevisiontoolbox/ImageLineFeatures.py +9 -1
- machinevisiontoolbox/ImagePointFeatures.py +21 -2
- machinevisiontoolbox/ImageRegionFeatures.py +16 -0
- machinevisiontoolbox/bin/mvtbtool.py +82 -14
- {machinevision_toolbox_python-2.1.0.dist-info → machinevision_toolbox_python-2.3.0.dist-info}/entry_points.txt +0 -0
- {machinevision_toolbox_python-2.1.0.dist-info → machinevision_toolbox_python-2.3.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: machinevision-toolbox-python
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.3.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
|
|
@@ -99,20 +99,24 @@ Description-Content-Type: text/markdown
|
|
|
99
99
|
|
|
100
100
|
---
|
|
101
101
|
|
|
102
|
-
### Status &
|
|
103
|
-
|
|
104
|
-
[](https://qcr.github.io)
|
|
102
|
+
### Status & Project Health
|
|
103
|
+
|
|
105
104
|
[](https://github.com/petercorke/machinevision-toolbox-python/actions/workflows/ci.yml)
|
|
106
|
-
[](https://pepy.tech/projects/machinevision-toolbox-python)
|
|
107
106
|

|
|
107
|
+
[](https://codecov.io/gh/petercorke/machinevision-toolbox-python)
|
|
108
108
|
[](https://opensource.org/licenses/MIT)
|
|
109
|
-
[](https://pypistats.org/packages/machinevision-toolbox-python)
|
|
110
109
|
|
|
111
|
-
###
|
|
112
|
-
[](https://github.com/petercorke/robotics-toolbox-python)
|
|
112
|
+
[](https://qcr.github.io)
|
|
113
|
+
|
|
114
|
+
[](https://numpy.org)
|
|
115
|
+
[](https://scipy.org)
|
|
116
|
+
[](https://matplotlib.org)
|
|
114
117
|
[](https://opencv.org)
|
|
115
118
|
[](https://open3d.org)
|
|
119
|
+
[](https://github.com/petercorke/spatialmath-python)
|
|
116
120
|
|
|
117
121
|
## Synopsis
|
|
118
122
|
|
{machinevision_toolbox_python-2.1.0.dist-info → machinevision_toolbox_python-2.3.0.dist-info}/RECORD
RENAMED
|
@@ -5,14 +5,14 @@ machinevisiontoolbox/ImageBlobs.py,sha256=TsXaJpqUN1WrPg0zHRmLQvP3wUBxcHHUBt9qDg
|
|
|
5
5
|
machinevisiontoolbox/ImageColor.py,sha256=fueFYDNlvqSJPgcKsG5v2wBxaW7mYNa6OA-_rK-UJK0,17577
|
|
6
6
|
machinevisiontoolbox/ImageConstants.py,sha256=R0sc9z8sbraruI6qkixLso6fTgxUAC5cQMSw4ms9m2k,38774
|
|
7
7
|
machinevisiontoolbox/ImageCore.py,sha256=ufDI7ynOGRwM8TSSDEdM4LMQxBhclc0ve976tsprBW8,140594
|
|
8
|
-
machinevisiontoolbox/ImageFiducials.py,sha256=
|
|
8
|
+
machinevisiontoolbox/ImageFiducials.py,sha256=wrCevhrkgdHzBMMRoDqADZE9OOD7L6nQCd3UDA_e3rU,26903
|
|
9
9
|
machinevisiontoolbox/ImageIO.py,sha256=MFUwjxdQ8oGttGMgzon9xOom-IGsVUFkiEWDrVEEgMs,27019
|
|
10
|
-
machinevisiontoolbox/ImageLineFeatures.py,sha256=
|
|
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=HYgBUs931OOYsM9kx4p_zFU5vAc9dOJsNPuUyTKxoY4,78209
|
|
14
14
|
machinevisiontoolbox/ImageProcessing.py,sha256=HBR6cbTlgfAdOiqXU8DPSFKZQXWTBIVufCyRaifky7g,51669
|
|
15
|
-
machinevisiontoolbox/ImageRegionFeatures.py,sha256=
|
|
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
|
|
@@ -41,7 +41,7 @@ machinevisiontoolbox/bin/_bintools.py,sha256=WwzyOqXEv4T-TB2GMBym4pGU2NACM5ACDCg
|
|
|
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=NuVm2lLCJSUT4_RvlSplqeuUb0duDxAoupL1UIrjjuQ,13785
|
|
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.3.0.dist-info/METADATA,sha256=GIbwk-9OjmGJcOWpGeEDlp1tuH0qTk-9YUtHFLdc9Nw,21733
|
|
55
|
+
machinevision_toolbox_python-2.3.0.dist-info/WHEEL,sha256=zOwg4jB6zX2kU910N-cMawjivD6tO8NEWvE12je1bVk,87
|
|
56
|
+
machinevision_toolbox_python-2.3.0.dist-info/entry_points.txt,sha256=R0sRa7JufKCG1VPkgo5KcL7j7pW4-E0Ch2Og4AqmXg4,310
|
|
57
|
+
machinevision_toolbox_python-2.3.0.dist-info/licenses/LICENSE,sha256=niCzY_CzbIMKuUZlUkGimjrKp0vkLAtcwFmoSuSKrdw,1068
|
|
58
|
+
machinevision_toolbox_python-2.3.0.dist-info/RECORD,,
|
|
@@ -133,20 +133,53 @@ class ImageFiducialsMixin(_ImageBase if TYPE_CHECKING else object):
|
|
|
133
133
|
# corners is a list of marker corners, one element per tag
|
|
134
134
|
# each element is 1x4x2 matrix holding corner coordinates
|
|
135
135
|
#
|
|
136
|
-
#
|
|
136
|
+
# --- OpenCV 4/5 compat -------------------------------------------
|
|
137
|
+
# ids is (N,1) on OpenCV 4 but a flat (N,) on OpenCV 5 (verified
|
|
138
|
+
# empirically 2026-08-03). Flatten so each `id` below is always a
|
|
139
|
+
# plain scalar -- on OpenCV 5, id[0] on an already-0-d element
|
|
140
|
+
# raises IndexError: invalid index to scalar variable.
|
|
141
|
+
# -------------------------------------------------------------------
|
|
137
142
|
|
|
138
143
|
fiducials = []
|
|
139
144
|
if ids is None or len(ids) == 0:
|
|
140
145
|
return fiducials # no markers found
|
|
146
|
+
ids = ids.reshape(-1)
|
|
141
147
|
if K is not None and side is not None:
|
|
142
|
-
|
|
143
|
-
|
|
148
|
+
# --- OpenCV 4/5 compat ---------------------------------------
|
|
149
|
+
# cv2.aruco.estimatePoseSingleMarkers was removed entirely in
|
|
150
|
+
# OpenCV 5 -- not moved/renamed, hasattr(cv2.aruco,
|
|
151
|
+
# "estimatePoseSingleMarkers") is False in a real 5.0.0
|
|
152
|
+
# install. Replaced with the per-marker cv2.solvePnP +
|
|
153
|
+
# SOLVEPNP_IPPE_SQUARE call that OpenCV's own migration notes
|
|
154
|
+
# recommend for planar square markers. The object points use
|
|
155
|
+
# the same convention (clockwise from top-left, Z out of the
|
|
156
|
+
# marker) the old function used internally, so this works
|
|
157
|
+
# unchanged on OpenCV 4 too -- no version branching needed.
|
|
158
|
+
# Verified empirically 2026-08-03.
|
|
159
|
+
# -----------------------------------------------------------------
|
|
160
|
+
half = side / 2
|
|
161
|
+
obj_pts = np.array(
|
|
162
|
+
[
|
|
163
|
+
[-half, half, 0],
|
|
164
|
+
[half, half, 0],
|
|
165
|
+
[half, -half, 0],
|
|
166
|
+
[-half, -half, 0],
|
|
167
|
+
],
|
|
168
|
+
dtype=np.float32,
|
|
144
169
|
)
|
|
145
|
-
|
|
146
|
-
|
|
170
|
+
p3d = obj_pts.reshape(4, 1, 3)
|
|
171
|
+
for id, corners in zip(ids, cornerss):
|
|
172
|
+
_, rvec, tvec = cv2.solvePnP(
|
|
173
|
+
obj_pts, corners[0], K, None, flags=cv2.SOLVEPNP_IPPE_SQUARE
|
|
174
|
+
)
|
|
175
|
+
fiducials.append(
|
|
176
|
+
Fiducial(
|
|
177
|
+
id, corners[0].T, K, rvec.reshape(1, 3), tvec.reshape(1, 3), p3d
|
|
178
|
+
)
|
|
179
|
+
)
|
|
147
180
|
else:
|
|
148
181
|
for id, corners in zip(ids, cornerss):
|
|
149
|
-
fiducials.append(Fiducial(id
|
|
182
|
+
fiducials.append(Fiducial(id, corners[0].T))
|
|
150
183
|
|
|
151
184
|
return fiducials
|
|
152
185
|
|
|
@@ -386,16 +419,23 @@ class FiducialCollection:
|
|
|
386
419
|
cornerss, ids, rejected = detector.detectMarkers(image=image.mono()._A)
|
|
387
420
|
|
|
388
421
|
# corners is a list of ndarray(1,4,2) of marker corners
|
|
389
|
-
# ids is ndarray of shape (N,1) holding the tag ids
|
|
390
422
|
# rejected is a list of rejected corners from tags whose inner code is incorrectly coded
|
|
423
|
+
#
|
|
424
|
+
# --- OpenCV 4/5 compat -------------------------------------------
|
|
425
|
+
# ids is (N,1) on OpenCV 4 but a flat (N,) on OpenCV 5 (verified
|
|
426
|
+
# empirically 2026-08-03). Flatten so each `id` below is always a
|
|
427
|
+
# plain scalar -- on OpenCV 5, id[0] on an already-0-d element
|
|
428
|
+
# raises IndexError: invalid index to scalar variable.
|
|
429
|
+
# -------------------------------------------------------------------
|
|
430
|
+
ids = ids.reshape(-1) if ids is not None else ids
|
|
391
431
|
|
|
392
432
|
# filter tags by ID
|
|
393
433
|
cornerss = [
|
|
394
434
|
corners.T.squeeze()
|
|
395
435
|
for corners, id in zip(cornerss, ids)
|
|
396
|
-
if id
|
|
436
|
+
if id in self._ids
|
|
397
437
|
]
|
|
398
|
-
ids = [id
|
|
438
|
+
ids = [id for corners, id in zip(cornerss, ids) if id in self._ids]
|
|
399
439
|
# ids = np.reshape(ids, (-1, 1))
|
|
400
440
|
|
|
401
441
|
# match the markers to the board
|
|
@@ -587,9 +627,21 @@ class ArUcoBoard(FiducialCollection):
|
|
|
587
627
|
:return: 3D object points and 2D image points
|
|
588
628
|
:rtype: ndarray(M,1,3), ndarray(M,1,2), where M is the number of corners found (multiple of 4)
|
|
589
629
|
"""
|
|
590
|
-
|
|
630
|
+
objPoints, imgPoints = self._board.matchImagePoints(
|
|
591
631
|
[corners.T.reshape(1, 4, 2) for corners in cornerss], np.c_[ids]
|
|
592
632
|
)
|
|
633
|
+
# --- OpenCV 4/5 compat -------------------------------------------
|
|
634
|
+
# cv2.aruco.Board.matchImagePoints returns (M,1,3)/(M,1,2) on
|
|
635
|
+
# OpenCV 4 but a true (M,3)/(M,2) on OpenCV 5 (verified
|
|
636
|
+
# empirically 2026-08-03). Normalize back to the documented
|
|
637
|
+
# (M,1,3)/(M,1,2) shape so this method's return contract is
|
|
638
|
+
# stable regardless of the installed OpenCV version.
|
|
639
|
+
# -------------------------------------------------------------------
|
|
640
|
+
if objPoints.ndim == 2:
|
|
641
|
+
objPoints = objPoints[:, np.newaxis, :]
|
|
642
|
+
if imgPoints.ndim == 2:
|
|
643
|
+
imgPoints = imgPoints[:, np.newaxis, :]
|
|
644
|
+
return objPoints, imgPoints
|
|
593
645
|
|
|
594
646
|
def chart(self, filename: str | None = None, dpi: int = 100) -> Image | None:
|
|
595
647
|
"""Write ArUco chart to a file
|
|
@@ -156,8 +156,16 @@ class HoughFeature:
|
|
|
156
156
|
)
|
|
157
157
|
if lines is None:
|
|
158
158
|
return np.zeros((0, 4))
|
|
159
|
-
|
|
159
|
+
# --- OpenCV 4/5 compat -----------------------------------------
|
|
160
|
+
# OpenCV 5's HoughLinesP returns a true (N,4) array; OpenCV 4
|
|
161
|
+
# returns (N,1,4) (the old std::vector<T>-wrapping convention).
|
|
162
|
+
# Verified empirically 2026-08-03 (opencv-contrib-python 5.0.0.93
|
|
163
|
+
# vs 4.13.0.92). Drop the singleton middle dim only if present.
|
|
164
|
+
# -----------------------------------------------------------------
|
|
165
|
+
elif lines.ndim == 3 and lines.shape[1] == 1:
|
|
160
166
|
return lines[:, 0, :]
|
|
167
|
+
else:
|
|
168
|
+
return lines
|
|
161
169
|
|
|
162
170
|
def plot_lines(self, lines: np.ndarray, *args: Any, **kwargs: Any) -> None:
|
|
163
171
|
r"""
|
|
@@ -2014,6 +2014,25 @@ class LUCIDFeature(BaseFeature2D):
|
|
|
2014
2014
|
pass
|
|
2015
2015
|
|
|
2016
2016
|
|
|
2017
|
+
# --- OpenCV 4/5 compat -------------------------------------------------
|
|
2018
|
+
# OpenCV 5 dropped BRISK/AKAZE/KAZE from the main cv2 namespace; they now
|
|
2019
|
+
# live under cv2.xfeatures2d (verified empirically 2026-08-02, opencv-
|
|
2020
|
+
# contrib-python 5.0.0.93 vs 4.13.0.92 -- no OPENCV_ENABLE_NONFREE build
|
|
2021
|
+
# flag needed for these, unlike SURF). SIFT/ORB are unaffected, stay in
|
|
2022
|
+
# main cv2 in both versions. Resolve by attribute existence, not a
|
|
2023
|
+
# version-string check, so this keeps working if a future OpenCV release
|
|
2024
|
+
# moves things back or a build has both.
|
|
2025
|
+
def _resolve_feature_create(name: str) -> Any:
|
|
2026
|
+
if hasattr(cv2, f"{name}_create"):
|
|
2027
|
+
return getattr(cv2, f"{name}_create")
|
|
2028
|
+
if hasattr(cv2, "xfeatures2d") and hasattr(cv2.xfeatures2d, f"{name}_create"):
|
|
2029
|
+
return getattr(cv2.xfeatures2d, f"{name}_create")
|
|
2030
|
+
raise AttributeError(f"{name}_create not found in cv2 or cv2.xfeatures2d")
|
|
2031
|
+
|
|
2032
|
+
|
|
2033
|
+
# -------------------------------------------------------------------------
|
|
2034
|
+
|
|
2035
|
+
|
|
2017
2036
|
class ImagePointFeaturesMixin(_ImageBase if TYPE_CHECKING else object):
|
|
2018
2037
|
def _image2feature(
|
|
2019
2038
|
self,
|
|
@@ -2030,8 +2049,8 @@ class ImagePointFeaturesMixin(_ImageBase if TYPE_CHECKING else object):
|
|
|
2030
2049
|
"SIFT": getattr(cv2, "SIFT_create"),
|
|
2031
2050
|
"ORB": getattr(cv2, "ORB_create"),
|
|
2032
2051
|
"Harris": _Harris_create,
|
|
2033
|
-
"BRISK":
|
|
2034
|
-
"AKAZE":
|
|
2052
|
+
"BRISK": _resolve_feature_create("BRISK"),
|
|
2053
|
+
"AKAZE": _resolve_feature_create("AKAZE"),
|
|
2035
2054
|
# 'FREAK': (cv2.FREAK_create, FREAKFeature),
|
|
2036
2055
|
# 'DAISY': (cv2.DAISY_create, DAISYFeature),
|
|
2037
2056
|
}
|
|
@@ -173,6 +173,22 @@ class MSERFeature:
|
|
|
173
173
|
self._points = [
|
|
174
174
|
mser.T for mser in msers
|
|
175
175
|
] # transpose point arrays to be Nx2
|
|
176
|
+
# --- OpenCV 4/5 compat ---------------------------------------
|
|
177
|
+
# When no regions are found, OpenCV 5's detectRegions() returns
|
|
178
|
+
# an empty tuple () for bboxes instead of an empty ndarray(0,4)
|
|
179
|
+
# -- bboxes[:, 2:] then raises TypeError: tuple indices must be
|
|
180
|
+
# integers or slices, not tuple. Normalize first.
|
|
181
|
+
#
|
|
182
|
+
# Separately (not a shape issue, not fixable here): verified
|
|
183
|
+
# empirically 2026-08-03 that with identical default parameters
|
|
184
|
+
# and identical grayscale input, OpenCV 5's MSER finds fewer or
|
|
185
|
+
# zero regions compared to OpenCV 4 on real photos -- an
|
|
186
|
+
# upstream algorithm behavior change, not an API surface
|
|
187
|
+
# change. Logged as its own issue (#54), not something this
|
|
188
|
+
# compat fix can address.
|
|
189
|
+
# -----------------------------------------------------------------
|
|
190
|
+
if len(bboxes) == 0:
|
|
191
|
+
bboxes = np.zeros((0, 4), dtype=int)
|
|
176
192
|
bboxes[:, 2:] = bboxes[:, 0:2] + bboxes[:, 2:] # convert to lrtb
|
|
177
193
|
self._bboxes = bboxes
|
|
178
194
|
|
|
@@ -148,6 +148,15 @@ def parse_arguments():
|
|
|
148
148
|
help="images to load on startup. These appear in the variable img; or img[0], img[1], ... if multiple are specified",
|
|
149
149
|
)
|
|
150
150
|
|
|
151
|
+
parser.add_argument(
|
|
152
|
+
"--test",
|
|
153
|
+
default=False,
|
|
154
|
+
action="store_true",
|
|
155
|
+
help="non-interactive environment smoke test: print package versions, "
|
|
156
|
+
"exercise one real numeric code path per package, exit 0/1 "
|
|
157
|
+
"instead of starting an interactive shell",
|
|
158
|
+
)
|
|
159
|
+
|
|
151
160
|
argv = env_arguments(parser) + sys.argv[1:]
|
|
152
161
|
return parser.parse_known_args(argv)
|
|
153
162
|
|
|
@@ -183,23 +192,29 @@ def optional_torch_imports(enable):
|
|
|
183
192
|
return modules, warnings
|
|
184
193
|
|
|
185
194
|
|
|
195
|
+
def get_versions() -> list[str]:
|
|
196
|
+
"""Package version strings shown in the banner and by --test."""
|
|
197
|
+
versions = [
|
|
198
|
+
f"Python=={sys.version.split('|')[0].strip()}",
|
|
199
|
+
f"MVTB=={version('machinevision-toolbox-python')}",
|
|
200
|
+
f"SMTB=={version('spatialmath-python')}",
|
|
201
|
+
f"NumPy=={version('numpy')}",
|
|
202
|
+
f"SciPy=={version('scipy')}",
|
|
203
|
+
f"Matplotlib=={version('matplotlib')}",
|
|
204
|
+
f"OpenCV=={cv2.__version__}",
|
|
205
|
+
]
|
|
206
|
+
try:
|
|
207
|
+
versions.append(f"Open3D=={version('open3d')}")
|
|
208
|
+
except PackageNotFoundError:
|
|
209
|
+
versions.append("Open3D==not installed")
|
|
210
|
+
return versions
|
|
211
|
+
|
|
212
|
+
|
|
186
213
|
def make_banner(args, optional_modules=None):
|
|
187
214
|
if optional_modules is None:
|
|
188
215
|
optional_modules = {}
|
|
189
216
|
|
|
190
|
-
versions =
|
|
191
|
-
|
|
192
|
-
versions.append(f"Python=={sys.version.split('|')[0].strip()}")
|
|
193
|
-
versions.append(f"MVTB=={version('machinevision-toolbox-python')}")
|
|
194
|
-
versions.append(f"SMTB=={version('spatialmath-python')}")
|
|
195
|
-
versions.append(f"NumPy=={version('numpy')}")
|
|
196
|
-
versions.append(f"SciPy=={version('scipy')}")
|
|
197
|
-
versions.append(f"Matplotlib=={version('matplotlib')}")
|
|
198
|
-
versions.append(f"OpenCV=={cv2.__version__}")
|
|
199
|
-
try:
|
|
200
|
-
versions.append(f"Open3D=={version('open3d')}")
|
|
201
|
-
except PackageNotFoundError:
|
|
202
|
-
pass
|
|
217
|
+
versions = get_versions()
|
|
203
218
|
|
|
204
219
|
if "torch" in optional_modules:
|
|
205
220
|
versions.append(
|
|
@@ -260,7 +275,61 @@ func/object?? - show source code"""
|
|
|
260
275
|
print(banner)
|
|
261
276
|
|
|
262
277
|
|
|
278
|
+
def run_smoke_test() -> bool:
|
|
279
|
+
"""Non-interactive environment sanity check, used by --test.
|
|
280
|
+
|
|
281
|
+
Not a substitute for the pytest suite -- a fast, human- or script-run
|
|
282
|
+
"did this environment actually come together correctly" check: real
|
|
283
|
+
versions, and one real numeric result per OpenCV- and Open3D-backed
|
|
284
|
+
code path, checked against a sanity condition rather than just "it
|
|
285
|
+
didn't raise". Missing optional dependencies (e.g. Open3D) are
|
|
286
|
+
reported as a FAIL with the reason, not silently skipped.
|
|
287
|
+
"""
|
|
288
|
+
print(", ".join(get_versions()))
|
|
289
|
+
|
|
290
|
+
checks: list[tuple[str, bool]] = []
|
|
291
|
+
|
|
292
|
+
try:
|
|
293
|
+
img = Image.Read("monalisa.png", mono=True)
|
|
294
|
+
smoothed = img.smooth(sigma=2)
|
|
295
|
+
ok = smoothed.shape == img.shape and not np.array_equal(
|
|
296
|
+
smoothed.array, img.array
|
|
297
|
+
)
|
|
298
|
+
checks.append(("Image.Read + smooth() (OpenCV-backed)", ok))
|
|
299
|
+
except Exception as e:
|
|
300
|
+
checks.append((f"Image.Read + smooth() (OpenCV-backed): {e}", False))
|
|
301
|
+
|
|
302
|
+
try:
|
|
303
|
+
import open3d # noqa: F401 -- presence check; PointCloud does the real work
|
|
304
|
+
except ImportError as e:
|
|
305
|
+
checks.append(
|
|
306
|
+
(f"PointCloud.Read('bunny.ply') + voxel_grid(): Open3D not installed ({e})", False)
|
|
307
|
+
)
|
|
308
|
+
else:
|
|
309
|
+
try:
|
|
310
|
+
bunny = PointCloud.Read("bunny.ply")
|
|
311
|
+
voxels = bunny.voxel_grid(voxel_size=0.01)
|
|
312
|
+
ok = len(bunny) > 0 and len(voxels._voxels.get_voxels()) > 0
|
|
313
|
+
checks.append(("PointCloud.Read('bunny.ply') + voxel_grid() (Open3D-backed)", ok))
|
|
314
|
+
except Exception as e:
|
|
315
|
+
checks.append(
|
|
316
|
+
(f"PointCloud.Read('bunny.ply') + voxel_grid() (Open3D-backed): {e}", False)
|
|
317
|
+
)
|
|
318
|
+
|
|
319
|
+
for name, passed in checks:
|
|
320
|
+
print(f"[{'PASS' if passed else 'FAIL'}] {name}")
|
|
321
|
+
|
|
322
|
+
n_passed = sum(1 for _, passed in checks if passed)
|
|
323
|
+
print(f"mvtbtool --test: {n_passed}/{len(checks)} checks passed")
|
|
324
|
+
return n_passed == len(checks)
|
|
325
|
+
|
|
326
|
+
|
|
263
327
|
def main():
|
|
328
|
+
args, ipython_args = parse_arguments()
|
|
329
|
+
|
|
330
|
+
if args.test:
|
|
331
|
+
sys.exit(0 if run_smoke_test() else 1)
|
|
332
|
+
|
|
264
333
|
try:
|
|
265
334
|
import IPython
|
|
266
335
|
from IPython.terminal.prompts import Prompts
|
|
@@ -273,7 +342,6 @@ def main():
|
|
|
273
342
|
" pip install machinevision-toolbox-python[tool]\n"
|
|
274
343
|
)
|
|
275
344
|
|
|
276
|
-
args, ipython_args = parse_arguments()
|
|
277
345
|
torch_modules, torch_warnings = optional_torch_imports(args.torch)
|
|
278
346
|
|
|
279
347
|
if args.backend is not None:
|
|
File without changes
|
|
File without changes
|