machinevision-toolbox-python 1.0.2__py3-none-any.whl → 2.0.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-1.0.2.dist-info → machinevision_toolbox_python-2.0.0.dist-info}/METADATA +103 -73
- machinevision_toolbox_python-2.0.0.dist-info/RECORD +68 -0
- {machinevision_toolbox_python-1.0.2.dist-info → machinevision_toolbox_python-2.0.0.dist-info}/WHEEL +1 -1
- {machinevision_toolbox_python-1.0.2.dist-info → machinevision_toolbox_python-2.0.0.dist-info}/entry_points.txt +3 -0
- machinevisiontoolbox/BagOfWords.py +74 -121
- machinevisiontoolbox/BundleAdjust.py +95 -77
- machinevisiontoolbox/Camera.py +642 -580
- machinevisiontoolbox/ImageBlobs.py +724 -571
- machinevisiontoolbox/ImageColor.py +105 -81
- machinevisiontoolbox/ImageConstants.py +674 -213
- machinevisiontoolbox/ImageCore.docstring +2877 -0
- machinevisiontoolbox/ImageCore.py +1644 -460
- machinevisiontoolbox/ImageFiducials.py +170 -98
- machinevisiontoolbox/ImageIO.py +330 -191
- machinevisiontoolbox/ImageLineFeatures.py +71 -37
- machinevisiontoolbox/ImageMorph.py +296 -117
- machinevisiontoolbox/ImageMultiview.py +129 -82
- machinevisiontoolbox/ImagePointFeatures.py +363 -419
- machinevisiontoolbox/ImageProcessing.py +703 -321
- machinevisiontoolbox/ImageRegionFeatures.py +79 -88
- machinevisiontoolbox/ImageReshape.py +308 -214
- machinevisiontoolbox/ImageSpatial.py +398 -936
- machinevisiontoolbox/ImageTensor.py +242 -0
- machinevisiontoolbox/ImageWholeFeatures.py +972 -310
- machinevisiontoolbox/Kernel.py +753 -0
- machinevisiontoolbox/PointCloud.py +122 -81
- machinevisiontoolbox/Sources.py +3675 -288
- machinevisiontoolbox/VisualServo.py +140 -99
- machinevisiontoolbox/__init__.py +160 -13
- machinevisiontoolbox/_image_typing.py +207 -0
- machinevisiontoolbox/base/__init__.py +11 -6
- machinevisiontoolbox/base/color.py +257 -156
- machinevisiontoolbox/base/data.py +25 -24
- machinevisiontoolbox/base/findpeaks.py +35 -10
- machinevisiontoolbox/base/graphics.py +378 -164
- machinevisiontoolbox/base/imageio.py +507 -216
- machinevisiontoolbox/base/meshgrid.py +13 -3
- machinevisiontoolbox/base/shapes.py +121 -62
- machinevisiontoolbox/base/types.py +28 -16
- machinevisiontoolbox/base/version.py +12 -5
- machinevisiontoolbox/bin/_bintools.py +61 -0
- machinevisiontoolbox/bin/bagtool.py +286 -0
- machinevisiontoolbox/bin/imtool.py +118 -45
- machinevisiontoolbox/bin/labeltool.py +147 -0
- machinevisiontoolbox/bin/mvtbtool.py +227 -47
- machinevisiontoolbox/bin/ocrtool.py +155 -0
- machinevisiontoolbox/bin/tagtool.py +163 -123
- machinevisiontoolbox/blocks/camera.py +14 -56
- machinevisiontoolbox/blocks/test_blocks.py +6 -5
- machinevisiontoolbox/camera_derivatives.py +1096 -5
- machinevisiontoolbox/cvfuncs.py +40 -0
- machinevisiontoolbox/decorators.py +17 -9
- machinevisiontoolbox/docbugs.py +47 -0
- machinevisiontoolbox/mvtb_types.py +15 -7
- machinevisiontoolbox/newcameras.py +4 -2
- machinevisiontoolbox/test_skimage.py +12 -0
- machinevisiontoolbox/testblobplots.py +17 -0
- machinevisiontoolbox/x.json +296 -0
- machinevision_toolbox_python-1.0.2.dist-info/RECORD +0 -58
- machinevisiontoolbox/base/esttheta.py +0 -97
- machinevisiontoolbox/bin/ce.py +0 -27
- machinevisiontoolbox/machinevision-toolbox-python.code-workspace +0 -7
- {machinevision_toolbox_python-1.0.2.dist-info → machinevision_toolbox_python-2.0.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,13 +1,27 @@
|
|
|
1
|
-
|
|
1
|
+
"""
|
|
2
|
+
Visual Bag of Words model for image classification and retrieval.
|
|
3
|
+
"""
|
|
2
4
|
|
|
3
5
|
from collections import Counter
|
|
4
|
-
from
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
import cv2
|
|
5
9
|
import numpy as np
|
|
6
|
-
|
|
10
|
+
|
|
11
|
+
from machinevisiontoolbox.ImagePointFeatures import BaseFeature2D
|
|
12
|
+
from machinevisiontoolbox import Image
|
|
13
|
+
|
|
7
14
|
|
|
8
15
|
# TODO: remove top N% and bottom M% of words by frequency
|
|
9
16
|
class BagOfWords:
|
|
10
|
-
def __init__(
|
|
17
|
+
def __init__(
|
|
18
|
+
self,
|
|
19
|
+
images,
|
|
20
|
+
k: int = 2_000,
|
|
21
|
+
nstopwords: int = 0,
|
|
22
|
+
attempts: int = 1,
|
|
23
|
+
seed: int | None = None,
|
|
24
|
+
) -> None:
|
|
11
25
|
r"""
|
|
12
26
|
Bag of words class
|
|
13
27
|
|
|
@@ -54,7 +68,7 @@ class BagOfWords:
|
|
|
54
68
|
|
|
55
69
|
:seealso: :meth:`recall` :meth:`~machinevisiontoolbox.ImagePointFeatures.BaseFeature2D`
|
|
56
70
|
:meth:`~machinevisiontoolbox.ImagePointFeatures.SIFT`
|
|
57
|
-
`cv2.kmeans <https://docs.opencv.org/
|
|
71
|
+
`cv2.kmeans <https://docs.opencv.org/4.x/d5/d38/group__core__cluster.html#ga9a34dc06c6ec9460e90860f15bcd2f88>`_
|
|
58
72
|
"""
|
|
59
73
|
|
|
60
74
|
if images is None:
|
|
@@ -80,18 +94,18 @@ class BagOfWords:
|
|
|
80
94
|
# do the clustering
|
|
81
95
|
# NO IDEA WHAT EPSILON ACTUALLY MEANS, NORM OF THE SHIFT IN CENTROIDS?
|
|
82
96
|
# NO IDEA HOW TO TELL WHAT CRITERIA IT TERMINATES ON
|
|
83
|
-
criteria = (
|
|
97
|
+
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0)
|
|
84
98
|
|
|
85
99
|
if seed is not None:
|
|
86
|
-
|
|
100
|
+
cv2.setRNGSeed(seed)
|
|
87
101
|
|
|
88
|
-
ret, labels, centroids =
|
|
102
|
+
ret, labels, centroids = cv2.kmeans(
|
|
89
103
|
data=features._descriptor,
|
|
90
104
|
K=k,
|
|
91
105
|
bestLabels=None,
|
|
92
106
|
criteria=criteria,
|
|
93
107
|
attempts=attempts,
|
|
94
|
-
flags=
|
|
108
|
+
flags=cv2.KMEANS_RANDOM_CENTERS,
|
|
95
109
|
)
|
|
96
110
|
|
|
97
111
|
self._k = k
|
|
@@ -122,8 +136,8 @@ class BagOfWords:
|
|
|
122
136
|
|
|
123
137
|
N = self.nimages
|
|
124
138
|
|
|
125
|
-
# total number of
|
|
126
|
-
# multiple
|
|
139
|
+
# total number of occurrences of word i
|
|
140
|
+
# multiple occurrences in the one image count only as one
|
|
127
141
|
ni = (W > 0).sum(axis=1)
|
|
128
142
|
idf = np.log(N / ni)
|
|
129
143
|
|
|
@@ -145,7 +159,7 @@ class BagOfWords:
|
|
|
145
159
|
self._word_freq_vectors = np.column_stack(M)
|
|
146
160
|
self._idf = idf
|
|
147
161
|
|
|
148
|
-
def wwfv(self, i=None):
|
|
162
|
+
def wwfv(self, i: int | None = None) -> np.ndarray:
|
|
149
163
|
"""
|
|
150
164
|
Weighted word frequency vector for image
|
|
151
165
|
|
|
@@ -171,7 +185,7 @@ class BagOfWords:
|
|
|
171
185
|
return self._word_freq_vectors
|
|
172
186
|
|
|
173
187
|
@property
|
|
174
|
-
def nimages(self):
|
|
188
|
+
def nimages(self) -> int:
|
|
175
189
|
"""
|
|
176
190
|
Number of images associated in the bag
|
|
177
191
|
|
|
@@ -181,7 +195,7 @@ class BagOfWords:
|
|
|
181
195
|
return self._nimages
|
|
182
196
|
|
|
183
197
|
@property
|
|
184
|
-
def images(self):
|
|
198
|
+
def images(self) -> Any:
|
|
185
199
|
"""
|
|
186
200
|
Images associated with this bag
|
|
187
201
|
|
|
@@ -193,7 +207,7 @@ class BagOfWords:
|
|
|
193
207
|
return self._images
|
|
194
208
|
|
|
195
209
|
@property
|
|
196
|
-
def k(self):
|
|
210
|
+
def k(self) -> int:
|
|
197
211
|
"""
|
|
198
212
|
Number of words in the visual vocabulary
|
|
199
213
|
|
|
@@ -205,7 +219,7 @@ class BagOfWords:
|
|
|
205
219
|
return self._k
|
|
206
220
|
|
|
207
221
|
@property
|
|
208
|
-
def words(self):
|
|
222
|
+
def words(self) -> np.ndarray:
|
|
209
223
|
"""
|
|
210
224
|
Word labels for every feature
|
|
211
225
|
|
|
@@ -221,7 +235,7 @@ class BagOfWords:
|
|
|
221
235
|
|
|
222
236
|
# TODO better name for above
|
|
223
237
|
|
|
224
|
-
def word(self, f):
|
|
238
|
+
def word(self, f: int) -> int:
|
|
225
239
|
"""
|
|
226
240
|
Word labels for original feature
|
|
227
241
|
|
|
@@ -233,7 +247,7 @@ class BagOfWords:
|
|
|
233
247
|
return self._labels[f]
|
|
234
248
|
|
|
235
249
|
@property
|
|
236
|
-
def nwords(self):
|
|
250
|
+
def nwords(self) -> int:
|
|
237
251
|
"""
|
|
238
252
|
Number of usable words
|
|
239
253
|
|
|
@@ -247,7 +261,7 @@ class BagOfWords:
|
|
|
247
261
|
return self._k - self._nstopwords
|
|
248
262
|
|
|
249
263
|
@property
|
|
250
|
-
def nstopwords(self):
|
|
264
|
+
def nstopwords(self) -> int:
|
|
251
265
|
"""
|
|
252
266
|
Number of stop words
|
|
253
267
|
|
|
@@ -259,7 +273,7 @@ class BagOfWords:
|
|
|
259
273
|
return self._nstopwords
|
|
260
274
|
|
|
261
275
|
@property
|
|
262
|
-
def firststop(self):
|
|
276
|
+
def firststop(self) -> int:
|
|
263
277
|
"""
|
|
264
278
|
First stop word
|
|
265
279
|
|
|
@@ -269,7 +283,7 @@ class BagOfWords:
|
|
|
269
283
|
return self.k - self._nstopwords
|
|
270
284
|
|
|
271
285
|
@property
|
|
272
|
-
def centroids(self):
|
|
286
|
+
def centroids(self) -> np.ndarray:
|
|
273
287
|
"""
|
|
274
288
|
Word feature centroids
|
|
275
289
|
|
|
@@ -289,15 +303,15 @@ class BagOfWords:
|
|
|
289
303
|
"""
|
|
290
304
|
return self._centroids
|
|
291
305
|
|
|
292
|
-
def __repr__(self):
|
|
306
|
+
def __repr__(self) -> str:
|
|
293
307
|
return str(self)
|
|
294
308
|
|
|
295
|
-
def __str__(self):
|
|
309
|
+
def __str__(self) -> str:
|
|
296
310
|
s = f"BagOfWords: {len(self.words)} features from {self.nimages} images"
|
|
297
311
|
s += f", {self.nwords} words, {self.nstopwords} stop words"
|
|
298
312
|
return s
|
|
299
313
|
|
|
300
|
-
def _remove_stopwords(self, verbose=True):
|
|
314
|
+
def _remove_stopwords(self, verbose: bool = True) -> None:
|
|
301
315
|
# BagOfWords.remove_stop Remove stop words
|
|
302
316
|
#
|
|
303
317
|
# B.remove_stop(N) removes the N most frequent words (the stop words)
|
|
@@ -346,22 +360,30 @@ class BagOfWords:
|
|
|
346
360
|
# rearrange the cluster centroids
|
|
347
361
|
self._centroids = self._centroids[map]
|
|
348
362
|
|
|
349
|
-
def similarity(self,
|
|
363
|
+
def similarity(self, query) -> np.ndarray:
|
|
350
364
|
"""
|
|
351
|
-
Compute similarity between bag and query
|
|
365
|
+
Compute similarity between bag of words and query
|
|
352
366
|
|
|
353
|
-
:param
|
|
354
|
-
:type
|
|
355
|
-
:return:
|
|
367
|
+
:param query: bag of words or image features
|
|
368
|
+
:type query: BagOfWords or ndarray
|
|
369
|
+
:return: similarity matrix
|
|
356
370
|
:rtype: ndarray(M,N)
|
|
357
371
|
|
|
358
372
|
The array has rows corresponding to the images in ``self`` and
|
|
359
|
-
columns corresponding to the
|
|
373
|
+
columns corresponding to the queries in ``query``.
|
|
374
|
+
|
|
375
|
+
``query`` can be:
|
|
376
|
+
|
|
377
|
+
- a single image, a list of images, or an Image iterator (like VideoFile,
|
|
378
|
+
ZipArchive etc.) for which the visual words are computed using the same
|
|
379
|
+
dictionary of visual words as the bag.
|
|
380
|
+
- a set of image features, in which case the similarity is computed between the
|
|
381
|
+
bag and the query features, or
|
|
360
382
|
|
|
361
383
|
:seealso: :meth:`.closest`
|
|
362
384
|
"""
|
|
363
|
-
if isinstance(
|
|
364
|
-
wwfv =
|
|
385
|
+
if isinstance(query, np.ndarray):
|
|
386
|
+
wwfv = query
|
|
365
387
|
sim = np.empty((wwfv.shape[1], self.nimages))
|
|
366
388
|
|
|
367
389
|
for j, vj in enumerate(wwfv.T):
|
|
@@ -373,10 +395,8 @@ class BagOfWords:
|
|
|
373
395
|
np.linalg.norm(vi) * np.linalg.norm(vj)
|
|
374
396
|
)
|
|
375
397
|
else:
|
|
376
|
-
images =
|
|
377
|
-
if
|
|
378
|
-
# if not iterable like a FileCollection or VideoFile turn the image
|
|
379
|
-
# into a list of 1
|
|
398
|
+
images = query
|
|
399
|
+
if isinstance(images, Image):
|
|
380
400
|
images = [images]
|
|
381
401
|
|
|
382
402
|
# similarity has bag index as column, query index as row
|
|
@@ -388,7 +408,7 @@ class BagOfWords:
|
|
|
388
408
|
# the elements of matches are:
|
|
389
409
|
# queryIdx: new feature index
|
|
390
410
|
# trainingIdx: cluster centre index
|
|
391
|
-
bfm =
|
|
411
|
+
bfm = cv2.BFMatcher(normType=cv2.NORM_L2, crossCheck=False)
|
|
392
412
|
matches = bfm.match(features._descriptor, self._centroids)
|
|
393
413
|
words = np.array([m.trainIdx for m in matches])
|
|
394
414
|
|
|
@@ -418,13 +438,13 @@ class BagOfWords:
|
|
|
418
438
|
sim = sim[0, :]
|
|
419
439
|
return sim
|
|
420
440
|
|
|
421
|
-
def retrieve(self, images):
|
|
441
|
+
def retrieve(self, images) -> tuple[int, float]:
|
|
422
442
|
|
|
423
443
|
S = self.similarity(images).ravel()
|
|
424
444
|
k = np.argmax(S)
|
|
425
445
|
return k, S[k]
|
|
426
446
|
|
|
427
|
-
def features(self, word):
|
|
447
|
+
def features(self, word: int) -> BaseFeature2D:
|
|
428
448
|
"""
|
|
429
449
|
Get features corresponding to word
|
|
430
450
|
|
|
@@ -439,7 +459,7 @@ class BagOfWords:
|
|
|
439
459
|
"""
|
|
440
460
|
return self._features[self.words == word]
|
|
441
461
|
|
|
442
|
-
def occurrence(self, word):
|
|
462
|
+
def occurrence(self, word: int) -> int:
|
|
443
463
|
"""
|
|
444
464
|
Number of occurrences of specified word
|
|
445
465
|
|
|
@@ -451,7 +471,7 @@ class BagOfWords:
|
|
|
451
471
|
return np.sum(self.words == word)
|
|
452
472
|
|
|
453
473
|
@staticmethod
|
|
454
|
-
def _word_freq_vector(words, maxwords):
|
|
474
|
+
def _word_freq_vector(words, maxwords: int) -> np.ndarray:
|
|
455
475
|
# create columns of the W
|
|
456
476
|
unique, unique_counts = np.unique(words, return_counts=True)
|
|
457
477
|
# [w,f] = count_unique(words)
|
|
@@ -459,7 +479,7 @@ class BagOfWords:
|
|
|
459
479
|
v[unique] = unique_counts
|
|
460
480
|
return v
|
|
461
481
|
|
|
462
|
-
def wordfreq(self):
|
|
482
|
+
def wordfreq(self) -> tuple[np.ndarray, np.ndarray]:
|
|
463
483
|
"""
|
|
464
484
|
Get visual word frequency
|
|
465
485
|
|
|
@@ -476,7 +496,7 @@ class BagOfWords:
|
|
|
476
496
|
# elements of N are the number of occurrences of that word.
|
|
477
497
|
return np.unique(self.words, return_counts=True)
|
|
478
498
|
|
|
479
|
-
def closest(self, S, i):
|
|
499
|
+
def closest(self, S: np.ndarray, i: int) -> tuple[np.ndarray, np.ndarray]:
|
|
480
500
|
"""
|
|
481
501
|
Find closest image
|
|
482
502
|
|
|
@@ -494,7 +514,7 @@ class BagOfWords:
|
|
|
494
514
|
|
|
495
515
|
return index, s[index]
|
|
496
516
|
|
|
497
|
-
def contains(self, word):
|
|
517
|
+
def contains(self, word: int) -> np.ndarray:
|
|
498
518
|
"""
|
|
499
519
|
Images that contain specified word
|
|
500
520
|
|
|
@@ -508,7 +528,14 @@ class BagOfWords:
|
|
|
508
528
|
return np.unique(self._image_id[self.words == word])
|
|
509
529
|
|
|
510
530
|
def exemplars(
|
|
511
|
-
self,
|
|
531
|
+
self,
|
|
532
|
+
word: int,
|
|
533
|
+
images=None,
|
|
534
|
+
maxperimage: int = 2,
|
|
535
|
+
columns: int = 10,
|
|
536
|
+
max: int | None = None,
|
|
537
|
+
width: int = 50,
|
|
538
|
+
**kwargs,
|
|
512
539
|
):
|
|
513
540
|
"""
|
|
514
541
|
Composite image containing exemplars of specified word
|
|
@@ -554,79 +581,5 @@ class BagOfWords:
|
|
|
554
581
|
|
|
555
582
|
|
|
556
583
|
if __name__ == "__main__":
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
import matplotlib.pyplot as plt
|
|
560
|
-
from machinevisiontoolbox import *
|
|
561
|
-
import cv2 as cv
|
|
562
|
-
|
|
563
|
-
cv.setRNGSeed(0)
|
|
564
|
-
|
|
565
|
-
images = ImageCollection("campus/*.png", mono=True)
|
|
566
|
-
|
|
567
|
-
features = []
|
|
568
|
-
for image in images:
|
|
569
|
-
features += image.SIFT()
|
|
570
|
-
# sort them in descending order by strength
|
|
571
|
-
features.sort(by="scale", inplace=True)
|
|
572
|
-
|
|
573
|
-
features[:10].table()
|
|
574
|
-
|
|
575
|
-
ex = []
|
|
576
|
-
for i in range(400):
|
|
577
|
-
ex.append(features[i].support(images))
|
|
578
|
-
|
|
579
|
-
Image.Tile(ex, columns=20).disp(plain=True)
|
|
580
|
-
|
|
581
|
-
feature = features[108]
|
|
582
|
-
print(feature)
|
|
583
|
-
|
|
584
|
-
bag = BagOfWords(features, 2_000)
|
|
585
|
-
|
|
586
|
-
w = bag.word(108)
|
|
587
|
-
print(w)
|
|
588
|
-
print(bag.occurrence(w))
|
|
589
|
-
print(bag.contains(w))
|
|
590
|
-
|
|
591
|
-
bag.exemplars(w, images)
|
|
592
|
-
|
|
593
|
-
bag = BagOfWords(images, 2_000)
|
|
594
|
-
print(bag)
|
|
595
|
-
|
|
596
|
-
w, f = bag.wordfreq()
|
|
597
|
-
print(len(w))
|
|
598
|
-
|
|
599
|
-
bag = BagOfWords(images, 2_000, nstopwords=50)
|
|
600
|
-
print(bag)
|
|
601
|
-
|
|
602
|
-
print(bag.wwfv(0).shape)
|
|
603
|
-
print(bag.wwfv().shape)
|
|
604
|
-
|
|
605
|
-
print(bag.similarity(bag.wwfv(3)))
|
|
606
|
-
print(bag.similarity(images[:5]))
|
|
607
|
-
|
|
608
|
-
sim_8 = bag.similarity(images[8]).ravel()
|
|
609
|
-
print(sim_8)
|
|
610
|
-
k = np.argsort(-sim_8)
|
|
611
|
-
print(np.c_[sim_8[k], k])
|
|
612
|
-
|
|
613
|
-
ss = []
|
|
614
|
-
for i in range(4):
|
|
615
|
-
ss.append(images[k[i]])
|
|
616
|
-
Image.Tile(ss, columns=2).disp()
|
|
617
|
-
|
|
618
|
-
holdout = ImageCollection("campus/holdout/*.png", mono=True)
|
|
619
|
-
|
|
620
|
-
sim = bag.similarity(holdout)
|
|
621
|
-
|
|
622
|
-
sim_2 = bag.similarity(holdout[2]).ravel()
|
|
623
|
-
print(sim_2)
|
|
624
|
-
k = np.argsort(-sim_2)
|
|
625
|
-
print(np.c_[sim_2[k], k])
|
|
626
|
-
|
|
627
|
-
ss = [holdout[2]]
|
|
628
|
-
for i in range(3):
|
|
629
|
-
ss.append(images[k[i]])
|
|
630
|
-
Image.Tile(ss, columns=2).disp()
|
|
631
|
-
|
|
632
|
-
Image(sim).disp(block=True)
|
|
584
|
+
# No dedicated test file for this module.
|
|
585
|
+
pass
|