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.
Files changed (63) hide show
  1. {machinevision_toolbox_python-1.0.2.dist-info → machinevision_toolbox_python-2.0.0.dist-info}/METADATA +103 -73
  2. machinevision_toolbox_python-2.0.0.dist-info/RECORD +68 -0
  3. {machinevision_toolbox_python-1.0.2.dist-info → machinevision_toolbox_python-2.0.0.dist-info}/WHEEL +1 -1
  4. {machinevision_toolbox_python-1.0.2.dist-info → machinevision_toolbox_python-2.0.0.dist-info}/entry_points.txt +3 -0
  5. machinevisiontoolbox/BagOfWords.py +74 -121
  6. machinevisiontoolbox/BundleAdjust.py +95 -77
  7. machinevisiontoolbox/Camera.py +642 -580
  8. machinevisiontoolbox/ImageBlobs.py +724 -571
  9. machinevisiontoolbox/ImageColor.py +105 -81
  10. machinevisiontoolbox/ImageConstants.py +674 -213
  11. machinevisiontoolbox/ImageCore.docstring +2877 -0
  12. machinevisiontoolbox/ImageCore.py +1644 -460
  13. machinevisiontoolbox/ImageFiducials.py +170 -98
  14. machinevisiontoolbox/ImageIO.py +330 -191
  15. machinevisiontoolbox/ImageLineFeatures.py +71 -37
  16. machinevisiontoolbox/ImageMorph.py +296 -117
  17. machinevisiontoolbox/ImageMultiview.py +129 -82
  18. machinevisiontoolbox/ImagePointFeatures.py +363 -419
  19. machinevisiontoolbox/ImageProcessing.py +703 -321
  20. machinevisiontoolbox/ImageRegionFeatures.py +79 -88
  21. machinevisiontoolbox/ImageReshape.py +308 -214
  22. machinevisiontoolbox/ImageSpatial.py +398 -936
  23. machinevisiontoolbox/ImageTensor.py +242 -0
  24. machinevisiontoolbox/ImageWholeFeatures.py +972 -310
  25. machinevisiontoolbox/Kernel.py +753 -0
  26. machinevisiontoolbox/PointCloud.py +122 -81
  27. machinevisiontoolbox/Sources.py +3675 -288
  28. machinevisiontoolbox/VisualServo.py +140 -99
  29. machinevisiontoolbox/__init__.py +160 -13
  30. machinevisiontoolbox/_image_typing.py +207 -0
  31. machinevisiontoolbox/base/__init__.py +11 -6
  32. machinevisiontoolbox/base/color.py +257 -156
  33. machinevisiontoolbox/base/data.py +25 -24
  34. machinevisiontoolbox/base/findpeaks.py +35 -10
  35. machinevisiontoolbox/base/graphics.py +378 -164
  36. machinevisiontoolbox/base/imageio.py +507 -216
  37. machinevisiontoolbox/base/meshgrid.py +13 -3
  38. machinevisiontoolbox/base/shapes.py +121 -62
  39. machinevisiontoolbox/base/types.py +28 -16
  40. machinevisiontoolbox/base/version.py +12 -5
  41. machinevisiontoolbox/bin/_bintools.py +61 -0
  42. machinevisiontoolbox/bin/bagtool.py +286 -0
  43. machinevisiontoolbox/bin/imtool.py +118 -45
  44. machinevisiontoolbox/bin/labeltool.py +147 -0
  45. machinevisiontoolbox/bin/mvtbtool.py +227 -47
  46. machinevisiontoolbox/bin/ocrtool.py +155 -0
  47. machinevisiontoolbox/bin/tagtool.py +163 -123
  48. machinevisiontoolbox/blocks/camera.py +14 -56
  49. machinevisiontoolbox/blocks/test_blocks.py +6 -5
  50. machinevisiontoolbox/camera_derivatives.py +1096 -5
  51. machinevisiontoolbox/cvfuncs.py +40 -0
  52. machinevisiontoolbox/decorators.py +17 -9
  53. machinevisiontoolbox/docbugs.py +47 -0
  54. machinevisiontoolbox/mvtb_types.py +15 -7
  55. machinevisiontoolbox/newcameras.py +4 -2
  56. machinevisiontoolbox/test_skimage.py +12 -0
  57. machinevisiontoolbox/testblobplots.py +17 -0
  58. machinevisiontoolbox/x.json +296 -0
  59. machinevision_toolbox_python-1.0.2.dist-info/RECORD +0 -58
  60. machinevisiontoolbox/base/esttheta.py +0 -97
  61. machinevisiontoolbox/bin/ce.py +0 -27
  62. machinevisiontoolbox/machinevision-toolbox-python.code-workspace +0 -7
  63. {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
- #!/usr/bin/env python3
1
+ """
2
+ Visual Bag of Words model for image classification and retrieval.
3
+ """
2
4
 
3
5
  from collections import Counter
4
- from machinevisiontoolbox.ImagePointFeatures import BaseFeature2D
6
+ from typing import Any
7
+
8
+ import cv2
5
9
  import numpy as np
6
- import cv2 as cv
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__(self, images, k=2_000, nstopwords=0, attempts=1, seed=None):
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/master/d5/d38/group__core__cluster.html#ga9a34dc06c6ec9460e90860f15bcd2f88>`_
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 = (cv.TERM_CRITERIA_EPS + cv.TERM_CRITERIA_MAX_ITER, 10, 1.0)
97
+ criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0)
84
98
 
85
99
  if seed is not None:
86
- cv.setRNGSeed(seed)
100
+ cv2.setRNGSeed(seed)
87
101
 
88
- ret, labels, centroids = cv.kmeans(
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=cv.KMEANS_RANDOM_CENTERS,
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 occurences of word i
126
- # multiple occurences in the one image count only as one
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, arg):
363
+ def similarity(self, query) -> np.ndarray:
350
364
  """
351
- Compute similarity between bag and query images
365
+ Compute similarity between bag of words and query
352
366
 
353
- :param other: bag of words
354
- :type other: BagOfWords
355
- :return: confusion matrix
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 images in ``other``.
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(arg, np.ndarray):
364
- wwfv = arg
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 = arg
377
- if not hasattr(images, "__iter__"):
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 = cv.BFMatcher(cv.NORM_L2, crossCheck=False)
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, word, images=None, maxperimage=2, columns=10, max=None, width=50, **kwargs
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
- import numpy as np
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